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,327 @@
1
+ """Async Module runtime hooks、partial shutdown 与 Dishka finalization。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Awaitable, Callable
7
+ from typing import cast
8
+
9
+ from ..diagnostics import EventStatus, LifecyclePhase, LifecycleStage
10
+ from ..errors import (
11
+ ContainerCleanupError,
12
+ LifecycleInvocationError,
13
+ LifecycleReturnValueError,
14
+ )
15
+ from ..lifecycle import (
16
+ InitializeContext,
17
+ PostInitializeContext,
18
+ PreInitializeContext,
19
+ ShutdownContext,
20
+ ShutdownReason,
21
+ )
22
+ from ..modularity import ModuleDescriptor
23
+ from .state import (
24
+ LIFECYCLE_ERROR_TYPE,
25
+ _ApplicationStateRoot,
26
+ _lifecycle_failure_message,
27
+ )
28
+
29
+
30
+ class _RuntimeLifecycle:
31
+ """拥有一次 Application 的 runtime invocation 与 best-effort cleanup policy。"""
32
+
33
+ __slots__ = ("_root",)
34
+
35
+ def __init__(self, root: _ApplicationStateRoot) -> None:
36
+ self._root = root
37
+
38
+ async def start_modules(self) -> None:
39
+ for descriptor in self._root.registry.dependency_order:
40
+ await self._invoke_pre_initialize(descriptor)
41
+ for descriptor in self._root.registry.dependency_order:
42
+ await self._invoke_initialize(descriptor)
43
+ for descriptor in self._root.registry.dependency_order:
44
+ await self._invoke_post_initialize(descriptor)
45
+
46
+ async def shutdown_touched(
47
+ self,
48
+ *,
49
+ reason: ShutdownReason,
50
+ ) -> tuple[LifecycleInvocationError, ...]:
51
+ failures: list[LifecycleInvocationError] = []
52
+ # hook 调用前即标为 touched,所以失败 hook 自身也会获得一次逆序释放机会。
53
+ for descriptor in self._root.registry.shutdown_order:
54
+ if (
55
+ descriptor.key not in self._root.runtime_touched
56
+ or descriptor.key in self._root.released_modules
57
+ ):
58
+ continue
59
+ context = ShutdownContext(
60
+ application_id=self._root.application_id,
61
+ descriptor=descriptor,
62
+ stage=LifecycleStage.SHUTDOWN,
63
+ application=self._root.view(self._root.state),
64
+ _emit=self._root.diagnostic_emitter(
65
+ descriptor,
66
+ LifecycleStage.SHUTDOWN,
67
+ ),
68
+ configuration=self._root.configuration,
69
+ options=self._root.require_options(),
70
+ reason=reason,
71
+ completed_stages=tuple(self._root.completed_stages[descriptor.key]),
72
+ container=self._root.require_container(),
73
+ )
74
+ failure = await self._invoke_shutdown(descriptor, context)
75
+ self._root.released_modules.add(descriptor.key)
76
+ if failure is not None:
77
+ failures.append(failure)
78
+ return tuple(failures)
79
+
80
+ async def close_container(
81
+ self,
82
+ *,
83
+ exception: BaseException | None,
84
+ ) -> tuple[ContainerCleanupError, ...]:
85
+ container = self._root.container
86
+ if container is None:
87
+ return ()
88
+ failures: list[ContainerCleanupError] = []
89
+ try:
90
+ while True:
91
+ try:
92
+ await container.close(exception)
93
+ except BaseException as original_error:
94
+ # 当前 lock 解析的 Dishka 会在调用前弹出当前 finalizer;重入 native close
95
+ # 才能在 BaseException 后继续穷尽其余资源,而非建立第二套 cleanup engine。
96
+ failures.append(self._container_cleanup_error(original_error))
97
+ continue
98
+ break
99
+ finally:
100
+ # 只在 native close 已穷尽后撤销引用;stop 后不再暴露可解析容器。
101
+ self._root.container = None
102
+ return tuple(failures)
103
+
104
+ def _container_cleanup_error(
105
+ self,
106
+ original_error: BaseException,
107
+ ) -> ContainerCleanupError:
108
+ subject = "dishka-container"
109
+ failure_id = self._root.journal.begin_failure(
110
+ error_type=ContainerCleanupError.__name__,
111
+ phase=LifecyclePhase.CLEANUP,
112
+ stage=LifecycleStage.SHUTDOWN,
113
+ subject=subject,
114
+ message=(f"{subject}/shutdown failed with {type(original_error).__name__}"),
115
+ source=None,
116
+ dependency_path=(),
117
+ )
118
+ self._root.journal.complete_failure(failure_id)
119
+ return ContainerCleanupError(
120
+ failure_id=failure_id,
121
+ original_error=original_error,
122
+ )
123
+
124
+ async def _invoke_pre_initialize(self, descriptor: ModuleDescriptor) -> None:
125
+ stage = LifecycleStage.PRE_INITIALIZE
126
+ context = PreInitializeContext(
127
+ application_id=self._root.application_id,
128
+ descriptor=descriptor,
129
+ stage=stage,
130
+ application=self._root.view(self._root.state),
131
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
132
+ configuration=self._root.configuration,
133
+ options=self._root.require_options(),
134
+ container=self._root.require_container(),
135
+ )
136
+ callback = cast(
137
+ Callable[[PreInitializeContext], Awaitable[object]],
138
+ descriptor.instance.pre_initialize,
139
+ )
140
+ await self._invoke_start_hook(
141
+ descriptor,
142
+ stage,
143
+ lambda: callback(context),
144
+ )
145
+
146
+ async def _invoke_initialize(self, descriptor: ModuleDescriptor) -> None:
147
+ stage = LifecycleStage.INITIALIZE
148
+ context = InitializeContext(
149
+ application_id=self._root.application_id,
150
+ descriptor=descriptor,
151
+ stage=stage,
152
+ application=self._root.view(self._root.state),
153
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
154
+ configuration=self._root.configuration,
155
+ options=self._root.require_options(),
156
+ container=self._root.require_container(),
157
+ )
158
+ callback = cast(
159
+ Callable[[InitializeContext], Awaitable[object]],
160
+ descriptor.instance.initialize,
161
+ )
162
+ await self._invoke_start_hook(
163
+ descriptor,
164
+ stage,
165
+ lambda: callback(context),
166
+ )
167
+
168
+ async def _invoke_post_initialize(self, descriptor: ModuleDescriptor) -> None:
169
+ stage = LifecycleStage.POST_INITIALIZE
170
+ context = PostInitializeContext(
171
+ application_id=self._root.application_id,
172
+ descriptor=descriptor,
173
+ stage=stage,
174
+ application=self._root.view(self._root.state),
175
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
176
+ configuration=self._root.configuration,
177
+ options=self._root.require_options(),
178
+ container=self._root.require_container(),
179
+ )
180
+ callback = cast(
181
+ Callable[[PostInitializeContext], Awaitable[object]],
182
+ descriptor.instance.post_initialize,
183
+ )
184
+ await self._invoke_start_hook(
185
+ descriptor,
186
+ stage,
187
+ lambda: callback(context),
188
+ )
189
+
190
+ async def _invoke_start_hook(
191
+ self,
192
+ descriptor: ModuleDescriptor,
193
+ stage: LifecycleStage,
194
+ callback: Callable[[], Awaitable[object]],
195
+ ) -> None:
196
+ subject = str(descriptor.key)
197
+ self._root.runtime_touched.add(descriptor.key)
198
+ active_event = self._root.journal.start_event(
199
+ phase=LifecyclePhase.RUNTIME,
200
+ stage=stage,
201
+ subject=subject,
202
+ source=descriptor.declaration_source,
203
+ )
204
+ try:
205
+ result = await callback()
206
+ self._validate_lifecycle_result(
207
+ result,
208
+ descriptor=descriptor,
209
+ phase=LifecyclePhase.RUNTIME,
210
+ stage=stage,
211
+ )
212
+ except BaseException as original_error:
213
+ # cancellation/system error 也必须结构化,start 才能统一回滚 touched Modules。
214
+ failure_id = self._root.journal.begin_failure(
215
+ error_type=LIFECYCLE_ERROR_TYPE,
216
+ phase=LifecyclePhase.RUNTIME,
217
+ stage=stage,
218
+ subject=subject,
219
+ message=_lifecycle_failure_message(subject, stage, original_error),
220
+ source=descriptor.declaration_source,
221
+ dependency_path=tuple(str(key) for key in descriptor.dependency_path),
222
+ )
223
+ duration_ns = self._root.journal.finish_event(
224
+ active_event,
225
+ status=EventStatus.FAILED,
226
+ failure_id=failure_id,
227
+ )
228
+ error = LifecycleInvocationError(
229
+ failure_id=failure_id,
230
+ module=descriptor.key,
231
+ phase=LifecyclePhase.RUNTIME,
232
+ stage=stage,
233
+ dependency_path=descriptor.dependency_path,
234
+ duration_ns=duration_ns,
235
+ source=descriptor.declaration_source,
236
+ original_error=original_error,
237
+ )
238
+ raise error from original_error
239
+
240
+ self._root.journal.finish_event(
241
+ active_event,
242
+ status=EventStatus.SUCCEEDED,
243
+ failure_id=None,
244
+ )
245
+ self._root.completed_stages[descriptor.key].append(stage)
246
+
247
+ async def _invoke_shutdown(
248
+ self,
249
+ descriptor: ModuleDescriptor,
250
+ context: ShutdownContext,
251
+ ) -> LifecycleInvocationError | None:
252
+ stage = LifecycleStage.SHUTDOWN
253
+ subject = str(descriptor.key)
254
+ active_event = self._root.journal.start_event(
255
+ phase=LifecyclePhase.CLEANUP,
256
+ stage=stage,
257
+ subject=subject,
258
+ source=descriptor.declaration_source,
259
+ )
260
+ try:
261
+ callback = cast(
262
+ Callable[[ShutdownContext], Awaitable[object]],
263
+ descriptor.instance.shutdown,
264
+ )
265
+ result = await callback(context)
266
+ self._validate_lifecycle_result(
267
+ result,
268
+ descriptor=descriptor,
269
+ phase=LifecyclePhase.CLEANUP,
270
+ stage=stage,
271
+ )
272
+ except BaseException as original_error:
273
+ # cleanup 首个 cancellation/system error 不能阻止后续 Module 获得释放机会。
274
+ failure_id = self._root.journal.begin_failure(
275
+ error_type=LIFECYCLE_ERROR_TYPE,
276
+ phase=LifecyclePhase.CLEANUP,
277
+ stage=stage,
278
+ subject=subject,
279
+ message=_lifecycle_failure_message(subject, stage, original_error),
280
+ source=descriptor.declaration_source,
281
+ dependency_path=tuple(str(key) for key in descriptor.dependency_path),
282
+ )
283
+ duration_ns = self._root.journal.finish_event(
284
+ active_event,
285
+ status=EventStatus.FAILED,
286
+ failure_id=failure_id,
287
+ )
288
+ error = LifecycleInvocationError(
289
+ failure_id=failure_id,
290
+ module=descriptor.key,
291
+ phase=LifecyclePhase.CLEANUP,
292
+ stage=stage,
293
+ dependency_path=descriptor.dependency_path,
294
+ duration_ns=duration_ns,
295
+ source=descriptor.declaration_source,
296
+ original_error=original_error,
297
+ )
298
+ self._root.journal.complete_failure(failure_id)
299
+ return error
300
+
301
+ self._root.journal.finish_event(
302
+ active_event,
303
+ status=EventStatus.SUCCEEDED,
304
+ failure_id=None,
305
+ )
306
+ self._root.completed_stages[descriptor.key].append(stage)
307
+ return None
308
+
309
+ @staticmethod
310
+ def _validate_lifecycle_result(
311
+ result: object,
312
+ *,
313
+ descriptor: ModuleDescriptor,
314
+ phase: LifecyclePhase,
315
+ stage: LifecycleStage,
316
+ ) -> None:
317
+ if result is None:
318
+ return
319
+ if inspect.iscoroutine(result):
320
+ result.close()
321
+ returned_type = f"{type(result).__module__}.{type(result).__qualname__}"
322
+ raise LifecycleReturnValueError(
323
+ module=descriptor.key,
324
+ phase=phase,
325
+ stage=stage,
326
+ returned_type=returned_type,
327
+ )
@@ -0,0 +1,216 @@
1
+ """Single-Application mutable root、read-only views 与 transition ownership。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import Callable
7
+ from dataclasses import dataclass, field
8
+ from typing import assert_never
9
+ from uuid import UUID
10
+
11
+ from dishka import AsyncContainer
12
+
13
+ from ..application_services import ApplicationServiceCatalog
14
+ from ..application_services.invocation import _InvocationRuntime
15
+ from ..authorization import PermissionCatalog
16
+ from ..background_jobs import BackgroundJobCatalog
17
+ from ..background_workers import BackgroundWorkerCatalog
18
+ from ..background_workers.runtime import _BackgroundCoordinator
19
+ from ..caching import CacheCatalog
20
+ from ..configuration import ConfigurationRoot
21
+ from ..diagnostics import LifecyclePhase, LifecycleStage
22
+ from ..diagnostics.journal import _DiagnosticJournal
23
+ from ..errors import ApplicationRuntimeError, InvalidApplicationStateError, LifecycleInvocationError
24
+ from ..events import LocalEventCatalog
25
+ from ..events.contribution import EventContributionRegistry
26
+ from ..hosted_services import ApplicationReadinessSnapshot, HostedServiceCatalog
27
+ from ..hosted_services.runtime import _HostedServiceCoordinator
28
+ from ..lifecycle import ApplicationState, ApplicationView
29
+ from ..modularity import ModuleDescriptor, ModuleKey, ModuleRegistry
30
+ from ..options import OptionsSnapshot, PreOptionsSnapshot
31
+ from ..options.registry import _OptionsRegistry
32
+ from ..services import ServiceCatalog
33
+ from ..services.contribution import ServiceContributionRegistry
34
+ from ..settings import SettingCatalog
35
+
36
+ LIFECYCLE_ERROR_TYPE = LifecycleInvocationError.__name__
37
+
38
+
39
+ @dataclass(slots=True)
40
+ class _ApplicationStateRoot:
41
+ """所有可变事实的唯一 owner;Application 之间不共享容器、状态或 journal。"""
42
+
43
+ application_id: UUID
44
+ registry: ModuleRegistry
45
+ configuration: ConfigurationRoot
46
+ pre_options_registry: _OptionsRegistry
47
+ options_registry: _OptionsRegistry
48
+ service_registry: ServiceContributionRegistry
49
+ event_registry: EventContributionRegistry
50
+ permission_catalog: PermissionCatalog
51
+ cache_catalog: CacheCatalog
52
+ setting_catalog: SettingCatalog
53
+ background_worker_catalog: BackgroundWorkerCatalog
54
+ background_job_catalog: BackgroundJobCatalog
55
+ hosted_service_catalog: HostedServiceCatalog
56
+ journal: _DiagnosticJournal
57
+ pre_options: PreOptionsSnapshot | None = None
58
+ options: OptionsSnapshot | None = None
59
+ service_catalog: ServiceCatalog | None = None
60
+ application_service_catalog: ApplicationServiceCatalog | None = None
61
+ event_catalog: LocalEventCatalog | None = None
62
+ invocation_runtime: _InvocationRuntime | None = None
63
+ background_coordinator: _BackgroundCoordinator | None = None
64
+ hosted_service_coordinator: _HostedServiceCoordinator | None = None
65
+ container: AsyncContainer | None = None
66
+ state: ApplicationState = ApplicationState.COMPOSED
67
+ transition_token: object | None = None
68
+ runtime_touched: set[ModuleKey] = field(default_factory=set)
69
+ released_modules: set[ModuleKey] = field(default_factory=set)
70
+ completed_stages: dict[ModuleKey, list[LifecycleStage]] = field(default_factory=dict)
71
+ termination_event: asyncio.Event = field(default_factory=asyncio.Event)
72
+ runtime_failure: ApplicationRuntimeError | None = None
73
+ termination_failure: BaseException | None = None
74
+ runtime_monitor_task: asyncio.Task[None] | None = None
75
+
76
+ def require_pre_options(self) -> PreOptionsSnapshot:
77
+ if self.pre_options is None:
78
+ raise RuntimeError(
79
+ "PreOptions snapshot is unavailable before the pre_configure barrier"
80
+ )
81
+ return self.pre_options
82
+
83
+ def require_options(self) -> OptionsSnapshot:
84
+ if self.options is None:
85
+ raise RuntimeError("Options snapshot is unavailable before composition completes")
86
+ return self.options
87
+
88
+ def require_service_catalog(self) -> ServiceCatalog:
89
+ if self.service_catalog is None:
90
+ raise RuntimeError("Service catalog is unavailable before composition completes")
91
+ return self.service_catalog
92
+
93
+ def require_container(self) -> AsyncContainer:
94
+ if self.container is None:
95
+ raise RuntimeError("Dishka container is unavailable after Application cleanup")
96
+ return self.container
97
+
98
+ def require_application_service_catalog(self) -> ApplicationServiceCatalog:
99
+ if self.application_service_catalog is None:
100
+ raise RuntimeError(
101
+ "Application Service catalog is unavailable before composition completes"
102
+ )
103
+ return self.application_service_catalog
104
+
105
+ def require_event_catalog(self) -> LocalEventCatalog:
106
+ if self.event_catalog is None:
107
+ raise RuntimeError("Local Event catalog is unavailable before composition completes")
108
+ return self.event_catalog
109
+
110
+ def require_permission_catalog(self) -> PermissionCatalog:
111
+ return self.permission_catalog
112
+
113
+ def require_cache_catalog(self) -> CacheCatalog:
114
+ return self.cache_catalog
115
+
116
+ def require_setting_catalog(self) -> SettingCatalog:
117
+ return self.setting_catalog
118
+
119
+ def require_background_worker_catalog(self) -> BackgroundWorkerCatalog:
120
+ return self.background_worker_catalog
121
+
122
+ def require_background_job_catalog(self) -> BackgroundJobCatalog:
123
+ return self.background_job_catalog
124
+
125
+ def require_invocation_runtime(self) -> _InvocationRuntime:
126
+ if self.invocation_runtime is None:
127
+ raise RuntimeError("Application invocation runtime is unavailable")
128
+ return self.invocation_runtime
129
+
130
+ def require_background_coordinator(self) -> _BackgroundCoordinator:
131
+ if self.background_coordinator is None:
132
+ raise RuntimeError("Application background coordinator is unavailable")
133
+ return self.background_coordinator
134
+
135
+ def require_hosted_service_catalog(self) -> HostedServiceCatalog:
136
+ return self.hosted_service_catalog
137
+
138
+ def require_hosted_service_coordinator(self) -> _HostedServiceCoordinator:
139
+ if self.hosted_service_coordinator is None:
140
+ raise RuntimeError("Hosted Service coordinator is unavailable")
141
+ return self.hosted_service_coordinator
142
+
143
+ def readiness(self) -> ApplicationReadinessSnapshot:
144
+ return self.require_hosted_service_coordinator().readiness()
145
+
146
+ def view(self, state: ApplicationState | None) -> ApplicationView:
147
+ return ApplicationView(
148
+ application_id=self.application_id,
149
+ state=state,
150
+ modules=self.registry.snapshot,
151
+ )
152
+
153
+ def diagnostic_emitter(
154
+ self,
155
+ descriptor: ModuleDescriptor,
156
+ stage: LifecycleStage,
157
+ ) -> Callable[[str], None]:
158
+ phase = _phase_for_stage(stage)
159
+
160
+ def emit(code: str) -> None:
161
+ self.journal.emit(
162
+ phase=phase,
163
+ stage=stage,
164
+ subject=str(descriptor.key),
165
+ code=code,
166
+ source=descriptor.declaration_source,
167
+ )
168
+
169
+ return emit
170
+
171
+ def claim_transition(
172
+ self,
173
+ *,
174
+ operation: str,
175
+ allowed: tuple[ApplicationState, ...],
176
+ ) -> object:
177
+ # claim 在首次 await 前完成;并发调用立即失败,不排队改变调用者语义。
178
+ if self.transition_token is not None or self.state not in allowed:
179
+ raise InvalidApplicationStateError(
180
+ operation=operation,
181
+ current=self.state,
182
+ allowed=allowed,
183
+ )
184
+ token = object()
185
+ self.transition_token = token
186
+ return token
187
+
188
+ def release_transition(self, token: object) -> None:
189
+ assert self.transition_token is token
190
+ self.transition_token = None
191
+
192
+
193
+ def _phase_for_stage(stage: LifecycleStage) -> LifecyclePhase:
194
+ match stage:
195
+ case (
196
+ LifecycleStage.PRE_CONFIGURE | LifecycleStage.CONFIGURE | LifecycleStage.POST_CONFIGURE
197
+ ):
198
+ return LifecyclePhase.COMPOSITION
199
+ case (
200
+ LifecycleStage.PRE_INITIALIZE
201
+ | LifecycleStage.INITIALIZE
202
+ | LifecycleStage.POST_INITIALIZE
203
+ ):
204
+ return LifecyclePhase.RUNTIME
205
+ case LifecycleStage.SHUTDOWN:
206
+ return LifecyclePhase.CLEANUP
207
+ case _ as unreachable:
208
+ assert_never(unreachable)
209
+
210
+
211
+ def _lifecycle_failure_message(
212
+ subject: str,
213
+ stage: LifecycleStage,
214
+ original_error: BaseException,
215
+ ) -> str:
216
+ return f"{subject}/{stage.value} failed with {type(original_error).__name__}"
@@ -0,0 +1,54 @@
1
+ from ..invocation import ApplicationInvocationContext
2
+ from .catalog import (
3
+ ApplicationServiceCatalog,
4
+ ApplicationServiceMethod,
5
+ ApplicationServiceRegistration,
6
+ )
7
+ from .contracts import (
8
+ ApplicationService,
9
+ ApplicationServiceContract,
10
+ IntegrationService,
11
+ application_service,
12
+ )
13
+ from .errors import (
14
+ ApplicationInvocationCleanupError,
15
+ ApplicationInvocationRejectedError,
16
+ ApplicationServiceDefinitionError,
17
+ ApplicationServiceMethodError,
18
+ ApplicationServiceValidationError,
19
+ ApplicationServiceValidationIssue,
20
+ CrossTaskInvocationError,
21
+ NestedConnectionError,
22
+ )
23
+ from .execution import MethodInvocation
24
+ from .interceptors import InterceptionStage, Interceptor, InterceptorRegistration
25
+ from .policies import disable_validation, enable_validation
26
+ from .seeding import DataSeedContributor, DataSeeder, DataSeedError
27
+
28
+ __all__ = (
29
+ "ApplicationInvocationCleanupError",
30
+ "ApplicationInvocationContext",
31
+ "ApplicationInvocationRejectedError",
32
+ "ApplicationService",
33
+ "ApplicationServiceCatalog",
34
+ "ApplicationServiceContract",
35
+ "ApplicationServiceDefinitionError",
36
+ "ApplicationServiceMethod",
37
+ "ApplicationServiceMethodError",
38
+ "ApplicationServiceRegistration",
39
+ "ApplicationServiceValidationError",
40
+ "ApplicationServiceValidationIssue",
41
+ "CrossTaskInvocationError",
42
+ "DataSeedContributor",
43
+ "DataSeedError",
44
+ "DataSeeder",
45
+ "IntegrationService",
46
+ "InterceptionStage",
47
+ "Interceptor",
48
+ "InterceptorRegistration",
49
+ "MethodInvocation",
50
+ "NestedConnectionError",
51
+ "application_service",
52
+ "disable_validation",
53
+ "enable_validation",
54
+ )