quantum-framework 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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())
|