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,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Phase I: Email Service
|
|
3
|
+
|
|
4
|
+
ColdFusion cfmail-inspired email sending with SMTP support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import smtplib
|
|
8
|
+
import os
|
|
9
|
+
from email.mime.text import MIMEText
|
|
10
|
+
from email.mime.multipart import MIMEMultipart
|
|
11
|
+
from email.mime.base import MIMEBase
|
|
12
|
+
from email import encoders
|
|
13
|
+
from typing import List, Dict, Any, Optional
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EmailError(Exception):
|
|
18
|
+
"""Raised when email sending fails"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EmailService:
|
|
23
|
+
"""Service for sending emails via SMTP"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
"""Initialize email service with config from environment"""
|
|
27
|
+
self.smtp_host = os.getenv('SMTP_HOST', 'localhost')
|
|
28
|
+
self.smtp_port = int(os.getenv('SMTP_PORT', '587'))
|
|
29
|
+
self.smtp_user = os.getenv('SMTP_USER', '')
|
|
30
|
+
self.smtp_password = os.getenv('SMTP_PASSWORD', '')
|
|
31
|
+
self.smtp_use_tls = os.getenv('SMTP_USE_TLS', 'true').lower() == 'true'
|
|
32
|
+
self.default_from = os.getenv('SMTP_FROM', 'noreply@quantum.dev')
|
|
33
|
+
|
|
34
|
+
def send_email(
|
|
35
|
+
self,
|
|
36
|
+
to: str,
|
|
37
|
+
subject: str,
|
|
38
|
+
body: str,
|
|
39
|
+
from_addr: Optional[str] = None,
|
|
40
|
+
cc: Optional[str] = None,
|
|
41
|
+
bcc: Optional[str] = None,
|
|
42
|
+
reply_to: Optional[str] = None,
|
|
43
|
+
email_type: str = "html",
|
|
44
|
+
attachments: Optional[List[str]] = None
|
|
45
|
+
) -> Dict[str, Any]:
|
|
46
|
+
"""
|
|
47
|
+
Send email via SMTP
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
to: Recipient email(s) (comma-separated)
|
|
51
|
+
subject: Email subject
|
|
52
|
+
body: Email body content
|
|
53
|
+
from_addr: Sender email (uses default if not provided)
|
|
54
|
+
cc: CC recipients (comma-separated)
|
|
55
|
+
bcc: BCC recipients (comma-separated)
|
|
56
|
+
reply_to: Reply-To address
|
|
57
|
+
email_type: 'html' or 'text'
|
|
58
|
+
attachments: List of file paths to attach
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dict with send result
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
EmailError: If sending fails
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
# Create message
|
|
68
|
+
msg = MIMEMultipart()
|
|
69
|
+
msg['From'] = from_addr or self.default_from
|
|
70
|
+
msg['To'] = to
|
|
71
|
+
msg['Subject'] = subject
|
|
72
|
+
|
|
73
|
+
if cc:
|
|
74
|
+
msg['Cc'] = cc
|
|
75
|
+
if reply_to:
|
|
76
|
+
msg['Reply-To'] = reply_to
|
|
77
|
+
|
|
78
|
+
# Attach body
|
|
79
|
+
mime_type = 'html' if email_type == 'html' else 'plain'
|
|
80
|
+
msg.attach(MIMEText(body, mime_type, 'utf-8'))
|
|
81
|
+
|
|
82
|
+
# Attach files if provided
|
|
83
|
+
if attachments:
|
|
84
|
+
for file_path in attachments:
|
|
85
|
+
self._attach_file(msg, file_path)
|
|
86
|
+
|
|
87
|
+
# Prepare recipients
|
|
88
|
+
recipients = self._parse_recipients(to, cc, bcc)
|
|
89
|
+
|
|
90
|
+
# Send email
|
|
91
|
+
if os.getenv('EMAIL_MOCK', 'true').lower() == 'true':
|
|
92
|
+
# Mock mode for development (don't actually send)
|
|
93
|
+
print(f"[MOCK] Email sent to {to}")
|
|
94
|
+
print(f" Subject: {subject}")
|
|
95
|
+
print(f" From: {msg['From']}")
|
|
96
|
+
print(f" Type: {email_type}")
|
|
97
|
+
return {
|
|
98
|
+
'success': True,
|
|
99
|
+
'mock': True,
|
|
100
|
+
'to': to,
|
|
101
|
+
'subject': subject,
|
|
102
|
+
'message': 'Email mock-sent successfully'
|
|
103
|
+
}
|
|
104
|
+
else:
|
|
105
|
+
# Real SMTP sending
|
|
106
|
+
with smtplib.SMTP(self.smtp_host, self.smtp_port) as server:
|
|
107
|
+
if self.smtp_use_tls:
|
|
108
|
+
server.starttls()
|
|
109
|
+
|
|
110
|
+
if self.smtp_user and self.smtp_password:
|
|
111
|
+
server.login(self.smtp_user, self.smtp_password)
|
|
112
|
+
|
|
113
|
+
server.sendmail(msg['From'], recipients, msg.as_string())
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
'success': True,
|
|
117
|
+
'to': to,
|
|
118
|
+
'subject': subject,
|
|
119
|
+
'message': 'Email sent successfully'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
raise EmailError(f"Failed to send email: {e}")
|
|
124
|
+
|
|
125
|
+
def _attach_file(self, msg: MIMEMultipart, file_path: str):
|
|
126
|
+
"""Attach a file to email message"""
|
|
127
|
+
try:
|
|
128
|
+
path = Path(file_path)
|
|
129
|
+
if not path.exists():
|
|
130
|
+
raise EmailError(f"Attachment file not found: {file_path}")
|
|
131
|
+
|
|
132
|
+
with open(file_path, 'rb') as f:
|
|
133
|
+
part = MIMEBase('application', 'octet-stream')
|
|
134
|
+
part.set_payload(f.read())
|
|
135
|
+
|
|
136
|
+
encoders.encode_base64(part)
|
|
137
|
+
part.add_header(
|
|
138
|
+
'Content-Disposition',
|
|
139
|
+
f'attachment; filename= {path.name}'
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
msg.attach(part)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
raise EmailError(f"Failed to attach file {file_path}: {e}")
|
|
145
|
+
|
|
146
|
+
def _parse_recipients(
|
|
147
|
+
self,
|
|
148
|
+
to: str,
|
|
149
|
+
cc: Optional[str] = None,
|
|
150
|
+
bcc: Optional[str] = None
|
|
151
|
+
) -> List[str]:
|
|
152
|
+
"""Parse comma-separated email addresses into list"""
|
|
153
|
+
recipients = []
|
|
154
|
+
|
|
155
|
+
if to:
|
|
156
|
+
recipients.extend([email.strip() for email in to.split(',')])
|
|
157
|
+
if cc:
|
|
158
|
+
recipients.extend([email.strip() for email in cc.split(',')])
|
|
159
|
+
if bcc:
|
|
160
|
+
recipients.extend([email.strip() for email in bcc.split(',')])
|
|
161
|
+
|
|
162
|
+
return recipients
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced Error Handler - Phase C: Developer Experience
|
|
3
|
+
|
|
4
|
+
Provides better error messages with:
|
|
5
|
+
- Context (line numbers, code snippets)
|
|
6
|
+
- Suggestions (did you mean...?)
|
|
7
|
+
- Helpful hints
|
|
8
|
+
- Color-coded output
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Optional, List
|
|
14
|
+
import difflib
|
|
15
|
+
|
|
16
|
+
class QuantumError(Exception):
|
|
17
|
+
"""Base class for Quantum errors with enhanced context"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: str, file_path: Optional[str] = None,
|
|
20
|
+
line: Optional[int] = None, column: Optional[int] = None,
|
|
21
|
+
suggestion: Optional[str] = None, hint: Optional[str] = None):
|
|
22
|
+
self.message = message
|
|
23
|
+
self.file_path = file_path
|
|
24
|
+
self.line = line
|
|
25
|
+
self.column = column
|
|
26
|
+
self.suggestion = suggestion
|
|
27
|
+
self.hint = hint
|
|
28
|
+
super().__init__(self.format_error())
|
|
29
|
+
|
|
30
|
+
def format_error(self) -> str:
|
|
31
|
+
"""Format error message with context"""
|
|
32
|
+
parts = []
|
|
33
|
+
|
|
34
|
+
# Header
|
|
35
|
+
parts.append("=" * 60)
|
|
36
|
+
parts.append("❌ QUANTUM ERROR")
|
|
37
|
+
parts.append("=" * 60)
|
|
38
|
+
|
|
39
|
+
# Location
|
|
40
|
+
if self.file_path:
|
|
41
|
+
location = f"📂 File: {self.file_path}"
|
|
42
|
+
if self.line:
|
|
43
|
+
location += f":{self.line}"
|
|
44
|
+
if self.column:
|
|
45
|
+
location += f":{self.column}"
|
|
46
|
+
parts.append(location)
|
|
47
|
+
|
|
48
|
+
# Error message
|
|
49
|
+
parts.append(f"\n💥 {self.message}")
|
|
50
|
+
|
|
51
|
+
# Code context
|
|
52
|
+
if self.file_path and self.line:
|
|
53
|
+
context = self._get_code_context()
|
|
54
|
+
if context:
|
|
55
|
+
parts.append("\n📝 Code:")
|
|
56
|
+
parts.append(context)
|
|
57
|
+
|
|
58
|
+
# Suggestion
|
|
59
|
+
if self.suggestion:
|
|
60
|
+
parts.append(f"\n💡 Suggestion: {self.suggestion}")
|
|
61
|
+
|
|
62
|
+
# Hint
|
|
63
|
+
if self.hint:
|
|
64
|
+
parts.append(f"\n🔍 Hint: {self.hint}")
|
|
65
|
+
|
|
66
|
+
parts.append("=" * 60)
|
|
67
|
+
|
|
68
|
+
return "\n".join(parts)
|
|
69
|
+
|
|
70
|
+
def _get_code_context(self, context_lines: int = 3) -> Optional[str]:
|
|
71
|
+
"""Get code context around error line"""
|
|
72
|
+
try:
|
|
73
|
+
file_path = Path(self.file_path)
|
|
74
|
+
if not file_path.exists():
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
lines = file_path.read_text(encoding='utf-8').splitlines()
|
|
78
|
+
|
|
79
|
+
start = max(0, self.line - context_lines - 1)
|
|
80
|
+
end = min(len(lines), self.line + context_lines)
|
|
81
|
+
|
|
82
|
+
context_parts = []
|
|
83
|
+
for i in range(start, end):
|
|
84
|
+
line_num = i + 1
|
|
85
|
+
line_content = lines[i]
|
|
86
|
+
|
|
87
|
+
# Mark error line with arrow
|
|
88
|
+
if line_num == self.line:
|
|
89
|
+
context_parts.append(f" → {line_num:4d} | {line_content}")
|
|
90
|
+
# Add column pointer if available
|
|
91
|
+
if self.column:
|
|
92
|
+
pointer = " " * (self.column + 10) + "^"
|
|
93
|
+
context_parts.append(pointer)
|
|
94
|
+
else:
|
|
95
|
+
context_parts.append(f" {line_num:4d} | {line_content}")
|
|
96
|
+
|
|
97
|
+
return "\n".join(context_parts)
|
|
98
|
+
|
|
99
|
+
except Exception:
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class ComponentNotFoundError(QuantumError):
|
|
104
|
+
"""Component import not found"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, component_name: str, file_path: str, line: int,
|
|
107
|
+
available_components: List[str] = None):
|
|
108
|
+
# Find similar component names
|
|
109
|
+
suggestion = None
|
|
110
|
+
if available_components:
|
|
111
|
+
matches = difflib.get_close_matches(component_name, available_components, n=3, cutoff=0.6)
|
|
112
|
+
if matches:
|
|
113
|
+
if len(matches) == 1:
|
|
114
|
+
suggestion = f"Did you mean '{matches[0]}'?"
|
|
115
|
+
else:
|
|
116
|
+
suggestion = f"Did you mean one of: {', '.join(matches)}?"
|
|
117
|
+
|
|
118
|
+
hint = f"Add: <q:import component=\"{component_name}\" />"
|
|
119
|
+
|
|
120
|
+
super().__init__(
|
|
121
|
+
f"Component '{component_name}' not found",
|
|
122
|
+
file_path=file_path,
|
|
123
|
+
line=line,
|
|
124
|
+
suggestion=suggestion,
|
|
125
|
+
hint=hint
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ValidationErrorWithContext(QuantumError):
|
|
130
|
+
"""Validation error with context"""
|
|
131
|
+
|
|
132
|
+
def __init__(self, field_name: str, validation_type: str,
|
|
133
|
+
value: any, file_path: str = None, line: int = None):
|
|
134
|
+
message = f"Validation failed for field '{field_name}' ({validation_type})"
|
|
135
|
+
|
|
136
|
+
if validation_type == "required":
|
|
137
|
+
suggestion = f"Field '{field_name}' is required but was not provided"
|
|
138
|
+
elif validation_type == "type":
|
|
139
|
+
suggestion = f"Expected type differs from provided value: {value}"
|
|
140
|
+
elif validation_type == "minlength":
|
|
141
|
+
suggestion = f"Value '{value}' is too short"
|
|
142
|
+
elif validation_type == "maxlength":
|
|
143
|
+
suggestion = f"Value '{value}' is too long"
|
|
144
|
+
elif validation_type == "pattern":
|
|
145
|
+
suggestion = f"Value '{value}' doesn't match required pattern"
|
|
146
|
+
else:
|
|
147
|
+
suggestion = f"Validation '{validation_type}' failed for value: {value}"
|
|
148
|
+
|
|
149
|
+
super().__init__(
|
|
150
|
+
message,
|
|
151
|
+
file_path=file_path,
|
|
152
|
+
line=line,
|
|
153
|
+
suggestion=suggestion
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class QueryErrorWithContext(QuantumError):
|
|
158
|
+
"""Query error with SQL context"""
|
|
159
|
+
|
|
160
|
+
def __init__(self, query_name: str, sql: str, error: str,
|
|
161
|
+
file_path: str = None, line: int = None):
|
|
162
|
+
message = f"Query '{query_name}' failed"
|
|
163
|
+
|
|
164
|
+
# Provide SQL-specific suggestions
|
|
165
|
+
suggestion = None
|
|
166
|
+
if "syntax error" in error.lower():
|
|
167
|
+
suggestion = "Check your SQL syntax"
|
|
168
|
+
elif "table" in error.lower() and "doesn't exist" in error.lower():
|
|
169
|
+
suggestion = "Table doesn't exist - check your database schema"
|
|
170
|
+
elif "column" in error.lower():
|
|
171
|
+
suggestion = "Column doesn't exist - check your table structure"
|
|
172
|
+
elif "duplicate" in error.lower():
|
|
173
|
+
suggestion = "Duplicate entry - check for unique constraints"
|
|
174
|
+
|
|
175
|
+
hint = f"SQL: {sql[:100]}..." if len(sql) > 100 else f"SQL: {sql}"
|
|
176
|
+
|
|
177
|
+
super().__init__(
|
|
178
|
+
f"{message}: {error}",
|
|
179
|
+
file_path=file_path,
|
|
180
|
+
line=line,
|
|
181
|
+
suggestion=suggestion,
|
|
182
|
+
hint=hint
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class TransactionErrorWithContext(QuantumError):
|
|
187
|
+
"""Transaction error with rollback info"""
|
|
188
|
+
|
|
189
|
+
def __init__(self, error: str, statements_executed: int,
|
|
190
|
+
file_path: str = None, line: int = None):
|
|
191
|
+
message = "Transaction failed and was rolled back"
|
|
192
|
+
|
|
193
|
+
suggestion = f"{statements_executed} statement(s) were executed before failure"
|
|
194
|
+
hint = "All changes have been rolled back. Check the error above and fix the issue."
|
|
195
|
+
|
|
196
|
+
super().__init__(
|
|
197
|
+
f"{message}: {error}",
|
|
198
|
+
file_path=file_path,
|
|
199
|
+
line=line,
|
|
200
|
+
suggestion=suggestion,
|
|
201
|
+
hint=hint
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class FileUploadErrorWithContext(QuantumError):
|
|
206
|
+
"""File upload error with details"""
|
|
207
|
+
|
|
208
|
+
def __init__(self, error: str, filename: str = None,
|
|
209
|
+
file_size: int = None, max_size: int = None,
|
|
210
|
+
file_path: str = None, line: int = None):
|
|
211
|
+
message = f"File upload failed"
|
|
212
|
+
|
|
213
|
+
suggestion = None
|
|
214
|
+
if "too large" in error.lower():
|
|
215
|
+
if file_size and max_size:
|
|
216
|
+
suggestion = f"File is {file_size} bytes but maximum is {max_size} bytes"
|
|
217
|
+
elif "type" in error.lower() or "extension" in error.lower():
|
|
218
|
+
suggestion = "File type not allowed - check allowed extensions"
|
|
219
|
+
|
|
220
|
+
hint = None
|
|
221
|
+
if filename:
|
|
222
|
+
hint = f"Filename: {filename}"
|
|
223
|
+
|
|
224
|
+
super().__init__(
|
|
225
|
+
f"{message}: {error}",
|
|
226
|
+
file_path=file_path,
|
|
227
|
+
line=line,
|
|
228
|
+
suggestion=suggestion,
|
|
229
|
+
hint=hint
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class ErrorHandler:
|
|
234
|
+
"""Central error handler for better error reporting"""
|
|
235
|
+
|
|
236
|
+
_logger = None
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def _get_logger(cls):
|
|
240
|
+
if cls._logger is None:
|
|
241
|
+
import logging
|
|
242
|
+
cls._logger = logging.getLogger('quantum.errors')
|
|
243
|
+
return cls._logger
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def handle_parse_error(error: Exception, file_path: str) -> QuantumError:
|
|
247
|
+
"""Convert parse errors to enhanced errors"""
|
|
248
|
+
error_msg = str(error)
|
|
249
|
+
|
|
250
|
+
# Extract line and column from XML parse errors
|
|
251
|
+
line = None
|
|
252
|
+
column = None
|
|
253
|
+
if "line" in error_msg:
|
|
254
|
+
import re
|
|
255
|
+
match = re.search(r'line (\d+)', error_msg)
|
|
256
|
+
if match:
|
|
257
|
+
line = int(match.group(1))
|
|
258
|
+
match = re.search(r'column (\d+)', error_msg)
|
|
259
|
+
if match:
|
|
260
|
+
column = int(match.group(1))
|
|
261
|
+
|
|
262
|
+
return QuantumError(
|
|
263
|
+
f"Parse error: {error_msg}",
|
|
264
|
+
file_path=file_path,
|
|
265
|
+
line=line,
|
|
266
|
+
column=column,
|
|
267
|
+
suggestion="Check your XML syntax",
|
|
268
|
+
hint="Make sure all tags are properly closed and attributes are quoted"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def handle_runtime_error(error: Exception, component_name: str,
|
|
273
|
+
file_path: str = None) -> QuantumError:
|
|
274
|
+
"""Convert runtime errors to enhanced errors"""
|
|
275
|
+
return QuantumError(
|
|
276
|
+
f"Runtime error in component '{component_name}': {error}",
|
|
277
|
+
file_path=file_path,
|
|
278
|
+
suggestion="Check your component logic and variable names",
|
|
279
|
+
hint="Use quantum inspect <component> to debug"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
@classmethod
|
|
283
|
+
def print_warning(cls, message: str):
|
|
284
|
+
"""Log formatted warning"""
|
|
285
|
+
cls._get_logger().warning(message)
|
|
286
|
+
|
|
287
|
+
@classmethod
|
|
288
|
+
def print_info(cls, message: str):
|
|
289
|
+
"""Log formatted info"""
|
|
290
|
+
cls._get_logger().info(message)
|
|
291
|
+
|
|
292
|
+
@classmethod
|
|
293
|
+
def print_success(cls, message: str):
|
|
294
|
+
"""Log formatted success"""
|
|
295
|
+
cls._get_logger().info(message)
|