quantum-framework 0.18.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.
Files changed (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,190 @@
1
+ """
2
+ Job Executor - Execute q:job statements
3
+
4
+ Handles job queue 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 JobNode
10
+
11
+
12
+ class JobExecutor(BaseExecutor):
13
+ """
14
+ Executor for q:job statements.
15
+
16
+ Supports:
17
+ - Job definition
18
+ - Job dispatch (enqueue)
19
+ - Batch job dispatch
20
+ - Priority and delay
21
+ - Retry with backoff
22
+ """
23
+
24
+ @property
25
+ def handles(self) -> List[Type]:
26
+ return [JobNode]
27
+
28
+ def execute(self, node: JobNode, exec_context) -> Any:
29
+ """
30
+ Execute job management.
31
+
32
+ Args:
33
+ node: JobNode with job configuration
34
+ exec_context: Execution context
35
+
36
+ Returns:
37
+ Job operation result dict
38
+ """
39
+ try:
40
+ context = exec_context.get_all_variables()
41
+
42
+ # Execute based on action
43
+ if node.action == 'define':
44
+ result = self._execute_define(node, context, exec_context)
45
+ elif node.action == 'dispatch':
46
+ result = self._execute_dispatch(node, context, exec_context)
47
+ elif node.action == 'batch':
48
+ result = self._execute_batch(node, context, exec_context)
49
+ else:
50
+ raise ExecutorError(f"Unknown job action: {node.action}")
51
+
52
+ # Store result with both naming conventions for compatibility
53
+ exec_context.set_variable(f"dispatched_job_{node.name}", result, scope="component")
54
+ exec_context.set_variable(f"job_{node.name}", result, scope="component")
55
+
56
+ # Also store in runtime context if available
57
+ if hasattr(self._runtime, 'context'):
58
+ self._runtime.context[f"dispatched_job_{node.name}"] = result
59
+ self._runtime.context[f"job_{node.name}"] = result
60
+
61
+ return result
62
+
63
+ except Exception as e:
64
+ raise ExecutorError(f"Job execution error: {e}")
65
+
66
+ def _execute_define(self, node: JobNode, context: Dict[str, Any], exec_context) -> Dict:
67
+ """Define a job (register handler)."""
68
+ name = node.name
69
+ queue = node.queue
70
+
71
+ # Get job_executor from runtime (same as legacy code)
72
+ job_executor = self._runtime.job_executor
73
+
74
+ # Create job handler that executes the job body
75
+ def job_handler(params: Dict[str, Any]):
76
+ from quantum.runtime.execution_context import ExecutionContext as EC
77
+ child_context = EC()
78
+ for param_name, param_value in params.items():
79
+ child_context.set_variable(param_name, param_value, scope="component")
80
+
81
+ # Execute job body statements
82
+ for statement in node.body:
83
+ self._runtime._execute_statement(statement, child_context)
84
+
85
+ # Register handler with job queue
86
+ job_executor.job_queue.register_handler(name, job_handler)
87
+
88
+ # Start worker for the queue if not already running
89
+ job_executor.job_queue.start_worker(queue)
90
+
91
+ result = {
92
+ 'name': name,
93
+ 'action': 'define',
94
+ 'queue': queue,
95
+ 'success': True
96
+ }
97
+
98
+ return result
99
+
100
+ def _execute_dispatch(self, node: JobNode, context: Dict[str, Any], exec_context) -> Dict:
101
+ """Dispatch a job to the queue."""
102
+ name = node.name
103
+
104
+ # Get job_executor from runtime (same as legacy code)
105
+ job_executor = self._runtime.job_executor
106
+
107
+ # Build params from job params
108
+ params = {}
109
+ for param in node.params:
110
+ param_value = getattr(param, 'default', None)
111
+ if param.name in context:
112
+ param_value = context[param.name]
113
+ # If param has value attribute, resolve it
114
+ if hasattr(param, 'value') and param.value:
115
+ param_value = self.apply_databinding(param.value, context)
116
+ params[param.name] = param_value
117
+
118
+ # Dispatch job to queue
119
+ job_id = job_executor.job_queue.dispatch(
120
+ name=name,
121
+ queue=node.queue,
122
+ params=params,
123
+ delay=node.delay,
124
+ priority=node.priority,
125
+ attempts=node.attempts,
126
+ backoff=node.backoff
127
+ )
128
+
129
+ result = {
130
+ 'job_id': job_id,
131
+ 'name': name,
132
+ 'action': 'dispatch',
133
+ 'queue': node.queue,
134
+ 'status': 'queued',
135
+ 'success': True
136
+ }
137
+
138
+ return result
139
+
140
+ def _execute_batch(self, node: JobNode, context: Dict[str, Any], exec_context) -> Dict:
141
+ """Dispatch multiple jobs as a batch."""
142
+ name = node.name
143
+
144
+ # Get job_executor from runtime (same as legacy code)
145
+ job_executor = self._runtime.job_executor
146
+
147
+ # Get tasks from node
148
+ tasks = node.tasks if node.tasks else []
149
+
150
+ # Resolve tasks
151
+ resolved_tasks = []
152
+ for task in tasks:
153
+ resolved_task = {}
154
+ for key, value in task.items():
155
+ if isinstance(value, str):
156
+ resolved_task[key] = self.apply_databinding(value, context)
157
+ else:
158
+ resolved_task[key] = value
159
+ resolved_tasks.append(resolved_task)
160
+
161
+ # Dispatch batch
162
+ job_ids = []
163
+ for task in resolved_tasks:
164
+ job_id = job_executor.job_queue.dispatch(
165
+ name=name,
166
+ queue=node.queue,
167
+ params=task,
168
+ priority=node.priority
169
+ )
170
+ job_ids.append(job_id)
171
+
172
+ result = {
173
+ 'job_ids': job_ids,
174
+ 'name': name,
175
+ 'action': 'batch',
176
+ 'queue': node.queue,
177
+ 'count': len(job_ids),
178
+ 'success': True
179
+ }
180
+
181
+ return result
182
+
183
+ def _param_to_dict(self, param) -> Dict:
184
+ """Convert param node to dict."""
185
+ return {
186
+ 'name': param.name,
187
+ 'type': getattr(param, 'type', 'string'),
188
+ 'required': getattr(param, 'required', False),
189
+ 'default': getattr(param, 'default', None)
190
+ }
@@ -0,0 +1,132 @@
1
+ """
2
+ Schedule Executor - Execute q:schedule statements
3
+
4
+ Handles scheduled task execution (cron-like).
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, List, Dict, Type
9
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
10
+ from quantum.core.ast_nodes import ScheduleNode
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def schedule_info_to_dict(info) -> Dict[str, Any]:
16
+ """Convert a ScheduleInfo dataclass to a template-friendly dict."""
17
+ def _iso(value):
18
+ return value.isoformat() if hasattr(value, 'isoformat') else value
19
+
20
+ return {
21
+ 'name': info.name,
22
+ 'triggerType': info.trigger_type,
23
+ 'triggerInfo': info.trigger_info,
24
+ 'nextRun': _iso(info.next_run),
25
+ 'enabled': info.enabled,
26
+ 'runCount': info.run_count,
27
+ 'lastRun': _iso(info.last_run),
28
+ 'lastError': info.last_error,
29
+ }
30
+
31
+
32
+ class ScheduleExecutor(BaseExecutor):
33
+ """
34
+ Executor for q:schedule statements.
35
+
36
+ Supports:
37
+ - Interval-based scheduling (30s, 5m, 1h, 1d)
38
+ - Cron expression scheduling
39
+ - One-time scheduled execution
40
+ - Timezone support
41
+ - Retry configuration
42
+ """
43
+
44
+ @property
45
+ def handles(self) -> List[Type]:
46
+ return [ScheduleNode]
47
+
48
+ def execute(self, node: ScheduleNode, exec_context) -> Any:
49
+ """
50
+ Execute schedule management.
51
+
52
+ Args:
53
+ node: ScheduleNode with schedule configuration
54
+ exec_context: Execution context
55
+
56
+ Returns:
57
+ Schedule operation result dict
58
+ """
59
+ try:
60
+ context = exec_context.get_all_variables()
61
+
62
+ # Execute based on action
63
+ if node.action == 'run':
64
+ result = self._execute_run(node, context, exec_context)
65
+ elif node.action == 'pause':
66
+ result = self._execute_pause(node.name)
67
+ elif node.action == 'resume':
68
+ result = self._execute_resume(node.name)
69
+ elif node.action == 'delete':
70
+ result = self._execute_delete(node.name)
71
+ else:
72
+ raise ExecutorError(f"Unknown schedule action: {node.action}")
73
+
74
+ # Store result
75
+ exec_context.set_variable(f"{node.name}_schedule", result, scope="component")
76
+
77
+ return result
78
+
79
+ except ExecutorError:
80
+ raise
81
+ except Exception as e:
82
+ raise ExecutorError(f"Schedule execution error: {e}")
83
+
84
+ @property
85
+ def _schedule_service(self):
86
+ """The real scheduling service.
87
+
88
+ There is no services.scheduler — schedules live on the JobExecutor
89
+ facade as services.job_executor.schedule (a ScheduleService).
90
+ """
91
+ return self.services.job_executor.schedule
92
+
93
+ def _execute_run(self, node: ScheduleNode, context: Dict[str, Any], exec_context) -> Dict:
94
+ """Create/update a scheduled task."""
95
+ interval = self.apply_databinding(node.interval, context) if node.interval else None
96
+ cron = self.apply_databinding(node.cron, context) if node.cron else None
97
+ at = self.apply_databinding(node.at, context) if node.at else None
98
+
99
+ body = node.body
100
+
101
+ def run_scheduled_body():
102
+ """Runs on the scheduler thread — must not raise into APScheduler."""
103
+ try:
104
+ return self.run_body_in_child_context(body)
105
+ except Exception as exc: # noqa: BLE001 - reported via ScheduleInfo.last_error
106
+ logger.error(f"Scheduled task '{node.name}' failed: {exc}")
107
+ raise
108
+
109
+ info = self._schedule_service.add_schedule(
110
+ name=node.name,
111
+ callback=run_scheduled_body,
112
+ interval=interval,
113
+ cron=cron,
114
+ at=at,
115
+ timezone=node.timezone or 'UTC',
116
+ enabled=node.enabled,
117
+ overlap=node.overlap,
118
+ )
119
+
120
+ return schedule_info_to_dict(info)
121
+
122
+ def _execute_pause(self, name: str) -> Dict:
123
+ """Pause a scheduled task."""
124
+ return {'name': name, 'action': 'pause', 'success': self._schedule_service.pause_schedule(name)}
125
+
126
+ def _execute_resume(self, name: str) -> Dict:
127
+ """Resume a paused scheduled task."""
128
+ return {'name': name, 'action': 'resume', 'success': self._schedule_service.resume_schedule(name)}
129
+
130
+ def _execute_delete(self, name: str) -> Dict:
131
+ """Delete a scheduled task."""
132
+ return {'name': name, 'action': 'delete', 'success': self._schedule_service.remove_schedule(name)}
@@ -0,0 +1,127 @@
1
+ """
2
+ Thread Executor - Execute q:thread statements
3
+
4
+ Handles async thread execution.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, List, Dict, Type
9
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
10
+ from quantum.core.ast_nodes import ThreadNode
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def thread_info_to_dict(info) -> Dict[str, Any]:
16
+ """Convert a ThreadInfo dataclass to a template-friendly dict."""
17
+ def _iso(value):
18
+ return value.isoformat() if hasattr(value, 'isoformat') else value
19
+
20
+ return {
21
+ 'name': info.name,
22
+ 'priority': info.priority,
23
+ 'startedAt': _iso(info.started_at),
24
+ 'status': info.status,
25
+ 'timeout': info.timeout,
26
+ 'result': info.result,
27
+ 'error': info.error,
28
+ }
29
+
30
+
31
+ class ThreadExecutor(BaseExecutor):
32
+ """
33
+ Executor for q:thread statements.
34
+
35
+ Supports:
36
+ - Async thread execution
37
+ - Thread join (wait for completion)
38
+ - Thread termination
39
+ - Priority levels
40
+ - Timeout and callbacks
41
+ """
42
+
43
+ @property
44
+ def handles(self) -> List[Type]:
45
+ return [ThreadNode]
46
+
47
+ def execute(self, node: ThreadNode, exec_context) -> Any:
48
+ """
49
+ Execute thread management.
50
+
51
+ Args:
52
+ node: ThreadNode with thread configuration
53
+ exec_context: Execution context
54
+
55
+ Returns:
56
+ Thread operation result dict
57
+ """
58
+ try:
59
+ context = exec_context.get_all_variables()
60
+
61
+ # Execute based on action
62
+ if node.action == 'run':
63
+ result = self._execute_run(node, context, exec_context)
64
+ elif node.action == 'join':
65
+ result = self._execute_join(node.name)
66
+ elif node.action == 'terminate':
67
+ result = self._execute_terminate(node.name)
68
+ else:
69
+ raise ExecutorError(f"Unknown thread action: {node.action}")
70
+
71
+ # Store result
72
+ exec_context.set_variable(f"{node.name}_thread", result, scope="component")
73
+
74
+ return result
75
+
76
+ except ExecutorError:
77
+ raise
78
+ except Exception as e:
79
+ raise ExecutorError(f"Thread execution error: {e}")
80
+
81
+ @property
82
+ def _thread_service(self):
83
+ """The real threading service.
84
+
85
+ There is no services.threading — threads live on the JobExecutor
86
+ facade as services.job_executor.thread (a ThreadService).
87
+ """
88
+ return self.services.job_executor.thread
89
+
90
+ def _execute_run(self, node: ThreadNode, context: Dict[str, Any], exec_context) -> Dict:
91
+ """Start a new thread."""
92
+ body = node.body
93
+ # Snapshot the caller's variables: the thread runs after this
94
+ # component's own execution has moved on.
95
+ snapshot = dict(context)
96
+
97
+ def run_thread_body():
98
+ try:
99
+ return self.run_body_in_child_context(body, snapshot)
100
+ except Exception as exc: # noqa: BLE001 - surfaced via ThreadInfo.error
101
+ logger.error(f"Thread '{node.name}' failed: {exc}")
102
+ raise
103
+
104
+ info = self._thread_service.run_thread(
105
+ name=node.name,
106
+ callback=run_thread_body,
107
+ priority=node.priority or 'normal',
108
+ timeout=node.timeout,
109
+ )
110
+
111
+ return thread_info_to_dict(info)
112
+
113
+ def _execute_join(self, name: str) -> Dict:
114
+ """Wait for thread to complete."""
115
+ result = self._thread_service.join_thread(name)
116
+ info = self._thread_service.get_thread(name)
117
+ joined = thread_info_to_dict(info) if info else {'name': name}
118
+ joined['result'] = result
119
+ return joined
120
+
121
+ def _execute_terminate(self, name: str) -> Dict:
122
+ """Terminate a running thread."""
123
+ return {
124
+ 'name': name,
125
+ 'action': 'terminate',
126
+ 'success': self._thread_service.terminate_thread(name),
127
+ }
@@ -0,0 +1,17 @@
1
+ """
2
+ Messaging Executors
3
+
4
+ Executors for messaging operations: websocket, message, queue, subscribe.
5
+ """
6
+
7
+ from .websocket_executor import WebSocketExecutor, WebSocketSendExecutor, WebSocketCloseExecutor
8
+ from .message_executor import MessageExecutor, SubscribeExecutor
9
+ from .queue_executor import QueueExecutor
10
+
11
+ __all__ = [
12
+ 'WebSocketExecutor', 'WebSocketSendExecutor', 'WebSocketCloseExecutor',
13
+ 'MessageExecutor', 'SubscribeExecutor', 'QueueExecutor'
14
+ ]
15
+ from quantum.runtime.executors.messaging.message_ack_executor import (
16
+ MessageAckExecutor, MessageNackExecutor,
17
+ )
@@ -0,0 +1,51 @@
1
+ """
2
+ Executors for q:messageAck and q:messageNack.
3
+
4
+ These nodes parsed and produced an AST node with NO executor registered, so
5
+ inside a `<q:subscribe ack="manual">` handler they did nothing. With manual
6
+ acknowledgement the broker waits for an ack that could never arrive, so the
7
+ message was redelivered forever — the audit's most-reported critical, and the
8
+ worst kind of silent failure: an infinite loop of side effects.
9
+
10
+ MessageQueueService already had ack() and nack(); nothing called them.
11
+ """
12
+
13
+ from typing import Any, List, Type
14
+
15
+ from quantum.core.ast_nodes import MessageAckNode, MessageNackNode
16
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
17
+
18
+
19
+ class MessageAckExecutor(BaseExecutor):
20
+ """Executor for q:messageAck — acknowledge the message being handled."""
21
+
22
+ @property
23
+ def handles(self) -> List[Type]:
24
+ return [MessageAckNode]
25
+
26
+ def execute(self, node: MessageAckNode, exec_context) -> Any:
27
+ try:
28
+ self.services.message_queue.ack()
29
+ return {'acknowledged': True}
30
+ except Exception as e:
31
+ raise ExecutorError(f"q:messageAck failed: {e}")
32
+
33
+
34
+ class MessageNackExecutor(BaseExecutor):
35
+ """Executor for q:messageNack — reject the message being handled."""
36
+
37
+ @property
38
+ def handles(self) -> List[Type]:
39
+ return [MessageNackNode]
40
+
41
+ def execute(self, node: MessageNackNode, exec_context) -> Any:
42
+ # requeue defaults to True: rejecting without requeueing silently
43
+ # drops the message, which should be the explicit choice.
44
+ requeue = getattr(node, 'requeue', True)
45
+ if isinstance(requeue, str):
46
+ requeue = requeue.strip().lower() not in ('false', '0', 'no', 'off')
47
+ try:
48
+ self.services.message_queue.nack(requeue=bool(requeue))
49
+ return {'acknowledged': False, 'requeued': bool(requeue)}
50
+ except Exception as e:
51
+ raise ExecutorError(f"q:messageNack failed: {e}")
@@ -0,0 +1,174 @@
1
+ """
2
+ Message Executor - Execute q:message statements
3
+
4
+ Handles message queue publish/send/request 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 MessageNode, SubscribeNode
10
+
11
+
12
+ class MessageExecutor(BaseExecutor):
13
+ """
14
+ Executor for q:message statements.
15
+
16
+ Supports:
17
+ - Pub/Sub (publish to topic)
18
+ - Direct queue send
19
+ - Request/reply pattern
20
+ - Message headers
21
+ """
22
+
23
+ @property
24
+ def handles(self) -> List[Type]:
25
+ return [MessageNode]
26
+
27
+ def execute(self, node: MessageNode, exec_context) -> Any:
28
+ """
29
+ Execute message publish/send.
30
+
31
+ Args:
32
+ node: MessageNode with message configuration
33
+ exec_context: Execution context
34
+
35
+ Returns:
36
+ Message operation result dict
37
+ """
38
+ try:
39
+ context = exec_context.get_all_variables()
40
+
41
+ # Resolve body
42
+ body = None
43
+ if node.body:
44
+ body = self.apply_databinding(node.body, context)
45
+
46
+ # Resolve headers
47
+ headers = {}
48
+ for header in node.headers:
49
+ header_name = header.name
50
+ header_value = self.apply_databinding(header.value, context)
51
+ headers[header_name] = header_value
52
+
53
+ # Resolve target
54
+ topic = None
55
+ queue = None
56
+ if node.topic:
57
+ topic = self.apply_databinding(node.topic, context)
58
+ if node.queue:
59
+ queue = self.apply_databinding(node.queue, context)
60
+
61
+ # Execute based on message type
62
+ if node.type == 'publish':
63
+ result = self.services.message_queue.publish(
64
+ topic=topic,
65
+ body=body,
66
+ headers=headers
67
+ )
68
+ elif node.type == 'send':
69
+ result = self.services.message_queue.send(
70
+ queue=queue,
71
+ body=body,
72
+ headers=headers
73
+ )
74
+ elif node.type == 'request':
75
+ timeout = int(node.timeout) if node.timeout else 30000
76
+ result = self.services.message_queue.request(
77
+ queue=queue,
78
+ body=body,
79
+ headers=headers,
80
+ timeout=timeout
81
+ )
82
+ else:
83
+ raise ExecutorError(f"Unknown message type: {node.type}")
84
+
85
+ # Store result
86
+ if node.name:
87
+ exec_context.set_variable(node.name, result, scope="component")
88
+
89
+ return result
90
+
91
+ except Exception as e:
92
+ if node.name:
93
+ exec_context.set_variable(node.name, {
94
+ 'success': False,
95
+ 'error': str(e)
96
+ }, scope="component")
97
+ raise ExecutorError(f"Message operation error: {e}")
98
+
99
+
100
+ class SubscribeExecutor(BaseExecutor):
101
+ """
102
+ Executor for q:subscribe statements.
103
+
104
+ Subscribes to topics or queues and handles incoming messages.
105
+ """
106
+
107
+ @property
108
+ def handles(self) -> List[Type]:
109
+ return [SubscribeNode]
110
+
111
+ def execute(self, node: SubscribeNode, exec_context) -> Any:
112
+ """
113
+ Execute subscription setup.
114
+
115
+ Args:
116
+ node: SubscribeNode with subscription configuration
117
+ exec_context: Execution context
118
+
119
+ Returns:
120
+ Subscription info dict
121
+ """
122
+ try:
123
+ context = exec_context.get_all_variables()
124
+
125
+ # Resolve topics/queue
126
+ topics = []
127
+ if node.topic:
128
+ topics = [self.apply_databinding(node.topic, context)]
129
+ elif node.topics:
130
+ topics_str = self.apply_databinding(node.topics, context)
131
+ topics = [t.strip() for t in topics_str.split(',')]
132
+
133
+ queue = None
134
+ if node.queue:
135
+ queue = self.apply_databinding(node.queue, context)
136
+
137
+ # MessageQueueService.subscribe() takes a single `handler`
138
+ # callable, not a handlers config dict, and `topics` as a
139
+ # comma-separated string.
140
+ on_message_body = node.on_message
141
+ on_error_body = node.on_error
142
+
143
+ def handle_message(message):
144
+ payload = message if isinstance(message, dict) else {'message': message}
145
+ try:
146
+ return self.run_body_in_child_context(on_message_body, payload)
147
+ except Exception as exc: # noqa: BLE001 - routed to on_error body
148
+ if on_error_body:
149
+ return self.run_body_in_child_context(
150
+ on_error_body, {**payload, 'error': str(exc)}
151
+ )
152
+ raise
153
+
154
+ # Create subscription
155
+ result = self.services.message_queue.subscribe(
156
+ name=node.name,
157
+ topics=','.join(topics) if topics else None,
158
+ queue=queue,
159
+ handler=handle_message,
160
+ ack=node.ack,
161
+ prefetch=node.prefetch,
162
+ )
163
+
164
+ # Store subscription reference
165
+ exec_context.set_variable(node.name, result, scope="component")
166
+
167
+ return result
168
+
169
+ except Exception as e:
170
+ exec_context.set_variable(node.name, {
171
+ 'active': False,
172
+ 'error': str(e)
173
+ }, scope="component")
174
+ raise ExecutorError(f"Subscription error: {e}")