quantum-framework 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Queue Executor - Execute q:queue statements
|
|
3
|
+
|
|
4
|
+
Handles queue/exchange management operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
from quantum.core.ast_nodes import QueueNode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QueueExecutor(BaseExecutor):
|
|
13
|
+
"""
|
|
14
|
+
Executor for q:queue statements.
|
|
15
|
+
|
|
16
|
+
Supports:
|
|
17
|
+
- Queue declaration
|
|
18
|
+
- Queue purge
|
|
19
|
+
- Queue deletion
|
|
20
|
+
- Queue info retrieval
|
|
21
|
+
- Dead letter queue configuration
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def handles(self) -> List[Type]:
|
|
26
|
+
return [QueueNode]
|
|
27
|
+
|
|
28
|
+
def execute(self, node: QueueNode, exec_context) -> Any:
|
|
29
|
+
"""
|
|
30
|
+
Execute queue management operation.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
node: QueueNode with queue configuration
|
|
34
|
+
exec_context: Execution context
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Queue operation result dict
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
context = exec_context.get_all_variables()
|
|
41
|
+
|
|
42
|
+
# Resolve queue name
|
|
43
|
+
queue_name = self.apply_databinding(node.name, context)
|
|
44
|
+
|
|
45
|
+
# Execute based on action
|
|
46
|
+
if node.action == 'declare':
|
|
47
|
+
result = self._execute_declare(node, queue_name, context)
|
|
48
|
+
elif node.action == 'purge':
|
|
49
|
+
result = self._execute_purge(queue_name)
|
|
50
|
+
elif node.action == 'delete':
|
|
51
|
+
result = self._execute_delete(queue_name)
|
|
52
|
+
elif node.action == 'info':
|
|
53
|
+
result = self._execute_info(queue_name)
|
|
54
|
+
else:
|
|
55
|
+
raise ExecutorError(f"Unknown queue action: {node.action}")
|
|
56
|
+
|
|
57
|
+
# Store result
|
|
58
|
+
result_var = node.result if hasattr(node, 'result') and node.result else f"{queue_name}_result"
|
|
59
|
+
exec_context.set_variable(result_var, result, scope="component")
|
|
60
|
+
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
raise ExecutorError(f"Queue operation error: {e}")
|
|
65
|
+
|
|
66
|
+
def _execute_declare(self, node: QueueNode, queue_name: str, context: Dict[str, Any]) -> Dict:
|
|
67
|
+
"""Declare a queue with configuration."""
|
|
68
|
+
# Build queue options
|
|
69
|
+
options = {
|
|
70
|
+
'durable': getattr(node, 'durable', True),
|
|
71
|
+
'exclusive': getattr(node, 'exclusive', False),
|
|
72
|
+
'auto_delete': getattr(node, 'auto_delete', False)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Dead letter queue
|
|
76
|
+
if hasattr(node, 'dead_letter') and node.dead_letter:
|
|
77
|
+
dlq = self.apply_databinding(node.dead_letter, context)
|
|
78
|
+
options['dead_letter_queue'] = dlq
|
|
79
|
+
|
|
80
|
+
# TTL
|
|
81
|
+
if hasattr(node, 'ttl') and node.ttl:
|
|
82
|
+
options['ttl'] = int(node.ttl)
|
|
83
|
+
|
|
84
|
+
# Max length
|
|
85
|
+
if hasattr(node, 'max_length') and node.max_length:
|
|
86
|
+
options['max_length'] = int(node.max_length)
|
|
87
|
+
|
|
88
|
+
return self.services.message_queue.declare_queue(
|
|
89
|
+
name=queue_name,
|
|
90
|
+
**options
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def _execute_purge(self, queue_name: str) -> Dict:
|
|
94
|
+
"""Purge all messages from a queue."""
|
|
95
|
+
return self.services.message_queue.purge_queue(queue_name)
|
|
96
|
+
|
|
97
|
+
def _execute_delete(self, queue_name: str) -> Dict:
|
|
98
|
+
"""Delete a queue."""
|
|
99
|
+
return self.services.message_queue.delete_queue(queue_name)
|
|
100
|
+
|
|
101
|
+
def _execute_info(self, queue_name: str) -> Dict:
|
|
102
|
+
"""Get queue information."""
|
|
103
|
+
return self.services.message_queue.get_queue_info(queue_name)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket Executor - Execute q:websocket statements
|
|
3
|
+
|
|
4
|
+
Handles WebSocket connection management and messaging.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
|
|
10
|
+
# Import from features module
|
|
11
|
+
try:
|
|
12
|
+
from quantum.core.features.websocket.src.ast_node import (
|
|
13
|
+
WebSocketNode, WebSocketSendNode, WebSocketCloseNode
|
|
14
|
+
)
|
|
15
|
+
except ImportError:
|
|
16
|
+
from quantum.core.features.websocket.src import (
|
|
17
|
+
WebSocketNode, WebSocketSendNode, WebSocketCloseNode
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WebSocketExecutor(BaseExecutor):
|
|
22
|
+
"""
|
|
23
|
+
Executor for q:websocket statements.
|
|
24
|
+
|
|
25
|
+
Supports:
|
|
26
|
+
- WebSocket connection creation
|
|
27
|
+
- Auto-connect and reconnection
|
|
28
|
+
- Event handlers (on-connect, on-message, on-error, on-close)
|
|
29
|
+
- Heartbeat/ping-pong
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def handles(self) -> List[Type]:
|
|
34
|
+
return [WebSocketNode]
|
|
35
|
+
|
|
36
|
+
def execute(self, node: WebSocketNode, exec_context) -> Any:
|
|
37
|
+
"""
|
|
38
|
+
Execute WebSocket connection creation.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
node: WebSocketNode with WebSocket configuration
|
|
42
|
+
exec_context: Execution context
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
WebSocket connection info dict
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
context = exec_context.get_all_variables()
|
|
49
|
+
|
|
50
|
+
# Resolve URL
|
|
51
|
+
url = self.apply_databinding(node.url, context)
|
|
52
|
+
|
|
53
|
+
# register_handler() expects a callable, not a config dict —
|
|
54
|
+
# build a closure per event that runs the handler's AST body
|
|
55
|
+
# with the incoming event payload bound as local variables.
|
|
56
|
+
handlers = {}
|
|
57
|
+
for handler in node.handlers:
|
|
58
|
+
handlers[handler.event] = self._build_event_handler(handler.body)
|
|
59
|
+
|
|
60
|
+
# Handlers first, connection second. Handlers are keyed by
|
|
61
|
+
# connection NAME, so registering them early is safe — and with a
|
|
62
|
+
# real transport, autoConnect opens the socket inside
|
|
63
|
+
# register_connection and fires on-connect immediately. Register
|
|
64
|
+
# after, and that first event is dispatched to nobody.
|
|
65
|
+
for event, handler in handlers.items():
|
|
66
|
+
self.services.websocket.register_handler(node.name, event, handler)
|
|
67
|
+
|
|
68
|
+
# WebSocketService.register_connection() takes (name, url,
|
|
69
|
+
# metadata) — there is no create_connection(). Connection options
|
|
70
|
+
# ride along as metadata; auto_connect there decides whether a
|
|
71
|
+
# socket is actually opened.
|
|
72
|
+
connection = self.services.websocket.register_connection(
|
|
73
|
+
name=node.name,
|
|
74
|
+
url=url,
|
|
75
|
+
metadata={
|
|
76
|
+
'auto_connect': node.auto_connect,
|
|
77
|
+
'reconnect': node.reconnect,
|
|
78
|
+
'reconnect_delay': node.reconnect_delay,
|
|
79
|
+
'max_reconnects': node.max_reconnects,
|
|
80
|
+
'heartbeat': node.heartbeat,
|
|
81
|
+
'protocols': node.protocols.split(',') if node.protocols else [],
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
result = connection.to_dict()
|
|
86
|
+
|
|
87
|
+
# Store connection reference
|
|
88
|
+
exec_context.set_variable(node.name, result, scope="component")
|
|
89
|
+
|
|
90
|
+
return result
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
exec_context.set_variable(node.name, {
|
|
94
|
+
'connected': False,
|
|
95
|
+
'error': str(e),
|
|
96
|
+
'url': node.url
|
|
97
|
+
}, scope="component")
|
|
98
|
+
raise ExecutorError(f"WebSocket creation error: {e}")
|
|
99
|
+
|
|
100
|
+
def _build_event_handler(self, body):
|
|
101
|
+
"""Build a callable for WebSocketService.register_handler().
|
|
102
|
+
|
|
103
|
+
The service calls handlers with the event payload dict; that payload
|
|
104
|
+
is bound as local variables so the handler body can reference it.
|
|
105
|
+
"""
|
|
106
|
+
def handle_event(event_data=None):
|
|
107
|
+
variables = event_data if isinstance(event_data, dict) else {'event': event_data}
|
|
108
|
+
return self.run_body_in_child_context(body, variables)
|
|
109
|
+
|
|
110
|
+
return handle_event
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class WebSocketSendExecutor(BaseExecutor):
|
|
114
|
+
"""
|
|
115
|
+
Executor for q:websocket-send statements.
|
|
116
|
+
|
|
117
|
+
Sends messages through an existing WebSocket connection.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def handles(self) -> List[Type]:
|
|
122
|
+
return [WebSocketSendNode]
|
|
123
|
+
|
|
124
|
+
def execute(self, node: WebSocketSendNode, exec_context) -> Any:
|
|
125
|
+
"""
|
|
126
|
+
Execute WebSocket message send.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
node: WebSocketSendNode with send configuration
|
|
130
|
+
exec_context: Execution context
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Send result dict
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
context = exec_context.get_all_variables()
|
|
137
|
+
|
|
138
|
+
# Resolve message
|
|
139
|
+
message = self.apply_databinding(node.message, context)
|
|
140
|
+
|
|
141
|
+
# Get connection
|
|
142
|
+
connection = node.connection
|
|
143
|
+
|
|
144
|
+
# Send message
|
|
145
|
+
sent = self.services.websocket.send_message(
|
|
146
|
+
connection_name=connection,
|
|
147
|
+
data=message,
|
|
148
|
+
msg_type=node.type or 'text',
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return {'connection': connection, 'sent': sent}
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
raise ExecutorError(f"WebSocket send error: {e}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class WebSocketCloseExecutor(BaseExecutor):
|
|
158
|
+
"""
|
|
159
|
+
Executor for q:websocket-close statements.
|
|
160
|
+
|
|
161
|
+
Closes an existing WebSocket connection.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def handles(self) -> List[Type]:
|
|
166
|
+
return [WebSocketCloseNode]
|
|
167
|
+
|
|
168
|
+
def execute(self, node: WebSocketCloseNode, exec_context) -> Any:
|
|
169
|
+
"""
|
|
170
|
+
Execute WebSocket connection close.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
node: WebSocketCloseNode with close configuration
|
|
174
|
+
exec_context: Execution context
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Close result dict
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
context = exec_context.get_all_variables()
|
|
181
|
+
|
|
182
|
+
# Resolve reason if provided
|
|
183
|
+
reason = ""
|
|
184
|
+
if node.reason:
|
|
185
|
+
reason = self.apply_databinding(node.reason, context)
|
|
186
|
+
|
|
187
|
+
# Close connection
|
|
188
|
+
self.services.websocket.close_connection(
|
|
189
|
+
connection_name=node.connection,
|
|
190
|
+
code=node.code,
|
|
191
|
+
reason=reason,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return {'connection': node.connection, 'closed': True, 'code': node.code}
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
raise ExecutorError(f"WebSocket close error: {e}")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scripting Executors
|
|
3
|
+
|
|
4
|
+
Executors for Python scripting: python, pyimport, pyclass.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .python_executor import PythonExecutor
|
|
8
|
+
from .pyimport_executor import PyImportExecutor
|
|
9
|
+
from .pyclass_executor import PyClassExecutor
|
|
10
|
+
|
|
11
|
+
__all__ = ['PythonExecutor', 'PyImportExecutor', 'PyClassExecutor']
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyClass Executor - Execute q:class statements
|
|
3
|
+
|
|
4
|
+
Handles inline Python class definitions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
from quantum.runtime.executors.scripting.python_executor import (
|
|
10
|
+
normalise_python_source,
|
|
11
|
+
)
|
|
12
|
+
from quantum.core.ast_nodes import PyClassNode
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PyClassExecutor(BaseExecutor):
|
|
16
|
+
"""
|
|
17
|
+
Executor for q:class statements.
|
|
18
|
+
|
|
19
|
+
Supports:
|
|
20
|
+
- Inline class definition
|
|
21
|
+
- Base class inheritance
|
|
22
|
+
- Decorator application
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def handles(self) -> List[Type]:
|
|
27
|
+
return [PyClassNode]
|
|
28
|
+
|
|
29
|
+
def execute(self, node: PyClassNode, exec_context) -> Any:
|
|
30
|
+
"""
|
|
31
|
+
Execute Python class definition.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
node: PyClassNode with class configuration
|
|
35
|
+
exec_context: Execution context
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Defined class
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
self.require_python_scripting('q:pyclass')
|
|
42
|
+
|
|
43
|
+
context = exec_context.get_all_variables()
|
|
44
|
+
|
|
45
|
+
# Build namespace with existing variables
|
|
46
|
+
namespace = dict(context)
|
|
47
|
+
|
|
48
|
+
# Resolve base classes
|
|
49
|
+
bases = []
|
|
50
|
+
for base_name in node.bases:
|
|
51
|
+
if base_name in namespace:
|
|
52
|
+
bases.append(namespace[base_name])
|
|
53
|
+
else:
|
|
54
|
+
# Try to import/eval the base class
|
|
55
|
+
try:
|
|
56
|
+
base = eval(base_name, namespace)
|
|
57
|
+
bases.append(base)
|
|
58
|
+
except:
|
|
59
|
+
raise ExecutorError(f"Base class '{base_name}' not found")
|
|
60
|
+
|
|
61
|
+
if not bases:
|
|
62
|
+
bases = [object]
|
|
63
|
+
|
|
64
|
+
# Build class body. The source is dedented for the same reason
|
|
65
|
+
# q:python's is: markup-indented code raised "unexpected indent"
|
|
66
|
+
# on line 2, which is every class body written inside XML.
|
|
67
|
+
class_body = {}
|
|
68
|
+
exec(normalise_python_source(node.code), namespace, class_body)
|
|
69
|
+
|
|
70
|
+
# Create the class
|
|
71
|
+
cls = type(node.name, tuple(bases), class_body)
|
|
72
|
+
|
|
73
|
+
# Apply decorators (in reverse order)
|
|
74
|
+
for decorator_name in reversed(node.decorators):
|
|
75
|
+
if decorator_name in namespace:
|
|
76
|
+
decorator = namespace[decorator_name]
|
|
77
|
+
else:
|
|
78
|
+
try:
|
|
79
|
+
decorator = eval(decorator_name, namespace)
|
|
80
|
+
except:
|
|
81
|
+
raise ExecutorError(f"Decorator '{decorator_name}' not found")
|
|
82
|
+
cls = decorator(cls)
|
|
83
|
+
|
|
84
|
+
# Store class in context
|
|
85
|
+
exec_context.set_variable(node.name, cls, scope="component")
|
|
86
|
+
|
|
87
|
+
return cls
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
raise ExecutorError(f"PyClass execution error: {e}")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyImport Executor - Execute q:pyimport statements
|
|
3
|
+
|
|
4
|
+
Handles Python module imports.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
from quantum.core.ast_nodes import PyImportNode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PyImportExecutor(BaseExecutor):
|
|
13
|
+
"""
|
|
14
|
+
Executor for q:pyimport statements.
|
|
15
|
+
|
|
16
|
+
Supports:
|
|
17
|
+
- Module import with alias
|
|
18
|
+
- Specific name imports (from X import Y)
|
|
19
|
+
- Star imports (from X import *)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def handles(self) -> List[Type]:
|
|
24
|
+
return [PyImportNode]
|
|
25
|
+
|
|
26
|
+
def execute(self, node: PyImportNode, exec_context) -> Any:
|
|
27
|
+
"""
|
|
28
|
+
Execute Python import.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
node: PyImportNode with import configuration
|
|
32
|
+
exec_context: Execution context
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Imported module or names
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
self.require_python_scripting('q:pyimport')
|
|
39
|
+
|
|
40
|
+
import importlib
|
|
41
|
+
|
|
42
|
+
# Import the module
|
|
43
|
+
module = importlib.import_module(node.module)
|
|
44
|
+
|
|
45
|
+
# Handle different import styles
|
|
46
|
+
if node.alias:
|
|
47
|
+
# import X as Y
|
|
48
|
+
exec_context.set_variable(node.alias, module, scope="component")
|
|
49
|
+
return module
|
|
50
|
+
|
|
51
|
+
elif node.names:
|
|
52
|
+
# from X import Y, Z
|
|
53
|
+
imported = {}
|
|
54
|
+
for name in node.names:
|
|
55
|
+
name = name.strip()
|
|
56
|
+
if name == '*':
|
|
57
|
+
# Import all public names
|
|
58
|
+
for attr_name in dir(module):
|
|
59
|
+
if not attr_name.startswith('_'):
|
|
60
|
+
attr = getattr(module, attr_name)
|
|
61
|
+
exec_context.set_variable(attr_name, attr, scope="component")
|
|
62
|
+
imported[attr_name] = attr
|
|
63
|
+
else:
|
|
64
|
+
attr = getattr(module, name)
|
|
65
|
+
exec_context.set_variable(name, attr, scope="component")
|
|
66
|
+
imported[name] = attr
|
|
67
|
+
return imported
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
# import X
|
|
71
|
+
# Use last part of module name
|
|
72
|
+
module_name = node.module.split('.')[-1]
|
|
73
|
+
exec_context.set_variable(module_name, module, scope="component")
|
|
74
|
+
return module
|
|
75
|
+
|
|
76
|
+
except ImportError as e:
|
|
77
|
+
raise ExecutorError(f"Import error: Cannot import '{node.module}': {e}")
|
|
78
|
+
except AttributeError as e:
|
|
79
|
+
raise ExecutorError(f"Import error: Module '{node.module}' has no attribute: {e}")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise ExecutorError(f"PyImport execution error: {e}")
|