python-ddd-framework 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_ddd_framework/__init__.py +139 -21
- python_ddd_framework/application/builder.py +23 -78
- python_ddd_framework/application/composition.py +4 -22
- python_ddd_framework/application/runtime.py +96 -291
- python_ddd_framework/application/state.py +7 -75
- python_ddd_framework/application_services/__init__.py +5 -21
- python_ddd_framework/{services/application_bindings.py → application_services/bindings.py} +52 -9
- python_ddd_framework/application_services/catalog.py +36 -288
- python_ddd_framework/application_services/contracts.py +14 -13
- python_ddd_framework/application_services/dispatcher.py +19 -269
- python_ddd_framework/application_services/invocation.py +126 -7
- python_ddd_framework/application_services/module.py +168 -0
- python_ddd_framework/application_services/pagination.py +21 -0
- python_ddd_framework/auditing/control.py +1 -1
- python_ddd_framework/auditing/module.py +15 -0
- python_ddd_framework/authorization/__init__.py +2 -0
- python_ddd_framework/authorization/contracts.py +7 -2
- python_ddd_framework/authorization/definitions.py +5 -0
- python_ddd_framework/authorization/module.py +40 -0
- python_ddd_framework/authorization/options.py +7 -0
- python_ddd_framework/background_execution/child.py +13 -3
- python_ddd_framework/background_execution/declarations.py +45 -0
- python_ddd_framework/background_execution/lifecycle.py +12 -0
- python_ddd_framework/background_execution/module.py +107 -1
- python_ddd_framework/background_jobs/catalog.py +59 -42
- python_ddd_framework/background_jobs/contracts.py +16 -19
- python_ddd_framework/background_jobs/pgqueuer/enqueue.py +4 -0
- python_ddd_framework/background_jobs/pgqueuer/module.py +2 -1
- python_ddd_framework/background_jobs/pgqueuer/runtime.py +7 -7
- python_ddd_framework/background_workers/catalog.py +23 -36
- python_ddd_framework/background_workers/contracts.py +3 -0
- python_ddd_framework/background_workers/execution.py +1 -1
- python_ddd_framework/background_workers/runtime.py +38 -1
- python_ddd_framework/caching/__init__.py +7 -6
- python_ddd_framework/caching/contracts.py +65 -67
- python_ddd_framework/caching/errors.py +0 -7
- python_ddd_framework/caching/module.py +18 -0
- python_ddd_framework/caching/naming.py +29 -0
- python_ddd_framework/caching/options.py +38 -0
- python_ddd_framework/caching/unit_of_work.py +46 -0
- python_ddd_framework/cli/__init__.py +31 -6
- python_ddd_framework/cli/project.py +68 -6
- python_ddd_framework/cli/runtime.py +1 -1
- python_ddd_framework/developer_kit/generation.py +75 -22
- python_ddd_framework/developer_kit/project_metadata.py +2 -4
- python_ddd_framework/developer_kit/templates/basic/cookiecutter.json +4 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/README.md +45 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/conversion_service.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/module.py.jinja +38 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +41 -49
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/handler.py.jinja +45 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/payload.py.jinja +8 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/handler.py.jinja +35 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/payload.py.jinja +5 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/schedule.py.jinja +15 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_maintenance_worker.py.jinja +28 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_statistics_worker.py.jinja +26 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/order_cache.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/statistics_cache.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/order_changed_handler.py.jinja +23 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{integration.py.jinja → hosted_services/order_integration_service.py.jinja} +8 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_observation_handler.py.jinja +16 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/order_reporting_service.py.jinja +16 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/order_timing_interceptor.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +105 -10
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{options.py.jinja → options/order_options.py.jinja} +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_approval_service.py.jinja +65 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_management_service.py.jinja +35 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_query_service.py.jinja +41 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/approval_setting_observer.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/approve_order.py.jinja +6 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/create_order.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/order_reporting_service.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +39 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_approval_service.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_management_service.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_query_service.py.jinja +12 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_statistics_snapshot.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_view.py.jinja +12 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/entities/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{orders.py.jinja → entities/order.py.jinja} +10 -18
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/order_changed.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +42 -3
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/order_repository.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/order_seed_contributor.py.jinja +22 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/order_approval_service.py.jinja +23 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{settings.py.jinja → settings/approval_settings.py.jinja} +3 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/order_title.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/order_constants.py.jinja +3 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/order_status.py.jinja +6 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/order_errors.py.jinja +11 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_messages.py.jinja +26 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_observation.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +40 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/{permissions.py.jinja → permissions/order_permission_provider.py.jinja} +6 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/order_permissions.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/money.py.jinja +18 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/export_filter.py.jinja +15 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/refresh_orders.py.jinja +5 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +85 -7
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/routers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/{files.py.jinja → routers/order_files.py.jinja} +23 -20
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/order_socket.py.jinja +44 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +1 -7
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/base.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/{orders.py.jinja → order_model.py.jinja} +7 -3
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +42 -5
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/order_repository.py.jinja +79 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +4 -3
- python_ddd_framework/developer_kit/templates/project/cookiecutter.json +1 -1
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +16 -3
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +29 -22
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{app.development.yaml.jinja → backend/app.development.yaml.jinja} +2 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{pyproject.toml.jinja → backend/pyproject.toml.jinja} +1 -1
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/main.py.jinja +2 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/module.py.jinja +40 -3
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +40 -10
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +134 -23
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep +0 -0
- python_ddd_framework/distributed_lock/contracts.py +4 -1
- python_ddd_framework/domain/__init__.py +2 -0
- python_ddd_framework/domain/services.py +11 -0
- python_ddd_framework/errors/business.py +10 -1
- python_ddd_framework/errors/lifecycle.py +3 -31
- python_ddd_framework/events/__init__.py +5 -2
- python_ddd_framework/events/aggregate.py +3 -3
- python_ddd_framework/events/catalog.py +12 -11
- python_ddd_framework/events/contracts.py +6 -1
- python_ddd_framework/events/contribution.py +1 -52
- python_ddd_framework/events/entities.py +23 -0
- python_ddd_framework/events/module.py +97 -0
- python_ddd_framework/events/runtime.py +80 -29
- python_ddd_framework/events/subscription.py +36 -0
- python_ddd_framework/events/unit_of_work.py +25 -0
- python_ddd_framework/extensions/__init__.py +22 -0
- python_ddd_framework/extensions/catalog.py +198 -0
- python_ddd_framework/extensions/contracts.py +85 -0
- python_ddd_framework/extensions/models.py +142 -0
- python_ddd_framework/extensions/module.py +21 -0
- python_ddd_framework/fastapi/__init__.py +2 -0
- python_ddd_framework/fastapi/action.py +1 -2
- python_ddd_framework/fastapi/adapter.py +16 -0
- python_ddd_framework/fastapi/application_services.py +43 -25
- python_ddd_framework/fastapi/background.py +2 -1
- python_ddd_framework/fastapi/contracts.py +26 -8
- python_ddd_framework/fastapi/health.py +1 -1
- python_ddd_framework/fastapi/module.py +14 -0
- python_ddd_framework/fastapi/parameters.py +3 -18
- python_ddd_framework/fastapi/realtime/connection.py +10 -7
- python_ddd_framework/fastapi/realtime/module.py +3 -0
- python_ddd_framework/fastapi/realtime/runtime.py +10 -24
- python_ddd_framework/fastapi/request_context.py +2 -1
- python_ddd_framework/fastapi/routing.py +2 -1
- python_ddd_framework/fastapi/server.py +8 -0
- python_ddd_framework/fastapi/settings.py +2 -1
- python_ddd_framework/hosted_services/bridge.py +20 -1
- python_ddd_framework/hosted_services/catalog.py +6 -16
- python_ddd_framework/hosted_services/contracts.py +4 -0
- python_ddd_framework/hosted_services/declarations.py +15 -0
- python_ddd_framework/hosted_services/module.py +69 -0
- python_ddd_framework/hosted_services/runtime.py +30 -1
- python_ddd_framework/identity/__init__.py +24 -0
- python_ddd_framework/identity/application.py +38 -90
- python_ddd_framework/identity/contracts.py +181 -16
- python_ddd_framework/identity/http_api.py +79 -2
- python_ddd_framework/identity/management.py +166 -0
- python_ddd_framework/identity/module.py +12 -4
- python_ddd_framework/identity/services.py +29 -16
- python_ddd_framework/identity/sqlalchemy/migrations/0003_extra_properties.py +26 -0
- python_ddd_framework/identity/sqlalchemy/migrations/0004_concurrency_version.py +29 -0
- python_ddd_framework/identity/sqlalchemy/models.py +21 -2
- python_ddd_framework/identity/sqlalchemy/module.py +11 -2
- python_ddd_framework/identity/sqlalchemy/repository.py +458 -0
- python_ddd_framework/identity/sqlalchemy/session_security.py +88 -0
- python_ddd_framework/identity/sqlalchemy/stores.py +69 -290
- python_ddd_framework/identity/tokens.py +2 -5
- python_ddd_framework/{application_services/validation.py → invocation/arguments.py} +8 -6
- python_ddd_framework/invocation/contribution.py +61 -0
- python_ddd_framework/invocation/dispatcher.py +291 -0
- python_ddd_framework/invocation/entries.py +2 -25
- python_ddd_framework/invocation/entrypoints.py +126 -0
- python_ddd_framework/{application_services → invocation}/execution.py +8 -5
- python_ddd_framework/invocation/function_runtime.py +3 -1
- python_ddd_framework/invocation/interception.py +113 -42
- python_ddd_framework/{application_services → invocation}/interceptors.py +2 -2
- python_ddd_framework/invocation/managed_proxy.py +116 -0
- python_ddd_framework/invocation/managed_services.py +250 -0
- python_ddd_framework/invocation/methods.py +345 -0
- python_ddd_framework/invocation/module.py +113 -0
- python_ddd_framework/{application_services → invocation}/policies.py +8 -1
- python_ddd_framework/invocation/validation.py +25 -0
- python_ddd_framework/lifecycle/__init__.py +2 -0
- python_ddd_framework/lifecycle/composition.py +5 -2
- python_ddd_framework/lifecycle/participants.py +33 -0
- python_ddd_framework/messaging/__init__.py +25 -0
- python_ddd_framework/messaging/channel.py +205 -0
- python_ddd_framework/messaging/contracts.py +69 -0
- python_ddd_framework/messaging/module.py +101 -0
- python_ddd_framework/messaging/options.py +13 -0
- python_ddd_framework/messaging/receipt.py +27 -0
- python_ddd_framework/messaging/runtime.py +124 -0
- python_ddd_framework/modularity/__init__.py +11 -1
- python_ddd_framework/modularity/contracts.py +47 -16
- python_ddd_framework/modularity/discovery.py +21 -2
- python_ddd_framework/modularity/graph.py +45 -1
- python_ddd_framework/modularity/registry.py +83 -3
- python_ddd_framework/notifications/__init__.py +2 -0
- python_ddd_framework/notifications/catalog.py +3 -2
- python_ddd_framework/notifications/declarations.py +18 -0
- python_ddd_framework/notifications/module.py +21 -0
- python_ddd_framework/observability/logging.py +3 -0
- python_ddd_framework/observability/tracing.py +3 -0
- python_ddd_framework/options/contribution.py +12 -0
- python_ddd_framework/options/registry.py +44 -11
- python_ddd_framework/realtime/__init__.py +2 -1
- python_ddd_framework/realtime/contracts.py +3 -7
- python_ddd_framework/realtime/messages.py +26 -1
- python_ddd_framework/redis/cache.py +243 -0
- python_ddd_framework/redis/cache_scripts.py +40 -0
- python_ddd_framework/redis/distributed_lock.py +10 -2
- python_ddd_framework/redis/module.py +13 -4
- python_ddd_framework/redis/notifications.py +5 -2
- python_ddd_framework/redis/runtime.py +1 -79
- python_ddd_framework/seeding/__init__.py +13 -0
- python_ddd_framework/seeding/contracts.py +45 -0
- python_ddd_framework/seeding/module.py +73 -0
- python_ddd_framework/seeding/runtime.py +63 -0
- python_ddd_framework/services/arbitration.py +23 -0
- python_ddd_framework/services/binding.py +2 -3
- python_ddd_framework/services/composition.py +108 -0
- python_ddd_framework/services/contribution.py +44 -54
- python_ddd_framework/services/convention.py +53 -181
- python_ddd_framework/services/fixed_lifetime.py +12 -15
- python_ddd_framework/services/framework_provider.py +3 -158
- python_ddd_framework/services/native_graph.py +35 -60
- python_ddd_framework/services/provider.py +59 -4
- python_ddd_framework/services/repository.py +6 -0
- python_ddd_framework/services/runtime.py +129 -215
- python_ddd_framework/settings/catalog.py +5 -4
- python_ddd_framework/settings/declarations.py +18 -0
- python_ddd_framework/settings/definitions.py +5 -0
- python_ddd_framework/settings/manager.py +1 -1
- python_ddd_framework/settings/module.py +50 -1
- python_ddd_framework/settings/notifications.py +4 -2
- python_ddd_framework/settings/refresh_module.py +9 -2
- python_ddd_framework/settings/sqlalchemy/module.py +2 -1
- python_ddd_framework/sqlalchemy/__init__.py +12 -0
- python_ddd_framework/sqlalchemy/alembic_runtime/env.py +1 -0
- python_ddd_framework/sqlalchemy/auditing.py +7 -1
- python_ddd_framework/sqlalchemy/extensions.py +186 -0
- python_ddd_framework/sqlalchemy/metadata.py +110 -6
- python_ddd_framework/sqlalchemy/migration.py +61 -8
- python_ddd_framework/sqlalchemy/migration_operations.py +86 -0
- python_ddd_framework/sqlalchemy/module.py +14 -11
- python_ddd_framework/sqlalchemy/module_migration.py +71 -3
- python_ddd_framework/sqlalchemy/repository.py +217 -13
- python_ddd_framework/sqlalchemy/session_provider.py +14 -1
- python_ddd_framework/testing/runtime.py +5 -4
- python_ddd_framework/unit_of_work/__init__.py +2 -1
- python_ddd_framework/unit_of_work/contracts.py +6 -7
- python_ddd_framework/unit_of_work/errors.py +11 -0
- python_ddd_framework/unit_of_work/lifecycle.py +19 -0
- python_ddd_framework/unit_of_work/manager.py +86 -32
- python_ddd_framework/unit_of_work/module.py +61 -0
- python_ddd_framework/unit_of_work/options.py +7 -1
- python_ddd_framework/validation/__init__.py +1 -0
- python_ddd_framework/validation/models.py +37 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/METADATA +139 -44
- python_ddd_framework-0.4.0.dist-info/RECORD +488 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/WHEEL +1 -1
- python_ddd_framework/application_services/seeding.py +0 -72
- python_ddd_framework/caching/catalog.py +0 -73
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +0 -10
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +0 -24
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +0 -83
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +0 -84
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +0 -35
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +0 -14
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +0 -19
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +0 -24
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +0 -36
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +0 -49
- python_ddd_framework-0.3.2.dist-info/RECORD +0 -355
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.dockerignore → backend/.dockerignore} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.gitignore → backend/.gitignore} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.python-version → backend/.python-version} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{Dockerfile → backend/Dockerfile} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.dev.yaml.jinja → backend/compose.dev.yaml.jinja} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.production.yaml.jinja → backend/compose.production.yaml.jinja} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/__init__.py.jinja +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/conftest.py.jinja +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/host/test_http.py.jinja +0 -0
- /python_ddd_framework/{application_services → invocation}/signature.py +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/entry_points.txt +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""应用服务发现、最终实现策略和 REQUEST 到 ACTION 代理的组装 owner。"""
|
|
2
|
+
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
5
|
+
from dishka import DEFAULT_COMPONENT, DependencyKey, Provider, Scope
|
|
6
|
+
|
|
7
|
+
from ..auditing.module import AuditingModule
|
|
8
|
+
from ..authorization.module import AuthorizationModule
|
|
9
|
+
from ..extensions.module import ExtensionsModule
|
|
10
|
+
from ..invocation.interceptors import InterceptionStage
|
|
11
|
+
from ..invocation.module import InvocationModule
|
|
12
|
+
from ..invocation.validation import _validate_interceptor_scopes
|
|
13
|
+
from ..lifecycle import ConfigureContext
|
|
14
|
+
from ..modularity import AppModule
|
|
15
|
+
from ..modularity.discovery import _is_concrete_implementation
|
|
16
|
+
from ..services.composition import (
|
|
17
|
+
ServiceBindingContext,
|
|
18
|
+
ServiceComposition,
|
|
19
|
+
ServiceConvention,
|
|
20
|
+
ServiceDiscoveryContext,
|
|
21
|
+
ServiceValidationContext,
|
|
22
|
+
)
|
|
23
|
+
from ..services.convention_contracts import (
|
|
24
|
+
_SERVICE_DEFINITION_ATTRIBUTE,
|
|
25
|
+
ConventionDiscoveryKind,
|
|
26
|
+
_ServiceDefinition,
|
|
27
|
+
)
|
|
28
|
+
from ..settings.module import SettingsModule
|
|
29
|
+
from ..unit_of_work.module import UnitOfWorkModule
|
|
30
|
+
from .bindings import (
|
|
31
|
+
_bind_application_services,
|
|
32
|
+
_runtime_providers,
|
|
33
|
+
_validate_application_service_bindings,
|
|
34
|
+
_validate_application_service_dependencies,
|
|
35
|
+
)
|
|
36
|
+
from .catalog import (
|
|
37
|
+
ApplicationServiceCatalog,
|
|
38
|
+
_build_application_service_catalog,
|
|
39
|
+
_DiscoveredApplicationService,
|
|
40
|
+
)
|
|
41
|
+
from .contracts import (
|
|
42
|
+
_APPLICATION_SERVICE_DEFINITION_ATTRIBUTE,
|
|
43
|
+
ApplicationService,
|
|
44
|
+
_definition_for,
|
|
45
|
+
)
|
|
46
|
+
from .dispatcher import _ApplicationServiceDispatcher, _proxy_factory
|
|
47
|
+
from .errors import ApplicationServiceDefinitionError
|
|
48
|
+
from .invocation import _ScopedApplicationServiceInvoker
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ApplicationServicesModule(AppModule):
|
|
52
|
+
dependencies = (
|
|
53
|
+
InvocationModule,
|
|
54
|
+
UnitOfWorkModule,
|
|
55
|
+
AuthorizationModule,
|
|
56
|
+
AuditingModule,
|
|
57
|
+
SettingsModule,
|
|
58
|
+
)
|
|
59
|
+
catalog: ApplicationServiceCatalog
|
|
60
|
+
|
|
61
|
+
def __init__(self) -> None:
|
|
62
|
+
self._discoveries: list[_DiscoveredApplicationService] = []
|
|
63
|
+
self._proxy_factories: dict[DependencyKey, object] = {}
|
|
64
|
+
|
|
65
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
66
|
+
context.services.add_composition(
|
|
67
|
+
ServiceComposition(
|
|
68
|
+
discover=self._discover,
|
|
69
|
+
bind=self._bind,
|
|
70
|
+
validate=self._validate,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
75
|
+
seen: set[tuple[object, type[object]]] = set()
|
|
76
|
+
for discovery in context.discoveries:
|
|
77
|
+
candidate = discovery.candidate
|
|
78
|
+
identity = (discovery.owner, candidate)
|
|
79
|
+
if identity in seen or not _is_concrete_implementation(candidate):
|
|
80
|
+
continue
|
|
81
|
+
seen.add(identity)
|
|
82
|
+
definition = _definition_for(candidate)
|
|
83
|
+
if definition is None:
|
|
84
|
+
continue
|
|
85
|
+
if _SERVICE_DEFINITION_ATTRIBUTE in candidate.__dict__:
|
|
86
|
+
raise ApplicationServiceDefinitionError(
|
|
87
|
+
symbol=f"{candidate.__module__}.{candidate.__qualname__}",
|
|
88
|
+
reason="application_service and service metadata cannot be combined",
|
|
89
|
+
)
|
|
90
|
+
decorated = _APPLICATION_SERVICE_DEFINITION_ATTRIBUTE in candidate.__dict__
|
|
91
|
+
context.conventions[discovery.owner, candidate] = ServiceConvention(
|
|
92
|
+
_ServiceDefinition(provides=definition.contract or candidate, scope=Scope.ACTION),
|
|
93
|
+
ConventionDiscoveryKind.APPLICATION_SERVICE_DECORATOR
|
|
94
|
+
if decorated
|
|
95
|
+
else ConventionDiscoveryKind.FRAMEWORK_MARKER,
|
|
96
|
+
"accepted from immutable application_service decorator metadata"
|
|
97
|
+
if decorated
|
|
98
|
+
else "accepted from unique inherited ApplicationService contract",
|
|
99
|
+
)
|
|
100
|
+
# 公开契约仍由扫描该契约的 Module 拥有;实现所在模块不是第二个契约 owner。
|
|
101
|
+
declarations = {
|
|
102
|
+
item.owner: item
|
|
103
|
+
for item in context.discoveries
|
|
104
|
+
if item.candidate is definition.contract
|
|
105
|
+
}
|
|
106
|
+
if len(declarations) > 1:
|
|
107
|
+
raise ApplicationServiceDefinitionError(
|
|
108
|
+
symbol=str(definition.contract),
|
|
109
|
+
reason="application service contract is scanned by multiple Modules",
|
|
110
|
+
)
|
|
111
|
+
declaration = next(iter(declarations.values()), discovery)
|
|
112
|
+
self._discoveries.append(
|
|
113
|
+
_DiscoveredApplicationService(
|
|
114
|
+
service_type=cast(type[ApplicationService], candidate),
|
|
115
|
+
definition=definition,
|
|
116
|
+
owner=declaration.owner,
|
|
117
|
+
source=declaration.source,
|
|
118
|
+
declaration_index=len(self._discoveries),
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
123
|
+
bound = _bind_application_services(tuple(self._discoveries), context.arbitration.drafts)
|
|
124
|
+
_validate_application_service_dependencies(
|
|
125
|
+
context.modules, bound, context.arbitration.drafts
|
|
126
|
+
)
|
|
127
|
+
self.catalog = _build_application_service_catalog(
|
|
128
|
+
tuple(item.binding for item in bound),
|
|
129
|
+
context.modules.instance(AuthorizationModule).catalog,
|
|
130
|
+
context.modules.instance(InvocationModule).rules,
|
|
131
|
+
context.modules.instance(ExtensionsModule).catalog,
|
|
132
|
+
)
|
|
133
|
+
# 只重定位仲裁后的原生绑定,保留其他能力已经生成的目录 Provider。
|
|
134
|
+
for provider in context.arbitration.providers:
|
|
135
|
+
context._providers.remove(provider)
|
|
136
|
+
context._providers[0:0] = _runtime_providers(context.arbitration, bound)
|
|
137
|
+
provider = Provider()
|
|
138
|
+
provider.provide(lambda: self.catalog, provides=ApplicationServiceCatalog, scope=Scope.APP)
|
|
139
|
+
provider.provide(_ApplicationServiceDispatcher, scope=Scope.REQUEST)
|
|
140
|
+
provider.provide(_ScopedApplicationServiceInvoker, scope=Scope.APP)
|
|
141
|
+
for registration in self.catalog.registrations:
|
|
142
|
+
factory = _proxy_factory(self.catalog._for_registration(registration))
|
|
143
|
+
self._proxy_factories[DependencyKey(registration.contract, DEFAULT_COMPONENT)] = factory
|
|
144
|
+
provider.provide(
|
|
145
|
+
factory,
|
|
146
|
+
provides=registration.contract,
|
|
147
|
+
scope=Scope.REQUEST,
|
|
148
|
+
)
|
|
149
|
+
context.add_provider(provider)
|
|
150
|
+
|
|
151
|
+
def _validate(self, context: ServiceValidationContext) -> None:
|
|
152
|
+
_validate_application_service_bindings(
|
|
153
|
+
context.native_graph, self.catalog, self._proxy_factories
|
|
154
|
+
)
|
|
155
|
+
_validate_interceptor_scopes(
|
|
156
|
+
context.native_graph,
|
|
157
|
+
(
|
|
158
|
+
(
|
|
159
|
+
registration,
|
|
160
|
+
Scope.ACTION
|
|
161
|
+
if registration.before is InterceptionStage.METHOD
|
|
162
|
+
else Scope.REQUEST,
|
|
163
|
+
)
|
|
164
|
+
for service in self.catalog.registrations
|
|
165
|
+
for method in service.methods
|
|
166
|
+
for registration in method.interceptors
|
|
167
|
+
),
|
|
168
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""标准分页 DTO;默认值只在输入契约声明。"""
|
|
2
|
+
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
_T = TypeVar("_T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PagedResultRequestDto(BaseModel):
|
|
11
|
+
skip_count: int = Field(default=0, ge=0)
|
|
12
|
+
max_result_count: int = Field(default=10, ge=1, le=1000)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PagedAndSortedResultRequestDto(PagedResultRequestDto):
|
|
16
|
+
sorting: str | None = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PagedResult(BaseModel, Generic[_T]):
|
|
20
|
+
items: list[_T]
|
|
21
|
+
total_count: int = Field(ge=0)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""审计默认配置与 Application-local 控制状态的 owner。"""
|
|
2
|
+
|
|
3
|
+
from dishka import Provider, Scope
|
|
4
|
+
|
|
5
|
+
from ..lifecycle import ConfigureContext
|
|
6
|
+
from ..modularity import AppModule
|
|
7
|
+
from .control import AuditingControl, AuditingOptions
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuditingModule(AppModule):
|
|
11
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
12
|
+
context.configure.default(AuditingOptions, section="auditing")
|
|
13
|
+
provider = Provider()
|
|
14
|
+
provider.provide(AuditingControl, scope=Scope.APP)
|
|
15
|
+
context.services.contribute(provider, reason="Application-local auditing control")
|
|
@@ -17,10 +17,12 @@ from .errors import (
|
|
|
17
17
|
PermissionDeniedError,
|
|
18
18
|
UnauthenticatedError,
|
|
19
19
|
)
|
|
20
|
+
from .options import AuthorizationOptions
|
|
20
21
|
|
|
21
22
|
__all__ = (
|
|
22
23
|
"AccessTokenAuthenticator",
|
|
23
24
|
"AuthorizationDefinitionError",
|
|
25
|
+
"AuthorizationOptions",
|
|
24
26
|
"CurrentUser",
|
|
25
27
|
"PermissionCatalog",
|
|
26
28
|
"PermissionChecker",
|
|
@@ -7,6 +7,7 @@ from dataclasses import dataclass
|
|
|
7
7
|
from typing import Protocol, TypeVar, runtime_checkable
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
|
+
from ..modularity import ModuleDeclaration, ModuleRef
|
|
10
11
|
from .errors import AuthorizationDefinitionError, PermissionDefinitionError
|
|
11
12
|
|
|
12
13
|
_AUTHORIZATION_ATTRIBUTE = "__python_ddd_framework_authorization__"
|
|
@@ -129,9 +130,13 @@ class AccessTokenAuthenticator(Protocol):
|
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
@dataclass(frozen=True, slots=True)
|
|
132
|
-
class _AuthorizationMetadata:
|
|
133
|
+
class _AuthorizationMetadata(ModuleDeclaration):
|
|
133
134
|
permissions: tuple[PermissionName, ...]
|
|
134
135
|
|
|
136
|
+
@property
|
|
137
|
+
def required_module(self) -> ModuleRef:
|
|
138
|
+
return ModuleRef("python_ddd_framework.authorization.module:AuthorizationModule")
|
|
139
|
+
|
|
135
140
|
|
|
136
141
|
@dataclass(frozen=True, slots=True)
|
|
137
142
|
class _AuthorizationRequirement:
|
|
@@ -228,7 +233,7 @@ def _metadata_for(target: object) -> _AuthorizationMetadata | None:
|
|
|
228
233
|
|
|
229
234
|
|
|
230
235
|
def _validate_authorization_target(target: object) -> None:
|
|
231
|
-
if not isinstance(target, type) and not callable(target):
|
|
236
|
+
if not isinstance(target, (type, staticmethod, classmethod)) and not callable(target):
|
|
232
237
|
raise AuthorizationDefinitionError(
|
|
233
238
|
symbol=repr(target),
|
|
234
239
|
reason="authorization decorator target must be a class or callable",
|
|
@@ -5,11 +5,16 @@ from __future__ import annotations
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
|
|
7
7
|
from ..diagnostics import SourceLocation
|
|
8
|
+
from ..modularity import ModuleRef, ModuleRequirement
|
|
8
9
|
from .contracts import PermissionDefinition
|
|
9
10
|
from .errors import PermissionDefinitionError
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class PermissionDefinitionProvider(ABC):
|
|
14
|
+
__authorization_module__ = ModuleRequirement(
|
|
15
|
+
ModuleRef("python_ddd_framework.authorization.module:AuthorizationModule")
|
|
16
|
+
)
|
|
17
|
+
|
|
13
18
|
@abstractmethod
|
|
14
19
|
def define(self, context: PermissionDefinitionContext) -> None:
|
|
15
20
|
"""同步贡献静态权限定义;无构造注入、I/O 或运行期服务解析。"""
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""权限声明与当前身份的注册 owner;认证和存储实现由 Host 选择。"""
|
|
2
|
+
|
|
3
|
+
from dishka import Provider, Scope
|
|
4
|
+
|
|
5
|
+
from ..application_services.invocation import _InvocationRuntime
|
|
6
|
+
from ..invocation.module import InvocationModule
|
|
7
|
+
from ..lifecycle import ConfigureContext
|
|
8
|
+
from ..modularity import AppModule
|
|
9
|
+
from ..services.composition import (
|
|
10
|
+
ServiceBindingContext,
|
|
11
|
+
ServiceComposition,
|
|
12
|
+
ServiceDiscoveryContext,
|
|
13
|
+
)
|
|
14
|
+
from .catalog import PermissionCatalog, _build_permission_catalog
|
|
15
|
+
from .contracts import CurrentUser
|
|
16
|
+
from .options import AuthorizationOptions
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AuthorizationModule(AppModule):
|
|
20
|
+
dependencies = (InvocationModule,)
|
|
21
|
+
catalog: PermissionCatalog
|
|
22
|
+
|
|
23
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
24
|
+
context.configure.default(AuthorizationOptions, section="authorization")
|
|
25
|
+
context.services.add_composition(
|
|
26
|
+
ServiceComposition(discover=self._discover, bind=self._bind)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
30
|
+
self.catalog = _build_permission_catalog(context.modules, context.discoveries)
|
|
31
|
+
|
|
32
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
33
|
+
provider = Provider()
|
|
34
|
+
provider.provide(lambda: self.catalog, provides=PermissionCatalog, scope=Scope.APP)
|
|
35
|
+
provider.provide(_current_user, provides=CurrentUser, scope=Scope.REQUEST)
|
|
36
|
+
context.add_provider(provider)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _current_user(runtime: _InvocationRuntime) -> CurrentUser:
|
|
40
|
+
return runtime.current_user()
|
|
@@ -5,6 +5,9 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
+
from ..background_workers.runtime import _BackgroundCoordinator
|
|
9
|
+
from ..hosted_services.runtime import _HostedServiceCoordinator
|
|
10
|
+
|
|
8
11
|
if TYPE_CHECKING:
|
|
9
12
|
from multiprocessing.connection import _ConnectionBase as Connection
|
|
10
13
|
|
|
@@ -51,8 +54,10 @@ async def _serve_child(
|
|
|
51
54
|
pipe: Connection,
|
|
52
55
|
) -> None:
|
|
53
56
|
application = spec.build()
|
|
54
|
-
application.
|
|
55
|
-
|
|
57
|
+
container = application._container_for_integration()
|
|
58
|
+
hosted = await container.get(_HostedServiceCoordinator)
|
|
59
|
+
hosted.select_child()
|
|
60
|
+
coordinator = await container.get(_BackgroundCoordinator)
|
|
56
61
|
coordinator.select_child(tasks, initial)
|
|
57
62
|
try:
|
|
58
63
|
await _start_while_parent_present(application, pipe)
|
|
@@ -91,7 +96,12 @@ async def _start_while_parent_present(application: Application, pipe: Connection
|
|
|
91
96
|
await application.start()
|
|
92
97
|
# 启动确认与 start 返回处于同一 task 连续段;首轮业务崩溃不能被误判为初始化失败。
|
|
93
98
|
pipe.send(
|
|
94
|
-
(
|
|
99
|
+
(
|
|
100
|
+
_Message.SNAPSHOTS,
|
|
101
|
+
(
|
|
102
|
+
await application._container_for_integration().get(_BackgroundCoordinator)
|
|
103
|
+
).snapshots(),
|
|
104
|
+
)
|
|
95
105
|
)
|
|
96
106
|
pipe.send((_Message.READY,))
|
|
97
107
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""后台执行的模块声明;任务身份和调度事实仍由原有定义契约校验。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from ..modularity import ModuleDeclaration, ModuleDependency, ModuleRef
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ..background_jobs import BackgroundJobDefinition, BackgroundJobSchedule
|
|
12
|
+
from ..background_workers.contracts import BackgroundWorker, BackgroundWorkerDefinition
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True, slots=True)
|
|
16
|
+
class BackgroundWorkers(ModuleDeclaration):
|
|
17
|
+
workers: tuple[type[BackgroundWorker] | BackgroundWorkerDefinition, ...]
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def required_module(self) -> ModuleDependency:
|
|
21
|
+
return ModuleRef(
|
|
22
|
+
"python_ddd_framework.background_execution.module:BackgroundExecutionModule"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, slots=True)
|
|
27
|
+
class BackgroundJobs(ModuleDeclaration):
|
|
28
|
+
definitions: tuple[BackgroundJobDefinition[Any], ...]
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def required_module(self) -> ModuleDependency:
|
|
32
|
+
return ModuleRef(
|
|
33
|
+
"python_ddd_framework.background_execution.module:BackgroundExecutionModule"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class BackgroundSchedules(ModuleDeclaration):
|
|
39
|
+
schedules: tuple[BackgroundJobSchedule[Any], ...]
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def required_module(self) -> ModuleDependency:
|
|
43
|
+
return ModuleRef(
|
|
44
|
+
"python_ddd_framework.background_execution.module:BackgroundExecutionModule"
|
|
45
|
+
)
|
|
@@ -5,6 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import Awaitable, Callable
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
|
|
8
|
+
from ..errors import FrameworkError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _BackgroundExecutionStartFailure(FrameworkError):
|
|
12
|
+
"""后台启动错误归能力 owner;不伪造 Module hook 的诊断位置。"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, *, original_error: BaseException) -> None:
|
|
15
|
+
self.original_error = original_error
|
|
16
|
+
self.__cause__ = original_error
|
|
17
|
+
self.__suppress_context__ = True
|
|
18
|
+
super().__init__(f"Background execution start failed with {type(original_error).__name__}")
|
|
19
|
+
|
|
8
20
|
|
|
9
21
|
@dataclass(frozen=True, slots=True)
|
|
10
22
|
class _BackgroundRuntimeFailure:
|
|
@@ -1,9 +1,115 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""后台 Worker/Job 的组装与运行 owner;执行模式仍由 Host 配置决定。"""
|
|
2
2
|
|
|
3
|
+
from dishka import Provider, Scope
|
|
4
|
+
|
|
5
|
+
from ..background_jobs import BackgroundJobCatalog
|
|
6
|
+
from ..background_jobs.catalog import _build_background_job_catalog
|
|
7
|
+
from ..background_workers import BackgroundWorkerCatalog
|
|
8
|
+
from ..background_workers.catalog import _build_background_worker_catalog
|
|
9
|
+
from ..background_workers.contracts import BackgroundExecutionOptions
|
|
10
|
+
from ..background_workers.runtime import _BackgroundCoordinator
|
|
11
|
+
from ..hosted_services.module import HostedServicesModule
|
|
12
|
+
from ..invocation import BackgroundSystemCaller
|
|
13
|
+
from ..invocation.interception import InvocationEntry
|
|
14
|
+
from ..invocation.module import InvocationModule
|
|
15
|
+
from ..lifecycle import ConfigureContext
|
|
3
16
|
from ..modularity import AppModule
|
|
17
|
+
from ..services.composition import (
|
|
18
|
+
ServiceBindingContext,
|
|
19
|
+
ServiceComposition,
|
|
20
|
+
ServiceDiscoveryContext,
|
|
21
|
+
ServiceValidationContext,
|
|
22
|
+
)
|
|
23
|
+
from ..unit_of_work.options import UnitOfWorkDeclaration
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BackgroundExecutionModule(AppModule):
|
|
27
|
+
dependencies = (HostedServicesModule,)
|
|
28
|
+
workers: BackgroundWorkerCatalog
|
|
29
|
+
jobs: BackgroundJobCatalog
|
|
30
|
+
coordinator: _BackgroundCoordinator
|
|
31
|
+
|
|
32
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
33
|
+
context.configure.default(BackgroundExecutionOptions, section="background_execution")
|
|
34
|
+
context.services.add_composition(
|
|
35
|
+
ServiceComposition(
|
|
36
|
+
discover=self._discover,
|
|
37
|
+
bind=self._bind,
|
|
38
|
+
validate=self._validate,
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
43
|
+
self.workers = _build_background_worker_catalog(context.modules)
|
|
44
|
+
self.jobs = _build_background_job_catalog(context.modules, context.discoveries)
|
|
45
|
+
# 默认实现也经过统一仲裁,显式替换后只保留最终获选的原生 binding。
|
|
46
|
+
targets = dict.fromkeys(
|
|
47
|
+
[
|
|
48
|
+
(item.worker_type, self.workers.owner_for(item.name))
|
|
49
|
+
for item in self.workers.definitions
|
|
50
|
+
]
|
|
51
|
+
+ [(item.handler_type, self.jobs.owner_for(item)) for item in self.jobs.definitions]
|
|
52
|
+
)
|
|
53
|
+
for target, owner in targets:
|
|
54
|
+
if not isinstance(target, type):
|
|
55
|
+
continue
|
|
56
|
+
provider = Provider()
|
|
57
|
+
provider.provide(target, scope=Scope.ACTION)
|
|
58
|
+
context.services._add_framework_default(
|
|
59
|
+
provider=provider,
|
|
60
|
+
owner=owner,
|
|
61
|
+
source=context.modules.descriptors[owner].declaration_source,
|
|
62
|
+
reason="Background entry default implementation",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
66
|
+
invocation = context.modules.instance(InvocationModule)
|
|
67
|
+
self.coordinator = _BackgroundCoordinator(
|
|
68
|
+
worker_catalog=self.workers,
|
|
69
|
+
job_catalog=self.jobs,
|
|
70
|
+
options=context.options.get(BackgroundExecutionOptions),
|
|
71
|
+
invocation_runtime=invocation.invocation_runtime,
|
|
72
|
+
build_spec=context.build_spec,
|
|
73
|
+
)
|
|
74
|
+
self.runtime = self.coordinator.runtime_participant()
|
|
75
|
+
for worker in self.workers.definitions:
|
|
76
|
+
invocation.entries.append(
|
|
77
|
+
InvocationEntry(
|
|
78
|
+
worker.worker_type,
|
|
79
|
+
"run_iteration",
|
|
80
|
+
self.workers.owner_for(worker.name),
|
|
81
|
+
1,
|
|
82
|
+
unit_of_work_default=UnitOfWorkDeclaration(disabled=True),
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
for job in self.jobs.definitions:
|
|
86
|
+
invocation.entries.append(
|
|
87
|
+
InvocationEntry(
|
|
88
|
+
job.handler_type,
|
|
89
|
+
"execute",
|
|
90
|
+
self.jobs.owner_for(job),
|
|
91
|
+
2,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
provider = Provider()
|
|
95
|
+
provider.provide(lambda: self.workers, provides=BackgroundWorkerCatalog, scope=Scope.APP)
|
|
96
|
+
provider.provide(lambda: self.jobs, provides=BackgroundJobCatalog, scope=Scope.APP)
|
|
97
|
+
provider.provide(lambda: self.coordinator, provides=_BackgroundCoordinator, scope=Scope.APP)
|
|
98
|
+
provider.provide(
|
|
99
|
+
lambda: self.coordinator.system_caller,
|
|
100
|
+
provides=BackgroundSystemCaller,
|
|
101
|
+
scope=Scope.APP,
|
|
102
|
+
)
|
|
103
|
+
context.add_provider(provider)
|
|
104
|
+
|
|
105
|
+
def _validate(self, context: ServiceValidationContext) -> None:
|
|
106
|
+
self.coordinator.attach_container(context.container)
|
|
4
107
|
|
|
5
108
|
|
|
6
109
|
class BackgroundManagementModule(AppModule):
|
|
110
|
+
"""管理接口由 Host 选择;任务声明不因此自动暴露管理入口。"""
|
|
111
|
+
|
|
112
|
+
dependencies = (BackgroundExecutionModule,)
|
|
7
113
|
scan_packages = (
|
|
8
114
|
"python_ddd_framework.background_execution.application",
|
|
9
115
|
"python_ddd_framework.background_execution.permissions",
|