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
|
@@ -1,228 +1,104 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""模块贡献、最终绑定仲裁与原生容器构建;不枚举具体能力。"""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Mapping
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from typing import get_origin
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
7
|
+
from typing import TYPE_CHECKING, get_origin
|
|
8
8
|
|
|
9
|
-
from dishka import
|
|
10
|
-
DEFAULT_COMPONENT,
|
|
11
|
-
STRICT_VALIDATION,
|
|
12
|
-
AsyncContainer,
|
|
13
|
-
DependencyKey,
|
|
14
|
-
make_async_container,
|
|
15
|
-
)
|
|
9
|
+
from dishka import STRICT_VALIDATION, AsyncContainer, make_async_container
|
|
16
10
|
|
|
17
|
-
from ..application_services import ApplicationServiceCatalog
|
|
18
|
-
from ..application_services.catalog import (
|
|
19
|
-
_build_application_service_catalog,
|
|
20
|
-
)
|
|
21
|
-
from ..application_services.invocation import (
|
|
22
|
-
_InvocationRuntime,
|
|
23
|
-
)
|
|
24
|
-
from ..authorization import PermissionCatalog
|
|
25
|
-
from ..background_jobs import BackgroundJobCatalog
|
|
26
|
-
from ..background_workers import BackgroundWorkerCatalog
|
|
27
|
-
from ..background_workers.runtime import _BackgroundCoordinator
|
|
28
|
-
from ..caching import CacheCatalog
|
|
29
11
|
from ..configuration import ConfigurationRoot
|
|
30
12
|
from ..errors import CatalogNativeGraphMismatchError, ContainerValidationError
|
|
31
|
-
from ..
|
|
32
|
-
from ..
|
|
33
|
-
from ..events.contribution import EventContributionRegistry
|
|
34
|
-
from ..events.discovery import _discover_event_handlers
|
|
35
|
-
from ..hosted_services import HostedServiceCatalog
|
|
36
|
-
from ..hosted_services.catalog import (
|
|
37
|
-
_validate_hosted_service_drafts,
|
|
38
|
-
_validate_hosted_service_entries,
|
|
39
|
-
)
|
|
40
|
-
from ..hosted_services.runtime import _HostedServiceCoordinator
|
|
41
|
-
from ..invocation.entries import _validate_callable_entries
|
|
42
|
-
from ..invocation.interception import _EntryInterceptionCatalog
|
|
43
|
-
from ..modularity import ModuleKey, ModuleRegistry
|
|
44
|
-
from ..modularity.discovery import _DiscoveredType
|
|
45
|
-
from ..notifications import NotificationCatalog
|
|
13
|
+
from ..modularity import ModuleDescriptor, ModuleKey, ModuleRegistry
|
|
14
|
+
from ..modularity.discovery import _DiscoveredType, _require_type_dependencies
|
|
46
15
|
from ..options import Options, OptionsSnapshot
|
|
47
|
-
from
|
|
48
|
-
from
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
_runtime_providers,
|
|
54
|
-
_validate_application_service_dependencies,
|
|
55
|
-
)
|
|
56
|
-
from .arbitration import (
|
|
57
|
-
_RegistrationDraft,
|
|
58
|
-
_ServiceArbitrator,
|
|
16
|
+
from .arbitration import _RegistrationDraft, _ServiceArbitrator
|
|
17
|
+
from .composition import (
|
|
18
|
+
ServiceBindingContext,
|
|
19
|
+
ServiceComposition,
|
|
20
|
+
ServiceDiscoveryContext,
|
|
21
|
+
ServiceValidationContext,
|
|
59
22
|
)
|
|
60
23
|
from .contribution import ServiceContributionRegistry
|
|
61
24
|
from .convention import _ConventionScanner
|
|
62
25
|
from .convention_contracts import ConventionCandidate
|
|
63
|
-
from .framework_provider import
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
from .
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
from .registration import (
|
|
72
|
-
RegistrationDecision,
|
|
73
|
-
RegistrationKind,
|
|
74
|
-
ServiceCatalog,
|
|
75
|
-
ServiceRegistration,
|
|
76
|
-
)
|
|
26
|
+
from .framework_provider import _framework_provider
|
|
27
|
+
from .native_graph import _NativeServiceGraph
|
|
28
|
+
from .provider import _NativeProviderInspector
|
|
29
|
+
from .registration import RegistrationDecision, ServiceCatalog, ServiceRegistration
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ..application import Application
|
|
33
|
+
from ..application.build_spec import _ApplicationBuildSpec
|
|
77
34
|
|
|
78
35
|
|
|
79
36
|
@dataclass(frozen=True, slots=True)
|
|
80
37
|
class _ServiceRuntime:
|
|
81
38
|
catalog: ServiceCatalog
|
|
82
|
-
application_service_catalog: ApplicationServiceCatalog
|
|
83
|
-
event_catalog: LocalEventCatalog
|
|
84
39
|
container: AsyncContainer
|
|
85
40
|
|
|
86
41
|
|
|
87
42
|
class _ServiceRuntimeBuilder:
|
|
88
|
-
"""完成一次 Application-local scan、仲裁、容器构建和诊断快照发布。"""
|
|
89
|
-
|
|
90
|
-
__slots__ = (
|
|
91
|
-
"_background_coordinator",
|
|
92
|
-
"_background_job_catalog",
|
|
93
|
-
"_background_worker_catalog",
|
|
94
|
-
"_cache_catalog",
|
|
95
|
-
"_configuration",
|
|
96
|
-
"_contributions",
|
|
97
|
-
"_discoveries",
|
|
98
|
-
"_event_contributions",
|
|
99
|
-
"_hosted_service_catalog",
|
|
100
|
-
"_hosted_service_coordinator",
|
|
101
|
-
"_invocation_runtime",
|
|
102
|
-
"_notification_catalog",
|
|
103
|
-
"_options",
|
|
104
|
-
"_permission_catalog",
|
|
105
|
-
"_registry",
|
|
106
|
-
"_scan_packages",
|
|
107
|
-
"_setting_catalog",
|
|
108
|
-
)
|
|
109
|
-
|
|
110
43
|
def __init__(
|
|
111
44
|
self,
|
|
112
45
|
registry: ModuleRegistry,
|
|
113
46
|
contributions: ServiceContributionRegistry,
|
|
114
|
-
event_contributions: EventContributionRegistry,
|
|
115
47
|
scan_packages: Mapping[ModuleKey, tuple[str, ...]],
|
|
116
48
|
discoveries: tuple[_DiscoveredType, ...],
|
|
117
49
|
configuration: ConfigurationRoot,
|
|
118
50
|
options: OptionsSnapshot,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
cache_catalog: CacheCatalog,
|
|
122
|
-
setting_catalog: SettingCatalog,
|
|
123
|
-
notification_catalog: NotificationCatalog,
|
|
124
|
-
background_worker_catalog: BackgroundWorkerCatalog,
|
|
125
|
-
background_job_catalog: BackgroundJobCatalog,
|
|
126
|
-
background_coordinator: _BackgroundCoordinator,
|
|
127
|
-
hosted_service_catalog: HostedServiceCatalog,
|
|
128
|
-
hosted_service_coordinator: _HostedServiceCoordinator,
|
|
51
|
+
application: Application,
|
|
52
|
+
build_spec: _ApplicationBuildSpec,
|
|
129
53
|
) -> None:
|
|
130
54
|
self._registry = registry
|
|
131
55
|
self._contributions = contributions
|
|
132
|
-
self._event_contributions = event_contributions
|
|
133
56
|
self._scan_packages = scan_packages
|
|
134
57
|
self._discoveries = discoveries
|
|
135
58
|
self._configuration = configuration
|
|
136
59
|
self._options = options
|
|
137
|
-
self.
|
|
138
|
-
self.
|
|
139
|
-
self._cache_catalog = cache_catalog
|
|
140
|
-
self._setting_catalog = setting_catalog
|
|
141
|
-
self._notification_catalog = notification_catalog
|
|
142
|
-
self._background_worker_catalog = background_worker_catalog
|
|
143
|
-
self._background_job_catalog = background_job_catalog
|
|
144
|
-
self._background_coordinator = background_coordinator
|
|
145
|
-
self._hosted_service_catalog = hosted_service_catalog
|
|
146
|
-
self._hosted_service_coordinator = hosted_service_coordinator
|
|
60
|
+
self._application = application
|
|
61
|
+
self._build_spec = build_spec
|
|
147
62
|
|
|
148
63
|
def build(self) -> _ServiceRuntime:
|
|
149
|
-
|
|
64
|
+
compositions = self._contributions.compositions
|
|
65
|
+
discovery = ServiceDiscoveryContext(
|
|
150
66
|
self._registry,
|
|
151
67
|
self._discoveries,
|
|
152
|
-
self.
|
|
68
|
+
self._contributions,
|
|
69
|
+
{},
|
|
153
70
|
)
|
|
71
|
+
for _, composition in compositions:
|
|
72
|
+
if composition.discover is not None:
|
|
73
|
+
composition.discover(discovery)
|
|
154
74
|
scanner = _ConventionScanner(
|
|
155
75
|
self._registry,
|
|
156
76
|
self._contributions,
|
|
157
77
|
self._scan_packages,
|
|
158
78
|
self._discoveries,
|
|
159
|
-
|
|
79
|
+
discovery.conventions,
|
|
160
80
|
)
|
|
161
81
|
convention_candidates = scanner.discover()
|
|
162
82
|
entries = self._contributions.freeze()
|
|
163
|
-
|
|
83
|
+
for _, composition in compositions:
|
|
84
|
+
if composition.before_binding is not None:
|
|
85
|
+
composition.before_binding(entries)
|
|
164
86
|
arbitration = _ServiceArbitrator(self._registry).arbitrate(entries)
|
|
165
87
|
scanner.validate_lifetimes(arbitration.drafts)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
scanner.application_services,
|
|
169
|
-
arbitration.drafts,
|
|
170
|
-
)
|
|
171
|
-
_validate_application_service_dependencies(
|
|
172
|
-
self._registry, bound_application_services, arbitration.drafts
|
|
173
|
-
)
|
|
174
|
-
application_service_catalog = _build_application_service_catalog(
|
|
175
|
-
tuple(item.binding for item in bound_application_services),
|
|
176
|
-
self._permission_catalog,
|
|
177
|
-
self._contributions.interceptor_rules,
|
|
178
|
-
)
|
|
179
|
-
event_catalog = _build_event_catalog(
|
|
88
|
+
binding = ServiceBindingContext(
|
|
89
|
+
self._registry.dependency_order[-1],
|
|
180
90
|
self._registry,
|
|
181
|
-
self._event_contributions,
|
|
182
|
-
)
|
|
183
|
-
entry_interceptors = _EntryInterceptionCatalog(
|
|
184
|
-
event_catalog,
|
|
185
|
-
self._contributions.interceptor_rules,
|
|
186
|
-
self._background_worker_catalog,
|
|
187
|
-
self._background_job_catalog,
|
|
188
|
-
)
|
|
189
|
-
framework_provider = _framework_provider(
|
|
190
|
-
entry_interceptors=entry_interceptors,
|
|
191
|
-
has_unit_of_work_factory=any(
|
|
192
|
-
draft.key.type_hint is _UnitOfWorkFactory for draft in arbitration.drafts
|
|
193
|
-
),
|
|
194
|
-
registry=self._registry,
|
|
195
|
-
options=self._options,
|
|
196
|
-
invocation_runtime=self._invocation_runtime,
|
|
197
|
-
application_service_catalog=application_service_catalog,
|
|
198
|
-
event_catalog=event_catalog,
|
|
199
|
-
permission_catalog=self._permission_catalog,
|
|
200
|
-
cache_catalog=self._cache_catalog,
|
|
201
|
-
setting_catalog=self._setting_catalog,
|
|
202
|
-
notification_catalog=self._notification_catalog,
|
|
203
|
-
background_worker_catalog=self._background_worker_catalog,
|
|
204
|
-
background_job_catalog=self._background_job_catalog,
|
|
205
|
-
background_coordinator=self._background_coordinator,
|
|
206
|
-
hosted_service_catalog=self._hosted_service_catalog,
|
|
207
|
-
hosted_service_coordinator=self._hosted_service_coordinator,
|
|
208
|
-
configuration=self._configuration,
|
|
209
|
-
)
|
|
210
|
-
_provide_settings(
|
|
211
|
-
framework_provider,
|
|
212
|
-
self._setting_catalog,
|
|
213
91
|
self._configuration,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
),
|
|
92
|
+
self._options,
|
|
93
|
+
arbitration,
|
|
94
|
+
list(arbitration.providers),
|
|
95
|
+
[],
|
|
96
|
+
[],
|
|
97
|
+
self._application,
|
|
98
|
+
self._build_spec,
|
|
222
99
|
)
|
|
223
|
-
|
|
100
|
+
self._bind_capabilities(binding, compositions)
|
|
224
101
|
try:
|
|
225
|
-
# Options 的唯一 owner 是阶段绑定快照;手工 provider 也不能为未声明类型创建旁路。
|
|
226
102
|
if any(
|
|
227
103
|
draft.key.type_hint is Options or get_origin(draft.key.type_hint) is Options
|
|
228
104
|
for draft in arbitration.drafts
|
|
@@ -231,8 +107,12 @@ class _ServiceRuntimeBuilder:
|
|
|
231
107
|
"Options bindings must come from Module configuration declarations"
|
|
232
108
|
)
|
|
233
109
|
container = make_async_container(
|
|
234
|
-
*
|
|
235
|
-
|
|
110
|
+
*binding._providers,
|
|
111
|
+
_framework_provider(
|
|
112
|
+
registry=self._registry,
|
|
113
|
+
configuration=self._configuration,
|
|
114
|
+
options=self._options,
|
|
115
|
+
),
|
|
236
116
|
context={
|
|
237
117
|
Options[model_type]: Options(value) # type: ignore[valid-type]
|
|
238
118
|
for model_type, value in self._options.values.items()
|
|
@@ -240,63 +120,97 @@ class _ServiceRuntimeBuilder:
|
|
|
240
120
|
validation_settings=STRICT_VALIDATION,
|
|
241
121
|
)
|
|
242
122
|
except Exception as original_error:
|
|
243
|
-
|
|
123
|
+
raise ContainerValidationError(
|
|
244
124
|
sources=tuple(entry.contribution.source for entry in arbitration.ordered_entries),
|
|
245
125
|
original_error=original_error,
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
native_graph = _NativeServiceGraph(container)
|
|
126
|
+
) from original_error
|
|
127
|
+
native_graph = _NativeServiceGraph(container, tuple(binding._decorators))
|
|
250
128
|
native_graph.validate_fixed_lifetimes()
|
|
251
|
-
_validate_event_scopes(
|
|
252
|
-
event_catalog,
|
|
253
|
-
_accepted_single_service_scopes(arbitration.drafts, native_graph),
|
|
254
|
-
)
|
|
255
|
-
_validate_interceptors(native_graph, application_service_catalog, entry_interceptors)
|
|
256
|
-
_validate_callable_entries(
|
|
257
|
-
container,
|
|
258
|
-
self._background_job_catalog,
|
|
259
|
-
self._background_worker_catalog,
|
|
260
|
-
event_catalog,
|
|
261
|
-
event_implementations={
|
|
262
|
-
item.handler_type: native_graph.implementation_for(item.handler_type)
|
|
263
|
-
for item in event_catalog.subscriptions
|
|
264
|
-
if isinstance(item.handler_type, type)
|
|
265
|
-
},
|
|
266
|
-
)
|
|
267
129
|
differences = native_graph.differences(arbitration.drafts)
|
|
268
130
|
if differences:
|
|
269
131
|
raise CatalogNativeGraphMismatchError(differences=differences)
|
|
132
|
+
for descriptor, composition in compositions:
|
|
133
|
+
if composition.validate is not None:
|
|
134
|
+
composition.validate(
|
|
135
|
+
ServiceValidationContext(
|
|
136
|
+
replace(binding, descriptor=descriptor), container, native_graph
|
|
137
|
+
)
|
|
138
|
+
)
|
|
270
139
|
return _ServiceRuntime(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
native_graph,
|
|
274
|
-
convention_candidates,
|
|
275
|
-
),
|
|
276
|
-
application_service_catalog=application_service_catalog,
|
|
277
|
-
event_catalog=event_catalog,
|
|
278
|
-
container=container,
|
|
140
|
+
self._freeze_catalog(arbitration.drafts, native_graph, convention_candidates),
|
|
141
|
+
container,
|
|
279
142
|
)
|
|
280
143
|
|
|
144
|
+
@staticmethod
|
|
145
|
+
def _bind_capabilities(
|
|
146
|
+
binding: ServiceBindingContext,
|
|
147
|
+
compositions: tuple[tuple[ModuleDescriptor, ServiceComposition], ...],
|
|
148
|
+
) -> None:
|
|
149
|
+
# 必须先裁决替换链,再解释能力的最终实现与 scope,避免 Catalog 保存落选事实。
|
|
150
|
+
for descriptor, composition in compositions:
|
|
151
|
+
if composition.bind is not None:
|
|
152
|
+
composition.bind(replace(binding, descriptor=descriptor))
|
|
153
|
+
# 入口来自所有能力的最终绑定;统一屏障后冻结,容器只消费已确定的事实。
|
|
154
|
+
for descriptor, composition in compositions:
|
|
155
|
+
if composition.finalize is not None:
|
|
156
|
+
composition.finalize(replace(binding, descriptor=descriptor))
|
|
157
|
+
# 所有派生绑定已完成后才装饰,不能漏掉后继能力 finalize 提供的服务。
|
|
158
|
+
# replace 生成的各 owner 视图共享准入状态,捕获旧 context 也不能迟到贡献。
|
|
159
|
+
binding._provider_registration.closed = True
|
|
160
|
+
for descriptor, composition in compositions:
|
|
161
|
+
if composition.decorate is not None:
|
|
162
|
+
composition.decorate(replace(binding, descriptor=descriptor))
|
|
163
|
+
_ServiceRuntimeBuilder._validate_final_contributions(binding)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def _validate_final_contributions(binding: ServiceBindingContext) -> None:
|
|
167
|
+
# 回调不得在仲裁后修改原生贡献,也不得通过派生 Provider 偷渡 override。
|
|
168
|
+
for entry in binding.arbitration.ordered_entries:
|
|
169
|
+
contribution = entry.contribution
|
|
170
|
+
_NativeProviderInspector(
|
|
171
|
+
source=contribution.source, kind=contribution.kind
|
|
172
|
+
).assert_unchanged(contribution.provider, entry.signature)
|
|
173
|
+
for draft in binding.arbitration.drafts:
|
|
174
|
+
if draft.decision is RegistrationDecision.ACCEPTED and draft.factory is not None:
|
|
175
|
+
for candidate in (draft.key.type_hint, draft.factory.source):
|
|
176
|
+
_require_type_dependencies(
|
|
177
|
+
binding.modules,
|
|
178
|
+
draft.contribution.owner,
|
|
179
|
+
candidate,
|
|
180
|
+
source=draft.contribution.source,
|
|
181
|
+
)
|
|
182
|
+
for descriptor, inspector, provider, signature in binding._provider_checks:
|
|
183
|
+
inspector.assert_unchanged(provider, signature)
|
|
184
|
+
for item in inspector.inspect(provider).factories:
|
|
185
|
+
for candidate in (item.key.type_hint, item.factory.source):
|
|
186
|
+
_require_type_dependencies(
|
|
187
|
+
binding.modules,
|
|
188
|
+
descriptor.key,
|
|
189
|
+
candidate,
|
|
190
|
+
source=descriptor.declaration_source,
|
|
191
|
+
)
|
|
192
|
+
|
|
281
193
|
@staticmethod
|
|
282
194
|
def _freeze_catalog(
|
|
283
195
|
drafts: tuple[_RegistrationDraft, ...],
|
|
284
196
|
native_graph: _NativeServiceGraph,
|
|
285
197
|
convention_candidates: tuple[ConventionCandidate, ...],
|
|
286
198
|
) -> ServiceCatalog:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
199
|
+
return ServiceCatalog(
|
|
200
|
+
tuple(
|
|
201
|
+
ServiceRegistration(
|
|
202
|
+
key=draft.key,
|
|
203
|
+
scope=native_graph.scope_for(draft),
|
|
204
|
+
owner=draft.contribution.owner,
|
|
205
|
+
source=draft.contribution.source,
|
|
206
|
+
kind=draft.contribution.kind,
|
|
207
|
+
declaration_index=draft.contribution.declaration_index,
|
|
208
|
+
decision=draft.decision,
|
|
209
|
+
decision_reason=draft.decision_reason,
|
|
210
|
+
replaced_sources=draft.replaced_sources,
|
|
211
|
+
multi_ordinal=draft.multi_ordinal,
|
|
212
|
+
)
|
|
213
|
+
for draft in sorted(drafts, key=lambda item: item.sequence)
|
|
214
|
+
),
|
|
215
|
+
convention_candidates,
|
|
301
216
|
)
|
|
302
|
-
return ServiceCatalog(registrations, convention_candidates)
|
|
@@ -10,6 +10,7 @@ from ..diagnostics import SourceLocation
|
|
|
10
10
|
from ..modularity import ModuleRegistry
|
|
11
11
|
from ..modularity.discovery import _DiscoveredType
|
|
12
12
|
from .contracts import SettingDefinition
|
|
13
|
+
from .declarations import SettingHandlers
|
|
13
14
|
from .definition_discovery import _collect_setting_definitions
|
|
14
15
|
from .errors import SettingDefinitionError
|
|
15
16
|
from .handlers import SettingHandlerDefinition
|
|
@@ -87,8 +88,8 @@ def _build_setting_catalog(
|
|
|
87
88
|
) -> SettingCatalog:
|
|
88
89
|
definitions, sources = _collect_setting_definitions(discoveries)
|
|
89
90
|
handlers: list[SettingHandlerDefinition[Any]] = []
|
|
90
|
-
for descriptor in registry.
|
|
91
|
-
declared_handlers =
|
|
91
|
+
for descriptor, declaration in registry.declarations(SettingHandlers):
|
|
92
|
+
declared_handlers = declaration.definitions
|
|
92
93
|
if not isinstance(declared_handlers, tuple):
|
|
93
94
|
raise SettingDefinitionError(
|
|
94
95
|
setting=str(descriptor.key), reason="setting_handler_definitions must be a tuple"
|
|
@@ -105,8 +106,8 @@ def _contribute_setting_handler_defaults(
|
|
|
105
106
|
|
|
106
107
|
# 声明只拥有 handler 与 setting 的关系;实例和依赖仍交给原生 Dishka APP binding。
|
|
107
108
|
registered: set[type[object]] = set()
|
|
108
|
-
for descriptor in registry.
|
|
109
|
-
for definition in
|
|
109
|
+
for descriptor, declaration in registry.declarations(SettingHandlers):
|
|
110
|
+
for definition in declaration.definitions:
|
|
110
111
|
handler_type = definition.handler_type
|
|
111
112
|
if handler_type in registered:
|
|
112
113
|
continue
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Settings handler 的关系声明;实例和生命周期仍由原生 DI 管理。"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from ..modularity import AppModule, ModuleDeclaration
|
|
7
|
+
from .handlers import SettingHandlerDefinition
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class SettingHandlers(ModuleDeclaration):
|
|
12
|
+
definitions: tuple[SettingHandlerDefinition[Any], ...]
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def required_module(self) -> type[AppModule]:
|
|
16
|
+
from .module import SettingsModule
|
|
17
|
+
|
|
18
|
+
return SettingsModule
|
|
@@ -6,11 +6,16 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
from ..diagnostics import SourceLocation
|
|
9
|
+
from ..modularity import ModuleRef, ModuleRequirement
|
|
9
10
|
from .contracts import SettingDefinition
|
|
10
11
|
from .errors import SettingDefinitionError
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SettingDefinitionProvider(ABC):
|
|
15
|
+
__settings_module__ = ModuleRequirement(
|
|
16
|
+
ModuleRef("python_ddd_framework.settings.module:SettingsModule")
|
|
17
|
+
)
|
|
18
|
+
|
|
14
19
|
@abstractmethod
|
|
15
20
|
def define(self, context: SettingDefinitionContext) -> None:
|
|
16
21
|
"""向所属 Application 贡献定义;必须同步完成并返回 None。"""
|
|
@@ -47,7 +47,7 @@ class SettingManager:
|
|
|
47
47
|
await self.reset(definition, expected_version=current.version)
|
|
48
48
|
|
|
49
49
|
async def update(
|
|
50
|
-
self, definition: SettingDefinition[_T], value:
|
|
50
|
+
self, definition: SettingDefinition[_T], value: _T, *, expected_version: UUID | None
|
|
51
51
|
) -> SettingWriteOutcome[_T]:
|
|
52
52
|
self._catalog.require_owned(definition)
|
|
53
53
|
try:
|
|
@@ -1,9 +1,58 @@
|
|
|
1
1
|
"""Provider-neutral 的 Settings 管理服务与权限贡献。"""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dishka import DEFAULT_COMPONENT, DependencyKey, Provider, Scope
|
|
4
|
+
|
|
5
|
+
from ..lifecycle import ConfigureContext
|
|
6
|
+
from ..modularity import AppModule, ModuleRef
|
|
7
|
+
from ..services.composition import (
|
|
8
|
+
ServiceBindingContext,
|
|
9
|
+
ServiceComposition,
|
|
10
|
+
ServiceDiscoveryContext,
|
|
11
|
+
)
|
|
12
|
+
from ..services.registration import RegistrationDecision, RegistrationKind
|
|
13
|
+
from .binding import _provide_settings
|
|
14
|
+
from .catalog import SettingCatalog, _build_setting_catalog, _contribute_setting_handler_defaults
|
|
15
|
+
from .store import RuntimeSettingStore
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SettingsModule(AppModule):
|
|
19
|
+
catalog: SettingCatalog
|
|
20
|
+
|
|
21
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
22
|
+
self.catalog = _build_setting_catalog(context.modules, context.discoveries)
|
|
23
|
+
if self.catalog.definitions:
|
|
24
|
+
context.configure.section("settings")
|
|
25
|
+
context.services.add_composition(
|
|
26
|
+
ServiceComposition(discover=self._discover, bind=self._bind)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
30
|
+
_contribute_setting_handler_defaults(context.modules, context.services)
|
|
31
|
+
|
|
32
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
33
|
+
provider = Provider()
|
|
34
|
+
provider.provide(lambda: self.catalog, provides=SettingCatalog, scope=Scope.APP)
|
|
35
|
+
# 存储能力只由最终原生绑定决定;alias 也是有效绑定,不能按 provider 名称推断。
|
|
36
|
+
_provide_settings(
|
|
37
|
+
provider,
|
|
38
|
+
self.catalog,
|
|
39
|
+
context.configuration,
|
|
40
|
+
has_store=any(
|
|
41
|
+
draft.key == DependencyKey(RuntimeSettingStore, DEFAULT_COMPONENT)
|
|
42
|
+
and draft.decision is RegistrationDecision.ACCEPTED
|
|
43
|
+
and draft.contribution.kind is not RegistrationKind.MULTI
|
|
44
|
+
and not draft.is_collection
|
|
45
|
+
for draft in context.arbitration.drafts
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
context.add_provider(provider)
|
|
4
49
|
|
|
5
50
|
|
|
6
51
|
class SettingsManagementModule(AppModule):
|
|
52
|
+
dependencies = (
|
|
53
|
+
SettingsModule,
|
|
54
|
+
ModuleRef("python_ddd_framework.application_services.module:ApplicationServicesModule"),
|
|
55
|
+
)
|
|
7
56
|
scan_packages = (
|
|
8
57
|
"python_ddd_framework.settings.application",
|
|
9
58
|
"python_ddd_framework.settings.permissions",
|
|
@@ -7,6 +7,8 @@ from contextlib import AsyncExitStack
|
|
|
7
7
|
from ..lifecycle import PreInitializeContext, ShutdownContext
|
|
8
8
|
from ..modularity import AppModule
|
|
9
9
|
from ..notifications import Notifications
|
|
10
|
+
from ..notifications.declarations import NotificationDefinitions
|
|
11
|
+
from ..notifications.module import NotificationsModule
|
|
10
12
|
from .changes import SETTINGS_CHANGED, SettingChanged
|
|
11
13
|
from .manager import SettingManager
|
|
12
14
|
from .refresh import SettingsRefreshRuntime
|
|
@@ -14,8 +16,8 @@ from .refresh_module import SettingsRefreshModule
|
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class SettingsNotificationsModule(AppModule):
|
|
17
|
-
dependencies = (SettingsRefreshModule,)
|
|
18
|
-
notification_definitions = (SETTINGS_CHANGED,)
|
|
19
|
+
dependencies = (SettingsRefreshModule, NotificationsModule)
|
|
20
|
+
notification_definitions = NotificationDefinitions((SETTINGS_CHANGED,))
|
|
19
21
|
|
|
20
22
|
def __init__(self) -> None:
|
|
21
23
|
self._subscriptions = AsyncExitStack()
|
|
@@ -6,21 +6,28 @@ from dishka import DEFAULT_COMPONENT, DependencyKey
|
|
|
6
6
|
|
|
7
7
|
from python_ddd_framework import ConfigureContext
|
|
8
8
|
|
|
9
|
+
from ..hosted_services.declarations import HostedServices
|
|
9
10
|
from ..lifecycle import PreInitializeContext, ShutdownContext
|
|
10
|
-
from ..modularity import AppModule
|
|
11
|
+
from ..modularity import AppModule, ModuleRef
|
|
11
12
|
from .catalog import SettingCatalog
|
|
12
13
|
from .manager import SettingManager
|
|
14
|
+
from .module import SettingsModule
|
|
13
15
|
from .refresh import SettingsRefreshOptions, SettingsRefreshRuntime
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class SettingsRefreshModule(AppModule):
|
|
19
|
+
dependencies = (
|
|
20
|
+
SettingsModule,
|
|
21
|
+
ModuleRef("python_ddd_framework.hosted_services.module:HostedServicesModule"),
|
|
22
|
+
)
|
|
23
|
+
|
|
17
24
|
def __init__(self) -> None:
|
|
18
25
|
self._manager: SettingManager | None = None
|
|
19
26
|
|
|
20
27
|
def configure(self, context: ConfigureContext) -> None:
|
|
21
28
|
context.configure(SettingsRefreshOptions, section="settings_refresh")
|
|
22
29
|
|
|
23
|
-
hosted_services = (SettingsRefreshRuntime,)
|
|
30
|
+
hosted_services = HostedServices((SettingsRefreshRuntime,))
|
|
24
31
|
|
|
25
32
|
async def pre_initialize(self, context: PreInitializeContext) -> None:
|
|
26
33
|
catalog = await context.container.get(SettingCatalog)
|
|
@@ -11,6 +11,7 @@ from ...sqlalchemy import (
|
|
|
11
11
|
SqlAlchemyPersistenceModule,
|
|
12
12
|
contributes_sqlalchemy_models,
|
|
13
13
|
)
|
|
14
|
+
from ..module import SettingsModule
|
|
14
15
|
from ..store import RuntimeSettingStore
|
|
15
16
|
from .models import RuntimeSettingsBase
|
|
16
17
|
from .store import SqlAlchemyRuntimeSettingStore
|
|
@@ -24,7 +25,7 @@ from .store import SqlAlchemyRuntimeSettingStore
|
|
|
24
25
|
)
|
|
25
26
|
)
|
|
26
27
|
class SqlAlchemyRuntimeSettingsModule(AppModule):
|
|
27
|
-
dependencies = (SqlAlchemyPersistenceModule,)
|
|
28
|
+
dependencies = (SqlAlchemyPersistenceModule, SettingsModule)
|
|
28
29
|
|
|
29
30
|
def configure(self, context: ConfigureContext) -> None:
|
|
30
31
|
provider = Provider()
|
|
@@ -12,6 +12,13 @@ from .errors import (
|
|
|
12
12
|
SqlAlchemyModelRegistrationError,
|
|
13
13
|
UnknownSqlAlchemyConnectionError,
|
|
14
14
|
)
|
|
15
|
+
from .extensions import (
|
|
16
|
+
ExtensionColumn,
|
|
17
|
+
SqlAlchemyExtensionMapping,
|
|
18
|
+
SqlAlchemyExtensionMigrations,
|
|
19
|
+
SqlAlchemyExtensionModel,
|
|
20
|
+
SqlAlchemyModelExtension,
|
|
21
|
+
)
|
|
15
22
|
from .metadata import (
|
|
16
23
|
SqlAlchemyMigrationContribution,
|
|
17
24
|
SqlAlchemyMigrationRegistration,
|
|
@@ -31,9 +38,13 @@ from .session_provider import SqlAlchemySessionProvider
|
|
|
31
38
|
__all__ = (
|
|
32
39
|
"AuditedEntityMixin",
|
|
33
40
|
"ConnectionStringsOptions",
|
|
41
|
+
"ExtensionColumn",
|
|
34
42
|
"FrameworkAsyncSession",
|
|
35
43
|
"MigrationHeadsMismatchError",
|
|
36
44
|
"SqlAlchemyConnectionError",
|
|
45
|
+
"SqlAlchemyExtensionMapping",
|
|
46
|
+
"SqlAlchemyExtensionMigrations",
|
|
47
|
+
"SqlAlchemyExtensionModel",
|
|
37
48
|
"SqlAlchemyMetadataChangedError",
|
|
38
49
|
"SqlAlchemyMigrationContribution",
|
|
39
50
|
"SqlAlchemyMigrationError",
|
|
@@ -42,6 +53,7 @@ __all__ = (
|
|
|
42
53
|
"SqlAlchemyMigrator",
|
|
43
54
|
"SqlAlchemyModelCatalog",
|
|
44
55
|
"SqlAlchemyModelContribution",
|
|
56
|
+
"SqlAlchemyModelExtension",
|
|
45
57
|
"SqlAlchemyModelRegistration",
|
|
46
58
|
"SqlAlchemyModelRegistrationError",
|
|
47
59
|
"SqlAlchemyPersistenceModule",
|