python-platform 0.1.1__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_platform/__init__.py +445 -0
- python_platform/application/__init__.py +6 -0
- python_platform/application/build_spec.py +37 -0
- python_platform/application/builder.py +260 -0
- python_platform/application/composition.py +219 -0
- python_platform/application/runtime.py +546 -0
- python_platform/application/runtime_hooks.py +327 -0
- python_platform/application/state.py +216 -0
- python_platform/application_services/__init__.py +54 -0
- python_platform/application_services/catalog.py +500 -0
- python_platform/application_services/contracts.py +188 -0
- python_platform/application_services/dispatcher.py +423 -0
- python_platform/application_services/errors.py +89 -0
- python_platform/application_services/execution.py +199 -0
- python_platform/application_services/interceptors.py +44 -0
- python_platform/application_services/invocation.py +427 -0
- python_platform/application_services/policies.py +46 -0
- python_platform/application_services/seeding.py +72 -0
- python_platform/application_services/signature.py +49 -0
- python_platform/application_services/validation.py +84 -0
- python_platform/auditing/__init__.py +12 -0
- python_platform/auditing/contracts.py +39 -0
- python_platform/auditing/control.py +48 -0
- python_platform/auditing/sqlalchemy/__init__.py +6 -0
- python_platform/auditing/sqlalchemy/migrations/0001_platform_auditing.py +45 -0
- python_platform/auditing/sqlalchemy/migrations/__init__.py +1 -0
- python_platform/auditing/sqlalchemy/models.py +31 -0
- python_platform/auditing/sqlalchemy/module.py +32 -0
- python_platform/auditing/sqlalchemy/store.py +63 -0
- python_platform/authorization/__init__.py +38 -0
- python_platform/authorization/catalog.py +63 -0
- python_platform/authorization/contracts.py +241 -0
- python_platform/authorization/definition_discovery.py +81 -0
- python_platform/authorization/definitions.py +43 -0
- python_platform/authorization/errors.py +40 -0
- python_platform/background_execution/__init__.py +15 -0
- python_platform/background_execution/application.py +41 -0
- python_platform/background_execution/child.py +123 -0
- python_platform/background_execution/contracts.py +39 -0
- python_platform/background_execution/lifecycle.py +22 -0
- python_platform/background_execution/local.py +127 -0
- python_platform/background_execution/locks.py +31 -0
- python_platform/background_execution/management.py +17 -0
- python_platform/background_execution/module.py +10 -0
- python_platform/background_execution/permissions.py +14 -0
- python_platform/background_execution/processes.py +331 -0
- python_platform/background_jobs/__init__.py +31 -0
- python_platform/background_jobs/catalog.py +287 -0
- python_platform/background_jobs/contracts.py +202 -0
- python_platform/background_jobs/errors.py +28 -0
- python_platform/background_jobs/execution.py +201 -0
- python_platform/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
- python_platform/background_jobs/pgqueuer/__init__.py +6 -0
- python_platform/background_jobs/pgqueuer/enqueue.py +82 -0
- python_platform/background_jobs/pgqueuer/migrations/0001_pgqueuer_1_3_2.py +63 -0
- python_platform/background_jobs/pgqueuer/migrations/__init__.py +1 -0
- python_platform/background_jobs/pgqueuer/module.py +102 -0
- python_platform/background_jobs/pgqueuer/options.py +70 -0
- python_platform/background_jobs/pgqueuer/runtime.py +479 -0
- python_platform/background_jobs/pgqueuer/sql/pgqueuer_1_3_2_install.sql +118 -0
- python_platform/background_jobs/pgqueuer/supervision.py +201 -0
- python_platform/background_workers/__init__.py +22 -0
- python_platform/background_workers/catalog.py +96 -0
- python_platform/background_workers/contracts.py +124 -0
- python_platform/background_workers/errors.py +20 -0
- python_platform/background_workers/execution.py +133 -0
- python_platform/background_workers/runtime.py +203 -0
- python_platform/caching/__init__.py +11 -0
- python_platform/caching/catalog.py +73 -0
- python_platform/caching/contracts.py +73 -0
- python_platform/caching/errors.py +20 -0
- python_platform/cli/__init__.py +87 -0
- python_platform/cli/errors.py +5 -0
- python_platform/cli/project.py +69 -0
- python_platform/cli/runtime.py +68 -0
- python_platform/configuration/__init__.py +21 -0
- python_platform/configuration/composition.py +65 -0
- python_platform/configuration/contracts.py +68 -0
- python_platform/configuration/dotenv_source.py +51 -0
- python_platform/configuration/environment_source.py +38 -0
- python_platform/configuration/immutability.py +124 -0
- python_platform/configuration/input_shape.py +58 -0
- python_platform/configuration/merge.py +103 -0
- python_platform/configuration/root.py +215 -0
- python_platform/configuration/sources.py +519 -0
- python_platform/configuration/values.py +427 -0
- python_platform/configuration/yaml_source.py +51 -0
- python_platform/developer_kit/__init__.py +1 -0
- python_platform/developer_kit/generation.py +182 -0
- python_platform/developer_kit/project_metadata.py +45 -0
- python_platform/developer_kit/source.py +53 -0
- python_platform/developer_kit/templates/module/cookiecutter.json +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +10 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +24 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +46 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +29 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options.py.jinja +8 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +81 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +86 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +10 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +36 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +10 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/orders.py.jinja +57 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +12 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +19 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings.py.jinja +16 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +24 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +7 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions.py.jinja +15 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/files.py.jinja +74 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +58 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +37 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models.py.jinja +26 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +32 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repository.py.jinja +49 -0
- python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +18 -0
- python_platform/developer_kit/templates/project/cookiecutter.json +6 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.dockerignore +8 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.env.jinja +13 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.gitignore +6 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.python-version +1 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/Dockerfile +22 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +86 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/app.yaml.jinja +16 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.dev.yaml.jinja +24 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.production.yaml.jinja +13 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/pyproject.toml.jinja +28 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/__init__.py.jinja +1 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/main.py.jinja +52 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/module.py.jinja +38 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/conftest.py.jinja +8 -0
- python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/host/test_http.py.jinja +101 -0
- python_platform/developer_kit/wiring.py +85 -0
- python_platform/diagnostics/__init__.py +23 -0
- python_platform/diagnostics/journal.py +174 -0
- python_platform/diagnostics/model.py +83 -0
- python_platform/diagnostics/source.py +25 -0
- python_platform/distributed_lock/__init__.py +6 -0
- python_platform/distributed_lock/contracts.py +20 -0
- python_platform/distributed_lock/options.py +19 -0
- python_platform/domain/__init__.py +13 -0
- python_platform/domain/aggregates.py +64 -0
- python_platform/domain/entities.py +33 -0
- python_platform/domain/errors.py +12 -0
- python_platform/domain/value_objects.py +31 -0
- python_platform/errors/__init__.py +75 -0
- python_platform/errors/base.py +13 -0
- python_platform/errors/business.py +53 -0
- python_platform/errors/configuration.py +119 -0
- python_platform/errors/diagnostics.py +12 -0
- python_platform/errors/lifecycle.py +164 -0
- python_platform/errors/modularity.py +100 -0
- python_platform/errors/services.py +100 -0
- python_platform/events/__init__.py +25 -0
- python_platform/events/aggregate.py +39 -0
- python_platform/events/catalog.py +136 -0
- python_platform/events/contracts.py +39 -0
- python_platform/events/contribution.py +128 -0
- python_platform/events/discovery.py +105 -0
- python_platform/events/errors.py +48 -0
- python_platform/events/runtime.py +192 -0
- python_platform/fastapi/__init__.py +49 -0
- python_platform/fastapi/action.py +175 -0
- python_platform/fastapi/adapter.py +381 -0
- python_platform/fastapi/application_services.py +647 -0
- python_platform/fastapi/background.py +42 -0
- python_platform/fastapi/contracts.py +278 -0
- python_platform/fastapi/errors.py +32 -0
- python_platform/fastapi/filters.py +111 -0
- python_platform/fastapi/health.py +44 -0
- python_platform/fastapi/http.py +55 -0
- python_platform/fastapi/http_router.py +212 -0
- python_platform/fastapi/manual_action.py +105 -0
- python_platform/fastapi/middleware.py +93 -0
- python_platform/fastapi/parameters.py +97 -0
- python_platform/fastapi/realtime/__init__.py +13 -0
- python_platform/fastapi/realtime/authentication.py +181 -0
- python_platform/fastapi/realtime/connection.py +166 -0
- python_platform/fastapi/realtime/module.py +41 -0
- python_platform/fastapi/realtime/options.py +50 -0
- python_platform/fastapi/realtime/runtime.py +423 -0
- python_platform/fastapi/request_context.py +244 -0
- python_platform/fastapi/route_integrity.py +88 -0
- python_platform/fastapi/routing.py +410 -0
- python_platform/fastapi/server.py +181 -0
- python_platform/fastapi/settings.py +40 -0
- python_platform/fastapi/tracing.py +62 -0
- python_platform/fastapi/transfer.py +128 -0
- python_platform/fastapi/upload_limits.py +63 -0
- python_platform/fastapi/uploads.py +94 -0
- python_platform/hosted_services/__init__.py +29 -0
- python_platform/hosted_services/bridge.py +420 -0
- python_platform/hosted_services/catalog.py +259 -0
- python_platform/hosted_services/contracts.py +55 -0
- python_platform/hosted_services/errors.py +53 -0
- python_platform/hosted_services/options.py +17 -0
- python_platform/hosted_services/runtime.py +267 -0
- python_platform/hosted_services/state.py +40 -0
- python_platform/hosting/__init__.py +4 -0
- python_platform/hosting/instance.py +40 -0
- python_platform/identity/__init__.py +73 -0
- python_platform/identity/application.py +287 -0
- python_platform/identity/contracts.py +283 -0
- python_platform/identity/errors.py +28 -0
- python_platform/identity/http_api.py +90 -0
- python_platform/identity/module.py +46 -0
- python_platform/identity/passwords.py +34 -0
- python_platform/identity/permissions.py +14 -0
- python_platform/identity/services.py +108 -0
- python_platform/identity/sqlalchemy/__init__.py +5 -0
- python_platform/identity/sqlalchemy/migrations/0001_platform_identity.py +151 -0
- python_platform/identity/sqlalchemy/migrations/0002_physical_delete.py +42 -0
- python_platform/identity/sqlalchemy/migrations/__init__.py +1 -0
- python_platform/identity/sqlalchemy/models.py +85 -0
- python_platform/identity/sqlalchemy/module.py +62 -0
- python_platform/identity/sqlalchemy/stores.py +526 -0
- python_platform/identity/tokens.py +106 -0
- python_platform/invocation/__init__.py +3 -0
- python_platform/invocation/callables.py +171 -0
- python_platform/invocation/contracts.py +33 -0
- python_platform/invocation/entries.py +54 -0
- python_platform/invocation/function_runtime.py +48 -0
- python_platform/invocation/interception.py +184 -0
- python_platform/lifecycle/__init__.py +25 -0
- python_platform/lifecycle/composition.py +35 -0
- python_platform/lifecycle/context.py +31 -0
- python_platform/lifecycle/runtime.py +43 -0
- python_platform/lifecycle/state.py +20 -0
- python_platform/modularity/__init__.py +15 -0
- python_platform/modularity/contracts.py +90 -0
- python_platform/modularity/discovery.py +143 -0
- python_platform/modularity/graph.py +334 -0
- python_platform/modularity/registry.py +100 -0
- python_platform/modularity/selection.py +16 -0
- python_platform/notifications/__init__.py +19 -0
- python_platform/notifications/catalog.py +46 -0
- python_platform/notifications/contracts.py +107 -0
- python_platform/observability/__init__.py +5 -0
- python_platform/observability/context.py +43 -0
- python_platform/observability/export.py +73 -0
- python_platform/observability/formatting.py +89 -0
- python_platform/observability/logging.py +118 -0
- python_platform/observability/options.py +45 -0
- python_platform/observability/tracing.py +80 -0
- python_platform/options/__init__.py +19 -0
- python_platform/options/aliases.py +331 -0
- python_platform/options/contribution.py +62 -0
- python_platform/options/immutability.py +48 -0
- python_platform/options/input_keys.py +214 -0
- python_platform/options/issues.py +335 -0
- python_platform/options/models.py +114 -0
- python_platform/options/registry.py +280 -0
- python_platform/options/schema.py +488 -0
- python_platform/options/validation.py +120 -0
- python_platform/py.typed +0 -0
- python_platform/realtime/__init__.py +12 -0
- python_platform/realtime/contracts.py +28 -0
- python_platform/realtime/diagnostics.py +37 -0
- python_platform/realtime/messages.py +113 -0
- python_platform/redis/__init__.py +6 -0
- python_platform/redis/distributed_lock.py +212 -0
- python_platform/redis/lease_lock.py +36 -0
- python_platform/redis/module.py +50 -0
- python_platform/redis/notification_runtime.py +182 -0
- python_platform/redis/notifications.py +25 -0
- python_platform/redis/options.py +35 -0
- python_platform/redis/runtime.py +116 -0
- python_platform/services/__init__.py +39 -0
- python_platform/services/application_bindings.py +221 -0
- python_platform/services/arbitration.py +381 -0
- python_platform/services/binding.py +102 -0
- python_platform/services/contribution.py +438 -0
- python_platform/services/convention.py +292 -0
- python_platform/services/convention_contracts.py +94 -0
- python_platform/services/exposure.py +23 -0
- python_platform/services/framework_provider.py +178 -0
- python_platform/services/native_graph.py +172 -0
- python_platform/services/provider.py +204 -0
- python_platform/services/registration.py +101 -0
- python_platform/services/runtime.py +298 -0
- python_platform/settings/__init__.py +30 -0
- python_platform/settings/application.py +95 -0
- python_platform/settings/binding.py +33 -0
- python_platform/settings/catalog.py +121 -0
- python_platform/settings/changes.py +14 -0
- python_platform/settings/contracts.py +115 -0
- python_platform/settings/definition_discovery.py +71 -0
- python_platform/settings/definitions.py +41 -0
- python_platform/settings/errors.py +22 -0
- python_platform/settings/handlers.py +43 -0
- python_platform/settings/management.py +57 -0
- python_platform/settings/manager.py +89 -0
- python_platform/settings/module.py +7 -0
- python_platform/settings/notifications.py +39 -0
- python_platform/settings/permissions.py +14 -0
- python_platform/settings/provider.py +74 -0
- python_platform/settings/refresh.py +140 -0
- python_platform/settings/refresh_module.py +45 -0
- python_platform/settings/sqlalchemy/__init__.py +5 -0
- python_platform/settings/sqlalchemy/migrations/0001_platform_settings.py +31 -0
- python_platform/settings/sqlalchemy/migrations/0002_physical_delete.py +32 -0
- python_platform/settings/sqlalchemy/migrations/0003_version_tokens.py +39 -0
- python_platform/settings/sqlalchemy/migrations/__init__.py +1 -0
- python_platform/settings/sqlalchemy/models.py +36 -0
- python_platform/settings/sqlalchemy/module.py +36 -0
- python_platform/settings/sqlalchemy/store.py +86 -0
- python_platform/settings/store.py +16 -0
- python_platform/settings/values.py +39 -0
- python_platform/sqlalchemy/__init__.py +51 -0
- python_platform/sqlalchemy/alembic_runtime/__init__.py +1 -0
- python_platform/sqlalchemy/alembic_runtime/env.py +27 -0
- python_platform/sqlalchemy/alembic_runtime/script.py.mako +14 -0
- python_platform/sqlalchemy/auditing.py +48 -0
- python_platform/sqlalchemy/errors.py +85 -0
- python_platform/sqlalchemy/metadata.py +538 -0
- python_platform/sqlalchemy/migration.py +346 -0
- python_platform/sqlalchemy/module.py +48 -0
- python_platform/sqlalchemy/module_migration.py +148 -0
- python_platform/sqlalchemy/options.py +66 -0
- python_platform/sqlalchemy/repository.py +33 -0
- python_platform/sqlalchemy/runtime.py +85 -0
- python_platform/sqlalchemy/session_provider.py +45 -0
- python_platform/sqlalchemy/unit_of_work.py +97 -0
- python_platform/testing/__init__.py +5 -0
- python_platform/testing/runtime.py +112 -0
- python_platform/unit_of_work/__init__.py +14 -0
- python_platform/unit_of_work/contracts.py +223 -0
- python_platform/unit_of_work/errors.py +27 -0
- python_platform/unit_of_work/manager.py +210 -0
- python_platform/unit_of_work/options.py +92 -0
- python_platform-0.1.1.dist-info/METADATA +122 -0
- python_platform-0.1.1.dist-info/RECORD +344 -0
- python_platform-0.1.1.dist-info/WHEEL +4 -0
- python_platform-0.1.1.dist-info/entry_points.txt +9 -0
- python_platform-0.1.1.dist-info/licenses/LICENSE +7 -0
- python_platform-0.1.1.dist-info/licenses/src/python_platform/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Async Module runtime hooks、partial shutdown 与 Dishka finalization。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from typing import cast
|
|
8
|
+
|
|
9
|
+
from ..diagnostics import EventStatus, LifecyclePhase, LifecycleStage
|
|
10
|
+
from ..errors import (
|
|
11
|
+
ContainerCleanupError,
|
|
12
|
+
LifecycleInvocationError,
|
|
13
|
+
LifecycleReturnValueError,
|
|
14
|
+
)
|
|
15
|
+
from ..lifecycle import (
|
|
16
|
+
InitializeContext,
|
|
17
|
+
PostInitializeContext,
|
|
18
|
+
PreInitializeContext,
|
|
19
|
+
ShutdownContext,
|
|
20
|
+
ShutdownReason,
|
|
21
|
+
)
|
|
22
|
+
from ..modularity import ModuleDescriptor
|
|
23
|
+
from .state import (
|
|
24
|
+
LIFECYCLE_ERROR_TYPE,
|
|
25
|
+
_ApplicationStateRoot,
|
|
26
|
+
_lifecycle_failure_message,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _RuntimeLifecycle:
|
|
31
|
+
"""拥有一次 Application 的 runtime invocation 与 best-effort cleanup policy。"""
|
|
32
|
+
|
|
33
|
+
__slots__ = ("_root",)
|
|
34
|
+
|
|
35
|
+
def __init__(self, root: _ApplicationStateRoot) -> None:
|
|
36
|
+
self._root = root
|
|
37
|
+
|
|
38
|
+
async def start_modules(self) -> None:
|
|
39
|
+
for descriptor in self._root.registry.dependency_order:
|
|
40
|
+
await self._invoke_pre_initialize(descriptor)
|
|
41
|
+
for descriptor in self._root.registry.dependency_order:
|
|
42
|
+
await self._invoke_initialize(descriptor)
|
|
43
|
+
for descriptor in self._root.registry.dependency_order:
|
|
44
|
+
await self._invoke_post_initialize(descriptor)
|
|
45
|
+
|
|
46
|
+
async def shutdown_touched(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
reason: ShutdownReason,
|
|
50
|
+
) -> tuple[LifecycleInvocationError, ...]:
|
|
51
|
+
failures: list[LifecycleInvocationError] = []
|
|
52
|
+
# hook 调用前即标为 touched,所以失败 hook 自身也会获得一次逆序释放机会。
|
|
53
|
+
for descriptor in self._root.registry.shutdown_order:
|
|
54
|
+
if (
|
|
55
|
+
descriptor.key not in self._root.runtime_touched
|
|
56
|
+
or descriptor.key in self._root.released_modules
|
|
57
|
+
):
|
|
58
|
+
continue
|
|
59
|
+
context = ShutdownContext(
|
|
60
|
+
application_id=self._root.application_id,
|
|
61
|
+
descriptor=descriptor,
|
|
62
|
+
stage=LifecycleStage.SHUTDOWN,
|
|
63
|
+
application=self._root.view(self._root.state),
|
|
64
|
+
_emit=self._root.diagnostic_emitter(
|
|
65
|
+
descriptor,
|
|
66
|
+
LifecycleStage.SHUTDOWN,
|
|
67
|
+
),
|
|
68
|
+
configuration=self._root.configuration,
|
|
69
|
+
options=self._root.require_options(),
|
|
70
|
+
reason=reason,
|
|
71
|
+
completed_stages=tuple(self._root.completed_stages[descriptor.key]),
|
|
72
|
+
container=self._root.require_container(),
|
|
73
|
+
)
|
|
74
|
+
failure = await self._invoke_shutdown(descriptor, context)
|
|
75
|
+
self._root.released_modules.add(descriptor.key)
|
|
76
|
+
if failure is not None:
|
|
77
|
+
failures.append(failure)
|
|
78
|
+
return tuple(failures)
|
|
79
|
+
|
|
80
|
+
async def close_container(
|
|
81
|
+
self,
|
|
82
|
+
*,
|
|
83
|
+
exception: BaseException | None,
|
|
84
|
+
) -> tuple[ContainerCleanupError, ...]:
|
|
85
|
+
container = self._root.container
|
|
86
|
+
if container is None:
|
|
87
|
+
return ()
|
|
88
|
+
failures: list[ContainerCleanupError] = []
|
|
89
|
+
try:
|
|
90
|
+
while True:
|
|
91
|
+
try:
|
|
92
|
+
await container.close(exception)
|
|
93
|
+
except BaseException as original_error:
|
|
94
|
+
# 当前 lock 解析的 Dishka 会在调用前弹出当前 finalizer;重入 native close
|
|
95
|
+
# 才能在 BaseException 后继续穷尽其余资源,而非建立第二套 cleanup engine。
|
|
96
|
+
failures.append(self._container_cleanup_error(original_error))
|
|
97
|
+
continue
|
|
98
|
+
break
|
|
99
|
+
finally:
|
|
100
|
+
# 只在 native close 已穷尽后撤销引用;stop 后不再暴露可解析容器。
|
|
101
|
+
self._root.container = None
|
|
102
|
+
return tuple(failures)
|
|
103
|
+
|
|
104
|
+
def _container_cleanup_error(
|
|
105
|
+
self,
|
|
106
|
+
original_error: BaseException,
|
|
107
|
+
) -> ContainerCleanupError:
|
|
108
|
+
subject = "dishka-container"
|
|
109
|
+
failure_id = self._root.journal.begin_failure(
|
|
110
|
+
error_type=ContainerCleanupError.__name__,
|
|
111
|
+
phase=LifecyclePhase.CLEANUP,
|
|
112
|
+
stage=LifecycleStage.SHUTDOWN,
|
|
113
|
+
subject=subject,
|
|
114
|
+
message=(f"{subject}/shutdown failed with {type(original_error).__name__}"),
|
|
115
|
+
source=None,
|
|
116
|
+
dependency_path=(),
|
|
117
|
+
)
|
|
118
|
+
self._root.journal.complete_failure(failure_id)
|
|
119
|
+
return ContainerCleanupError(
|
|
120
|
+
failure_id=failure_id,
|
|
121
|
+
original_error=original_error,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
async def _invoke_pre_initialize(self, descriptor: ModuleDescriptor) -> None:
|
|
125
|
+
stage = LifecycleStage.PRE_INITIALIZE
|
|
126
|
+
context = PreInitializeContext(
|
|
127
|
+
application_id=self._root.application_id,
|
|
128
|
+
descriptor=descriptor,
|
|
129
|
+
stage=stage,
|
|
130
|
+
application=self._root.view(self._root.state),
|
|
131
|
+
_emit=self._root.diagnostic_emitter(descriptor, stage),
|
|
132
|
+
configuration=self._root.configuration,
|
|
133
|
+
options=self._root.require_options(),
|
|
134
|
+
container=self._root.require_container(),
|
|
135
|
+
)
|
|
136
|
+
callback = cast(
|
|
137
|
+
Callable[[PreInitializeContext], Awaitable[object]],
|
|
138
|
+
descriptor.instance.pre_initialize,
|
|
139
|
+
)
|
|
140
|
+
await self._invoke_start_hook(
|
|
141
|
+
descriptor,
|
|
142
|
+
stage,
|
|
143
|
+
lambda: callback(context),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
async def _invoke_initialize(self, descriptor: ModuleDescriptor) -> None:
|
|
147
|
+
stage = LifecycleStage.INITIALIZE
|
|
148
|
+
context = InitializeContext(
|
|
149
|
+
application_id=self._root.application_id,
|
|
150
|
+
descriptor=descriptor,
|
|
151
|
+
stage=stage,
|
|
152
|
+
application=self._root.view(self._root.state),
|
|
153
|
+
_emit=self._root.diagnostic_emitter(descriptor, stage),
|
|
154
|
+
configuration=self._root.configuration,
|
|
155
|
+
options=self._root.require_options(),
|
|
156
|
+
container=self._root.require_container(),
|
|
157
|
+
)
|
|
158
|
+
callback = cast(
|
|
159
|
+
Callable[[InitializeContext], Awaitable[object]],
|
|
160
|
+
descriptor.instance.initialize,
|
|
161
|
+
)
|
|
162
|
+
await self._invoke_start_hook(
|
|
163
|
+
descriptor,
|
|
164
|
+
stage,
|
|
165
|
+
lambda: callback(context),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
async def _invoke_post_initialize(self, descriptor: ModuleDescriptor) -> None:
|
|
169
|
+
stage = LifecycleStage.POST_INITIALIZE
|
|
170
|
+
context = PostInitializeContext(
|
|
171
|
+
application_id=self._root.application_id,
|
|
172
|
+
descriptor=descriptor,
|
|
173
|
+
stage=stage,
|
|
174
|
+
application=self._root.view(self._root.state),
|
|
175
|
+
_emit=self._root.diagnostic_emitter(descriptor, stage),
|
|
176
|
+
configuration=self._root.configuration,
|
|
177
|
+
options=self._root.require_options(),
|
|
178
|
+
container=self._root.require_container(),
|
|
179
|
+
)
|
|
180
|
+
callback = cast(
|
|
181
|
+
Callable[[PostInitializeContext], Awaitable[object]],
|
|
182
|
+
descriptor.instance.post_initialize,
|
|
183
|
+
)
|
|
184
|
+
await self._invoke_start_hook(
|
|
185
|
+
descriptor,
|
|
186
|
+
stage,
|
|
187
|
+
lambda: callback(context),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
async def _invoke_start_hook(
|
|
191
|
+
self,
|
|
192
|
+
descriptor: ModuleDescriptor,
|
|
193
|
+
stage: LifecycleStage,
|
|
194
|
+
callback: Callable[[], Awaitable[object]],
|
|
195
|
+
) -> None:
|
|
196
|
+
subject = str(descriptor.key)
|
|
197
|
+
self._root.runtime_touched.add(descriptor.key)
|
|
198
|
+
active_event = self._root.journal.start_event(
|
|
199
|
+
phase=LifecyclePhase.RUNTIME,
|
|
200
|
+
stage=stage,
|
|
201
|
+
subject=subject,
|
|
202
|
+
source=descriptor.declaration_source,
|
|
203
|
+
)
|
|
204
|
+
try:
|
|
205
|
+
result = await callback()
|
|
206
|
+
self._validate_lifecycle_result(
|
|
207
|
+
result,
|
|
208
|
+
descriptor=descriptor,
|
|
209
|
+
phase=LifecyclePhase.RUNTIME,
|
|
210
|
+
stage=stage,
|
|
211
|
+
)
|
|
212
|
+
except BaseException as original_error:
|
|
213
|
+
# cancellation/system error 也必须结构化,start 才能统一回滚 touched Modules。
|
|
214
|
+
failure_id = self._root.journal.begin_failure(
|
|
215
|
+
error_type=LIFECYCLE_ERROR_TYPE,
|
|
216
|
+
phase=LifecyclePhase.RUNTIME,
|
|
217
|
+
stage=stage,
|
|
218
|
+
subject=subject,
|
|
219
|
+
message=_lifecycle_failure_message(subject, stage, original_error),
|
|
220
|
+
source=descriptor.declaration_source,
|
|
221
|
+
dependency_path=tuple(str(key) for key in descriptor.dependency_path),
|
|
222
|
+
)
|
|
223
|
+
duration_ns = self._root.journal.finish_event(
|
|
224
|
+
active_event,
|
|
225
|
+
status=EventStatus.FAILED,
|
|
226
|
+
failure_id=failure_id,
|
|
227
|
+
)
|
|
228
|
+
error = LifecycleInvocationError(
|
|
229
|
+
failure_id=failure_id,
|
|
230
|
+
module=descriptor.key,
|
|
231
|
+
phase=LifecyclePhase.RUNTIME,
|
|
232
|
+
stage=stage,
|
|
233
|
+
dependency_path=descriptor.dependency_path,
|
|
234
|
+
duration_ns=duration_ns,
|
|
235
|
+
source=descriptor.declaration_source,
|
|
236
|
+
original_error=original_error,
|
|
237
|
+
)
|
|
238
|
+
raise error from original_error
|
|
239
|
+
|
|
240
|
+
self._root.journal.finish_event(
|
|
241
|
+
active_event,
|
|
242
|
+
status=EventStatus.SUCCEEDED,
|
|
243
|
+
failure_id=None,
|
|
244
|
+
)
|
|
245
|
+
self._root.completed_stages[descriptor.key].append(stage)
|
|
246
|
+
|
|
247
|
+
async def _invoke_shutdown(
|
|
248
|
+
self,
|
|
249
|
+
descriptor: ModuleDescriptor,
|
|
250
|
+
context: ShutdownContext,
|
|
251
|
+
) -> LifecycleInvocationError | None:
|
|
252
|
+
stage = LifecycleStage.SHUTDOWN
|
|
253
|
+
subject = str(descriptor.key)
|
|
254
|
+
active_event = self._root.journal.start_event(
|
|
255
|
+
phase=LifecyclePhase.CLEANUP,
|
|
256
|
+
stage=stage,
|
|
257
|
+
subject=subject,
|
|
258
|
+
source=descriptor.declaration_source,
|
|
259
|
+
)
|
|
260
|
+
try:
|
|
261
|
+
callback = cast(
|
|
262
|
+
Callable[[ShutdownContext], Awaitable[object]],
|
|
263
|
+
descriptor.instance.shutdown,
|
|
264
|
+
)
|
|
265
|
+
result = await callback(context)
|
|
266
|
+
self._validate_lifecycle_result(
|
|
267
|
+
result,
|
|
268
|
+
descriptor=descriptor,
|
|
269
|
+
phase=LifecyclePhase.CLEANUP,
|
|
270
|
+
stage=stage,
|
|
271
|
+
)
|
|
272
|
+
except BaseException as original_error:
|
|
273
|
+
# cleanup 首个 cancellation/system error 不能阻止后续 Module 获得释放机会。
|
|
274
|
+
failure_id = self._root.journal.begin_failure(
|
|
275
|
+
error_type=LIFECYCLE_ERROR_TYPE,
|
|
276
|
+
phase=LifecyclePhase.CLEANUP,
|
|
277
|
+
stage=stage,
|
|
278
|
+
subject=subject,
|
|
279
|
+
message=_lifecycle_failure_message(subject, stage, original_error),
|
|
280
|
+
source=descriptor.declaration_source,
|
|
281
|
+
dependency_path=tuple(str(key) for key in descriptor.dependency_path),
|
|
282
|
+
)
|
|
283
|
+
duration_ns = self._root.journal.finish_event(
|
|
284
|
+
active_event,
|
|
285
|
+
status=EventStatus.FAILED,
|
|
286
|
+
failure_id=failure_id,
|
|
287
|
+
)
|
|
288
|
+
error = LifecycleInvocationError(
|
|
289
|
+
failure_id=failure_id,
|
|
290
|
+
module=descriptor.key,
|
|
291
|
+
phase=LifecyclePhase.CLEANUP,
|
|
292
|
+
stage=stage,
|
|
293
|
+
dependency_path=descriptor.dependency_path,
|
|
294
|
+
duration_ns=duration_ns,
|
|
295
|
+
source=descriptor.declaration_source,
|
|
296
|
+
original_error=original_error,
|
|
297
|
+
)
|
|
298
|
+
self._root.journal.complete_failure(failure_id)
|
|
299
|
+
return error
|
|
300
|
+
|
|
301
|
+
self._root.journal.finish_event(
|
|
302
|
+
active_event,
|
|
303
|
+
status=EventStatus.SUCCEEDED,
|
|
304
|
+
failure_id=None,
|
|
305
|
+
)
|
|
306
|
+
self._root.completed_stages[descriptor.key].append(stage)
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def _validate_lifecycle_result(
|
|
311
|
+
result: object,
|
|
312
|
+
*,
|
|
313
|
+
descriptor: ModuleDescriptor,
|
|
314
|
+
phase: LifecyclePhase,
|
|
315
|
+
stage: LifecycleStage,
|
|
316
|
+
) -> None:
|
|
317
|
+
if result is None:
|
|
318
|
+
return
|
|
319
|
+
if inspect.iscoroutine(result):
|
|
320
|
+
result.close()
|
|
321
|
+
returned_type = f"{type(result).__module__}.{type(result).__qualname__}"
|
|
322
|
+
raise LifecycleReturnValueError(
|
|
323
|
+
module=descriptor.key,
|
|
324
|
+
phase=phase,
|
|
325
|
+
stage=stage,
|
|
326
|
+
returned_type=returned_type,
|
|
327
|
+
)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Single-Application mutable root、read-only views 与 transition ownership。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import assert_never
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from dishka import AsyncContainer
|
|
12
|
+
|
|
13
|
+
from ..application_services import ApplicationServiceCatalog
|
|
14
|
+
from ..application_services.invocation import _InvocationRuntime
|
|
15
|
+
from ..authorization import PermissionCatalog
|
|
16
|
+
from ..background_jobs import BackgroundJobCatalog
|
|
17
|
+
from ..background_workers import BackgroundWorkerCatalog
|
|
18
|
+
from ..background_workers.runtime import _BackgroundCoordinator
|
|
19
|
+
from ..caching import CacheCatalog
|
|
20
|
+
from ..configuration import ConfigurationRoot
|
|
21
|
+
from ..diagnostics import LifecyclePhase, LifecycleStage
|
|
22
|
+
from ..diagnostics.journal import _DiagnosticJournal
|
|
23
|
+
from ..errors import ApplicationRuntimeError, InvalidApplicationStateError, LifecycleInvocationError
|
|
24
|
+
from ..events import LocalEventCatalog
|
|
25
|
+
from ..events.contribution import EventContributionRegistry
|
|
26
|
+
from ..hosted_services import ApplicationReadinessSnapshot, HostedServiceCatalog
|
|
27
|
+
from ..hosted_services.runtime import _HostedServiceCoordinator
|
|
28
|
+
from ..lifecycle import ApplicationState, ApplicationView
|
|
29
|
+
from ..modularity import ModuleDescriptor, ModuleKey, ModuleRegistry
|
|
30
|
+
from ..options import OptionsSnapshot, PreOptionsSnapshot
|
|
31
|
+
from ..options.registry import _OptionsRegistry
|
|
32
|
+
from ..services import ServiceCatalog
|
|
33
|
+
from ..services.contribution import ServiceContributionRegistry
|
|
34
|
+
from ..settings import SettingCatalog
|
|
35
|
+
|
|
36
|
+
LIFECYCLE_ERROR_TYPE = LifecycleInvocationError.__name__
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(slots=True)
|
|
40
|
+
class _ApplicationStateRoot:
|
|
41
|
+
"""所有可变事实的唯一 owner;Application 之间不共享容器、状态或 journal。"""
|
|
42
|
+
|
|
43
|
+
application_id: UUID
|
|
44
|
+
registry: ModuleRegistry
|
|
45
|
+
configuration: ConfigurationRoot
|
|
46
|
+
pre_options_registry: _OptionsRegistry
|
|
47
|
+
options_registry: _OptionsRegistry
|
|
48
|
+
service_registry: ServiceContributionRegistry
|
|
49
|
+
event_registry: EventContributionRegistry
|
|
50
|
+
permission_catalog: PermissionCatalog
|
|
51
|
+
cache_catalog: CacheCatalog
|
|
52
|
+
setting_catalog: SettingCatalog
|
|
53
|
+
background_worker_catalog: BackgroundWorkerCatalog
|
|
54
|
+
background_job_catalog: BackgroundJobCatalog
|
|
55
|
+
hosted_service_catalog: HostedServiceCatalog
|
|
56
|
+
journal: _DiagnosticJournal
|
|
57
|
+
pre_options: PreOptionsSnapshot | None = None
|
|
58
|
+
options: OptionsSnapshot | None = None
|
|
59
|
+
service_catalog: ServiceCatalog | None = None
|
|
60
|
+
application_service_catalog: ApplicationServiceCatalog | None = None
|
|
61
|
+
event_catalog: LocalEventCatalog | None = None
|
|
62
|
+
invocation_runtime: _InvocationRuntime | None = None
|
|
63
|
+
background_coordinator: _BackgroundCoordinator | None = None
|
|
64
|
+
hosted_service_coordinator: _HostedServiceCoordinator | None = None
|
|
65
|
+
container: AsyncContainer | None = None
|
|
66
|
+
state: ApplicationState = ApplicationState.COMPOSED
|
|
67
|
+
transition_token: object | None = None
|
|
68
|
+
runtime_touched: set[ModuleKey] = field(default_factory=set)
|
|
69
|
+
released_modules: set[ModuleKey] = field(default_factory=set)
|
|
70
|
+
completed_stages: dict[ModuleKey, list[LifecycleStage]] = field(default_factory=dict)
|
|
71
|
+
termination_event: asyncio.Event = field(default_factory=asyncio.Event)
|
|
72
|
+
runtime_failure: ApplicationRuntimeError | None = None
|
|
73
|
+
termination_failure: BaseException | None = None
|
|
74
|
+
runtime_monitor_task: asyncio.Task[None] | None = None
|
|
75
|
+
|
|
76
|
+
def require_pre_options(self) -> PreOptionsSnapshot:
|
|
77
|
+
if self.pre_options is None:
|
|
78
|
+
raise RuntimeError(
|
|
79
|
+
"PreOptions snapshot is unavailable before the pre_configure barrier"
|
|
80
|
+
)
|
|
81
|
+
return self.pre_options
|
|
82
|
+
|
|
83
|
+
def require_options(self) -> OptionsSnapshot:
|
|
84
|
+
if self.options is None:
|
|
85
|
+
raise RuntimeError("Options snapshot is unavailable before composition completes")
|
|
86
|
+
return self.options
|
|
87
|
+
|
|
88
|
+
def require_service_catalog(self) -> ServiceCatalog:
|
|
89
|
+
if self.service_catalog is None:
|
|
90
|
+
raise RuntimeError("Service catalog is unavailable before composition completes")
|
|
91
|
+
return self.service_catalog
|
|
92
|
+
|
|
93
|
+
def require_container(self) -> AsyncContainer:
|
|
94
|
+
if self.container is None:
|
|
95
|
+
raise RuntimeError("Dishka container is unavailable after Application cleanup")
|
|
96
|
+
return self.container
|
|
97
|
+
|
|
98
|
+
def require_application_service_catalog(self) -> ApplicationServiceCatalog:
|
|
99
|
+
if self.application_service_catalog is None:
|
|
100
|
+
raise RuntimeError(
|
|
101
|
+
"Application Service catalog is unavailable before composition completes"
|
|
102
|
+
)
|
|
103
|
+
return self.application_service_catalog
|
|
104
|
+
|
|
105
|
+
def require_event_catalog(self) -> LocalEventCatalog:
|
|
106
|
+
if self.event_catalog is None:
|
|
107
|
+
raise RuntimeError("Local Event catalog is unavailable before composition completes")
|
|
108
|
+
return self.event_catalog
|
|
109
|
+
|
|
110
|
+
def require_permission_catalog(self) -> PermissionCatalog:
|
|
111
|
+
return self.permission_catalog
|
|
112
|
+
|
|
113
|
+
def require_cache_catalog(self) -> CacheCatalog:
|
|
114
|
+
return self.cache_catalog
|
|
115
|
+
|
|
116
|
+
def require_setting_catalog(self) -> SettingCatalog:
|
|
117
|
+
return self.setting_catalog
|
|
118
|
+
|
|
119
|
+
def require_background_worker_catalog(self) -> BackgroundWorkerCatalog:
|
|
120
|
+
return self.background_worker_catalog
|
|
121
|
+
|
|
122
|
+
def require_background_job_catalog(self) -> BackgroundJobCatalog:
|
|
123
|
+
return self.background_job_catalog
|
|
124
|
+
|
|
125
|
+
def require_invocation_runtime(self) -> _InvocationRuntime:
|
|
126
|
+
if self.invocation_runtime is None:
|
|
127
|
+
raise RuntimeError("Application invocation runtime is unavailable")
|
|
128
|
+
return self.invocation_runtime
|
|
129
|
+
|
|
130
|
+
def require_background_coordinator(self) -> _BackgroundCoordinator:
|
|
131
|
+
if self.background_coordinator is None:
|
|
132
|
+
raise RuntimeError("Application background coordinator is unavailable")
|
|
133
|
+
return self.background_coordinator
|
|
134
|
+
|
|
135
|
+
def require_hosted_service_catalog(self) -> HostedServiceCatalog:
|
|
136
|
+
return self.hosted_service_catalog
|
|
137
|
+
|
|
138
|
+
def require_hosted_service_coordinator(self) -> _HostedServiceCoordinator:
|
|
139
|
+
if self.hosted_service_coordinator is None:
|
|
140
|
+
raise RuntimeError("Hosted Service coordinator is unavailable")
|
|
141
|
+
return self.hosted_service_coordinator
|
|
142
|
+
|
|
143
|
+
def readiness(self) -> ApplicationReadinessSnapshot:
|
|
144
|
+
return self.require_hosted_service_coordinator().readiness()
|
|
145
|
+
|
|
146
|
+
def view(self, state: ApplicationState | None) -> ApplicationView:
|
|
147
|
+
return ApplicationView(
|
|
148
|
+
application_id=self.application_id,
|
|
149
|
+
state=state,
|
|
150
|
+
modules=self.registry.snapshot,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def diagnostic_emitter(
|
|
154
|
+
self,
|
|
155
|
+
descriptor: ModuleDescriptor,
|
|
156
|
+
stage: LifecycleStage,
|
|
157
|
+
) -> Callable[[str], None]:
|
|
158
|
+
phase = _phase_for_stage(stage)
|
|
159
|
+
|
|
160
|
+
def emit(code: str) -> None:
|
|
161
|
+
self.journal.emit(
|
|
162
|
+
phase=phase,
|
|
163
|
+
stage=stage,
|
|
164
|
+
subject=str(descriptor.key),
|
|
165
|
+
code=code,
|
|
166
|
+
source=descriptor.declaration_source,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return emit
|
|
170
|
+
|
|
171
|
+
def claim_transition(
|
|
172
|
+
self,
|
|
173
|
+
*,
|
|
174
|
+
operation: str,
|
|
175
|
+
allowed: tuple[ApplicationState, ...],
|
|
176
|
+
) -> object:
|
|
177
|
+
# claim 在首次 await 前完成;并发调用立即失败,不排队改变调用者语义。
|
|
178
|
+
if self.transition_token is not None or self.state not in allowed:
|
|
179
|
+
raise InvalidApplicationStateError(
|
|
180
|
+
operation=operation,
|
|
181
|
+
current=self.state,
|
|
182
|
+
allowed=allowed,
|
|
183
|
+
)
|
|
184
|
+
token = object()
|
|
185
|
+
self.transition_token = token
|
|
186
|
+
return token
|
|
187
|
+
|
|
188
|
+
def release_transition(self, token: object) -> None:
|
|
189
|
+
assert self.transition_token is token
|
|
190
|
+
self.transition_token = None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _phase_for_stage(stage: LifecycleStage) -> LifecyclePhase:
|
|
194
|
+
match stage:
|
|
195
|
+
case (
|
|
196
|
+
LifecycleStage.PRE_CONFIGURE | LifecycleStage.CONFIGURE | LifecycleStage.POST_CONFIGURE
|
|
197
|
+
):
|
|
198
|
+
return LifecyclePhase.COMPOSITION
|
|
199
|
+
case (
|
|
200
|
+
LifecycleStage.PRE_INITIALIZE
|
|
201
|
+
| LifecycleStage.INITIALIZE
|
|
202
|
+
| LifecycleStage.POST_INITIALIZE
|
|
203
|
+
):
|
|
204
|
+
return LifecyclePhase.RUNTIME
|
|
205
|
+
case LifecycleStage.SHUTDOWN:
|
|
206
|
+
return LifecyclePhase.CLEANUP
|
|
207
|
+
case _ as unreachable:
|
|
208
|
+
assert_never(unreachable)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _lifecycle_failure_message(
|
|
212
|
+
subject: str,
|
|
213
|
+
stage: LifecycleStage,
|
|
214
|
+
original_error: BaseException,
|
|
215
|
+
) -> str:
|
|
216
|
+
return f"{subject}/{stage.value} failed with {type(original_error).__name__}"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from ..invocation import ApplicationInvocationContext
|
|
2
|
+
from .catalog import (
|
|
3
|
+
ApplicationServiceCatalog,
|
|
4
|
+
ApplicationServiceMethod,
|
|
5
|
+
ApplicationServiceRegistration,
|
|
6
|
+
)
|
|
7
|
+
from .contracts import (
|
|
8
|
+
ApplicationService,
|
|
9
|
+
ApplicationServiceContract,
|
|
10
|
+
IntegrationService,
|
|
11
|
+
application_service,
|
|
12
|
+
)
|
|
13
|
+
from .errors import (
|
|
14
|
+
ApplicationInvocationCleanupError,
|
|
15
|
+
ApplicationInvocationRejectedError,
|
|
16
|
+
ApplicationServiceDefinitionError,
|
|
17
|
+
ApplicationServiceMethodError,
|
|
18
|
+
ApplicationServiceValidationError,
|
|
19
|
+
ApplicationServiceValidationIssue,
|
|
20
|
+
CrossTaskInvocationError,
|
|
21
|
+
NestedConnectionError,
|
|
22
|
+
)
|
|
23
|
+
from .execution import MethodInvocation
|
|
24
|
+
from .interceptors import InterceptionStage, Interceptor, InterceptorRegistration
|
|
25
|
+
from .policies import disable_validation, enable_validation
|
|
26
|
+
from .seeding import DataSeedContributor, DataSeeder, DataSeedError
|
|
27
|
+
|
|
28
|
+
__all__ = (
|
|
29
|
+
"ApplicationInvocationCleanupError",
|
|
30
|
+
"ApplicationInvocationContext",
|
|
31
|
+
"ApplicationInvocationRejectedError",
|
|
32
|
+
"ApplicationService",
|
|
33
|
+
"ApplicationServiceCatalog",
|
|
34
|
+
"ApplicationServiceContract",
|
|
35
|
+
"ApplicationServiceDefinitionError",
|
|
36
|
+
"ApplicationServiceMethod",
|
|
37
|
+
"ApplicationServiceMethodError",
|
|
38
|
+
"ApplicationServiceRegistration",
|
|
39
|
+
"ApplicationServiceValidationError",
|
|
40
|
+
"ApplicationServiceValidationIssue",
|
|
41
|
+
"CrossTaskInvocationError",
|
|
42
|
+
"DataSeedContributor",
|
|
43
|
+
"DataSeedError",
|
|
44
|
+
"DataSeeder",
|
|
45
|
+
"IntegrationService",
|
|
46
|
+
"InterceptionStage",
|
|
47
|
+
"Interceptor",
|
|
48
|
+
"InterceptorRegistration",
|
|
49
|
+
"MethodInvocation",
|
|
50
|
+
"NestedConnectionError",
|
|
51
|
+
"application_service",
|
|
52
|
+
"disable_validation",
|
|
53
|
+
"enable_validation",
|
|
54
|
+
)
|