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.
Files changed (344) hide show
  1. python_platform/__init__.py +445 -0
  2. python_platform/application/__init__.py +6 -0
  3. python_platform/application/build_spec.py +37 -0
  4. python_platform/application/builder.py +260 -0
  5. python_platform/application/composition.py +219 -0
  6. python_platform/application/runtime.py +546 -0
  7. python_platform/application/runtime_hooks.py +327 -0
  8. python_platform/application/state.py +216 -0
  9. python_platform/application_services/__init__.py +54 -0
  10. python_platform/application_services/catalog.py +500 -0
  11. python_platform/application_services/contracts.py +188 -0
  12. python_platform/application_services/dispatcher.py +423 -0
  13. python_platform/application_services/errors.py +89 -0
  14. python_platform/application_services/execution.py +199 -0
  15. python_platform/application_services/interceptors.py +44 -0
  16. python_platform/application_services/invocation.py +427 -0
  17. python_platform/application_services/policies.py +46 -0
  18. python_platform/application_services/seeding.py +72 -0
  19. python_platform/application_services/signature.py +49 -0
  20. python_platform/application_services/validation.py +84 -0
  21. python_platform/auditing/__init__.py +12 -0
  22. python_platform/auditing/contracts.py +39 -0
  23. python_platform/auditing/control.py +48 -0
  24. python_platform/auditing/sqlalchemy/__init__.py +6 -0
  25. python_platform/auditing/sqlalchemy/migrations/0001_platform_auditing.py +45 -0
  26. python_platform/auditing/sqlalchemy/migrations/__init__.py +1 -0
  27. python_platform/auditing/sqlalchemy/models.py +31 -0
  28. python_platform/auditing/sqlalchemy/module.py +32 -0
  29. python_platform/auditing/sqlalchemy/store.py +63 -0
  30. python_platform/authorization/__init__.py +38 -0
  31. python_platform/authorization/catalog.py +63 -0
  32. python_platform/authorization/contracts.py +241 -0
  33. python_platform/authorization/definition_discovery.py +81 -0
  34. python_platform/authorization/definitions.py +43 -0
  35. python_platform/authorization/errors.py +40 -0
  36. python_platform/background_execution/__init__.py +15 -0
  37. python_platform/background_execution/application.py +41 -0
  38. python_platform/background_execution/child.py +123 -0
  39. python_platform/background_execution/contracts.py +39 -0
  40. python_platform/background_execution/lifecycle.py +22 -0
  41. python_platform/background_execution/local.py +127 -0
  42. python_platform/background_execution/locks.py +31 -0
  43. python_platform/background_execution/management.py +17 -0
  44. python_platform/background_execution/module.py +10 -0
  45. python_platform/background_execution/permissions.py +14 -0
  46. python_platform/background_execution/processes.py +331 -0
  47. python_platform/background_jobs/__init__.py +31 -0
  48. python_platform/background_jobs/catalog.py +287 -0
  49. python_platform/background_jobs/contracts.py +202 -0
  50. python_platform/background_jobs/errors.py +28 -0
  51. python_platform/background_jobs/execution.py +201 -0
  52. python_platform/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
  53. python_platform/background_jobs/pgqueuer/__init__.py +6 -0
  54. python_platform/background_jobs/pgqueuer/enqueue.py +82 -0
  55. python_platform/background_jobs/pgqueuer/migrations/0001_pgqueuer_1_3_2.py +63 -0
  56. python_platform/background_jobs/pgqueuer/migrations/__init__.py +1 -0
  57. python_platform/background_jobs/pgqueuer/module.py +102 -0
  58. python_platform/background_jobs/pgqueuer/options.py +70 -0
  59. python_platform/background_jobs/pgqueuer/runtime.py +479 -0
  60. python_platform/background_jobs/pgqueuer/sql/pgqueuer_1_3_2_install.sql +118 -0
  61. python_platform/background_jobs/pgqueuer/supervision.py +201 -0
  62. python_platform/background_workers/__init__.py +22 -0
  63. python_platform/background_workers/catalog.py +96 -0
  64. python_platform/background_workers/contracts.py +124 -0
  65. python_platform/background_workers/errors.py +20 -0
  66. python_platform/background_workers/execution.py +133 -0
  67. python_platform/background_workers/runtime.py +203 -0
  68. python_platform/caching/__init__.py +11 -0
  69. python_platform/caching/catalog.py +73 -0
  70. python_platform/caching/contracts.py +73 -0
  71. python_platform/caching/errors.py +20 -0
  72. python_platform/cli/__init__.py +87 -0
  73. python_platform/cli/errors.py +5 -0
  74. python_platform/cli/project.py +69 -0
  75. python_platform/cli/runtime.py +68 -0
  76. python_platform/configuration/__init__.py +21 -0
  77. python_platform/configuration/composition.py +65 -0
  78. python_platform/configuration/contracts.py +68 -0
  79. python_platform/configuration/dotenv_source.py +51 -0
  80. python_platform/configuration/environment_source.py +38 -0
  81. python_platform/configuration/immutability.py +124 -0
  82. python_platform/configuration/input_shape.py +58 -0
  83. python_platform/configuration/merge.py +103 -0
  84. python_platform/configuration/root.py +215 -0
  85. python_platform/configuration/sources.py +519 -0
  86. python_platform/configuration/values.py +427 -0
  87. python_platform/configuration/yaml_source.py +51 -0
  88. python_platform/developer_kit/__init__.py +1 -0
  89. python_platform/developer_kit/generation.py +182 -0
  90. python_platform/developer_kit/project_metadata.py +45 -0
  91. python_platform/developer_kit/source.py +53 -0
  92. python_platform/developer_kit/templates/module/cookiecutter.json +1 -0
  93. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/__init__.py.jinja +1 -0
  94. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/__init__.py.jinja +1 -0
  95. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +10 -0
  96. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +24 -0
  97. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +46 -0
  98. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +29 -0
  99. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options.py.jinja +8 -0
  100. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +81 -0
  101. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +86 -0
  102. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/__init__.py.jinja +1 -0
  103. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +10 -0
  104. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +36 -0
  105. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/__init__.py.jinja +1 -0
  106. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +10 -0
  107. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/orders.py.jinja +57 -0
  108. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +12 -0
  109. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +19 -0
  110. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings.py.jinja +16 -0
  111. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/__init__.py.jinja +1 -0
  112. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +24 -0
  113. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +7 -0
  114. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions.py.jinja +15 -0
  115. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/__init__.py.jinja +1 -0
  116. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/files.py.jinja +74 -0
  117. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +58 -0
  118. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +37 -0
  119. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/__init__.py.jinja +1 -0
  120. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -0
  121. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models.py.jinja +26 -0
  122. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +32 -0
  123. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repository.py.jinja +49 -0
  124. python_platform/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +18 -0
  125. python_platform/developer_kit/templates/project/cookiecutter.json +6 -0
  126. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.dockerignore +8 -0
  127. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.env.jinja +13 -0
  128. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.gitignore +6 -0
  129. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/.python-version +1 -0
  130. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/Dockerfile +22 -0
  131. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +86 -0
  132. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/app.yaml.jinja +16 -0
  133. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.dev.yaml.jinja +24 -0
  134. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.production.yaml.jinja +13 -0
  135. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/pyproject.toml.jinja +28 -0
  136. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/__init__.py.jinja +1 -0
  137. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/main.py.jinja +52 -0
  138. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/module.py.jinja +38 -0
  139. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/conftest.py.jinja +8 -0
  140. python_platform/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/host/test_http.py.jinja +101 -0
  141. python_platform/developer_kit/wiring.py +85 -0
  142. python_platform/diagnostics/__init__.py +23 -0
  143. python_platform/diagnostics/journal.py +174 -0
  144. python_platform/diagnostics/model.py +83 -0
  145. python_platform/diagnostics/source.py +25 -0
  146. python_platform/distributed_lock/__init__.py +6 -0
  147. python_platform/distributed_lock/contracts.py +20 -0
  148. python_platform/distributed_lock/options.py +19 -0
  149. python_platform/domain/__init__.py +13 -0
  150. python_platform/domain/aggregates.py +64 -0
  151. python_platform/domain/entities.py +33 -0
  152. python_platform/domain/errors.py +12 -0
  153. python_platform/domain/value_objects.py +31 -0
  154. python_platform/errors/__init__.py +75 -0
  155. python_platform/errors/base.py +13 -0
  156. python_platform/errors/business.py +53 -0
  157. python_platform/errors/configuration.py +119 -0
  158. python_platform/errors/diagnostics.py +12 -0
  159. python_platform/errors/lifecycle.py +164 -0
  160. python_platform/errors/modularity.py +100 -0
  161. python_platform/errors/services.py +100 -0
  162. python_platform/events/__init__.py +25 -0
  163. python_platform/events/aggregate.py +39 -0
  164. python_platform/events/catalog.py +136 -0
  165. python_platform/events/contracts.py +39 -0
  166. python_platform/events/contribution.py +128 -0
  167. python_platform/events/discovery.py +105 -0
  168. python_platform/events/errors.py +48 -0
  169. python_platform/events/runtime.py +192 -0
  170. python_platform/fastapi/__init__.py +49 -0
  171. python_platform/fastapi/action.py +175 -0
  172. python_platform/fastapi/adapter.py +381 -0
  173. python_platform/fastapi/application_services.py +647 -0
  174. python_platform/fastapi/background.py +42 -0
  175. python_platform/fastapi/contracts.py +278 -0
  176. python_platform/fastapi/errors.py +32 -0
  177. python_platform/fastapi/filters.py +111 -0
  178. python_platform/fastapi/health.py +44 -0
  179. python_platform/fastapi/http.py +55 -0
  180. python_platform/fastapi/http_router.py +212 -0
  181. python_platform/fastapi/manual_action.py +105 -0
  182. python_platform/fastapi/middleware.py +93 -0
  183. python_platform/fastapi/parameters.py +97 -0
  184. python_platform/fastapi/realtime/__init__.py +13 -0
  185. python_platform/fastapi/realtime/authentication.py +181 -0
  186. python_platform/fastapi/realtime/connection.py +166 -0
  187. python_platform/fastapi/realtime/module.py +41 -0
  188. python_platform/fastapi/realtime/options.py +50 -0
  189. python_platform/fastapi/realtime/runtime.py +423 -0
  190. python_platform/fastapi/request_context.py +244 -0
  191. python_platform/fastapi/route_integrity.py +88 -0
  192. python_platform/fastapi/routing.py +410 -0
  193. python_platform/fastapi/server.py +181 -0
  194. python_platform/fastapi/settings.py +40 -0
  195. python_platform/fastapi/tracing.py +62 -0
  196. python_platform/fastapi/transfer.py +128 -0
  197. python_platform/fastapi/upload_limits.py +63 -0
  198. python_platform/fastapi/uploads.py +94 -0
  199. python_platform/hosted_services/__init__.py +29 -0
  200. python_platform/hosted_services/bridge.py +420 -0
  201. python_platform/hosted_services/catalog.py +259 -0
  202. python_platform/hosted_services/contracts.py +55 -0
  203. python_platform/hosted_services/errors.py +53 -0
  204. python_platform/hosted_services/options.py +17 -0
  205. python_platform/hosted_services/runtime.py +267 -0
  206. python_platform/hosted_services/state.py +40 -0
  207. python_platform/hosting/__init__.py +4 -0
  208. python_platform/hosting/instance.py +40 -0
  209. python_platform/identity/__init__.py +73 -0
  210. python_platform/identity/application.py +287 -0
  211. python_platform/identity/contracts.py +283 -0
  212. python_platform/identity/errors.py +28 -0
  213. python_platform/identity/http_api.py +90 -0
  214. python_platform/identity/module.py +46 -0
  215. python_platform/identity/passwords.py +34 -0
  216. python_platform/identity/permissions.py +14 -0
  217. python_platform/identity/services.py +108 -0
  218. python_platform/identity/sqlalchemy/__init__.py +5 -0
  219. python_platform/identity/sqlalchemy/migrations/0001_platform_identity.py +151 -0
  220. python_platform/identity/sqlalchemy/migrations/0002_physical_delete.py +42 -0
  221. python_platform/identity/sqlalchemy/migrations/__init__.py +1 -0
  222. python_platform/identity/sqlalchemy/models.py +85 -0
  223. python_platform/identity/sqlalchemy/module.py +62 -0
  224. python_platform/identity/sqlalchemy/stores.py +526 -0
  225. python_platform/identity/tokens.py +106 -0
  226. python_platform/invocation/__init__.py +3 -0
  227. python_platform/invocation/callables.py +171 -0
  228. python_platform/invocation/contracts.py +33 -0
  229. python_platform/invocation/entries.py +54 -0
  230. python_platform/invocation/function_runtime.py +48 -0
  231. python_platform/invocation/interception.py +184 -0
  232. python_platform/lifecycle/__init__.py +25 -0
  233. python_platform/lifecycle/composition.py +35 -0
  234. python_platform/lifecycle/context.py +31 -0
  235. python_platform/lifecycle/runtime.py +43 -0
  236. python_platform/lifecycle/state.py +20 -0
  237. python_platform/modularity/__init__.py +15 -0
  238. python_platform/modularity/contracts.py +90 -0
  239. python_platform/modularity/discovery.py +143 -0
  240. python_platform/modularity/graph.py +334 -0
  241. python_platform/modularity/registry.py +100 -0
  242. python_platform/modularity/selection.py +16 -0
  243. python_platform/notifications/__init__.py +19 -0
  244. python_platform/notifications/catalog.py +46 -0
  245. python_platform/notifications/contracts.py +107 -0
  246. python_platform/observability/__init__.py +5 -0
  247. python_platform/observability/context.py +43 -0
  248. python_platform/observability/export.py +73 -0
  249. python_platform/observability/formatting.py +89 -0
  250. python_platform/observability/logging.py +118 -0
  251. python_platform/observability/options.py +45 -0
  252. python_platform/observability/tracing.py +80 -0
  253. python_platform/options/__init__.py +19 -0
  254. python_platform/options/aliases.py +331 -0
  255. python_platform/options/contribution.py +62 -0
  256. python_platform/options/immutability.py +48 -0
  257. python_platform/options/input_keys.py +214 -0
  258. python_platform/options/issues.py +335 -0
  259. python_platform/options/models.py +114 -0
  260. python_platform/options/registry.py +280 -0
  261. python_platform/options/schema.py +488 -0
  262. python_platform/options/validation.py +120 -0
  263. python_platform/py.typed +0 -0
  264. python_platform/realtime/__init__.py +12 -0
  265. python_platform/realtime/contracts.py +28 -0
  266. python_platform/realtime/diagnostics.py +37 -0
  267. python_platform/realtime/messages.py +113 -0
  268. python_platform/redis/__init__.py +6 -0
  269. python_platform/redis/distributed_lock.py +212 -0
  270. python_platform/redis/lease_lock.py +36 -0
  271. python_platform/redis/module.py +50 -0
  272. python_platform/redis/notification_runtime.py +182 -0
  273. python_platform/redis/notifications.py +25 -0
  274. python_platform/redis/options.py +35 -0
  275. python_platform/redis/runtime.py +116 -0
  276. python_platform/services/__init__.py +39 -0
  277. python_platform/services/application_bindings.py +221 -0
  278. python_platform/services/arbitration.py +381 -0
  279. python_platform/services/binding.py +102 -0
  280. python_platform/services/contribution.py +438 -0
  281. python_platform/services/convention.py +292 -0
  282. python_platform/services/convention_contracts.py +94 -0
  283. python_platform/services/exposure.py +23 -0
  284. python_platform/services/framework_provider.py +178 -0
  285. python_platform/services/native_graph.py +172 -0
  286. python_platform/services/provider.py +204 -0
  287. python_platform/services/registration.py +101 -0
  288. python_platform/services/runtime.py +298 -0
  289. python_platform/settings/__init__.py +30 -0
  290. python_platform/settings/application.py +95 -0
  291. python_platform/settings/binding.py +33 -0
  292. python_platform/settings/catalog.py +121 -0
  293. python_platform/settings/changes.py +14 -0
  294. python_platform/settings/contracts.py +115 -0
  295. python_platform/settings/definition_discovery.py +71 -0
  296. python_platform/settings/definitions.py +41 -0
  297. python_platform/settings/errors.py +22 -0
  298. python_platform/settings/handlers.py +43 -0
  299. python_platform/settings/management.py +57 -0
  300. python_platform/settings/manager.py +89 -0
  301. python_platform/settings/module.py +7 -0
  302. python_platform/settings/notifications.py +39 -0
  303. python_platform/settings/permissions.py +14 -0
  304. python_platform/settings/provider.py +74 -0
  305. python_platform/settings/refresh.py +140 -0
  306. python_platform/settings/refresh_module.py +45 -0
  307. python_platform/settings/sqlalchemy/__init__.py +5 -0
  308. python_platform/settings/sqlalchemy/migrations/0001_platform_settings.py +31 -0
  309. python_platform/settings/sqlalchemy/migrations/0002_physical_delete.py +32 -0
  310. python_platform/settings/sqlalchemy/migrations/0003_version_tokens.py +39 -0
  311. python_platform/settings/sqlalchemy/migrations/__init__.py +1 -0
  312. python_platform/settings/sqlalchemy/models.py +36 -0
  313. python_platform/settings/sqlalchemy/module.py +36 -0
  314. python_platform/settings/sqlalchemy/store.py +86 -0
  315. python_platform/settings/store.py +16 -0
  316. python_platform/settings/values.py +39 -0
  317. python_platform/sqlalchemy/__init__.py +51 -0
  318. python_platform/sqlalchemy/alembic_runtime/__init__.py +1 -0
  319. python_platform/sqlalchemy/alembic_runtime/env.py +27 -0
  320. python_platform/sqlalchemy/alembic_runtime/script.py.mako +14 -0
  321. python_platform/sqlalchemy/auditing.py +48 -0
  322. python_platform/sqlalchemy/errors.py +85 -0
  323. python_platform/sqlalchemy/metadata.py +538 -0
  324. python_platform/sqlalchemy/migration.py +346 -0
  325. python_platform/sqlalchemy/module.py +48 -0
  326. python_platform/sqlalchemy/module_migration.py +148 -0
  327. python_platform/sqlalchemy/options.py +66 -0
  328. python_platform/sqlalchemy/repository.py +33 -0
  329. python_platform/sqlalchemy/runtime.py +85 -0
  330. python_platform/sqlalchemy/session_provider.py +45 -0
  331. python_platform/sqlalchemy/unit_of_work.py +97 -0
  332. python_platform/testing/__init__.py +5 -0
  333. python_platform/testing/runtime.py +112 -0
  334. python_platform/unit_of_work/__init__.py +14 -0
  335. python_platform/unit_of_work/contracts.py +223 -0
  336. python_platform/unit_of_work/errors.py +27 -0
  337. python_platform/unit_of_work/manager.py +210 -0
  338. python_platform/unit_of_work/options.py +92 -0
  339. python_platform-0.1.1.dist-info/METADATA +122 -0
  340. python_platform-0.1.1.dist-info/RECORD +344 -0
  341. python_platform-0.1.1.dist-info/WHEEL +4 -0
  342. python_platform-0.1.1.dist-info/entry_points.txt +9 -0
  343. python_platform-0.1.1.dist-info/licenses/LICENSE +7 -0
  344. 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, ())