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,171 @@
1
+ """调用签名的 DI 分类与原生注入包装;作用域创建仍归各运行入口。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Awaitable, Callable, Mapping
7
+ from dataclasses import dataclass
8
+ from types import MappingProxyType, SimpleNamespace
9
+ from typing import Annotated, Any, TypeVar, get_args, get_origin, get_type_hints
10
+
11
+ from dishka import AsyncContainer, BaseScope, DependencyKey
12
+ from dishka.integrations.base import default_parse_dependency, wrap_injection
13
+
14
+ _R = TypeVar("_R")
15
+
16
+
17
+ def _resolved_parameter_hints(
18
+ function: Callable[..., object],
19
+ annotations: Mapping[str, object],
20
+ ) -> dict[str, Any]:
21
+ # 入口只解释自己拥有的参数;业务 payload 的局部引用不属于 DI 参数契约。
22
+ try:
23
+ return get_type_hints(
24
+ SimpleNamespace(__annotations__=dict(annotations)),
25
+ globalns=function.__globals__,
26
+ include_extras=True,
27
+ )
28
+ except (NameError, TypeError) as error:
29
+ raise TypeError(f"{function.__qualname__}: cannot resolve callable annotations") from error
30
+
31
+
32
+ def _dependency_key(
33
+ parameter: inspect.Parameter,
34
+ hint: object,
35
+ container: AsyncContainer,
36
+ scope: BaseScope,
37
+ ) -> DependencyKey | None:
38
+ explicit = default_parse_dependency(parameter, hint)
39
+ hint_type = get_args(hint)[0] if get_origin(hint) is Annotated else hint
40
+ key = explicit or DependencyKey(hint_type, "")
41
+ root = container
42
+ while root.parent_container is not None:
43
+ root = root.parent_container
44
+ registry = root.registry
45
+ while True:
46
+ factory = registry.get_factory(key)
47
+ if factory is not None:
48
+ if registry.scope > scope:
49
+ raise TypeError(
50
+ f"{parameter.name}: dependency {key} is unavailable in {scope.name}"
51
+ )
52
+ return key
53
+ if registry.child_registry is None:
54
+ break
55
+ registry = registry.child_registry
56
+ if explicit is not None:
57
+ raise TypeError(f"{parameter.name}: explicitly requested dependency {key} is unregistered")
58
+ return None
59
+
60
+
61
+ @dataclass(frozen=True, slots=True)
62
+ class _CallablePlan:
63
+ """冻结一次签名解释;只存契约事实,不缓存容器或解析出的服务实例。"""
64
+
65
+ signature: inspect.Signature
66
+ dependencies: Mapping[str, DependencyKey]
67
+
68
+ @classmethod
69
+ def compile(
70
+ cls,
71
+ function: Callable[..., object],
72
+ container: AsyncContainer,
73
+ scope: BaseScope,
74
+ *,
75
+ business_parameters: int = 0,
76
+ ) -> _CallablePlan:
77
+ if not inspect.iscoroutinefunction(function):
78
+ raise TypeError("Application callable must be an async function or bound async method")
79
+ signature = inspect.signature(function)
80
+ annotations = {
81
+ name: parameter.annotation
82
+ for index, (name, parameter) in enumerate(signature.parameters.items())
83
+ if index >= business_parameters and parameter.annotation is not inspect.Parameter.empty
84
+ }
85
+ hints = _resolved_parameter_hints(function, annotations)
86
+ dependencies: dict[str, DependencyKey] = {}
87
+ business: list[inspect.Parameter] = []
88
+ for index, parameter in enumerate(signature.parameters.values()):
89
+ if parameter.kind in (parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD):
90
+ raise TypeError(
91
+ f"{function.__qualname__}.{parameter.name}: "
92
+ "variadic entry parameters are unsupported"
93
+ )
94
+ key = (
95
+ None
96
+ if index < business_parameters
97
+ else _dependency_key(
98
+ parameter,
99
+ hints.get(parameter.name, parameter.annotation),
100
+ container,
101
+ scope,
102
+ )
103
+ )
104
+ if key is None:
105
+ business.append(parameter)
106
+ elif parameter.kind is parameter.POSITIONAL_ONLY:
107
+ raise TypeError(
108
+ f"{function.__qualname__}.{parameter.name}: "
109
+ "DI parameter cannot be positional-only"
110
+ )
111
+ else:
112
+ dependencies[parameter.name] = key
113
+ return cls(signature.replace(parameters=business), MappingProxyType(dependencies))
114
+
115
+ async def invoke(
116
+ self,
117
+ function: Callable[..., Awaitable[_R]],
118
+ container: AsyncContainer,
119
+ args: tuple[object, ...],
120
+ kwargs: dict[str, object],
121
+ ) -> _R:
122
+ if self.dependencies.keys() & kwargs.keys():
123
+ raise TypeError("caller cannot override automatically injected parameters")
124
+ # 先验证全部业务实参,再解析任何依赖,避免错误调用触发 resource factory。
125
+ bound = self.signature.bind(*args, **kwargs)
126
+ bound.apply_defaults()
127
+
128
+ def parse(parameter: inspect.Parameter, hint: Any) -> DependencyKey | None:
129
+ return self.dependencies.get(parameter.name)
130
+
131
+ async def target(*args: object, **kwargs: object) -> _R:
132
+ return await function(*args, **kwargs)
133
+
134
+ # 原函数可能被多个 Application 共享;只给当前调用的 native wrapper 提供签名。
135
+ target.__signature__ = inspect.signature(function) # type: ignore[attr-defined]
136
+ target.__annotations__ = {}
137
+ injected = wrap_injection(
138
+ func=target,
139
+ container_getter=lambda args, kwargs: container,
140
+ is_async=True,
141
+ parse_dependency=parse,
142
+ )
143
+ # DI 参数从签名移除后位置会变化;普通业务参数按名称交给原函数。
144
+ positional = tuple(
145
+ bound.arguments[name]
146
+ for name, parameter in self.signature.parameters.items()
147
+ if parameter.kind is parameter.POSITIONAL_ONLY
148
+ )
149
+ named = {
150
+ name: value
151
+ for name, value in bound.arguments.items()
152
+ if self.signature.parameters[name].kind is not inspect.Parameter.POSITIONAL_ONLY
153
+ }
154
+ return await injected(*positional, **named)
155
+
156
+
157
+ async def _call_in_scope(
158
+ function: Callable[..., Awaitable[_R]],
159
+ container: AsyncContainer,
160
+ *args: object,
161
+ **kwargs: object,
162
+ ) -> _R:
163
+ # handler 的既有 payload/context 是入口契约,即使其类型也可解析也不能被 DI 替换。
164
+ from ..application_services.invocation import _InvocationRuntime
165
+
166
+ plan = _CallablePlan.compile(
167
+ function, container, container.scope, business_parameters=len(args)
168
+ )
169
+ runtime = await container.get(_InvocationRuntime)
170
+ with runtime.call_scope(container):
171
+ return await plan.invoke(function, container, args, kwargs)
@@ -0,0 +1,33 @@
1
+ """跨 Application Service、Event 与 persistence 共享的只读 invocation facts。"""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TypeGuard
5
+ from uuid import UUID
6
+
7
+ _CORRELATION_ID_MAX_LENGTH = 128
8
+
9
+
10
+ def _is_safe_correlation_id(value: object) -> TypeGuard[str]:
11
+ """所有入口共用同一 correlation 标识安全契约。"""
12
+
13
+ return (
14
+ isinstance(value, str)
15
+ and bool(value)
16
+ and value == value.strip()
17
+ and len(value) <= _CORRELATION_ID_MAX_LENGTH
18
+ and value.isascii()
19
+ and value.isprintable()
20
+ )
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class ApplicationInvocationContext:
25
+ application_id: UUID
26
+ invocation_id: UUID
27
+ parent_invocation_id: UUID | None
28
+ connection_name: str
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class BackgroundSystemCaller:
33
+ """只以 type 表达 framework system identity,不携带用户授权事实。"""
@@ -0,0 +1,54 @@
1
+ """已登记运行入口的构建期签名验证,读取最终原生图而不实例化处理器。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable, Mapping
6
+ from typing import cast
7
+
8
+ from dishka import AsyncContainer, BaseScope, Scope
9
+
10
+ from ..background_jobs import BackgroundJobCatalog
11
+ from ..background_workers import BackgroundWorkerCatalog
12
+ from ..events import LocalEventCatalog, LocalEventPhase
13
+ from .callables import _CallablePlan
14
+
15
+
16
+ def _validate_callable_entries(
17
+ container: AsyncContainer,
18
+ jobs: BackgroundJobCatalog,
19
+ workers: BackgroundWorkerCatalog,
20
+ events: LocalEventCatalog,
21
+ *,
22
+ event_implementations: Mapping[type[object], type[object]],
23
+ ) -> None:
24
+ for job in jobs.definitions:
25
+ _validate_entry(container, job.handler_type, "execute", 2, Scope.ACTION)
26
+ for worker in workers.definitions:
27
+ _validate_entry(container, worker.worker_type, "run_iteration", 1, Scope.ACTION)
28
+ for subscription in events.subscriptions:
29
+ scope = Scope.ACTION if subscription.phase is LocalEventPhase.DOMAIN else Scope.REQUEST
30
+ target = subscription.handler_type
31
+ if isinstance(target, type):
32
+ target = event_implementations[target]
33
+ _validate_entry(container, target, "handle", 1, scope)
34
+
35
+
36
+ def _validate_entry(
37
+ container: AsyncContainer,
38
+ target: type[object] | Callable[..., Awaitable[None]],
39
+ method_name: str,
40
+ business_parameters: int,
41
+ scope: BaseScope,
42
+ ) -> None:
43
+ if isinstance(target, type):
44
+ function = cast(Callable[..., Awaitable[None]], getattr(target, method_name))
45
+ business_parameters += 1 # 未绑定方法的 self 也是调用入口负责的参数。
46
+ else:
47
+ function = target
48
+ plan = _CallablePlan.compile(
49
+ function, container, scope, business_parameters=business_parameters
50
+ )
51
+ try:
52
+ plan.signature.bind(*(object() for _ in range(business_parameters)))
53
+ except TypeError as error:
54
+ raise TypeError(f"{function.__qualname__}: unresolved entry parameters: {error}") from error
@@ -0,0 +1,48 @@
1
+ """Application 与 HostedService 共用的函数入口作用域和调用许可。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable
6
+ from typing import TYPE_CHECKING, TypeVar
7
+
8
+ from dishka import AsyncContainer, Scope
9
+
10
+ from ..application_services.errors import ApplicationInvocationRejectedError
11
+ from ..authorization import CurrentUser
12
+ from .callables import _CallablePlan
13
+
14
+ if TYPE_CHECKING:
15
+ from ..application_services.invocation import _InvocationRuntime
16
+
17
+ _R = TypeVar("_R")
18
+
19
+
20
+ async def _invoke_callable(
21
+ container: AsyncContainer,
22
+ runtime: _InvocationRuntime,
23
+ current_user: CurrentUser,
24
+ correlation_id: str,
25
+ function: Callable[..., Awaitable[_R]],
26
+ args: tuple[object, ...],
27
+ kwargs: dict[str, object],
28
+ ) -> _R:
29
+ async with runtime.outer_lease():
30
+ ambient = runtime.current()
31
+ existing = ambient.action_container if ambient is not None else runtime.current_call_scope()
32
+ if existing is not None:
33
+ # REQUEST 已缓存的 CurrentUser 必须与授权/审计读取的身份一致。
34
+ # 同一调用作用域内不得通过 call_as 改变身份或污染外层缓存。
35
+ if current_user != runtime.current_user():
36
+ raise ApplicationInvocationRejectedError(
37
+ reason="cannot switch caller identity inside an existing invocation scope",
38
+ )
39
+ plan = _CallablePlan.compile(function, existing, existing.scope)
40
+ return await plan.invoke(function, existing, args, kwargs)
41
+ token = runtime.bind_caller(current_user, correlation_id)
42
+ try:
43
+ plan = _CallablePlan.compile(function, container, Scope.REQUEST)
44
+ async with container(scope=Scope.REQUEST) as request:
45
+ with runtime.call_scope(request):
46
+ return await plan.invoke(function, request, args, kwargs)
47
+ finally:
48
+ runtime.reset_caller(token)
@@ -0,0 +1,184 @@
1
+ """正式 Job/Worker/Handler 的扩展链;编译规则复用服务入口的同一权威。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable, Mapping
6
+ from dataclasses import dataclass, replace
7
+ from types import MappingProxyType
8
+ from typing import cast
9
+
10
+ from dishka import AsyncContainer, Scope
11
+
12
+ from ..application_services.catalog import (
13
+ ApplicationServiceMethod,
14
+ _match_interceptors,
15
+ _method_source,
16
+ )
17
+ from ..application_services.execution import _InterceptorPipeline
18
+ from ..application_services.interceptors import (
19
+ InterceptionStage,
20
+ InterceptorRegistration,
21
+ _InterceptorRule,
22
+ )
23
+ from ..application_services.invocation import _InvocationRuntime
24
+ from ..events.catalog import LocalEventCatalog
25
+ from ..events.contracts import LocalEventPhase
26
+ from ..modularity import ModuleKey, ModuleRegistry
27
+ from ..unit_of_work.options import _UNIT_OF_WORK_ATTRIBUTE, UnitOfWorkDeclaration, _declaration
28
+ from .callables import _call_in_scope
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class _CompiledEntry:
33
+ descriptor: ApplicationServiceMethod
34
+ steps: Mapping[InterceptionStage, tuple[InterceptorRegistration, ...]]
35
+ outer_scope: Scope
36
+ method_scope: Scope
37
+
38
+
39
+ class _EntryInterceptionCatalog:
40
+ def __init__(
41
+ self,
42
+ registry: ModuleRegistry,
43
+ events: LocalEventCatalog,
44
+ rules: tuple[_InterceptorRule, ...],
45
+ ) -> None:
46
+ entries: dict[tuple[object, str], _CompiledEntry] = {}
47
+ for module in registry.dependency_order:
48
+ for worker in module.module_type.background_worker_definitions:
49
+ self._add(entries, worker.worker_type, "run_iteration", module.key, rules, True)
50
+ for job in module.module_type.background_job_definitions:
51
+ self._add(entries, job.handler_type, "execute", module.key, rules, False)
52
+ for subscription in events.subscriptions:
53
+ scope = Scope.ACTION if subscription.phase is LocalEventPhase.DOMAIN else Scope.REQUEST
54
+ self._add(
55
+ entries,
56
+ subscription.handler_type,
57
+ "handle",
58
+ subscription.owner,
59
+ rules,
60
+ False,
61
+ outer_scope=scope,
62
+ method_scope=scope,
63
+ )
64
+ self._entries: Mapping[tuple[object, str], _CompiledEntry] = MappingProxyType(entries)
65
+
66
+ @property
67
+ def interceptor_scopes(self) -> tuple[tuple[InterceptorRegistration, Scope], ...]:
68
+ return tuple(
69
+ (
70
+ registration,
71
+ entry.method_scope
72
+ if registration.before is InterceptionStage.METHOD
73
+ else entry.outer_scope,
74
+ )
75
+ for entry in self._entries.values()
76
+ for registration in entry.descriptor.interceptors
77
+ )
78
+
79
+ @staticmethod
80
+ def _add(
81
+ entries: dict[tuple[object, str], _CompiledEntry],
82
+ target: object,
83
+ name: str,
84
+ owner: ModuleKey,
85
+ rules: tuple[_InterceptorRule, ...],
86
+ worker: bool,
87
+ *,
88
+ outer_scope: Scope = Scope.REQUEST,
89
+ method_scope: Scope = Scope.ACTION,
90
+ ) -> None:
91
+ if (target, name) in entries:
92
+ current = entries[target, name]
93
+ # 同一 handler 可订阅两个 phase;构建时使用所有实际入口的最窄解析边界。
94
+ entries[target, name] = replace(
95
+ current,
96
+ outer_scope=min(current.outer_scope, outer_scope),
97
+ method_scope=min(current.method_scope, method_scope),
98
+ )
99
+ return
100
+ function = getattr(target, name) if isinstance(target, type) else target
101
+ implementation = target if isinstance(target, type) else type(target)
102
+ declaration = _declaration(implementation, function)
103
+ if worker and not any(
104
+ hasattr(item, _UNIT_OF_WORK_ATTRIBUTE) for item in (implementation, function)
105
+ ):
106
+ declaration = UnitOfWorkDeclaration(disabled=True)
107
+ source = _method_source(function, owner)
108
+ descriptor = ApplicationServiceMethod(
109
+ name=name,
110
+ source=source,
111
+ requires_authenticated_user=False,
112
+ permissions=(),
113
+ allows_anonymous=True,
114
+ service=implementation,
115
+ implementation=implementation,
116
+ method=cast(Callable[..., Awaitable[object]], function),
117
+ validation_enabled=True,
118
+ auditing_enabled=False,
119
+ policy_sources=(source,),
120
+ unit_of_work=declaration,
121
+ )
122
+ descriptor = replace(descriptor, interceptors=_match_interceptors(descriptor, rules))
123
+ entries[target, name] = _CompiledEntry(
124
+ descriptor,
125
+ MappingProxyType(
126
+ {
127
+ stage: tuple(item for item in descriptor.interceptors if item.before is stage)
128
+ for stage in InterceptionStage
129
+ }
130
+ ),
131
+ outer_scope,
132
+ method_scope,
133
+ )
134
+
135
+ async def invoke(
136
+ self,
137
+ runtime: _InvocationRuntime,
138
+ container: AsyncContainer,
139
+ target: object,
140
+ name: str,
141
+ args: tuple[object, ...],
142
+ *,
143
+ connection_name: str | None = None,
144
+ ) -> object:
145
+ entry = self._entries[target, name]
146
+ pipeline = _InterceptorPipeline(entry.descriptor, entry.steps, args, {})
147
+
148
+ async def call(scope: AsyncContainer) -> object:
149
+ instance: object = (
150
+ await scope.get(cast(type[object], target)) if isinstance(target, type) else None
151
+ )
152
+ function = getattr(instance, name) if instance is not None else target
153
+ return await _call_in_scope(
154
+ cast(Callable[..., Awaitable[object]], function),
155
+ scope,
156
+ *pipeline.arguments.args,
157
+ **pipeline.arguments.kwargs,
158
+ )
159
+
160
+ async def method(scope: AsyncContainer) -> object:
161
+ return await pipeline.run(InterceptionStage.METHOD, scope, lambda: call(scope))
162
+
163
+ async def boundary() -> object:
164
+ if connection_name is None:
165
+ # Domain 参与原事务;Post-Commit 自身不复活旧 UoW,服务调用仍会建立新边界。
166
+ return await method(container)
167
+ return await runtime.run_action(
168
+ container,
169
+ connection_name=connection_name,
170
+ callback=method,
171
+ declaration=entry.descriptor.unit_of_work,
172
+ method_name=name,
173
+ )
174
+
175
+ stages = tuple(
176
+ stage for stage in InterceptionStage if stage is not InterceptionStage.METHOD
177
+ )
178
+
179
+ async def run(index: int) -> object:
180
+ if index == len(stages):
181
+ return await boundary()
182
+ return await pipeline.run(stages[index], container, lambda: run(index + 1))
183
+
184
+ return await run(0)
@@ -0,0 +1,25 @@
1
+ """Lifecycle state 与按 stage 限权的 context capability。"""
2
+
3
+ from .composition import ConfigureContext, PostConfigureContext, PreConfigureContext
4
+ from .context import ApplicationView, LifecycleContext
5
+ from .runtime import (
6
+ InitializeContext,
7
+ PostInitializeContext,
8
+ PreInitializeContext,
9
+ ShutdownContext,
10
+ )
11
+ from .state import ApplicationState, ShutdownReason
12
+
13
+ __all__ = (
14
+ "ApplicationState",
15
+ "ApplicationView",
16
+ "ConfigureContext",
17
+ "InitializeContext",
18
+ "LifecycleContext",
19
+ "PostConfigureContext",
20
+ "PostInitializeContext",
21
+ "PreConfigureContext",
22
+ "PreInitializeContext",
23
+ "ShutdownContext",
24
+ "ShutdownReason",
25
+ )
@@ -0,0 +1,35 @@
1
+ """同步 composition stages 各自可见的最小 capability context。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from ..configuration import ConfigurationRoot
8
+ from ..events import EventContributor
9
+ from ..options import PreOptionsSnapshot
10
+ from ..options.contribution import _OptionsContributor
11
+ from ..services import ServiceContributor
12
+ from .context import LifecycleContext
13
+
14
+
15
+ @dataclass(frozen=True, slots=True)
16
+ class PreConfigureContext(LifecycleContext):
17
+ configuration: ConfigurationRoot
18
+ pre_configure: _OptionsContributor
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class ConfigureContext(LifecycleContext):
23
+ configuration: ConfigurationRoot
24
+ pre_options: PreOptionsSnapshot
25
+ configure: _OptionsContributor
26
+ services: ServiceContributor
27
+ events: EventContributor
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class PostConfigureContext(LifecycleContext):
32
+ configuration: ConfigurationRoot
33
+ pre_options: PreOptionsSnapshot
34
+ configure: _OptionsContributor
35
+ services: ServiceContributor
@@ -0,0 +1,31 @@
1
+ """所有 lifecycle stage 共享的只读 Application view 与 context 根。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass, field
7
+ from uuid import UUID
8
+
9
+ from ..diagnostics import LifecycleStage
10
+ from ..modularity import ModuleDescriptor, ModuleGraphSnapshot
11
+ from .state import ApplicationState
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class ApplicationView:
16
+ application_id: UUID
17
+ state: ApplicationState | None
18
+ modules: ModuleGraphSnapshot
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class LifecycleContext:
23
+ application_id: UUID
24
+ descriptor: ModuleDescriptor
25
+ stage: LifecycleStage
26
+ application: ApplicationView
27
+ _emit: Callable[[str], None] = field(repr=False, compare=False)
28
+
29
+ def emit(self, code: str) -> None:
30
+ """写入当前 Module/stage 已限定作用域的稳定诊断 code。"""
31
+ self._emit(code)
@@ -0,0 +1,43 @@
1
+ """异步 runtime 与 shutdown stages 的 native container context。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from dishka import AsyncContainer
8
+
9
+ from ..configuration import ConfigurationRoot
10
+ from ..diagnostics import LifecycleStage
11
+ from ..options import OptionsSnapshot
12
+ from .context import LifecycleContext
13
+ from .state import ShutdownReason
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class PreInitializeContext(LifecycleContext):
18
+ configuration: ConfigurationRoot
19
+ options: OptionsSnapshot
20
+ container: AsyncContainer
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class InitializeContext(LifecycleContext):
25
+ configuration: ConfigurationRoot
26
+ options: OptionsSnapshot
27
+ container: AsyncContainer
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class PostInitializeContext(LifecycleContext):
32
+ configuration: ConfigurationRoot
33
+ options: OptionsSnapshot
34
+ container: AsyncContainer
35
+
36
+
37
+ @dataclass(frozen=True, slots=True)
38
+ class ShutdownContext(LifecycleContext):
39
+ configuration: ConfigurationRoot
40
+ options: OptionsSnapshot
41
+ reason: ShutdownReason
42
+ completed_stages: tuple[LifecycleStage, ...]
43
+ container: AsyncContainer
@@ -0,0 +1,20 @@
1
+ """Application lifecycle 的公开状态与 shutdown reason。"""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class ApplicationState(str, Enum):
7
+ COMPOSED = "composed"
8
+ STARTING = "starting"
9
+ RUNNING = "running"
10
+ ROLLING_BACK = "rolling_back"
11
+ FAILED_START = "failed_start"
12
+ STOPPING = "stopping"
13
+ STOPPED = "stopped"
14
+ STOPPED_WITH_ERRORS = "stopped_with_errors"
15
+
16
+
17
+ class ShutdownReason(str, Enum):
18
+ NORMAL = "normal"
19
+ START_FAILURE = "start_failure"
20
+ RUNTIME_FAILURE = "runtime_failure"
@@ -0,0 +1,15 @@
1
+ """Module contracts、graph builder 与 immutable registry capability。"""
2
+
3
+ from .contracts import AppModule, ModuleDependency, ModuleDependencyEdge, ModuleKey, ModuleRef
4
+ from .registry import ModuleDescriptor, ModuleGraphSnapshot, ModuleRegistry
5
+
6
+ __all__ = (
7
+ "AppModule",
8
+ "ModuleDependency",
9
+ "ModuleDependencyEdge",
10
+ "ModuleDescriptor",
11
+ "ModuleGraphSnapshot",
12
+ "ModuleKey",
13
+ "ModuleRef",
14
+ "ModuleRegistry",
15
+ )