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,86 @@
1
+ """
2
+ Transaction Parser - Parse q:transaction statements
3
+
4
+ Handles atomic database transactions.
5
+ """
6
+
7
+ from typing import List
8
+ from xml.etree import ElementTree as ET
9
+ from quantum.core.parsers.base import BaseTagParser, ParserError
10
+ from quantum.core.ast_nodes import TransactionNode
11
+
12
+
13
+ class TransactionParser(BaseTagParser):
14
+ """
15
+ Parser for q:transaction statements.
16
+
17
+ Supports:
18
+ - Atomic transaction blocks
19
+ - Automatic rollback on error
20
+ - Nested statement execution
21
+ """
22
+
23
+ @property
24
+ def tag_names(self) -> List[str]:
25
+ return ['transaction']
26
+
27
+ def parse(self, element: ET.Element) -> TransactionNode:
28
+ """
29
+ Parse q:transaction statement.
30
+
31
+ Args:
32
+ element: XML element for q:transaction
33
+
34
+ Returns:
35
+ TransactionNode AST node
36
+ """
37
+ # datasource= is optional. Requiring it was stricter than the runtime
38
+ # (DatabaseService.begin_transaction defaults it) and stricter than
39
+ # everything written against it: all four q:transaction blocks in this
40
+ # repository omit it, and three of them are tests that had been
41
+ # failing on this line — "Transaction requires 'datasource'
42
+ # attribute" — rather than on anything about transactions.
43
+ datasource = self.get_attr(element, 'datasource')
44
+
45
+ transaction_node = TransactionNode(datasource or '')
46
+
47
+ # Isolation level. Everything written in this repository and in the
48
+ # docs spells it isolationLevel; the parser read `isolation`, so the
49
+ # level was silently dropped every time. Both spellings work.
50
+ transaction_node.isolation = (
51
+ self.get_attr(element, 'isolationLevel')
52
+ or self.get_attr(element, 'isolation')
53
+ )
54
+
55
+ # DB-4: a q:query inside a transaction with datasource= uses it. Every
56
+ # query had to repeat it, or parsing failed with "Query requires
57
+ # either 'datasource' or 'source' attribute" — the guide's own
58
+ # transaction example did not parse.
59
+ for child in element:
60
+ if datasource and self.get_element_name(child) == 'query' \
61
+ and not child.get('datasource') and not child.get('source'):
62
+ child.set('datasource', datasource)
63
+
64
+ # Parse child statements
65
+ for child in element:
66
+ statement = self.parse_statement(child)
67
+ if statement:
68
+ transaction_node.add_statement(statement)
69
+
70
+ if not datasource:
71
+ # Take it from the queries being wrapped, which is what the author
72
+ # means by wrapping them. Only if none of them names one does this
73
+ # fall back to "default".
74
+ transaction_node.datasource = (
75
+ self._datasource_of_children(transaction_node) or 'default'
76
+ )
77
+
78
+ return transaction_node
79
+
80
+ def _datasource_of_children(self, transaction_node) -> str:
81
+ """The datasource of the first child query that names one."""
82
+ for statement in getattr(transaction_node, 'statements', []) or []:
83
+ name = getattr(statement, 'datasource', None)
84
+ if name:
85
+ return name
86
+ return ''
@@ -0,0 +1,9 @@
1
+ """
2
+ Events Parsers
3
+
4
+ Parsers for event-related tags: dispatchEvent
5
+ """
6
+
7
+ from .dispatch_event_parser import DispatchEventParser
8
+
9
+ __all__ = ['DispatchEventParser']
@@ -0,0 +1,44 @@
1
+ """
2
+ DispatchEvent Parser - Parse q:dispatchEvent statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import DispatchEventNode
9
+
10
+
11
+ class DispatchEventParser(BaseTagParser):
12
+ """
13
+ Parser for q:dispatchEvent statements.
14
+
15
+ Examples:
16
+ <q:dispatchEvent event="userCreated" data="{userData}" />
17
+ <q:dispatchEvent event="orderPlaced" queue="orders" priority="high" />
18
+ """
19
+
20
+ @property
21
+ def tag_names(self) -> List[str]:
22
+ return ['dispatchEvent']
23
+
24
+ def parse(self, element: ET.Element) -> DispatchEventNode:
25
+ """Parse q:dispatchEvent element"""
26
+ event = element.get('event')
27
+
28
+ if not event:
29
+ from quantum.core.parser import QuantumParseError
30
+ raise QuantumParseError("dispatchEvent requires 'event' attribute")
31
+
32
+ dispatch_node = DispatchEventNode(event)
33
+
34
+ # Optional attributes
35
+ dispatch_node.data = element.get('data')
36
+ dispatch_node.queue = element.get('queue')
37
+ dispatch_node.exchange = element.get('exchange')
38
+ dispatch_node.routing_key = element.get('routingKey')
39
+ dispatch_node.priority = element.get('priority', 'normal')
40
+ dispatch_node.delay = element.get('delay')
41
+ dispatch_node.ttl = element.get('ttl')
42
+ dispatch_node.metadata = element.get('metadata')
43
+
44
+ return dispatch_node
@@ -0,0 +1,11 @@
1
+ """
2
+ Forms & Actions Parsers
3
+
4
+ Parsers for form handling tags: action, redirect, flash
5
+ """
6
+
7
+ from .action_parser import ActionParser
8
+ from .redirect_parser import RedirectParser
9
+ from .flash_parser import FlashParser
10
+
11
+ __all__ = ['ActionParser', 'RedirectParser', 'FlashParser']
@@ -0,0 +1,79 @@
1
+ """
2
+ Action Parser - Parse q:action statements for form handling
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import ActionNode
9
+
10
+
11
+ _NEVER_ENFORCED = {
12
+ 'require_auth': 'Protect the page with require_auth / require_role on the '
13
+ 'q:component: that covers its actions too (AUTH-1, AUTH-6).',
14
+ 'csrf': 'There is no CSRF token; the session cookie is SameSite=Lax (AUTH-5).',
15
+ 'rate_limit': 'Nothing limits requests; put a reverse proxy in front for that.',
16
+ }
17
+
18
+
19
+ class ActionParser(BaseTagParser):
20
+ """
21
+ Parser for q:action statements.
22
+
23
+ Examples:
24
+ <q:action name="createUser" method="POST">
25
+ <q:param name="email" type="email" required="true" />
26
+ <q:query datasource="db">...</q:query>
27
+ <q:redirect url="/users" flash="User created!" />
28
+ </q:action>
29
+ """
30
+
31
+ @property
32
+ def tag_names(self) -> List[str]:
33
+ return ['action']
34
+
35
+ def parse(self, element: ET.Element) -> ActionNode:
36
+ """Parse q:action element"""
37
+ name = element.get('name', '')
38
+ method = element.get('method', 'POST')
39
+ action_node = ActionNode(name, method)
40
+
41
+ # AUTH-7: attributes that were parsed and never enforced. The worst
42
+ # is require_auth="true": the action ran for anyone, while the page
43
+ # read as protected. The docs promised csrf= and rate_limit= too.
44
+ for attr, porque in _NEVER_ENFORCED.items():
45
+ if element.get(attr) is not None:
46
+ from quantum.core.parser import QuantumParseError
47
+ raise QuantumParseError(
48
+ f'<q:action name="{name}"> {attr}= is not supported: it was accepted '
49
+ f'and never enforced. {porque}')
50
+
51
+ # UI-10 (M17): the action's params come from a table's schema, read
52
+ # from the database when the action is used; a q:param written wins.
53
+ action_node.table = element.get('table')
54
+ action_node.table_datasource = element.get('datasource')
55
+ colunas = element.get('columns')
56
+ action_node.table_columns = [c.strip() for c in colunas.split(',') if c.strip()] if colunas else None
57
+ if (action_node.table_datasource or colunas) and not action_node.table:
58
+ from quantum.core.parser import QuantumParseError
59
+ raise QuantumParseError(
60
+ f'<q:action name="{name}"> datasource= and columns= describe table=, which is missing')
61
+ if action_node.table and not action_node.table_datasource:
62
+ from quantum.core.parser import QuantumParseError
63
+ raise QuantumParseError(
64
+ f'<q:action name="{name}" table="{action_node.table}"> needs datasource= (where the table is)')
65
+
66
+ # Parse children (params, statements)
67
+ for child in element:
68
+ child_type = self._get_element_name(child)
69
+
70
+ if child_type == 'param':
71
+ param = self.parse_param(child)
72
+ action_node.add_param(param)
73
+ else:
74
+ # Parse other statements (query, set, redirect, etc.)
75
+ statement = self.parse_child(child)
76
+ if statement:
77
+ action_node.add_statement(statement)
78
+
79
+ return action_node
@@ -0,0 +1,34 @@
1
+ """
2
+ Flash Parser - Parse q:flash statements for flash messages
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import FlashNode
9
+
10
+
11
+ class FlashParser(BaseTagParser):
12
+ """
13
+ Parser for q:flash statements.
14
+
15
+ Examples:
16
+ <q:flash type="success" message="User created!" />
17
+ <q:flash type="error" message="{errorMessage}" />
18
+ <q:flash type="warning">Please verify your email</q:flash>
19
+ """
20
+
21
+ @property
22
+ def tag_names(self) -> List[str]:
23
+ return ['flash']
24
+
25
+ def parse(self, element: ET.Element) -> FlashNode:
26
+ """Parse q:flash element"""
27
+ flash_type = element.get('type', 'info')
28
+ message = element.get('message')
29
+
30
+ # If message not in attribute, check text content
31
+ if not message and element.text:
32
+ message = element.text.strip()
33
+
34
+ return FlashNode(message, flash_type)
@@ -0,0 +1,33 @@
1
+ """
2
+ Redirect Parser - Parse q:redirect statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import RedirectNode
9
+
10
+
11
+ class RedirectParser(BaseTagParser):
12
+ """
13
+ Parser for q:redirect statements.
14
+
15
+ Examples:
16
+ <q:redirect url="/thank-you" />
17
+ <q:redirect url="/users/{userId}" />
18
+ <q:redirect url="/products" flash="Product created!" />
19
+ <q:redirect url="/error" status="500" flash="Error occurred" />
20
+ """
21
+
22
+ @property
23
+ def tag_names(self) -> List[str]:
24
+ return ['redirect']
25
+
26
+ def parse(self, element: ET.Element) -> RedirectNode:
27
+ """Parse q:redirect element"""
28
+ # Accept to= as an alias for url= (both appear across examples)
29
+ url = element.get('url') or element.get('to', '')
30
+ flash = element.get('flash')
31
+ status = int(element.get('status', '302'))
32
+
33
+ return RedirectNode(url, flash, status)
@@ -0,0 +1,11 @@
1
+ """
2
+ Functions Parsers
3
+
4
+ Parsers for function-related tags: function, return, param
5
+ """
6
+
7
+ from .function_parser import FunctionParser
8
+ from .return_parser import ReturnParser
9
+ from .param_parser import ParamParser
10
+
11
+ __all__ = ['FunctionParser', 'ReturnParser', 'ParamParser']
@@ -0,0 +1,98 @@
1
+ """
2
+ Function Parser - Parse q:function statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import FunctionNode
9
+
10
+
11
+ _NO_OP = "it was accepted and never did anything, and was removed in Quantum 0.11"
12
+ _NEVER_IMPLEMENTED = {
13
+ 'validate': "parameters are always converted and checked against their rules (FN-1); remove it",
14
+ 'cache': _NO_OP, 'memoize': _NO_OP, 'pure': _NO_OP, 'async': _NO_OP,
15
+ 'retry': _NO_OP, 'timeout': _NO_OP, 'access': _NO_OP,
16
+ 'scope': "a function belongs to its component; scope=\"global\" never made it callable from another page. " + _NO_OP,
17
+ 'endpoint': "functions are not served as REST endpoints; " + _NO_OP,
18
+ 'method': _NO_OP, 'produces': _NO_OP, 'consumes': _NO_OP, 'auth': _NO_OP,
19
+ 'roles': _NO_OP, 'rateLimit': _NO_OP, 'cors': _NO_OP, 'status': _NO_OP,
20
+ }
21
+
22
+
23
+ class FunctionParser(BaseTagParser):
24
+ """
25
+ Parser for q:function statements.
26
+
27
+ Examples:
28
+ <q:function name="calculateTotal" returnType="number">
29
+ <q:param name="items" type="array" />
30
+ <q:set name="total" value="0" />
31
+ <q:loop array="{items}" item="item">
32
+ <q:set name="total" value="{total + item.price}" />
33
+ </q:loop>
34
+ </q:function>
35
+
36
+ <q:function name="getUser" endpoint="/api/users/{id}" method="GET">
37
+ <q:param name="id" type="integer" source="path" />
38
+ <q:query datasource="db">SELECT * FROM users WHERE id = :id</q:query>
39
+ </q:function>
40
+ """
41
+
42
+ @property
43
+ def tag_names(self) -> List[str]:
44
+ return ['function']
45
+
46
+ def parse(self, element: ET.Element) -> FunctionNode:
47
+ """Parse q:function element"""
48
+ name = element.get('name')
49
+
50
+ if not name:
51
+ from quantum.core.parser import QuantumParseError
52
+ raise QuantumParseError("Function requires 'name' attribute")
53
+
54
+ func_node = FunctionNode(name)
55
+
56
+ # FN-2: attributes that were parsed into the AST and never did anything.
57
+ # memoize="true" or endpoint="/api/x" looked like working features —
58
+ # the guide documented them — and silently changed nothing.
59
+ for attr in element.attrib:
60
+ if attr in _NEVER_IMPLEMENTED:
61
+ from quantum.core.parser import QuantumParseError
62
+ raise QuantumParseError(
63
+ f'<q:function name="{name}"> {attr}= is not supported: '
64
+ f'{_NEVER_IMPLEMENTED[attr]}')
65
+
66
+ # Core attributes
67
+ func_node.return_type = element.get('returnType', 'any')
68
+ # FN-4: returnType is checked on every return (see ComponentRuntime).
69
+ from quantum.runtime.param_validation import RETURN_TYPES
70
+ if func_node.return_type.lower() not in RETURN_TYPES:
71
+ from quantum.core.parser import QuantumParseError
72
+ raise QuantumParseError(
73
+ f'<q:function name="{name}"> returnType="{func_node.return_type}" is not a type; '
74
+ f'use one of: {", ".join(sorted(RETURN_TYPES))}')
75
+ func_node.description = element.get('description')
76
+ func_node.hint = element.get('hint')
77
+
78
+ # Parse function params and body
79
+ for child in element:
80
+ child_type = self._get_element_name(child)
81
+
82
+ if child_type == 'param':
83
+ param = self.parse_param(child)
84
+ func_node.add_param(param)
85
+ elif child_type == 'return':
86
+ # ReturnParser is deliberately not in the tag registry (it is
87
+ # an internal parser), so parse_child() returns None for it —
88
+ # which silently dropped every <q:return> from every function
89
+ # body, making functions return nothing. Parse it directly.
90
+ from quantum.core.parsers.functions.return_parser import ReturnParser
91
+ func_node.add_statement(ReturnParser(self._parser).parse(child))
92
+ else:
93
+ # Parse body statements
94
+ statement = self.parse_child(child)
95
+ if statement:
96
+ func_node.add_statement(statement)
97
+
98
+ return func_node
@@ -0,0 +1,73 @@
1
+ """
2
+ Param Parser - Parse q:param statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import QuantumParam
9
+
10
+
11
+ class ParamParser(BaseTagParser):
12
+ """
13
+ Parser for q:param statements.
14
+
15
+ Examples:
16
+ <q:param name="email" type="email" required="true" />
17
+ <q:param name="password" type="string" minlength="8" />
18
+ <q:param name="age" type="integer" min="0" max="150" />
19
+ """
20
+
21
+ @property
22
+ def tag_names(self) -> List[str]:
23
+ return ['param']
24
+
25
+ def parse(self, element: ET.Element) -> QuantumParam:
26
+ """Parse q:param element"""
27
+ param = QuantumParam(
28
+ name=element.get('name', ''),
29
+ type=element.get('type', 'string'),
30
+ required=element.get('required', 'false').lower() == 'true',
31
+ default=element.get('default'),
32
+ description=element.get('description')
33
+ )
34
+ if element.get('validation') is not None:
35
+ # A2: accepted and never read. The rules are validate=, type=,
36
+ # pattern=, min/max, minlength/maxlength and enum=.
37
+ from quantum.core.parser import QuantumParseError
38
+ raise QuantumParseError(
39
+ f'<q:param name="{element.get("name", "")}"> validation= is not supported: it was '
40
+ f'accepted and never did anything. Use type=, pattern=, min/max, '
41
+ f'minlength/maxlength or enum=.')
42
+
43
+ # REST-specific
44
+ param.source = element.get('source', 'auto')
45
+
46
+ # Validation
47
+ param.validate_rule = element.get('validate')
48
+ param.pattern = element.get('pattern')
49
+ param.min = element.get('min')
50
+ param.max = element.get('max')
51
+
52
+ minlength_str = element.get('minlength')
53
+ if minlength_str:
54
+ try:
55
+ param.minlength = int(minlength_str)
56
+ except ValueError:
57
+ pass
58
+
59
+ maxlength_str = element.get('maxlength')
60
+ if maxlength_str:
61
+ try:
62
+ param.maxlength = int(maxlength_str)
63
+ except ValueError:
64
+ pass
65
+
66
+ param.range = element.get('range')
67
+ param.enum = element.get('enum')
68
+
69
+ # File upload
70
+ param.maxsize = element.get('maxsize')
71
+ param.accept = element.get('accept')
72
+
73
+ return param
@@ -0,0 +1,31 @@
1
+ """
2
+ Return Parser - Parse q:return statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import QuantumReturn
9
+
10
+
11
+ class ReturnParser(BaseTagParser):
12
+ """
13
+ Parser for q:return statements.
14
+
15
+ Examples:
16
+ <q:return name="result" type="string" value="{computedValue}" />
17
+ <q:return type="integer" value="42" />
18
+ """
19
+
20
+ @property
21
+ def tag_names(self) -> List[str]:
22
+ return ['return']
23
+
24
+ def parse(self, element: ET.Element) -> QuantumReturn:
25
+ """Parse q:return element"""
26
+ return QuantumReturn(
27
+ name=element.get('name'),
28
+ type=element.get('type', 'string'),
29
+ value=element.get('value', ''),
30
+ description=element.get('description')
31
+ )
@@ -0,0 +1,10 @@
1
+ """
2
+ HTML Parsers
3
+
4
+ Parsers for HTML elements and component calls.
5
+ """
6
+
7
+ from .html_parser import HTMLParser
8
+ from .component_call_parser import ComponentCallParser
9
+
10
+ __all__ = ['HTMLParser', 'ComponentCallParser']
@@ -0,0 +1,130 @@
1
+ """
2
+ Component Call Parser - Parse component invocations
3
+
4
+ Handles parsing of component calls (PascalCase tags).
5
+ """
6
+
7
+ from typing import List, Optional, Dict, Any
8
+ from xml.etree import ElementTree as ET
9
+ from quantum.core.parsers.base import BaseTagParser
10
+ from quantum.core.ast_nodes import ComponentCallNode, TextNode
11
+
12
+
13
+ class ComponentCallParser(BaseTagParser):
14
+ """
15
+ Parser for component call elements.
16
+
17
+ Component calls are identified by PascalCase naming convention:
18
+ - <MyComponent /> -> calls imported component "MyComponent"
19
+ - <UserCard name="John" /> -> calls "UserCard" with props
20
+
21
+ These are similar to React/Vue component usage.
22
+ """
23
+
24
+ @property
25
+ def tag_names(self) -> List[str]:
26
+ """
27
+ Component call parser doesn't register for specific tags.
28
+ Instead, it's called when a PascalCase tag is detected.
29
+ """
30
+ return []
31
+
32
+ def parse(self, element: ET.Element) -> ComponentCallNode:
33
+ """
34
+ Parse component call element.
35
+
36
+ Args:
37
+ element: XML element representing component call
38
+
39
+ Returns:
40
+ ComponentCallNode AST node
41
+ """
42
+ component_name = self._get_local_name(element)
43
+
44
+ # Get props from attributes
45
+ props = self._parse_props(element)
46
+
47
+ # Create component call node
48
+ call_node = ComponentCallNode(component_name, props)
49
+
50
+ # Parse children (slot content)
51
+ call_node.children = self._parse_children(element)
52
+
53
+ return call_node
54
+
55
+ def _get_local_name(self, element: ET.Element) -> str:
56
+ """Extract local name from element tag."""
57
+ tag = element.tag
58
+ if '}' in tag:
59
+ return tag.split('}')[1]
60
+ if ':' in tag:
61
+ return tag.split(':')[1]
62
+ return tag
63
+
64
+ def _parse_props(self, element: ET.Element) -> Dict[str, Any]:
65
+ """
66
+ Parse element attributes as component props.
67
+
68
+ Handles:
69
+ - Simple string values: name="John"
70
+ - Databinding expressions: user="{currentUser}"
71
+ - Boolean shorthand: disabled (treated as disabled="true")
72
+ """
73
+ props = {}
74
+
75
+ for key, value in element.attrib.items():
76
+ # Skip namespace declarations
77
+ if key.startswith('xmlns'):
78
+ continue
79
+
80
+ # Handle namespaced attributes
81
+ if '}' in key:
82
+ key = key.split('}')[1]
83
+
84
+ props[key] = value
85
+
86
+ return props
87
+
88
+ def _parse_children(self, element: ET.Element) -> List:
89
+ """Parse child elements as slot content."""
90
+ children = []
91
+
92
+ # Add leading text if present
93
+ if element.text and element.text.strip():
94
+ children.append(TextNode(element.text))
95
+
96
+ # Parse child elements
97
+ for child in element:
98
+ child_node = self.parse_statement(child)
99
+ if child_node:
100
+ children.append(child_node)
101
+
102
+ # Add tail text after child
103
+ if child.tail and child.tail.strip():
104
+ children.append(TextNode(child.tail))
105
+
106
+ return children
107
+
108
+ @staticmethod
109
+ def is_component_call(tag_name: str) -> bool:
110
+ """
111
+ Check if tag name represents a component call.
112
+
113
+ Components use PascalCase naming:
114
+ - MyComponent -> True
115
+ - UserCard -> True
116
+ - div -> False
117
+ - myComponent -> False
118
+ """
119
+ if not tag_name:
120
+ return False
121
+
122
+ # Must start with uppercase letter
123
+ if not tag_name[0].isupper():
124
+ return False
125
+
126
+ # Should not be all uppercase (likely a constant/builtin)
127
+ if tag_name.isupper():
128
+ return False
129
+
130
+ return True