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,546 @@
1
+ """公开 Application 状态机、生命周期与非 DI 调用入口。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator
7
+ from contextlib import asynccontextmanager, contextmanager
8
+ from typing import Concatenate, ParamSpec, TypeAlias, TypeVar, cast
9
+ from uuid import UUID, uuid4
10
+
11
+ from dishka import AsyncContainer, Scope
12
+
13
+ from ..application_services import (
14
+ ApplicationInvocationRejectedError,
15
+ ApplicationServiceCatalog,
16
+ ApplicationServiceContract,
17
+ ApplicationServiceRegistration,
18
+ )
19
+ from ..application_services.dispatcher import _ApplicationServiceDispatcher
20
+ from ..application_services.invocation import (
21
+ _ScopedApplicationServiceInvoker,
22
+ )
23
+ from ..authorization import CurrentUser, PermissionCatalog
24
+ from ..background_jobs import BackgroundJobCatalog
25
+ from ..background_workers import BackgroundWorkerCatalog
26
+ from ..caching import CacheCatalog
27
+ from ..configuration import ConfigurationRoot
28
+ from ..diagnostics import DiagnosticSnapshot
29
+ from ..errors import (
30
+ ApplicationRuntimeError,
31
+ ApplicationStartError,
32
+ ContainerCleanupError,
33
+ InvalidApplicationStateError,
34
+ LifecycleInvocationError,
35
+ )
36
+ from ..errors.lifecycle import (
37
+ _BackgroundExecutionStartFailure,
38
+ _exclude_runtime_failure,
39
+ )
40
+ from ..events import LocalEventCatalog
41
+ from ..hosted_services import (
42
+ ApplicationReadinessSnapshot,
43
+ HostedServiceCatalog,
44
+ HostedServiceLifecycleError,
45
+ )
46
+ from ..invocation.function_runtime import _invoke_callable
47
+ from ..lifecycle import ApplicationState, ShutdownReason
48
+ from ..modularity import ModuleRegistry
49
+ from ..options import OptionsSnapshot
50
+ from ..services import ServiceCatalog
51
+ from ..settings import SettingCatalog
52
+ from .runtime_hooks import _RuntimeLifecycle
53
+ from .state import _ApplicationStateRoot
54
+
55
+ _P = ParamSpec("_P")
56
+ _R = TypeVar("_R")
57
+ _TContract = TypeVar("_TContract", bound=ApplicationServiceContract)
58
+ _StartCleanupFailure: TypeAlias = (
59
+ LifecycleInvocationError
60
+ | ContainerCleanupError
61
+ | _BackgroundExecutionStartFailure
62
+ | HostedServiceLifecycleError
63
+ )
64
+
65
+
66
+ class Application:
67
+ """拥有单次 build 的 Module instances、状态、transition 与 diagnostics。"""
68
+
69
+ __slots__ = ("_root",)
70
+
71
+ def __init__(self, root: _ApplicationStateRoot) -> None:
72
+ self._root = root
73
+
74
+ @property
75
+ def id(self) -> UUID:
76
+ return self._root.application_id
77
+
78
+ @property
79
+ def state(self) -> ApplicationState:
80
+ return self._root.state
81
+
82
+ @property
83
+ def modules(self) -> ModuleRegistry:
84
+ return self._root.registry
85
+
86
+ @property
87
+ def configuration(self) -> ConfigurationRoot:
88
+ return self._root.configuration
89
+
90
+ @property
91
+ def options(self) -> OptionsSnapshot:
92
+ return self._root.require_options()
93
+
94
+ @property
95
+ def service_catalog(self) -> ServiceCatalog:
96
+ return self._root.require_service_catalog()
97
+
98
+ @property
99
+ def application_service_catalog(self) -> ApplicationServiceCatalog:
100
+ return self._root.require_application_service_catalog()
101
+
102
+ @property
103
+ def event_catalog(self) -> LocalEventCatalog:
104
+ return self._root.require_event_catalog()
105
+
106
+ @property
107
+ def permission_catalog(self) -> PermissionCatalog:
108
+ return self._root.require_permission_catalog()
109
+
110
+ @property
111
+ def cache_catalog(self) -> CacheCatalog:
112
+ return self._root.require_cache_catalog()
113
+
114
+ @property
115
+ def setting_catalog(self) -> SettingCatalog:
116
+ return self._root.require_setting_catalog()
117
+
118
+ @property
119
+ def background_worker_catalog(self) -> BackgroundWorkerCatalog:
120
+ return self._root.require_background_worker_catalog()
121
+
122
+ @property
123
+ def background_job_catalog(self) -> BackgroundJobCatalog:
124
+ return self._root.require_background_job_catalog()
125
+
126
+ @property
127
+ def hosted_service_catalog(self) -> HostedServiceCatalog:
128
+ return self._root.require_hosted_service_catalog()
129
+
130
+ @property
131
+ def readiness(self) -> ApplicationReadinessSnapshot:
132
+ return self._root.readiness()
133
+
134
+ @property
135
+ def diagnostics(self) -> DiagnosticSnapshot:
136
+ return self._root.journal.snapshot(self._root.application_id)
137
+
138
+ def _container_for_integration(self) -> AsyncContainer:
139
+ # 外部 adapter 只能在 composition root 建桥;不把 resolver/container 变成 public API。
140
+ return self._root.require_container()
141
+
142
+ async def start(self) -> None:
143
+ await self._start(run_services=True)
144
+
145
+ async def _start(self, *, run_services: bool) -> None:
146
+ token = self._root.claim_transition(
147
+ operation="start",
148
+ allowed=(ApplicationState.COMPOSED,),
149
+ )
150
+ self._root.state = ApplicationState.STARTING
151
+ if not run_services:
152
+ # 数据库 CLI 只需要 Module 初始化及正式 invocation;不打开后台生产者或外部 SDK。
153
+ self._root.require_background_coordinator().disable_for_database()
154
+ lifecycle = _RuntimeLifecycle(self._root)
155
+ started = False
156
+ try:
157
+ try:
158
+ await lifecycle.start_modules()
159
+ except LifecycleInvocationError as primary:
160
+ self._root.state = ApplicationState.ROLLING_BACK
161
+ cleanup_failures = await lifecycle.shutdown_touched(
162
+ reason=ShutdownReason.START_FAILURE,
163
+ )
164
+ container_failures = await lifecycle.close_container(
165
+ exception=primary.original_error,
166
+ )
167
+ all_cleanup_failures: tuple[
168
+ LifecycleInvocationError | ContainerCleanupError,
169
+ ...,
170
+ ] = (
171
+ *cleanup_failures,
172
+ *container_failures,
173
+ )
174
+ self._root.journal.complete_failure(
175
+ primary.failure_id,
176
+ cleanup_failure_ids=tuple(
177
+ failure.failure_id for failure in all_cleanup_failures
178
+ ),
179
+ )
180
+ self._root.state = ApplicationState.FAILED_START
181
+ raise ApplicationStartError(
182
+ primary=primary,
183
+ cleanup_failures=all_cleanup_failures,
184
+ ) from primary
185
+ self._root.state = ApplicationState.RUNNING
186
+ self._root.require_invocation_runtime().start_accepting()
187
+ try:
188
+ await self._root.require_background_coordinator().start()
189
+ except BaseException as original_error:
190
+ background_failure = _BackgroundExecutionStartFailure(original_error=original_error)
191
+ runtime_cleanup_failures = await self._rollback_runtime_start(
192
+ lifecycle,
193
+ primary=background_failure,
194
+ )
195
+ raise ApplicationStartError(
196
+ primary=background_failure,
197
+ cleanup_failures=runtime_cleanup_failures,
198
+ ) from background_failure
199
+ try:
200
+ if run_services:
201
+ await self._root.require_hosted_service_coordinator().start()
202
+ except BaseException as original_error:
203
+ hosted_failure = (
204
+ original_error
205
+ if isinstance(original_error, HostedServiceLifecycleError)
206
+ else HostedServiceLifecycleError(
207
+ service="hosted-service-coordinator",
208
+ operation="start",
209
+ error_type=type(original_error).__name__,
210
+ )
211
+ )
212
+ runtime_cleanup_failures = await self._rollback_runtime_start(
213
+ lifecycle,
214
+ primary=hosted_failure,
215
+ )
216
+ raise ApplicationStartError(
217
+ primary=hosted_failure,
218
+ cleanup_failures=runtime_cleanup_failures,
219
+ ) from hosted_failure
220
+ started = True
221
+ finally:
222
+ self._root.release_transition(token)
223
+ if started and (self._root.require_background_coordinator().has_runtime_failure_source):
224
+ self._root.runtime_monitor_task = asyncio.create_task(
225
+ self._monitor_runtime_failure(),
226
+ name=f"python-platform-runtime-monitor:{self.id}",
227
+ )
228
+
229
+ async def _rollback_runtime_start(
230
+ self,
231
+ lifecycle: _RuntimeLifecycle,
232
+ *,
233
+ primary: BaseException,
234
+ ) -> tuple[_StartCleanupFailure, ...]:
235
+ self._root.state = ApplicationState.ROLLING_BACK
236
+ hosted = self._root.require_hosted_service_coordinator()
237
+ hosted.close_admission()
238
+ cleanup_failures: list[_StartCleanupFailure] = []
239
+ # bootstrap 是严格栈式 ownership:先穷尽已 touch 的 Hosted Service,
240
+ # 再撤销更早启动的 background,不能复用正常 stop 的并行收口策略。
241
+ try:
242
+ hosted_failures = await hosted.stop(ShutdownReason.START_FAILURE)
243
+ except BaseException as hosted_error:
244
+ cleanup_failures.append(
245
+ HostedServiceLifecycleError(
246
+ service="hosted-service-coordinator",
247
+ operation="stop",
248
+ error_type=type(hosted_error).__name__,
249
+ )
250
+ )
251
+ else:
252
+ cleanup_failures.extend(hosted_failures)
253
+ try:
254
+ await self._root.require_background_coordinator().stop()
255
+ except BaseException as background_error:
256
+ cleanup_failures.append(
257
+ _BackgroundExecutionStartFailure(original_error=background_error)
258
+ )
259
+ await self._root.require_invocation_runtime().stop_accepting_and_wait()
260
+ cleanup_failures.extend(
261
+ await lifecycle.shutdown_touched(reason=ShutdownReason.START_FAILURE)
262
+ )
263
+ cleanup_failures.extend(await lifecycle.close_container(exception=primary))
264
+ self._root.state = ApplicationState.FAILED_START
265
+ return tuple(cleanup_failures)
266
+
267
+ async def stop(self) -> None:
268
+ if self._root.state in (
269
+ ApplicationState.STOPPED,
270
+ ApplicationState.STOPPED_WITH_ERRORS,
271
+ ApplicationState.FAILED_START,
272
+ ):
273
+ return
274
+
275
+ token = self._root.claim_transition(
276
+ operation="stop",
277
+ allowed=(ApplicationState.COMPOSED, ApplicationState.RUNNING),
278
+ )
279
+ self._root.state = ApplicationState.STOPPING
280
+ # shield 只保留弱引用;局部 task 同时承担 cleanup 的强 ownership。
281
+ cleanup_task = asyncio.create_task(self._complete_stop())
282
+ cancellation: asyncio.CancelledError | None = None
283
+ cleanup_failure: BaseException | None = None
284
+ try:
285
+ while not cleanup_task.done():
286
+ try:
287
+ await asyncio.shield(cleanup_task)
288
+ except asyncio.CancelledError as original_cancellation:
289
+ if cancellation is None:
290
+ cancellation = original_cancellation
291
+ cleanup_failure = cleanup_task.result()
292
+ finally:
293
+ self._root.release_transition(token)
294
+
295
+ if cancellation is not None:
296
+ if cleanup_failure is not None:
297
+ raise cancellation from cleanup_failure
298
+ raise cancellation
299
+ if cleanup_failure is not None:
300
+ raise cleanup_failure
301
+
302
+ async def _complete_stop(self) -> BaseException | None:
303
+ lifecycle = _RuntimeLifecycle(self._root)
304
+ reason = (
305
+ ShutdownReason.RUNTIME_FAILURE
306
+ if self._root.runtime_failure is not None
307
+ else ShutdownReason.NORMAL
308
+ )
309
+ hosted = self._root.require_hosted_service_coordinator()
310
+ # 先关闭 SDK thread bridge,再让 background 与 Hosted Service 并行停止生产新工作。
311
+ hosted.close_admission()
312
+ background_task = asyncio.create_task(
313
+ self._root.require_background_coordinator().stop(),
314
+ name=f"python-platform-background-stop:{self.id}",
315
+ )
316
+ hosted_task = asyncio.create_task(
317
+ hosted.stop(reason),
318
+ name=f"python-platform-hosted-service-stop:{self.id}",
319
+ )
320
+ background_outcome, hosted_outcome = await asyncio.gather(
321
+ background_task,
322
+ hosted_task,
323
+ return_exceptions=True,
324
+ )
325
+ infrastructure_failures: list[BaseException] = []
326
+ if isinstance(background_outcome, BaseException):
327
+ infrastructure_failures.append(background_outcome)
328
+ if isinstance(hosted_outcome, BaseException):
329
+ infrastructure_failures.append(hosted_outcome)
330
+ else:
331
+ infrastructure_failures.extend(hosted_outcome)
332
+ await self._root.require_invocation_runtime().stop_accepting_and_wait()
333
+ module_failures = await lifecycle.shutdown_touched(
334
+ reason=reason,
335
+ )
336
+ container_failures = await lifecycle.close_container(exception=None)
337
+ runtime_failure = self._root.runtime_failure
338
+ if runtime_failure is not None:
339
+ infrastructure_failures = [
340
+ remaining
341
+ for error in infrastructure_failures
342
+ if (remaining := _exclude_runtime_failure(error, runtime_failure)) is not None
343
+ ]
344
+ cleanup_failures: tuple[BaseException, ...] = (
345
+ *infrastructure_failures,
346
+ *module_failures,
347
+ *container_failures,
348
+ )
349
+ terminal_failure: BaseException | None
350
+ if runtime_failure is not None and cleanup_failures:
351
+ terminal_failure = BaseExceptionGroup(
352
+ "Application runtime termination failed",
353
+ (runtime_failure, *cleanup_failures),
354
+ )
355
+ elif runtime_failure is not None:
356
+ terminal_failure = runtime_failure
357
+ elif cleanup_failures:
358
+ terminal_failure = BaseExceptionGroup(
359
+ "Application shutdown failed",
360
+ cleanup_failures,
361
+ )
362
+ else:
363
+ terminal_failure = None
364
+ self._root.state = (
365
+ ApplicationState.STOPPED_WITH_ERRORS
366
+ if terminal_failure is not None
367
+ else ApplicationState.STOPPED
368
+ )
369
+ self._root.termination_failure = terminal_failure
370
+ self._root.termination_event.set()
371
+ monitor = self._root.runtime_monitor_task
372
+ if (
373
+ runtime_failure is None
374
+ and monitor is not None
375
+ and monitor is not asyncio.current_task()
376
+ ):
377
+ monitor.cancel()
378
+ await asyncio.gather(monitor, return_exceptions=True)
379
+ self._root.runtime_monitor_task = None
380
+ return terminal_failure
381
+
382
+ async def wait_for_termination(self) -> None:
383
+ """等待正常 stop 或受监督 runtime fatal 完整清理,不暴露内部 Task/Future。"""
384
+
385
+ allowed = (
386
+ ApplicationState.RUNNING,
387
+ ApplicationState.STOPPING,
388
+ ApplicationState.STOPPED,
389
+ ApplicationState.STOPPED_WITH_ERRORS,
390
+ )
391
+ if self._root.state not in allowed:
392
+ raise InvalidApplicationStateError(
393
+ operation="wait_for_termination",
394
+ current=self._root.state,
395
+ allowed=allowed,
396
+ )
397
+ await self._root.termination_event.wait()
398
+ failure = self._root.termination_failure
399
+ if failure is not None:
400
+ raise failure
401
+
402
+ async def _monitor_runtime_failure(self) -> None:
403
+ current = asyncio.current_task()
404
+ try:
405
+ # HostedService 只拥有 start/stop;持久任务等其他能力仍保留自身故障传播。
406
+ failure = await self._root.require_background_coordinator().wait_for_runtime_failure()
407
+ if self._root.runtime_failure is None:
408
+ self._root.runtime_failure = ApplicationRuntimeError(
409
+ component=failure.component,
410
+ error_type=failure.error_type,
411
+ )
412
+ self._root.runtime_failure._source = failure._source
413
+ try:
414
+ await self.stop()
415
+ except InvalidApplicationStateError:
416
+ # 显式 stop 已拥有 transition 时,由同一 cleanup 路径发布终态。
417
+ await self._root.termination_event.wait()
418
+ except BaseException:
419
+ # stop 已缓存同一个脱敏终态错误;monitor 不制造未观察异常。
420
+ pass
421
+ finally:
422
+ if self._root.runtime_monitor_task is current:
423
+ self._root.runtime_monitor_task = None
424
+
425
+ async def call(
426
+ self,
427
+ function: Callable[..., Awaitable[_R]],
428
+ /,
429
+ *args: object,
430
+ **kwargs: object,
431
+ ) -> _R:
432
+ """调用异步入口,业务实参显式传入,服务参数由最终 Dishka 图补齐。"""
433
+ return await self.call_as(
434
+ self._root.require_invocation_runtime().current_user(),
435
+ function,
436
+ *args,
437
+ **kwargs,
438
+ )
439
+
440
+ async def call_as(
441
+ self,
442
+ current_user: CurrentUser,
443
+ function: Callable[..., Awaitable[_R]],
444
+ /,
445
+ *args: object,
446
+ **kwargs: object,
447
+ ) -> _R:
448
+ """可信身份与调用许可覆盖 REQUEST 创建、业务执行和最终资源释放。"""
449
+ runtime = self._root.require_invocation_runtime()
450
+ return await _invoke_callable(
451
+ self._root.require_container(),
452
+ runtime,
453
+ current_user,
454
+ str(uuid4()),
455
+ function,
456
+ args,
457
+ kwargs,
458
+ )
459
+
460
+ async def invoke(
461
+ self,
462
+ method: Callable[Concatenate[_TContract, _P], Awaitable[_R]],
463
+ /,
464
+ *args: _P.args,
465
+ **kwargs: _P.kwargs,
466
+ ) -> _R:
467
+ """匿名调用公开 Protocol method;可信 caller 需显式使用 invoke_as。"""
468
+
469
+ return await self.invoke_as(CurrentUser.anonymous(), method, *args, **kwargs)
470
+
471
+ async def invoke_as(
472
+ self,
473
+ current_user: CurrentUser,
474
+ method: Callable[Concatenate[_TContract, _P], Awaitable[_R]],
475
+ /,
476
+ *args: _P.args,
477
+ **kwargs: _P.kwargs,
478
+ ) -> _R:
479
+ """由可信 Host/test 传入已验证身份,身份只绑定当前 Application 与 asyncio Task。"""
480
+
481
+ if self._root.state is not ApplicationState.RUNNING:
482
+ raise ApplicationInvocationRejectedError(
483
+ reason=f"Application must be running, got {self._root.state.value}"
484
+ )
485
+ container = self._root.require_container()
486
+ invoker = await container.get(_ScopedApplicationServiceInvoker)
487
+ result = await invoker.invoke(
488
+ container,
489
+ current_user,
490
+ str(uuid4()),
491
+ cast(Callable[..., Awaitable[object]], method),
492
+ cast(tuple[object, ...], args),
493
+ cast(dict[str, object], kwargs),
494
+ )
495
+ return cast(_R, result)
496
+
497
+ @asynccontextmanager
498
+ async def _invocation_lease_for_integration(self) -> AsyncIterator[None]:
499
+ """Web/internal entry 共用的 Application-local drain lease。"""
500
+
501
+ async with self._root.require_invocation_runtime().outer_lease():
502
+ yield
503
+
504
+ @asynccontextmanager
505
+ async def _http_request_lease_for_integration(self) -> AsyncIterator[None]:
506
+ # Phase 2 health contract 允许 composed/pre-start probe,但该 request 仍参与 stop drain;
507
+ # lease 中的 capability 会让随后进入 dispatcher 的业务 route 继续 fail closed。
508
+ async with self._root.require_invocation_runtime().outer_lease(allow_composed_request=True):
509
+ yield
510
+
511
+ @contextmanager
512
+ def _caller_context_for_integration(
513
+ self,
514
+ current_user: CurrentUser,
515
+ correlation_id: str,
516
+ ) -> Iterator[None]:
517
+ """HTTP bridge 的窄化入口;不公开 mutable principal 或 process-global current app。"""
518
+
519
+ runtime = self._root.require_invocation_runtime()
520
+ token = runtime.bind_caller(current_user, correlation_id)
521
+ try:
522
+ yield
523
+ finally:
524
+ runtime.reset_caller(token)
525
+
526
+ async def _invoke_registration(
527
+ self,
528
+ registration: ApplicationServiceRegistration,
529
+ method_name: str,
530
+ ) -> object:
531
+ """框架内部 contributor 仍走 dispatcher,但不发布 public single Contract。"""
532
+
533
+ if self._root.state is not ApplicationState.RUNNING:
534
+ raise ApplicationInvocationRejectedError(
535
+ reason=f"Application must be running, got {self._root.state.value}"
536
+ )
537
+ service = self.application_service_catalog._for_registration(registration)
538
+ method = service.methods.get(method_name)
539
+ if method is None:
540
+ raise AssertionError("internal contributor method is absent from its catalog entry")
541
+ container = self._root.require_container()
542
+ async with self._invocation_lease_for_integration():
543
+ with self._caller_context_for_integration(CurrentUser.anonymous(), str(uuid4())):
544
+ async with container(scope=Scope.REQUEST) as request_container:
545
+ dispatcher = await request_container.get(_ApplicationServiceDispatcher)
546
+ return await dispatcher.invoke(service, method, (), {})