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,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging feature for Quantum Language
|
|
3
|
+
|
|
4
|
+
Provides structured logging with multiple severity levels,
|
|
5
|
+
conditional output, and external service integration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .ast_node import LogNode
|
|
9
|
+
from .parser import parse_log
|
|
10
|
+
from .runtime import LoggingService
|
|
11
|
+
|
|
12
|
+
__all__ = ['LogNode', 'parse_log', 'LoggingService']
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST Node for q:log - Structured Logging
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from quantum.core.ast_nodes import QuantumNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LogNode(QuantumNode):
|
|
10
|
+
"""
|
|
11
|
+
Represents a <q:log> - Structured logging statement
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
<q:log level="info" message="User {userName} logged in" context="{user}" />
|
|
15
|
+
<q:log level="error" message="API failed" when="{!api_result.success}" />
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
VALID_LEVELS = ['trace', 'debug', 'info', 'warning', 'error', 'critical']
|
|
19
|
+
|
|
20
|
+
# Spellings people actually write, mapped to the level they mean. `warn`
|
|
21
|
+
# is what Python's own logging, JavaScript's console and most log
|
|
22
|
+
# libraries call it, and two examples in this repository use it — one of
|
|
23
|
+
# them the only q:websocket example there is, which therefore did not
|
|
24
|
+
# parse. Rejecting a word the whole industry uses is not strictness, it
|
|
25
|
+
# is a papercut.
|
|
26
|
+
LEVEL_ALIASES = {
|
|
27
|
+
'warn': 'warning',
|
|
28
|
+
'fatal': 'critical',
|
|
29
|
+
'err': 'error',
|
|
30
|
+
'verbose': 'trace',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
level: str,
|
|
36
|
+
message: str,
|
|
37
|
+
context: Optional[str] = None,
|
|
38
|
+
when: Optional[str] = None,
|
|
39
|
+
provider: Optional[str] = None,
|
|
40
|
+
async_mode: bool = True,
|
|
41
|
+
correlation_id: Optional[str] = None
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Initialize a LogNode
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
level: Log severity level (trace, debug, info, warning, error, critical)
|
|
48
|
+
message: Log message with databinding support
|
|
49
|
+
context: Optional structured data (JSON expression)
|
|
50
|
+
when: Optional conditional expression
|
|
51
|
+
provider: Optional output destination (Phase 2)
|
|
52
|
+
async_mode: Async logging mode (Phase 2)
|
|
53
|
+
correlation_id: Optional request tracking ID (Phase 2)
|
|
54
|
+
"""
|
|
55
|
+
# Validate level
|
|
56
|
+
if not level:
|
|
57
|
+
raise ValueError("Log requires 'level' attribute")
|
|
58
|
+
|
|
59
|
+
level = self.LEVEL_ALIASES.get(str(level).strip().lower(), level)
|
|
60
|
+
|
|
61
|
+
if level not in self.VALID_LEVELS:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"Invalid log level '{level}'. Must be one of: {', '.join(self.VALID_LEVELS)}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Validate message
|
|
67
|
+
if message is None:
|
|
68
|
+
raise ValueError("Log requires 'message' attribute")
|
|
69
|
+
|
|
70
|
+
self.level = level
|
|
71
|
+
self.message = message
|
|
72
|
+
self.context = context
|
|
73
|
+
self.when = when
|
|
74
|
+
self.provider = provider
|
|
75
|
+
self.async_mode = async_mode
|
|
76
|
+
self.correlation_id = correlation_id
|
|
77
|
+
|
|
78
|
+
def __repr__(self):
|
|
79
|
+
attrs = [f"level={self.level!r}", f"message={self.message!r}"]
|
|
80
|
+
if self.context:
|
|
81
|
+
attrs.append(f"context={self.context!r}")
|
|
82
|
+
if self.when:
|
|
83
|
+
attrs.append(f"when={self.when!r}")
|
|
84
|
+
return f"LogNode({', '.join(attrs)})"
|
|
85
|
+
|
|
86
|
+
def to_dict(self):
|
|
87
|
+
"""Convert node to dictionary for debugging/serialization"""
|
|
88
|
+
return {
|
|
89
|
+
"type": "log",
|
|
90
|
+
"level": self.level,
|
|
91
|
+
"message": self.message[:50] + "..." if self.message and len(self.message) > 50 else self.message,
|
|
92
|
+
"context": self.context,
|
|
93
|
+
"when": self.when,
|
|
94
|
+
"provider": self.provider,
|
|
95
|
+
"async": self.async_mode,
|
|
96
|
+
"correlation_id": self.correlation_id
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def validate(self):
|
|
100
|
+
"""Validate the node and return list of errors"""
|
|
101
|
+
errors = []
|
|
102
|
+
|
|
103
|
+
if not self.level:
|
|
104
|
+
errors.append("Log requires 'level' attribute")
|
|
105
|
+
elif self.level not in self.VALID_LEVELS:
|
|
106
|
+
errors.append(f"Invalid log level '{self.level}'. Must be one of: {', '.join(self.VALID_LEVELS)}")
|
|
107
|
+
|
|
108
|
+
if not self.message:
|
|
109
|
+
errors.append("Log requires 'message' attribute")
|
|
110
|
+
|
|
111
|
+
return errors
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parser for q:log - Structured Logging
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from .ast_node import LogNode
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_log(log_element: ET.Element) -> LogNode:
|
|
11
|
+
"""
|
|
12
|
+
Parse a <q:log> element into a LogNode
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
log_element: The XML element representing the log statement
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
LogNode instance
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: If required attributes are missing or invalid
|
|
22
|
+
"""
|
|
23
|
+
# Get required attributes
|
|
24
|
+
level = log_element.get('level')
|
|
25
|
+
message = log_element.get('message')
|
|
26
|
+
|
|
27
|
+
# Get optional attributes
|
|
28
|
+
context = log_element.get('context')
|
|
29
|
+
when = log_element.get('when')
|
|
30
|
+
provider = log_element.get('provider')
|
|
31
|
+
async_mode_str = log_element.get('async', 'true')
|
|
32
|
+
correlation_id = log_element.get('correlation_id')
|
|
33
|
+
|
|
34
|
+
# Parse async mode
|
|
35
|
+
async_mode = async_mode_str.lower() == 'true' if async_mode_str else True
|
|
36
|
+
|
|
37
|
+
# Validate no child elements (q:log should be self-closing)
|
|
38
|
+
if len(log_element) > 0:
|
|
39
|
+
raise ValueError("Log tag must be self-closing")
|
|
40
|
+
|
|
41
|
+
# Create and return LogNode (validation happens in __init__)
|
|
42
|
+
return LogNode(
|
|
43
|
+
level=level,
|
|
44
|
+
message=message,
|
|
45
|
+
context=context,
|
|
46
|
+
when=when,
|
|
47
|
+
provider=provider,
|
|
48
|
+
async_mode=async_mode,
|
|
49
|
+
correlation_id=correlation_id
|
|
50
|
+
)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Runtime service for q:log - Structured Logging
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LoggingService:
|
|
13
|
+
"""
|
|
14
|
+
Service to handle structured logging with multiple severity levels
|
|
15
|
+
|
|
16
|
+
Phase 1 Features:
|
|
17
|
+
- Console and file output
|
|
18
|
+
- JSON structured format
|
|
19
|
+
- Multiple log levels
|
|
20
|
+
- Conditional logging
|
|
21
|
+
- Context data
|
|
22
|
+
|
|
23
|
+
Phase 2 Features (TODO):
|
|
24
|
+
- Loguru integration
|
|
25
|
+
- External services (Sentry, Datadog)
|
|
26
|
+
- Async logging
|
|
27
|
+
- Log rotation
|
|
28
|
+
- Correlation IDs
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# Map Quantum log levels to Python logging levels
|
|
32
|
+
LEVEL_MAP = {
|
|
33
|
+
'trace': logging.DEBUG - 5, # Custom level below DEBUG
|
|
34
|
+
'debug': logging.DEBUG,
|
|
35
|
+
'info': logging.INFO,
|
|
36
|
+
'warning': logging.WARNING,
|
|
37
|
+
'error': logging.ERROR,
|
|
38
|
+
'critical': logging.CRITICAL
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_instance: Optional['LoggingService'] = None
|
|
42
|
+
_initialized: bool = False
|
|
43
|
+
|
|
44
|
+
def __new__(cls):
|
|
45
|
+
"""Singleton pattern to ensure one logger instance"""
|
|
46
|
+
if cls._instance is None:
|
|
47
|
+
cls._instance = super().__new__(cls)
|
|
48
|
+
return cls._instance
|
|
49
|
+
|
|
50
|
+
def __init__(self):
|
|
51
|
+
"""Initialize the logging service"""
|
|
52
|
+
if not LoggingService._initialized:
|
|
53
|
+
self._setup_logging()
|
|
54
|
+
LoggingService._initialized = True
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def configure_from_external(cls, logger):
|
|
58
|
+
"""
|
|
59
|
+
Inject an externally configured logger so q:log tags reuse
|
|
60
|
+
the same handlers without duplication.
|
|
61
|
+
|
|
62
|
+
Called by logging_setup.setup_logging() after configuring
|
|
63
|
+
the 'quantum' root logger.
|
|
64
|
+
"""
|
|
65
|
+
instance = cls.__new__(cls)
|
|
66
|
+
instance.logger = logger
|
|
67
|
+
cls._instance = instance
|
|
68
|
+
cls._initialized = True
|
|
69
|
+
|
|
70
|
+
def _setup_logging(self):
|
|
71
|
+
"""Setup logging configuration"""
|
|
72
|
+
# Create logger
|
|
73
|
+
self.logger = logging.getLogger('quantum')
|
|
74
|
+
self.logger.setLevel(logging.DEBUG) # Capture all levels
|
|
75
|
+
|
|
76
|
+
# Prevent duplicate handlers
|
|
77
|
+
if self.logger.handlers:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# Console handler - formatted output for development
|
|
81
|
+
console_handler = logging.StreamHandler()
|
|
82
|
+
console_handler.setLevel(logging.DEBUG)
|
|
83
|
+
console_formatter = logging.Formatter(
|
|
84
|
+
'%(asctime)s [%(levelname)s] %(message)s',
|
|
85
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
86
|
+
)
|
|
87
|
+
console_handler.setFormatter(console_formatter)
|
|
88
|
+
self.logger.addHandler(console_handler)
|
|
89
|
+
|
|
90
|
+
# File handler - JSON format for production (Phase 1: basic file logging)
|
|
91
|
+
# Phase 2 will add rotation with Loguru
|
|
92
|
+
log_dir = 'logs'
|
|
93
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
94
|
+
log_file = os.path.join(log_dir, f'quantum_{datetime.now().strftime("%Y%m%d")}.log')
|
|
95
|
+
|
|
96
|
+
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
97
|
+
file_handler.setLevel(logging.DEBUG)
|
|
98
|
+
# For now, use same format. Phase 2 will use JSON formatter
|
|
99
|
+
file_handler.setFormatter(console_formatter)
|
|
100
|
+
self.logger.addHandler(file_handler)
|
|
101
|
+
|
|
102
|
+
# Custom TRACE level
|
|
103
|
+
logging.addLevelName(self.LEVEL_MAP['trace'], 'TRACE')
|
|
104
|
+
|
|
105
|
+
def log(
|
|
106
|
+
self,
|
|
107
|
+
level: str,
|
|
108
|
+
message: str,
|
|
109
|
+
context: Optional[Dict[str, Any]] = None,
|
|
110
|
+
correlation_id: Optional[str] = None
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""
|
|
113
|
+
Write a log entry
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
level: Log severity level
|
|
117
|
+
message: Log message (already resolved with databinding)
|
|
118
|
+
context: Optional structured context data
|
|
119
|
+
correlation_id: Optional request tracking ID
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Result dictionary with success status
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
# Get Python logging level
|
|
126
|
+
py_level = self.LEVEL_MAP.get(level, logging.INFO)
|
|
127
|
+
|
|
128
|
+
# Build log entry
|
|
129
|
+
log_data = {
|
|
130
|
+
'timestamp': datetime.now().isoformat(),
|
|
131
|
+
'level': level,
|
|
132
|
+
'message': message
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if context:
|
|
136
|
+
log_data['context'] = context
|
|
137
|
+
|
|
138
|
+
if correlation_id:
|
|
139
|
+
log_data['correlation_id'] = correlation_id
|
|
140
|
+
|
|
141
|
+
# Log to Python logger
|
|
142
|
+
# For Phase 1, just log the message. Phase 2 will log full JSON
|
|
143
|
+
extra_info = []
|
|
144
|
+
if context:
|
|
145
|
+
extra_info.append(f"context={json.dumps(context)}")
|
|
146
|
+
if correlation_id:
|
|
147
|
+
extra_info.append(f"correlation_id={correlation_id}")
|
|
148
|
+
|
|
149
|
+
full_message = message
|
|
150
|
+
if extra_info:
|
|
151
|
+
full_message = f"{message} | {' | '.join(extra_info)}"
|
|
152
|
+
|
|
153
|
+
self.logger.log(py_level, full_message)
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
'success': True,
|
|
157
|
+
'timestamp': log_data['timestamp'],
|
|
158
|
+
'level': level,
|
|
159
|
+
'message': message,
|
|
160
|
+
'destination': 'file+console'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
# Logging should never crash the application
|
|
165
|
+
# Silently capture errors and return failure
|
|
166
|
+
return {
|
|
167
|
+
'success': False,
|
|
168
|
+
'error': str(e)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
def should_log(self, condition: Any) -> bool:
|
|
172
|
+
"""
|
|
173
|
+
Evaluate if logging should occur based on 'when' condition
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
condition: Evaluated condition value
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
True if should log, False otherwise
|
|
180
|
+
"""
|
|
181
|
+
# If no condition, always log
|
|
182
|
+
if condition is None:
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
# Evaluate boolean condition
|
|
186
|
+
if isinstance(condition, bool):
|
|
187
|
+
return condition
|
|
188
|
+
|
|
189
|
+
# Truthy evaluation for other types
|
|
190
|
+
return bool(condition)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST Node for Loops (q:loop)
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, List
|
|
6
|
+
|
|
7
|
+
# Import base class
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from quantum.core.ast_nodes import QuantumNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LoopNode(QuantumNode):
|
|
14
|
+
"""Represents a <q:loop> with various types"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, loop_type: str, var_name: str):
|
|
17
|
+
self.loop_type = loop_type # range, array, list, object, query, while
|
|
18
|
+
self.var_name = var_name # variable name for current item
|
|
19
|
+
self.index_name = None # optional index variable
|
|
20
|
+
self.items = None # data source expression
|
|
21
|
+
self.condition = None # for while loops
|
|
22
|
+
self.from_value = None # for range loops
|
|
23
|
+
self.to_value = None # for range loops
|
|
24
|
+
self.step_value = 1 # for range loops
|
|
25
|
+
self.delimiter = "," # for list loops
|
|
26
|
+
self.body: List[QuantumNode] = [] # statements inside loop
|
|
27
|
+
|
|
28
|
+
def add_statement(self, statement: QuantumNode):
|
|
29
|
+
self.body.append(statement)
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
32
|
+
return {
|
|
33
|
+
"type": "loop",
|
|
34
|
+
"loop_type": self.loop_type,
|
|
35
|
+
"var_name": self.var_name,
|
|
36
|
+
"index_name": self.index_name,
|
|
37
|
+
"from_value": self.from_value,
|
|
38
|
+
"to_value": self.to_value,
|
|
39
|
+
"step_value": self.step_value,
|
|
40
|
+
"body_statements": len(self.body)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def validate(self) -> List[str]:
|
|
44
|
+
errors = []
|
|
45
|
+
if not self.var_name:
|
|
46
|
+
errors.append("Loop variable name is required")
|
|
47
|
+
|
|
48
|
+
if self.loop_type == 'range':
|
|
49
|
+
if not self.from_value:
|
|
50
|
+
errors.append("Range loop requires 'from' attribute")
|
|
51
|
+
if not self.to_value:
|
|
52
|
+
errors.append("Range loop requires 'to' attribute")
|
|
53
|
+
elif self.loop_type in ['array', 'list', 'object']:
|
|
54
|
+
if not self.items:
|
|
55
|
+
errors.append(f"{self.loop_type.title()} loop requires 'items' attribute")
|
|
56
|
+
|
|
57
|
+
for statement in self.body:
|
|
58
|
+
errors.extend(statement.validate())
|
|
59
|
+
|
|
60
|
+
return errors
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Query Validators — re-export of the canonical implementation.
|
|
3
|
+
|
|
4
|
+
This module used to hold a full 290-line COPY of QueryValidator. Nothing
|
|
5
|
+
imported it, so it quietly drifted out of step with the module the runtime
|
|
6
|
+
actually uses (quantum/runtime/query_validators.py) — and by the time the
|
|
7
|
+
audit looked, the two copies of a SECURITY validator disagreed with each
|
|
8
|
+
other. A stale duplicate of a validator is worse than no duplicate: whoever
|
|
9
|
+
reads this one believes they are reading what runs.
|
|
10
|
+
|
|
11
|
+
It re-exports instead of copying, so the two cannot diverge again. Import
|
|
12
|
+
from quantum.runtime.query_validators directly in new code.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from quantum.runtime.query_validators import ( # noqa: F401
|
|
16
|
+
QueryValidationError,
|
|
17
|
+
QueryValidator,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = ["QueryValidationError", "QueryValidator"]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST Node for State Management (q:set)
|
|
3
|
+
|
|
4
|
+
This module contains the SetNode class, which represents the <q:set> tag
|
|
5
|
+
for state management in Quantum Language.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any, List, Optional
|
|
9
|
+
|
|
10
|
+
# Import base class from parent AST nodes
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from quantum.core.ast_nodes import QuantumNode
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SetNode(QuantumNode):
|
|
17
|
+
"""Represents a <q:set> - State Management"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, name: str):
|
|
20
|
+
# Obrigatórios
|
|
21
|
+
self.name = name
|
|
22
|
+
|
|
23
|
+
# Tipo e valor
|
|
24
|
+
self.type = "string"
|
|
25
|
+
self.value = None
|
|
26
|
+
self.default = None
|
|
27
|
+
|
|
28
|
+
# Validação
|
|
29
|
+
self.required = False
|
|
30
|
+
self.nullable = True
|
|
31
|
+
self.validate_rule = None # Renomeado de 'validate' para não conflitar com o método
|
|
32
|
+
self.pattern = None
|
|
33
|
+
self.mask = None
|
|
34
|
+
self.range = None
|
|
35
|
+
self.enum = None
|
|
36
|
+
self.unique = None
|
|
37
|
+
self.min = None
|
|
38
|
+
self.max = None
|
|
39
|
+
self.minlength = None
|
|
40
|
+
self.maxlength = None
|
|
41
|
+
|
|
42
|
+
# Comportamento
|
|
43
|
+
self.scope = "local"
|
|
44
|
+
self.operation = "assign"
|
|
45
|
+
self.step = 1
|
|
46
|
+
|
|
47
|
+
# Para operações em collections
|
|
48
|
+
self.index = None
|
|
49
|
+
self.key = None
|
|
50
|
+
self.source = None
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
result = {
|
|
54
|
+
"type": "set",
|
|
55
|
+
"name": self.name,
|
|
56
|
+
"value_type": self.type,
|
|
57
|
+
"value": self.value,
|
|
58
|
+
"default": self.default,
|
|
59
|
+
"scope": self.scope,
|
|
60
|
+
"operation": self.operation,
|
|
61
|
+
"required": self.required,
|
|
62
|
+
"nullable": self.nullable,
|
|
63
|
+
"validation": {
|
|
64
|
+
"validate": self.validate_rule,
|
|
65
|
+
"pattern": self.pattern,
|
|
66
|
+
"mask": self.mask,
|
|
67
|
+
"range": self.range,
|
|
68
|
+
"enum": self.enum,
|
|
69
|
+
"unique": self.unique,
|
|
70
|
+
"min": self.min,
|
|
71
|
+
"max": self.max,
|
|
72
|
+
"minlength": self.minlength,
|
|
73
|
+
"maxlength": self.maxlength
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def validate(self) -> List[str]:
|
|
79
|
+
errors = []
|
|
80
|
+
|
|
81
|
+
if not self.name:
|
|
82
|
+
errors.append("Set variable name is required")
|
|
83
|
+
|
|
84
|
+
# Validar tipo
|
|
85
|
+
# 'integer' was missing here even though SetExecutor._convert_to_type()
|
|
86
|
+
# already handles it (target_type in ("integer", "number")), and other
|
|
87
|
+
# AST nodes like AgentToolParamNode accept it — this rejected any
|
|
88
|
+
# q:set/q:param using type="integer" (test-query-loop.q,
|
|
89
|
+
# test-query-params.q, and every qg: sprite param in game_engine_2d).
|
|
90
|
+
valid_types = ['string', 'number', 'integer', 'decimal', 'boolean', 'date', 'datetime',
|
|
91
|
+
'array', 'object', 'json', 'binary', 'null']
|
|
92
|
+
if self.type not in valid_types:
|
|
93
|
+
errors.append(f"Invalid type: {self.type}. Must be one of {valid_types}")
|
|
94
|
+
|
|
95
|
+
# Validar operação
|
|
96
|
+
valid_operations = ['assign', 'increment', 'decrement', 'add', 'multiply',
|
|
97
|
+
'append', 'prepend', 'remove', 'removeAt', 'clear', 'sort',
|
|
98
|
+
'reverse', 'unique', 'merge', 'setProperty', 'deleteProperty',
|
|
99
|
+
'clone', 'uppercase', 'lowercase', 'trim', 'format']
|
|
100
|
+
if self.operation not in valid_operations:
|
|
101
|
+
errors.append(f"Invalid operation: {self.operation}")
|
|
102
|
+
|
|
103
|
+
# Validar valor obrigatório para assign
|
|
104
|
+
if self.operation == "assign" and not self.value and not self.default and self.required:
|
|
105
|
+
errors.append("Set value or default is required when required=true")
|
|
106
|
+
|
|
107
|
+
# Validar enum
|
|
108
|
+
if self.enum and self.value:
|
|
109
|
+
enum_values = [v.strip() for v in self.enum.split(',')]
|
|
110
|
+
if self.value not in enum_values:
|
|
111
|
+
errors.append(f"Value '{self.value}' not in enum: {enum_values}")
|
|
112
|
+
|
|
113
|
+
# Validar minlength/maxlength
|
|
114
|
+
if self.type == "string" and self.value:
|
|
115
|
+
if self.minlength and len(str(self.value)) < int(self.minlength):
|
|
116
|
+
errors.append(f"Value length {len(str(self.value))} is less than minlength {self.minlength}")
|
|
117
|
+
if self.maxlength and len(str(self.value)) > int(self.maxlength):
|
|
118
|
+
errors.append(f"Value length {len(str(self.value))} exceeds maxlength {self.maxlength}")
|
|
119
|
+
|
|
120
|
+
return errors
|
|
121
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Terminal Engine - AST nodes and parser for the qt: namespace.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .ast_nodes import (
|
|
6
|
+
ScreenNode, PanelNode, LayoutNode, TableNode, ColumnNode,
|
|
7
|
+
TerminalInputNode, ButtonNode, MenuNode, OptionNode,
|
|
8
|
+
TextNode_Terminal, ProgressNode, TreeNode, TabsNode, TabNode,
|
|
9
|
+
LogNode_Terminal, HeaderNode_Terminal, FooterNode, StatusNode,
|
|
10
|
+
KeybindingNode, TimerNode_Terminal, ServiceNode, CssNode,
|
|
11
|
+
OnEventNode_Terminal, RawCodeNode_Terminal,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'ScreenNode', 'PanelNode', 'LayoutNode', 'TableNode', 'ColumnNode',
|
|
16
|
+
'TerminalInputNode', 'ButtonNode', 'MenuNode', 'OptionNode',
|
|
17
|
+
'TextNode_Terminal', 'ProgressNode', 'TreeNode', 'TabsNode', 'TabNode',
|
|
18
|
+
'LogNode_Terminal', 'HeaderNode_Terminal', 'FooterNode', 'StatusNode',
|
|
19
|
+
'KeybindingNode', 'TimerNode_Terminal', 'ServiceNode', 'CssNode',
|
|
20
|
+
'OnEventNode_Terminal', 'RawCodeNode_Terminal',
|
|
21
|
+
]
|