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.
Files changed (262) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/commands/__init__.py +15 -0
  4. quantum/cli/commands/build.py +417 -0
  5. quantum/cli/commands/dev.py +185 -0
  6. quantum/cli/commands/docs.py +329 -0
  7. quantum/cli/commands/lint.py +523 -0
  8. quantum/cli/commands/migrate.py +527 -0
  9. quantum/cli/commands/new.py +622 -0
  10. quantum/cli/commands/serve.py +190 -0
  11. quantum/cli/commands/test.py +193 -0
  12. quantum/cli/deploy.py +810 -0
  13. quantum/cli/hot_reload.py +951 -0
  14. quantum/cli/jobs.py +356 -0
  15. quantum/cli/mq.py +582 -0
  16. quantum/cli/pkg.py +390 -0
  17. quantum/cli/runner.py +547 -0
  18. quantum/cli/server_process.py +159 -0
  19. quantum/cli/utils.py +334 -0
  20. quantum/compiler/__init__.py +30 -0
  21. quantum/compiler/base_generator.py +367 -0
  22. quantum/compiler/cli.py +295 -0
  23. quantum/compiler/expression_transformer.py +444 -0
  24. quantum/compiler/javascript/__init__.py +10 -0
  25. quantum/compiler/javascript/generator.py +659 -0
  26. quantum/compiler/optimizer.py +270 -0
  27. quantum/compiler/python/__init__.py +10 -0
  28. quantum/compiler/python/generator.py +883 -0
  29. quantum/compiler/python/runtime.py +863 -0
  30. quantum/compiler/transpiler.py +330 -0
  31. quantum/core/__init__.py +3 -0
  32. quantum/core/ast_nodes.py +2611 -0
  33. quantum/core/expression_diagnostics.py +87 -0
  34. quantum/core/expression_stdlib.py +148 -0
  35. quantum/core/expressions.py +591 -0
  36. quantum/core/features/agents/src/__init__.py +30 -0
  37. quantum/core/features/agents/src/ast_node.py +540 -0
  38. quantum/core/features/conditionals/src/__init__.py +8 -0
  39. quantum/core/features/conditionals/src/ast_node.py +68 -0
  40. quantum/core/features/data_fetching/src/__init__.py +21 -0
  41. quantum/core/features/data_fetching/src/ast_node.py +312 -0
  42. quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
  43. quantum/core/features/data_fetching/src/html_adapter.py +474 -0
  44. quantum/core/features/data_fetching/src/parser.py +225 -0
  45. quantum/core/features/data_import/src/__init__.py +0 -0
  46. quantum/core/features/data_import/src/ast_node.py +291 -0
  47. quantum/core/features/data_import/src/runtime.py +538 -0
  48. quantum/core/features/dump/src/__init__.py +12 -0
  49. quantum/core/features/dump/src/ast_node.py +106 -0
  50. quantum/core/features/dump/src/parser.py +61 -0
  51. quantum/core/features/dump/src/runtime.py +246 -0
  52. quantum/core/features/functions/src/__init__.py +8 -0
  53. quantum/core/features/functions/src/ast_node.py +149 -0
  54. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  55. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  56. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  57. quantum/core/features/invocation/src/__init__.py +0 -0
  58. quantum/core/features/invocation/src/ast_node.py +146 -0
  59. quantum/core/features/invocation/src/runtime.py +327 -0
  60. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  61. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  62. quantum/core/features/knowledge_base/src/parser.py +82 -0
  63. quantum/core/features/logging/src/__init__.py +12 -0
  64. quantum/core/features/logging/src/ast_node.py +111 -0
  65. quantum/core/features/logging/src/parser.py +50 -0
  66. quantum/core/features/logging/src/runtime.py +190 -0
  67. quantum/core/features/loops/src/__init__.py +8 -0
  68. quantum/core/features/loops/src/ast_node.py +60 -0
  69. quantum/core/features/query/src/__init__.py +0 -0
  70. quantum/core/features/query/src/database_service.py +322 -0
  71. quantum/core/features/query/src/query_validators.py +20 -0
  72. quantum/core/features/state_management/src/__init__.py +11 -0
  73. quantum/core/features/state_management/src/ast_node.py +228 -0
  74. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  75. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  76. quantum/core/features/terminal_engine/src/parser.py +361 -0
  77. quantum/core/features/testing_engine/src/__init__.py +41 -0
  78. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  79. quantum/core/features/testing_engine/src/parser.py +604 -0
  80. quantum/core/features/theming/src/__init__.py +48 -0
  81. quantum/core/features/theming/src/ast_node.py +137 -0
  82. quantum/core/features/theming/src/presets.py +405 -0
  83. quantum/core/features/ui_engine/src/__init__.py +20 -0
  84. quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
  85. quantum/core/features/ui_engine/src/parser.py +1106 -0
  86. quantum/core/features/websocket/src/__init__.py +24 -0
  87. quantum/core/features/websocket/src/ast_node.py +247 -0
  88. quantum/core/html_compat.py +299 -0
  89. quantum/core/parser.py +1235 -0
  90. quantum/core/parser_registry.py +213 -0
  91. quantum/core/parsers/__init__.py +76 -0
  92. quantum/core/parsers/ai/__init__.py +12 -0
  93. quantum/core/parsers/ai/agent_parser.py +106 -0
  94. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  95. quantum/core/parsers/ai/llm_parser.py +78 -0
  96. quantum/core/parsers/ai/team_parser.py +88 -0
  97. quantum/core/parsers/base.py +322 -0
  98. quantum/core/parsers/composition/__init__.py +10 -0
  99. quantum/core/parsers/composition/import_parser.py +50 -0
  100. quantum/core/parsers/composition/slot_parser.py +48 -0
  101. quantum/core/parsers/control_flow/__init__.py +11 -0
  102. quantum/core/parsers/control_flow/if_parser.py +68 -0
  103. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  104. quantum/core/parsers/control_flow/set_parser.py +89 -0
  105. quantum/core/parsers/data/__init__.py +12 -0
  106. quantum/core/parsers/data/data_parser.py +217 -0
  107. quantum/core/parsers/data/invoke_parser.py +116 -0
  108. quantum/core/parsers/data/query_parser.py +188 -0
  109. quantum/core/parsers/data/transaction_parser.py +77 -0
  110. quantum/core/parsers/events/__init__.py +9 -0
  111. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  112. quantum/core/parsers/forms/__init__.py +11 -0
  113. quantum/core/parsers/forms/action_parser.py +54 -0
  114. quantum/core/parsers/forms/flash_parser.py +34 -0
  115. quantum/core/parsers/forms/redirect_parser.py +33 -0
  116. quantum/core/parsers/functions/__init__.py +11 -0
  117. quantum/core/parsers/functions/function_parser.py +137 -0
  118. quantum/core/parsers/functions/param_parser.py +66 -0
  119. quantum/core/parsers/functions/return_parser.py +31 -0
  120. quantum/core/parsers/html/__init__.py +10 -0
  121. quantum/core/parsers/html/component_call_parser.py +130 -0
  122. quantum/core/parsers/html/html_parser.py +115 -0
  123. quantum/core/parsers/jobs/__init__.py +11 -0
  124. quantum/core/parsers/jobs/job_parser.py +71 -0
  125. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  126. quantum/core/parsers/jobs/thread_parser.py +57 -0
  127. quantum/core/parsers/messaging/__init__.py +17 -0
  128. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  130. quantum/core/parsers/messaging/message_parser.py +114 -0
  131. quantum/core/parsers/messaging/queue_parser.py +61 -0
  132. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  133. quantum/core/parsers/persistence/__init__.py +9 -0
  134. quantum/core/parsers/persistence/persist_parser.py +64 -0
  135. quantum/core/parsers/routing/__init__.py +9 -0
  136. quantum/core/parsers/routing/route_parser.py +41 -0
  137. quantum/core/parsers/scripting/__init__.py +12 -0
  138. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  139. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  140. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  141. quantum/core/parsers/scripting/python_parser.py +49 -0
  142. quantum/core/parsers/services/__init__.py +12 -0
  143. quantum/core/parsers/services/dump_parser.py +52 -0
  144. quantum/core/parsers/services/file_parser.py +48 -0
  145. quantum/core/parsers/services/log_parser.py +46 -0
  146. quantum/core/parsers/services/mail_parser.py +65 -0
  147. quantum/core/tiers.py +82 -0
  148. quantum/packages/__init__.py +28 -0
  149. quantum/packages/manager.py +413 -0
  150. quantum/packages/manifest.py +351 -0
  151. quantum/packages/registry.py +399 -0
  152. quantum/packages/resolver.py +336 -0
  153. quantum/plugins/__init__.py +33 -0
  154. quantum/plugins/hooks.py +329 -0
  155. quantum/plugins/loader.py +479 -0
  156. quantum/plugins/manifest.py +336 -0
  157. quantum/plugins/registry.py +371 -0
  158. quantum/runtime/__init__.py +28 -0
  159. quantum/runtime/action_handler.py +443 -0
  160. quantum/runtime/adapters/__init__.py +88 -0
  161. quantum/runtime/adapters/memory_adapter.py +690 -0
  162. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  163. quantum/runtime/adapters/redis_adapter.py +582 -0
  164. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  165. quantum/runtime/agent_service.py +1133 -0
  166. quantum/runtime/api_server.py +86 -0
  167. quantum/runtime/ast_cache.py +506 -0
  168. quantum/runtime/auth_service.py +267 -0
  169. quantum/runtime/component.py +990 -0
  170. quantum/runtime/component_composer.py +319 -0
  171. quantum/runtime/component_resolver.py +174 -0
  172. quantum/runtime/database_service.py +598 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +286 -0
  176. quantum/runtime/executor_registry.py +171 -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 +262 -0
  184. quantum/runtime/executors/control_flow/__init__.py +11 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +93 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +307 -0
  187. quantum/runtime/executors/control_flow/set_executor.py +412 -0
  188. quantum/runtime/executors/data/__init__.py +12 -0
  189. quantum/runtime/executors/data/data_executor.py +145 -0
  190. quantum/runtime/executors/data/invoke_executor.py +176 -0
  191. quantum/runtime/executors/data/query_executor.py +256 -0
  192. quantum/runtime/executors/data/transaction_executor.py +91 -0
  193. quantum/runtime/executors/jobs/__init__.py +11 -0
  194. quantum/runtime/executors/jobs/job_executor.py +190 -0
  195. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  196. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  197. quantum/runtime/executors/messaging/__init__.py +17 -0
  198. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  199. quantum/runtime/executors/messaging/message_executor.py +174 -0
  200. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  201. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  202. quantum/runtime/executors/scripting/__init__.py +11 -0
  203. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  204. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  205. quantum/runtime/executors/scripting/python_executor.py +249 -0
  206. quantum/runtime/executors/services/__init__.py +12 -0
  207. quantum/runtime/executors/services/dump_executor.py +72 -0
  208. quantum/runtime/executors/services/file_executor.py +89 -0
  209. quantum/runtime/executors/services/log_executor.py +77 -0
  210. quantum/runtime/executors/services/mail_executor.py +81 -0
  211. quantum/runtime/expression_cache.py +498 -0
  212. quantum/runtime/file_upload_service.py +326 -0
  213. quantum/runtime/function_registry.py +118 -0
  214. quantum/runtime/game_builder.py +166 -0
  215. quantum/runtime/game_code_generator.py +2371 -0
  216. quantum/runtime/game_templates.py +2006 -0
  217. quantum/runtime/godot_code_generator.py +4681 -0
  218. quantum/runtime/godot_templates.py +1449 -0
  219. quantum/runtime/job_executor.py +1599 -0
  220. quantum/runtime/knowledge_service.py +500 -0
  221. quantum/runtime/llm_cache.py +100 -0
  222. quantum/runtime/llm_providers.py +704 -0
  223. quantum/runtime/llm_service.py +287 -0
  224. quantum/runtime/logging_setup.py +140 -0
  225. quantum/runtime/message_broker.py +364 -0
  226. quantum/runtime/message_queue_service.py +571 -0
  227. quantum/runtime/param_validation.py +184 -0
  228. quantum/runtime/pypy_compat.py +315 -0
  229. quantum/runtime/python_bridge.py +698 -0
  230. quantum/runtime/query_validators.py +304 -0
  231. quantum/runtime/renderer.py +733 -0
  232. quantum/runtime/service_container.py +444 -0
  233. quantum/runtime/terminal_builder.py +76 -0
  234. quantum/runtime/terminal_code_generator.py +607 -0
  235. quantum/runtime/terminal_templates.py +243 -0
  236. quantum/runtime/testing_builder.py +77 -0
  237. quantum/runtime/testing_code_generator.py +833 -0
  238. quantum/runtime/testing_templates.py +85 -0
  239. quantum/runtime/ui_builder.py +188 -0
  240. quantum/runtime/ui_desktop_adapter.py +1730 -0
  241. quantum/runtime/ui_desktop_templates.py +307 -0
  242. quantum/runtime/ui_html_adapter.py +2691 -0
  243. quantum/runtime/ui_html_templates.py +2297 -0
  244. quantum/runtime/ui_mobile_adapter.py +1832 -0
  245. quantum/runtime/ui_mobile_templates.py +1003 -0
  246. quantum/runtime/ui_textual_adapter.py +1866 -0
  247. quantum/runtime/ui_textual_templates.py +45 -0
  248. quantum/runtime/ui_tokens.py +465 -0
  249. quantum/runtime/ui_validator.py +365 -0
  250. quantum/runtime/validators.py +256 -0
  251. quantum/runtime/web_server.py +1766 -0
  252. quantum/runtime/websocket_adapter.py +501 -0
  253. quantum/runtime/websocket_service.py +585 -0
  254. quantum/runtime/websocket_transport.py +289 -0
  255. quantum/runtime/wsgi.py +101 -0
  256. quantum/utils/__init__.py +1 -0
  257. quantum_framework-0.9.0.dist-info/METADATA +244 -0
  258. quantum_framework-0.9.0.dist-info/RECORD +262 -0
  259. quantum_framework-0.9.0.dist-info/WHEEL +5 -0
  260. quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
  261. quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
  262. quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,243 @@
1
+ """
2
+ Terminal Engine - Python Code Templates and PyBuilder
3
+
4
+ Provides:
5
+ - PyBuilder: Structured Python code emitter with proper indentation
6
+ - App template for Textual applications
7
+ """
8
+
9
+ import re
10
+
11
+
12
+ # ==========================================================================
13
+ # PyBuilder - Structured Python code emitter
14
+ # ==========================================================================
15
+
16
+ class PyBuilder:
17
+ """Builds syntactically valid Python code with proper indentation.
18
+
19
+ Every append method returns self for chaining.
20
+ """
21
+
22
+ def __init__(self, indent_size: int = 4):
23
+ self._lines: list[str] = []
24
+ self._indent: int = 0
25
+ self._indent_size: int = indent_size
26
+
27
+ # -- core --
28
+
29
+ @property
30
+ def _pad(self) -> str:
31
+ return ' ' * (self._indent * self._indent_size)
32
+
33
+ def line(self, code: str = '') -> 'PyBuilder':
34
+ """Emit a single line at current indent."""
35
+ self._lines.append(f"{self._pad}{code}" if code else '')
36
+ return self
37
+
38
+ def raw(self, code: str) -> 'PyBuilder':
39
+ """Emit raw code at current indent (multi-line)."""
40
+ for ln in code.split('\n'):
41
+ self._lines.append(f"{self._pad}{ln}")
42
+ return self
43
+
44
+ def blank(self) -> 'PyBuilder':
45
+ self._lines.append('')
46
+ return self
47
+
48
+ def comment(self, text: str) -> 'PyBuilder':
49
+ return self.line(f"# {text}")
50
+
51
+ def section(self, title: str) -> 'PyBuilder':
52
+ return self.blank().line(f"# === {title} ===")
53
+
54
+ def indent(self) -> 'PyBuilder':
55
+ self._indent += 1
56
+ return self
57
+
58
+ def dedent(self) -> 'PyBuilder':
59
+ self._indent = max(0, self._indent - 1)
60
+ return self
61
+
62
+ # -- Python constructs --
63
+
64
+ def assign(self, target: str, value: str) -> 'PyBuilder':
65
+ return self.line(f"{target} = {value}")
66
+
67
+ def class_def(self, name: str, bases: str = '') -> 'PyBuilder':
68
+ """Open a class definition."""
69
+ if bases:
70
+ self.line(f"class {name}({bases}):")
71
+ else:
72
+ self.line(f"class {name}:")
73
+ self._indent += 1
74
+ return self
75
+
76
+ def method_def(self, name: str, params: str = 'self') -> 'PyBuilder':
77
+ """Open a method definition."""
78
+ self.line(f"def {name}({params}):")
79
+ self._indent += 1
80
+ return self
81
+
82
+ def async_method_def(self, name: str, params: str = 'self') -> 'PyBuilder':
83
+ """Open an async method definition."""
84
+ self.line(f"async def {name}({params}):")
85
+ self._indent += 1
86
+ return self
87
+
88
+ def func_def(self, name: str, params: str = '') -> 'PyBuilder':
89
+ """Open a function definition."""
90
+ self.line(f"def {name}({params}):")
91
+ self._indent += 1
92
+ return self
93
+
94
+ def if_block(self, condition: str) -> 'PyBuilder':
95
+ self.line(f"if {condition}:")
96
+ self._indent += 1
97
+ return self
98
+
99
+ def elif_block(self, condition: str) -> 'PyBuilder':
100
+ self._indent = max(0, self._indent - 1)
101
+ self.line(f"elif {condition}:")
102
+ self._indent += 1
103
+ return self
104
+
105
+ def else_block(self) -> 'PyBuilder':
106
+ self._indent = max(0, self._indent - 1)
107
+ self.line("else:")
108
+ self._indent += 1
109
+ return self
110
+
111
+ def for_block(self, var: str, iterable: str) -> 'PyBuilder':
112
+ self.line(f"for {var} in {iterable}:")
113
+ self._indent += 1
114
+ return self
115
+
116
+ def with_block(self, expr: str) -> 'PyBuilder':
117
+ self.line(f"with {expr}:")
118
+ self._indent += 1
119
+ return self
120
+
121
+ def try_block(self) -> 'PyBuilder':
122
+ self.line("try:")
123
+ self._indent += 1
124
+ return self
125
+
126
+ def except_block(self, exc: str = 'Exception as e') -> 'PyBuilder':
127
+ self._indent = max(0, self._indent - 1)
128
+ self.line(f"except {exc}:")
129
+ self._indent += 1
130
+ return self
131
+
132
+ def end_block(self) -> 'PyBuilder':
133
+ """Dedent to close a block."""
134
+ self._indent = max(0, self._indent - 1)
135
+ return self
136
+
137
+ def decorator(self, name: str) -> 'PyBuilder':
138
+ return self.line(f"@{name}")
139
+
140
+ def docstring(self, text: str) -> 'PyBuilder':
141
+ return self.line(f'"""{text}"""')
142
+
143
+ def return_stmt(self, value: str = '') -> 'PyBuilder':
144
+ if value:
145
+ return self.line(f"return {value}")
146
+ return self.line("return")
147
+
148
+ def yield_stmt(self, value: str) -> 'PyBuilder':
149
+ return self.line(f"yield {value}")
150
+
151
+ def pass_stmt(self) -> 'PyBuilder':
152
+ return self.line("pass")
153
+
154
+ def build(self) -> str:
155
+ return '\n'.join(self._lines)
156
+
157
+
158
+ # ==========================================================================
159
+ # Utility functions
160
+ # ==========================================================================
161
+
162
+ def py_string(value: str) -> str:
163
+ """Safe Python string literal."""
164
+ escaped = value.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
165
+ return f'"{escaped}"'
166
+
167
+
168
+ def py_bool(value: bool) -> str:
169
+ return "True" if value else "False"
170
+
171
+
172
+ def py_id(name: str) -> str:
173
+ """Convert a string to a valid Python identifier.
174
+
175
+ Trocava tres caracteres — `-`, `.` e espaco — e devolvia o resto como
176
+ estava. Um `on-click="salvar()"` chegava aqui e saia `salvar()`, que o
177
+ gerador entao escrevia como
178
+
179
+ def action_salvar()(self):
180
+
181
+ ou seja: o arquivo Python gerado nao COMPILAVA. Um unico botao com
182
+ parenteses — a forma como se escreve uma chamada em toda outra
183
+ linguagem — derrubava o aplicativo inteiro na hora de importar.
184
+
185
+ Agora tudo que nao serve num identificador vira `_`, entao esta funcao
186
+ nao consegue mais produzir codigo invalido. Quem quiser tratar
187
+ `nome(args)` como chamada deve usar `split_call` ANTES.
188
+ """
189
+ result = re.sub(r'\W', '_', name or '')
190
+ if not result:
191
+ return '_'
192
+ if result[0].isdigit():
193
+ result = '_' + result
194
+ return result
195
+
196
+
197
+ _CALL = re.compile(r'^\s*([A-Za-z_][\w.-]*)\s*\((.*)\)\s*$', re.DOTALL)
198
+
199
+
200
+ def split_call(handler: str):
201
+ """`"salvar()"` -> `("salvar", "")`; `"salvar"` -> `("salvar", None)`.
202
+
203
+ Devolve (nome, argumentos) com argumentos None quando o handler nao e
204
+ uma chamada. Existe porque os geradores comparavam o texto CRU do
205
+ on-click com os nomes das q:function: `"salvar()"` nunca casava com
206
+ `salvar`, entao a funcao do usuario nao era chamada nem quando existia.
207
+ """
208
+ if not handler:
209
+ return '', None
210
+ m = _CALL.match(handler)
211
+ if m:
212
+ return m.group(1), m.group(2).strip()
213
+ return handler.strip(), None
214
+
215
+
216
+ # ==========================================================================
217
+ # App template
218
+ # ==========================================================================
219
+
220
+ APP_TEMPLATE = '''#!/usr/bin/env python3
221
+ """
222
+ {title} - Generated by Quantum Terminal Engine
223
+ Run: python {filename}
224
+ Requires: pip install textual
225
+ """
226
+
227
+ from textual.app import App, ComposeResult
228
+ from textual.containers import Horizontal, Vertical
229
+ from textual.widgets import (
230
+ Header, Footer, Static, Button, Input,
231
+ DataTable, ProgressBar, Tree, RichLog,
232
+ TabbedContent, TabPane, OptionList,
233
+ )
234
+ from textual.binding import Binding
235
+ from textual.reactive import reactive
236
+ {extra_imports}
237
+
238
+ {app_code}
239
+
240
+ if __name__ == "__main__":
241
+ app = {app_class}()
242
+ app.run()
243
+ '''
@@ -0,0 +1,77 @@
1
+ """
2
+ Testing Engine - Builder/Orchestrator
3
+
4
+ Orchestrates the compilation pipeline:
5
+ ApplicationNode (type="testing") -> extract suites/config/fixtures/mocks -> TestingCodeGenerator -> test_*.py
6
+
7
+ Usage:
8
+ builder = TestingBuilder()
9
+ python_code = builder.build(app_node)
10
+ builder.build_to_file(app_node, "test_output.py")
11
+ """
12
+
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+ from quantum.core.ast_nodes import ApplicationNode
17
+ from quantum.core.features.testing_engine.src.ast_nodes import (
18
+ QTestSuiteNode, BrowserConfigNode, FixtureNode_Testing,
19
+ MockNode_Testing, AuthNode,
20
+ )
21
+ from quantum.runtime.testing_code_generator import QTestCodeGenerator
22
+
23
+
24
+ class TestingBuildError(Exception):
25
+ """Error during testing app build."""
26
+ pass
27
+
28
+
29
+ class TestingBuilder:
30
+ """Builds a standalone pytest file from a Quantum testing ApplicationNode."""
31
+
32
+ def build(self, app: ApplicationNode) -> str:
33
+ """Build Python test source string from an ApplicationNode with type='testing'.
34
+
35
+ The ApplicationNode is expected to have:
36
+ - app.test_suites: list of TestSuiteNode
37
+ - app.test_config: optional BrowserConfigNode
38
+ - app.test_fixtures: list of FixtureNode_Testing
39
+ - app.test_mocks: list of MockNode_Testing
40
+ - app.test_auth_states: list of AuthNode
41
+ """
42
+ suites = getattr(app, 'test_suites', [])
43
+ config = getattr(app, 'test_config', None)
44
+ fixtures = getattr(app, 'test_fixtures', [])
45
+ mocks = getattr(app, 'test_mocks', [])
46
+ auth_states = getattr(app, 'test_auth_states', [])
47
+
48
+ if not suites:
49
+ raise TestingBuildError("No test suites found in testing application")
50
+
51
+ valid_suites = [s for s in suites if isinstance(s, QTestSuiteNode)]
52
+ if not valid_suites:
53
+ raise TestingBuildError("No valid QTestSuiteNode found in testing application")
54
+
55
+ generator = QTestCodeGenerator()
56
+ python_code = generator.generate(
57
+ suites=valid_suites,
58
+ title=app.app_id,
59
+ browser_config=config if isinstance(config, BrowserConfigNode) else None,
60
+ fixtures=[f for f in fixtures if isinstance(f, FixtureNode_Testing)],
61
+ mocks=[m for m in mocks if isinstance(m, MockNode_Testing)],
62
+ auth_states=[a for a in auth_states if isinstance(a, AuthNode)],
63
+ )
64
+ return python_code
65
+
66
+ def build_to_file(self, app: ApplicationNode, output_path: Optional[str] = None) -> str:
67
+ """Build and write test_*.py file. Returns the output file path."""
68
+ python_code = self.build(app)
69
+
70
+ if output_path is None:
71
+ output_path = f"test_{app.app_id}.py"
72
+
73
+ path = Path(output_path)
74
+ path.parent.mkdir(parents=True, exist_ok=True)
75
+ path.write_text(python_code, encoding='utf-8')
76
+
77
+ return str(path.resolve())