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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/admin.py +131 -0
- quantum/cli/check.py +223 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +414 -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 +548 -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 +565 -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 +2619 -0
- quantum/core/config_env.py +54 -0
- quantum/core/docs_links.py +8 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +208 -0
- quantum/core/expressions.py +677 -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_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +292 -0
- quantum/core/features/data_import/src/runtime.py +533 -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 +147 -0
- quantum/core/features/invocation/src/runtime.py +332 -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/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +121 -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 +1913 -0
- quantum/core/features/ui_engine/src/parser.py +1192 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/guards.py +53 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1551 -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 +71 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +93 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +229 -0
- quantum/core/parsers/data/invoke_parser.py +133 -0
- quantum/core/parsers/data/query_parser.py +213 -0
- quantum/core/parsers/data/transaction_parser.py +86 -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 +79 -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 +98 -0
- quantum/core/parsers/functions/param_parser.py +73 -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/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 +164 -0
- quantum/core/xml_lines.py +81 -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 +25 -0
- quantum/runtime/action_handler.py +532 -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/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +1048 -0
- quantum/runtime/component_composer.py +133 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/conditions.py +54 -0
- quantum/runtime/database_service.py +595 -0
- quantum/runtime/db_schema.py +100 -0
- quantum/runtime/dev_panel.py +178 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +283 -0
- quantum/runtime/executor_registry.py +178 -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 +264 -0
- quantum/runtime/executors/control_flow/__init__.py +12 -0
- quantum/runtime/executors/control_flow/if_executor.py +98 -0
- quantum/runtime/executors/control_flow/loop_executor.py +308 -0
- quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
- quantum/runtime/executors/control_flow/set_executor.py +464 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +156 -0
- quantum/runtime/executors/data/invoke_executor.py +228 -0
- quantum/runtime/executors/data/query_executor.py +290 -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 +546 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/local_server.py +28 -0
- quantum/runtime/logging_setup.py +141 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +218 -0
- quantum/runtime/pypy_compat.py +317 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +734 -0
- quantum/runtime/service_container.py +470 -0
- quantum/runtime/table_params.py +163 -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 +222 -0
- quantum/runtime/ui_console.py +405 -0
- quantum/runtime/ui_desktop.py +56 -0
- quantum/runtime/ui_form_rules.py +193 -0
- quantum/runtime/ui_html_adapter.py +2567 -0
- quantum/runtime/ui_html_templates.py +2106 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_page_renderer.py +392 -0
- quantum/runtime/ui_pager.py +73 -0
- quantum/runtime/ui_table_edit.py +139 -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/ui_view.py +221 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +2131 -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/services.py +109 -0
- quantum/utils/__init__.py +1 -0
- quantum_admin/__init__.py +4 -0
- quantum_admin/assets/quantum-admin-connectors.js +123 -0
- quantum_admin/assets/quantum-admin.css +2058 -0
- quantum_admin/assets/quantum-source-highlight.js +106 -0
- quantum_admin/backend/__init__.py +4 -0
- quantum_admin/backend/audit_service.py +346 -0
- quantum_admin/backend/auth_service.py +394 -0
- quantum_admin/backend/component_discovery.py +766 -0
- quantum_admin/backend/config_generator.py +586 -0
- quantum_admin/backend/connector_service.py +1016 -0
- quantum_admin/backend/crud.py +557 -0
- quantum_admin/backend/database.py +226 -0
- quantum_admin/backend/db_setup_service.py +193 -0
- quantum_admin/backend/deploy_service.py +698 -0
- quantum_admin/backend/docker_service.py +422 -0
- quantum_admin/backend/environment_service.py +418 -0
- quantum_admin/backend/git_service.py +370 -0
- quantum_admin/backend/health_service.py +390 -0
- quantum_admin/backend/job_service.py +347 -0
- quantum_admin/backend/log_service.py +331 -0
- quantum_admin/backend/main.py +14426 -0
- quantum_admin/backend/models.py +986 -0
- quantum_admin/backend/pipeline_service.py +812 -0
- quantum_admin/backend/resource_manager.py +1118 -0
- quantum_admin/backend/schemas.py +419 -0
- quantum_admin/backend/secret_manager.py +277 -0
- quantum_admin/backend/settings_service.py +488 -0
- quantum_admin/backend/template_service.py +710 -0
- quantum_admin/backend/test_execution_service.py +364 -0
- quantum_admin/backend/test_generator.py +782 -0
- quantum_admin/backend/webhook_service.py +454 -0
- quantum_admin/backend/websocket_manager.py +360 -0
- quantum_admin/components/admin/_layout/AdminShell.q +121 -0
- quantum_admin/components/admin/agents.q +107 -0
- quantum_admin/components/admin/app/[name].q +558 -0
- quantum_admin/components/admin/applications.q +160 -0
- quantum_admin/components/admin/component/[...path].q +205 -0
- quantum_admin/components/admin/components.q +59 -0
- quantum_admin/components/admin/connectors.q +283 -0
- quantum_admin/components/admin/dashboard.q +86 -0
- quantum_admin/components/admin/database.q +95 -0
- quantum_admin/components/admin/features.q +69 -0
- quantum_admin/components/admin/index.q +12 -0
- quantum_admin/components/admin/jobs.q +75 -0
- quantum_admin/components/admin/login.q +65 -0
- quantum_admin/components/admin/logout.q +11 -0
- quantum_admin/components/admin/settings.q +174 -0
- quantum_admin/components/admin/source.q +45 -0
- quantum_admin/components/admin/tests.q +73 -0
- quantum_admin/run.py +13 -0
- quantum_admin/services/__init__.py +16 -0
- quantum_admin/services/_base.py +84 -0
- quantum_admin/services/_component_test_generator.py +205 -0
- quantum_admin/services/_yaml_editor.py +82 -0
- quantum_admin/services/apps.py +330 -0
- quantum_admin/services/auth.py +61 -0
- quantum_admin/services/components.py +210 -0
- quantum_admin/services/connectors.py +164 -0
- quantum_admin/services/projects.py +189 -0
- quantum_admin/services/repository.py +223 -0
- quantum_admin/services/settings.py +150 -0
- quantum_admin/services/yaml_import.py +118 -0
- quantum_framework-0.18.0.dist-info/METADATA +281 -0
- quantum_framework-0.18.0.dist-info/RECORD +333 -0
- quantum_framework-0.18.0.dist-info/WHEEL +5 -0
- quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Testing Engine - Python Code Templates
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- TEST_TEMPLATE: Base template for generated pytest files
|
|
6
|
+
- Reuses PyBuilder from terminal_templates
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from quantum.runtime.terminal_templates import PyBuilder, py_string, py_id
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ==========================================================================
|
|
13
|
+
# Test file template
|
|
14
|
+
# ==========================================================================
|
|
15
|
+
|
|
16
|
+
TEST_TEMPLATE = '''"""
|
|
17
|
+
{title} - Generated by Quantum Testing Engine
|
|
18
|
+
Run: pytest {filename} -v
|
|
19
|
+
Requires: pip install pytest playwright pytest-playwright
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import pytest
|
|
23
|
+
import json
|
|
24
|
+
import time
|
|
25
|
+
from unittest.mock import MagicMock, patch
|
|
26
|
+
{extra_imports}
|
|
27
|
+
|
|
28
|
+
{test_code}
|
|
29
|
+
'''
|
|
30
|
+
|
|
31
|
+
BROWSER_IMPORTS = """import re
|
|
32
|
+
from playwright.sync_api import Page, expect, BrowserContext
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
ASYNC_BROWSER_IMPORTS = """import asyncio
|
|
36
|
+
from playwright.async_api import async_playwright, Page, expect, BrowserContext
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ==========================================================================
|
|
41
|
+
# Helper code snippets embedded in generated tests
|
|
42
|
+
# ==========================================================================
|
|
43
|
+
|
|
44
|
+
FIXTURE_HELPER = '''
|
|
45
|
+
@pytest.fixture
|
|
46
|
+
def {name}({params}):
|
|
47
|
+
"""{docstring}"""
|
|
48
|
+
{body}
|
|
49
|
+
'''
|
|
50
|
+
|
|
51
|
+
CONFTEST_TEMPLATE = '''"""
|
|
52
|
+
conftest.py - Generated by Quantum Testing Engine
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
import pytest
|
|
56
|
+
from playwright.sync_api import sync_playwright
|
|
57
|
+
|
|
58
|
+
{fixtures}
|
|
59
|
+
'''
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def sanitize_test_name(name: str) -> str:
|
|
63
|
+
"""Convert a human-readable test name to a valid Python identifier."""
|
|
64
|
+
result = name.lower()
|
|
65
|
+
result = result.replace(' ', '_').replace('-', '_').replace('.', '_')
|
|
66
|
+
result = ''.join(c for c in result if c.isalnum() or c == '_')
|
|
67
|
+
if result and result[0].isdigit():
|
|
68
|
+
result = '_' + result
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def sanitize_class_name(name: str) -> str:
|
|
73
|
+
"""Convert a suite name to a valid Python class name."""
|
|
74
|
+
# Replace separators with spaces for splitting
|
|
75
|
+
normalized = name.replace('-', ' ').replace('_', ' ')
|
|
76
|
+
words = normalized.split()
|
|
77
|
+
# Capitalize first letter of each word, preserve rest of casing
|
|
78
|
+
result = ''.join(w[0].upper() + w[1:] if w else '' for w in words)
|
|
79
|
+
# Remove non-alphanumeric chars
|
|
80
|
+
result = ''.join(c for c in result if c.isalnum())
|
|
81
|
+
if result and result[0].isdigit():
|
|
82
|
+
result = 'Test' + result
|
|
83
|
+
if not result.startswith('Test'):
|
|
84
|
+
result = 'Test' + result
|
|
85
|
+
return result
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
UI Engine - Builder/Orchestrator
|
|
3
|
+
|
|
4
|
+
Orchestrates the compilation pipeline:
|
|
5
|
+
ApplicationNode (type="ui") -> extract UI nodes -> Adapter -> output file
|
|
6
|
+
|
|
7
|
+
Targets (UI-8):
|
|
8
|
+
- html: a standalone HTML/CSS page — layout only
|
|
9
|
+
- textual: a standalone Python Textual app — layout only
|
|
10
|
+
- mobile / react-native: React Native (LABORATORIO: it translates q:set and
|
|
11
|
+
q:function to JavaScript, with no stability promise)
|
|
12
|
+
|
|
13
|
+
The application's logic runs in pages: a <q:component> with ui:* served by
|
|
14
|
+
`quantum start`, drawn by `quantum console` and `quantum desktop` too. The
|
|
15
|
+
old desktop target (a pywebview app that translated the logic to a JS bridge)
|
|
16
|
+
was removed in 0.16: `quantum desktop` opens the pages in a window.
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
builder = UIBuilder()
|
|
20
|
+
code = builder.build(app_node, target='html')
|
|
21
|
+
builder.build_to_file(app_node, target='textual', output_path='app.py')
|
|
22
|
+
builder.build_to_file(app_node, target='mobile', output_path='App.js')
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Optional, Dict, Any, List
|
|
27
|
+
|
|
28
|
+
from quantum.core.ast_nodes import ApplicationNode
|
|
29
|
+
from quantum.core.features.state_management.src.ast_node import SetNode
|
|
30
|
+
from quantum.core.features.functions.src.ast_node import FunctionNode
|
|
31
|
+
from quantum.core.features.ui_engine.src.ast_nodes import UIWindowNode, is_ui_node
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class UIBuildError(Exception):
|
|
35
|
+
"""Error during UI app build."""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class UIBuilder:
|
|
40
|
+
"""Builds multi-target UI output from a Quantum UI ApplicationNode."""
|
|
41
|
+
|
|
42
|
+
def build(self, app: ApplicationNode, target: str = 'html') -> str:
|
|
43
|
+
"""Build output string from an ApplicationNode with type='ui'.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
app: ApplicationNode with ui_windows and ui_children populated.
|
|
47
|
+
target: 'html', 'textual', or 'desktop'.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Generated source code string.
|
|
51
|
+
"""
|
|
52
|
+
if not isinstance(app, ApplicationNode) or getattr(app, 'app_type', None) != 'ui':
|
|
53
|
+
# UI-0: it used to fail with "'ComponentNode' object has no
|
|
54
|
+
# attribute 'app_id'".
|
|
55
|
+
raise UIBuildError(
|
|
56
|
+
'a UI build needs <q:application type="ui">; this file is a '
|
|
57
|
+
f'<q:{"component" if not isinstance(app, ApplicationNode) else "application"}>. '
|
|
58
|
+
'Wrap the ui:window in <q:application id="MyApp" type="ui">.')
|
|
59
|
+
windows = getattr(app, 'ui_windows', [])
|
|
60
|
+
ui_children = getattr(app, 'ui_children', [])
|
|
61
|
+
title = app.app_id or 'Quantum UI'
|
|
62
|
+
|
|
63
|
+
if not windows and not ui_children:
|
|
64
|
+
raise UIBuildError("No UI elements found in application")
|
|
65
|
+
|
|
66
|
+
# Get theme from application
|
|
67
|
+
theme = getattr(app, 'ui_theme', None)
|
|
68
|
+
|
|
69
|
+
if target == 'desktop':
|
|
70
|
+
raise UIBuildError(
|
|
71
|
+
'--target desktop was removed in Quantum 0.16: it translated the logic to a '
|
|
72
|
+
'JavaScript bridge of its own. Write the screens as pages (<q:component> with '
|
|
73
|
+
'ui:* in components/) and run `quantum desktop` — the same pages, in a window.')
|
|
74
|
+
if target in ('html', 'textual'):
|
|
75
|
+
self._layout_only(app, target)
|
|
76
|
+
|
|
77
|
+
if target == 'html':
|
|
78
|
+
from quantum.runtime.ui_html_adapter import UIHtmlAdapter
|
|
79
|
+
adapter = UIHtmlAdapter()
|
|
80
|
+
return adapter.generate(windows, ui_children, title, theme=theme)
|
|
81
|
+
elif target == 'textual':
|
|
82
|
+
from quantum.runtime.ui_textual_adapter import UITextualAdapter
|
|
83
|
+
adapter = UITextualAdapter()
|
|
84
|
+
return adapter.generate(windows, ui_children, title)
|
|
85
|
+
elif target in ('mobile', 'react-native'):
|
|
86
|
+
from quantum.core.tiers import warn_ui_target
|
|
87
|
+
from quantum.runtime.ui_mobile_adapter import UIReactNativeAdapter
|
|
88
|
+
warn_ui_target(target)
|
|
89
|
+
adapter = UIReactNativeAdapter()
|
|
90
|
+
|
|
91
|
+
# Extract functions and state for React Native hooks
|
|
92
|
+
functions = self._extract_functions(app)
|
|
93
|
+
state_vars = self._extract_state(app)
|
|
94
|
+
|
|
95
|
+
return adapter.generate(
|
|
96
|
+
windows, ui_children, title,
|
|
97
|
+
functions=functions,
|
|
98
|
+
state_vars=state_vars
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
raise UIBuildError(f"Unknown target: {target}. Must be html, textual or mobile")
|
|
102
|
+
|
|
103
|
+
def build_to_file(self, app: ApplicationNode, target: str = 'html',
|
|
104
|
+
output_path: Optional[str] = None) -> str:
|
|
105
|
+
"""Build and write output file. Returns the output file path."""
|
|
106
|
+
code = self.build(app, target)
|
|
107
|
+
|
|
108
|
+
if output_path is None:
|
|
109
|
+
# UI-0: one file per target. textual and desktop both wrote
|
|
110
|
+
# <id>.py, so building one erased the other.
|
|
111
|
+
sufixo = {'html': '.html', 'textual': '_console.py',
|
|
112
|
+
'mobile': '.js', 'react-native': '.js'}.get(target, '.py')
|
|
113
|
+
output_path = f"{app.app_id}{sufixo}"
|
|
114
|
+
|
|
115
|
+
path = Path(output_path)
|
|
116
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
path.write_text(code, encoding='utf-8')
|
|
118
|
+
|
|
119
|
+
return str(path.resolve())
|
|
120
|
+
|
|
121
|
+
def _layout_only(self, app: ApplicationNode, target: str) -> None:
|
|
122
|
+
"""UI-8: html and textual builds draw layout; logic in them is an error.
|
|
123
|
+
|
|
124
|
+
It used to be dropped without a word — `{count}` came out raw and a
|
|
125
|
+
button called a function that did not exist.
|
|
126
|
+
"""
|
|
127
|
+
logica = [f'<q:function name="{f.name}">' for f in getattr(app, 'functions', [])]
|
|
128
|
+
logica += [f'<q:set name="{v.name}">' for v in getattr(app, 'state_vars', [])]
|
|
129
|
+
|
|
130
|
+
def andar(nos):
|
|
131
|
+
for no in nos or []:
|
|
132
|
+
if is_ui_node(no):
|
|
133
|
+
andar(getattr(no, 'children', None))
|
|
134
|
+
elif isinstance(no, (SetNode, FunctionNode)):
|
|
135
|
+
logica.append(f'<q:{"set" if isinstance(no, SetNode) else "function"} name="{no.name}">')
|
|
136
|
+
elif type(no).__name__ not in ('TextNode', 'HTMLNode', 'CommentNode'):
|
|
137
|
+
logica.append(f'<q:{type(no).__name__.replace("Node", "").lower()}>')
|
|
138
|
+
|
|
139
|
+
andar(getattr(app, 'ui_windows', []))
|
|
140
|
+
andar(getattr(app, 'ui_children', []))
|
|
141
|
+
if logica:
|
|
142
|
+
raise UIBuildError(
|
|
143
|
+
f'a {target} UI build draws layout only, and {logica[0]} would not run in it. '
|
|
144
|
+
'Write the screen as a page — <q:component> with ui:* in components/ — '
|
|
145
|
+
'and run it with `quantum start`, `quantum console` or `quantum desktop`, '
|
|
146
|
+
"where the page's runtime runs its logic.")
|
|
147
|
+
|
|
148
|
+
def _extract_functions(self, app: ApplicationNode) -> Dict[str, FunctionNode]:
|
|
149
|
+
"""Extract q:function nodes from application.
|
|
150
|
+
|
|
151
|
+
Only extracts UI functions (those without REST endpoints),
|
|
152
|
+
as REST functions are for server-side use.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
app: ApplicationNode to extract functions from.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Dict mapping function name to FunctionNode.
|
|
159
|
+
"""
|
|
160
|
+
functions = {}
|
|
161
|
+
|
|
162
|
+
# Check components for functions
|
|
163
|
+
for component in getattr(app, 'components', []):
|
|
164
|
+
for func in getattr(component, 'functions', []):
|
|
165
|
+
# Only include UI functions (not REST endpoints)
|
|
166
|
+
if not func.is_rest_enabled():
|
|
167
|
+
functions[func.name] = func
|
|
168
|
+
|
|
169
|
+
# Check for functions directly on app (if any)
|
|
170
|
+
for func in getattr(app, 'functions', []):
|
|
171
|
+
if not func.is_rest_enabled():
|
|
172
|
+
functions[func.name] = func
|
|
173
|
+
|
|
174
|
+
# Check ui_children for functions (when parsed directly into app)
|
|
175
|
+
for child in getattr(app, 'ui_children', []):
|
|
176
|
+
if isinstance(child, FunctionNode) and not child.is_rest_enabled():
|
|
177
|
+
functions[child.name] = child
|
|
178
|
+
|
|
179
|
+
# Check ui_windows children for functions
|
|
180
|
+
for window in getattr(app, 'ui_windows', []):
|
|
181
|
+
for child in getattr(window, 'children', []):
|
|
182
|
+
if isinstance(child, FunctionNode) and not child.is_rest_enabled():
|
|
183
|
+
functions[child.name] = child
|
|
184
|
+
|
|
185
|
+
return functions
|
|
186
|
+
|
|
187
|
+
def _extract_state(self, app: ApplicationNode) -> Dict[str, Any]:
|
|
188
|
+
"""Extract q:set nodes from application level.
|
|
189
|
+
|
|
190
|
+
Extracts initial state variables defined at the application or
|
|
191
|
+
component level for state initialization.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
app: ApplicationNode to extract state from.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Dict mapping variable name to initial value.
|
|
198
|
+
"""
|
|
199
|
+
state = {}
|
|
200
|
+
|
|
201
|
+
# Check ui_children for SetNode (top-level state declarations)
|
|
202
|
+
for child in getattr(app, 'ui_children', []):
|
|
203
|
+
if isinstance(child, SetNode):
|
|
204
|
+
# Use default if value is not set
|
|
205
|
+
value = child.value if child.value is not None else child.default
|
|
206
|
+
state[child.name] = value
|
|
207
|
+
|
|
208
|
+
# Check ui_windows children for SetNode
|
|
209
|
+
for window in getattr(app, 'ui_windows', []):
|
|
210
|
+
for child in getattr(window, 'children', []):
|
|
211
|
+
if isinstance(child, SetNode):
|
|
212
|
+
value = child.value if child.value is not None else child.default
|
|
213
|
+
state[child.name] = value
|
|
214
|
+
|
|
215
|
+
# Check components for statements that are SetNode
|
|
216
|
+
for component in getattr(app, 'components', []):
|
|
217
|
+
for stmt in getattr(component, 'statements', []):
|
|
218
|
+
if isinstance(stmt, SetNode):
|
|
219
|
+
value = stmt.value if stmt.value is not None else stmt.default
|
|
220
|
+
state[stmt.name] = value
|
|
221
|
+
|
|
222
|
+
return state
|