quantum-framework 0.16.0__tar.gz → 0.17.0__tar.gz
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_framework-0.16.0/quantum_framework.egg-info → quantum_framework-0.17.0}/PKG-INFO +1 -1
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/pyproject.toml +1 -1
- quantum_framework-0.17.0/quantum/cli/check.py +202 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/runner.py +9 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/ui_engine/src/parser.py +17 -2
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parser.py +62 -17
- quantum_framework-0.17.0/quantum/core/xml_lines.py +81 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/action_handler.py +7 -2
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/component.py +1 -1
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/database_service.py +5 -0
- quantum_framework-0.17.0/quantum/runtime/db_schema.py +100 -0
- quantum_framework-0.17.0/quantum/runtime/dev_panel.py +178 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executor_registry.py +8 -1
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/renderer.py +9 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_page_renderer.py +13 -7
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_view.py +3 -1
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/web_server.py +149 -22
- {quantum_framework-0.16.0 → quantum_framework-0.17.0/quantum_framework.egg-info}/PKG-INFO +1 -1
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_framework.egg-info/SOURCES.txt +4 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/LICENSE +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/README.md +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/admin.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/build.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/dev.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/docs.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/lint.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/migrate.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/new.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/serve.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/commands/test.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/deploy.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/hot_reload.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/jobs.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/mq.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/pkg.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/server_process.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/cli/utils.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/base_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/cli.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/expression_transformer.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/javascript/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/javascript/generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/optimizer.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/python/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/python/generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/python/runtime.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/compiler/transpiler.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/ast_nodes.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/config_env.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/docs_links.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/expression_diagnostics.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/expression_stdlib.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/expressions.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/agents/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/agents/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/data_import/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/data_import/src/runtime.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/dump/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/dump/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/dump/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/dump/src/runtime.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/functions/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/functions/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/invocation/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/invocation/src/runtime.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/logging/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/logging/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/logging/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/logging/src/runtime.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/loops/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/loops/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/query/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/query/src/query_validators.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/state_management/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/theming/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/theming/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/theming/src/presets.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/websocket/src/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/guards.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/html_compat.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parser_registry.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/ai/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/ai/team_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/base.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/composition/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/composition/import_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/data/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/data/data_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/data/invoke_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/data/query_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/events/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/forms/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/forms/action_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/functions/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/functions/function_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/functions/param_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/functions/return_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/html/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/html/html_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/jobs/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/routing/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/routing/route_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/scripting/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/services/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/services/dump_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/services/file_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/services/log_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/parsers/services/mail_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/tiers.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/packages/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/packages/manager.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/packages/manifest.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/packages/registry.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/packages/resolver.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/plugins/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/plugins/hooks.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/plugins/loader.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/plugins/manifest.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/plugins/registry.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/adapters/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/agent_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ast_cache.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/auth_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/component_composer.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/component_resolver.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/conditions.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/email_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/error_handler.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/execution_context.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/ai/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/base.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/control_flow/redirect_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/data/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/data/data_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/data/invoke_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/data/query_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/services/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/services/file_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/services/log_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/expression_cache.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/file_upload_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/function_registry.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/game_builder.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/game_code_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/game_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/godot_code_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/godot_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/job_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/knowledge_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/llm_cache.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/llm_providers.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/llm_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/local_server.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/logging_setup.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/message_broker.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/message_queue_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/param_validation.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/pypy_compat.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/python_bridge.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/query_validators.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/service_container.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/terminal_builder.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/terminal_code_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/terminal_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/testing_builder.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/testing_code_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/testing_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_builder.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_console.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_desktop.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_html_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_html_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_mobile_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_textual_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_textual_templates.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_tokens.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/ui_validator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/validators.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/websocket_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/websocket_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/websocket_transport.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/runtime/wsgi.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/services.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/utils/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/assets/quantum-admin-connectors.js +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/assets/quantum-admin.css +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/assets/quantum-source-highlight.js +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/audit_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/auth_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/component_discovery.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/config_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/connector_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/crud.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/database.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/db_setup_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/deploy_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/docker_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/environment_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/git_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/health_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/job_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/log_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/main.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/models.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/pipeline_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/resource_manager.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/schemas.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/secret_manager.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/settings_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/template_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/test_execution_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/test_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/webhook_service.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/backend/websocket_manager.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/_layout/AdminShell.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/agents.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/app/[name].q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/applications.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/component/[...path].q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/components.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/connectors.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/dashboard.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/database.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/features.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/index.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/jobs.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/login.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/logout.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/settings.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/source.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/components/admin/tests.q +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/run.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/__init__.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/_base.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/_component_test_generator.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/_yaml_editor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/apps.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/auth.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/components.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/connectors.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/projects.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/repository.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/settings.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_admin/services/yaml_import.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_framework.egg-info/entry_points.txt +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_framework.egg-info/requires.txt +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum_framework.egg-info/top_level.txt +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/setup.cfg +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_agent.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_animations.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_ast_cache.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_cli_commands.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_component_library.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_expression_cache.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_form_validation.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_game_codegen.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_game_e2e.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_game_engine.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_game_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_godot_codegen.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_job_executor.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_llm_providers.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_message_broker.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_message_queue.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_mobile_target.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_multi_agent.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_packages.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_plugins.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_pypy_compat.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_python_scripting.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_terminal_codegen.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_terminal_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_testing_codegen.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_testing_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_theming.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_ui_html_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_ui_parity.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_ui_parser.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_ui_textual_adapter.py +0 -0
- {quantum_framework-0.16.0 → quantum_framework-0.17.0}/tests/test_websocket.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantum-framework"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.17.0"
|
|
8
8
|
description = "Full-stack declarative framework for building web, desktop, and mobile applications"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""`quantum check`: every page parses, every q:query compiles against the database,
|
|
2
|
+
and every `{query.field}` a page reads is a column the query returns (M8, DEV-3).
|
|
3
|
+
|
|
4
|
+
A typo in SQL or in a field name used to surface only when someone opened the
|
|
5
|
+
page — `no such column` as a 500, or, for a field, an empty cell. This reads
|
|
6
|
+
the project the way the server would and reports each problem with its file
|
|
7
|
+
and line, without running anything: a query is compiled by the database
|
|
8
|
+
(EXPLAIN) and a SELECT's columns come from running it with LIMIT 0.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Dict, Iterator, List, Optional
|
|
15
|
+
|
|
16
|
+
# Fields of `<name>_result` and of a query row that are not columns.
|
|
17
|
+
_META = {'recordCount', 'columnList', 'executionTime', 'success', 'error', 'sql', 'page',
|
|
18
|
+
'totalPages', 'hasMore', 'cached', 'affectedRows', 'lastInsertId', 'data', 'length'}
|
|
19
|
+
_EXPR = re.compile(r'\{([^{}]*)\}')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Problem:
|
|
24
|
+
file: str
|
|
25
|
+
line: Optional[int]
|
|
26
|
+
message: str
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
return f'{self.file}:{self.line or "?"}: {self.message}'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _config(config_path: Path) -> dict:
|
|
33
|
+
import yaml
|
|
34
|
+
from quantum.core.config_env import expand_env
|
|
35
|
+
if not config_path.exists():
|
|
36
|
+
return {}
|
|
37
|
+
return expand_env(yaml.safe_load(config_path.read_text(encoding='utf-8')) or {})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _children(node) -> Iterator:
|
|
41
|
+
"""The nodes inside a node, whatever the field is called."""
|
|
42
|
+
for campo in ('statements', 'body', 'if_body', 'else_body', 'children'):
|
|
43
|
+
for filho in getattr(node, campo, None) or []:
|
|
44
|
+
if hasattr(filho, '__dict__'):
|
|
45
|
+
yield filho
|
|
46
|
+
for bloco in getattr(node, 'elseif_blocks', None) or []:
|
|
47
|
+
corpo = bloco.get('body') if isinstance(bloco, dict) else getattr(bloco, 'body', None)
|
|
48
|
+
for filho in corpo or []:
|
|
49
|
+
yield filho
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _texts(node) -> List[str]:
|
|
53
|
+
"""The strings of a node that may hold {expressions}."""
|
|
54
|
+
textos = []
|
|
55
|
+
for campo, valor in vars(node).items():
|
|
56
|
+
if campo.startswith('_') or campo in ('sql',):
|
|
57
|
+
continue
|
|
58
|
+
if isinstance(valor, str) and '{' in valor:
|
|
59
|
+
textos.append(valor)
|
|
60
|
+
elif isinstance(valor, dict):
|
|
61
|
+
textos += [v for v in valor.values() if isinstance(v, str) and '{' in v]
|
|
62
|
+
return textos
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ProjectChecker:
|
|
66
|
+
def __init__(self, project: Path, config_path: Path):
|
|
67
|
+
self.project = project
|
|
68
|
+
self.config = _config(config_path)
|
|
69
|
+
self.datasources = self.config.get('datasources') or {}
|
|
70
|
+
self._conexoes: Dict[str, object] = {}
|
|
71
|
+
self.problems: List[Problem] = []
|
|
72
|
+
self.notes: List[str] = []
|
|
73
|
+
self.queries_checked = 0
|
|
74
|
+
self.files = 0
|
|
75
|
+
|
|
76
|
+
# -- the database ---------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def _connection(self, nome: str):
|
|
79
|
+
from quantum.runtime.db_schema import SchemaError, connect_readonly
|
|
80
|
+
if nome not in self._conexoes:
|
|
81
|
+
try:
|
|
82
|
+
self._conexoes[nome] = connect_readonly(self.datasources[nome], self.project)
|
|
83
|
+
except SchemaError as exc:
|
|
84
|
+
self._conexoes[nome] = None
|
|
85
|
+
self.notes.append(f'datasource {nome!r} not checked: {exc}')
|
|
86
|
+
return self._conexoes[nome]
|
|
87
|
+
|
|
88
|
+
# -- a file ---------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def check_file(self, arquivo: Path) -> None:
|
|
91
|
+
from quantum.core.parser import QuantumParser
|
|
92
|
+
rel = arquivo.relative_to(self.project).as_posix() if arquivo.is_relative_to(self.project) else str(arquivo)
|
|
93
|
+
self.files += 1
|
|
94
|
+
try:
|
|
95
|
+
ast = QuantumParser(use_cache=False).parse_file(str(arquivo))
|
|
96
|
+
except Exception as exc: # a parse error is a problem like any other
|
|
97
|
+
primeira = str(exc).split('\n')[0]
|
|
98
|
+
self.problems.append(Problem(rel, getattr(exc, 'line', None), primeira))
|
|
99
|
+
return
|
|
100
|
+
colunas: Dict[str, List[str]] = {}
|
|
101
|
+
self._queries(ast, rel, colunas)
|
|
102
|
+
self._fields(ast, rel, colunas, {nome: nome for nome in colunas}, None)
|
|
103
|
+
|
|
104
|
+
def _queries(self, node, rel: str, colunas: Dict[str, List[str]]) -> None:
|
|
105
|
+
from quantum.core.ast_nodes import QueryNode
|
|
106
|
+
from quantum.runtime.db_schema import check_sql
|
|
107
|
+
for filho in _children(node):
|
|
108
|
+
if isinstance(filho, QueryNode):
|
|
109
|
+
ds = filho.datasource
|
|
110
|
+
if not ds or ds.startswith('knowledge:') or getattr(filho, 'source', None):
|
|
111
|
+
pass # RAG or query of queries: not SQL on a database
|
|
112
|
+
elif ds not in self.datasources:
|
|
113
|
+
self.problems.append(Problem(rel, getattr(filho, 'source_line', None),
|
|
114
|
+
f'<q:query name="{filho.name}">: datasource {ds!r} is not '
|
|
115
|
+
f'declared in quantum.config.yaml'))
|
|
116
|
+
else:
|
|
117
|
+
conexao = self._connection(ds)
|
|
118
|
+
if conexao is not None:
|
|
119
|
+
self.queries_checked += 1
|
|
120
|
+
erro, cols = check_sql(conexao, filho.sql or '')
|
|
121
|
+
if erro:
|
|
122
|
+
self.problems.append(Problem(rel, getattr(filho, 'source_line', None),
|
|
123
|
+
f'<q:query name="{filho.name}">: {erro}'))
|
|
124
|
+
elif cols is not None:
|
|
125
|
+
colunas[filho.name] = cols
|
|
126
|
+
self._queries(filho, rel, colunas)
|
|
127
|
+
|
|
128
|
+
def _fields(self, node, rel: str, colunas: Dict[str, List[str]], ligados: Dict[str, str],
|
|
129
|
+
linha: Optional[int]) -> None:
|
|
130
|
+
"""Each `{var.field}` where var is a query, or a row of one, names a column it returns."""
|
|
131
|
+
from quantum.core.features.loops.src.ast_node import LoopNode
|
|
132
|
+
from quantum.core.features.ui_engine.src.ast_nodes import UIColumnNode, UIListNode, UITableNode
|
|
133
|
+
linha = getattr(node, 'source_line', None) or linha
|
|
134
|
+
for texto in _texts(node):
|
|
135
|
+
for expressao in _EXPR.findall(texto):
|
|
136
|
+
for var, campo in re.findall(r'(?<![\w.])(\w+)\.(\w+)', expressao):
|
|
137
|
+
self._field(rel, linha, colunas, ligados, var, campo)
|
|
138
|
+
if isinstance(node, UIColumnNode) and node.key and '$tabela' in ligados:
|
|
139
|
+
self._field(rel, linha, colunas, ligados, '$tabela', node.key, f'<ui:column key="{node.key}">')
|
|
140
|
+
|
|
141
|
+
for filho in _children(node):
|
|
142
|
+
dentro = dict(ligados)
|
|
143
|
+
origem = None
|
|
144
|
+
if isinstance(filho, LoopNode):
|
|
145
|
+
origem = filho.query_name if filho.loop_type == 'query' else _single_name(filho.items)
|
|
146
|
+
if origem in colunas:
|
|
147
|
+
dentro[filho.var_name] = origem
|
|
148
|
+
elif isinstance(filho, (UITableNode, UIListNode)):
|
|
149
|
+
origem = _single_name(filho.source)
|
|
150
|
+
if origem in colunas:
|
|
151
|
+
dentro[filho.as_var or ('row' if isinstance(filho, UITableNode) else 'item')] = origem
|
|
152
|
+
if isinstance(filho, UITableNode):
|
|
153
|
+
dentro['$tabela'] = origem
|
|
154
|
+
self._fields(filho, rel, colunas, dentro, linha)
|
|
155
|
+
|
|
156
|
+
def _field(self, rel, linha, colunas, ligados, var, campo, como=None) -> None:
|
|
157
|
+
consulta = ligados.get(var)
|
|
158
|
+
if consulta is None or campo in _META:
|
|
159
|
+
return
|
|
160
|
+
cols = colunas.get(consulta, [])
|
|
161
|
+
if campo not in cols:
|
|
162
|
+
quem = como or f'{{{var}.{campo}}}'
|
|
163
|
+
self.problems.append(Problem(
|
|
164
|
+
rel, linha, f'{quem}: query "{consulta}" returns no column "{campo}" '
|
|
165
|
+
f'(columns: {", ".join(cols) or "none"})'))
|
|
166
|
+
|
|
167
|
+
# -- the project ----------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
def run(self) -> List[Problem]:
|
|
170
|
+
pasta = self.project / (self.config.get('paths', {}) or {}).get('components', 'components')
|
|
171
|
+
if not pasta.exists():
|
|
172
|
+
self.notes.append(f'no components folder at {pasta}')
|
|
173
|
+
return self.problems
|
|
174
|
+
for arquivo in sorted(pasta.rglob('*.q')):
|
|
175
|
+
self.check_file(arquivo)
|
|
176
|
+
for conexao in self._conexoes.values():
|
|
177
|
+
if conexao is not None:
|
|
178
|
+
conexao.close()
|
|
179
|
+
return self.problems
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _single_name(expr: Optional[str]) -> Optional[str]:
|
|
183
|
+
"""`{tarefas}` -> 'tarefas'; anything more complex -> None."""
|
|
184
|
+
m = re.fullmatch(r'\s*\{\s*(\w+)\s*\}\s*', expr or '')
|
|
185
|
+
return m.group(1) if m else None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def run_check(config_path: str = 'quantum.config.yaml') -> int:
|
|
189
|
+
"""`quantum check`: print each problem as file:line: message; exit 1 if there is any."""
|
|
190
|
+
caminho = Path(config_path).resolve()
|
|
191
|
+
checker = ProjectChecker(caminho.parent, caminho)
|
|
192
|
+
problemas = checker.run()
|
|
193
|
+
for nota in checker.notes:
|
|
194
|
+
print(f'[NOTE] {nota}')
|
|
195
|
+
for problema in problemas:
|
|
196
|
+
print(problema)
|
|
197
|
+
resumo = (f'{checker.files} file(s), {checker.queries_checked} query(ies) checked against the database')
|
|
198
|
+
if problemas:
|
|
199
|
+
print(f'\n{len(problemas)} problem(s) — {resumo}.')
|
|
200
|
+
return 1
|
|
201
|
+
print(f'OK — {resumo}.')
|
|
202
|
+
return 0
|
|
@@ -287,6 +287,7 @@ Examples:
|
|
|
287
287
|
quantum admin # Start the Quantum Admin
|
|
288
288
|
quantum console # The application's pages in the terminal
|
|
289
289
|
quantum desktop # The application's pages in a desktop window
|
|
290
|
+
quantum check # Pages parse, SQL compiles, query fields exist
|
|
290
291
|
quantum run game.q --engine godot # Build Godot 4 project
|
|
291
292
|
quantum run backup-job.q # Execute job
|
|
292
293
|
quantum deploy ./my-app # Deploy application
|
|
@@ -342,6 +343,10 @@ Examples:
|
|
|
342
343
|
console_parser.add_argument('path', nargs='?', default='/', help='Page to open (default: /)')
|
|
343
344
|
console_parser.add_argument('--config', default='quantum.config.yaml', help='Config file')
|
|
344
345
|
|
|
346
|
+
# Check: pages parse, queries compile, fields exist (M8, DEV-3)
|
|
347
|
+
check_parser = subparsers.add_parser('check', help='Check pages, SQL and query fields against the database')
|
|
348
|
+
check_parser.add_argument('--config', default='quantum.config.yaml', help='Config file')
|
|
349
|
+
|
|
345
350
|
# Desktop: the application's pages in a native window (UI-4)
|
|
346
351
|
desktop_parser = subparsers.add_parser('desktop', help="Open the application's pages in a desktop window")
|
|
347
352
|
desktop_parser.add_argument('path', nargs='?', default='/', help='Page to open (default: /)')
|
|
@@ -419,6 +424,10 @@ Examples:
|
|
|
419
424
|
from quantum.runtime.ui_console import run_console
|
|
420
425
|
sys.exit(run_console(args.config, args.path))
|
|
421
426
|
|
|
427
|
+
elif args.command == 'check':
|
|
428
|
+
from quantum.cli.check import run_check
|
|
429
|
+
sys.exit(run_check(args.config))
|
|
430
|
+
|
|
422
431
|
elif args.command == 'desktop':
|
|
423
432
|
from quantum.runtime.ui_desktop import run_desktop
|
|
424
433
|
sys.exit(run_desktop(args.config, args.path, width=args.width, height=args.height))
|
{quantum_framework-0.16.0 → quantum_framework-0.17.0}/quantum/core/features/ui_engine/src/parser.py
RENAMED
|
@@ -137,9 +137,24 @@ class UIParser:
|
|
|
137
137
|
import difflib
|
|
138
138
|
parecidas = difflib.get_close_matches(local_name, self.UI_TAG_MAP, n=1)
|
|
139
139
|
dica = f"; did you mean <ui:{parecidas[0]}>?" if parecidas else ""
|
|
140
|
-
|
|
140
|
+
erro = UIParseError(f"Unknown UI tag: <ui:{local_name}>{dica}")
|
|
141
|
+
erro.line = getattr(element, 'sourceline', None)
|
|
142
|
+
raise erro
|
|
141
143
|
method = getattr(self, method_name)
|
|
142
|
-
|
|
144
|
+
try:
|
|
145
|
+
node = method(element)
|
|
146
|
+
except UIParseError as exc:
|
|
147
|
+
if getattr(exc, 'line', None) is None:
|
|
148
|
+
exc.line = getattr(element, 'sourceline', None)
|
|
149
|
+
raise
|
|
150
|
+
# DEV-2: a ui:* element knows its line and file, like a q: statement.
|
|
151
|
+
if getattr(element, 'sourceline', None) and node is not None:
|
|
152
|
+
try:
|
|
153
|
+
node.source_line = element.sourceline
|
|
154
|
+
node.source_file = getattr(self.parent, '_source_path', None)
|
|
155
|
+
except AttributeError:
|
|
156
|
+
pass
|
|
157
|
+
return node
|
|
143
158
|
|
|
144
159
|
# ------------------------------------------------------------------
|
|
145
160
|
# Helpers
|
|
@@ -11,6 +11,7 @@ from typing import Union, Optional, List
|
|
|
11
11
|
# Fix imports
|
|
12
12
|
|
|
13
13
|
from quantum.core.html_compat import normalise_html
|
|
14
|
+
from quantum.core.xml_lines import fromstring_with_lines, line_of
|
|
14
15
|
from quantum.core.ast_nodes import (
|
|
15
16
|
QuantumNode, ComponentNode, ApplicationNode, JobNode,
|
|
16
17
|
QuantumParam, QuantumReturn, QuantumRoute,
|
|
@@ -222,6 +223,23 @@ def _explain_xml_error(error, source: str) -> str:
|
|
|
222
223
|
return message
|
|
223
224
|
|
|
224
225
|
|
|
226
|
+
def _at(erro: 'QuantumParseError', onde) -> 'QuantumParseError':
|
|
227
|
+
"""The error belongs to this node's (or element's) line."""
|
|
228
|
+
erro.line = line_of(onde)
|
|
229
|
+
return erro
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _with_line(erro: 'QuantumParseError', source: str, line: Optional[int] = None) -> 'QuantumParseError':
|
|
233
|
+
"""DEV-2: a parse error says where it is — `at line N: <the line>`."""
|
|
234
|
+
line = line or getattr(erro, 'line', None)
|
|
235
|
+
erro.line = line
|
|
236
|
+
if line and ' at line ' not in str(erro):
|
|
237
|
+
linhas = source.splitlines()
|
|
238
|
+
trecho = linhas[line - 1].strip() if 0 < line <= len(linhas) else ''
|
|
239
|
+
erro.args = (f"{erro}\n at line {line}: {trecho}" if trecho else f"{erro} (line {line})",)
|
|
240
|
+
return erro
|
|
241
|
+
|
|
242
|
+
|
|
225
243
|
class QuantumParser:
|
|
226
244
|
"""Main parser for .q files"""
|
|
227
245
|
|
|
@@ -370,17 +388,19 @@ class QuantumParser:
|
|
|
370
388
|
# parse for exactly those two reasons and returned HTTP 400.
|
|
371
389
|
content = normalise_html(source)
|
|
372
390
|
content = self._inject_namespace(content)
|
|
373
|
-
root =
|
|
391
|
+
root = fromstring_with_lines(content)
|
|
374
392
|
self._nest_else_siblings(root)
|
|
375
393
|
return self._parse_root_element(root, Path("<string>"))
|
|
376
394
|
except ET.ParseError as e:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
raise
|
|
395
|
+
erro = QuantumParseError(_explain_xml_error(e, source))
|
|
396
|
+
erro.line = getattr(e, 'position', (None, None))[0]
|
|
397
|
+
raise erro
|
|
398
|
+
except QuantumParseError as e:
|
|
399
|
+
raise _with_line(e, source)
|
|
380
400
|
except ParserError as e:
|
|
381
401
|
# A tag parser's own error is a parse error with a message meant
|
|
382
402
|
# for the author, not an "Unexpected error".
|
|
383
|
-
raise QuantumParseError(str(e)) from e
|
|
403
|
+
raise _with_line(QuantumParseError(str(e)), source, getattr(e, 'line', None)) from e
|
|
384
404
|
except Exception as e:
|
|
385
405
|
raise QuantumParseError(f"Unexpected error: {e}")
|
|
386
406
|
|
|
@@ -412,7 +432,9 @@ class QuantumParser:
|
|
|
412
432
|
self._source_path = str(path.resolve())
|
|
413
433
|
source = Path(file_path).read_text(encoding="utf-8")
|
|
414
434
|
return self.parse(source)
|
|
415
|
-
except QuantumParseError:
|
|
435
|
+
except QuantumParseError as e:
|
|
436
|
+
if getattr(e, 'file', None) is None:
|
|
437
|
+
e.file = str(path.resolve()) # DEV-2
|
|
416
438
|
raise
|
|
417
439
|
except ParserError as e:
|
|
418
440
|
# A tag parser's own error is a parse error with a message meant
|
|
@@ -498,7 +520,7 @@ class QuantumParser:
|
|
|
498
520
|
conhecidas |= {'return', 'else', 'elseif'}
|
|
499
521
|
parecidas = difflib.get_close_matches(name, sorted(conhecidas), n=3, cutoff=0.6)
|
|
500
522
|
dica = (" Did you mean " + " or ".join(f"<q:{p}>" for p in parecidas) + "?") if parecidas else ""
|
|
501
|
-
return f"<q:{name}> is not a Quantum tag.{dica}"
|
|
523
|
+
return f"<q:{name}> is not a Quantum tag (PARSE-1).{dica}"
|
|
502
524
|
|
|
503
525
|
@staticmethod
|
|
504
526
|
def _is_quantum_tag(element: ET.Element) -> bool:
|
|
@@ -683,12 +705,12 @@ class QuantumParser:
|
|
|
683
705
|
lidos = names_read(re.sub(r'\{([^{}]*)\}', r'\1', condicao)) & definidos
|
|
684
706
|
if lidos:
|
|
685
707
|
nome = sorted(lidos)[0]
|
|
686
|
-
raise QuantumParseError(
|
|
708
|
+
raise _at(QuantumParseError(
|
|
687
709
|
f'<q:if condition="{condicao}"> in <q:component name="{component.name}"> '
|
|
688
|
-
f'redirects, so it also runs before the page\'s q:action — where the page '
|
|
710
|
+
f'redirects (AUTH-6), so it also runs before the page\'s q:action — where the page '
|
|
689
711
|
f'has not run and {{{nome}}} does not exist, and the guard would let the '
|
|
690
712
|
f'action through. Read the scope directly (e.g. session.x), or protect the '
|
|
691
|
-
f'page with require_auth / require_role. See {HOW_A_PAGE_RUNS}')
|
|
713
|
+
f'page with require_auth / require_role. See {HOW_A_PAGE_RUNS}'), guarda)
|
|
692
714
|
|
|
693
715
|
# Nodes the HTML renderer draws. Anything else inside markup is a statement,
|
|
694
716
|
# and statements run in the execution pass, before the render pass sees
|
|
@@ -762,21 +784,21 @@ class QuantumParser:
|
|
|
762
784
|
for node in nodes:
|
|
763
785
|
if not isinstance(node, self._RENDERED) and not is_ui_node(node):
|
|
764
786
|
if onde and onde[0] == 'markup':
|
|
765
|
-
raise QuantumParseError(
|
|
787
|
+
raise _at(QuantumParseError(
|
|
766
788
|
f'{tag(node)} inside {onde[1]} in <q:component name="{component.name}"> '
|
|
767
|
-
f'never runs: statements run before the page is rendered, and markup '
|
|
789
|
+
f'never runs (PARSE-2): statements run before the page is rendered, and markup '
|
|
768
790
|
f'is only rendered. Move it above the markup (or into a q:action). '
|
|
769
|
-
f'See {HOW_A_PAGE_RUNS}')
|
|
791
|
+
f'See {HOW_A_PAGE_RUNS}'), node)
|
|
770
792
|
nome = getattr(node, 'name', None)
|
|
771
793
|
# A running total read AFTER the loop is fine; a value the
|
|
772
794
|
# loop's own rows read is not.
|
|
773
795
|
if onde and isinstance(nome, str) and usado(nome, onde[1]):
|
|
774
|
-
raise QuantumParseError(
|
|
796
|
+
raise _at(QuantumParseError(
|
|
775
797
|
f'{tag(node)} inside a q:loop whose rows use {{{nome}}} in '
|
|
776
798
|
f'<q:component name="{component.name}">: the loop runs for every item '
|
|
777
|
-
f'before any row is rendered, so every row would show the last value. '
|
|
799
|
+
f'before any row is rendered, so every row would show the last value (PARSE-2). '
|
|
778
800
|
f'Compute it in the expression instead, e.g. {{item.price * item.qty}}. '
|
|
779
|
-
f'See {HOW_A_PAGE_RUNS}')
|
|
801
|
+
f'See {HOW_A_PAGE_RUNS}'), node)
|
|
780
802
|
continue
|
|
781
803
|
if isinstance(node, HTMLNode):
|
|
782
804
|
andar(filhos(node), onde if onde and onde[0] == 'markup' else ('markup', f'<{node.tag}>'))
|
|
@@ -844,6 +866,27 @@ class QuantumParser:
|
|
|
844
866
|
if_node.add_else_statement(stmt)
|
|
845
867
|
|
|
846
868
|
def _parse_statement(self, element: ET.Element) -> Optional[QuantumNode]:
|
|
869
|
+
"""Parse one statement, and remember where it is in the file (DEV-2).
|
|
870
|
+
|
|
871
|
+
The node gets `source_line`, which runtime errors report; a parse
|
|
872
|
+
error from this element — or from inside it, if nothing deeper
|
|
873
|
+
claimed it — gets `line`.
|
|
874
|
+
"""
|
|
875
|
+
try:
|
|
876
|
+
node = self._parse_statement_node(element)
|
|
877
|
+
except (QuantumParseError, ParserError) as exc:
|
|
878
|
+
if getattr(exc, 'line', None) is None:
|
|
879
|
+
exc.line = getattr(element, 'sourceline', None)
|
|
880
|
+
raise
|
|
881
|
+
if node is not None and getattr(element, 'sourceline', None):
|
|
882
|
+
try:
|
|
883
|
+
node.source_line = element.sourceline
|
|
884
|
+
node.source_file = self._source_path
|
|
885
|
+
except AttributeError:
|
|
886
|
+
pass # a node with __slots__
|
|
887
|
+
return node
|
|
888
|
+
|
|
889
|
+
def _parse_statement_node(self, element: ET.Element) -> Optional[QuantumNode]:
|
|
847
890
|
"""Parse individual statement (return, set, dispatchEvent, etc)"""
|
|
848
891
|
element_type = self._get_element_name(element)
|
|
849
892
|
|
|
@@ -857,7 +900,9 @@ class QuantumParser:
|
|
|
857
900
|
try:
|
|
858
901
|
return UIParser(self, page_mode=True).parse_ui_element(element_type, element)
|
|
859
902
|
except UIParseError as exc:
|
|
860
|
-
|
|
903
|
+
erro = QuantumParseError(str(exc))
|
|
904
|
+
erro.line = getattr(exc, 'line', None) # the inner ui:* element's line
|
|
905
|
+
raise erro from exc
|
|
861
906
|
|
|
862
907
|
# PARSE-1: <q:html>, <q:div> — an HTML element name in the q:
|
|
863
908
|
# namespace. The registry answered for it with the HTML parser, so
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""XML into an ElementTree whose elements know their line (M2, DEV-2).
|
|
2
|
+
|
|
3
|
+
`xml.etree` does not keep source positions, so an error found after parsing —
|
|
4
|
+
a statement inside markup, an unknown attribute, a failing expression — could
|
|
5
|
+
name the tag but not where it is. This builds the same tree with expat (the
|
|
6
|
+
parser ElementTree itself runs on) and sets `element.sourceline` on each
|
|
7
|
+
element.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from xml.etree import ElementTree as ET
|
|
11
|
+
from xml.parsers import expat
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _Element(ET.Element):
|
|
15
|
+
"""An Element that can carry `sourceline` (the C Element takes no new attributes)."""
|
|
16
|
+
sourceline = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def fromstring_with_lines(text: str) -> ET.Element:
|
|
20
|
+
"""Like ET.fromstring(text); every element gets `sourceline` (1-based)."""
|
|
21
|
+
builder = ET.TreeBuilder(element_factory=_Element)
|
|
22
|
+
parser = expat.ParserCreate(None, '}')
|
|
23
|
+
parser.buffer_text = True
|
|
24
|
+
parser.ordered_attributes = True
|
|
25
|
+
|
|
26
|
+
def nome(bruto: str) -> str:
|
|
27
|
+
# expat with a separator reports "uri}local"; ElementTree spells it "{uri}local".
|
|
28
|
+
return '{' + bruto if '}' in bruto else bruto
|
|
29
|
+
|
|
30
|
+
def inicio(tag, atributos):
|
|
31
|
+
pares = dict(zip(atributos[0::2], atributos[1::2]))
|
|
32
|
+
elemento = builder.start(nome(tag), {nome(k): v for k, v in pares.items()})
|
|
33
|
+
elemento.sourceline = parser.CurrentLineNumber
|
|
34
|
+
|
|
35
|
+
parser.StartElementHandler = inicio
|
|
36
|
+
parser.EndElementHandler = lambda tag: builder.end(nome(tag))
|
|
37
|
+
parser.CharacterDataHandler = builder.data
|
|
38
|
+
try:
|
|
39
|
+
parser.Parse(text, True)
|
|
40
|
+
except expat.ExpatError as exc:
|
|
41
|
+
erro = ET.ParseError(str(exc))
|
|
42
|
+
erro.code = exc.code
|
|
43
|
+
erro.position = (exc.lineno, exc.offset)
|
|
44
|
+
raise erro from None
|
|
45
|
+
return builder.close()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def line_of(item) -> int:
|
|
49
|
+
"""The source line of an element or an AST node, or None."""
|
|
50
|
+
return getattr(item, 'sourceline', None) or getattr(item, 'source_line', None)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def tag_error(exc: BaseException, node) -> None:
|
|
54
|
+
"""DEV-2: the innermost node an error passes through names its file and line.
|
|
55
|
+
|
|
56
|
+
Innermost across wrapping too: an error in a called component is re-raised
|
|
57
|
+
as a new exception by the page; the line inside the component, already in
|
|
58
|
+
the chain, wins over the line of the call."""
|
|
59
|
+
if error_location(exc)[1] is not None:
|
|
60
|
+
return
|
|
61
|
+
linha = getattr(node, 'source_line', None)
|
|
62
|
+
if not linha:
|
|
63
|
+
return
|
|
64
|
+
try:
|
|
65
|
+
exc.quantum_line = linha
|
|
66
|
+
exc.quantum_file = getattr(node, 'source_file', None)
|
|
67
|
+
except (AttributeError, TypeError):
|
|
68
|
+
pass # an exception type with __slots__
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def error_location(exc: BaseException):
|
|
72
|
+
"""(file, line) of an error or of any error it wraps; (None, None) if unknown."""
|
|
73
|
+
visto = set()
|
|
74
|
+
while exc is not None and id(exc) not in visto:
|
|
75
|
+
visto.add(id(exc))
|
|
76
|
+
if getattr(exc, 'quantum_line', None) is not None:
|
|
77
|
+
return getattr(exc, 'quantum_file', None), exc.quantum_line
|
|
78
|
+
if getattr(exc, 'line', None) is not None and type(exc).__name__ == 'QuantumParseError':
|
|
79
|
+
return getattr(exc, 'file', None), exc.line
|
|
80
|
+
exc = exc.__cause__ or exc.__context__
|
|
81
|
+
return None, None
|
|
@@ -107,7 +107,12 @@ class ActionHandler:
|
|
|
107
107
|
context.set_variable(key, value)
|
|
108
108
|
|
|
109
109
|
# 4. Execute action body
|
|
110
|
-
|
|
110
|
+
try:
|
|
111
|
+
redirect_info = self._execute_action_body(action, context)
|
|
112
|
+
finally:
|
|
113
|
+
# M12: the /_dev panel shows the action's variables (no-op off debug).
|
|
114
|
+
from quantum.runtime.dev_panel import note_scopes
|
|
115
|
+
note_scopes(context, 'action')
|
|
111
116
|
|
|
112
117
|
# 5. Sync session mutations back to Flask (mirrors web_server.py's
|
|
113
118
|
# render path, which does the same after execute_component()).
|
|
@@ -144,7 +149,7 @@ class ActionHandler:
|
|
|
144
149
|
mensagem = str(e)
|
|
145
150
|
if 'is not defined' in mensagem:
|
|
146
151
|
from quantum.core.docs_links import HOW_A_PAGE_RUNS
|
|
147
|
-
mensagem += (". A q:action does not run the page's statements: "
|
|
152
|
+
mensagem += (". A q:action does not run the page's statements (ACT-9): "
|
|
148
153
|
f"query or compute what it needs inside the action. See {HOW_A_PAGE_RUNS}")
|
|
149
154
|
logger.exception(
|
|
150
155
|
"q:action %r failed: %s", getattr(action, 'name', '?'), mensagem
|
|
@@ -284,7 +284,7 @@ class ComponentRuntime:
|
|
|
284
284
|
return None
|
|
285
285
|
|
|
286
286
|
except Exception as e:
|
|
287
|
-
raise ComponentExecutionError(f"Execution error: {e}")
|
|
287
|
+
raise ComponentExecutionError(f"Execution error: {e}") from e
|
|
288
288
|
|
|
289
289
|
def _enter(self, component: ComponentNode, params: Dict[str, Any]) -> None:
|
|
290
290
|
"""Make `component` current: its functions, and the scopes passed in params."""
|
|
@@ -7,11 +7,13 @@ import time
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Dict, Any, List, Optional
|
|
9
9
|
from dataclasses import dataclass, asdict
|
|
10
|
+
from quantum.runtime.dev_panel import note_query
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger('quantum.database')
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@dataclass
|
|
16
|
+
|
|
15
17
|
class QueryResult:
|
|
16
18
|
"""Container for query results and metadata"""
|
|
17
19
|
data: List[Dict[str, Any]]
|
|
@@ -346,6 +348,8 @@ class DatabaseService:
|
|
|
346
348
|
|
|
347
349
|
# Calculate execution time
|
|
348
350
|
execution_time = (time.time() - start_time) * 1000 # Convert to ms
|
|
351
|
+
# M12: the /_dev panel shows each query of the request (no-op off debug).
|
|
352
|
+
note_query(datasource_name, sql, params, execution_time, record_count)
|
|
349
353
|
|
|
350
354
|
return QueryResult(
|
|
351
355
|
data=data,
|
|
@@ -369,6 +373,7 @@ class DatabaseService:
|
|
|
369
373
|
# a failed rollback on a broken connection must not
|
|
370
374
|
# replace it. Deliberately silent.
|
|
371
375
|
pass
|
|
376
|
+
note_query(datasource_name, sql, params, (time.time() - start_time) * 1000, None, error=str(e))
|
|
372
377
|
raise QueryExecutionError(f"Query execution failed: {e}")
|
|
373
378
|
|
|
374
379
|
def _prepare_query(self, sql: str, params: Dict[str, Any], db_type: str) -> tuple:
|