quantum-framework 0.12.0__tar.gz → 0.14.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.12.0/quantum_framework.egg-info → quantum_framework-0.14.0}/PKG-INFO +2 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/pyproject.toml +2 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/migrate.py +44 -4
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/runner.py +4 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/expression_stdlib.py +28 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/expressions.py +69 -4
- quantum_framework-0.14.0/quantum/core/guards.py +53 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parser.py +89 -18
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/control_flow/if_parser.py +25 -22
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/control_flow/set_parser.py +6 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/invoke_parser.py +6 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/forms/action_parser.py +17 -7
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/functions/function_parser.py +7 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/functions/param_parser.py +8 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/action_handler.py +8 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/component.py +64 -62
- quantum_framework-0.14.0/quantum/runtime/conditions.py +54 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/database_service.py +32 -40
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/error_handler.py +1 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/execution_context.py +0 -3
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/__init__.py +2 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/base.py +3 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/control_flow/__init__.py +2 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/control_flow/if_executor.py +14 -10
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/control_flow/loop_executor.py +6 -6
- quantum_framework-0.14.0/quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/logging_setup.py +3 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/param_validation.py +34 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/renderer.py +12 -38
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_builder.py +12 -7
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/web_server.py +141 -53
- {quantum_framework-0.12.0 → quantum_framework-0.14.0/quantum_framework.egg-info}/PKG-INFO +2 -1
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_framework.egg-info/SOURCES.txt +3 -2
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_framework.egg-info/requires.txt +1 -0
- quantum_framework-0.12.0/quantum/core/features/query/src/database_service.py +0 -322
- quantum_framework-0.12.0/tests/test_projects_blog_components_admin.py +0 -514
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/LICENSE +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/README.md +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/admin.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/build.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/dev.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/docs.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/lint.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/new.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/serve.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/commands/test.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/deploy.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/hot_reload.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/jobs.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/mq.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/pkg.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/server_process.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/cli/utils.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/base_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/cli.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/expression_transformer.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/javascript/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/javascript/generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/optimizer.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/python/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/python/generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/python/runtime.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/compiler/transpiler.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/ast_nodes.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/config_env.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/expression_diagnostics.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/agents/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/agents/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/data_import/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/data_import/src/runtime.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/dump/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/dump/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/dump/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/dump/src/runtime.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/functions/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/functions/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/invocation/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/invocation/src/runtime.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/logging/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/logging/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/logging/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/logging/src/runtime.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/loops/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/loops/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/query/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/query/src/query_validators.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/state_management/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/theming/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/theming/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/theming/src/presets.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/ui_engine/src/parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/websocket/src/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/html_compat.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parser_registry.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/ai/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/ai/team_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/base.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/composition/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/composition/import_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/data_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/query_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/events/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/forms/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/functions/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/functions/return_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/html/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/html/html_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/jobs/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/persistence/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/persistence/persist_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/routing/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/routing/route_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/scripting/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/services/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/services/dump_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/services/file_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/services/log_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/services/mail_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/tiers.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/packages/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/packages/manager.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/packages/manifest.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/packages/registry.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/packages/resolver.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/plugins/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/plugins/hooks.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/plugins/loader.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/plugins/manifest.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/plugins/registry.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/adapters/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/agent_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ast_cache.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/auth_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/component_composer.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/component_resolver.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/email_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executor_registry.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/ai/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/data/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/data/data_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/data/invoke_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/data/query_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/services/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/services/file_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/services/log_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/expression_cache.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/file_upload_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/function_registry.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/game_builder.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/game_code_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/game_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/godot_code_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/godot_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/job_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/knowledge_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/llm_cache.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/llm_providers.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/llm_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/message_broker.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/message_queue_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/pypy_compat.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/python_bridge.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/query_validators.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/service_container.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/terminal_builder.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/terminal_code_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/terminal_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/testing_builder.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/testing_code_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/testing_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_desktop_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_desktop_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_html_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_html_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_mobile_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_textual_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_textual_templates.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_tokens.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/ui_validator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/validators.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/websocket_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/websocket_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/websocket_transport.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/runtime/wsgi.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/services.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/utils/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/assets/quantum-admin-connectors.js +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/assets/quantum-admin.css +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/assets/quantum-source-highlight.js +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/audit_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/auth_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/component_discovery.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/config_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/connector_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/crud.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/database.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/db_setup_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/deploy_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/docker_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/environment_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/git_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/health_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/job_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/log_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/main.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/models.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/pipeline_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/resource_manager.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/schemas.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/secret_manager.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/settings_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/template_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/test_execution_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/test_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/webhook_service.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/backend/websocket_manager.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/_layout/AdminShell.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/agents.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/app/[name].q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/applications.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/component/[...path].q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/components.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/connectors.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/dashboard.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/database.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/features.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/index.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/jobs.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/login.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/logout.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/settings.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/source.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/components/admin/tests.q +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/run.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/__init__.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/_base.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/_component_test_generator.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/_yaml_editor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/apps.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/auth.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/components.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/connectors.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/projects.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/repository.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/settings.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_admin/services/yaml_import.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_framework.egg-info/entry_points.txt +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum_framework.egg-info/top_level.txt +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/setup.cfg +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_agent.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_animations.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_ast_cache.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_cli_commands.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_component_library.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_desktop_bridge.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_expression_cache.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_form_validation.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_game_codegen.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_game_e2e.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_game_engine.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_game_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_godot_codegen.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_job_executor.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_llm_providers.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_message_broker.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_message_queue.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_mobile_target.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_multi_agent.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_packages.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_plugins.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_pypy_compat.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_python_scripting.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_state_persistence.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_terminal_codegen.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_terminal_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_testing_codegen.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_testing_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_theming.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_ui_html_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_ui_parity.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_ui_parser.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_ui_textual_adapter.py +0 -0
- {quantum_framework-0.12.0 → quantum_framework-0.14.0}/tests/test_websocket.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantum-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: Full-stack declarative framework for building web, desktop, and mobile applications
|
|
5
5
|
Author-email: Daniel Gregorio <danielgregorio2611@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -28,6 +28,7 @@ Provides-Extra: dev
|
|
|
28
28
|
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
30
30
|
Requires-Dist: pytest-mock>=3.11.1; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-xdist>=3.5; extra == "dev"
|
|
31
32
|
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
32
33
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
33
34
|
Provides-Extra: db
|
|
@@ -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.14.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"}
|
|
@@ -35,6 +35,7 @@ dev = [
|
|
|
35
35
|
"pytest>=7.4.0",
|
|
36
36
|
"pytest-cov>=4.1.0",
|
|
37
37
|
"pytest-mock>=3.11.1",
|
|
38
|
+
"pytest-xdist>=3.5",
|
|
38
39
|
"black>=23.0.0",
|
|
39
40
|
"ruff>=0.1.0",
|
|
40
41
|
]
|
|
@@ -70,7 +70,9 @@ class DatabaseConnection:
|
|
|
70
70
|
if not caminho.is_absolute():
|
|
71
71
|
caminho = self.project_path / caminho
|
|
72
72
|
caminho.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
-
|
|
73
|
+
# isolation_level=None: execute_script opens the transaction
|
|
74
|
+
# itself, so a CREATE TABLE is inside it too (DB-8).
|
|
75
|
+
self._conn = sqlite3.connect(str(caminho), isolation_level=None)
|
|
74
76
|
self._db_type = 'sqlite'
|
|
75
77
|
logger.info("Migrating datasource %s (SQLite at %s)", nome, caminho)
|
|
76
78
|
return self._conn
|
|
@@ -128,6 +130,32 @@ class DatabaseConnection:
|
|
|
128
130
|
cursor.execute(sql)
|
|
129
131
|
return cursor
|
|
130
132
|
|
|
133
|
+
def execute_script(self, sql: str):
|
|
134
|
+
"""Run a whole migration file, in one transaction (DB-8).
|
|
135
|
+
|
|
136
|
+
sqlite3's execute() takes ONE statement, so every migration with more
|
|
137
|
+
than one — a table and its index — failed with "You can only execute
|
|
138
|
+
one statement at a time". The statements are split here and run
|
|
139
|
+
inside an explicit BEGIN, which commit()/rollback() close: a failure
|
|
140
|
+
halfway leaves no table behind.
|
|
141
|
+
"""
|
|
142
|
+
if self._db_type != 'sqlite':
|
|
143
|
+
return self.execute(sql)
|
|
144
|
+
import sqlite3
|
|
145
|
+
cursor = self._conn.cursor()
|
|
146
|
+
if not self._conn.in_transaction:
|
|
147
|
+
cursor.execute('BEGIN')
|
|
148
|
+
pedaco = ''
|
|
149
|
+
for linha in sql.splitlines(keepends=True):
|
|
150
|
+
pedaco += linha
|
|
151
|
+
if sqlite3.complete_statement(pedaco):
|
|
152
|
+
if pedaco.strip():
|
|
153
|
+
cursor.execute(pedaco)
|
|
154
|
+
pedaco = ''
|
|
155
|
+
if pedaco.strip() and not all(l.strip().startswith('--') or not l.strip() for l in pedaco.splitlines()):
|
|
156
|
+
raise MigrationError(f"incomplete SQL statement at the end: {pedaco.strip()[:80]!r}")
|
|
157
|
+
return cursor
|
|
158
|
+
|
|
131
159
|
def commit(self):
|
|
132
160
|
self._conn.commit()
|
|
133
161
|
|
|
@@ -139,6 +167,18 @@ class DatabaseConnection:
|
|
|
139
167
|
self._conn.close()
|
|
140
168
|
|
|
141
169
|
|
|
170
|
+
def _paths_migrations(project_path: Path) -> str:
|
|
171
|
+
"""CFG-3: paths.migrations in quantum.config.yaml (default ./migrations)."""
|
|
172
|
+
try:
|
|
173
|
+
import yaml
|
|
174
|
+
from quantum.core.config_env import expand_env
|
|
175
|
+
with open(project_path / "quantum.config.yaml", encoding="utf-8") as f:
|
|
176
|
+
config = expand_env(yaml.safe_load(f) or {})
|
|
177
|
+
return (config.get("paths") or {}).get("migrations") or "migrations"
|
|
178
|
+
except (OSError, ValueError, AttributeError):
|
|
179
|
+
return "migrations"
|
|
180
|
+
|
|
181
|
+
|
|
142
182
|
class MigrationRunner:
|
|
143
183
|
"""Runs database migrations"""
|
|
144
184
|
|
|
@@ -146,7 +186,7 @@ class MigrationRunner:
|
|
|
146
186
|
|
|
147
187
|
def __init__(self, project_path: Path = None, datasource: str = None):
|
|
148
188
|
self.project_path = project_path or Path.cwd()
|
|
149
|
-
self.migrations_dir = self.project_path /
|
|
189
|
+
self.migrations_dir = self.project_path / _paths_migrations(self.project_path)
|
|
150
190
|
self.db = DatabaseConnection(self.project_path, datasource)
|
|
151
191
|
|
|
152
192
|
def _ensure_migrations_table(self):
|
|
@@ -275,7 +315,7 @@ class MigrationRunner:
|
|
|
275
315
|
click.echo(f"Applying {migration.version}: {migration.name}...")
|
|
276
316
|
|
|
277
317
|
# Execute migration SQL
|
|
278
|
-
self.db.
|
|
318
|
+
self.db.execute_script(migration.content)
|
|
279
319
|
|
|
280
320
|
# Record migration
|
|
281
321
|
self._record_migration(migration)
|
|
@@ -338,7 +378,7 @@ class MigrationRunner:
|
|
|
338
378
|
click.echo(f"Rolling back {version}: {migration_info['name']}...")
|
|
339
379
|
|
|
340
380
|
# Execute rollback SQL
|
|
341
|
-
self.db.
|
|
381
|
+
self.db.execute_script(down_file.read_text())
|
|
342
382
|
|
|
343
383
|
# Remove migration record
|
|
344
384
|
self._remove_migration_record(version)
|
|
@@ -24,6 +24,7 @@ if os.name == 'nt': # Windows
|
|
|
24
24
|
from quantum.core.parser import QuantumParser, QuantumParseError
|
|
25
25
|
from quantum.core.ast_nodes import ComponentNode, ApplicationNode, JobNode
|
|
26
26
|
from quantum.runtime.component import ComponentRuntime, ComponentExecutionError
|
|
27
|
+
from quantum.runtime.executors.control_flow.redirect_executor import PageRedirect
|
|
27
28
|
from quantum.runtime.web_server import QuantumWebServer
|
|
28
29
|
|
|
29
30
|
# Import deploy commands (lazy load to avoid import errors if requests not installed)
|
|
@@ -166,6 +167,9 @@ class QuantumRunner:
|
|
|
166
167
|
except ComponentExecutionError as e:
|
|
167
168
|
print(f"[ERROR] Execution error: {e}")
|
|
168
169
|
return 1
|
|
170
|
+
except PageRedirect as r:
|
|
171
|
+
print(f"[REDIRECT] {r.status} {r.url}")
|
|
172
|
+
return 0
|
|
169
173
|
|
|
170
174
|
def _execute_application(self, app: ApplicationNode, debug: bool = False) -> int:
|
|
171
175
|
"""Execute q:application"""
|
|
@@ -15,6 +15,9 @@ Functions are looked up by name from expressions, so anything added here
|
|
|
15
15
|
becomes language surface. That is the reason to keep it short.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
+
import math
|
|
19
|
+
import re
|
|
20
|
+
from decimal import Decimal, ROUND_HALF_UP
|
|
18
21
|
from datetime import datetime, timedelta
|
|
19
22
|
from typing import Any, Iterable, Optional
|
|
20
23
|
|
|
@@ -84,6 +87,13 @@ def _split(v: Any, sep: str = ",") -> list: return str(v).split(sep)
|
|
|
84
87
|
def _contains(v: Any, needle: Any) -> bool: return str(needle) in str(v)
|
|
85
88
|
|
|
86
89
|
|
|
90
|
+
def _slugify(v: Any) -> str:
|
|
91
|
+
"""'Olá, Mundo!' -> 'ola-mundo': for URLs made from a title (EXPR-9)."""
|
|
92
|
+
import unicodedata
|
|
93
|
+
texto = unicodedata.normalize('NFKD', str(v)).encode('ascii', 'ignore').decode('ascii')
|
|
94
|
+
return re.sub(r'[^a-z0-9]+', '-', texto.lower()).strip('-')
|
|
95
|
+
|
|
96
|
+
|
|
87
97
|
# -- collections -----------------------------------------------------------
|
|
88
98
|
|
|
89
99
|
def _len(v: Any) -> int:
|
|
@@ -114,8 +124,21 @@ def _sort(v: Iterable, reverse: bool = False) -> list:
|
|
|
114
124
|
# -- numbers ---------------------------------------------------------------
|
|
115
125
|
|
|
116
126
|
def _round(v: Any, digits: int = 0) -> Any:
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
# EXPR-9: half away from zero, as in ColdFusion and spreadsheets. Python's
|
|
128
|
+
# round() is banker's rounding over binary floats: round(2.5) was 2 and
|
|
129
|
+
# round(0.125, 2) was 0.12. Decimal over the number's text avoids both.
|
|
130
|
+
casas = int(digits)
|
|
131
|
+
passo = Decimal(1).scaleb(-casas)
|
|
132
|
+
result = Decimal(str(v)).quantize(passo, rounding=ROUND_HALF_UP)
|
|
133
|
+
return int(result) if casas <= 0 else float(result)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _ceil(v: Any) -> int:
|
|
137
|
+
return math.ceil(float(v))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _floor(v: Any) -> int:
|
|
141
|
+
return math.floor(float(v))
|
|
119
142
|
|
|
120
143
|
|
|
121
144
|
# -- passwords ---------------------------------------------------------------
|
|
@@ -165,6 +188,7 @@ STDLIB = {
|
|
|
165
188
|
'replace': _replace,
|
|
166
189
|
'split': _split,
|
|
167
190
|
'contains': _contains,
|
|
191
|
+
'slugify': _slugify,
|
|
168
192
|
# collections
|
|
169
193
|
'len': _len,
|
|
170
194
|
'first': _first,
|
|
@@ -173,6 +197,8 @@ STDLIB = {
|
|
|
173
197
|
'sort': _sort,
|
|
174
198
|
# numbers
|
|
175
199
|
'round': _round,
|
|
200
|
+
'ceil': _ceil,
|
|
201
|
+
'floor': _floor,
|
|
176
202
|
'abs': abs,
|
|
177
203
|
'min': min,
|
|
178
204
|
'max': max,
|
|
@@ -23,6 +23,7 @@ access, and the whole arithmetic/comparison/call surface is about 15%.
|
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
import ast
|
|
26
|
+
import contextvars
|
|
26
27
|
import math
|
|
27
28
|
import operator
|
|
28
29
|
import re
|
|
@@ -30,6 +31,11 @@ from typing import Any, Callable, Dict, Optional
|
|
|
30
31
|
|
|
31
32
|
from quantum.core.expression_stdlib import STDLIB
|
|
32
33
|
|
|
34
|
+
# EXPR-8: in a condition, a key a scope does not have (`session.userId`
|
|
35
|
+
# before login) is absent, not a failure — see ExpressionEvaluator.evaluate.
|
|
36
|
+
_SCOPES = ('session', 'application', 'request', 'cookie', 'form', 'query')
|
|
37
|
+
_ABSENT_SCOPE_KEY_IS_NONE = contextvars.ContextVar('absent_scope_key_is_none', default=False)
|
|
38
|
+
|
|
33
39
|
|
|
34
40
|
class ExpressionError(Exception):
|
|
35
41
|
"""Raised when an expression cannot be evaluated."""
|
|
@@ -249,6 +255,43 @@ def _normalise_js_operators(source: str) -> str:
|
|
|
249
255
|
return ''.join(out)
|
|
250
256
|
|
|
251
257
|
|
|
258
|
+
# EXPR-10: what a JavaScript habit is called here. `Math.ceil(x)` and
|
|
259
|
+
# `text.split(' ')` are the first things people write; the bare "only named
|
|
260
|
+
# function calls are allowed" did not say what to write instead.
|
|
261
|
+
_JS_EQUIVALENTS = {
|
|
262
|
+
'Math.ceil': 'ceil(x)', 'Math.floor': 'floor(x)', 'Math.round': 'round(x)',
|
|
263
|
+
'Math.max': 'max(a, b)', 'Math.min': 'min(a, b)', 'Math.abs': 'abs(x)',
|
|
264
|
+
'Date.now': 'now()', 'parseInt': 'int(x)', 'parseFloat': 'float(x)',
|
|
265
|
+
'split': "split(text, ' ')", 'join': "join(items, ', ')", 'toUpperCase': 'upper(text)',
|
|
266
|
+
'toLowerCase': 'lower(text)', 'trim': 'trim(text)', 'replace': 'replace(text, old, new)',
|
|
267
|
+
'includes': 'contains(text, part)', 'indexOf': 'contains(text, part)',
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _method_hint(func: ast.AST) -> str:
|
|
272
|
+
if not isinstance(func, ast.Attribute):
|
|
273
|
+
return ""
|
|
274
|
+
base = func.value.id if isinstance(func.value, ast.Name) else None
|
|
275
|
+
chave = f"{base}.{func.attr}" if base in ('Math', 'Date') else func.attr
|
|
276
|
+
if chave in _JS_EQUIVALENTS:
|
|
277
|
+
escrito = chave if '.' in chave else f"x.{func.attr}"
|
|
278
|
+
return f"; write {_JS_EQUIVALENTS[chave]} instead of {escrito}(...)"
|
|
279
|
+
return f"; call functions by name, e.g. {func.attr}(x), not x.{func.attr}()"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def names_read(expression: str) -> set:
|
|
283
|
+
"""The root names an expression reads — `user` in `user.name > 1` — not the
|
|
284
|
+
functions it calls. An expression that does not parse reads nothing here;
|
|
285
|
+
evaluating it reports the error."""
|
|
286
|
+
try:
|
|
287
|
+
tree = ast.parse(_normalise_js_operators(expression.strip()), mode='eval')
|
|
288
|
+
except SyntaxError:
|
|
289
|
+
return set()
|
|
290
|
+
chamadas = {id(n.func) for n in ast.walk(tree) if isinstance(n, ast.Call)}
|
|
291
|
+
return {n.id for n in ast.walk(tree)
|
|
292
|
+
if isinstance(n, ast.Name) and id(n) not in chamadas}
|
|
293
|
+
|
|
294
|
+
|
|
252
295
|
class ExpressionEvaluator:
|
|
253
296
|
"""Evaluates Quantum `{...}` expressions against a variable context."""
|
|
254
297
|
|
|
@@ -274,8 +317,24 @@ class ExpressionEvaluator:
|
|
|
274
317
|
|
|
275
318
|
# -- public API --------------------------------------------------------
|
|
276
319
|
|
|
277
|
-
def evaluate(self, expression: str, context: Dict[str, Any]
|
|
278
|
-
|
|
320
|
+
def evaluate(self, expression: str, context: Dict[str, Any],
|
|
321
|
+
absent_scope_key_is_none: bool = False) -> Any:
|
|
322
|
+
"""Evaluate an expression, raising ExpressionError on any failure.
|
|
323
|
+
|
|
324
|
+
absent_scope_key_is_none (conditions, EXPR-8): `session.x` where the
|
|
325
|
+
scope has no `x` reads as None instead of raising. A condition is a
|
|
326
|
+
presence test, and a scope key that is not there yet is the ordinary
|
|
327
|
+
case (a page runs before login) — raising made the WHOLE condition
|
|
328
|
+
false, so `not session.authenticated` was false for a visitor with no
|
|
329
|
+
session and a guard written that way never fired.
|
|
330
|
+
"""
|
|
331
|
+
marca = _ABSENT_SCOPE_KEY_IS_NONE.set(absent_scope_key_is_none)
|
|
332
|
+
try:
|
|
333
|
+
return self._evaluate(expression, context)
|
|
334
|
+
finally:
|
|
335
|
+
_ABSENT_SCOPE_KEY_IS_NONE.reset(marca)
|
|
336
|
+
|
|
337
|
+
def _evaluate(self, expression: str, context: Dict[str, Any]) -> Any:
|
|
279
338
|
if expression is None:
|
|
280
339
|
raise ExpressionError("expression is None")
|
|
281
340
|
|
|
@@ -473,6 +532,11 @@ class ExpressionEvaluator:
|
|
|
473
532
|
plano = self._dotted_name(node)
|
|
474
533
|
if plano is not None and plano in ctx:
|
|
475
534
|
return ctx[plano]
|
|
535
|
+
if (_ABSENT_SCOPE_KEY_IS_NONE.get() and isinstance(node.value, ast.Name)
|
|
536
|
+
and node.value.id in _SCOPES):
|
|
537
|
+
escopo = ctx.get(node.value.id)
|
|
538
|
+
if escopo is None or (isinstance(escopo, dict) and node.attr not in escopo):
|
|
539
|
+
return None
|
|
476
540
|
return self._attribute(self._eval(node.value, ctx), node.attr)
|
|
477
541
|
|
|
478
542
|
if isinstance(node, ast.Subscript):
|
|
@@ -588,7 +652,7 @@ class ExpressionEvaluator:
|
|
|
588
652
|
|
|
589
653
|
def _call(self, node: ast.Call, ctx: Dict[str, Any]) -> Any:
|
|
590
654
|
if not isinstance(node.func, ast.Name):
|
|
591
|
-
raise ExpressionError("only named function calls are allowed")
|
|
655
|
+
raise ExpressionError("only named function calls are allowed" + _method_hint(node.func))
|
|
592
656
|
fn = self.functions.get(node.func.id)
|
|
593
657
|
if fn is None and self.function_resolver is not None:
|
|
594
658
|
# A q:function declared by the component being executed.
|
|
@@ -600,7 +664,8 @@ class ExpressionEvaluator:
|
|
|
600
664
|
if fn is None:
|
|
601
665
|
raise ExpressionError(
|
|
602
666
|
f"function {node.func.id!r} is not defined"
|
|
603
|
-
f"{
|
|
667
|
+
+ (f"; write {_JS_EQUIVALENTS[node.func.id]}" if node.func.id in _JS_EQUIVALENTS
|
|
668
|
+
else _suggest(node.func.id, self.functions))
|
|
604
669
|
)
|
|
605
670
|
args = [self._eval(a, ctx) for a in node.args]
|
|
606
671
|
kwargs = {kw.arg: self._eval(kw.value, ctx) for kw in node.keywords}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Page guards (AUTH-6): a q:if at the top of a page whose branch has a q:redirect.
|
|
2
|
+
|
|
3
|
+
<q:if condition="not session.authenticated">
|
|
4
|
+
<q:redirect url="/login" />
|
|
5
|
+
</q:if>
|
|
6
|
+
|
|
7
|
+
A q:action does not run the page's statements, so a guard written like this
|
|
8
|
+
used to protect only the GET — the POST ran the action without a session.
|
|
9
|
+
Guards now run before each action of the page as well as before the render.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import List
|
|
13
|
+
|
|
14
|
+
from quantum.core.ast_nodes import RedirectNode
|
|
15
|
+
from quantum.core.features.conditionals.src.ast_node import IfNode
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _branches(node: IfNode) -> List:
|
|
19
|
+
corpo = list(node.if_body)
|
|
20
|
+
for bloco in node.elseif_blocks:
|
|
21
|
+
corpo += list(bloco.get('body', []))
|
|
22
|
+
return corpo + list(node.else_body)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _has_redirect(nodes) -> bool:
|
|
26
|
+
return any(isinstance(n, RedirectNode) or (isinstance(n, IfNode) and _has_redirect(_branches(n)))
|
|
27
|
+
for n in nodes)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def page_guards(component) -> List[IfNode]:
|
|
31
|
+
"""The top-level q:if statements that can redirect, in document order."""
|
|
32
|
+
return [s for s in getattr(component, 'statements', [])
|
|
33
|
+
if isinstance(s, IfNode) and _has_redirect(_branches(s))]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def guard_conditions(node: IfNode) -> List[str]:
|
|
37
|
+
"""Every condition a guard evaluates, nested q:if included."""
|
|
38
|
+
condicoes = [node.condition] + [b.get('condition', '') for b in node.elseif_blocks]
|
|
39
|
+
for filho in _branches(node):
|
|
40
|
+
if isinstance(filho, IfNode):
|
|
41
|
+
condicoes += guard_conditions(filho)
|
|
42
|
+
return [c for c in condicoes if c]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def mark_guards(component) -> None:
|
|
46
|
+
"""Flag each guard, nested q:if included, so its condition fails closed (AUTH-6)."""
|
|
47
|
+
def marcar(node):
|
|
48
|
+
node.is_guard = True
|
|
49
|
+
for filho in _branches(node):
|
|
50
|
+
if isinstance(filho, IfNode):
|
|
51
|
+
marcar(filho)
|
|
52
|
+
for guarda in page_guards(component):
|
|
53
|
+
marcar(guarda)
|
|
@@ -575,10 +575,12 @@ class QuantumParser:
|
|
|
575
575
|
|
|
576
576
|
# Parse component-level attributes
|
|
577
577
|
component.port = root.get('port')
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
578
|
+
# A2: accepted and never read by anything — refused, like FN-2/DB-5.
|
|
579
|
+
for attr in ('basePath', 'health', 'metrics', 'trace'):
|
|
580
|
+
if root.get(attr) is not None:
|
|
581
|
+
raise QuantumParseError(
|
|
582
|
+
f'<q:component name="{name}"> {attr}= is not supported: it was accepted '
|
|
583
|
+
f'and never did anything.')
|
|
582
584
|
|
|
583
585
|
# Phase G: Authentication & Security attributes
|
|
584
586
|
require_auth_attr = root.get('require_auth', 'false').lower()
|
|
@@ -626,9 +628,60 @@ class QuantumParser:
|
|
|
626
628
|
self._parse_control_flow_statements(root, component)
|
|
627
629
|
|
|
628
630
|
self._check_statements_in_markup(component)
|
|
631
|
+
self._check_guards(component)
|
|
629
632
|
|
|
630
633
|
return component
|
|
631
634
|
|
|
635
|
+
def _check_guards(self, component: ComponentNode) -> None:
|
|
636
|
+
"""AUTH-6: a guard of a page with actions reads only what exists before the page runs.
|
|
637
|
+
|
|
638
|
+
Guards also run before each q:action, and an action does not run the
|
|
639
|
+
page's other statements. A guard reading a page variable would find it
|
|
640
|
+
missing there, read the condition as false, and let the action run.
|
|
641
|
+
"""
|
|
642
|
+
from quantum.core.guards import guard_conditions, mark_guards, page_guards
|
|
643
|
+
from quantum.core.expressions import names_read
|
|
644
|
+
|
|
645
|
+
mark_guards(component)
|
|
646
|
+
if not any(isinstance(s, ActionNode) for s in component.statements):
|
|
647
|
+
return
|
|
648
|
+
guardas = page_guards(component)
|
|
649
|
+
if not guardas:
|
|
650
|
+
return
|
|
651
|
+
|
|
652
|
+
definidos = {p.name for p in getattr(component, 'params', []) or []}
|
|
653
|
+
|
|
654
|
+
def definir(nodes):
|
|
655
|
+
for node in nodes:
|
|
656
|
+
if isinstance(node, (ActionNode, FunctionNode, HTMLNode, TextNode, CommentNode)):
|
|
657
|
+
continue
|
|
658
|
+
if isinstance(node, IfNode):
|
|
659
|
+
corpo = list(node.if_body) + list(node.else_body)
|
|
660
|
+
for bloco in node.elseif_blocks:
|
|
661
|
+
corpo += list(bloco.get('body', []))
|
|
662
|
+
definir(corpo)
|
|
663
|
+
continue
|
|
664
|
+
if isinstance(node, LoopNode):
|
|
665
|
+
definir(node.body)
|
|
666
|
+
continue
|
|
667
|
+
nome = getattr(node, 'name', None)
|
|
668
|
+
if isinstance(nome, str) and nome and '.' not in nome:
|
|
669
|
+
definidos.update({nome, f'{nome}_result'})
|
|
670
|
+
|
|
671
|
+
definir([s for s in component.statements if not any(s is g for g in guardas)])
|
|
672
|
+
|
|
673
|
+
for guarda in guardas:
|
|
674
|
+
for condicao in guard_conditions(guarda):
|
|
675
|
+
lidos = names_read(re.sub(r'\{([^{}]*)\}', r'\1', condicao)) & definidos
|
|
676
|
+
if lidos:
|
|
677
|
+
nome = sorted(lidos)[0]
|
|
678
|
+
raise QuantumParseError(
|
|
679
|
+
f'<q:if condition="{condicao}"> in <q:component name="{component.name}"> '
|
|
680
|
+
f'redirects, so it also runs before the page\'s q:action — where the page '
|
|
681
|
+
f'has not run and {{{nome}}} does not exist, and the guard would let the '
|
|
682
|
+
f'action through. Read the scope directly (e.g. session.x), or protect the '
|
|
683
|
+
f'page with require_auth / require_role.')
|
|
684
|
+
|
|
632
685
|
# Nodes the HTML renderer draws. Anything else inside markup is a statement,
|
|
633
686
|
# and statements run in the execution pass, before the render pass sees
|
|
634
687
|
# the markup around them.
|
|
@@ -769,20 +822,13 @@ class QuantumParser:
|
|
|
769
822
|
|
|
770
823
|
def _attach_else_sibling(self, if_node: 'IfNode', kind: str, element: ET.Element):
|
|
771
824
|
"""Anexa um else/elseif IRMAO ao IfNode anterior — mesmo resultado da
|
|
772
|
-
forma aninhada que o IfParser ja monta."""
|
|
825
|
+
forma aninhada que o IfParser ja monta, texto incluido (IF-3)."""
|
|
826
|
+
corpo = self._parser_registry.get_parser('if')._body(element)
|
|
773
827
|
if kind == 'elseif':
|
|
774
|
-
|
|
775
|
-
corpo = []
|
|
776
|
-
for filho in element:
|
|
777
|
-
stmt = self._parse_statement(filho)
|
|
778
|
-
if stmt:
|
|
779
|
-
corpo.append(stmt)
|
|
780
|
-
if_node.add_elseif_block(condicao, corpo)
|
|
828
|
+
if_node.add_elseif_block(element.get('condition', ''), corpo)
|
|
781
829
|
else:
|
|
782
|
-
for
|
|
783
|
-
|
|
784
|
-
if stmt:
|
|
785
|
-
if_node.add_else_statement(stmt)
|
|
830
|
+
for stmt in corpo:
|
|
831
|
+
if_node.add_else_statement(stmt)
|
|
786
832
|
|
|
787
833
|
def _parse_statement(self, element: ET.Element) -> Optional[QuantumNode]:
|
|
788
834
|
"""Parse individual statement (return, set, dispatchEvent, etc)"""
|
|
@@ -791,6 +837,17 @@ class QuantumParser:
|
|
|
791
837
|
# === MODULAR PARSER REGISTRY (no fallback) ===
|
|
792
838
|
# PHASE 1 DIAGNOSTIC: Legacy fallback disabled to test modular coverage
|
|
793
839
|
if self._use_modular_parsers and self._parser_registry:
|
|
840
|
+
# PARSE-1: <q:html>, <q:div> — an HTML element name in the q:
|
|
841
|
+
# namespace. The registry answered for it with the HTML parser, so
|
|
842
|
+
# <q:html value="{post.content}"/> became an empty <html> element
|
|
843
|
+
# instead of an unknown tag.
|
|
844
|
+
# q:script is a Quantum tag on purpose: client JavaScript, kept as
|
|
845
|
+
# an HTML node (see ui_desktop_adapter).
|
|
846
|
+
if (self._is_quantum_tag(element) and element_type != 'script'
|
|
847
|
+
and element_type in self._parser_registry.html_parser.tag_names):
|
|
848
|
+
raise QuantumParseError(
|
|
849
|
+
f"<q:{element_type}> is not a Quantum tag: HTML elements are written "
|
|
850
|
+
f"without the q: prefix (<{element_type}>)")
|
|
794
851
|
if self._parser_registry.has_parser(element_type):
|
|
795
852
|
return self._parser_registry.parse(element, element_type)
|
|
796
853
|
|
|
@@ -812,7 +869,14 @@ class QuantumParser:
|
|
|
812
869
|
|
|
813
870
|
if self._is_html_element(element):
|
|
814
871
|
if self._parser_registry.html_parser.can_parse(element_type):
|
|
815
|
-
|
|
872
|
+
node = self._parser_registry.html_parser.parse(element)
|
|
873
|
+
# UI-0: a ui:* element inside a page. Parsed as before
|
|
874
|
+
# (examples and tests load these files), but flagged so the
|
|
875
|
+
# renderer refuses it instead of emitting <window>/<vbox>,
|
|
876
|
+
# which no browser lays out.
|
|
877
|
+
if self._get_element_ui_namespace(element) == 'ui':
|
|
878
|
+
node.ui_tag = element_type
|
|
879
|
+
return node
|
|
816
880
|
|
|
817
881
|
# PARSE-1: a q: tag nobody parses is an error. It used to return
|
|
818
882
|
# None here and vanish — <q:sett>, <q:retrun> and <q:iff> ran as if
|
|
@@ -1180,9 +1244,16 @@ class QuantumParser:
|
|
|
1180
1244
|
type=element.get('type', 'string'),
|
|
1181
1245
|
required=element.get('required', 'false').lower() == 'true',
|
|
1182
1246
|
default=element.get('default'),
|
|
1183
|
-
validation=element.get('validation'),
|
|
1184
1247
|
description=element.get('description')
|
|
1185
1248
|
)
|
|
1249
|
+
if element.get('validation') is not None:
|
|
1250
|
+
# A2: accepted and never read. The rules are validate=, type=,
|
|
1251
|
+
# pattern=, min/max, minlength/maxlength and enum=.
|
|
1252
|
+
from quantum.core.parser import QuantumParseError
|
|
1253
|
+
raise QuantumParseError(
|
|
1254
|
+
f'<q:param name="{element.get("name", "")}"> validation= is not supported: it was '
|
|
1255
|
+
f'accepted and never did anything. Use type=, pattern=, min/max, '
|
|
1256
|
+
f'minlength/maxlength or enum=.')
|
|
1186
1257
|
|
|
1187
1258
|
# REST-specific
|
|
1188
1259
|
param.source = element.get('source', 'auto')
|
{quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/control_flow/if_parser.py
RENAMED
|
@@ -8,6 +8,7 @@ from typing import List
|
|
|
8
8
|
from xml.etree import ElementTree as ET
|
|
9
9
|
from quantum.core.parsers.base import BaseTagParser
|
|
10
10
|
from quantum.core.features.conditionals.src.ast_node import IfNode
|
|
11
|
+
from quantum.core.ast_nodes import TextNode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class IfParser(BaseTagParser):
|
|
@@ -37,32 +38,34 @@ class IfParser(BaseTagParser):
|
|
|
37
38
|
condition = self.get_attr(element, 'condition', '')
|
|
38
39
|
if_node = IfNode(condition)
|
|
39
40
|
|
|
41
|
+
# IF-3: text directly in a branch is content, like in q:loop and in an
|
|
42
|
+
# HTML element. Only child elements were read, so
|
|
43
|
+
# <h2><q:if condition="tag">Posts tagged {tag}</q:if></h2> rendered
|
|
44
|
+
# an empty heading.
|
|
45
|
+
principal = self._body(element, skip=('elseif', 'else'))
|
|
46
|
+
for statement in principal:
|
|
47
|
+
if_node.add_if_statement(statement)
|
|
48
|
+
|
|
40
49
|
for child in element:
|
|
41
50
|
child_type = self.get_element_name(child)
|
|
42
|
-
|
|
43
51
|
if child_type == 'elseif':
|
|
44
|
-
|
|
45
|
-
elseif_condition = self.get_attr(child, 'condition', '')
|
|
46
|
-
elseif_body = []
|
|
47
|
-
|
|
48
|
-
for elseif_child in child:
|
|
49
|
-
statement = self.parse_statement(elseif_child)
|
|
50
|
-
if statement:
|
|
51
|
-
elseif_body.append(statement)
|
|
52
|
-
|
|
53
|
-
if_node.add_elseif_block(elseif_condition, elseif_body)
|
|
54
|
-
|
|
52
|
+
if_node.add_elseif_block(self.get_attr(child, 'condition', ''), self._body(child))
|
|
55
53
|
elif child_type == 'else':
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
statement = self.parse_statement(else_child)
|
|
59
|
-
if statement:
|
|
60
|
-
if_node.add_else_statement(statement)
|
|
54
|
+
for statement in self._body(child):
|
|
55
|
+
if_node.add_else_statement(statement)
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
return if_node
|
|
58
|
+
|
|
59
|
+
def _body(self, element: ET.Element, skip=()) -> list:
|
|
60
|
+
"""The statements and text of a branch, in document order."""
|
|
61
|
+
corpo = []
|
|
62
|
+
if element.text and element.text.strip():
|
|
63
|
+
corpo.append(TextNode(element.text))
|
|
64
|
+
for child in element:
|
|
65
|
+
if self.get_element_name(child) not in skip:
|
|
64
66
|
statement = self.parse_statement(child)
|
|
65
67
|
if statement:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
corpo.append(statement)
|
|
69
|
+
if child.tail and child.tail.strip():
|
|
70
|
+
corpo.append(TextNode(child.tail))
|
|
71
|
+
return corpo
|
|
@@ -53,7 +53,12 @@ class SetParser(BaseTagParser):
|
|
|
53
53
|
set_node.nullable = self.get_bool_attr(element, 'nullable', True)
|
|
54
54
|
set_node.validate_rule = self.get_attr(element, 'validate')
|
|
55
55
|
set_node.pattern = self.get_attr(element, 'pattern')
|
|
56
|
-
|
|
56
|
+
if element.get('mask') is not None:
|
|
57
|
+
# A2: accepted and never applied.
|
|
58
|
+
from quantum.core.parser import QuantumParseError
|
|
59
|
+
raise QuantumParseError(
|
|
60
|
+
f'<q:set name="{set_node.name}"> mask= is not supported: it was accepted and '
|
|
61
|
+
f'never did anything. To validate a format use pattern=.')
|
|
57
62
|
set_node.range = self.get_attr(element, 'range')
|
|
58
63
|
set_node.enum = self.get_attr(element, 'enum')
|
|
59
64
|
set_node.unique = self.get_attr(element, 'unique')
|
{quantum_framework-0.12.0 → quantum_framework-0.14.0}/quantum/core/parsers/data/invoke_parser.py
RENAMED
|
@@ -70,7 +70,12 @@ class InvokeParser(BaseTagParser):
|
|
|
70
70
|
|
|
71
71
|
# Response handling
|
|
72
72
|
invoke_node.response_format = self.get_attr(element, 'responseFormat', 'auto')
|
|
73
|
-
|
|
73
|
+
if element.get('transform') is not None:
|
|
74
|
+
# A2: accepted and never applied.
|
|
75
|
+
from quantum.core.parser import QuantumParseError
|
|
76
|
+
raise QuantumParseError(
|
|
77
|
+
f'<q:invoke name="{invoke_node.name}"> transform= is not supported: it was '
|
|
78
|
+
f'accepted and never did anything. Transform the result with q:set.')
|
|
74
79
|
invoke_node.cache = self.get_bool_attr(element, 'cache', False)
|
|
75
80
|
invoke_node.ttl = self.get_int_attr(element, 'ttl', 0) or None
|
|
76
81
|
invoke_node.result = self.get_attr(element, 'result')
|