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,203 @@
1
+ """Application-owned 任务编排;目录提供任务事实,执行 owner 各自管理本地或子进程资源。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections import defaultdict
7
+ from collections.abc import Mapping
8
+
9
+ from dishka import AsyncContainer
10
+
11
+ from ..application.build_spec import _ApplicationBuildSpec
12
+ from ..application_services.invocation import _InvocationRuntime
13
+ from ..background_execution import (
14
+ BackgroundExecutionMode,
15
+ BackgroundTaskOptions,
16
+ BackgroundTaskSnapshot,
17
+ )
18
+ from ..background_execution.lifecycle import _BackgroundRuntimeFailure, _TaskExecution
19
+ from ..background_execution.local import _LocalTask
20
+ from ..background_execution.locks import _check_previous_owners
21
+ from ..background_execution.processes import _ProcessSlot
22
+ from ..background_jobs import BackgroundJobCatalog
23
+ from ..invocation import BackgroundSystemCaller
24
+ from .catalog import BackgroundWorkerCatalog
25
+ from .contracts import BackgroundExecutionOptions, BackgroundExecutionProfile
26
+ from .execution import _WorkerExecution
27
+
28
+
29
+ class _BackgroundCoordinator:
30
+ def __init__(
31
+ self,
32
+ *,
33
+ worker_catalog: BackgroundWorkerCatalog,
34
+ job_catalog: BackgroundJobCatalog,
35
+ options: BackgroundExecutionOptions,
36
+ invocation_runtime: _InvocationRuntime,
37
+ build_spec: _ApplicationBuildSpec,
38
+ ) -> None:
39
+ self._worker_catalog = worker_catalog
40
+ self._job_catalog = job_catalog
41
+ self._options = options
42
+ self._invocation_runtime = invocation_runtime
43
+ self._spec = build_spec
44
+ self._caller = BackgroundSystemCaller()
45
+ self._container: AsyncContainer | None = None
46
+ self._started = False
47
+ self._prepared = False
48
+ self._child = False
49
+ self._database_only = False
50
+ self._failures: asyncio.Queue[_BackgroundRuntimeFailure] = asyncio.Queue()
51
+ self._tasks: dict[str, _LocalTask] = {}
52
+ self._slots: dict[str, _ProcessSlot] = {}
53
+ self._task_slots: dict[str, _ProcessSlot] = {}
54
+ self._register_tasks(
55
+ "worker", {item.name for item in worker_catalog.definitions}, options.workers
56
+ )
57
+ self._register_tasks("job", {item.name for item in job_catalog.definitions}, options.jobs)
58
+ self._local = {
59
+ key
60
+ for key, task in self._tasks.items()
61
+ if task.options.mode is BackgroundExecutionMode.HOST
62
+ }
63
+ self._initial = {key for key in self._local if self._tasks[key].options.enabled}
64
+
65
+ def _register_tasks(
66
+ self, kind: str, names: set[str], configured: Mapping[str, BackgroundTaskOptions]
67
+ ) -> None:
68
+ unknown = set(configured) - names
69
+ if unknown:
70
+ raise ValueError(f"Unknown background {kind} configuration: {sorted(unknown)}")
71
+ for name in sorted(names):
72
+ key = f"{kind}:{name}"
73
+ options = configured.get(name, BackgroundTaskOptions())
74
+ self._tasks[key] = _LocalTask(
75
+ key,
76
+ options,
77
+ self._options.graceful_shutdown_timeout.total_seconds(),
78
+ self._failures.put_nowait,
79
+ )
80
+
81
+ @property
82
+ def profile(self) -> BackgroundExecutionProfile:
83
+ return BackgroundExecutionProfile.DISABLED if self._database_only else self._options.profile
84
+
85
+ def disable_for_database(self) -> None:
86
+ if self._started or self._prepared:
87
+ raise RuntimeError("Database execution must be selected before initialization")
88
+ self._database_only = True
89
+
90
+ @property
91
+ def system_caller(self) -> BackgroundSystemCaller:
92
+ return self._caller
93
+
94
+ @property
95
+ def has_runtime_failure_source(self) -> bool:
96
+ return any(
97
+ task.execution is not None and task.execution.wait_for_failure is not None
98
+ for key, task in self._tasks.items()
99
+ if key in self._local
100
+ )
101
+
102
+ @property
103
+ def local_job_names(self) -> tuple[str, ...]:
104
+ if self.profile is BackgroundExecutionProfile.DISABLED:
105
+ return ()
106
+ return tuple(
107
+ key.removeprefix("job:")
108
+ for key in self._local
109
+ if key.startswith("job:") and self._tasks[key].options.enabled
110
+ )
111
+
112
+ def select_child(
113
+ self, tasks: dict[str, BackgroundTaskOptions], initial: tuple[str, ...]
114
+ ) -> None:
115
+ if self._prepared or self._started:
116
+ raise RuntimeError("Child task assignment is frozen before startup")
117
+ for key, options in tasks.items():
118
+ if key not in self._tasks or self._tasks[key].options != options:
119
+ raise RuntimeError("Child task catalog/configuration differs from parent")
120
+ self._child = True
121
+ self._local = set(tasks)
122
+ self._initial = set(initial)
123
+
124
+ def prepare(self) -> None:
125
+ if self._prepared:
126
+ raise RuntimeError("Background coordinator is already prepared")
127
+ self._prepared = True
128
+ self._worker_catalog._assert_unchanged()
129
+ self._job_catalog._assert_unchanged()
130
+ if self._child or self.profile is BackgroundExecutionProfile.DISABLED:
131
+ return
132
+ groups: dict[str, dict[str, BackgroundTaskOptions]] = defaultdict(dict)
133
+ for key, task in self._tasks.items():
134
+ mode = task.options.mode
135
+ if mode is not BackgroundExecutionMode.HOST:
136
+ position = "shared" if mode is BackgroundExecutionMode.SHARED else key
137
+ groups[position][key] = task.options
138
+ if groups:
139
+ _check_previous_owners(self._spec.project, self._spec.environment)
140
+ for position, tasks in groups.items():
141
+ slot = _ProcessSlot(position, tasks, self._spec, self._options)
142
+ slot.reserve()
143
+ self._slots[position] = slot
144
+ self._task_slots.update({key: slot for key in tasks})
145
+
146
+ def attach_container(self, container: AsyncContainer) -> None:
147
+ if self._container is not None:
148
+ raise RuntimeError("Background coordinator container is already attached")
149
+ self._container = container
150
+ for definition in self._worker_catalog.definitions:
151
+ self._tasks[f"worker:{definition.name}"].execution = _WorkerExecution(
152
+ definition, container, self._invocation_runtime, self._caller
153
+ ).binding
154
+
155
+ def bind_durable(self, name: str, binding: _TaskExecution) -> None:
156
+ task = self._tasks[f"job:{name}"]
157
+ if self._started or task.execution is not None:
158
+ raise RuntimeError("Durable background task may only be bound once before start")
159
+ task.execution = binding
160
+
161
+ async def start(self) -> None:
162
+ if self._started:
163
+ raise RuntimeError("Background coordinator is already started")
164
+ self._started = True
165
+ self.prepare()
166
+ if self.profile is BackgroundExecutionProfile.DISABLED:
167
+ return
168
+ for key in self._tasks:
169
+ if key in self._initial:
170
+ await self._tasks[key].start()
171
+ for slot in self._slots.values():
172
+ await slot.start()
173
+
174
+ def snapshots(self) -> tuple[BackgroundTaskSnapshot, ...]:
175
+ remote = {item.id: item for slot in self._slots.values() for item in slot.snapshots()}
176
+ return tuple(
177
+ remote.get(key, task.snapshot())
178
+ for key, task in self._tasks.items()
179
+ if not self._child or key in self._local
180
+ )
181
+
182
+ def request(self, key: str, *, start: bool) -> BackgroundTaskSnapshot:
183
+ if not self._started or self.profile is BackgroundExecutionProfile.DISABLED:
184
+ raise ValueError("Background execution is not active")
185
+ if key not in self._tasks or (self._child and key not in self._local):
186
+ raise KeyError(key)
187
+ slot = self._task_slots.get(key)
188
+ if slot is not None:
189
+ return slot.request(key, start)
190
+ return self._tasks[key].request(start)
191
+
192
+ async def stop(self) -> None:
193
+ # 即使启动前的预占或 Module 初始化失败,也必须释放已取得的位置。
194
+ operations = [task.shutdown() for key, task in self._tasks.items() if key in self._local]
195
+ operations.extend(slot.shutdown() for slot in self._slots.values())
196
+ outcomes = await asyncio.gather(*operations, return_exceptions=True)
197
+ self._started = False
198
+ failures = tuple(item for item in outcomes if isinstance(item, BaseException))
199
+ if failures:
200
+ raise BaseExceptionGroup("Background runtime drain failed", failures)
201
+
202
+ async def wait_for_runtime_failure(self) -> _BackgroundRuntimeFailure:
203
+ return await self._failures.get()
@@ -0,0 +1,11 @@
1
+ from .catalog import CacheCatalog
2
+ from .contracts import CacheDefinition, DistributedCache
3
+ from .errors import CacheDefinitionError, DistributedCacheError
4
+
5
+ __all__ = (
6
+ "CacheCatalog",
7
+ "CacheDefinition",
8
+ "CacheDefinitionError",
9
+ "DistributedCache",
10
+ "DistributedCacheError",
11
+ )
@@ -0,0 +1,73 @@
1
+ """Cache metadata 的 Application-local 动态聚合与 immutable view。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator, Mapping
6
+ from types import MappingProxyType
7
+ from typing import Any
8
+
9
+ from ..modularity import ModuleRegistry
10
+ from .contracts import CacheDefinition
11
+ from .errors import CacheDefinitionError
12
+
13
+
14
+ class CacheCatalog:
15
+ __slots__ = ("_definitions",)
16
+
17
+ def __init__(self, definitions: tuple[CacheDefinition[Any], ...]) -> None:
18
+ by_name: dict[str, CacheDefinition[Any]] = {}
19
+ for definition in definitions:
20
+ if not isinstance(definition, CacheDefinition):
21
+ raise CacheDefinitionError(
22
+ cache=repr(definition),
23
+ reason="catalog entries must be CacheDefinition values",
24
+ )
25
+ if definition.name in by_name:
26
+ raise CacheDefinitionError(
27
+ cache=definition.name,
28
+ reason="cache is declared more than once",
29
+ )
30
+ by_name[definition.name] = definition
31
+ self._definitions: Mapping[str, CacheDefinition[Any]] = MappingProxyType(
32
+ dict(sorted(by_name.items()))
33
+ )
34
+
35
+ @property
36
+ def definitions(self) -> tuple[CacheDefinition[Any], ...]:
37
+ return tuple(self._definitions.values())
38
+
39
+ def __iter__(self) -> Iterator[str]:
40
+ return iter(self._definitions)
41
+
42
+ def get(self, name: str) -> CacheDefinition[Any] | None:
43
+ return self._definitions.get(name)
44
+
45
+ def owns(self, definition: CacheDefinition[Any]) -> bool:
46
+ return self._definitions.get(definition.name) is definition
47
+
48
+
49
+ def _build_cache_catalog(registry: ModuleRegistry) -> CacheCatalog:
50
+ definitions: list[CacheDefinition[Any]] = []
51
+ owners: dict[str, str] = {}
52
+ for descriptor in registry.dependency_order:
53
+ declared = descriptor.module_type.cache_definitions
54
+ if not isinstance(declared, tuple):
55
+ raise CacheDefinitionError(
56
+ cache=str(descriptor.key),
57
+ reason="Module cache_definitions must be an immutable tuple",
58
+ )
59
+ for definition in declared:
60
+ if not isinstance(definition, CacheDefinition):
61
+ raise CacheDefinitionError(
62
+ cache=str(descriptor.key),
63
+ reason="Module cache_definitions contains an invalid item",
64
+ )
65
+ previous = owners.get(definition.name)
66
+ if previous is not None:
67
+ raise CacheDefinitionError(
68
+ cache=definition.name,
69
+ reason=f"cache is already owned by {previous}",
70
+ )
71
+ owners[definition.name] = str(descriptor.key)
72
+ definitions.append(definition)
73
+ return CacheCatalog(tuple(definitions))
@@ -0,0 +1,73 @@
1
+ """强制 TTL 的 typed cache definition 与最小读写契约。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import timedelta
6
+ from typing import Any, Generic, Protocol, TypeVar
7
+
8
+ from pydantic import TypeAdapter
9
+
10
+ from .errors import CacheDefinitionError
11
+
12
+ _T = TypeVar("_T")
13
+
14
+
15
+ class CacheDefinition(Generic[_T]):
16
+ __slots__ = ("_adapter", "_name", "_ttl", "_value_type")
17
+
18
+ def __init__(self, name: str, value_type: type[_T], *, ttl: timedelta) -> None:
19
+ if (
20
+ not isinstance(name, str)
21
+ or not name
22
+ or name != name.strip()
23
+ or name.startswith(":")
24
+ or name.endswith(":")
25
+ or "::" in name
26
+ # ':' 是 Redis key 的结构分隔符,definition name 不得制造跨 cache key 歧义。
27
+ or any(not (item.isalnum() or item in "._-") for item in name)
28
+ ):
29
+ raise CacheDefinitionError(
30
+ cache=name if isinstance(name, str) else repr(name),
31
+ reason="name must be a trimmed cache identifier",
32
+ )
33
+ if not isinstance(value_type, type):
34
+ raise CacheDefinitionError(cache=name, reason="value_type must be a runtime type")
35
+ if not isinstance(ttl, timedelta) or ttl.total_seconds() <= 0:
36
+ raise CacheDefinitionError(cache=name, reason="ttl must be a positive timedelta")
37
+ if ttl.total_seconds() > 2_147_483_647:
38
+ raise CacheDefinitionError(cache=name, reason="ttl exceeds the supported Redis range")
39
+ self._name = name
40
+ self._value_type = value_type
41
+ self._ttl = ttl
42
+ self._adapter: TypeAdapter[_T] = TypeAdapter(value_type)
43
+
44
+ @property
45
+ def name(self) -> str:
46
+ return self._name
47
+
48
+ @property
49
+ def value_type(self) -> type[_T]:
50
+ return self._value_type
51
+
52
+ @property
53
+ def ttl(self) -> timedelta:
54
+ return self._ttl
55
+
56
+ def __repr__(self) -> str:
57
+ return (
58
+ f"CacheDefinition(name={self.name!r}, value_type={self.value_type!r}, ttl={self.ttl!r})"
59
+ )
60
+
61
+ def _serialize(self, value: _T) -> bytes:
62
+ return self._adapter.dump_json(self._adapter.validate_python(value))
63
+
64
+ def _deserialize(self, value: bytes | str) -> _T:
65
+ return self._adapter.validate_json(value)
66
+
67
+
68
+ class DistributedCache(Protocol):
69
+ async def get(self, definition: CacheDefinition[_T], key: str) -> _T | None: ...
70
+
71
+ async def set(self, definition: CacheDefinition[_T], key: str, value: _T) -> None: ...
72
+
73
+ async def remove(self, definition: CacheDefinition[Any], key: str) -> None: ...
@@ -0,0 +1,20 @@
1
+ """Typed distributed cache definition 与 serialization failures。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..errors.base import FrameworkError
6
+
7
+
8
+ class CacheDefinitionError(FrameworkError):
9
+ def __init__(self, *, cache: str, reason: str) -> None:
10
+ self.cache = cache
11
+ self.reason = reason
12
+ super().__init__(f"Invalid cache {cache!r}: {reason}")
13
+
14
+
15
+ class DistributedCacheError(FrameworkError):
16
+ def __init__(self, *, operation: str, cache: str, error_type: str) -> None:
17
+ self.operation = operation
18
+ self.cache = cache
19
+ self.error_type = error_type
20
+ super().__init__(f"Distributed cache {operation} failed for {cache!r} with {error_type}")
@@ -0,0 +1,87 @@
1
+ """pp 的轻量 argparse 入口;只有生成命令才加载 Developer Kit。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import os
8
+ import subprocess
9
+ from collections.abc import Sequence
10
+ from enum import Enum
11
+ from pathlib import Path
12
+
13
+ from ..hosting import ENVIRONMENT_VARIABLE as ENVIRONMENT_VARIABLE
14
+ from .errors import CommandError
15
+
16
+
17
+ class _Command(str, Enum):
18
+ NEW = "new"
19
+ ADD = "add"
20
+ DEV = "dev"
21
+ DEV_INIT = "dev-init"
22
+ DB = "db"
23
+
24
+
25
+ def _parser() -> argparse.ArgumentParser:
26
+ parser = argparse.ArgumentParser(prog="pp")
27
+ commands = parser.add_subparsers(dest="command", required=True)
28
+ new = commands.add_parser(_Command.NEW.value, help="Create a project from bundled templates")
29
+ new.add_argument("name")
30
+ new.add_argument("--framework-wheel", type=Path, help="Validate with a local framework wheel")
31
+ new.add_argument("--dry-run", action="store_true")
32
+ add = commands.add_parser(_Command.ADD.value).add_subparsers(dest="kind", required=True)
33
+ module = add.add_parser("module")
34
+ module.add_argument("name")
35
+ module.add_argument("--dry-run", action="store_true")
36
+ dev = commands.add_parser(_Command.DEV.value)
37
+ dev.add_argument("--host", default="127.0.0.1")
38
+ dev.add_argument("--port", type=int, default=8000)
39
+ for command in (_Command.DEV_INIT, _Command.DB):
40
+ action = commands.add_parser(command.value)
41
+ action.add_argument(
42
+ "--environment", default=os.environ.get(ENVIRONMENT_VARIABLE, "development")
43
+ )
44
+ if command is _Command.DB:
45
+ action.add_argument("operation", choices=("revision", "upgrade", "status", "seed"))
46
+ action.add_argument("--module", required=True)
47
+ return parser
48
+
49
+
50
+ def main(argv: Sequence[str] | None = None) -> int:
51
+ parser = _parser()
52
+ args = parser.parse_args(argv)
53
+ try:
54
+ return _execute(args)
55
+ except CommandError as error:
56
+ parser.exit(2, f"pp: {error}\n")
57
+ except subprocess.CalledProcessError as error:
58
+ parser.exit(
59
+ error.returncode, "pp: external command failed; diagnostic files were retained\n"
60
+ )
61
+
62
+
63
+ def _execute(args: argparse.Namespace) -> int:
64
+ command = _Command(args.command)
65
+ if command in (_Command.NEW, _Command.ADD):
66
+ try:
67
+ from ..developer_kit.generation import add_module, new_project
68
+ except ModuleNotFoundError as error:
69
+ if error.name not in {"cookiecutter", "libcst"}:
70
+ raise
71
+ raise CommandError(
72
+ "Install python-platform[developer-kit] to use generation commands"
73
+ ) from None
74
+ if command is _Command.NEW:
75
+ new_project(args.name, args.framework_wheel, args.dry_run)
76
+ else:
77
+ add_module(args.name, args.dry_run)
78
+ else:
79
+ from .runtime import database, dev_init, development_server
80
+
81
+ if command is _Command.DEV:
82
+ development_server(args.host, args.port)
83
+ elif command is _Command.DEV_INIT:
84
+ asyncio.run(dev_init(args.environment))
85
+ else:
86
+ asyncio.run(database(args.operation, args.environment, args.module))
87
+ return 0
@@ -0,0 +1,5 @@
1
+ """CLI 可直接展示的操作诊断;依赖命令的退出码由调用方保留。"""
2
+
3
+
4
+ class CommandError(Exception):
5
+ pass
@@ -0,0 +1,69 @@
1
+ """当前项目的标准 pyproject/distribution/entry points,禁止第二份 Host 清单。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.metadata as metadata
6
+ import tomllib
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from ..application import Application
12
+ from ..modularity import AppModule
13
+ from .errors import CommandError
14
+
15
+ HOST_ENTRY_POINT_GROUP = "python_platform.hosts"
16
+ MODULE_ENTRY_POINT_GROUP = "python_platform.modules"
17
+
18
+
19
+ def read_project(path: Path) -> dict[str, Any]:
20
+ try:
21
+ with path.open("rb") as stream:
22
+ return tomllib.load(stream)
23
+ except (OSError, ValueError) as error:
24
+ raise CommandError(
25
+ f"Cannot read project metadata at {path}: {type(error).__name__}"
26
+ ) from None
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class Project:
31
+ root: Path
32
+ name: str
33
+
34
+ @classmethod
35
+ def current(cls) -> Project:
36
+ root = Path.cwd().resolve()
37
+ project = read_project(root / "pyproject.toml").get("project", {})
38
+ name = project.get("name") if isinstance(project, dict) else None
39
+ if not isinstance(name, str) or not name:
40
+ raise CommandError("Current pyproject.toml must declare project.name")
41
+ return cls(root, name)
42
+
43
+ def host(self, name: str) -> metadata.EntryPoint:
44
+ try:
45
+ distribution = metadata.distribution(self.name)
46
+ except metadata.PackageNotFoundError:
47
+ raise CommandError(
48
+ "Install the current project with uv sync, then use uv run pp"
49
+ ) from None
50
+ matches = tuple(
51
+ entry
52
+ for entry in distribution.entry_points
53
+ if entry.group == HOST_ENTRY_POINT_GROUP and entry.name == name
54
+ )
55
+ if len(matches) != 1:
56
+ raise CommandError(f"Project must declare exactly one {HOST_ENTRY_POINT_GROUP}:{name}")
57
+ return matches[0]
58
+
59
+ def module(self, name: str, application: Application) -> type[AppModule]:
60
+ # entry point 只命名已在当前图中的正式 Module;不导入未启用的 optional provider。
61
+ live = {str(item.key): item.module_type for item in application.modules.dependency_order}
62
+ matches = {
63
+ entry.value
64
+ for entry in metadata.entry_points(group=MODULE_ENTRY_POINT_GROUP)
65
+ if entry.name == name and entry.value in live
66
+ }
67
+ if len(matches) != 1:
68
+ raise CommandError(f"Module alias {name!r} must select exactly one live Module")
69
+ return live[matches.pop()]
@@ -0,0 +1,68 @@
1
+ """项目运行命令只编排正式 Host、迁移与 seed,不拥有业务初始化规则。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+
8
+ from . import ENVIRONMENT_VARIABLE
9
+ from .errors import CommandError
10
+ from .project import Project
11
+
12
+
13
+ async def database(operation: str, environment: str, module: str) -> None:
14
+ from ..application import Application
15
+ from ..application_services.seeding import DataSeeder
16
+ from ..sqlalchemy import SqlAlchemyMigrator
17
+
18
+ project = Project.current()
19
+ factory = project.host("application").load()
20
+ application = factory(environment=environment)
21
+ if not isinstance(application, Application):
22
+ raise CommandError("The application entry point must return Application")
23
+ try:
24
+ selected = project.module(module, application)
25
+ migrator = SqlAlchemyMigrator(application)
26
+ if operation == "revision":
27
+ await migrator.revision(selected)
28
+ elif operation == "upgrade":
29
+ await migrator.upgrade_heads(module=selected)
30
+ elif operation == "status":
31
+ await migrator.check_current_heads(module=selected)
32
+ elif operation == "seed":
33
+ await migrator.check_current_heads(module=selected)
34
+ await application._start(run_services=False)
35
+ await DataSeeder(application).seed_all(module=selected)
36
+ else:
37
+ raise CommandError(f"Unknown database operation: {operation}")
38
+ finally:
39
+ await application.stop()
40
+ print(f"Database {operation} completed")
41
+
42
+
43
+ async def dev_init(environment: str) -> None:
44
+ if environment != "development":
45
+ raise CommandError("dev-init is only available in the development environment")
46
+ project = Project.current()
47
+ subprocess.run(
48
+ ["docker", "compose", "-f", str(project.root / "compose.dev.yaml"), "up", "-d", "--wait"],
49
+ cwd=project.root,
50
+ check=True,
51
+ )
52
+
53
+
54
+ def development_server(host: str, port: int) -> None:
55
+ from ..fastapi.server import _run_host
56
+
57
+ project = Project.current()
58
+ entry = project.host("web")
59
+ os.environ[ENVIRONMENT_VARIABLE] = "development"
60
+ # 原生 access handler 不传播到 Host 的安全 formatter,不能记录含凭据的 query。
61
+ _run_host(
62
+ entry.value,
63
+ host=host,
64
+ port=port,
65
+ reload=True,
66
+ project=project.root,
67
+ environment="development",
68
+ )
@@ -0,0 +1,21 @@
1
+ """Configuration public contracts and Application-internal composition owners."""
2
+
3
+ from .contracts import (
4
+ ConfigurationContribution,
5
+ ConfigurationProvenance,
6
+ ConfigurationSource,
7
+ ConfigurationSourceDiagnostic,
8
+ ConfigurationSourceStatus,
9
+ ConfigurationValueDiagnostic,
10
+ )
11
+ from .root import ConfigurationRoot
12
+
13
+ __all__ = (
14
+ "ConfigurationContribution",
15
+ "ConfigurationProvenance",
16
+ "ConfigurationRoot",
17
+ "ConfigurationSource",
18
+ "ConfigurationSourceDiagnostic",
19
+ "ConfigurationSourceStatus",
20
+ "ConfigurationValueDiagnostic",
21
+ )