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,260 @@
1
+ """Startup Module 到 isolated Application object graph 的 composition root。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ from collections.abc import Mapping
7
+ from pathlib import Path
8
+ from uuid import uuid4
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from ..application_services.invocation import _InvocationRuntime
13
+ from ..authorization.catalog import _build_permission_catalog
14
+ from ..background_jobs.catalog import _build_background_job_catalog
15
+ from ..background_workers import BackgroundExecutionOptions
16
+ from ..background_workers.catalog import _build_background_worker_catalog
17
+ from ..background_workers.runtime import _BackgroundCoordinator
18
+ from ..caching.catalog import _build_cache_catalog
19
+ from ..configuration.composition import _ConfigurationComposer
20
+ from ..configuration.contracts import _ConfigurationDefaultContribution
21
+ from ..diagnostics.journal import _DiagnosticJournal
22
+ from ..events.contribution import EventContributionRegistry
23
+ from ..hosted_services import HostedServiceOptions
24
+ from ..hosted_services.catalog import (
25
+ _build_hosted_service_catalog,
26
+ _contribute_hosted_service_defaults,
27
+ )
28
+ from ..hosted_services.runtime import _HostedServiceCoordinator
29
+ from ..modularity import AppModule, ModuleDescriptor, ModuleRef
30
+ from ..modularity.discovery import _discover_types, _snapshot_scan_packages
31
+ from ..modularity.graph import _ModuleGraphBuilder
32
+ from ..notifications.catalog import _build_notification_catalog
33
+ from ..options.registry import _OptionsRegistry
34
+ from ..services.contribution import ServiceContributionRegistry
35
+ from ..services.runtime import _ServiceRuntimeBuilder
36
+ from ..settings.catalog import _build_setting_catalog, _contribute_setting_handler_defaults
37
+ from .build_spec import _ApplicationBuildSpec
38
+ from .composition import _CompositionLifecycle
39
+ from .runtime import Application
40
+ from .state import _ApplicationStateRoot
41
+
42
+
43
+ class ApplicationBuilder:
44
+ """以 startup Module 为唯一入口建立完整、隔离的 Application object graph。"""
45
+
46
+ __slots__ = (
47
+ "_base_path",
48
+ "_cli_args",
49
+ "_dotenv_path",
50
+ "_env_prefix",
51
+ "_environment",
52
+ "_explicit_overrides",
53
+ "_startup_module",
54
+ )
55
+
56
+ def __init__(
57
+ self,
58
+ startup_module: type[AppModule] | ModuleRef,
59
+ *,
60
+ environment: str,
61
+ base_path: Path,
62
+ env_prefix: str,
63
+ cli_args: tuple[str, ...] | list[str] | None = None,
64
+ dotenv_path: Path | None = None,
65
+ explicit_overrides: Mapping[str, object] | None = None,
66
+ ) -> None:
67
+ if cli_args is not None and (
68
+ not isinstance(cli_args, (tuple, list))
69
+ or any(not isinstance(arg, str) for arg in cli_args)
70
+ ):
71
+ raise TypeError("cli_args must be a sequence of strings")
72
+ self._cli_args = tuple(cli_args or ())
73
+ self._startup_module = startup_module
74
+ self._environment = _validate_environment(environment)
75
+ self._base_path = _validate_base_path(base_path)
76
+ self._env_prefix = _validate_env_prefix(env_prefix)
77
+ self._dotenv_path = _resolve_dotenv_path(
78
+ dotenv_path,
79
+ base_path=self._base_path,
80
+ )
81
+ self._explicit_overrides = _copy_explicit_overrides(explicit_overrides)
82
+
83
+ def build(self) -> Application:
84
+ registry = _ModuleGraphBuilder(self._startup_module).build()
85
+ scan_packages = _snapshot_scan_packages(registry)
86
+ discoveries = _discover_types(registry, scan_packages)
87
+ claims: dict[type[BaseModel], tuple[str, ModuleDescriptor]] = {}
88
+ pre_options_registry = _OptionsRegistry(kind="PreOptions", claims=claims)
89
+ options_registry = _OptionsRegistry(kind="Options", claims=claims)
90
+ permission_catalog = _build_permission_catalog(registry, discoveries)
91
+ cache_catalog = _build_cache_catalog(registry)
92
+ setting_catalog = _build_setting_catalog(registry, discoveries)
93
+ notification_catalog = _build_notification_catalog(registry)
94
+ background_worker_catalog = _build_background_worker_catalog(registry)
95
+ background_job_catalog = _build_background_job_catalog(registry)
96
+ hosted_service_catalog = _build_hosted_service_catalog(registry)
97
+ configuration = _ConfigurationComposer(
98
+ environment=self._environment,
99
+ base_path=self._base_path,
100
+ env_prefix=self._env_prefix,
101
+ dotenv_path=self._dotenv_path,
102
+ explicit_overrides=self._explicit_overrides,
103
+ cli_args=self._cli_args,
104
+ ).build(
105
+ tuple(
106
+ _ConfigurationDefaultContribution(
107
+ source_location=descriptor.declaration_source,
108
+ values=descriptor.module_type.configuration_defaults,
109
+ )
110
+ for descriptor in registry.dependency_order
111
+ )
112
+ )
113
+ root = _ApplicationStateRoot(
114
+ application_id=uuid4(),
115
+ registry=registry,
116
+ configuration=configuration,
117
+ pre_options_registry=pre_options_registry,
118
+ options_registry=options_registry,
119
+ service_registry=ServiceContributionRegistry(),
120
+ event_registry=EventContributionRegistry(),
121
+ permission_catalog=permission_catalog,
122
+ cache_catalog=cache_catalog,
123
+ setting_catalog=setting_catalog,
124
+ background_worker_catalog=background_worker_catalog,
125
+ background_job_catalog=background_job_catalog,
126
+ hosted_service_catalog=hosted_service_catalog,
127
+ journal=_DiagnosticJournal(),
128
+ completed_stages={descriptor.key: [] for descriptor in registry.dependency_order},
129
+ )
130
+ _CompositionLifecycle(root).run()
131
+ _contribute_hosted_service_defaults(hosted_service_catalog, root.service_registry)
132
+ _contribute_setting_handler_defaults(registry, root.service_registry)
133
+ root.invocation_runtime = _InvocationRuntime(
134
+ application_id=root.application_id,
135
+ state_getter=lambda: root.state,
136
+ )
137
+ root.background_coordinator = _BackgroundCoordinator(
138
+ worker_catalog=background_worker_catalog,
139
+ job_catalog=background_job_catalog,
140
+ options=root.require_options().get(BackgroundExecutionOptions),
141
+ invocation_runtime=root.require_invocation_runtime(),
142
+ build_spec=_ApplicationBuildSpec(
143
+ self._startup_module,
144
+ self._environment,
145
+ self._base_path,
146
+ self._env_prefix,
147
+ self._cli_args,
148
+ self._dotenv_path,
149
+ dict(self._explicit_overrides),
150
+ Path.cwd().resolve(),
151
+ ),
152
+ )
153
+ root.hosted_service_coordinator = _HostedServiceCoordinator(
154
+ catalog=hosted_service_catalog,
155
+ options=root.require_options().get(HostedServiceOptions),
156
+ state_getter=lambda: root.state,
157
+ )
158
+ service_runtime = _ServiceRuntimeBuilder(
159
+ registry,
160
+ root.service_registry,
161
+ root.event_registry,
162
+ scan_packages,
163
+ discoveries,
164
+ root.configuration,
165
+ root.require_options(),
166
+ root.require_invocation_runtime(),
167
+ permission_catalog,
168
+ cache_catalog,
169
+ setting_catalog,
170
+ notification_catalog,
171
+ background_worker_catalog,
172
+ background_job_catalog,
173
+ root.require_background_coordinator(),
174
+ hosted_service_catalog,
175
+ root.require_hosted_service_coordinator(),
176
+ ).build()
177
+ root.service_catalog = service_runtime.catalog
178
+ root.application_service_catalog = service_runtime.application_service_catalog
179
+ root.event_catalog = service_runtime.event_catalog
180
+ root.container = service_runtime.container
181
+ root.require_background_coordinator().attach_container(service_runtime.container)
182
+ root.require_hosted_service_coordinator().attach_container(
183
+ service_runtime.container,
184
+ service_runtime.application_service_catalog,
185
+ )
186
+ return Application(root)
187
+
188
+
189
+ def _validate_environment(environment: str) -> str:
190
+ if not isinstance(environment, str) or not environment:
191
+ raise TypeError("environment must be a non-empty string")
192
+ if environment != environment.strip() or any(
193
+ not (character.isalnum() or character in "_-") for character in environment
194
+ ):
195
+ raise ValueError("environment must contain only letters, digits, '_' or '-'")
196
+ return environment
197
+
198
+
199
+ def _validate_base_path(base_path: Path) -> Path:
200
+ if not isinstance(base_path, Path):
201
+ raise TypeError("base_path must be pathlib.Path")
202
+ resolved = base_path.resolve()
203
+ if not resolved.is_dir():
204
+ raise ValueError("base_path must be an existing directory")
205
+ return resolved
206
+
207
+
208
+ def _validate_env_prefix(env_prefix: str) -> str:
209
+ if not isinstance(env_prefix, str) or not env_prefix:
210
+ raise TypeError("env_prefix must be a non-empty string")
211
+ if not (env_prefix[0].isalpha() or env_prefix[0] == "_") or any(
212
+ not (character.isalnum() or character == "_") for character in env_prefix
213
+ ):
214
+ raise ValueError("env_prefix must be a valid environment-variable prefix")
215
+ return env_prefix
216
+
217
+
218
+ def _resolve_dotenv_path(
219
+ dotenv_path: Path | None,
220
+ *,
221
+ base_path: Path,
222
+ ) -> Path | None:
223
+ if dotenv_path is None:
224
+ return None
225
+ if not isinstance(dotenv_path, Path):
226
+ raise TypeError("dotenv_path must be pathlib.Path or None")
227
+ candidate = dotenv_path if dotenv_path.is_absolute() else base_path / dotenv_path
228
+ return candidate.resolve()
229
+
230
+
231
+ def _copy_explicit_overrides(
232
+ explicit_overrides: Mapping[str, object] | None,
233
+ ) -> dict[str, object]:
234
+ if explicit_overrides is None:
235
+ return {}
236
+ if not isinstance(explicit_overrides, Mapping):
237
+ raise TypeError("explicit_overrides must be a mapping")
238
+ try:
239
+ return {key: _copy_explicit_value(value) for key, value in explicit_overrides.items()}
240
+ except Exception as original_error:
241
+ raise TypeError("explicit_overrides must be defensively copyable") from original_error
242
+
243
+
244
+ def _copy_explicit_value(value: object) -> object:
245
+ # deepcopy 不支持 MappingProxyType;容器先按配置语义复制,再交给 source boundary 校验。
246
+ if isinstance(value, Mapping):
247
+ copied: object = {key: _copy_explicit_value(item) for key, item in value.items()}
248
+ elif isinstance(value, list):
249
+ copied = [_copy_explicit_value(item) for item in value]
250
+ elif isinstance(value, tuple):
251
+ copied = tuple(_copy_explicit_value(item) for item in value)
252
+ elif isinstance(value, set):
253
+ copied = {_copy_explicit_value(item) for item in value}
254
+ elif isinstance(value, frozenset):
255
+ copied = frozenset(_copy_explicit_value(item) for item in value)
256
+ elif isinstance(value, bytearray):
257
+ copied = bytearray(value)
258
+ else:
259
+ copied = copy.deepcopy(value)
260
+ return copied
@@ -0,0 +1,219 @@
1
+ """Synchronous Module composition stages 与 PreOptions/Options barriers。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Callable
7
+ from typing import cast
8
+
9
+ from ..auditing import AuditingOptions
10
+ from ..background_workers import BackgroundExecutionOptions
11
+ from ..diagnostics import EventStatus, LifecyclePhase, LifecycleStage
12
+ from ..errors import (
13
+ CompositionResourceError,
14
+ LifecycleInvocationError,
15
+ )
16
+ from ..hosted_services import HostedServiceOptions
17
+ from ..hosted_services.options import HOSTED_SERVICE_OPTIONS_SECTION
18
+ from ..lifecycle import ConfigureContext, PostConfigureContext, PreConfigureContext
19
+ from ..modularity import ModuleDescriptor
20
+ from ..unit_of_work import UnitOfWorkOptions
21
+ from .state import (
22
+ LIFECYCLE_ERROR_TYPE,
23
+ _ApplicationStateRoot,
24
+ _lifecycle_failure_message,
25
+ )
26
+
27
+
28
+ class _CompositionLifecycle:
29
+ """拥有一次全局 stage barrier 编排,不保存任何跨 Application 状态。"""
30
+
31
+ __slots__ = ("_root",)
32
+
33
+ def __init__(self, root: _ApplicationStateRoot) -> None:
34
+ self._root = root
35
+
36
+ def run(self) -> None:
37
+ # typed 调用是 lifecycle contract;不通过 getattr/string table 建第二份阶段权威。
38
+ for descriptor in self._root.registry.dependency_order:
39
+ self._invoke_pre_configure(descriptor)
40
+ self._root.pre_options = self._root.pre_options_registry.bind_pre_options(
41
+ self._root.configuration
42
+ )
43
+ for descriptor in self._root.registry.dependency_order:
44
+ self._invoke_configure(descriptor)
45
+ for descriptor in self._root.registry.dependency_order:
46
+ self._invoke_post_configure(descriptor)
47
+ options = self._root.options_registry
48
+ owner = self._root.registry.startup
49
+ # 常驻框架能力的默认声明只有此处负责;显式 Module 声明优先,默认值仍由 model 验证。
50
+ options.declare_default(BackgroundExecutionOptions, "background_execution", owner=owner)
51
+ options.declare_default(HostedServiceOptions, HOSTED_SERVICE_OPTIONS_SECTION, owner=owner)
52
+ options.declare_default(AuditingOptions, "auditing", owner=owner)
53
+ options.declare_default(UnitOfWorkOptions, "unit_of_work", owner=owner)
54
+ self._root.options = options.bind_options(self._root.configuration)
55
+ self._root.configuration = self._root.configuration._finalize(
56
+ sections=(
57
+ options.sections
58
+ | self._root.pre_options_registry.sections
59
+ | ({"settings"} if self._root.setting_catalog.definitions else set())
60
+ ),
61
+ secret_paths=options.secret_paths | self._root.pre_options_registry.secret_paths,
62
+ mapping_paths=lambda values, reference: (
63
+ options.mapping_paths(values, reference, self._root.options)
64
+ | self._root.pre_options_registry.mapping_paths(
65
+ values, reference, self._root.pre_options
66
+ )
67
+ ),
68
+ )
69
+
70
+ def _invoke_pre_configure(self, descriptor: ModuleDescriptor) -> None:
71
+ stage = LifecycleStage.PRE_CONFIGURE
72
+ editor = self._root.pre_options_registry.contributor(
73
+ owner=descriptor,
74
+ stage=stage,
75
+ )
76
+ context = PreConfigureContext(
77
+ application_id=self._root.application_id,
78
+ descriptor=descriptor,
79
+ stage=stage,
80
+ application=self._root.view(None),
81
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
82
+ configuration=self._root.configuration,
83
+ pre_configure=editor,
84
+ )
85
+ callback = cast(
86
+ Callable[[PreConfigureContext], object],
87
+ descriptor.instance.pre_configure,
88
+ )
89
+ try:
90
+ self._invoke(descriptor, stage, lambda: callback(context))
91
+ finally:
92
+ editor._close()
93
+
94
+ def _invoke_configure(self, descriptor: ModuleDescriptor) -> None:
95
+ stage = LifecycleStage.CONFIGURE
96
+ editor = self._root.options_registry.contributor(
97
+ owner=descriptor,
98
+ stage=stage,
99
+ )
100
+ services = self._root.service_registry.contributor(
101
+ descriptor=descriptor,
102
+ stage=stage,
103
+ )
104
+ events = self._root.event_registry.contributor(
105
+ descriptor=descriptor,
106
+ stage=stage,
107
+ )
108
+ context = ConfigureContext(
109
+ application_id=self._root.application_id,
110
+ descriptor=descriptor,
111
+ stage=stage,
112
+ application=self._root.view(None),
113
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
114
+ configuration=self._root.configuration,
115
+ pre_options=self._root.require_pre_options(),
116
+ configure=editor,
117
+ services=services,
118
+ events=events,
119
+ )
120
+ callback = cast(
121
+ Callable[[ConfigureContext], object],
122
+ descriptor.instance.configure,
123
+ )
124
+ try:
125
+ self._invoke(descriptor, stage, lambda: callback(context))
126
+ finally:
127
+ editor._close()
128
+ services._close()
129
+ events._close()
130
+
131
+ def _invoke_post_configure(self, descriptor: ModuleDescriptor) -> None:
132
+ stage = LifecycleStage.POST_CONFIGURE
133
+ editor = self._root.options_registry.contributor(
134
+ owner=descriptor,
135
+ stage=stage,
136
+ )
137
+ services = self._root.service_registry.contributor(
138
+ descriptor=descriptor,
139
+ stage=stage,
140
+ )
141
+ context = PostConfigureContext(
142
+ application_id=self._root.application_id,
143
+ descriptor=descriptor,
144
+ stage=stage,
145
+ application=self._root.view(None),
146
+ _emit=self._root.diagnostic_emitter(descriptor, stage),
147
+ configuration=self._root.configuration,
148
+ pre_options=self._root.require_pre_options(),
149
+ configure=editor,
150
+ services=services,
151
+ )
152
+ callback = cast(
153
+ Callable[[PostConfigureContext], object],
154
+ descriptor.instance.post_configure,
155
+ )
156
+ try:
157
+ self._invoke(descriptor, stage, lambda: callback(context))
158
+ finally:
159
+ editor._close()
160
+ services._close()
161
+
162
+ def _invoke(
163
+ self,
164
+ descriptor: ModuleDescriptor,
165
+ stage: LifecycleStage,
166
+ callback: Callable[[], object],
167
+ ) -> None:
168
+ subject = str(descriptor.key)
169
+ active_event = self._root.journal.start_event(
170
+ phase=LifecyclePhase.COMPOSITION,
171
+ stage=stage,
172
+ subject=subject,
173
+ source=descriptor.declaration_source,
174
+ )
175
+ try:
176
+ result = callback()
177
+ is_awaitable = inspect.isawaitable(result)
178
+ if inspect.iscoroutine(result):
179
+ result.close()
180
+ if result is not None:
181
+ raise CompositionResourceError(
182
+ module=descriptor.key,
183
+ stage=stage,
184
+ resource_kind=("awaitable" if is_awaitable else "non-None value"),
185
+ )
186
+ except Exception as original_error:
187
+ failure_id = self._root.journal.begin_failure(
188
+ error_type=LIFECYCLE_ERROR_TYPE,
189
+ phase=LifecyclePhase.COMPOSITION,
190
+ stage=stage,
191
+ subject=subject,
192
+ message=_lifecycle_failure_message(subject, stage, original_error),
193
+ source=descriptor.declaration_source,
194
+ dependency_path=tuple(str(key) for key in descriptor.dependency_path),
195
+ )
196
+ duration_ns = self._root.journal.finish_event(
197
+ active_event,
198
+ status=EventStatus.FAILED,
199
+ failure_id=failure_id,
200
+ )
201
+ self._root.journal.complete_failure(failure_id)
202
+ error = LifecycleInvocationError(
203
+ failure_id=failure_id,
204
+ module=descriptor.key,
205
+ phase=LifecyclePhase.COMPOSITION,
206
+ stage=stage,
207
+ dependency_path=descriptor.dependency_path,
208
+ duration_ns=duration_ns,
209
+ source=descriptor.declaration_source,
210
+ original_error=original_error,
211
+ )
212
+ raise error from original_error
213
+
214
+ self._root.journal.finish_event(
215
+ active_event,
216
+ status=EventStatus.SUCCEEDED,
217
+ failure_id=None,
218
+ )
219
+ self._root.completed_stages[descriptor.key].append(stage)