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
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
-
from collections.abc import Mapping
|
|
8
7
|
from dataclasses import dataclass
|
|
9
8
|
from typing import cast
|
|
10
9
|
from uuid import UUID, uuid4
|
|
@@ -17,14 +16,14 @@ from ...authorization import AccessTokenAuthenticator, CurrentUser, Unauthentica
|
|
|
17
16
|
from ...options import Options
|
|
18
17
|
from ...realtime import (
|
|
19
18
|
RealtimeDeliveryReport,
|
|
20
|
-
|
|
19
|
+
RealtimeMessage,
|
|
21
20
|
RealtimePublisher,
|
|
22
21
|
RealtimeSnapshot,
|
|
23
22
|
)
|
|
24
|
-
from ...realtime.messages import _encode_envelope
|
|
23
|
+
from ...realtime.messages import _encode_envelope, _message_envelope
|
|
25
24
|
from .connection import (
|
|
26
|
-
_FRAMEWORK_PROBLEM_MESSAGE_TYPE,
|
|
27
25
|
WebSocketConnection,
|
|
26
|
+
_FrameworkProblemMessage,
|
|
28
27
|
_WebSocketCloseCode,
|
|
29
28
|
)
|
|
30
29
|
from .options import FastApiRealtimeOptions
|
|
@@ -212,21 +211,17 @@ class _FastApiRealtimeRuntime:
|
|
|
212
211
|
async def send_to_connection(
|
|
213
212
|
self,
|
|
214
213
|
connection_id: UUID,
|
|
215
|
-
|
|
216
|
-
message_type: str,
|
|
217
|
-
payload: Mapping[str, object],
|
|
214
|
+
message: RealtimeMessage,
|
|
218
215
|
) -> RealtimeDeliveryReport:
|
|
219
216
|
async with self._lock:
|
|
220
217
|
state = self._connections.get(connection_id)
|
|
221
218
|
states = () if state is None else (state,)
|
|
222
|
-
return await self._send(states,
|
|
219
|
+
return await self._send(states, message)
|
|
223
220
|
|
|
224
221
|
async def send_to_user(
|
|
225
222
|
self,
|
|
226
223
|
user_id: UUID,
|
|
227
|
-
|
|
228
|
-
message_type: str,
|
|
229
|
-
payload: Mapping[str, object],
|
|
224
|
+
message: RealtimeMessage,
|
|
230
225
|
) -> RealtimeDeliveryReport:
|
|
231
226
|
async with self._lock:
|
|
232
227
|
connection_ids = tuple(self._user_connections.get(user_id, ()))
|
|
@@ -235,7 +230,7 @@ class _FastApiRealtimeRuntime:
|
|
|
235
230
|
for connection_id in connection_ids
|
|
236
231
|
if (state := self._connections.get(connection_id)) is not None
|
|
237
232
|
)
|
|
238
|
-
return await self._send(states,
|
|
233
|
+
return await self._send(states, message)
|
|
239
234
|
|
|
240
235
|
async def snapshot(self) -> RealtimeSnapshot:
|
|
241
236
|
async with self._lock:
|
|
@@ -277,18 +272,10 @@ class _FastApiRealtimeRuntime:
|
|
|
277
272
|
async def _send(
|
|
278
273
|
self,
|
|
279
274
|
states: tuple[_ConnectionState, ...],
|
|
280
|
-
|
|
281
|
-
message_type: str,
|
|
282
|
-
payload: Mapping[str, object],
|
|
275
|
+
message: RealtimeMessage,
|
|
283
276
|
) -> RealtimeDeliveryReport:
|
|
284
277
|
correlation_id = self._invocations.correlation_id() or str(uuid4())
|
|
285
|
-
text = _encode_envelope(
|
|
286
|
-
RealtimeEnvelope(
|
|
287
|
-
message_type=message_type,
|
|
288
|
-
correlation_id=correlation_id,
|
|
289
|
-
payload=payload,
|
|
290
|
-
)
|
|
291
|
-
)
|
|
278
|
+
text = _encode_envelope(_message_envelope(message, correlation_id))
|
|
292
279
|
if not states:
|
|
293
280
|
return RealtimeDeliveryReport(matched=0, enqueued=0, dropped=0)
|
|
294
281
|
enqueued = 0
|
|
@@ -327,8 +314,7 @@ class _FastApiRealtimeRuntime:
|
|
|
327
314
|
) -> RealtimeDeliveryReport:
|
|
328
315
|
return await self._send(
|
|
329
316
|
(state,),
|
|
330
|
-
|
|
331
|
-
payload={"code": code, "status": status},
|
|
317
|
+
_FrameworkProblemMessage(code=code, status=status),
|
|
332
318
|
)
|
|
333
319
|
|
|
334
320
|
async def _request_close(
|
|
@@ -26,6 +26,7 @@ from ..application_services.invocation import (
|
|
|
26
26
|
from ..auditing import AuditSink, RequestAuditRecord
|
|
27
27
|
from ..authorization import AccessTokenAuthenticator, CurrentUser, UnauthenticatedError
|
|
28
28
|
from ..invocation.contracts import _is_safe_correlation_id
|
|
29
|
+
from ..invocation.entrypoints import http_request_lease
|
|
29
30
|
from ..unit_of_work import UnitOfWorkManager
|
|
30
31
|
from .errors import FastApiAdapterError
|
|
31
32
|
|
|
@@ -46,7 +47,7 @@ class _RequestScopeMiddleware:
|
|
|
46
47
|
if scope["type"] != "http":
|
|
47
48
|
await self._other(scope, receive, send)
|
|
48
49
|
return
|
|
49
|
-
async with self._application
|
|
50
|
+
async with http_request_lease(self._application):
|
|
50
51
|
request = Request(scope, receive, send)
|
|
51
52
|
container = self._application._container_for_integration()(
|
|
52
53
|
{Request: request}, scope=DIScope.REQUEST
|
|
@@ -15,6 +15,7 @@ from fastapi.routing import APIRoute, APIWebSocketRoute, RouteContext, iter_rout
|
|
|
15
15
|
from starlette.routing import BaseRoute, WebSocketRoute
|
|
16
16
|
|
|
17
17
|
from ..application import Application
|
|
18
|
+
from ..authorization.module import AuthorizationModule
|
|
18
19
|
from ..diagnostics import SourceLocation
|
|
19
20
|
from ..modularity import ModuleKey
|
|
20
21
|
from .application_services import (
|
|
@@ -111,7 +112,7 @@ def _build_route_plan(
|
|
|
111
112
|
unknown = tuple(
|
|
112
113
|
permission
|
|
113
114
|
for permission in route._definition.authorization.permissions
|
|
114
|
-
if permission not in application.
|
|
115
|
+
if permission not in application.modules.instance(AuthorizationModule).catalog
|
|
115
116
|
)
|
|
116
117
|
if unknown:
|
|
117
118
|
raise FastApiAdapterError(
|
|
@@ -14,6 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
16
|
from uvicorn import Config, Server
|
|
17
|
+
from uvicorn.supervisors.statreload import StatReload
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from uvicorn.supervisors.basereload import BaseReload as ChangeReload
|
|
@@ -125,12 +126,19 @@ class _HostReload(ChangeReload):
|
|
|
125
126
|
self.shutdown()
|
|
126
127
|
|
|
127
128
|
def restart(self) -> None:
|
|
129
|
+
# 接管进程退出仍须保留原生 StatReload 的 mtime 重置;否则同一次修改会循环重启。
|
|
130
|
+
if isinstance(self, StatReload):
|
|
131
|
+
self.mtimes.clear()
|
|
128
132
|
# 旧进程尚未退出就一直占位;不靠超时/PID 猜测,更不强杀后立即补位。
|
|
133
|
+
logger = logging.getLogger(__name__)
|
|
134
|
+
logger.info("Host reload requesting shutdown of process %s", self.process.pid)
|
|
129
135
|
self._shutdown_requested.set()
|
|
130
136
|
self.process.join()
|
|
137
|
+
logger.info("Host reload previous process %s exited", self.process.pid)
|
|
131
138
|
self._shutdown_requested.clear()
|
|
132
139
|
# 原生 startup 仍负责 spawn、信号接线和 socket 传递,不复制进程创建实现。
|
|
133
140
|
self.startup()
|
|
141
|
+
logger.info("Host reload spawned replacement process %s", self.process.pid)
|
|
134
142
|
|
|
135
143
|
def shutdown(self) -> None:
|
|
136
144
|
self._shutdown_requested.set()
|
|
@@ -4,6 +4,7 @@ from ..modularity import AppModule
|
|
|
4
4
|
from ..settings.management import SettingsManagementApplicationService
|
|
5
5
|
from ..settings.module import SettingsManagementModule
|
|
6
6
|
from . import ApplicationServiceRouteOverride, exposes_application_services
|
|
7
|
+
from .module import FastApiModule
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@exposes_application_services(
|
|
@@ -37,4 +38,4 @@ from . import ApplicationServiceRouteOverride, exposes_application_services
|
|
|
37
38
|
),
|
|
38
39
|
)
|
|
39
40
|
class SettingsManagementHttpApiModule(AppModule):
|
|
40
|
-
dependencies = (
|
|
41
|
+
dependencies = (FastApiModule,)
|
|
@@ -15,7 +15,11 @@ from uuid import uuid4
|
|
|
15
15
|
from dishka import AsyncContainer
|
|
16
16
|
|
|
17
17
|
from ..application_services import ApplicationServiceCatalog, ApplicationServiceContract
|
|
18
|
-
from ..application_services.invocation import
|
|
18
|
+
from ..application_services.invocation import (
|
|
19
|
+
_InvocationReservation,
|
|
20
|
+
_InvocationRuntime,
|
|
21
|
+
_ScopedApplicationServiceInvoker,
|
|
22
|
+
)
|
|
19
23
|
from ..authorization import CurrentUser
|
|
20
24
|
from ..invocation.contracts import _is_safe_correlation_id
|
|
21
25
|
from ..invocation.function_runtime import _invoke_callable
|
|
@@ -297,6 +301,21 @@ class _HostedServiceBridge:
|
|
|
297
301
|
kwargs,
|
|
298
302
|
)
|
|
299
303
|
|
|
304
|
+
async def invoke_reserved(
|
|
305
|
+
self,
|
|
306
|
+
reservation: _InvocationReservation,
|
|
307
|
+
function: Callable[..., Awaitable[_R]],
|
|
308
|
+
*args: object,
|
|
309
|
+
) -> _R:
|
|
310
|
+
"""兑现已接受工作的单次许可;不重新开放 bridge/public admission。"""
|
|
311
|
+
self._require_application_loop("invoke_reserved")
|
|
312
|
+
runtime = await self._container.get(_InvocationRuntime)
|
|
313
|
+
async with runtime.reserved_lease(reservation):
|
|
314
|
+
return cast(
|
|
315
|
+
_R,
|
|
316
|
+
await self._invoke_pipeline(str(uuid4()), function, args, {}, callable_entry=True),
|
|
317
|
+
)
|
|
318
|
+
|
|
300
319
|
def _validate_call(
|
|
301
320
|
self,
|
|
302
321
|
operation: str,
|
|
@@ -12,8 +12,9 @@ from typing import TYPE_CHECKING
|
|
|
12
12
|
from dishka import DEFAULT_COMPONENT, DependencyKey, Provider, Scope
|
|
13
13
|
|
|
14
14
|
from ..diagnostics import SourceLocation
|
|
15
|
-
from ..modularity import
|
|
15
|
+
from ..modularity import ModuleKey, ModuleRegistry
|
|
16
16
|
from .contracts import HostedService, _validate_service_type
|
|
17
|
+
from .declarations import HostedServices
|
|
17
18
|
from .errors import HostedServiceContractError, HostedServiceDefinitionError
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
@@ -36,19 +37,16 @@ class HostedServiceRegistration:
|
|
|
36
37
|
class HostedServiceCatalog:
|
|
37
38
|
"""顺序来自 Module dependency order 与各 Module 的声明顺序。"""
|
|
38
39
|
|
|
39
|
-
__slots__ = ("_by_name", "
|
|
40
|
+
__slots__ = ("_by_name", "_fingerprint", "_registrations")
|
|
40
41
|
|
|
41
42
|
def __init__(
|
|
42
43
|
self,
|
|
43
44
|
registrations: tuple[HostedServiceRegistration, ...],
|
|
44
|
-
*,
|
|
45
|
-
declarations: tuple[tuple[type[AppModule], tuple[type[HostedService], ...]], ...],
|
|
46
45
|
) -> None:
|
|
47
46
|
self._registrations = registrations
|
|
48
47
|
self._by_name: Mapping[str, HostedServiceRegistration] = MappingProxyType(
|
|
49
48
|
{item.name: item for item in registrations}
|
|
50
49
|
)
|
|
51
|
-
self._declarations = declarations
|
|
52
50
|
encoded = json.dumps(
|
|
53
51
|
[
|
|
54
52
|
{
|
|
@@ -100,26 +98,18 @@ class HostedServiceCatalog:
|
|
|
100
98
|
service=item.name,
|
|
101
99
|
reason="HostedService process metadata changed after build",
|
|
102
100
|
)
|
|
103
|
-
for module_type, declared in self._declarations:
|
|
104
|
-
if module_type.__dict__.get("hosted_services", ()) is not declared:
|
|
105
|
-
raise HostedServiceDefinitionError(
|
|
106
|
-
service=f"{module_type.__module__}.{module_type.__qualname__}",
|
|
107
|
-
reason="Module hosted_services metadata changed after build",
|
|
108
|
-
)
|
|
109
101
|
|
|
110
102
|
|
|
111
103
|
def _build_hosted_service_catalog(registry: ModuleRegistry) -> HostedServiceCatalog:
|
|
112
104
|
registrations: list[HostedServiceRegistration] = []
|
|
113
|
-
declarations: list[tuple[type[AppModule], tuple[type[HostedService], ...]]] = []
|
|
114
105
|
names: dict[str, ModuleKey] = {}
|
|
115
|
-
for descriptor in registry.
|
|
116
|
-
declared =
|
|
106
|
+
for descriptor, declaration in registry.declarations(HostedServices):
|
|
107
|
+
declared = declaration.services
|
|
117
108
|
if not isinstance(declared, tuple):
|
|
118
109
|
raise HostedServiceDefinitionError(
|
|
119
110
|
service=str(descriptor.key),
|
|
120
111
|
reason="Module hosted_services must be an immutable tuple",
|
|
121
112
|
)
|
|
122
|
-
declarations.append((descriptor.module_type, declared))
|
|
123
113
|
for declaration_index, candidate in enumerate(declared):
|
|
124
114
|
_validate_service_type(candidate)
|
|
125
115
|
name = f"{candidate.__module__}.{candidate.__qualname__}"
|
|
@@ -140,7 +130,7 @@ def _build_hosted_service_catalog(registry: ModuleRegistry) -> HostedServiceCata
|
|
|
140
130
|
run_in_background_processes=candidate.run_in_background_processes,
|
|
141
131
|
)
|
|
142
132
|
)
|
|
143
|
-
return HostedServiceCatalog(tuple(registrations)
|
|
133
|
+
return HostedServiceCatalog(tuple(registrations))
|
|
144
134
|
|
|
145
135
|
|
|
146
136
|
def _contribute_hosted_service_defaults(
|
|
@@ -7,6 +7,8 @@ from abc import ABC, abstractmethod
|
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from typing import TYPE_CHECKING, ClassVar
|
|
9
9
|
|
|
10
|
+
from dishka import Scope
|
|
11
|
+
|
|
10
12
|
from .errors import HostedServiceDefinitionError
|
|
11
13
|
|
|
12
14
|
if TYPE_CHECKING:
|
|
@@ -17,6 +19,8 @@ if TYPE_CHECKING:
|
|
|
17
19
|
class HostedService(ABC):
|
|
18
20
|
"""APP 单例;start 返回即启动成功,stop 必须等待自身资源退出。"""
|
|
19
21
|
|
|
22
|
+
_fixed_service_scope: ClassVar[Scope] = Scope.APP
|
|
23
|
+
|
|
20
24
|
run_in_background_processes: ClassVar[bool] = False
|
|
21
25
|
|
|
22
26
|
@abstractmethod
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""托管服务声明;顺序属于声明模块,解释和运行状态属于能力模块。"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from ..modularity import ModuleDeclaration, ModuleDependency, ModuleRef
|
|
6
|
+
from .contracts import HostedService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True, slots=True)
|
|
10
|
+
class HostedServices(ModuleDeclaration):
|
|
11
|
+
services: tuple[type[HostedService], ...]
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def required_module(self) -> ModuleDependency:
|
|
15
|
+
return ModuleRef("python_ddd_framework.hosted_services.module:HostedServicesModule")
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""托管服务配置、最终绑定与运行状态的模块 owner。"""
|
|
2
|
+
|
|
3
|
+
from dishka import Provider, Scope
|
|
4
|
+
|
|
5
|
+
from ..application_services.module import ApplicationServicesModule
|
|
6
|
+
from ..lifecycle import ConfigureContext
|
|
7
|
+
from ..modularity import AppModule
|
|
8
|
+
from ..services.composition import (
|
|
9
|
+
ServiceBindingContext,
|
|
10
|
+
ServiceComposition,
|
|
11
|
+
ServiceDiscoveryContext,
|
|
12
|
+
ServiceValidationContext,
|
|
13
|
+
)
|
|
14
|
+
from .catalog import (
|
|
15
|
+
HostedServiceCatalog,
|
|
16
|
+
_build_hosted_service_catalog,
|
|
17
|
+
_contribute_hosted_service_defaults,
|
|
18
|
+
_validate_hosted_service_drafts,
|
|
19
|
+
_validate_hosted_service_entries,
|
|
20
|
+
)
|
|
21
|
+
from .options import HOSTED_SERVICE_OPTIONS_SECTION, HostedServiceOptions
|
|
22
|
+
from .runtime import _HostedServiceCoordinator
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class HostedServicesModule(AppModule):
|
|
26
|
+
dependencies = (ApplicationServicesModule,)
|
|
27
|
+
catalog: HostedServiceCatalog
|
|
28
|
+
coordinator: _HostedServiceCoordinator
|
|
29
|
+
|
|
30
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
31
|
+
context.configure.default(HostedServiceOptions, section=HOSTED_SERVICE_OPTIONS_SECTION)
|
|
32
|
+
context.services.add_composition(
|
|
33
|
+
ServiceComposition(
|
|
34
|
+
discover=self._discover,
|
|
35
|
+
before_binding=lambda entries: _validate_hosted_service_entries(
|
|
36
|
+
entries, self.catalog
|
|
37
|
+
),
|
|
38
|
+
bind=self._bind,
|
|
39
|
+
validate=self._validate,
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
44
|
+
self.catalog = _build_hosted_service_catalog(context.modules)
|
|
45
|
+
_contribute_hosted_service_defaults(self.catalog, context.services)
|
|
46
|
+
|
|
47
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
48
|
+
_validate_hosted_service_drafts(context.arbitration.drafts, self.catalog)
|
|
49
|
+
application = context.application
|
|
50
|
+
self.coordinator = _HostedServiceCoordinator(
|
|
51
|
+
catalog=self.catalog,
|
|
52
|
+
options=context.options.get(HostedServiceOptions),
|
|
53
|
+
state_getter=lambda: application.state,
|
|
54
|
+
)
|
|
55
|
+
self.runtime = self.coordinator.runtime_participant()
|
|
56
|
+
provider = Provider()
|
|
57
|
+
provider.provide(lambda: self.catalog, provides=HostedServiceCatalog, scope=Scope.APP)
|
|
58
|
+
provider.provide(
|
|
59
|
+
lambda: self.coordinator,
|
|
60
|
+
provides=_HostedServiceCoordinator,
|
|
61
|
+
scope=Scope.APP,
|
|
62
|
+
)
|
|
63
|
+
context.add_provider(provider)
|
|
64
|
+
|
|
65
|
+
def _validate(self, context: ServiceValidationContext) -> None:
|
|
66
|
+
self.coordinator.attach_container(
|
|
67
|
+
context.container,
|
|
68
|
+
context.binding.modules.instance(ApplicationServicesModule).catalog,
|
|
69
|
+
)
|
|
@@ -10,7 +10,7 @@ from typing import cast
|
|
|
10
10
|
from dishka import AsyncContainer
|
|
11
11
|
|
|
12
12
|
from ..application_services import ApplicationServiceCatalog
|
|
13
|
-
from ..lifecycle import ApplicationState, ShutdownReason
|
|
13
|
+
from ..lifecycle import ApplicationState, RuntimeParticipant, ShutdownReason
|
|
14
14
|
from .bridge import HostedServiceContext, _HostedServiceBridge
|
|
15
15
|
from .catalog import HostedServiceCatalog, HostedServiceRegistration
|
|
16
16
|
from .contracts import HostedService, HostedServiceStatus
|
|
@@ -72,6 +72,35 @@ class _HostedServiceCoordinator:
|
|
|
72
72
|
def readiness(self) -> ApplicationReadinessSnapshot:
|
|
73
73
|
return self._state_owner.snapshot(self._state_getter())
|
|
74
74
|
|
|
75
|
+
def runtime_participant(self) -> RuntimeParticipant:
|
|
76
|
+
enabled = True
|
|
77
|
+
|
|
78
|
+
def prepare(run_services: bool) -> None:
|
|
79
|
+
nonlocal enabled
|
|
80
|
+
enabled = run_services
|
|
81
|
+
|
|
82
|
+
async def start() -> None:
|
|
83
|
+
if not enabled:
|
|
84
|
+
return
|
|
85
|
+
try:
|
|
86
|
+
await self.start()
|
|
87
|
+
except HostedServiceLifecycleError:
|
|
88
|
+
raise
|
|
89
|
+
except BaseException as error:
|
|
90
|
+
raise HostedServiceLifecycleError(
|
|
91
|
+
service=type(self).__qualname__,
|
|
92
|
+
operation="start",
|
|
93
|
+
error_type=type(error).__name__,
|
|
94
|
+
) from error
|
|
95
|
+
|
|
96
|
+
return RuntimeParticipant(
|
|
97
|
+
prepare=prepare,
|
|
98
|
+
start=start,
|
|
99
|
+
stop=self.stop,
|
|
100
|
+
close_admission=self.close_admission,
|
|
101
|
+
ready=lambda: self.readiness().ready,
|
|
102
|
+
)
|
|
103
|
+
|
|
75
104
|
def select_child(self) -> None:
|
|
76
105
|
if self._started:
|
|
77
106
|
raise RuntimeError("Hosted Service process selection must precede start")
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
from .application import (
|
|
2
|
+
DefaultAuthenticationApplicationService,
|
|
3
|
+
)
|
|
1
4
|
from .contracts import (
|
|
2
5
|
IDENTITY_ROLES_MANAGE,
|
|
6
|
+
IDENTITY_USER_EXTENSION,
|
|
3
7
|
IDENTITY_USERS_MANAGE,
|
|
4
8
|
AuthenticationApplicationService,
|
|
9
|
+
ChangePasswordCommand,
|
|
5
10
|
CreateRoleCommand,
|
|
6
11
|
CreateUserCommand,
|
|
7
12
|
CurrentUserView,
|
|
8
13
|
IdentityAccessStore,
|
|
14
|
+
IdentityConcurrencyCommand,
|
|
9
15
|
IdentityLoginStore,
|
|
10
16
|
IdentityManagementApplicationService,
|
|
11
17
|
IdentityOptions,
|
|
@@ -19,10 +25,16 @@ from .contracts import (
|
|
|
19
25
|
RefreshSessionStore,
|
|
20
26
|
RefreshTokenCommand,
|
|
21
27
|
RevokeTokenCommand,
|
|
28
|
+
RolePermissionsView,
|
|
22
29
|
RoleView,
|
|
30
|
+
SetPasswordCommand,
|
|
23
31
|
SetRolePermissionsCommand,
|
|
32
|
+
SetUserActiveCommand,
|
|
24
33
|
SetUserRolesCommand,
|
|
25
34
|
TokenPair,
|
|
35
|
+
UpdateRoleCommand,
|
|
36
|
+
UpdateUserCommand,
|
|
37
|
+
UserRolesView,
|
|
26
38
|
UserView,
|
|
27
39
|
)
|
|
28
40
|
from .errors import (
|
|
@@ -31,6 +43,7 @@ from .errors import (
|
|
|
31
43
|
IdentityResourceNotFoundError,
|
|
32
44
|
InvalidCredentialsError,
|
|
33
45
|
)
|
|
46
|
+
from .management import DefaultIdentityManagementApplicationService
|
|
34
47
|
from .module import IdentityModule
|
|
35
48
|
from .passwords import PasswordHasher
|
|
36
49
|
from .services import IdentityAuthenticator
|
|
@@ -39,12 +52,17 @@ from .tokens import JwtTokenService
|
|
|
39
52
|
__all__ = (
|
|
40
53
|
"IDENTITY_ROLES_MANAGE",
|
|
41
54
|
"IDENTITY_USERS_MANAGE",
|
|
55
|
+
"IDENTITY_USER_EXTENSION",
|
|
42
56
|
"AuthenticationApplicationService",
|
|
57
|
+
"ChangePasswordCommand",
|
|
43
58
|
"CreateRoleCommand",
|
|
44
59
|
"CreateUserCommand",
|
|
45
60
|
"CurrentUserView",
|
|
61
|
+
"DefaultAuthenticationApplicationService",
|
|
62
|
+
"DefaultIdentityManagementApplicationService",
|
|
46
63
|
"IdentityAccessStore",
|
|
47
64
|
"IdentityAuthenticator",
|
|
65
|
+
"IdentityConcurrencyCommand",
|
|
48
66
|
"IdentityConflictError",
|
|
49
67
|
"IdentityDefinitionError",
|
|
50
68
|
"IdentityLoginStore",
|
|
@@ -65,9 +83,15 @@ __all__ = (
|
|
|
65
83
|
"RefreshSessionStore",
|
|
66
84
|
"RefreshTokenCommand",
|
|
67
85
|
"RevokeTokenCommand",
|
|
86
|
+
"RolePermissionsView",
|
|
68
87
|
"RoleView",
|
|
88
|
+
"SetPasswordCommand",
|
|
69
89
|
"SetRolePermissionsCommand",
|
|
90
|
+
"SetUserActiveCommand",
|
|
70
91
|
"SetUserRolesCommand",
|
|
71
92
|
"TokenPair",
|
|
93
|
+
"UpdateRoleCommand",
|
|
94
|
+
"UpdateUserCommand",
|
|
95
|
+
"UserRolesView",
|
|
72
96
|
"UserView",
|
|
73
97
|
)
|