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,172 @@
|
|
|
1
|
+
"""只读投影原生 Dishka 图,核对平台 catalog 与实际 binding 的一致性。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from dishka import (
|
|
9
|
+
DEFAULT_COMPONENT,
|
|
10
|
+
AsyncContainer,
|
|
11
|
+
BaseScope,
|
|
12
|
+
DependencyKey,
|
|
13
|
+
Scope,
|
|
14
|
+
)
|
|
15
|
+
from dishka.dependency_source import Factory
|
|
16
|
+
from dishka.entities.factory_type import FactoryType
|
|
17
|
+
|
|
18
|
+
from ..application_services import ApplicationServiceCatalog
|
|
19
|
+
from ..application_services.errors import ApplicationServiceDefinitionError
|
|
20
|
+
from ..application_services.interceptors import InterceptionStage
|
|
21
|
+
from ..errors import CatalogNativeGraphMismatchError
|
|
22
|
+
from ..invocation.interception import _EntryInterceptionCatalog
|
|
23
|
+
from .arbitration import (
|
|
24
|
+
_RegistrationDraft,
|
|
25
|
+
)
|
|
26
|
+
from .registration import (
|
|
27
|
+
RegistrationDecision,
|
|
28
|
+
RegistrationKind,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class _NativeGraphRegistration:
|
|
34
|
+
key: DependencyKey
|
|
35
|
+
scope: BaseScope
|
|
36
|
+
factory: Factory
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class _NativeServiceGraph:
|
|
40
|
+
"""只读投影 Dishka 已构建图,用于证明 catalog 与真实容器一致。"""
|
|
41
|
+
|
|
42
|
+
__slots__ = ("_rows",)
|
|
43
|
+
|
|
44
|
+
def __init__(self, container: AsyncContainer) -> None:
|
|
45
|
+
rows: list[_NativeGraphRegistration] = []
|
|
46
|
+
native_registry = container.registry
|
|
47
|
+
while True:
|
|
48
|
+
rows.extend(
|
|
49
|
+
_NativeGraphRegistration(
|
|
50
|
+
key=key,
|
|
51
|
+
scope=native_registry.scope,
|
|
52
|
+
factory=factory,
|
|
53
|
+
)
|
|
54
|
+
for key, factory in native_registry.factories.items()
|
|
55
|
+
)
|
|
56
|
+
child_registry = native_registry.child_registry
|
|
57
|
+
if child_registry is None:
|
|
58
|
+
break
|
|
59
|
+
native_registry = child_registry
|
|
60
|
+
self._rows = tuple(rows)
|
|
61
|
+
|
|
62
|
+
def differences(self, drafts: tuple[_RegistrationDraft, ...]) -> tuple[str, ...]:
|
|
63
|
+
return tuple(
|
|
64
|
+
f"missing accepted {draft.contribution.kind.value} registration for {draft.key}"
|
|
65
|
+
for draft in drafts
|
|
66
|
+
if draft.decision is RegistrationDecision.ACCEPTED and not self._matches(draft)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def scope_for(self, draft: _RegistrationDraft) -> BaseScope:
|
|
70
|
+
if draft.scope is not None:
|
|
71
|
+
return draft.scope
|
|
72
|
+
for row in self._rows:
|
|
73
|
+
if row.key == draft.key:
|
|
74
|
+
return row.scope
|
|
75
|
+
raise CatalogNativeGraphMismatchError(
|
|
76
|
+
differences=(f"native scope unavailable for {draft.key}",)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def interceptor_scope(self, interceptor: type[object]) -> BaseScope | None:
|
|
80
|
+
return next(
|
|
81
|
+
(
|
|
82
|
+
row.scope
|
|
83
|
+
for row in self._rows
|
|
84
|
+
if row.key == DependencyKey(interceptor, DEFAULT_COMPONENT)
|
|
85
|
+
),
|
|
86
|
+
None,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def implementation_for(self, target: type[object]) -> type[object]:
|
|
90
|
+
key = DependencyKey(target, DEFAULT_COMPONENT)
|
|
91
|
+
while True:
|
|
92
|
+
factory = next(row.factory for row in self._rows if row.key == key)
|
|
93
|
+
if factory.type is FactoryType.ALIAS:
|
|
94
|
+
# 原生图已验证 alias 链及循环;沿真实依赖取得最终 class factory。
|
|
95
|
+
key = factory.dependencies[0]
|
|
96
|
+
continue
|
|
97
|
+
# 动态工厂只承诺注册契约,不能在 build 执行它来猜测运行实例。
|
|
98
|
+
return factory.source if isinstance(factory.source, type) else target
|
|
99
|
+
|
|
100
|
+
def _matches(self, draft: _RegistrationDraft) -> bool:
|
|
101
|
+
if draft.is_collection:
|
|
102
|
+
return any(
|
|
103
|
+
row.key == draft.key and row.factory.type is FactoryType.COLLECTION
|
|
104
|
+
for row in self._rows
|
|
105
|
+
)
|
|
106
|
+
assert draft.factory is not None
|
|
107
|
+
if draft.alias is not None:
|
|
108
|
+
return any(
|
|
109
|
+
row.key == draft.key
|
|
110
|
+
and row.factory.type is FactoryType.ALIAS
|
|
111
|
+
and row.factory.dependencies == draft.factory.dependencies
|
|
112
|
+
for row in self._rows
|
|
113
|
+
)
|
|
114
|
+
if draft.contribution.kind is RegistrationKind.MULTI:
|
|
115
|
+
return any(
|
|
116
|
+
row.key.type_hint == draft.key.type_hint
|
|
117
|
+
and row.key.component == draft.key.component
|
|
118
|
+
and row.factory.source is draft.factory.source
|
|
119
|
+
and row.scope is draft.scope
|
|
120
|
+
for row in self._rows
|
|
121
|
+
)
|
|
122
|
+
return any(
|
|
123
|
+
row.key == draft.key
|
|
124
|
+
and row.factory.source is draft.factory.source
|
|
125
|
+
and row.scope is draft.scope
|
|
126
|
+
for row in self._rows
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _validate_interceptors(
|
|
131
|
+
graph: _NativeServiceGraph,
|
|
132
|
+
catalog: ApplicationServiceCatalog,
|
|
133
|
+
entries: _EntryInterceptionCatalog,
|
|
134
|
+
) -> None:
|
|
135
|
+
# 匹配结果和实际入口 scope 来自同一编译 catalog,不另建后台类型或规则清单。
|
|
136
|
+
requirements = (
|
|
137
|
+
(
|
|
138
|
+
registration,
|
|
139
|
+
Scope.ACTION if registration.before is InterceptionStage.METHOD else Scope.REQUEST,
|
|
140
|
+
)
|
|
141
|
+
for service in catalog.registrations
|
|
142
|
+
for method in service.methods
|
|
143
|
+
for registration in method.interceptors
|
|
144
|
+
)
|
|
145
|
+
for registration, available in (*requirements, *entries.interceptor_scopes):
|
|
146
|
+
scope = graph.interceptor_scope(registration.interceptor)
|
|
147
|
+
if scope not in (Scope.APP, Scope.REQUEST, Scope.ACTION) or scope > available:
|
|
148
|
+
raise ApplicationServiceDefinitionError(
|
|
149
|
+
symbol=registration.interceptor.__qualname__,
|
|
150
|
+
reason=(
|
|
151
|
+
f"interceptor scope is unavailable before {registration.before.value}; "
|
|
152
|
+
f"owner={registration.owner}"
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _accepted_single_service_scopes(
|
|
158
|
+
drafts: tuple[_RegistrationDraft, ...],
|
|
159
|
+
native_graph: _NativeServiceGraph,
|
|
160
|
+
) -> Mapping[type[object], BaseScope]:
|
|
161
|
+
accepted: dict[type[object], BaseScope] = {}
|
|
162
|
+
for draft in drafts:
|
|
163
|
+
key_type = draft.key.type_hint
|
|
164
|
+
if (
|
|
165
|
+
draft.decision is RegistrationDecision.ACCEPTED
|
|
166
|
+
and draft.contribution.kind is not RegistrationKind.MULTI
|
|
167
|
+
and draft.factory is not None
|
|
168
|
+
and isinstance(key_type, type)
|
|
169
|
+
and draft.key.component == DEFAULT_COMPONENT
|
|
170
|
+
):
|
|
171
|
+
accepted[key_type] = native_graph.scope_for(draft)
|
|
172
|
+
return accepted
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Dishka native Provider 的受支持边界、结构验证与 mutation fingerprint。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from types import MethodType
|
|
7
|
+
|
|
8
|
+
from dishka import BaseScope, DependencyKey, Provider
|
|
9
|
+
from dishka.dependency_source import Alias, Factory, FactoryUnionMode
|
|
10
|
+
from dishka.entities.factory_type import FactoryType
|
|
11
|
+
from dishka.entities.marker import BoolMarker
|
|
12
|
+
|
|
13
|
+
from ..diagnostics import SourceLocation
|
|
14
|
+
from ..errors import RegistrationConflictError
|
|
15
|
+
from .registration import SUPPORTED_SCOPE_LABEL, SUPPORTED_SCOPES, RegistrationKind
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class _FactorySignature:
|
|
20
|
+
key: DependencyKey
|
|
21
|
+
scope: BaseScope | None
|
|
22
|
+
source_identity: tuple[int, int | None]
|
|
23
|
+
factory_type: FactoryType
|
|
24
|
+
dependencies: tuple[DependencyKey, ...]
|
|
25
|
+
keyword_dependencies: tuple[tuple[str, DependencyKey], ...]
|
|
26
|
+
cache: bool
|
|
27
|
+
allow_static_evaluation: bool
|
|
28
|
+
is_to_bind: bool
|
|
29
|
+
native_override: bool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class _CollectionSignature:
|
|
34
|
+
source_key: DependencyKey
|
|
35
|
+
key: DependencyKey
|
|
36
|
+
scope: BaseScope | None
|
|
37
|
+
cache: bool
|
|
38
|
+
collect: bool
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class _ProviderSignature:
|
|
43
|
+
factories: tuple[_FactorySignature, ...]
|
|
44
|
+
collections: tuple[_CollectionSignature, ...]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True, slots=True)
|
|
48
|
+
class _FactoryDefinition:
|
|
49
|
+
factory: Factory
|
|
50
|
+
key: DependencyKey
|
|
51
|
+
scope: BaseScope | None
|
|
52
|
+
native_override: bool
|
|
53
|
+
alias: Alias | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, slots=True)
|
|
57
|
+
class _CollectionDefinition:
|
|
58
|
+
collection: FactoryUnionMode
|
|
59
|
+
source_key: DependencyKey
|
|
60
|
+
key: DependencyKey
|
|
61
|
+
scope: BaseScope | None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True, slots=True)
|
|
65
|
+
class _ValidatedProvider:
|
|
66
|
+
factories: tuple[_FactoryDefinition, ...]
|
|
67
|
+
collections: tuple[_CollectionDefinition, ...]
|
|
68
|
+
signature: _ProviderSignature
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _NativeProviderInspector:
|
|
72
|
+
"""隔离 Dishka private dependency-source shape,返回框架拥有的稳定事实。"""
|
|
73
|
+
|
|
74
|
+
__slots__ = ("_kind", "_source")
|
|
75
|
+
|
|
76
|
+
def __init__(self, *, source: SourceLocation, kind: RegistrationKind) -> None:
|
|
77
|
+
self._source = source
|
|
78
|
+
self._kind = kind
|
|
79
|
+
|
|
80
|
+
def inspect(self, provider: Provider) -> _ValidatedProvider:
|
|
81
|
+
if provider.decorators or provider.activators:
|
|
82
|
+
raise RegistrationConflictError(
|
|
83
|
+
key=None,
|
|
84
|
+
sources=(self._source,),
|
|
85
|
+
reason=(
|
|
86
|
+
"service composition supports native provide/provide_all/alias/collect/"
|
|
87
|
+
"from_context; "
|
|
88
|
+
"decorator and activator sources are not catalog-safe"
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
if not (
|
|
92
|
+
provider.factories
|
|
93
|
+
or provider.factory_union_mode
|
|
94
|
+
or provider.aliases
|
|
95
|
+
or provider.context_vars
|
|
96
|
+
):
|
|
97
|
+
raise RegistrationConflictError(
|
|
98
|
+
key=None,
|
|
99
|
+
sources=(self._source,),
|
|
100
|
+
reason="native Provider contribution contains no dependency source",
|
|
101
|
+
)
|
|
102
|
+
# Alias 的执行与缓存仍使用 Dishka 原生模型;这里只归一化用于仲裁和指纹。
|
|
103
|
+
factories = (
|
|
104
|
+
*(
|
|
105
|
+
self._inspect_factory(provider, item.as_factory(provider.component))
|
|
106
|
+
for item in provider.context_vars
|
|
107
|
+
),
|
|
108
|
+
*(self._inspect_factory(provider, item) for item in provider.factories),
|
|
109
|
+
*(
|
|
110
|
+
self._inspect_factory(provider, alias.as_factory(None, provider.component), alias)
|
|
111
|
+
for alias in provider.aliases
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
collections = tuple(
|
|
115
|
+
self._inspect_collection(provider, item) for item in provider.factory_union_mode
|
|
116
|
+
)
|
|
117
|
+
signature = _ProviderSignature(
|
|
118
|
+
factories=tuple(
|
|
119
|
+
_FactorySignature(
|
|
120
|
+
key=item.key,
|
|
121
|
+
scope=item.scope,
|
|
122
|
+
source_identity=self.source_identity(item.factory.source),
|
|
123
|
+
factory_type=item.factory.type,
|
|
124
|
+
dependencies=tuple(item.factory.dependencies),
|
|
125
|
+
keyword_dependencies=tuple(sorted(item.factory.kw_dependencies.items())),
|
|
126
|
+
cache=item.factory.cache,
|
|
127
|
+
allow_static_evaluation=item.factory.allow_static_evaluation,
|
|
128
|
+
is_to_bind=item.factory.is_to_bind,
|
|
129
|
+
native_override=item.native_override,
|
|
130
|
+
)
|
|
131
|
+
for item in factories
|
|
132
|
+
),
|
|
133
|
+
collections=tuple(
|
|
134
|
+
_CollectionSignature(
|
|
135
|
+
source_key=item.source_key,
|
|
136
|
+
key=item.key,
|
|
137
|
+
scope=item.scope,
|
|
138
|
+
cache=item.collection.cache,
|
|
139
|
+
collect=item.collection.collect,
|
|
140
|
+
)
|
|
141
|
+
for item in collections
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
return _ValidatedProvider(factories, collections, signature)
|
|
145
|
+
|
|
146
|
+
def _inspect_factory(
|
|
147
|
+
self,
|
|
148
|
+
provider: Provider,
|
|
149
|
+
factory: Factory,
|
|
150
|
+
alias: Alias | None = None,
|
|
151
|
+
) -> _FactoryDefinition:
|
|
152
|
+
key = factory.provides.with_component(provider.component)
|
|
153
|
+
scope = factory.scope
|
|
154
|
+
if scope not in SUPPORTED_SCOPES and not (alias is not None and scope is None):
|
|
155
|
+
self._conflict(key, f"service scope must be {SUPPORTED_SCOPE_LABEL}")
|
|
156
|
+
if factory.allow_static_evaluation:
|
|
157
|
+
self._conflict(
|
|
158
|
+
key,
|
|
159
|
+
"native static evaluation would execute a service factory during "
|
|
160
|
+
"Application.build()",
|
|
161
|
+
)
|
|
162
|
+
if (
|
|
163
|
+
factory.when_active is not None and factory.type is not FactoryType.CONTEXT
|
|
164
|
+
) or factory.when_dependencies:
|
|
165
|
+
self._conflict(
|
|
166
|
+
key, "conditional native factories are outside the service arbitration contract"
|
|
167
|
+
)
|
|
168
|
+
native_override = factory.when_override == BoolMarker(True)
|
|
169
|
+
if factory.when_override is not None and not native_override:
|
|
170
|
+
self._conflict(
|
|
171
|
+
key, "conditional native override is outside the service arbitration contract"
|
|
172
|
+
)
|
|
173
|
+
if (self._kind is RegistrationKind.OVERRIDE) != native_override:
|
|
174
|
+
expected = "must" if self._kind is RegistrationKind.OVERRIDE else "must not"
|
|
175
|
+
self._conflict(
|
|
176
|
+
key, f"{self._kind.value} contribution {expected} use native override=True"
|
|
177
|
+
)
|
|
178
|
+
return _FactoryDefinition(factory, key, scope, native_override, alias)
|
|
179
|
+
|
|
180
|
+
def _inspect_collection(
|
|
181
|
+
self, provider: Provider, collection: FactoryUnionMode
|
|
182
|
+
) -> _CollectionDefinition:
|
|
183
|
+
source_key = collection.source.with_component(provider.component)
|
|
184
|
+
key = collection.provides.with_component(provider.component)
|
|
185
|
+
if self._kind is not RegistrationKind.MULTI:
|
|
186
|
+
self._conflict(
|
|
187
|
+
source_key, "native collect() must be contributed with RegistrationKind.MULTI"
|
|
188
|
+
)
|
|
189
|
+
if not collection.collect:
|
|
190
|
+
self._conflict(source_key, "multi contribution must retain native collect() semantics")
|
|
191
|
+
if collection.scope is not None and collection.scope not in SUPPORTED_SCOPES:
|
|
192
|
+
self._conflict(key, f"collection scope must be {SUPPORTED_SCOPE_LABEL}")
|
|
193
|
+
return _CollectionDefinition(collection, source_key, key, collection.scope)
|
|
194
|
+
|
|
195
|
+
def _conflict(self, key: DependencyKey, reason: str) -> None:
|
|
196
|
+
raise RegistrationConflictError(key=key, sources=(self._source,), reason=reason)
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def source_identity(source: object) -> tuple[int, int | None]:
|
|
200
|
+
if isinstance(source, MethodType):
|
|
201
|
+
# bound method 的稳定 identity 同时由 function 与 receiver 决定;
|
|
202
|
+
# 不同 Provider instance 复用同一 method 定义不是重复 factory。
|
|
203
|
+
return id(source.__func__), id(source.__self__)
|
|
204
|
+
return id(source), None
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Service registration intent、provenance 与 immutable catalog contracts。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from types import MappingProxyType
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from dishka import BaseScope, DependencyKey, Provider, Scope
|
|
12
|
+
|
|
13
|
+
from ..diagnostics import SourceLocation
|
|
14
|
+
from ..modularity import ModuleKey
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from .convention_contracts import ConventionCandidate
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
SUPPORTED_SCOPES = (Scope.APP, Scope.SESSION, Scope.REQUEST, Scope.ACTION)
|
|
21
|
+
SUPPORTED_SCOPE_LABEL = " or ".join(
|
|
22
|
+
f"{type(scope).__name__}.{scope.name}" for scope in SUPPORTED_SCOPES
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RegistrationKind(str, Enum):
|
|
27
|
+
DEFAULT = "default"
|
|
28
|
+
CONVENTION = "convention"
|
|
29
|
+
EXPLICIT = "explicit"
|
|
30
|
+
OVERRIDE = "override"
|
|
31
|
+
MULTI = "multi"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RegistrationDecision(str, Enum):
|
|
35
|
+
ACCEPTED = "accepted"
|
|
36
|
+
BACKED_OFF = "backed_off"
|
|
37
|
+
OVERRIDDEN = "overridden"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, slots=True)
|
|
41
|
+
class ProviderContribution:
|
|
42
|
+
provider: Provider
|
|
43
|
+
owner: ModuleKey
|
|
44
|
+
kind: RegistrationKind
|
|
45
|
+
source: SourceLocation
|
|
46
|
+
reason: str
|
|
47
|
+
declaration_index: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class ProviderContributionView:
|
|
52
|
+
owner: ModuleKey
|
|
53
|
+
kind: RegistrationKind
|
|
54
|
+
source: SourceLocation
|
|
55
|
+
reason: str
|
|
56
|
+
declaration_index: int
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True, slots=True)
|
|
60
|
+
class ServiceRegistration:
|
|
61
|
+
key: DependencyKey
|
|
62
|
+
scope: BaseScope
|
|
63
|
+
owner: ModuleKey
|
|
64
|
+
source: SourceLocation
|
|
65
|
+
kind: RegistrationKind
|
|
66
|
+
declaration_index: int
|
|
67
|
+
decision: RegistrationDecision
|
|
68
|
+
decision_reason: str
|
|
69
|
+
replaced_sources: tuple[SourceLocation, ...]
|
|
70
|
+
multi_ordinal: int | None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ServiceCatalog:
|
|
74
|
+
"""Application-local 诊断目录;不保存 factory,也不参与 service resolution。"""
|
|
75
|
+
|
|
76
|
+
__slots__ = ("_by_key", "_convention_candidates", "_registrations")
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
registrations: tuple[ServiceRegistration, ...],
|
|
81
|
+
convention_candidates: tuple[ConventionCandidate, ...] = (),
|
|
82
|
+
) -> None:
|
|
83
|
+
by_key: dict[DependencyKey, list[ServiceRegistration]] = {}
|
|
84
|
+
for registration in registrations:
|
|
85
|
+
by_key.setdefault(registration.key, []).append(registration)
|
|
86
|
+
self._registrations = registrations
|
|
87
|
+
self._convention_candidates = convention_candidates
|
|
88
|
+
self._by_key: Mapping[DependencyKey, tuple[ServiceRegistration, ...]] = MappingProxyType(
|
|
89
|
+
{key: tuple(values) for key, values in by_key.items()}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def registrations(self) -> tuple[ServiceRegistration, ...]:
|
|
94
|
+
return self._registrations
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def convention_candidates(self) -> tuple[ConventionCandidate, ...]:
|
|
98
|
+
return self._convention_candidates
|
|
99
|
+
|
|
100
|
+
def for_key(self, key: DependencyKey) -> tuple[ServiceRegistration, ...]:
|
|
101
|
+
return self._by_key.get(key, ())
|