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,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Route Parser - Parse q:route 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 QuantumRoute
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RouteParser(BaseTagParser):
|
|
12
|
+
"""
|
|
13
|
+
Parser for q:route statements.
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
<q:route path="/users" method="GET" />
|
|
17
|
+
<q:route path="/api/products/{id}" method="POST">
|
|
18
|
+
<q:return name="product" type="object" />
|
|
19
|
+
</q:route>
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def tag_names(self) -> List[str]:
|
|
24
|
+
return ['route']
|
|
25
|
+
|
|
26
|
+
def parse(self, element: ET.Element) -> QuantumRoute:
|
|
27
|
+
"""Parse q:route element"""
|
|
28
|
+
path = element.get('path', '/')
|
|
29
|
+
method = element.get('method', 'GET').upper()
|
|
30
|
+
|
|
31
|
+
route = QuantumRoute(path, method)
|
|
32
|
+
|
|
33
|
+
# Parse q:return inside the route
|
|
34
|
+
for child in element:
|
|
35
|
+
child_type = self._get_element_name(child)
|
|
36
|
+
if child_type == 'return':
|
|
37
|
+
return_node = self.parse_child(child)
|
|
38
|
+
if return_node:
|
|
39
|
+
route.returns.append(return_node)
|
|
40
|
+
|
|
41
|
+
return route
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scripting Parsers
|
|
3
|
+
|
|
4
|
+
Parsers for Python scripting: python, pyimport, pyclass, pydecorator.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .python_parser import PythonParser
|
|
8
|
+
from .pyimport_parser import PyImportParser
|
|
9
|
+
from .pyclass_parser import PyClassParser
|
|
10
|
+
from .pydecorator_parser import PyDecoratorParser
|
|
11
|
+
|
|
12
|
+
__all__ = ['PythonParser', 'PyImportParser', 'PyClassParser', 'PyDecoratorParser']
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyClass Parser - Parse q:class statements
|
|
3
|
+
|
|
4
|
+
Handles inline Python class definitions.
|
|
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 PyClassNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PyClassParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:class statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Class definition
|
|
19
|
+
- Base class inheritance
|
|
20
|
+
- Decorator application
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def tag_names(self) -> List[str]:
|
|
25
|
+
return ['class', 'pyclass']
|
|
26
|
+
|
|
27
|
+
def parse(self, element: ET.Element) -> PyClassNode:
|
|
28
|
+
"""
|
|
29
|
+
Parse q:class statement.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
element: XML element for q:class
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
PyClassNode AST node
|
|
36
|
+
"""
|
|
37
|
+
name = self.get_attr(element, 'name')
|
|
38
|
+
code = self.get_text(element)
|
|
39
|
+
|
|
40
|
+
if not name:
|
|
41
|
+
raise ParserError("class requires 'name' attribute")
|
|
42
|
+
if not code or not code.strip():
|
|
43
|
+
raise ParserError("class body cannot be empty")
|
|
44
|
+
|
|
45
|
+
# Parse bases
|
|
46
|
+
bases_attr = self.get_attr(element, 'bases')
|
|
47
|
+
bases = []
|
|
48
|
+
if bases_attr:
|
|
49
|
+
bases = [b.strip() for b in bases_attr.split(',')]
|
|
50
|
+
|
|
51
|
+
# Parse decorators
|
|
52
|
+
decorators_attr = self.get_attr(element, 'decorators')
|
|
53
|
+
decorators = []
|
|
54
|
+
if decorators_attr:
|
|
55
|
+
decorators = [d.strip() for d in decorators_attr.split(',')]
|
|
56
|
+
|
|
57
|
+
return PyClassNode(
|
|
58
|
+
name=name,
|
|
59
|
+
code=code,
|
|
60
|
+
bases=bases,
|
|
61
|
+
decorators=decorators
|
|
62
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyDecorator Parser - Parse q:decorator statements
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import textwrap
|
|
6
|
+
from typing import List
|
|
7
|
+
from xml.etree import ElementTree as ET
|
|
8
|
+
from quantum.core.parsers.base import BaseTagParser
|
|
9
|
+
from quantum.core.ast_nodes import PyDecoratorNode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PyDecoratorParser(BaseTagParser):
|
|
13
|
+
"""
|
|
14
|
+
Parser for q:decorator statements for Python decorator definitions.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
<!-- Caching decorator -->
|
|
18
|
+
<q:decorator name="cached">
|
|
19
|
+
from functools import wraps
|
|
20
|
+
def decorator(func):
|
|
21
|
+
cache = {}
|
|
22
|
+
@wraps(func)
|
|
23
|
+
def wrapper(*args):
|
|
24
|
+
if args in cache:
|
|
25
|
+
return cache[args]
|
|
26
|
+
result = func(*args)
|
|
27
|
+
cache[args] = result
|
|
28
|
+
return result
|
|
29
|
+
return wrapper
|
|
30
|
+
return decorator
|
|
31
|
+
</q:decorator>
|
|
32
|
+
|
|
33
|
+
<!-- Decorator with parameters -->
|
|
34
|
+
<q:decorator name="retry" params="attempts, delay">
|
|
35
|
+
import time
|
|
36
|
+
def decorator(func):
|
|
37
|
+
def wrapper(*args, **kwargs):
|
|
38
|
+
for i in range(attempts):
|
|
39
|
+
try:
|
|
40
|
+
return func(*args, **kwargs)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
if i == attempts - 1:
|
|
43
|
+
raise
|
|
44
|
+
time.sleep(delay)
|
|
45
|
+
return wrapper
|
|
46
|
+
return decorator
|
|
47
|
+
</q:decorator>
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def tag_names(self) -> List[str]:
|
|
52
|
+
return ['decorator', 'pydecorator']
|
|
53
|
+
|
|
54
|
+
def parse(self, element: ET.Element) -> PyDecoratorNode:
|
|
55
|
+
"""Parse q:decorator element"""
|
|
56
|
+
name = element.get('name', '')
|
|
57
|
+
code = element.text or ""
|
|
58
|
+
code = textwrap.dedent(code).strip()
|
|
59
|
+
|
|
60
|
+
# Parse params (comma-separated)
|
|
61
|
+
params_str = element.get('params', '')
|
|
62
|
+
params = [p.strip() for p in params_str.split(',') if p.strip()]
|
|
63
|
+
|
|
64
|
+
return PyDecoratorNode(
|
|
65
|
+
name=name,
|
|
66
|
+
code=code,
|
|
67
|
+
params=params
|
|
68
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyImport Parser - Parse q:pyimport statements
|
|
3
|
+
|
|
4
|
+
Handles Python module imports.
|
|
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 PyImportNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PyImportParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:pyimport statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Module import with alias
|
|
19
|
+
- Specific name imports
|
|
20
|
+
- Star imports
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def tag_names(self) -> List[str]:
|
|
25
|
+
return ['pyimport']
|
|
26
|
+
|
|
27
|
+
def parse(self, element: ET.Element) -> PyImportNode:
|
|
28
|
+
"""
|
|
29
|
+
Parse q:pyimport statement.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
element: XML element for q:pyimport
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
PyImportNode AST node
|
|
36
|
+
"""
|
|
37
|
+
module = self.get_attr(element, 'module')
|
|
38
|
+
|
|
39
|
+
if not module:
|
|
40
|
+
raise ParserError("pyimport requires 'module' attribute")
|
|
41
|
+
|
|
42
|
+
# Parse names if present
|
|
43
|
+
names_attr = self.get_attr(element, 'names')
|
|
44
|
+
names = []
|
|
45
|
+
if names_attr:
|
|
46
|
+
names = [n.strip() for n in names_attr.split(',')]
|
|
47
|
+
|
|
48
|
+
return PyImportNode(
|
|
49
|
+
module=module,
|
|
50
|
+
alias=self.get_attr(element, 'as'),
|
|
51
|
+
names=names
|
|
52
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python Parser - Parse q:python statements
|
|
3
|
+
|
|
4
|
+
Handles embedded Python code.
|
|
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 PythonNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PythonParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:python statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Inline Python code
|
|
19
|
+
- Scope management
|
|
20
|
+
- Async execution
|
|
21
|
+
- Result capture
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def tag_names(self) -> List[str]:
|
|
26
|
+
return ['python']
|
|
27
|
+
|
|
28
|
+
def parse(self, element: ET.Element) -> PythonNode:
|
|
29
|
+
"""
|
|
30
|
+
Parse q:python statement.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
element: XML element for q:python
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
PythonNode AST node
|
|
37
|
+
"""
|
|
38
|
+
code = self.get_text(element)
|
|
39
|
+
|
|
40
|
+
if not code or not code.strip():
|
|
41
|
+
raise ParserError("Python block cannot be empty")
|
|
42
|
+
|
|
43
|
+
return PythonNode(
|
|
44
|
+
code=code,
|
|
45
|
+
scope=self.get_attr(element, 'scope', 'component'),
|
|
46
|
+
async_mode=self.get_bool_attr(element, 'async', False),
|
|
47
|
+
timeout=self.get_attr(element, 'timeout'),
|
|
48
|
+
result=self.get_attr(element, 'result')
|
|
49
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Services Parsers
|
|
3
|
+
|
|
4
|
+
Parsers for service operations: log, dump, file, mail.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .log_parser import LogParser
|
|
8
|
+
from .dump_parser import DumpParser
|
|
9
|
+
from .file_parser import FileParser
|
|
10
|
+
from .mail_parser import MailParser
|
|
11
|
+
|
|
12
|
+
__all__ = ['LogParser', 'DumpParser', 'FileParser', 'MailParser']
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dump Parser - Parse q:dump statements
|
|
3
|
+
|
|
4
|
+
Handles variable inspection and debugging.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List
|
|
8
|
+
from xml.etree import ElementTree as ET
|
|
9
|
+
from quantum.core.parsers.base import BaseTagParser
|
|
10
|
+
from quantum.core.features.dump.src import DumpNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DumpParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:dump statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Variable inspection
|
|
19
|
+
- Multiple output formats
|
|
20
|
+
- Depth control
|
|
21
|
+
- Conditional dumping
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def tag_names(self) -> List[str]:
|
|
26
|
+
return ['dump']
|
|
27
|
+
|
|
28
|
+
def parse(self, element: ET.Element) -> DumpNode:
|
|
29
|
+
"""
|
|
30
|
+
Parse q:dump statement.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
element: XML element for q:dump
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
DumpNode AST node
|
|
37
|
+
"""
|
|
38
|
+
var = self.get_attr(element, 'var', '')
|
|
39
|
+
label = self.get_attr(element, 'label', var)
|
|
40
|
+
format_type = self.get_attr(element, 'format', 'text')
|
|
41
|
+
depth = self.get_int_attr(element, 'depth', 3)
|
|
42
|
+
when = self.get_attr(element, 'when')
|
|
43
|
+
|
|
44
|
+
dump_node = DumpNode(
|
|
45
|
+
var=var,
|
|
46
|
+
label=label,
|
|
47
|
+
format=format_type,
|
|
48
|
+
depth=depth,
|
|
49
|
+
when=when
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return dump_node
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File Parser - Parse q:file statements
|
|
3
|
+
|
|
4
|
+
Handles file upload and deletion.
|
|
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 FileNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FileParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:file statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- File upload with conflict handling
|
|
19
|
+
- File deletion
|
|
20
|
+
- Result storage
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def tag_names(self) -> List[str]:
|
|
25
|
+
return ['file']
|
|
26
|
+
|
|
27
|
+
def parse(self, element: ET.Element) -> FileNode:
|
|
28
|
+
"""
|
|
29
|
+
Parse q:file statement.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
element: XML element for q:file
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
FileNode AST node
|
|
36
|
+
"""
|
|
37
|
+
action = self.get_attr(element, 'action', 'upload')
|
|
38
|
+
file_attr = self.get_attr(element, 'file')
|
|
39
|
+
|
|
40
|
+
if not file_attr:
|
|
41
|
+
raise ParserError("File requires 'file' attribute")
|
|
42
|
+
|
|
43
|
+
file_node = FileNode(action, file_attr)
|
|
44
|
+
file_node.destination = self.get_attr(element, 'destination')
|
|
45
|
+
file_node.name_conflict = self.get_attr(element, 'nameConflict', 'makeUnique')
|
|
46
|
+
file_node.result = self.get_attr(element, 'result')
|
|
47
|
+
|
|
48
|
+
return file_node
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Log Parser - Parse q:log statements
|
|
3
|
+
|
|
4
|
+
Handles structured logging.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List
|
|
8
|
+
from xml.etree import ElementTree as ET
|
|
9
|
+
from quantum.core.parsers.base import BaseTagParser
|
|
10
|
+
from quantum.core.features.logging.src import LogNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:log statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Log levels (debug, info, warn, error)
|
|
19
|
+
- Conditional logging
|
|
20
|
+
- Context data
|
|
21
|
+
- Correlation IDs
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def tag_names(self) -> List[str]:
|
|
26
|
+
return ['log']
|
|
27
|
+
|
|
28
|
+
def parse(self, element: ET.Element) -> LogNode:
|
|
29
|
+
"""
|
|
30
|
+
Parse q:log statement.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
element: XML element for q:log
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
LogNode AST node
|
|
37
|
+
"""
|
|
38
|
+
level = self.get_attr(element, 'level', 'info')
|
|
39
|
+
message = self.get_attr(element, 'message') or self.get_text(element)
|
|
40
|
+
|
|
41
|
+
log_node = LogNode(level=level, message=message)
|
|
42
|
+
log_node.when = self.get_attr(element, 'when')
|
|
43
|
+
log_node.context = self.get_attr(element, 'context')
|
|
44
|
+
log_node.correlation_id = self.get_attr(element, 'correlationId')
|
|
45
|
+
|
|
46
|
+
return log_node
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mail Parser - Parse q:mail statements
|
|
3
|
+
|
|
4
|
+
Handles email sending.
|
|
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 MailNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MailParser(BaseTagParser):
|
|
14
|
+
"""
|
|
15
|
+
Parser for q:mail statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- HTML and plain text emails
|
|
19
|
+
- CC, BCC, Reply-To
|
|
20
|
+
- Attachments
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def tag_names(self) -> List[str]:
|
|
25
|
+
return ['mail']
|
|
26
|
+
|
|
27
|
+
def parse(self, element: ET.Element) -> MailNode:
|
|
28
|
+
"""
|
|
29
|
+
Parse q:mail statement.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
element: XML element for q:mail
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
MailNode AST node
|
|
36
|
+
"""
|
|
37
|
+
to = self.get_attr(element, 'to')
|
|
38
|
+
subject = self.get_attr(element, 'subject')
|
|
39
|
+
|
|
40
|
+
if not to:
|
|
41
|
+
raise ParserError("Mail requires 'to' attribute")
|
|
42
|
+
if not subject:
|
|
43
|
+
raise ParserError("Mail requires 'subject' attribute")
|
|
44
|
+
|
|
45
|
+
mail_node = MailNode(to, subject)
|
|
46
|
+
|
|
47
|
+
# Get body from content or attribute
|
|
48
|
+
mail_node.body = self.get_text(element) or self.get_attr(element, 'body', '')
|
|
49
|
+
|
|
50
|
+
# Optional attributes
|
|
51
|
+
mail_node.from_addr = self.get_attr(element, 'from')
|
|
52
|
+
mail_node.cc = self.get_attr(element, 'cc')
|
|
53
|
+
mail_node.bcc = self.get_attr(element, 'bcc')
|
|
54
|
+
mail_node.reply_to = self.get_attr(element, 'replyTo')
|
|
55
|
+
mail_node.type = self.get_attr(element, 'type', 'html')
|
|
56
|
+
|
|
57
|
+
# Parse attachments
|
|
58
|
+
for child in element:
|
|
59
|
+
child_type = self.get_element_name(child)
|
|
60
|
+
if child_type == 'attachment':
|
|
61
|
+
attachment = self.get_attr(child, 'file')
|
|
62
|
+
if attachment:
|
|
63
|
+
mail_node.attachments.append(attachment)
|
|
64
|
+
|
|
65
|
+
return mail_node
|
quantum/core/tiers.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Support tiers for Quantum tags.
|
|
3
|
+
|
|
4
|
+
PRODUCTION_READINESS.md Fase 0. "Production-ready" is meaningful only relative
|
|
5
|
+
to a defined surface, so the surface is defined here, in code, and a tag
|
|
6
|
+
outside the supported set announces itself the first time it is used instead
|
|
7
|
+
of pretending to be first-class.
|
|
8
|
+
|
|
9
|
+
The lists mirror SUPPORT_TIERS.md. When they disagree, this file is the one the
|
|
10
|
+
engine actually enforces, so keep it in step.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("quantum.tiers")
|
|
16
|
+
|
|
17
|
+
# Documented, tested end-to-end, stable. A break here is a critical bug.
|
|
18
|
+
CORE = frozenset({
|
|
19
|
+
"component", "param", "return", "set", "if", "loop", "function",
|
|
20
|
+
"query", "action", "redirect", "flash", "invoke", "data",
|
|
21
|
+
"import", "slot",
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
# The reason the project exists. Same contract as Core is the goal; some of it
|
|
25
|
+
# is still being validated end-to-end (PRODUCTION_READINESS.md Fase 4).
|
|
26
|
+
DIFERENCIAL = frozenset({"llm", "knowledge", "agent", "team"})
|
|
27
|
+
|
|
28
|
+
# Kept and registered, but no stability promise and not in the README. Using
|
|
29
|
+
# one is fine for experiments; relying on one in production is not supported.
|
|
30
|
+
EXPERIMENTAL = frozenset({
|
|
31
|
+
"job", "schedule", "thread",
|
|
32
|
+
"message", "queue", "subscribe", "messageAck", "messageNack",
|
|
33
|
+
"websocket", "websocket-send", "websocket-close",
|
|
34
|
+
"mail", "file", "log", "dump",
|
|
35
|
+
"python", "pyclass", "pyimport", "class", "decorator", "pydecorator",
|
|
36
|
+
"dispatchEvent", "transaction",
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
SUPPORTED = CORE | DIFERENCIAL
|
|
40
|
+
|
|
41
|
+
# Authentication is Core too (decision D4, 2026-09-10), but it is not a tag:
|
|
42
|
+
# it is the require_auth / require_role attributes of q:component plus the
|
|
43
|
+
# session scope. Listed here so the surface is complete in one place.
|
|
44
|
+
CORE_ATTRIBUTES = frozenset({"require_auth", "require_role"})
|
|
45
|
+
|
|
46
|
+
# Application types, by tier. A q:application's type decides which engine
|
|
47
|
+
# runs it, so the tier of the type is the tier of the whole app.
|
|
48
|
+
#
|
|
49
|
+
# LABORATORIO (decision D1/D2): the 2D game engine (qg:, including the Godot
|
|
50
|
+
# codegen) stays in the repository on purpose — games press on the language
|
|
51
|
+
# and surface features and bugs the core needs — but it carries no stability
|
|
52
|
+
# promise and is not part of the pitch. Its tests run in the main suite, so a
|
|
53
|
+
# core change that breaks a game shows up in CI.
|
|
54
|
+
LAB_APP_TYPES = frozenset({"game"})
|
|
55
|
+
EXPERIMENTAL_APP_TYPES = frozenset({"terminal", "ui", "testing"})
|
|
56
|
+
|
|
57
|
+
# Removed in 0.11 (APP-1), because they never worked: `quantum run` on a
|
|
58
|
+
# type="html" application failed at once (gap G17), and the type="api" server —
|
|
59
|
+
# also behind "microservices" — never executed a route's body, serving the
|
|
60
|
+
# literal text of the first q:return (gap G18). A web app is pages in
|
|
61
|
+
# components/ served by `quantum start`. "html" was also the default when
|
|
62
|
+
# type= was left out.
|
|
63
|
+
REMOVED_APP_TYPES = frozenset({"html", "api", "microservices"})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def removed_app_type_message(app_type: str, declared: bool) -> str:
|
|
67
|
+
what = f'type="{app_type}"' if declared else 'with no type= (it meant type="html")'
|
|
68
|
+
return (
|
|
69
|
+
f"<q:application> {what} was removed in Quantum 0.11: it never ran its "
|
|
70
|
+
f"routes. Build a web app as pages in components/ (components/index.q is "
|
|
71
|
+
f"/) and run `quantum start`. See "
|
|
72
|
+
f"https://danielgregorio.github.io/quantum/guide/getting-started")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def tier_of(tag_name: str) -> str:
|
|
76
|
+
"""'core' | 'diferencial' | 'experimental' | 'unknown'."""
|
|
77
|
+
if tag_name in CORE:
|
|
78
|
+
return "core"
|
|
79
|
+
if tag_name in DIFERENCIAL:
|
|
80
|
+
return "diferencial"
|
|
81
|
+
if tag_name in EXPERIMENTAL:
|
|
82
|
+
return "experimental"
|
|
83
|
+
return "unknown"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Warn once per tag per process — a q:loop over 1,000 rows must not emit 1,000
|
|
87
|
+
# lines, the same discipline as expression_diagnostics.
|
|
88
|
+
_warned: set = set()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def warn_if_unsupported(tag_name: str) -> None:
|
|
92
|
+
"""Emit a one-time warning when an experimental tag is used.
|
|
93
|
+
|
|
94
|
+
Not an error: experimental tags still run. The point is that an operator
|
|
95
|
+
reading the log sees, once, that their app leans on something the project
|
|
96
|
+
does not promise to keep working — the audit found apps built entirely on
|
|
97
|
+
tags that parse and then explode, with nothing warning anyone.
|
|
98
|
+
"""
|
|
99
|
+
tier = tier_of(tag_name)
|
|
100
|
+
if tier in ("core", "diferencial", "unknown"):
|
|
101
|
+
# 'unknown' is handled by the parser's own error path, not here.
|
|
102
|
+
return
|
|
103
|
+
if tag_name in _warned:
|
|
104
|
+
return
|
|
105
|
+
_warned.add(tag_name)
|
|
106
|
+
logger.warning(
|
|
107
|
+
"q:%s is EXPERIMENTAL — not covered by the production support surface "
|
|
108
|
+
"(SUPPORT_TIERS.md). It may change or break without notice; do not rely "
|
|
109
|
+
"on it in production.", tag_name
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def app_tier_of(app_type: str) -> str:
|
|
114
|
+
"""'laboratorio' | 'experimental' | 'supported' for a q:application type."""
|
|
115
|
+
if app_type in LAB_APP_TYPES:
|
|
116
|
+
return "laboratorio"
|
|
117
|
+
if app_type in EXPERIMENTAL_APP_TYPES:
|
|
118
|
+
return "experimental"
|
|
119
|
+
return "supported"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def warn_app_type(app_type: str) -> None:
|
|
123
|
+
"""One-time warning when an application of a non-supported type runs."""
|
|
124
|
+
tier = app_tier_of(app_type)
|
|
125
|
+
key = f"application:{app_type}"
|
|
126
|
+
if tier == "supported" or key in _warned:
|
|
127
|
+
return
|
|
128
|
+
_warned.add(key)
|
|
129
|
+
if tier == "laboratorio":
|
|
130
|
+
logger.warning(
|
|
131
|
+
"q:application type=%r is LABORATÓRIO — it exists to press on the "
|
|
132
|
+
"language, with no stability promise (SUPPORT_TIERS.md). It may "
|
|
133
|
+
"change or break without notice.", app_type
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
logger.warning(
|
|
137
|
+
"q:application type=%r is EXPERIMENTAL — not covered by the "
|
|
138
|
+
"supported surface (SUPPORT_TIERS.md). It may change or break "
|
|
139
|
+
"without notice; do not rely on it in production.", app_type
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# UI-8: React Native translates the logic to JavaScript on its own — the
|
|
144
|
+
# opposite of "one runtime, many renderers" — and phones are out of 1.0. It
|
|
145
|
+
# stays to press on the language, like the games.
|
|
146
|
+
LAB_UI_TARGETS = frozenset({"mobile", "react-native"})
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def warn_ui_target(target: str) -> None:
|
|
150
|
+
"""One-time warning when a LABORATÓRIO UI target is built."""
|
|
151
|
+
key = f"ui-target:{target}"
|
|
152
|
+
if target not in LAB_UI_TARGETS or key in _warned:
|
|
153
|
+
return
|
|
154
|
+
_warned.add(key)
|
|
155
|
+
logger.warning(
|
|
156
|
+
"quantum run --target %s is LABORATÓRIO — it translates q:set and "
|
|
157
|
+
"q:function to JavaScript with no stability promise (SUPPORT_TIERS.md). "
|
|
158
|
+
"It may change or break without notice.", target
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def reset_warnings() -> None:
|
|
163
|
+
"""For tests, and for a reloading server."""
|
|
164
|
+
_warned.clear()
|