python-ddd-framework 0.3.1__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.4.0.dist-info/METADATA +474 -0
- python_ddd_framework-0.4.0.dist-info/RECORD +488 -0
- {python_ddd_framework-0.3.1.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.1.dist-info/METADATA +0 -379
- python_ddd_framework-0.3.1.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.1.dist-info → python_ddd_framework-0.4.0.dist-info}/entry_points.txt +0 -0
- {python_ddd_framework-0.3.1.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {python_ddd_framework-0.3.1.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Mapping
|
|
5
6
|
from dataclasses import dataclass
|
|
7
|
+
from typing import get_origin
|
|
6
8
|
|
|
7
9
|
from dishka import (
|
|
8
10
|
DEFAULT_COMPONENT,
|
|
@@ -11,24 +13,26 @@ from dishka import (
|
|
|
11
13
|
Scope,
|
|
12
14
|
)
|
|
13
15
|
|
|
14
|
-
from ..application_services import ApplicationService
|
|
15
|
-
from ..application_services.catalog import (
|
|
16
|
-
_APPLICATION_SERVICE_COMPONENT,
|
|
17
|
-
_ApplicationServiceBinding,
|
|
18
|
-
_DiscoveredApplicationService,
|
|
19
|
-
)
|
|
20
|
-
from ..application_services.errors import ApplicationServiceDefinitionError
|
|
21
16
|
from ..diagnostics import SourceLocation
|
|
22
17
|
from ..modularity import ModuleRegistry
|
|
23
|
-
from .arbitration import (
|
|
18
|
+
from ..services.arbitration import (
|
|
24
19
|
_ArbitrationResult,
|
|
25
20
|
_provider_with_active_sources,
|
|
26
21
|
_RegistrationDraft,
|
|
27
22
|
)
|
|
28
|
-
from .
|
|
23
|
+
from ..services.native_graph import _NativeServiceGraph
|
|
24
|
+
from ..services.registration import (
|
|
29
25
|
RegistrationDecision,
|
|
30
26
|
RegistrationKind,
|
|
31
27
|
)
|
|
28
|
+
from .catalog import (
|
|
29
|
+
_APPLICATION_SERVICE_COMPONENT,
|
|
30
|
+
ApplicationServiceCatalog,
|
|
31
|
+
_ApplicationServiceBinding,
|
|
32
|
+
_DiscoveredApplicationService,
|
|
33
|
+
)
|
|
34
|
+
from .contracts import ApplicationService
|
|
35
|
+
from .errors import ApplicationServiceDefinitionError
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
@dataclass(frozen=True, slots=True)
|
|
@@ -160,6 +164,45 @@ def _validate_application_service_dependencies(
|
|
|
160
164
|
)
|
|
161
165
|
|
|
162
166
|
|
|
167
|
+
def _validate_application_service_bindings(
|
|
168
|
+
native_graph: _NativeServiceGraph,
|
|
169
|
+
catalog: ApplicationServiceCatalog,
|
|
170
|
+
proxy_factories: Mapping[DependencyKey, object],
|
|
171
|
+
) -> None:
|
|
172
|
+
implementations = {
|
|
173
|
+
DependencyKey(item.contract, _APPLICATION_SERVICE_COMPONENT): item.implementation
|
|
174
|
+
for item in catalog.registrations
|
|
175
|
+
}
|
|
176
|
+
for row in native_graph:
|
|
177
|
+
factory = native_graph.effective_factory(row)
|
|
178
|
+
candidates = (get_origin(row.key.type_hint) or row.key.type_hint, factory.source)
|
|
179
|
+
is_implementation = any(
|
|
180
|
+
isinstance(candidate, type) and issubclass(candidate, ApplicationService)
|
|
181
|
+
for candidate in candidates
|
|
182
|
+
)
|
|
183
|
+
is_proxy = any(factory.source is source for source in proxy_factories.values())
|
|
184
|
+
if not (is_implementation or is_proxy or row.key in proxy_factories):
|
|
185
|
+
continue
|
|
186
|
+
expected_source = implementations.get(row.key) or proxy_factories.get(row.key)
|
|
187
|
+
expected_scope = Scope.ACTION if row.key in implementations else Scope.REQUEST
|
|
188
|
+
# scope 相同不代表经过管线;必须证明是本次最终实现或 owner 实际生成的代理。
|
|
189
|
+
# 沿 alias 识别角色,但 alias 不能额外发布私有实现或冒充正式公开身份。
|
|
190
|
+
if (
|
|
191
|
+
expected_source is None
|
|
192
|
+
or row.factory.source is not expected_source
|
|
193
|
+
or row.factory is not factory
|
|
194
|
+
or row.scope is not expected_scope
|
|
195
|
+
or not factory.cache
|
|
196
|
+
):
|
|
197
|
+
raise ApplicationServiceDefinitionError(
|
|
198
|
+
symbol=str(row.key.type_hint),
|
|
199
|
+
reason=(
|
|
200
|
+
"ApplicationService must use its managed proxy "
|
|
201
|
+
"or private implementation binding"
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
163
206
|
def _runtime_providers(
|
|
164
207
|
arbitration: _ArbitrationResult,
|
|
165
208
|
services: tuple[_BoundApplicationService, ...],
|
|
@@ -3,58 +3,35 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
-
from collections.abc import
|
|
7
|
-
from dataclasses import dataclass
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
8
8
|
from types import MappingProxyType
|
|
9
|
-
from typing import Any, cast, get_type_hints
|
|
10
9
|
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
from ..auditing.control import _AUDITING_ATTRIBUTE
|
|
14
|
-
from ..authorization import PermissionCatalog, PermissionName
|
|
15
|
-
from ..authorization.contracts import (
|
|
16
|
-
_ALLOW_ANONYMOUS_ATTRIBUTE,
|
|
17
|
-
_AUTHORIZATION_ATTRIBUTE,
|
|
18
|
-
_authorization_requirement,
|
|
19
|
-
_AuthorizationRequirement,
|
|
20
|
-
)
|
|
21
|
-
from ..authorization.errors import AuthorizationDefinitionError
|
|
10
|
+
from ..authorization import PermissionCatalog
|
|
22
11
|
from ..diagnostics import SourceLocation
|
|
12
|
+
from ..extensions.catalog import ExtensionCatalog
|
|
13
|
+
from ..invocation.interceptors import _InterceptorRule
|
|
14
|
+
from ..invocation.methods import (
|
|
15
|
+
_POLICY_ATTRIBUTES,
|
|
16
|
+
ServiceMethod,
|
|
17
|
+
_compile_method,
|
|
18
|
+
_CompiledMethod,
|
|
19
|
+
_public_method_names,
|
|
20
|
+
)
|
|
23
21
|
from ..modularity import ModuleKey
|
|
24
|
-
from ..unit_of_work.options import
|
|
22
|
+
from ..unit_of_work.options import UnitOfWorkDeclaration
|
|
25
23
|
from .contracts import (
|
|
26
24
|
ApplicationService,
|
|
27
25
|
ApplicationServiceContract,
|
|
28
26
|
IntegrationService,
|
|
29
27
|
_ApplicationServiceDefinition,
|
|
30
28
|
_inherited_contracts,
|
|
31
|
-
_is_internal_contributor,
|
|
32
29
|
)
|
|
33
30
|
from .errors import ApplicationServiceDefinitionError, ApplicationServiceMethodError
|
|
34
|
-
from .interceptors import InterceptionStage, InterceptorRegistration, _InterceptorRule
|
|
35
|
-
from .policies import _VALIDATION_ATTRIBUTE, _effective_switch
|
|
36
|
-
from .signature import _business_signature
|
|
37
31
|
|
|
38
32
|
_APPLICATION_SERVICE_COMPONENT = "__python_ddd_framework_application_service_implementation__"
|
|
39
33
|
|
|
40
34
|
|
|
41
|
-
@dataclass(frozen=True, slots=True)
|
|
42
|
-
class ApplicationServiceMethod:
|
|
43
|
-
name: str
|
|
44
|
-
source: SourceLocation
|
|
45
|
-
requires_authenticated_user: bool
|
|
46
|
-
permissions: tuple[PermissionName, ...]
|
|
47
|
-
allows_anonymous: bool
|
|
48
|
-
service: type[object]
|
|
49
|
-
implementation: type[object]
|
|
50
|
-
method: Callable[..., Awaitable[object]]
|
|
51
|
-
validation_enabled: bool
|
|
52
|
-
auditing_enabled: bool
|
|
53
|
-
policy_sources: tuple[SourceLocation, ...]
|
|
54
|
-
interceptors: tuple[InterceptorRegistration, ...] = ()
|
|
55
|
-
unit_of_work: UnitOfWorkDeclaration = field(default_factory=UnitOfWorkDeclaration)
|
|
56
|
-
|
|
57
|
-
|
|
58
35
|
@dataclass(frozen=True, slots=True)
|
|
59
36
|
class ApplicationServiceRegistration:
|
|
60
37
|
contract: type[object]
|
|
@@ -66,8 +43,7 @@ class ApplicationServiceRegistration:
|
|
|
66
43
|
implementation_source: SourceLocation
|
|
67
44
|
replaced_sources: tuple[SourceLocation, ...]
|
|
68
45
|
declaration_index: int
|
|
69
|
-
methods: tuple[
|
|
70
|
-
is_public_service: bool
|
|
46
|
+
methods: tuple[ServiceMethod, ...]
|
|
71
47
|
has_contract: bool
|
|
72
48
|
is_integration_service: bool
|
|
73
49
|
|
|
@@ -90,22 +66,6 @@ class _ApplicationServiceBinding:
|
|
|
90
66
|
replaced_sources: tuple[SourceLocation, ...]
|
|
91
67
|
|
|
92
68
|
|
|
93
|
-
_ValidatedArguments = tuple[tuple[object, ...], dict[str, object]]
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@dataclass(frozen=True, slots=True)
|
|
97
|
-
class _CompiledMethod:
|
|
98
|
-
name: str
|
|
99
|
-
contract_method: Callable[..., Awaitable[object]]
|
|
100
|
-
implementation_method: Callable[..., Awaitable[object]]
|
|
101
|
-
validator: Callable[..., Awaitable[_ValidatedArguments]]
|
|
102
|
-
parameter_names: frozenset[str]
|
|
103
|
-
authorization: _AuthorizationRequirement
|
|
104
|
-
descriptor: ApplicationServiceMethod
|
|
105
|
-
signature: inspect.Signature
|
|
106
|
-
interceptor_steps: Mapping[InterceptionStage, tuple[InterceptorRegistration, ...]]
|
|
107
|
-
|
|
108
|
-
|
|
109
69
|
@dataclass(frozen=True, slots=True)
|
|
110
70
|
class _CompiledApplicationService:
|
|
111
71
|
registration: ApplicationServiceRegistration
|
|
@@ -120,15 +80,14 @@ class ApplicationServiceCatalog:
|
|
|
120
80
|
def __init__(self, services: tuple[_CompiledApplicationService, ...]) -> None:
|
|
121
81
|
self._compiled = services
|
|
122
82
|
self._registrations = tuple(item.registration for item in services)
|
|
123
|
-
public_services = tuple(item for item in services if item.registration.is_public_service)
|
|
124
83
|
self._by_contract: Mapping[type[object], _CompiledApplicationService] = MappingProxyType(
|
|
125
|
-
{item.registration.contract: item for item in
|
|
84
|
+
{item.registration.contract: item for item in services}
|
|
126
85
|
)
|
|
127
86
|
methods_by_identity: dict[
|
|
128
87
|
object,
|
|
129
88
|
tuple[_CompiledApplicationService, _CompiledMethod],
|
|
130
89
|
] = {}
|
|
131
|
-
for service in
|
|
90
|
+
for service in services:
|
|
132
91
|
for method in service.methods.values():
|
|
133
92
|
previous = methods_by_identity.get(method.contract_method)
|
|
134
93
|
if previous is not None:
|
|
@@ -181,17 +140,22 @@ class ApplicationServiceCatalog:
|
|
|
181
140
|
def _build_application_service_catalog(
|
|
182
141
|
bindings: tuple[_ApplicationServiceBinding, ...],
|
|
183
142
|
permission_catalog: PermissionCatalog,
|
|
184
|
-
interceptor_rules: tuple[_InterceptorRule, ...]
|
|
143
|
+
interceptor_rules: tuple[_InterceptorRule, ...],
|
|
144
|
+
extensions: ExtensionCatalog,
|
|
185
145
|
) -> ApplicationServiceCatalog:
|
|
186
146
|
return ApplicationServiceCatalog(
|
|
187
|
-
tuple(
|
|
147
|
+
tuple(
|
|
148
|
+
_compile_service(item, permission_catalog, interceptor_rules, extensions)
|
|
149
|
+
for item in bindings
|
|
150
|
+
)
|
|
188
151
|
)
|
|
189
152
|
|
|
190
153
|
|
|
191
154
|
def _compile_service(
|
|
192
155
|
binding: _ApplicationServiceBinding,
|
|
193
156
|
permission_catalog: PermissionCatalog,
|
|
194
|
-
interceptor_rules: tuple[_InterceptorRule, ...]
|
|
157
|
+
interceptor_rules: tuple[_InterceptorRule, ...],
|
|
158
|
+
extensions: ExtensionCatalog,
|
|
195
159
|
) -> _CompiledApplicationService:
|
|
196
160
|
"""把 contract declaration 与仲裁胜出的 implementation 编译成唯一调用事实。"""
|
|
197
161
|
|
|
@@ -199,7 +163,6 @@ def _compile_service(
|
|
|
199
163
|
implementation = binding.implementation
|
|
200
164
|
contract: type[object] = discovery.definition.contract or discovery.service_type
|
|
201
165
|
has_contract = discovery.definition.contract is not None
|
|
202
|
-
is_public_service = not _is_internal_contributor(implementation)
|
|
203
166
|
is_integration = issubclass(implementation, IntegrationService)
|
|
204
167
|
_validate_policy_locations(contract if has_contract else None, implementation, is_integration)
|
|
205
168
|
if is_integration and not has_contract:
|
|
@@ -218,92 +181,22 @@ def _compile_service(
|
|
|
218
181
|
contract_methods = _public_method_names(contract, is_contract=has_contract)
|
|
219
182
|
|
|
220
183
|
compiled_methods: dict[str, _CompiledMethod] = {}
|
|
221
|
-
public_methods: list[
|
|
184
|
+
public_methods: list[ServiceMethod] = []
|
|
222
185
|
for name in contract_methods:
|
|
223
|
-
|
|
224
|
-
|
|
186
|
+
method = _compile_method(
|
|
187
|
+
contract,
|
|
225
188
|
implementation,
|
|
226
189
|
name,
|
|
227
|
-
|
|
190
|
+
discovery.owner,
|
|
191
|
+
binding.implementation_owner,
|
|
192
|
+
permission_catalog,
|
|
193
|
+
interceptor_rules,
|
|
194
|
+
auditing_default=not is_integration,
|
|
195
|
+
unit_of_work_default=UnitOfWorkDeclaration(),
|
|
196
|
+
extensions=extensions,
|
|
228
197
|
)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if contract_signature.return_annotation is inspect.Signature.empty:
|
|
232
|
-
raise ApplicationServiceDefinitionError(
|
|
233
|
-
symbol=f"{implementation_symbol}.{name}",
|
|
234
|
-
reason="public service methods must declare a return annotation",
|
|
235
|
-
)
|
|
236
|
-
if _business_signature(contract_signature) != _business_signature(implementation_signature):
|
|
237
|
-
raise ApplicationServiceDefinitionError(
|
|
238
|
-
symbol=f"{implementation_symbol}.{name}",
|
|
239
|
-
reason="implementation signature must exactly match its public contract",
|
|
240
|
-
)
|
|
241
|
-
try:
|
|
242
|
-
validator = _compile_argument_validator(
|
|
243
|
-
contract_method,
|
|
244
|
-
contract_signature,
|
|
245
|
-
annotations,
|
|
246
|
-
)
|
|
247
|
-
except Exception as original_error:
|
|
248
|
-
raise ApplicationServiceDefinitionError(
|
|
249
|
-
symbol=f"{contract.__module__}.{contract.__qualname__}.{name}",
|
|
250
|
-
reason=f"Pydantic could not compile the method: {type(original_error).__name__}",
|
|
251
|
-
) from None
|
|
252
|
-
source = _method_source(contract_method, discovery.owner)
|
|
253
|
-
authorization = _authorization_requirement(implementation, implementation_method)
|
|
254
|
-
unknown_permissions = tuple(
|
|
255
|
-
permission
|
|
256
|
-
for permission in authorization.permissions
|
|
257
|
-
if permission not in permission_catalog
|
|
258
|
-
)
|
|
259
|
-
if unknown_permissions:
|
|
260
|
-
raise AuthorizationDefinitionError(
|
|
261
|
-
symbol=f"{contract.__module__}.{contract.__qualname__}.{name}",
|
|
262
|
-
reason=(
|
|
263
|
-
"authorization references unknown permissions: "
|
|
264
|
-
f"{tuple(str(item) for item in unknown_permissions)!r}"
|
|
265
|
-
),
|
|
266
|
-
)
|
|
267
|
-
descriptor = ApplicationServiceMethod(
|
|
268
|
-
name=name,
|
|
269
|
-
source=source,
|
|
270
|
-
requires_authenticated_user=authorization.requires_authenticated_user,
|
|
271
|
-
permissions=authorization.permissions,
|
|
272
|
-
allows_anonymous=authorization.allows_anonymous,
|
|
273
|
-
service=contract,
|
|
274
|
-
implementation=implementation,
|
|
275
|
-
method=implementation_method,
|
|
276
|
-
unit_of_work=_declaration(implementation, implementation_method),
|
|
277
|
-
validation_enabled=_effective_switch(
|
|
278
|
-
implementation, implementation_method, _VALIDATION_ATTRIBUTE, True
|
|
279
|
-
),
|
|
280
|
-
auditing_enabled=_effective_switch(
|
|
281
|
-
implementation, implementation_method, _AUDITING_ATTRIBUTE, not is_integration
|
|
282
|
-
),
|
|
283
|
-
policy_sources=_policy_sources(
|
|
284
|
-
implementation, implementation_method, binding.implementation_owner
|
|
285
|
-
),
|
|
286
|
-
)
|
|
287
|
-
descriptor = replace(
|
|
288
|
-
descriptor, interceptors=_match_interceptors(descriptor, interceptor_rules)
|
|
289
|
-
)
|
|
290
|
-
compiled_methods[name] = _CompiledMethod(
|
|
291
|
-
name=name,
|
|
292
|
-
contract_method=contract_method,
|
|
293
|
-
implementation_method=implementation_method,
|
|
294
|
-
validator=validator,
|
|
295
|
-
parameter_names=frozenset(tuple(contract_signature.parameters)[1:]),
|
|
296
|
-
authorization=authorization,
|
|
297
|
-
descriptor=descriptor,
|
|
298
|
-
signature=contract_signature,
|
|
299
|
-
interceptor_steps=MappingProxyType(
|
|
300
|
-
{
|
|
301
|
-
stage: tuple(item for item in descriptor.interceptors if item.before is stage)
|
|
302
|
-
for stage in InterceptionStage
|
|
303
|
-
}
|
|
304
|
-
),
|
|
305
|
-
)
|
|
306
|
-
public_methods.append(descriptor)
|
|
198
|
+
compiled_methods[name] = method
|
|
199
|
+
public_methods.append(method.descriptor)
|
|
307
200
|
|
|
308
201
|
if not public_methods:
|
|
309
202
|
raise ApplicationServiceDefinitionError(
|
|
@@ -321,7 +214,6 @@ def _compile_service(
|
|
|
321
214
|
replaced_sources=binding.replaced_sources,
|
|
322
215
|
declaration_index=discovery.declaration_index,
|
|
323
216
|
methods=tuple(public_methods),
|
|
324
|
-
is_public_service=is_public_service,
|
|
325
217
|
has_contract=has_contract,
|
|
326
218
|
is_integration_service=is_integration,
|
|
327
219
|
)
|
|
@@ -331,15 +223,6 @@ def _compile_service(
|
|
|
331
223
|
)
|
|
332
224
|
|
|
333
225
|
|
|
334
|
-
_POLICY_ATTRIBUTES = (
|
|
335
|
-
_UNIT_OF_WORK_ATTRIBUTE,
|
|
336
|
-
_AUTHORIZATION_ATTRIBUTE,
|
|
337
|
-
_ALLOW_ANONYMOUS_ATTRIBUTE,
|
|
338
|
-
_VALIDATION_ATTRIBUTE,
|
|
339
|
-
_AUDITING_ATTRIBUTE,
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
|
|
343
226
|
def _validate_policy_locations(
|
|
344
227
|
contract: type[object] | None, implementation: type[object], integration: bool
|
|
345
228
|
) -> None:
|
|
@@ -363,138 +246,3 @@ def _validate_policy_locations(
|
|
|
363
246
|
if root in contracts
|
|
364
247
|
else "IntegrationService cannot declare user authorization",
|
|
365
248
|
)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
def _policy_sources(
|
|
369
|
-
implementation: type[object], method: Callable[..., Any], owner: ModuleKey
|
|
370
|
-
) -> tuple[SourceLocation, ...]:
|
|
371
|
-
return tuple(
|
|
372
|
-
_method_source(target, owner)
|
|
373
|
-
for target in (*reversed(implementation.__mro__), method)
|
|
374
|
-
if any(attribute in vars(target) for attribute in _POLICY_ATTRIBUTES)
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
def _match_interceptors(
|
|
379
|
-
method: ApplicationServiceMethod, rules: tuple[_InterceptorRule, ...]
|
|
380
|
-
) -> tuple[InterceptorRegistration, ...]:
|
|
381
|
-
matched: list[InterceptorRegistration] = []
|
|
382
|
-
for rule in rules:
|
|
383
|
-
try:
|
|
384
|
-
accepted = rule.match(method)
|
|
385
|
-
if not isinstance(accepted, bool):
|
|
386
|
-
raise TypeError("interceptor match must return bool")
|
|
387
|
-
except Exception as error:
|
|
388
|
-
raise ApplicationServiceDefinitionError(
|
|
389
|
-
symbol=rule.registration.source.symbol
|
|
390
|
-
or rule.registration.interceptor.__qualname__,
|
|
391
|
-
reason=(
|
|
392
|
-
f"interceptor match failed for {rule.registration.owner}: "
|
|
393
|
-
f"{type(error).__name__}"
|
|
394
|
-
),
|
|
395
|
-
) from None
|
|
396
|
-
if accepted:
|
|
397
|
-
matched.append(rule.registration)
|
|
398
|
-
return tuple(sorted(matched, key=lambda item: tuple(InterceptionStage).index(item.before)))
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
def _public_method_names(candidate: type[object], *, is_contract: bool) -> tuple[str, ...]:
|
|
402
|
-
names: dict[str, None] = {}
|
|
403
|
-
for owner in reversed(candidate.__mro__):
|
|
404
|
-
if owner in (object, ApplicationService, ApplicationServiceContract):
|
|
405
|
-
continue
|
|
406
|
-
if is_contract and owner.__module__ == "typing":
|
|
407
|
-
continue
|
|
408
|
-
for name, value in owner.__dict__.items():
|
|
409
|
-
if name.startswith("_"):
|
|
410
|
-
continue
|
|
411
|
-
if inspect.isfunction(value) or isinstance(value, (staticmethod, classmethod)):
|
|
412
|
-
names[name] = None
|
|
413
|
-
return tuple(names)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def _require_async_instance_method(
|
|
417
|
-
owner: type[object],
|
|
418
|
-
name: str,
|
|
419
|
-
*,
|
|
420
|
-
role: str,
|
|
421
|
-
) -> Callable[..., Awaitable[object]]:
|
|
422
|
-
symbol = f"{owner.__module__}.{owner.__qualname__}.{name}"
|
|
423
|
-
descriptor = inspect.getattr_static(owner, name)
|
|
424
|
-
if isinstance(descriptor, (staticmethod, classmethod)):
|
|
425
|
-
raise ApplicationServiceDefinitionError(
|
|
426
|
-
symbol=symbol,
|
|
427
|
-
reason=f"public {role} methods must be instance methods",
|
|
428
|
-
)
|
|
429
|
-
method = getattr(owner, name)
|
|
430
|
-
if not inspect.isfunction(method) or not inspect.iscoroutinefunction(method):
|
|
431
|
-
raise ApplicationServiceDefinitionError(
|
|
432
|
-
symbol=symbol,
|
|
433
|
-
reason=f"public {role} methods must use async def",
|
|
434
|
-
)
|
|
435
|
-
parameters = tuple(inspect.signature(method).parameters.values())
|
|
436
|
-
if not parameters or parameters[0].kind not in (
|
|
437
|
-
inspect.Parameter.POSITIONAL_ONLY,
|
|
438
|
-
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
439
|
-
):
|
|
440
|
-
# Catalog 与 HTTP generator 都会剥离 receiver;缺少首个位置参数时不能伪装成实例方法。
|
|
441
|
-
raise ApplicationServiceDefinitionError(
|
|
442
|
-
symbol=symbol,
|
|
443
|
-
reason=f"public {role} methods must declare an instance receiver",
|
|
444
|
-
)
|
|
445
|
-
return cast(Callable[..., Awaitable[object]], method)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
def _resolved_signature(
|
|
449
|
-
method: Callable[..., Awaitable[object]],
|
|
450
|
-
) -> tuple[inspect.Signature, dict[str, object]]:
|
|
451
|
-
annotations = get_type_hints(method, include_extras=True)
|
|
452
|
-
signature = inspect.signature(method)
|
|
453
|
-
parameters = tuple(
|
|
454
|
-
parameter.replace(annotation=annotations.get(name, parameter.annotation))
|
|
455
|
-
for name, parameter in signature.parameters.items()
|
|
456
|
-
)
|
|
457
|
-
resolved = signature.replace(
|
|
458
|
-
parameters=parameters,
|
|
459
|
-
return_annotation=annotations.get("return", signature.return_annotation),
|
|
460
|
-
)
|
|
461
|
-
return resolved, annotations
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def _compile_argument_validator(
|
|
465
|
-
method: Callable[..., Awaitable[object]],
|
|
466
|
-
signature: inspect.Signature,
|
|
467
|
-
annotations: Mapping[str, object],
|
|
468
|
-
) -> Callable[..., Awaitable[_ValidatedArguments]]:
|
|
469
|
-
async def capture(*args: object, **kwargs: object) -> _ValidatedArguments:
|
|
470
|
-
# 第一个值是 validation-only self sentinel;业务 implementation 尚未解析或执行。
|
|
471
|
-
return tuple(args[1:]), dict(kwargs)
|
|
472
|
-
|
|
473
|
-
capture.__name__ = method.__name__
|
|
474
|
-
capture.__qualname__ = method.__qualname__
|
|
475
|
-
cast(Any, capture).__signature__ = signature
|
|
476
|
-
capture.__annotations__ = dict(annotations)
|
|
477
|
-
validated = validate_call(
|
|
478
|
-
config=ConfigDict(
|
|
479
|
-
arbitrary_types_allowed=True,
|
|
480
|
-
validate_by_name=True,
|
|
481
|
-
hide_input_in_errors=True,
|
|
482
|
-
)
|
|
483
|
-
)(capture)
|
|
484
|
-
return cast(Callable[..., Awaitable[_ValidatedArguments]], validated)
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
def _method_source(method: Any, owner: ModuleKey) -> SourceLocation:
|
|
488
|
-
file_name: str | None = None
|
|
489
|
-
line: int | None = None
|
|
490
|
-
try:
|
|
491
|
-
file_name = inspect.getsourcefile(method)
|
|
492
|
-
_, line = inspect.getsourcelines(method)
|
|
493
|
-
except (OSError, TypeError):
|
|
494
|
-
pass
|
|
495
|
-
return SourceLocation(
|
|
496
|
-
owner=str(owner),
|
|
497
|
-
file=file_name,
|
|
498
|
-
line=line,
|
|
499
|
-
symbol=f"{method.__module__}.{method.__qualname__}",
|
|
500
|
-
)
|
|
@@ -4,8 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from typing import TYPE_CHECKING, Protocol, TypeVar
|
|
7
|
+
from typing import TYPE_CHECKING, ClassVar, Protocol, TypeVar
|
|
8
8
|
|
|
9
|
+
from dishka import Scope
|
|
10
|
+
|
|
11
|
+
from ..modularity import ModuleRef, ModuleRequirement
|
|
12
|
+
from ..unit_of_work.options import _DEFAULT_CONNECTION_NAME
|
|
9
13
|
from .errors import ApplicationServiceDefinitionError
|
|
10
14
|
|
|
11
15
|
if TYPE_CHECKING:
|
|
@@ -15,7 +19,6 @@ _APPLICATION_SERVICE_DEFINITION_ATTRIBUTE = (
|
|
|
15
19
|
"__python_ddd_framework_application_service_definition__"
|
|
16
20
|
)
|
|
17
21
|
_TApplicationService = TypeVar("_TApplicationService", bound="ApplicationService")
|
|
18
|
-
_DEFAULT_CONNECTION_NAME = "default"
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class ApplicationServiceContract(Protocol):
|
|
@@ -25,6 +28,14 @@ class ApplicationServiceContract(Protocol):
|
|
|
25
28
|
class ApplicationService:
|
|
26
29
|
"""业务实现;无独立 Contract 时,以自身类型发布受管理代理。"""
|
|
27
30
|
|
|
31
|
+
_fixed_service_scope: ClassVar[Scope] = Scope.ACTION
|
|
32
|
+
_public_service_scope: ClassVar[Scope] = Scope.REQUEST
|
|
33
|
+
_requires_convention: ClassVar[bool] = True
|
|
34
|
+
|
|
35
|
+
__application_services_module__ = ModuleRequirement(
|
|
36
|
+
ModuleRef("python_ddd_framework.application_services.module:ApplicationServicesModule")
|
|
37
|
+
)
|
|
38
|
+
|
|
28
39
|
_setting_provider: SettingProvider | None = None
|
|
29
40
|
|
|
30
41
|
@property
|
|
@@ -39,10 +50,6 @@ class IntegrationService(ApplicationService):
|
|
|
39
50
|
"""仅供应用内协作的服务;必须声明 Contract,不承载用户授权。"""
|
|
40
51
|
|
|
41
52
|
|
|
42
|
-
class _InternalApplicationServiceContributor(ApplicationService):
|
|
43
|
-
"""框架内部多贡献者基类;不发布含糊的 single Protocol。"""
|
|
44
|
-
|
|
45
|
-
|
|
46
53
|
@dataclass(frozen=True, slots=True)
|
|
47
54
|
class _ApplicationServiceDefinition:
|
|
48
55
|
contract: type[ApplicationServiceContract] | None
|
|
@@ -74,8 +81,7 @@ def application_service(
|
|
|
74
81
|
)
|
|
75
82
|
contract = provides
|
|
76
83
|
if contract is None:
|
|
77
|
-
|
|
78
|
-
contract = _infer_contract(service_type)
|
|
84
|
+
contract = _infer_contract(service_type)
|
|
79
85
|
else:
|
|
80
86
|
_validate_contract(
|
|
81
87
|
provides,
|
|
@@ -100,7 +106,6 @@ def _definition_for(
|
|
|
100
106
|
if not issubclass(candidate, ApplicationService) or candidate in (
|
|
101
107
|
ApplicationService,
|
|
102
108
|
IntegrationService,
|
|
103
|
-
_InternalApplicationServiceContributor,
|
|
104
109
|
):
|
|
105
110
|
return None
|
|
106
111
|
metadata = candidate.__dict__.get(_APPLICATION_SERVICE_DEFINITION_ATTRIBUTE)
|
|
@@ -170,10 +175,6 @@ def _validate_contract(candidate: object, *, symbol: str) -> None:
|
|
|
170
175
|
)
|
|
171
176
|
|
|
172
177
|
|
|
173
|
-
def _is_internal_contributor(candidate: type[object]) -> bool:
|
|
174
|
-
return issubclass(candidate, _InternalApplicationServiceContributor)
|
|
175
|
-
|
|
176
|
-
|
|
177
178
|
def _validate_connection_name(connection_name: str) -> str:
|
|
178
179
|
if not isinstance(connection_name, str) or not connection_name:
|
|
179
180
|
raise ApplicationServiceDefinitionError(
|