quantum-framework 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Python Bridge
|
|
3
|
+
|
|
4
|
+
Provides the magical 'q' object that connects Python code to the Quantum context.
|
|
5
|
+
This bridge allows seamless interaction between embedded Python code and the
|
|
6
|
+
Quantum runtime environment.
|
|
7
|
+
|
|
8
|
+
Usage in q:python blocks:
|
|
9
|
+
<q:python>
|
|
10
|
+
# Access variables
|
|
11
|
+
user_id = q.user_id
|
|
12
|
+
q.result = calculate(user_id)
|
|
13
|
+
|
|
14
|
+
# Database queries
|
|
15
|
+
users = q.query('SELECT * FROM users WHERE active = :active', active=True)
|
|
16
|
+
|
|
17
|
+
# HTTP requests
|
|
18
|
+
response = q.get('https://api.example.com/data')
|
|
19
|
+
|
|
20
|
+
# Message queue
|
|
21
|
+
q.publish('orders.created', order_data)
|
|
22
|
+
|
|
23
|
+
# Logging
|
|
24
|
+
q.info('Processing complete')
|
|
25
|
+
</q:python>
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import json
|
|
29
|
+
import time
|
|
30
|
+
import threading
|
|
31
|
+
from typing import Any, Dict, Optional, List, Callable, Union
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class QuantumBridgeError(Exception):
|
|
36
|
+
"""Base exception for bridge errors"""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class QuantumBridge:
|
|
41
|
+
"""
|
|
42
|
+
The magical 'q' object - bridge between Python and Quantum.
|
|
43
|
+
|
|
44
|
+
This object is automatically available in all q:python blocks and provides
|
|
45
|
+
access to:
|
|
46
|
+
- Component variables (read/write)
|
|
47
|
+
- Database queries
|
|
48
|
+
- HTTP requests
|
|
49
|
+
- Message queue operations
|
|
50
|
+
- Job dispatching
|
|
51
|
+
- Caching
|
|
52
|
+
- Session management
|
|
53
|
+
- Logging
|
|
54
|
+
- And more...
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
context: Any,
|
|
60
|
+
services: Optional[Dict[str, Any]] = None,
|
|
61
|
+
request: Optional[Any] = None
|
|
62
|
+
):
|
|
63
|
+
"""
|
|
64
|
+
Initialize the bridge.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
context: The Quantum execution context
|
|
68
|
+
services: Optional dict of services (db, cache, broker, etc.)
|
|
69
|
+
request: Optional HTTP request object
|
|
70
|
+
"""
|
|
71
|
+
object.__setattr__(self, '_context', context)
|
|
72
|
+
object.__setattr__(self, '_services', services or {})
|
|
73
|
+
object.__setattr__(self, '_request', request)
|
|
74
|
+
object.__setattr__(self, '_exports', {})
|
|
75
|
+
object.__setattr__(self, '_local', threading.local())
|
|
76
|
+
|
|
77
|
+
# =========================================================================
|
|
78
|
+
# Variable Access (q.variable / q.variable = value)
|
|
79
|
+
# =========================================================================
|
|
80
|
+
|
|
81
|
+
def __getattr__(self, name: str) -> Any:
|
|
82
|
+
"""
|
|
83
|
+
Get a variable from the Quantum context.
|
|
84
|
+
|
|
85
|
+
Usage:
|
|
86
|
+
user_id = q.user_id
|
|
87
|
+
items = q.cart_items
|
|
88
|
+
"""
|
|
89
|
+
# Check exports first
|
|
90
|
+
exports = object.__getattribute__(self, '_exports')
|
|
91
|
+
if name in exports:
|
|
92
|
+
return exports[name]
|
|
93
|
+
|
|
94
|
+
# Then check context
|
|
95
|
+
context = object.__getattribute__(self, '_context')
|
|
96
|
+
if hasattr(context, 'get'):
|
|
97
|
+
return context.get(name)
|
|
98
|
+
elif hasattr(context, 'variables'):
|
|
99
|
+
return context.variables.get(name)
|
|
100
|
+
elif isinstance(context, dict):
|
|
101
|
+
return context.get(name)
|
|
102
|
+
|
|
103
|
+
raise AttributeError(f"Variable '{name}' not found in Quantum context")
|
|
104
|
+
|
|
105
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Set a variable in the Quantum context.
|
|
108
|
+
|
|
109
|
+
Usage:
|
|
110
|
+
q.result = 42
|
|
111
|
+
q.user_data = {'name': 'John'}
|
|
112
|
+
"""
|
|
113
|
+
context = object.__getattribute__(self, '_context')
|
|
114
|
+
if hasattr(context, 'set'):
|
|
115
|
+
context.set(name, value)
|
|
116
|
+
elif hasattr(context, 'variables'):
|
|
117
|
+
context.variables[name] = value
|
|
118
|
+
elif isinstance(context, dict):
|
|
119
|
+
context[name] = value
|
|
120
|
+
else:
|
|
121
|
+
# Store in exports
|
|
122
|
+
exports = object.__getattribute__(self, '_exports')
|
|
123
|
+
exports[name] = value
|
|
124
|
+
|
|
125
|
+
def get(self, name: str, default: Any = None) -> Any:
|
|
126
|
+
"""
|
|
127
|
+
Get a variable with optional default.
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
value = q.get('optional_var', 'default_value')
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
return self.__getattr__(name)
|
|
134
|
+
except AttributeError:
|
|
135
|
+
return default
|
|
136
|
+
|
|
137
|
+
def set(self, name: str, value: Any) -> None:
|
|
138
|
+
"""
|
|
139
|
+
Explicitly set a variable.
|
|
140
|
+
|
|
141
|
+
Usage:
|
|
142
|
+
q.set('result', computed_value)
|
|
143
|
+
"""
|
|
144
|
+
self.__setattr__(name, value)
|
|
145
|
+
|
|
146
|
+
def export(self, name: str, value: Any) -> None:
|
|
147
|
+
"""
|
|
148
|
+
Export a variable from isolated scope to component scope.
|
|
149
|
+
|
|
150
|
+
Usage (in isolated scope):
|
|
151
|
+
temp = calculate()
|
|
152
|
+
q.export('result', temp)
|
|
153
|
+
"""
|
|
154
|
+
exports = object.__getattribute__(self, '_exports')
|
|
155
|
+
exports[name] = value
|
|
156
|
+
# Also set in context
|
|
157
|
+
self.__setattr__(name, value)
|
|
158
|
+
|
|
159
|
+
def get_exports(self) -> Dict[str, Any]:
|
|
160
|
+
"""Get all exported variables."""
|
|
161
|
+
return dict(object.__getattribute__(self, '_exports'))
|
|
162
|
+
|
|
163
|
+
# =========================================================================
|
|
164
|
+
# Database Queries (q.query, q.fetch, q.execute)
|
|
165
|
+
# =========================================================================
|
|
166
|
+
|
|
167
|
+
def query(self, sql: str, **params) -> Any:
|
|
168
|
+
"""
|
|
169
|
+
Execute a SQL query and return results.
|
|
170
|
+
|
|
171
|
+
Usage:
|
|
172
|
+
users = q.query('SELECT * FROM users WHERE active = :active', active=True)
|
|
173
|
+
for user in users:
|
|
174
|
+
print(user['name'])
|
|
175
|
+
"""
|
|
176
|
+
services = object.__getattribute__(self, '_services')
|
|
177
|
+
db = services.get('db') or services.get('database')
|
|
178
|
+
|
|
179
|
+
if db is None:
|
|
180
|
+
raise QuantumBridgeError("Database service not available")
|
|
181
|
+
|
|
182
|
+
if hasattr(db, 'execute_query'):
|
|
183
|
+
return db.execute_query(sql, params)
|
|
184
|
+
elif hasattr(db, 'execute'):
|
|
185
|
+
return db.execute(sql, params)
|
|
186
|
+
else:
|
|
187
|
+
raise QuantumBridgeError("Database service doesn't support query execution")
|
|
188
|
+
|
|
189
|
+
def fetch(self, sql: str, **params) -> List[Dict[str, Any]]:
|
|
190
|
+
"""
|
|
191
|
+
Execute query and fetch all results as list of dicts.
|
|
192
|
+
|
|
193
|
+
Usage:
|
|
194
|
+
users = q.fetch('SELECT * FROM users')
|
|
195
|
+
"""
|
|
196
|
+
result = self.query(sql, **params)
|
|
197
|
+
if hasattr(result, 'fetchall'):
|
|
198
|
+
return [dict(row) for row in result.fetchall()]
|
|
199
|
+
return list(result) if result else []
|
|
200
|
+
|
|
201
|
+
def fetchone(self, sql: str, **params) -> Optional[Dict[str, Any]]:
|
|
202
|
+
"""
|
|
203
|
+
Execute query and fetch first result.
|
|
204
|
+
|
|
205
|
+
Usage:
|
|
206
|
+
user = q.fetchone('SELECT * FROM users WHERE id = :id', id=1)
|
|
207
|
+
"""
|
|
208
|
+
result = self.query(sql, **params)
|
|
209
|
+
if hasattr(result, 'fetchone'):
|
|
210
|
+
row = result.fetchone()
|
|
211
|
+
return dict(row) if row else None
|
|
212
|
+
return result[0] if result else None
|
|
213
|
+
|
|
214
|
+
def execute(self, sql: str, **params) -> int:
|
|
215
|
+
"""
|
|
216
|
+
Execute SQL statement (INSERT, UPDATE, DELETE) and return affected rows.
|
|
217
|
+
|
|
218
|
+
Usage:
|
|
219
|
+
affected = q.execute('UPDATE users SET active = :active WHERE id = :id',
|
|
220
|
+
active=True, id=1)
|
|
221
|
+
"""
|
|
222
|
+
services = object.__getattribute__(self, '_services')
|
|
223
|
+
db = services.get('db') or services.get('database')
|
|
224
|
+
|
|
225
|
+
if db is None:
|
|
226
|
+
raise QuantumBridgeError("Database service not available")
|
|
227
|
+
|
|
228
|
+
if hasattr(db, 'execute'):
|
|
229
|
+
result = db.execute(sql, params)
|
|
230
|
+
if hasattr(result, 'rowcount'):
|
|
231
|
+
return result.rowcount
|
|
232
|
+
return result
|
|
233
|
+
raise QuantumBridgeError("Database service doesn't support execute")
|
|
234
|
+
|
|
235
|
+
# =========================================================================
|
|
236
|
+
# HTTP Requests (q.http, q.get, q.post, etc.)
|
|
237
|
+
# =========================================================================
|
|
238
|
+
|
|
239
|
+
def http(self, url: str, method: str = 'GET', **kwargs) -> Any:
|
|
240
|
+
"""
|
|
241
|
+
Make an HTTP request.
|
|
242
|
+
|
|
243
|
+
Usage:
|
|
244
|
+
response = q.http('https://api.example.com/data', method='POST', json={'key': 'value'})
|
|
245
|
+
"""
|
|
246
|
+
try:
|
|
247
|
+
import requests
|
|
248
|
+
except ImportError:
|
|
249
|
+
raise QuantumBridgeError("requests library not installed. Run: pip install requests")
|
|
250
|
+
|
|
251
|
+
method = method.upper()
|
|
252
|
+
response = requests.request(method, url, **kwargs)
|
|
253
|
+
|
|
254
|
+
# Return a simple response object
|
|
255
|
+
return HttpResponse(response)
|
|
256
|
+
|
|
257
|
+
def get_http(self, url: str, **kwargs) -> Any:
|
|
258
|
+
"""GET request."""
|
|
259
|
+
return self.http(url, 'GET', **kwargs)
|
|
260
|
+
|
|
261
|
+
def post(self, url: str, **kwargs) -> Any:
|
|
262
|
+
"""POST request."""
|
|
263
|
+
return self.http(url, 'POST', **kwargs)
|
|
264
|
+
|
|
265
|
+
def put(self, url: str, **kwargs) -> Any:
|
|
266
|
+
"""PUT request."""
|
|
267
|
+
return self.http(url, 'PUT', **kwargs)
|
|
268
|
+
|
|
269
|
+
def delete(self, url: str, **kwargs) -> Any:
|
|
270
|
+
"""DELETE request."""
|
|
271
|
+
return self.http(url, 'DELETE', **kwargs)
|
|
272
|
+
|
|
273
|
+
def patch(self, url: str, **kwargs) -> Any:
|
|
274
|
+
"""PATCH request."""
|
|
275
|
+
return self.http(url, 'PATCH', **kwargs)
|
|
276
|
+
|
|
277
|
+
# =========================================================================
|
|
278
|
+
# Message Queue (q.publish, q.send, q.dispatch)
|
|
279
|
+
# =========================================================================
|
|
280
|
+
|
|
281
|
+
def publish(self, topic: str, message: Any, headers: Optional[Dict] = None) -> None:
|
|
282
|
+
"""
|
|
283
|
+
Publish a message to a topic.
|
|
284
|
+
|
|
285
|
+
Usage:
|
|
286
|
+
q.publish('orders.created', {'orderId': 123, 'total': 99.99})
|
|
287
|
+
"""
|
|
288
|
+
services = object.__getattribute__(self, '_services')
|
|
289
|
+
broker = services.get('broker') or services.get('message_broker')
|
|
290
|
+
|
|
291
|
+
if broker is None:
|
|
292
|
+
raise QuantumBridgeError("Message broker not available")
|
|
293
|
+
|
|
294
|
+
from quantum.runtime.message_broker import Message
|
|
295
|
+
msg = Message(body=message, headers=headers or {})
|
|
296
|
+
broker.publish(topic, msg)
|
|
297
|
+
|
|
298
|
+
def send(self, queue: str, message: Any, headers: Optional[Dict] = None) -> None:
|
|
299
|
+
"""
|
|
300
|
+
Send a message to a queue.
|
|
301
|
+
|
|
302
|
+
Usage:
|
|
303
|
+
q.send('email-queue', {'to': 'user@example.com', 'subject': 'Welcome'})
|
|
304
|
+
"""
|
|
305
|
+
services = object.__getattribute__(self, '_services')
|
|
306
|
+
broker = services.get('broker') or services.get('message_broker')
|
|
307
|
+
|
|
308
|
+
if broker is None:
|
|
309
|
+
raise QuantumBridgeError("Message broker not available")
|
|
310
|
+
|
|
311
|
+
from quantum.runtime.message_broker import Message
|
|
312
|
+
msg = Message(body=message, headers=headers or {})
|
|
313
|
+
broker.send(queue, msg)
|
|
314
|
+
|
|
315
|
+
def dispatch(self, job_name: str, delay: Optional[str] = None, **params) -> int:
|
|
316
|
+
"""
|
|
317
|
+
Dispatch a job to the job queue.
|
|
318
|
+
|
|
319
|
+
Usage:
|
|
320
|
+
job_id = q.dispatch('process-order', order_id=123)
|
|
321
|
+
job_id = q.dispatch('send-reminder', delay='1h', user_id=456)
|
|
322
|
+
"""
|
|
323
|
+
services = object.__getattribute__(self, '_services')
|
|
324
|
+
jobs = services.get('jobs') or services.get('job_queue')
|
|
325
|
+
|
|
326
|
+
if jobs is None:
|
|
327
|
+
raise QuantumBridgeError("Job queue service not available")
|
|
328
|
+
|
|
329
|
+
if hasattr(jobs, 'dispatch'):
|
|
330
|
+
return jobs.dispatch(name=job_name, params=params, delay=delay)
|
|
331
|
+
raise QuantumBridgeError("Job queue doesn't support dispatch")
|
|
332
|
+
|
|
333
|
+
def schedule(self, job_name: str, cron: Optional[str] = None,
|
|
334
|
+
interval: Optional[str] = None, **params) -> str:
|
|
335
|
+
"""
|
|
336
|
+
Schedule a recurring job.
|
|
337
|
+
|
|
338
|
+
Usage:
|
|
339
|
+
q.schedule('daily-report', cron='0 8 * * *')
|
|
340
|
+
q.schedule('cleanup', interval='1h')
|
|
341
|
+
"""
|
|
342
|
+
services = object.__getattribute__(self, '_services')
|
|
343
|
+
scheduler = services.get('scheduler') or services.get('schedule')
|
|
344
|
+
|
|
345
|
+
if scheduler is None:
|
|
346
|
+
raise QuantumBridgeError("Scheduler service not available")
|
|
347
|
+
|
|
348
|
+
if hasattr(scheduler, 'add_schedule'):
|
|
349
|
+
return scheduler.add_schedule(job_name, cron=cron, interval=interval, **params)
|
|
350
|
+
raise QuantumBridgeError("Scheduler doesn't support scheduling")
|
|
351
|
+
|
|
352
|
+
# =========================================================================
|
|
353
|
+
# Cache (q.cache)
|
|
354
|
+
# =========================================================================
|
|
355
|
+
|
|
356
|
+
def cache(self, key: str, value: Any = None, ttl: Optional[Union[int, str]] = None) -> Any:
|
|
357
|
+
"""
|
|
358
|
+
Get or set cache value.
|
|
359
|
+
|
|
360
|
+
Usage:
|
|
361
|
+
# Get
|
|
362
|
+
cached = q.cache('user:123')
|
|
363
|
+
|
|
364
|
+
# Set with TTL
|
|
365
|
+
q.cache('user:123', user_data, ttl='5m')
|
|
366
|
+
"""
|
|
367
|
+
services = object.__getattribute__(self, '_services')
|
|
368
|
+
cache_service = services.get('cache')
|
|
369
|
+
|
|
370
|
+
if cache_service is None:
|
|
371
|
+
# Use simple in-memory cache
|
|
372
|
+
if not hasattr(self._local, 'cache'):
|
|
373
|
+
self._local.cache = {}
|
|
374
|
+
|
|
375
|
+
if value is None:
|
|
376
|
+
return self._local.cache.get(key)
|
|
377
|
+
else:
|
|
378
|
+
self._local.cache[key] = value
|
|
379
|
+
return value
|
|
380
|
+
|
|
381
|
+
if value is None:
|
|
382
|
+
return cache_service.get(key)
|
|
383
|
+
else:
|
|
384
|
+
return cache_service.set(key, value, ttl=ttl)
|
|
385
|
+
|
|
386
|
+
def cache_delete(self, key: str) -> bool:
|
|
387
|
+
"""Delete a cache entry."""
|
|
388
|
+
services = object.__getattribute__(self, '_services')
|
|
389
|
+
cache_service = services.get('cache')
|
|
390
|
+
|
|
391
|
+
if cache_service is None:
|
|
392
|
+
if hasattr(self._local, 'cache'):
|
|
393
|
+
return self._local.cache.pop(key, None) is not None
|
|
394
|
+
return False
|
|
395
|
+
|
|
396
|
+
return cache_service.delete(key)
|
|
397
|
+
|
|
398
|
+
# =========================================================================
|
|
399
|
+
# Session (q.session)
|
|
400
|
+
# =========================================================================
|
|
401
|
+
|
|
402
|
+
def session(self, key: str, value: Any = None) -> Any:
|
|
403
|
+
"""
|
|
404
|
+
Get or set session value.
|
|
405
|
+
|
|
406
|
+
Usage:
|
|
407
|
+
# Get
|
|
408
|
+
user_id = q.session('user_id')
|
|
409
|
+
|
|
410
|
+
# Set
|
|
411
|
+
q.session('cart', cart_items)
|
|
412
|
+
"""
|
|
413
|
+
services = object.__getattribute__(self, '_services')
|
|
414
|
+
session_service = services.get('session')
|
|
415
|
+
|
|
416
|
+
if session_service is None:
|
|
417
|
+
context = object.__getattribute__(self, '_context')
|
|
418
|
+
if hasattr(context, 'session'):
|
|
419
|
+
session_service = context.session
|
|
420
|
+
else:
|
|
421
|
+
raise QuantumBridgeError("Session service not available")
|
|
422
|
+
|
|
423
|
+
if value is None:
|
|
424
|
+
return session_service.get(key) if hasattr(session_service, 'get') else session_service[key]
|
|
425
|
+
else:
|
|
426
|
+
if hasattr(session_service, 'set'):
|
|
427
|
+
session_service.set(key, value)
|
|
428
|
+
else:
|
|
429
|
+
session_service[key] = value
|
|
430
|
+
|
|
431
|
+
# =========================================================================
|
|
432
|
+
# Logging (q.log, q.info, q.warn, q.error, q.debug)
|
|
433
|
+
# =========================================================================
|
|
434
|
+
|
|
435
|
+
def log(self, message: str, level: str = 'info', **extra) -> None:
|
|
436
|
+
"""
|
|
437
|
+
Log a message.
|
|
438
|
+
|
|
439
|
+
Usage:
|
|
440
|
+
q.log('Processing order', level='info', order_id=123)
|
|
441
|
+
"""
|
|
442
|
+
import logging
|
|
443
|
+
logger = logging.getLogger('quantum.python')
|
|
444
|
+
|
|
445
|
+
log_func = getattr(logger, level.lower(), logger.info)
|
|
446
|
+
|
|
447
|
+
if extra:
|
|
448
|
+
message = f"{message} | {extra}"
|
|
449
|
+
|
|
450
|
+
log_func(message)
|
|
451
|
+
|
|
452
|
+
def info(self, message: str, **extra) -> None:
|
|
453
|
+
"""Log info message."""
|
|
454
|
+
self.log(message, 'info', **extra)
|
|
455
|
+
|
|
456
|
+
def warn(self, message: str, **extra) -> None:
|
|
457
|
+
"""Log warning message."""
|
|
458
|
+
self.log(message, 'warning', **extra)
|
|
459
|
+
|
|
460
|
+
def warning(self, message: str, **extra) -> None:
|
|
461
|
+
"""Log warning message (alias)."""
|
|
462
|
+
self.log(message, 'warning', **extra)
|
|
463
|
+
|
|
464
|
+
def error(self, message: str, **extra) -> None:
|
|
465
|
+
"""Log error message."""
|
|
466
|
+
self.log(message, 'error', **extra)
|
|
467
|
+
|
|
468
|
+
def debug(self, message: str, **extra) -> None:
|
|
469
|
+
"""Log debug message."""
|
|
470
|
+
self.log(message, 'debug', **extra)
|
|
471
|
+
|
|
472
|
+
# =========================================================================
|
|
473
|
+
# Request Access (q.form, q.request, q.files)
|
|
474
|
+
# =========================================================================
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def form(self) -> Dict[str, Any]:
|
|
478
|
+
"""
|
|
479
|
+
Access form data from the request.
|
|
480
|
+
|
|
481
|
+
Usage:
|
|
482
|
+
email = q.form['email']
|
|
483
|
+
password = q.form.get('password')
|
|
484
|
+
"""
|
|
485
|
+
request = object.__getattribute__(self, '_request')
|
|
486
|
+
if request is None:
|
|
487
|
+
context = object.__getattribute__(self, '_context')
|
|
488
|
+
if hasattr(context, 'form'):
|
|
489
|
+
return context.form
|
|
490
|
+
return {}
|
|
491
|
+
|
|
492
|
+
if hasattr(request, 'form'):
|
|
493
|
+
return dict(request.form)
|
|
494
|
+
return {}
|
|
495
|
+
|
|
496
|
+
@property
|
|
497
|
+
def request(self) -> Any:
|
|
498
|
+
"""
|
|
499
|
+
Access the full request object.
|
|
500
|
+
|
|
501
|
+
Usage:
|
|
502
|
+
method = q.request.method
|
|
503
|
+
headers = q.request.headers
|
|
504
|
+
"""
|
|
505
|
+
request = object.__getattribute__(self, '_request')
|
|
506
|
+
if request is None:
|
|
507
|
+
context = object.__getattribute__(self, '_context')
|
|
508
|
+
if hasattr(context, 'request'):
|
|
509
|
+
return context.request
|
|
510
|
+
return request
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def files(self) -> Dict[str, Any]:
|
|
514
|
+
"""
|
|
515
|
+
Access uploaded files.
|
|
516
|
+
|
|
517
|
+
Usage:
|
|
518
|
+
avatar = q.files['avatar']
|
|
519
|
+
"""
|
|
520
|
+
request = object.__getattribute__(self, '_request')
|
|
521
|
+
if request and hasattr(request, 'files'):
|
|
522
|
+
return dict(request.files)
|
|
523
|
+
return {}
|
|
524
|
+
|
|
525
|
+
# =========================================================================
|
|
526
|
+
# Control Flow (q.redirect, q.abort)
|
|
527
|
+
# =========================================================================
|
|
528
|
+
|
|
529
|
+
def redirect(self, url: str, status: int = 302) -> None:
|
|
530
|
+
"""
|
|
531
|
+
Redirect to a URL.
|
|
532
|
+
|
|
533
|
+
Usage:
|
|
534
|
+
q.redirect('/dashboard')
|
|
535
|
+
q.redirect('/login', status=301)
|
|
536
|
+
"""
|
|
537
|
+
raise RedirectException(url, status)
|
|
538
|
+
|
|
539
|
+
def abort(self, status: int, message: Optional[str] = None) -> None:
|
|
540
|
+
"""
|
|
541
|
+
Abort request with error.
|
|
542
|
+
|
|
543
|
+
Usage:
|
|
544
|
+
q.abort(404, 'User not found')
|
|
545
|
+
q.abort(403)
|
|
546
|
+
"""
|
|
547
|
+
raise AbortException(status, message)
|
|
548
|
+
|
|
549
|
+
# =========================================================================
|
|
550
|
+
# Events (q.emit, q.on)
|
|
551
|
+
# =========================================================================
|
|
552
|
+
|
|
553
|
+
def emit(self, event: str, data: Any = None) -> None:
|
|
554
|
+
"""
|
|
555
|
+
Emit an event.
|
|
556
|
+
|
|
557
|
+
Usage:
|
|
558
|
+
q.emit('user:registered', user_data)
|
|
559
|
+
"""
|
|
560
|
+
services = object.__getattribute__(self, '_services')
|
|
561
|
+
events = services.get('events')
|
|
562
|
+
|
|
563
|
+
if events and hasattr(events, 'emit'):
|
|
564
|
+
events.emit(event, data)
|
|
565
|
+
else:
|
|
566
|
+
# Fallback to publish if no event service
|
|
567
|
+
self.publish(event.replace(':', '.'), data)
|
|
568
|
+
|
|
569
|
+
def on(self, event: str, handler: Callable) -> None:
|
|
570
|
+
"""
|
|
571
|
+
Register an event handler.
|
|
572
|
+
|
|
573
|
+
Usage:
|
|
574
|
+
def on_user_created(user):
|
|
575
|
+
send_welcome_email(user)
|
|
576
|
+
|
|
577
|
+
q.on('user:created', on_user_created)
|
|
578
|
+
"""
|
|
579
|
+
services = object.__getattribute__(self, '_services')
|
|
580
|
+
events = services.get('events')
|
|
581
|
+
|
|
582
|
+
if events and hasattr(events, 'on'):
|
|
583
|
+
events.on(event, handler)
|
|
584
|
+
else:
|
|
585
|
+
raise QuantumBridgeError("Event service not available")
|
|
586
|
+
|
|
587
|
+
# =========================================================================
|
|
588
|
+
# Utilities (q.render, q.json, q.now, etc.)
|
|
589
|
+
# =========================================================================
|
|
590
|
+
|
|
591
|
+
def render(self, template: str, **context) -> str:
|
|
592
|
+
"""
|
|
593
|
+
Render a template.
|
|
594
|
+
|
|
595
|
+
Usage:
|
|
596
|
+
html = q.render('email/welcome.html', user=user)
|
|
597
|
+
"""
|
|
598
|
+
services = object.__getattribute__(self, '_services')
|
|
599
|
+
renderer = services.get('renderer') or services.get('template')
|
|
600
|
+
|
|
601
|
+
if renderer and hasattr(renderer, 'render'):
|
|
602
|
+
return renderer.render(template, context)
|
|
603
|
+
|
|
604
|
+
# Simple string formatting fallback
|
|
605
|
+
return template.format(**context)
|
|
606
|
+
|
|
607
|
+
def json(self, data: Any) -> str:
|
|
608
|
+
"""Convert to JSON string."""
|
|
609
|
+
return json.dumps(data, default=str)
|
|
610
|
+
|
|
611
|
+
def parse_json(self, json_str: str) -> Any:
|
|
612
|
+
"""Parse JSON string."""
|
|
613
|
+
return json.loads(json_str)
|
|
614
|
+
|
|
615
|
+
def now(self) -> 'datetime':
|
|
616
|
+
"""Get current datetime."""
|
|
617
|
+
from datetime import datetime
|
|
618
|
+
return datetime.now()
|
|
619
|
+
|
|
620
|
+
def today(self) -> 'date':
|
|
621
|
+
"""Get current date."""
|
|
622
|
+
from datetime import date
|
|
623
|
+
return date.today()
|
|
624
|
+
|
|
625
|
+
def uuid(self) -> str:
|
|
626
|
+
"""Generate a UUID."""
|
|
627
|
+
import uuid
|
|
628
|
+
return str(uuid.uuid4())
|
|
629
|
+
|
|
630
|
+
def sleep(self, seconds: float) -> None:
|
|
631
|
+
"""Sleep for specified seconds."""
|
|
632
|
+
time.sleep(seconds)
|
|
633
|
+
|
|
634
|
+
# =========================================================================
|
|
635
|
+
# Convenience methods
|
|
636
|
+
# =========================================================================
|
|
637
|
+
|
|
638
|
+
def __contains__(self, name: str) -> bool:
|
|
639
|
+
"""Check if variable exists."""
|
|
640
|
+
try:
|
|
641
|
+
self.__getattr__(name)
|
|
642
|
+
return True
|
|
643
|
+
except AttributeError:
|
|
644
|
+
return False
|
|
645
|
+
|
|
646
|
+
def __repr__(self) -> str:
|
|
647
|
+
return "<QuantumBridge>"
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
@dataclass
|
|
651
|
+
class HttpResponse:
|
|
652
|
+
"""Simple HTTP response wrapper."""
|
|
653
|
+
|
|
654
|
+
_response: Any
|
|
655
|
+
|
|
656
|
+
@property
|
|
657
|
+
def status_code(self) -> int:
|
|
658
|
+
return self._response.status_code
|
|
659
|
+
|
|
660
|
+
@property
|
|
661
|
+
def ok(self) -> bool:
|
|
662
|
+
return self._response.ok
|
|
663
|
+
|
|
664
|
+
@property
|
|
665
|
+
def text(self) -> str:
|
|
666
|
+
return self._response.text
|
|
667
|
+
|
|
668
|
+
@property
|
|
669
|
+
def content(self) -> bytes:
|
|
670
|
+
return self._response.content
|
|
671
|
+
|
|
672
|
+
@property
|
|
673
|
+
def headers(self) -> Dict[str, str]:
|
|
674
|
+
return dict(self._response.headers)
|
|
675
|
+
|
|
676
|
+
def json(self) -> Any:
|
|
677
|
+
return self._response.json()
|
|
678
|
+
|
|
679
|
+
def raise_for_status(self) -> None:
|
|
680
|
+
self._response.raise_for_status()
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
class RedirectException(Exception):
|
|
684
|
+
"""Raised to trigger a redirect."""
|
|
685
|
+
|
|
686
|
+
def __init__(self, url: str, status: int = 302):
|
|
687
|
+
self.url = url
|
|
688
|
+
self.status = status
|
|
689
|
+
super().__init__(f"Redirect to {url}")
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
class AbortException(Exception):
|
|
693
|
+
"""Raised to abort the request."""
|
|
694
|
+
|
|
695
|
+
def __init__(self, status: int, message: Optional[str] = None):
|
|
696
|
+
self.status = status
|
|
697
|
+
self.message = message or f"HTTP {status}"
|
|
698
|
+
super().__init__(self.message)
|