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,381 @@
1
+ """default/explicit/override/multi 的单一仲裁策略与稳定 provider order。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from dishka import BaseScope, DependencyKey, Provider
8
+ from dishka.dependency_source import Alias, Factory, FactoryUnionMode
9
+ from dishka.entities.factory_type import FactoryType
10
+
11
+ from ..diagnostics import SourceLocation
12
+ from ..errors import RegistrationConflictError
13
+ from ..modularity import ModuleRegistry
14
+ from .contribution import _ContributionEntry
15
+ from .provider import _NativeProviderInspector
16
+ from .registration import (
17
+ ProviderContribution,
18
+ RegistrationDecision,
19
+ RegistrationKind,
20
+ )
21
+
22
+
23
+ @dataclass(slots=True)
24
+ class _RegistrationDraft:
25
+ sequence: int
26
+ entry: _ContributionEntry
27
+ key: DependencyKey
28
+ scope: BaseScope | None
29
+ factory: Factory | None
30
+ collection: FactoryUnionMode | None
31
+ collection_source_key: DependencyKey | None
32
+ is_collection: bool
33
+ decision: RegistrationDecision = RegistrationDecision.ACCEPTED
34
+ decision_reason: str = "accepted by service composition policy"
35
+ replaced_sources: tuple[SourceLocation, ...] = ()
36
+ multi_ordinal: int | None = None
37
+ alias: Alias | None = None
38
+
39
+ @property
40
+ def contribution(self) -> ProviderContribution:
41
+ return self.entry.contribution
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class _ArbitrationResult:
46
+ providers: tuple[Provider, ...]
47
+ drafts: tuple[_RegistrationDraft, ...]
48
+ ordered_entries: tuple[_ContributionEntry, ...]
49
+
50
+
51
+ class _ServiceArbitrator:
52
+ """拥有一次 service policy decision;只修改尚未发布的 registration drafts。"""
53
+
54
+ __slots__ = ("_registry",)
55
+
56
+ def __init__(self, registry: ModuleRegistry) -> None:
57
+ self._registry = registry
58
+
59
+ def arbitrate(self, entries: tuple[_ContributionEntry, ...]) -> _ArbitrationResult:
60
+ ordered_entries = self._order_entries(entries)
61
+ drafts = self._create_drafts(ordered_entries)
62
+ factories = tuple(draft for draft in drafts if not draft.is_collection)
63
+ collections = tuple(draft for draft in drafts if draft.is_collection)
64
+ self._validate_collection_output_keys(factories, collections)
65
+ self._arbitrate_multi(factories, collections)
66
+ self._arbitrate_single(factories)
67
+ retained_providers = self._retained_providers(ordered_entries, drafts)
68
+ return _ArbitrationResult(
69
+ providers=retained_providers,
70
+ drafts=drafts,
71
+ ordered_entries=ordered_entries,
72
+ )
73
+
74
+ def _order_entries(
75
+ self, entries: tuple[_ContributionEntry, ...]
76
+ ) -> tuple[_ContributionEntry, ...]:
77
+ module_positions = {
78
+ descriptor.key: position
79
+ for position, descriptor in enumerate(self._registry.dependency_order)
80
+ }
81
+ return tuple(
82
+ sorted(
83
+ entries,
84
+ key=lambda entry: (
85
+ module_positions[entry.contribution.owner],
86
+ entry.contribution.declaration_index,
87
+ ),
88
+ )
89
+ )
90
+
91
+ @staticmethod
92
+ def _create_drafts(
93
+ entries: tuple[_ContributionEntry, ...],
94
+ ) -> tuple[_RegistrationDraft, ...]:
95
+ drafts: list[_RegistrationDraft] = []
96
+ for entry in entries:
97
+ contribution = entry.contribution
98
+ validated = _NativeProviderInspector(
99
+ source=contribution.source, kind=contribution.kind
100
+ ).inspect(contribution.provider)
101
+ if validated.signature != entry.signature:
102
+ raise RegistrationConflictError(
103
+ key=None,
104
+ sources=(contribution.source,),
105
+ reason="native Provider was mutated after contribution",
106
+ )
107
+ for factory in validated.factories:
108
+ drafts.append(
109
+ _RegistrationDraft(
110
+ sequence=len(drafts),
111
+ entry=entry,
112
+ key=factory.key,
113
+ scope=factory.scope,
114
+ factory=factory.factory,
115
+ collection=None,
116
+ collection_source_key=None,
117
+ is_collection=False,
118
+ alias=factory.alias,
119
+ )
120
+ )
121
+ for collection in validated.collections:
122
+ drafts.append(
123
+ _RegistrationDraft(
124
+ sequence=len(drafts),
125
+ entry=entry,
126
+ key=collection.key,
127
+ scope=collection.scope,
128
+ factory=None,
129
+ collection=collection.collection,
130
+ collection_source_key=collection.source_key,
131
+ is_collection=True,
132
+ )
133
+ )
134
+ return tuple(drafts)
135
+
136
+ @classmethod
137
+ def _validate_collection_output_keys(
138
+ cls,
139
+ factories: tuple[_RegistrationDraft, ...],
140
+ collections: tuple[_RegistrationDraft, ...],
141
+ ) -> None:
142
+ single_by_key = cls._group_by_key(
143
+ tuple(
144
+ draft
145
+ for draft in factories
146
+ if draft.contribution.kind is not RegistrationKind.MULTI
147
+ )
148
+ )
149
+ for collection in collections:
150
+ conflicts = single_by_key.get(collection.key, ())
151
+ if conflicts:
152
+ raise RegistrationConflictError(
153
+ key=collection.key,
154
+ sources=(
155
+ collection.contribution.source,
156
+ *(item.contribution.source for item in conflicts),
157
+ ),
158
+ reason="native collect() output key conflicts with a single binding",
159
+ )
160
+
161
+ @classmethod
162
+ def _arbitrate_multi(
163
+ cls,
164
+ factories: tuple[_RegistrationDraft, ...],
165
+ collections: tuple[_RegistrationDraft, ...],
166
+ ) -> None:
167
+ multi_factories = tuple(
168
+ draft for draft in factories if draft.contribution.kind is RegistrationKind.MULTI
169
+ )
170
+ single_by_key = cls._group_by_key(
171
+ tuple(
172
+ draft
173
+ for draft in factories
174
+ if draft.contribution.kind is not RegistrationKind.MULTI
175
+ )
176
+ )
177
+ collectors_by_source: dict[DependencyKey, list[_RegistrationDraft]] = {}
178
+ for collection in collections:
179
+ assert collection.collection_source_key is not None
180
+ collectors_by_source.setdefault(collection.collection_source_key, []).append(collection)
181
+
182
+ for key, items in cls._group_by_key(multi_factories).items():
183
+ if key in single_by_key:
184
+ raise RegistrationConflictError(
185
+ key=key,
186
+ sources=tuple(
187
+ item.contribution.source for item in (*items, *single_by_key[key])
188
+ ),
189
+ reason="a dependency key cannot be both single and multi",
190
+ )
191
+ collectors = collectors_by_source.get(key, [])
192
+ if len(collectors) != 1:
193
+ raise RegistrationConflictError(
194
+ key=key,
195
+ sources=tuple(item.contribution.source for item in (*items, *collectors)),
196
+ reason="multi key must have exactly one native collect() definition",
197
+ )
198
+ seen_sources: dict[object, _RegistrationDraft] = {}
199
+ for ordinal, item in enumerate(items):
200
+ assert item.factory is not None
201
+ source_identity = (
202
+ tuple(item.factory.dependencies)
203
+ if item.factory.type is FactoryType.ALIAS
204
+ else _NativeProviderInspector.source_identity(item.factory.source)
205
+ )
206
+ previous = seen_sources.get(source_identity)
207
+ if previous is not None:
208
+ raise RegistrationConflictError(
209
+ key=key,
210
+ sources=(previous.contribution.source, item.contribution.source),
211
+ reason="duplicate multi contribution uses the same native factory source",
212
+ )
213
+ seen_sources[source_identity] = item
214
+ item.multi_ordinal = ordinal
215
+ item.decision_reason = "accepted as ordered native collect() item"
216
+
217
+ for source_key, collectors in collectors_by_source.items():
218
+ if len(collectors) > 1:
219
+ raise RegistrationConflictError(
220
+ key=source_key,
221
+ sources=tuple(item.contribution.source for item in collectors),
222
+ reason="multi key has more than one native collect() definition",
223
+ )
224
+
225
+ def _arbitrate_single(self, factories: tuple[_RegistrationDraft, ...]) -> None:
226
+ singles = tuple(
227
+ draft for draft in factories if draft.contribution.kind is not RegistrationKind.MULTI
228
+ )
229
+ for key, candidates in self._group_by_key(singles).items():
230
+ defaults = tuple(
231
+ candidate
232
+ for candidate in candidates
233
+ if candidate.contribution.kind
234
+ in (RegistrationKind.DEFAULT, RegistrationKind.CONVENTION)
235
+ )
236
+ explicits = tuple(
237
+ candidate
238
+ for candidate in candidates
239
+ if candidate.contribution.kind is RegistrationKind.EXPLICIT
240
+ )
241
+ overrides = tuple(
242
+ candidate
243
+ for candidate in candidates
244
+ if candidate.contribution.kind is RegistrationKind.OVERRIDE
245
+ )
246
+ base = self._select_single_base(key, defaults, explicits)
247
+ self._apply_override_chain(key, base, overrides)
248
+
249
+ @staticmethod
250
+ def _select_single_base(
251
+ key: DependencyKey,
252
+ defaults: tuple[_RegistrationDraft, ...],
253
+ explicits: tuple[_RegistrationDraft, ...],
254
+ ) -> _RegistrationDraft | None:
255
+ if len(explicits) > 1:
256
+ raise RegistrationConflictError(
257
+ key=key,
258
+ sources=tuple(item.contribution.source for item in explicits),
259
+ reason="multiple explicit single bindings require an explicit override",
260
+ )
261
+ if explicits:
262
+ explicit = explicits[0]
263
+ for default in defaults:
264
+ default.decision = RegistrationDecision.BACKED_OFF
265
+ default.decision_reason = (
266
+ f"{default.contribution.reason}; backed off because explicit contribution from "
267
+ f"{explicit.contribution.owner} exists"
268
+ )
269
+ explicit.decision_reason = (
270
+ f"{explicit.contribution.reason}; accepted as the unique explicit single binding"
271
+ )
272
+ return explicit
273
+ if len(defaults) > 1:
274
+ raise RegistrationConflictError(
275
+ key=key,
276
+ sources=tuple(item.contribution.source for item in defaults),
277
+ reason="multiple default/convention single bindings are ambiguous",
278
+ )
279
+ if defaults:
280
+ defaults[0].decision_reason = (
281
+ f"{defaults[0].contribution.reason}; accepted because no explicit "
282
+ "single binding exists"
283
+ )
284
+ return defaults[0]
285
+ return None
286
+
287
+ def _apply_override_chain(
288
+ self,
289
+ key: DependencyKey,
290
+ base: _RegistrationDraft | None,
291
+ overrides: tuple[_RegistrationDraft, ...],
292
+ ) -> None:
293
+ if not overrides:
294
+ return
295
+ if base is None:
296
+ raise RegistrationConflictError(
297
+ key=key,
298
+ sources=tuple(item.contribution.source for item in overrides),
299
+ reason="override target does not exist",
300
+ )
301
+ current = base
302
+ chain = [base.contribution.source]
303
+ for override in overrides:
304
+ descriptor = self._registry.descriptors[override.contribution.owner]
305
+ if current.contribution.owner not in descriptor.dependencies:
306
+ raise RegistrationConflictError(
307
+ key=key,
308
+ sources=(current.contribution.source, override.contribution.source),
309
+ reason=(
310
+ f"override owner {override.contribution.owner} must directly depend on "
311
+ f"current target owner {current.contribution.owner}"
312
+ ),
313
+ )
314
+ current.decision = RegistrationDecision.OVERRIDDEN
315
+ current.decision_reason = f"overridden by {override.contribution.owner}"
316
+ override.replaced_sources = tuple(chain)
317
+ override.decision_reason = f"accepted explicit override of {current.contribution.owner}"
318
+ chain.append(override.contribution.source)
319
+ current = override
320
+
321
+ @staticmethod
322
+ def _retained_providers(
323
+ entries: tuple[_ContributionEntry, ...],
324
+ drafts: tuple[_RegistrationDraft, ...],
325
+ ) -> tuple[Provider, ...]:
326
+ drafts_by_entry: dict[int, list[_RegistrationDraft]] = {}
327
+ for draft in drafts:
328
+ drafts_by_entry.setdefault(draft.entry.entry_id, []).append(draft)
329
+ retained: list[Provider] = []
330
+ for entry in entries:
331
+ entry_drafts = drafts_by_entry[entry.entry_id]
332
+ active = tuple(
333
+ draft
334
+ for draft in entry_drafts
335
+ if draft.decision is not RegistrationDecision.BACKED_OFF
336
+ )
337
+ if not active:
338
+ continue
339
+ if len(active) == len(entry_drafts):
340
+ retained.append(entry.contribution.provider)
341
+ continue
342
+ # 仲裁粒度是 dependency source,而 Provider 只是 native 输入批次。
343
+ # 局部 back-off 时仅重组原生 source 集合,不复制 factory model。
344
+ retained.append(_provider_with_active_sources(entry.contribution.provider, active))
345
+ return tuple(retained)
346
+
347
+ @staticmethod
348
+ def _group_by_key(
349
+ drafts: tuple[_RegistrationDraft, ...],
350
+ ) -> dict[DependencyKey, tuple[_RegistrationDraft, ...]]:
351
+ grouped: dict[DependencyKey, list[_RegistrationDraft]] = {}
352
+ try:
353
+ for draft in drafts:
354
+ grouped.setdefault(draft.key, []).append(draft)
355
+ except TypeError as original_error:
356
+ error = RegistrationConflictError(
357
+ key=None,
358
+ sources=tuple(draft.contribution.source for draft in drafts),
359
+ reason="native DependencyKey must be hashable",
360
+ )
361
+ raise error from original_error
362
+ return {key: tuple(values) for key, values in grouped.items()}
363
+
364
+
365
+ def _provider_with_active_sources(
366
+ provider: Provider,
367
+ drafts: tuple[_RegistrationDraft, ...],
368
+ ) -> Provider:
369
+ retained = Provider(
370
+ scope=provider.scope,
371
+ component=provider.component,
372
+ when=provider.when,
373
+ )
374
+ retained.factories.extend(
375
+ draft.factory for draft in drafts if draft.factory is not None and draft.alias is None
376
+ )
377
+ retained.aliases.extend(draft.alias for draft in drafts if draft.alias is not None)
378
+ retained.factory_union_mode.extend(
379
+ draft.collection for draft in drafts if draft.collection is not None
380
+ )
381
+ return retained
@@ -0,0 +1,102 @@
1
+ """通用注册声明到 Dishka factory/alias/collect 的原生映射。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ from dishka import BaseScope, FromComponent, Provider
8
+
9
+ from ..application_services.contracts import ApplicationService
10
+ from ..diagnostics import SourceLocation
11
+ from ..errors import RegistrationConflictError
12
+ from .registration import RegistrationKind
13
+
14
+ _COLLECTION_COMPONENT = "python_platform.services.collection"
15
+ _REGISTRATION_COMPONENT = "python_platform.services.registration"
16
+
17
+
18
+ def _registration_providers(
19
+ service: type[object],
20
+ implementation: type[object] | None,
21
+ *,
22
+ scope: BaseScope,
23
+ cache: bool,
24
+ component: str,
25
+ multi: bool,
26
+ default: bool,
27
+ override: bool,
28
+ identity: str,
29
+ source: SourceLocation,
30
+ ) -> tuple[tuple[Provider, RegistrationKind], ...]:
31
+ if not isinstance(service, type) or (
32
+ implementation is not None and not isinstance(implementation, type)
33
+ ):
34
+ raise RegistrationConflictError(
35
+ key=None,
36
+ sources=(source,),
37
+ reason="registration requires service and implementation classes",
38
+ )
39
+ if not isinstance(component, str) or component.startswith(
40
+ (_COLLECTION_COMPONENT, _REGISTRATION_COMPONENT)
41
+ ):
42
+ raise RegistrationConflictError(
43
+ key=None, sources=(source,), reason="component must be a non-reserved string"
44
+ )
45
+ if override and (multi or default):
46
+ raise RegistrationConflictError(
47
+ key=None, sources=(source,), reason="override cannot also declare multi or default"
48
+ )
49
+ kind = RegistrationKind.OVERRIDE if override else RegistrationKind.EXPLICIT
50
+ provider = Provider(scope=scope, component=component)
51
+ concrete = implementation or service
52
+ if issubclass(concrete, ApplicationService):
53
+ raise RegistrationConflictError(
54
+ key=None,
55
+ sources=(source,),
56
+ reason=(
57
+ "ApplicationService registration requires convention discovery "
58
+ "and its contract proxy"
59
+ ),
60
+ )
61
+ if not multi:
62
+ provider.provide(concrete, provides=service, cache=cache, override=override)
63
+ if default and component:
64
+ provider.alias(service, provides=Annotated[service, FromComponent("")], cache=False)
65
+ # default=True 是明确选择;旧 DEFAULT contribution 的 fallback 语义不参与这里。
66
+ return ((provider, kind),)
67
+
68
+ # 每项只有一个创建来源。公开指定项、默认项和集合项都通过原生 alias 引用它;
69
+ # alias 不缓存,避免把 transient 意外提升为 scoped。
70
+ source_component = f"{_REGISTRATION_COMPONENT}.{identity}"
71
+ native_source: object = Annotated[service, FromComponent(source_component)]
72
+ provider.provide(concrete, provides=native_source, cache=cache)
73
+ if component:
74
+ provider.alias(service, component=source_component, provides=service, cache=False)
75
+ if default:
76
+ provider.alias(
77
+ service,
78
+ component=source_component,
79
+ provides=Annotated[service, FromComponent("")],
80
+ cache=False,
81
+ )
82
+ member = Provider(scope=scope)
83
+ member.alias(
84
+ service,
85
+ component=source_component,
86
+ provides=Annotated[service, FromComponent(_COLLECTION_COMPONENT)],
87
+ cache=False,
88
+ )
89
+ # collect 收集 factory;用 Alias 自己的转换保留其依赖、实例身份和失败语义。
90
+ member.factories.extend(alias.as_factory(scope, member.component) for alias in member.aliases)
91
+ member.aliases.clear()
92
+ return ((provider, kind), (member, RegistrationKind.MULTI))
93
+
94
+
95
+ def _collection_provider(service: type[object]) -> Provider:
96
+ provider = Provider()
97
+ provider.collect(
98
+ Annotated[service, FromComponent(_COLLECTION_COMPONENT)],
99
+ provides=list.__class_getitem__(service),
100
+ cache=False,
101
+ )
102
+ return provider