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,292 @@
1
+ """受 Module-owned metadata 控制且完整可诊断的 Convention discovery。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Mapping
7
+ from typing import cast
8
+
9
+ from dishka import Provider, Scope
10
+
11
+ from ..application_services.catalog import _DiscoveredApplicationService
12
+ from ..application_services.contracts import (
13
+ _APPLICATION_SERVICE_DEFINITION_ATTRIBUTE,
14
+ ApplicationService,
15
+ _ApplicationServiceDefinition,
16
+ )
17
+ from ..application_services.contracts import (
18
+ _definition_for as _application_service_definition,
19
+ )
20
+ from ..application_services.errors import ApplicationServiceDefinitionError
21
+ from ..diagnostics import SourceLocation
22
+ from ..errors import ConventionScanError, InvalidConventionDefinitionError
23
+ from ..modularity import ModuleKey, ModuleRegistry
24
+ from ..modularity.discovery import _DiscoveredType
25
+ from .arbitration import _RegistrationDraft
26
+ from .contribution import ServiceContributionRegistry
27
+ from .convention_contracts import (
28
+ _SERVICE_DEFINITION_ATTRIBUTE,
29
+ ConventionalService,
30
+ ConventionCandidate,
31
+ ConventionCandidateDecision,
32
+ ConventionDiscoveryKind,
33
+ ScopedDependency,
34
+ SingletonDependency,
35
+ TransientDependency,
36
+ _ServiceDefinition,
37
+ )
38
+ from .exposure import _business_interfaces
39
+ from .registration import RegistrationDecision, RegistrationKind
40
+
41
+
42
+ class _ConventionScanner:
43
+ """拥有一次 Application scan 的顺序、去重状态和 candidate diagnostics。"""
44
+
45
+ __slots__ = (
46
+ "_application_services",
47
+ "_contributions",
48
+ "_discoveries",
49
+ "_event_handler_scopes",
50
+ "_lifetime_conflicts",
51
+ "_records",
52
+ "_registry",
53
+ "_scan_packages",
54
+ )
55
+
56
+ def __init__(
57
+ self,
58
+ registry: ModuleRegistry,
59
+ contributions: ServiceContributionRegistry,
60
+ scan_packages: Mapping[ModuleKey, tuple[str, ...]],
61
+ discoveries: tuple[_DiscoveredType, ...],
62
+ event_handler_scopes: Mapping[type[object], Scope],
63
+ ) -> None:
64
+ self._discoveries = discoveries
65
+ self._event_handler_scopes = event_handler_scopes
66
+ self._registry = registry
67
+ self._contributions = contributions
68
+ self._scan_packages = scan_packages
69
+ self._records: list[ConventionCandidate] = []
70
+ self._application_services: list[_DiscoveredApplicationService] = []
71
+ self._lifetime_conflicts: set[type[object]] = set()
72
+
73
+ def validate_lifetimes(self, drafts: tuple[_RegistrationDraft, ...]) -> None:
74
+ for draft in drafts:
75
+ if (
76
+ draft.factory is not None
77
+ and isinstance(draft.factory.source, type)
78
+ and draft.factory.source in self._lifetime_conflicts
79
+ and draft.contribution.kind is RegistrationKind.CONVENTION
80
+ and draft.decision is RegistrationDecision.ACCEPTED
81
+ ):
82
+ candidate = draft.factory.source
83
+ raise InvalidConventionDefinitionError(
84
+ symbol=f"{candidate.__module__}.{candidate.__qualname__}",
85
+ reason="conflicting lifetime markers require explicit service metadata",
86
+ )
87
+
88
+ def discover(self) -> tuple[ConventionCandidate, ...]:
89
+ for descriptor in self._registry.dependency_order:
90
+ if descriptor.module_type.scan_packages != self._scan_packages[descriptor.key]:
91
+ raise ConventionScanError(
92
+ owner=descriptor.key,
93
+ package=None,
94
+ source=descriptor.declaration_source,
95
+ reason="scan_packages metadata was mutated after the Application snapshot",
96
+ original_error=None,
97
+ )
98
+ seen: dict[ModuleKey, set[type[object]]] = {}
99
+ for discovery in self._discoveries:
100
+ self._record_candidate(discovery, seen.setdefault(discovery.owner, set()))
101
+ return tuple(self._records)
102
+
103
+ @property
104
+ def application_services(self) -> tuple[_DiscoveredApplicationService, ...]:
105
+ return tuple(self._application_services)
106
+
107
+ def _record_candidate(self, discovery: _DiscoveredType, seen_types: set[type[object]]) -> None:
108
+ candidate = discovery.candidate
109
+ source = discovery.source
110
+ owner = discovery.owner
111
+ package_name = discovery.package
112
+ definition, application_definition, discovered_by, reason = self._definition(candidate)
113
+ if candidate in seen_types:
114
+ self._append_record(
115
+ owner,
116
+ package_name,
117
+ source,
118
+ discovered_by,
119
+ ConventionCandidateDecision.DUPLICATE,
120
+ "candidate already interpreted from an earlier scan package",
121
+ )
122
+ return
123
+ seen_types.add(candidate)
124
+ if definition is None:
125
+ self._append_record(
126
+ owner,
127
+ package_name,
128
+ source,
129
+ discovered_by,
130
+ ConventionCandidateDecision.REJECTED,
131
+ reason,
132
+ )
133
+ return
134
+ provider = Provider(scope=definition.scope)
135
+ provider.provide(
136
+ candidate,
137
+ provides=definition.provides if definition.provides is not None else candidate,
138
+ scope=definition.scope,
139
+ cache=definition.cache,
140
+ )
141
+ if definition.provides is None:
142
+ for interface in _business_interfaces(candidate):
143
+ provider.alias(candidate, provides=interface, cache=False)
144
+ self._contributions.add_convention(
145
+ provider=provider,
146
+ owner=owner,
147
+ source=source,
148
+ reason=reason,
149
+ )
150
+ if application_definition is not None:
151
+ # 分层模块的公开契约由实际扫描它的 Module 拥有;实现 DI 的 owner 仍由上面的
152
+ # contribution 保留。HTTP 因此可只依赖 Contracts,不反向引用应用实现。
153
+ declarations = {
154
+ item.owner: item
155
+ for item in self._discoveries
156
+ if item.candidate is application_definition.contract
157
+ }
158
+ if len(declarations) > 1:
159
+ raise ApplicationServiceDefinitionError(
160
+ symbol=str(application_definition.contract),
161
+ reason="application service contract is scanned by multiple Modules",
162
+ )
163
+ declaration = next(iter(declarations.values()), discovery)
164
+ self._application_services.append(
165
+ _DiscoveredApplicationService(
166
+ service_type=cast(type[ApplicationService], candidate),
167
+ definition=application_definition,
168
+ owner=declaration.owner,
169
+ source=declaration.source,
170
+ declaration_index=len(self._application_services),
171
+ )
172
+ )
173
+ self._append_record(
174
+ owner,
175
+ package_name,
176
+ source,
177
+ discovered_by,
178
+ ConventionCandidateDecision.ACCEPTED,
179
+ reason,
180
+ )
181
+
182
+ def _append_record(
183
+ self,
184
+ owner: ModuleKey,
185
+ package: str,
186
+ source: SourceLocation,
187
+ discovered_by: ConventionDiscoveryKind,
188
+ decision: ConventionCandidateDecision,
189
+ reason: str,
190
+ ) -> None:
191
+ self._records.append(
192
+ ConventionCandidate(
193
+ owner=owner,
194
+ package=package,
195
+ source=source,
196
+ discovered_by=discovered_by,
197
+ decision=decision,
198
+ reason=reason,
199
+ ordinal=len(self._records),
200
+ )
201
+ )
202
+
203
+ def _definition(
204
+ self,
205
+ candidate: type[object],
206
+ ) -> tuple[
207
+ _ServiceDefinition | None,
208
+ _ApplicationServiceDefinition | None,
209
+ ConventionDiscoveryKind,
210
+ str,
211
+ ]:
212
+ application_definition = _application_service_definition(candidate)
213
+ if application_definition is not None:
214
+ if _SERVICE_DEFINITION_ATTRIBUTE in candidate.__dict__:
215
+ raise ApplicationServiceDefinitionError(
216
+ symbol=f"{candidate.__module__}.{candidate.__qualname__}",
217
+ reason="application_service and service metadata cannot be combined",
218
+ )
219
+ decorated = _APPLICATION_SERVICE_DEFINITION_ATTRIBUTE in candidate.__dict__
220
+ discovered_by = (
221
+ ConventionDiscoveryKind.APPLICATION_SERVICE_DECORATOR
222
+ if decorated
223
+ else ConventionDiscoveryKind.FRAMEWORK_MARKER
224
+ )
225
+ reason = (
226
+ "accepted from immutable application_service decorator metadata"
227
+ if decorated
228
+ else "accepted from unique inherited ApplicationService contract"
229
+ )
230
+ return (
231
+ _ServiceDefinition(
232
+ provides=application_definition.contract or candidate,
233
+ scope=Scope.ACTION,
234
+ ),
235
+ application_definition,
236
+ discovered_by,
237
+ reason,
238
+ )
239
+ metadata = candidate.__dict__.get(_SERVICE_DEFINITION_ATTRIBUTE)
240
+ if metadata is not None:
241
+ if not isinstance(metadata, _ServiceDefinition):
242
+ raise InvalidConventionDefinitionError(
243
+ symbol=f"{candidate.__module__}.{candidate.__qualname__}",
244
+ reason="service decorator metadata has an invalid runtime shape",
245
+ )
246
+ return (
247
+ metadata,
248
+ None,
249
+ ConventionDiscoveryKind.DECORATOR_METADATA,
250
+ "accepted from immutable service decorator metadata",
251
+ )
252
+ if candidate in self._event_handler_scopes:
253
+ return (
254
+ _ServiceDefinition(provides=candidate, scope=self._event_handler_scopes[candidate]),
255
+ None,
256
+ ConventionDiscoveryKind.DECORATOR_METADATA,
257
+ "accepted from immutable local_event_handler metadata",
258
+ )
259
+ lifetimes = tuple(
260
+ (marker, scope, cache)
261
+ for marker, scope, cache in (
262
+ (SingletonDependency, Scope.APP, True),
263
+ (ScopedDependency, Scope.REQUEST, True),
264
+ (TransientDependency, Scope.REQUEST, False),
265
+ )
266
+ if candidate is not marker and issubclass(candidate, marker)
267
+ )
268
+ if lifetimes and not inspect.isabstract(candidate):
269
+ if len(lifetimes) != 1:
270
+ # 冲突先挂起到统一仲裁后;若约定 factory 胜出,容器构建前必定拒绝。
271
+ # 显式覆盖链可以让它退让,扫描阶段不自行挑选有效注册。
272
+ self._lifetime_conflicts.add(candidate)
273
+ marker, scope, cache = lifetimes[0]
274
+ return (
275
+ _ServiceDefinition(provides=None, scope=scope, cache=cache),
276
+ None,
277
+ ConventionDiscoveryKind.FRAMEWORK_MARKER,
278
+ f"accepted from {marker.__name__} marker",
279
+ )
280
+ if issubclass(candidate, ConventionalService) and candidate is not ConventionalService:
281
+ return (
282
+ _ServiceDefinition(provides=candidate, scope=Scope.APP),
283
+ None,
284
+ ConventionDiscoveryKind.FRAMEWORK_MARKER,
285
+ "accepted from ConventionalService marker",
286
+ )
287
+ return (
288
+ None,
289
+ None,
290
+ ConventionDiscoveryKind.NONE,
291
+ "rejected because no service decorator metadata or framework marker exists",
292
+ )
@@ -0,0 +1,94 @@
1
+ """Convention metadata、candidate diagnostics 与显式 marker contract。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+ from typing import TypeVar
9
+
10
+ from dishka import BaseScope, Scope
11
+
12
+ from ..diagnostics import SourceLocation
13
+ from ..errors import InvalidConventionDefinitionError
14
+ from ..modularity import ModuleKey
15
+ from .registration import SUPPORTED_SCOPE_LABEL, SUPPORTED_SCOPES
16
+
17
+ _SERVICE_DEFINITION_ATTRIBUTE = "__python_platform_service_definition__"
18
+ _TService = TypeVar("_TService")
19
+
20
+
21
+ class ConventionDiscoveryKind(str, Enum):
22
+ DECORATOR_METADATA = "decorator_metadata"
23
+ FRAMEWORK_MARKER = "framework_marker"
24
+ APPLICATION_SERVICE_DECORATOR = "application_service_decorator"
25
+ NONE = "none"
26
+
27
+
28
+ class ConventionCandidateDecision(str, Enum):
29
+ ACCEPTED = "accepted"
30
+ REJECTED = "rejected"
31
+ DUPLICATE = "duplicate"
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class ConventionCandidate:
36
+ owner: ModuleKey
37
+ package: str
38
+ source: SourceLocation
39
+ discovered_by: ConventionDiscoveryKind
40
+ decision: ConventionCandidateDecision
41
+ reason: str
42
+ ordinal: int
43
+
44
+
45
+ @dataclass(frozen=True, slots=True)
46
+ class _ServiceDefinition:
47
+ provides: object | None
48
+ scope: BaseScope
49
+ cache: bool = True
50
+
51
+
52
+ class ConventionalService:
53
+ """只表达“按自身类型注册”的 Convention marker,不承载容器行为。"""
54
+
55
+
56
+ class SingletonDependency:
57
+ """约定 APP 缓存生命周期;实例所有权仍属于 Application 的 Dishka 容器。"""
58
+
59
+
60
+ class ScopedDependency:
61
+ """约定 REQUEST 缓存生命周期,不允许越过原生作用域访问。"""
62
+
63
+
64
+ class TransientDependency:
65
+ """约定 REQUEST 非缓存生命周期;不缓存不等于解除作用域约束。"""
66
+
67
+
68
+ def service(
69
+ *,
70
+ provides: object | None = None,
71
+ scope: BaseScope = Scope.APP,
72
+ ) -> Callable[[type[_TService]], type[_TService]]:
73
+ """附着 immutable definition metadata;真正解释结果仍归每个 Application。"""
74
+ if scope not in SUPPORTED_SCOPES:
75
+ raise InvalidConventionDefinitionError(
76
+ symbol="<decorator>", reason=f"convention scope must be {SUPPORTED_SCOPE_LABEL}"
77
+ )
78
+ definition = _ServiceDefinition(provides=provides, scope=scope)
79
+
80
+ def decorate(service_type: type[_TService]) -> type[_TService]:
81
+ if not isinstance(service_type, type):
82
+ raise InvalidConventionDefinitionError(
83
+ symbol=type(service_type).__qualname__,
84
+ reason="service decorator can only target a class",
85
+ )
86
+ if _SERVICE_DEFINITION_ATTRIBUTE in service_type.__dict__:
87
+ raise InvalidConventionDefinitionError(
88
+ symbol=f"{service_type.__module__}.{service_type.__qualname__}",
89
+ reason="service decorator metadata is already declared",
90
+ )
91
+ setattr(service_type, _SERVICE_DEFINITION_ATTRIBUTE, definition)
92
+ return service_type
93
+
94
+ return decorate
@@ -0,0 +1,23 @@
1
+ """从显式继承关系解释业务接口,不按名称或结构相似度猜测注册关系。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ import sys
7
+ from abc import ABC
8
+ from typing import Generic, Protocol
9
+
10
+
11
+ def _business_interfaces(candidate: type[object]) -> tuple[type[object], ...]:
12
+ # 只接受名义继承的 Protocol/ABC;实现类和标准库角色不会成为业务契约。
13
+ interfaces = tuple(
14
+ base
15
+ for base in candidate.__mro__[1:]
16
+ if base not in (ABC, Protocol, Generic)
17
+ and base.__module__.partition(".")[0] not in sys.stdlib_module_names
18
+ and (getattr(base, "_is_protocol", False) or inspect.isabstract(base))
19
+ )
20
+ # 派生契约已经表达父契约的要求;自动契约选择只看最具体的候选。
21
+ return tuple(
22
+ base for base in interfaces if not any(base in other.__mro__[1:] for other in interfaces)
23
+ )
@@ -0,0 +1,178 @@
1
+ """组装平台自有依赖与 scoped proxy;业务贡献仍由原生 Dishka 管理。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dishka import (
6
+ Provider,
7
+ Scope,
8
+ )
9
+
10
+ from ..application_services import ApplicationServiceCatalog
11
+ from ..application_services.dispatcher import _ApplicationServiceDispatcher, _proxy_factory
12
+ from ..application_services.invocation import (
13
+ _InvocationRuntime,
14
+ _ScopedApplicationServiceInvoker,
15
+ )
16
+ from ..auditing.control import AuditingControl
17
+ from ..authorization import CurrentUser, PermissionCatalog
18
+ from ..background_jobs import BackgroundJobCatalog
19
+ from ..background_workers import BackgroundWorkerCatalog
20
+ from ..background_workers.runtime import _BackgroundCoordinator
21
+ from ..caching import CacheCatalog
22
+ from ..configuration import ConfigurationRoot
23
+ from ..events import AggregateEventCollector, LocalEventBus, LocalEventCatalog
24
+ from ..hosted_services import HostedServiceCatalog
25
+ from ..hosted_services.runtime import _HostedServiceCoordinator
26
+ from ..invocation import ApplicationInvocationContext, BackgroundSystemCaller
27
+ from ..invocation.interception import _EntryInterceptionCatalog
28
+ from ..modularity import ModuleRegistry
29
+ from ..notifications import NotificationCatalog
30
+ from ..options import Options, OptionsSnapshot
31
+ from ..settings import SettingCatalog
32
+ from ..unit_of_work import UnitOfWork, UnitOfWorkManager, UnitOfWorkOptions
33
+ from ..unit_of_work.contracts import _UnitOfWorkFactory
34
+
35
+
36
+ def _framework_provider(
37
+ *,
38
+ registry: ModuleRegistry,
39
+ has_unit_of_work_factory: bool,
40
+ entry_interceptors: _EntryInterceptionCatalog,
41
+ configuration: ConfigurationRoot,
42
+ options: OptionsSnapshot,
43
+ invocation_runtime: _InvocationRuntime,
44
+ application_service_catalog: ApplicationServiceCatalog,
45
+ event_catalog: LocalEventCatalog,
46
+ permission_catalog: PermissionCatalog,
47
+ cache_catalog: CacheCatalog,
48
+ setting_catalog: SettingCatalog,
49
+ notification_catalog: NotificationCatalog,
50
+ background_worker_catalog: BackgroundWorkerCatalog,
51
+ background_job_catalog: BackgroundJobCatalog,
52
+ background_coordinator: _BackgroundCoordinator,
53
+ hosted_service_catalog: HostedServiceCatalog,
54
+ hosted_service_coordinator: _HostedServiceCoordinator,
55
+ ) -> Provider:
56
+ """框架自身 contract 直接进入 Dishka;不写入业务 ServiceCatalog 第二清单。"""
57
+ provider = Provider()
58
+ provider.provide(
59
+ lambda: entry_interceptors, provides=_EntryInterceptionCatalog, scope=Scope.APP
60
+ )
61
+ provider.provide(AuditingControl, scope=Scope.APP)
62
+ _provide_unit_of_work(provider, has_unit_of_work_factory)
63
+ provider.provide(lambda: registry, provides=ModuleRegistry, scope=Scope.APP)
64
+ provider.provide(lambda: configuration, provides=ConfigurationRoot, scope=Scope.APP)
65
+ provider.provide(lambda: options, provides=OptionsSnapshot, scope=Scope.APP)
66
+ # Dishka 原生 context binding 直接发布已冻结实例,保持每个 Application 的唯一 snapshot。
67
+ for model_type in options.values:
68
+ provider.from_context(provides=Options[model_type], scope=Scope.APP) # type: ignore[valid-type]
69
+ provider.provide(
70
+ lambda: invocation_runtime,
71
+ provides=_InvocationRuntime,
72
+ scope=Scope.APP,
73
+ )
74
+ provider.provide(
75
+ lambda: application_service_catalog,
76
+ provides=ApplicationServiceCatalog,
77
+ scope=Scope.APP,
78
+ )
79
+ provider.provide(lambda: event_catalog, provides=LocalEventCatalog, scope=Scope.APP)
80
+ provider.provide(lambda: permission_catalog, provides=PermissionCatalog, scope=Scope.APP)
81
+ provider.provide(lambda: cache_catalog, provides=CacheCatalog, scope=Scope.APP)
82
+ provider.provide(lambda: setting_catalog, provides=SettingCatalog, scope=Scope.APP)
83
+ provider.provide(lambda: notification_catalog, provides=NotificationCatalog, scope=Scope.APP)
84
+ provider.provide(
85
+ lambda: background_worker_catalog,
86
+ provides=BackgroundWorkerCatalog,
87
+ scope=Scope.APP,
88
+ )
89
+ provider.provide(
90
+ lambda: background_job_catalog,
91
+ provides=BackgroundJobCatalog,
92
+ scope=Scope.APP,
93
+ )
94
+ provider.provide(
95
+ lambda: background_coordinator,
96
+ provides=_BackgroundCoordinator,
97
+ scope=Scope.APP,
98
+ )
99
+ provider.provide(
100
+ lambda: hosted_service_catalog,
101
+ provides=HostedServiceCatalog,
102
+ scope=Scope.APP,
103
+ )
104
+ provider.provide(
105
+ lambda: hosted_service_coordinator,
106
+ provides=_HostedServiceCoordinator,
107
+ scope=Scope.APP,
108
+ )
109
+ provider.provide(
110
+ lambda: background_coordinator.system_caller,
111
+ provides=BackgroundSystemCaller,
112
+ scope=Scope.APP,
113
+ )
114
+ provider.provide(
115
+ _current_user,
116
+ provides=CurrentUser,
117
+ scope=Scope.REQUEST,
118
+ )
119
+ provider.from_context(provides=ApplicationInvocationContext, scope=Scope.ACTION)
120
+ provider.provide(
121
+ _ApplicationServiceDispatcher,
122
+ provides=_ApplicationServiceDispatcher,
123
+ scope=Scope.REQUEST,
124
+ )
125
+ provider.provide(
126
+ _ScopedApplicationServiceInvoker,
127
+ provides=_ScopedApplicationServiceInvoker,
128
+ scope=Scope.APP,
129
+ )
130
+ for registration in application_service_catalog.registrations:
131
+ if not registration.is_public_service:
132
+ continue
133
+ provider.provide(
134
+ _proxy_factory(application_service_catalog._for_registration(registration)),
135
+ provides=registration.contract,
136
+ scope=Scope.REQUEST,
137
+ )
138
+ provider.provide(LocalEventBus, provides=LocalEventBus, scope=Scope.REQUEST)
139
+ provider.provide(
140
+ AggregateEventCollector,
141
+ provides=AggregateEventCollector,
142
+ scope=Scope.ACTION,
143
+ )
144
+ for worker_type in {item.worker_type for item in background_worker_catalog.definitions}:
145
+ if isinstance(worker_type, type):
146
+ provider.provide(worker_type, provides=worker_type, scope=Scope.ACTION)
147
+ for handler_type in {item.handler_type for item in background_job_catalog.definitions}:
148
+ if isinstance(handler_type, type):
149
+ provider.provide(handler_type, provides=handler_type, scope=Scope.ACTION)
150
+ return provider
151
+
152
+
153
+ def _current_user(invocation_runtime: _InvocationRuntime) -> CurrentUser:
154
+ # CurrentUser 只来自当前 Application 的 task-local request context。
155
+ return invocation_runtime.current_user()
156
+
157
+
158
+ def _unit_of_work_manager(
159
+ runtime: _InvocationRuntime,
160
+ factory: _UnitOfWorkFactory,
161
+ options: Options[UnitOfWorkOptions],
162
+ catalog: LocalEventCatalog,
163
+ ) -> UnitOfWorkManager:
164
+ return UnitOfWorkManager(runtime, factory, options.value, catalog)
165
+
166
+
167
+ def _current_unit_of_work(manager: UnitOfWorkManager) -> UnitOfWork:
168
+ return manager.require_current()
169
+
170
+
171
+ def _provide_unit_of_work(
172
+ provider: Provider,
173
+ has_unit_of_work_factory: bool,
174
+ ) -> None:
175
+ if not has_unit_of_work_factory:
176
+ provider.provide(_UnitOfWorkFactory, scope=Scope.APP)
177
+ provider.provide(_unit_of_work_manager, provides=UnitOfWorkManager, scope=Scope.APP)
178
+ provider.provide(_current_unit_of_work, provides=UnitOfWork, scope=Scope.ACTION, cache=False)