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,298 @@
1
+ """Dishka container 构建、native graph 核对与 catalog 发布。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass
7
+ from typing import get_origin
8
+
9
+ from dishka import (
10
+ DEFAULT_COMPONENT,
11
+ STRICT_VALIDATION,
12
+ AsyncContainer,
13
+ DependencyKey,
14
+ make_async_container,
15
+ )
16
+
17
+ from ..application_services import ApplicationServiceCatalog
18
+ from ..application_services.catalog import (
19
+ _build_application_service_catalog,
20
+ )
21
+ from ..application_services.invocation import (
22
+ _InvocationRuntime,
23
+ )
24
+ from ..authorization import PermissionCatalog
25
+ from ..background_jobs import BackgroundJobCatalog
26
+ from ..background_workers import BackgroundWorkerCatalog
27
+ from ..background_workers.runtime import _BackgroundCoordinator
28
+ from ..caching import CacheCatalog
29
+ from ..configuration import ConfigurationRoot
30
+ from ..errors import CatalogNativeGraphMismatchError, ContainerValidationError
31
+ from ..events import LocalEventCatalog
32
+ from ..events.catalog import _build_event_catalog, _validate_event_scopes
33
+ from ..events.contribution import EventContributionRegistry
34
+ from ..events.discovery import _discover_event_handlers
35
+ from ..hosted_services import HostedServiceCatalog
36
+ from ..hosted_services.catalog import (
37
+ _validate_hosted_service_drafts,
38
+ _validate_hosted_service_entries,
39
+ )
40
+ from ..hosted_services.runtime import _HostedServiceCoordinator
41
+ from ..invocation.entries import _validate_callable_entries
42
+ from ..invocation.interception import _EntryInterceptionCatalog
43
+ from ..modularity import ModuleKey, ModuleRegistry
44
+ from ..modularity.discovery import _DiscoveredType
45
+ from ..notifications import NotificationCatalog
46
+ from ..options import Options, OptionsSnapshot
47
+ from ..settings import SettingCatalog
48
+ from ..settings.binding import _provide_settings
49
+ from ..settings.store import RuntimeSettingStore
50
+ from ..unit_of_work.contracts import _UnitOfWorkFactory
51
+ from .application_bindings import (
52
+ _bind_application_services,
53
+ _runtime_providers,
54
+ _validate_application_service_dependencies,
55
+ )
56
+ from .arbitration import (
57
+ _RegistrationDraft,
58
+ _ServiceArbitrator,
59
+ )
60
+ from .contribution import ServiceContributionRegistry
61
+ from .convention import _ConventionScanner
62
+ from .convention_contracts import ConventionCandidate
63
+ from .framework_provider import (
64
+ _framework_provider,
65
+ )
66
+ from .native_graph import (
67
+ _accepted_single_service_scopes,
68
+ _NativeServiceGraph,
69
+ _validate_interceptors,
70
+ )
71
+ from .registration import (
72
+ RegistrationDecision,
73
+ RegistrationKind,
74
+ ServiceCatalog,
75
+ ServiceRegistration,
76
+ )
77
+
78
+
79
+ @dataclass(frozen=True, slots=True)
80
+ class _ServiceRuntime:
81
+ catalog: ServiceCatalog
82
+ application_service_catalog: ApplicationServiceCatalog
83
+ event_catalog: LocalEventCatalog
84
+ container: AsyncContainer
85
+
86
+
87
+ class _ServiceRuntimeBuilder:
88
+ """完成一次 Application-local scan、仲裁、容器构建和诊断快照发布。"""
89
+
90
+ __slots__ = (
91
+ "_background_coordinator",
92
+ "_background_job_catalog",
93
+ "_background_worker_catalog",
94
+ "_cache_catalog",
95
+ "_configuration",
96
+ "_contributions",
97
+ "_discoveries",
98
+ "_event_contributions",
99
+ "_hosted_service_catalog",
100
+ "_hosted_service_coordinator",
101
+ "_invocation_runtime",
102
+ "_notification_catalog",
103
+ "_options",
104
+ "_permission_catalog",
105
+ "_registry",
106
+ "_scan_packages",
107
+ "_setting_catalog",
108
+ )
109
+
110
+ def __init__(
111
+ self,
112
+ registry: ModuleRegistry,
113
+ contributions: ServiceContributionRegistry,
114
+ event_contributions: EventContributionRegistry,
115
+ scan_packages: Mapping[ModuleKey, tuple[str, ...]],
116
+ discoveries: tuple[_DiscoveredType, ...],
117
+ configuration: ConfigurationRoot,
118
+ options: OptionsSnapshot,
119
+ invocation_runtime: _InvocationRuntime,
120
+ permission_catalog: PermissionCatalog,
121
+ cache_catalog: CacheCatalog,
122
+ setting_catalog: SettingCatalog,
123
+ notification_catalog: NotificationCatalog,
124
+ background_worker_catalog: BackgroundWorkerCatalog,
125
+ background_job_catalog: BackgroundJobCatalog,
126
+ background_coordinator: _BackgroundCoordinator,
127
+ hosted_service_catalog: HostedServiceCatalog,
128
+ hosted_service_coordinator: _HostedServiceCoordinator,
129
+ ) -> None:
130
+ self._registry = registry
131
+ self._contributions = contributions
132
+ self._event_contributions = event_contributions
133
+ self._scan_packages = scan_packages
134
+ self._discoveries = discoveries
135
+ self._configuration = configuration
136
+ self._options = options
137
+ self._invocation_runtime = invocation_runtime
138
+ self._permission_catalog = permission_catalog
139
+ self._cache_catalog = cache_catalog
140
+ self._setting_catalog = setting_catalog
141
+ self._notification_catalog = notification_catalog
142
+ self._background_worker_catalog = background_worker_catalog
143
+ self._background_job_catalog = background_job_catalog
144
+ self._background_coordinator = background_coordinator
145
+ self._hosted_service_catalog = hosted_service_catalog
146
+ self._hosted_service_coordinator = hosted_service_coordinator
147
+
148
+ def build(self) -> _ServiceRuntime:
149
+ event_handler_scopes = _discover_event_handlers(
150
+ self._registry,
151
+ self._discoveries,
152
+ self._event_contributions,
153
+ )
154
+ scanner = _ConventionScanner(
155
+ self._registry,
156
+ self._contributions,
157
+ self._scan_packages,
158
+ self._discoveries,
159
+ event_handler_scopes,
160
+ )
161
+ convention_candidates = scanner.discover()
162
+ entries = self._contributions.freeze()
163
+ _validate_hosted_service_entries(entries, self._hosted_service_catalog)
164
+ arbitration = _ServiceArbitrator(self._registry).arbitrate(entries)
165
+ scanner.validate_lifetimes(arbitration.drafts)
166
+ _validate_hosted_service_drafts(arbitration.drafts, self._hosted_service_catalog)
167
+ bound_application_services = _bind_application_services(
168
+ scanner.application_services,
169
+ arbitration.drafts,
170
+ )
171
+ _validate_application_service_dependencies(
172
+ self._registry, bound_application_services, arbitration.drafts
173
+ )
174
+ application_service_catalog = _build_application_service_catalog(
175
+ tuple(item.binding for item in bound_application_services),
176
+ self._permission_catalog,
177
+ self._contributions.interceptor_rules,
178
+ )
179
+ event_catalog = _build_event_catalog(
180
+ self._registry,
181
+ self._event_contributions,
182
+ )
183
+ entry_interceptors = _EntryInterceptionCatalog(
184
+ self._registry, event_catalog, self._contributions.interceptor_rules
185
+ )
186
+ framework_provider = _framework_provider(
187
+ entry_interceptors=entry_interceptors,
188
+ has_unit_of_work_factory=any(
189
+ draft.key.type_hint is _UnitOfWorkFactory for draft in arbitration.drafts
190
+ ),
191
+ registry=self._registry,
192
+ options=self._options,
193
+ invocation_runtime=self._invocation_runtime,
194
+ application_service_catalog=application_service_catalog,
195
+ event_catalog=event_catalog,
196
+ permission_catalog=self._permission_catalog,
197
+ cache_catalog=self._cache_catalog,
198
+ setting_catalog=self._setting_catalog,
199
+ notification_catalog=self._notification_catalog,
200
+ background_worker_catalog=self._background_worker_catalog,
201
+ background_job_catalog=self._background_job_catalog,
202
+ background_coordinator=self._background_coordinator,
203
+ hosted_service_catalog=self._hosted_service_catalog,
204
+ hosted_service_coordinator=self._hosted_service_coordinator,
205
+ configuration=self._configuration,
206
+ )
207
+ _provide_settings(
208
+ framework_provider,
209
+ self._setting_catalog,
210
+ self._configuration,
211
+ # Alias 的 scope 由原生图解析,不能以 draft.scope 是否已知遗漏有效 binding。
212
+ has_store=any(
213
+ draft.key == DependencyKey(RuntimeSettingStore, DEFAULT_COMPONENT)
214
+ and draft.decision is RegistrationDecision.ACCEPTED
215
+ and draft.contribution.kind is not RegistrationKind.MULTI
216
+ and not draft.is_collection
217
+ for draft in arbitration.drafts
218
+ ),
219
+ )
220
+ runtime_providers = _runtime_providers(arbitration, bound_application_services)
221
+ try:
222
+ # Options 的唯一 owner 是阶段绑定快照;手工 provider 也不能为未声明类型创建旁路。
223
+ if any(
224
+ draft.key.type_hint is Options or get_origin(draft.key.type_hint) is Options
225
+ for draft in arbitration.drafts
226
+ ):
227
+ raise ValueError(
228
+ "Options bindings must come from Module configuration declarations"
229
+ )
230
+ container = make_async_container(
231
+ *runtime_providers,
232
+ framework_provider,
233
+ context={
234
+ Options[model_type]: Options(value) # type: ignore[valid-type]
235
+ for model_type, value in self._options.values.items()
236
+ },
237
+ validation_settings=STRICT_VALIDATION,
238
+ )
239
+ except Exception as original_error:
240
+ error = ContainerValidationError(
241
+ sources=tuple(entry.contribution.source for entry in arbitration.ordered_entries),
242
+ original_error=original_error,
243
+ )
244
+ raise error from original_error
245
+
246
+ native_graph = _NativeServiceGraph(container)
247
+ _validate_event_scopes(
248
+ event_catalog,
249
+ _accepted_single_service_scopes(arbitration.drafts, native_graph),
250
+ )
251
+ _validate_interceptors(native_graph, application_service_catalog, entry_interceptors)
252
+ _validate_callable_entries(
253
+ container,
254
+ self._background_job_catalog,
255
+ self._background_worker_catalog,
256
+ event_catalog,
257
+ event_implementations={
258
+ item.handler_type: native_graph.implementation_for(item.handler_type)
259
+ for item in event_catalog.subscriptions
260
+ if isinstance(item.handler_type, type)
261
+ },
262
+ )
263
+ differences = native_graph.differences(arbitration.drafts)
264
+ if differences:
265
+ raise CatalogNativeGraphMismatchError(differences=differences)
266
+ return _ServiceRuntime(
267
+ catalog=self._freeze_catalog(
268
+ arbitration.drafts,
269
+ native_graph,
270
+ convention_candidates,
271
+ ),
272
+ application_service_catalog=application_service_catalog,
273
+ event_catalog=event_catalog,
274
+ container=container,
275
+ )
276
+
277
+ @staticmethod
278
+ def _freeze_catalog(
279
+ drafts: tuple[_RegistrationDraft, ...],
280
+ native_graph: _NativeServiceGraph,
281
+ convention_candidates: tuple[ConventionCandidate, ...],
282
+ ) -> ServiceCatalog:
283
+ registrations = tuple(
284
+ ServiceRegistration(
285
+ key=draft.key,
286
+ scope=native_graph.scope_for(draft),
287
+ owner=draft.contribution.owner,
288
+ source=draft.contribution.source,
289
+ kind=draft.contribution.kind,
290
+ declaration_index=draft.contribution.declaration_index,
291
+ decision=draft.decision,
292
+ decision_reason=draft.decision_reason,
293
+ replaced_sources=draft.replaced_sources,
294
+ multi_ordinal=draft.multi_ordinal,
295
+ )
296
+ for draft in sorted(drafts, key=lambda item: item.sequence)
297
+ )
298
+ return ServiceCatalog(registrations, convention_candidates)
@@ -0,0 +1,30 @@
1
+ """Global Runtime Settings 的 typed contract 与 Application-local catalog。"""
2
+
3
+ from .catalog import SettingCatalog
4
+ from .contracts import SettingDefinition
5
+ from .definitions import SettingDefinitionContext, SettingDefinitionProvider
6
+ from .errors import RuntimeSettingError, SettingDefinitionError
7
+ from .handlers import SettingApplicationStatus, SettingHandler, SettingHandlerDefinition
8
+ from .manager import SettingManager
9
+ from .provider import SettingProvider
10
+ from .store import RuntimeSettingStore
11
+ from .values import SettingSnapshot, SettingValueSource, SettingWriteOutcome, StoredSetting
12
+
13
+ __all__ = (
14
+ "RuntimeSettingError",
15
+ "RuntimeSettingStore",
16
+ "SettingApplicationStatus",
17
+ "SettingCatalog",
18
+ "SettingDefinition",
19
+ "SettingDefinitionContext",
20
+ "SettingDefinitionError",
21
+ "SettingDefinitionProvider",
22
+ "SettingHandler",
23
+ "SettingHandlerDefinition",
24
+ "SettingManager",
25
+ "SettingProvider",
26
+ "SettingSnapshot",
27
+ "SettingValueSource",
28
+ "SettingWriteOutcome",
29
+ "StoredSetting",
30
+ )
@@ -0,0 +1,95 @@
1
+ """Settings 管理只编排正式 catalog 与 SettingManager,不拥有 provider 或值缓存。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any, cast
7
+
8
+ from pydantic import JsonValue
9
+
10
+ from ..application_services import ApplicationService, application_service
11
+ from ..authorization import authorize
12
+ from ..configuration.immutability import _serialization_value
13
+ from ..errors import BusinessError, BusinessErrorDefinition, BusinessErrorField
14
+ from ..errors.base import ResourceNotFoundError
15
+ from .catalog import SettingCatalog
16
+ from .contracts import SettingDefinition
17
+ from .management import (
18
+ SETTINGS_READ,
19
+ SETTINGS_WRITE,
20
+ ResetSettingCommand,
21
+ SettingsManagementApplicationService,
22
+ SettingView,
23
+ SettingWriteResult,
24
+ UpdateSettingCommand,
25
+ )
26
+ from .manager import SettingManager
27
+ from .values import SettingSnapshot, SettingValueSource
28
+
29
+ _INVALID_VALUE = BusinessErrorDefinition("settings.invalid_value", "Invalid setting value")
30
+ _VALUE_FIELD = BusinessErrorField(UpdateSettingCommand, "value")
31
+
32
+
33
+ @application_service(provides=SettingsManagementApplicationService)
34
+ class _SettingsManagementApplicationService(ApplicationService):
35
+ def __init__(self, catalog: SettingCatalog, settings: SettingManager) -> None:
36
+ self._catalog = catalog
37
+ self._settings = settings
38
+
39
+ @authorize(SETTINGS_READ)
40
+ async def get_list(self) -> tuple[SettingView, ...]:
41
+ return tuple(
42
+ [
43
+ _view(definition, await self.setting_provider.snapshot(definition))
44
+ for definition in self._catalog.definitions
45
+ ]
46
+ )
47
+
48
+ @authorize(SETTINGS_READ)
49
+ async def get(self, name: str) -> SettingView:
50
+ definition = self._definition(name)
51
+ return _view(definition, await self.setting_provider.snapshot(definition))
52
+
53
+ @authorize(SETTINGS_WRITE)
54
+ async def update(self, name: str, command: UpdateSettingCommand) -> SettingWriteResult:
55
+ definition = self._definition(name)
56
+ try:
57
+ # JSON 数组/日期须由 Pydantic 的 JSON 模式解释,不能误用 strict Python 输入语义。
58
+ value = definition._validate_json(json.dumps(command.value, allow_nan=False))
59
+ except (TypeError, ValueError):
60
+ raise BusinessError(_INVALID_VALUE, fields=(_VALUE_FIELD,)) from None
61
+ saved = await self._settings.update(
62
+ definition, value, expected_version=command.expected_version
63
+ )
64
+ return SettingWriteResult(
65
+ setting=_view(definition, saved.snapshot), notification=saved.notification
66
+ )
67
+
68
+ @authorize(SETTINGS_WRITE)
69
+ async def reset(self, name: str, command: ResetSettingCommand) -> SettingWriteResult:
70
+ definition = self._definition(name)
71
+ saved = await self._settings.reset(definition, expected_version=command.expected_version)
72
+ return SettingWriteResult(
73
+ setting=_view(definition, saved.snapshot), notification=saved.notification
74
+ )
75
+
76
+ def _definition(self, name: str) -> SettingDefinition[Any]:
77
+ definition = self._catalog.get(name)
78
+ if definition is None:
79
+ raise ResourceNotFoundError("Setting not found")
80
+ return definition
81
+
82
+
83
+ def _view(definition: SettingDefinition[Any], snapshot: SettingSnapshot[Any]) -> SettingView:
84
+ # HTTP DTO 是独立 JSON 副本;不会把内部不可变快照的容器直接交给调用方。
85
+ value = (
86
+ None
87
+ if snapshot.source is SettingValueSource.UNSET
88
+ else cast(
89
+ JsonValue,
90
+ definition._adapter.dump_python(_serialization_value(snapshot.value), mode="json"),
91
+ )
92
+ )
93
+ return SettingView(
94
+ name=snapshot.name, value=value, source=snapshot.source, version=snapshot.version
95
+ )
@@ -0,0 +1,33 @@
1
+ """按最终存储 binding 组装原生 Dishka 服务;不以 provider Module 名单推断能力。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dishka import Provider, Scope
6
+
7
+ from ..configuration import ConfigurationRoot
8
+ from .catalog import SettingCatalog
9
+ from .manager import SettingManager
10
+ from .provider import SettingProvider
11
+ from .store import RuntimeSettingStore
12
+
13
+
14
+ def _provide_settings(
15
+ provider: Provider,
16
+ catalog: SettingCatalog,
17
+ configuration: ConfigurationRoot,
18
+ *,
19
+ has_store: bool,
20
+ ) -> None:
21
+ if has_store:
22
+
23
+ def reader(store: RuntimeSettingStore) -> SettingProvider:
24
+ return SettingProvider(catalog, configuration, store)
25
+
26
+ provider.provide(reader, scope=Scope.APP)
27
+ provider.provide(SettingManager, scope=Scope.APP)
28
+ else:
29
+ provider.provide(
30
+ lambda: SettingProvider(catalog, configuration),
31
+ provides=SettingProvider,
32
+ scope=Scope.APP,
33
+ )
@@ -0,0 +1,121 @@
1
+ """Module setting metadata 的 Application-local immutable catalog。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator, Mapping
6
+ from types import MappingProxyType
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from ..diagnostics import SourceLocation
10
+ from ..modularity import ModuleRegistry
11
+ from ..modularity.discovery import _DiscoveredType
12
+ from .contracts import SettingDefinition
13
+ from .definition_discovery import _collect_setting_definitions
14
+ from .errors import SettingDefinitionError
15
+ from .handlers import SettingHandlerDefinition
16
+
17
+ if TYPE_CHECKING:
18
+ from ..services.contribution import ServiceContributionRegistry
19
+
20
+
21
+ class SettingCatalog:
22
+ __slots__ = ("_definitions", "_handlers", "_sources")
23
+
24
+ def __init__(
25
+ self,
26
+ definitions: tuple[SettingDefinition[Any], ...],
27
+ handlers: tuple[SettingHandlerDefinition[Any], ...] = (),
28
+ *,
29
+ sources: Mapping[str, SourceLocation] | None = None,
30
+ ) -> None:
31
+ indexed: dict[str, SettingDefinition[Any]] = {}
32
+ for definition in definitions:
33
+ if not isinstance(definition, SettingDefinition):
34
+ raise SettingDefinitionError(
35
+ setting=repr(definition),
36
+ reason="catalog entries must be SettingDefinition values",
37
+ )
38
+ if definition.name in indexed:
39
+ raise SettingDefinitionError(
40
+ setting=definition.name,
41
+ reason="setting is declared more than once",
42
+ )
43
+ indexed[definition.name] = definition
44
+ self._definitions: Mapping[str, SettingDefinition[Any]] = MappingProxyType(
45
+ dict(sorted(indexed.items()))
46
+ )
47
+ seen: set[tuple[str, type[object]]] = set()
48
+ for handler in handlers:
49
+ if not isinstance(handler, SettingHandlerDefinition):
50
+ raise SettingDefinitionError(setting="handler", reason="invalid handler definition")
51
+ self.require_owned(handler.setting)
52
+ key = (handler.setting.name, handler.handler_type)
53
+ if key in seen:
54
+ raise SettingDefinitionError(setting=key[0], reason="handler is declared twice")
55
+ seen.add(key)
56
+ self._handlers = handlers
57
+ self._sources = MappingProxyType(dict(sources or {}))
58
+
59
+ @property
60
+ def sources(self) -> Mapping[str, SourceLocation]:
61
+ return self._sources
62
+
63
+ @property
64
+ def handlers(self) -> tuple[SettingHandlerDefinition[Any], ...]:
65
+ return self._handlers
66
+
67
+ @property
68
+ def definitions(self) -> tuple[SettingDefinition[Any], ...]:
69
+ return tuple(self._definitions.values())
70
+
71
+ def __iter__(self) -> Iterator[str]:
72
+ return iter(self._definitions)
73
+
74
+ def get(self, name: str) -> SettingDefinition[Any] | None:
75
+ return self._definitions.get(name)
76
+
77
+ def require_owned(self, definition: SettingDefinition[Any]) -> None:
78
+ if self._definitions.get(definition.name) is not definition:
79
+ raise SettingDefinitionError(
80
+ setting=definition.name,
81
+ reason="definition is not owned by this Application",
82
+ )
83
+
84
+
85
+ def _build_setting_catalog(
86
+ registry: ModuleRegistry, discoveries: tuple[_DiscoveredType, ...]
87
+ ) -> SettingCatalog:
88
+ definitions, sources = _collect_setting_definitions(discoveries)
89
+ handlers: list[SettingHandlerDefinition[Any]] = []
90
+ for descriptor in registry.dependency_order:
91
+ declared_handlers = descriptor.module_type.setting_handler_definitions
92
+ if not isinstance(declared_handlers, tuple):
93
+ raise SettingDefinitionError(
94
+ setting=str(descriptor.key), reason="setting_handler_definitions must be a tuple"
95
+ )
96
+ handlers.extend(declared_handlers)
97
+ return SettingCatalog(definitions, tuple(handlers), sources=sources)
98
+
99
+
100
+ def _contribute_setting_handler_defaults(
101
+ registry: ModuleRegistry,
102
+ contributions: ServiceContributionRegistry,
103
+ ) -> None:
104
+ from dishka import Provider, Scope
105
+
106
+ # 声明只拥有 handler 与 setting 的关系;实例和依赖仍交给原生 Dishka APP binding。
107
+ registered: set[type[object]] = set()
108
+ for descriptor in registry.dependency_order:
109
+ for definition in descriptor.module_type.setting_handler_definitions:
110
+ handler_type = definition.handler_type
111
+ if handler_type in registered:
112
+ continue
113
+ registered.add(handler_type)
114
+ provider = Provider()
115
+ provider.provide(handler_type, scope=Scope.APP)
116
+ contributions._add_framework_default(
117
+ provider=provider,
118
+ owner=descriptor.key,
119
+ source=descriptor.declaration_source,
120
+ reason=f"Settings handler default for {definition.setting.name}",
121
+ )
@@ -0,0 +1,14 @@
1
+ """Settings 变更只通知标识,数据库仍是有效值的唯一权威。"""
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+ from ..notifications import NotificationDefinition
6
+
7
+
8
+ class SettingChanged(BaseModel):
9
+ model_config = ConfigDict(frozen=True, extra="forbid")
10
+
11
+ name: str
12
+
13
+
14
+ SETTINGS_CHANGED = NotificationDefinition("settings.changed", SettingChanged)