python-ddd-framework 0.3.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 (355) hide show
  1. python_ddd_framework/__init__.py +449 -0
  2. python_ddd_framework/application/__init__.py +6 -0
  3. python_ddd_framework/application/build_spec.py +37 -0
  4. python_ddd_framework/application/builder.py +260 -0
  5. python_ddd_framework/application/composition.py +219 -0
  6. python_ddd_framework/application/runtime.py +549 -0
  7. python_ddd_framework/application/runtime_hooks.py +327 -0
  8. python_ddd_framework/application/state.py +216 -0
  9. python_ddd_framework/application_services/__init__.py +54 -0
  10. python_ddd_framework/application_services/catalog.py +500 -0
  11. python_ddd_framework/application_services/contracts.py +190 -0
  12. python_ddd_framework/application_services/dispatcher.py +423 -0
  13. python_ddd_framework/application_services/errors.py +89 -0
  14. python_ddd_framework/application_services/execution.py +199 -0
  15. python_ddd_framework/application_services/interceptors.py +44 -0
  16. python_ddd_framework/application_services/invocation.py +431 -0
  17. python_ddd_framework/application_services/policies.py +46 -0
  18. python_ddd_framework/application_services/seeding.py +72 -0
  19. python_ddd_framework/application_services/signature.py +49 -0
  20. python_ddd_framework/application_services/validation.py +84 -0
  21. python_ddd_framework/auditing/__init__.py +12 -0
  22. python_ddd_framework/auditing/contracts.py +39 -0
  23. python_ddd_framework/auditing/control.py +48 -0
  24. python_ddd_framework/auditing/sqlalchemy/__init__.py +6 -0
  25. python_ddd_framework/auditing/sqlalchemy/migrations/0001_auditing.py +45 -0
  26. python_ddd_framework/auditing/sqlalchemy/migrations/__init__.py +1 -0
  27. python_ddd_framework/auditing/sqlalchemy/models.py +31 -0
  28. python_ddd_framework/auditing/sqlalchemy/module.py +32 -0
  29. python_ddd_framework/auditing/sqlalchemy/store.py +63 -0
  30. python_ddd_framework/authorization/__init__.py +38 -0
  31. python_ddd_framework/authorization/catalog.py +63 -0
  32. python_ddd_framework/authorization/contracts.py +241 -0
  33. python_ddd_framework/authorization/definition_discovery.py +81 -0
  34. python_ddd_framework/authorization/definitions.py +43 -0
  35. python_ddd_framework/authorization/errors.py +40 -0
  36. python_ddd_framework/background_execution/__init__.py +17 -0
  37. python_ddd_framework/background_execution/application.py +41 -0
  38. python_ddd_framework/background_execution/child.py +123 -0
  39. python_ddd_framework/background_execution/contracts.py +48 -0
  40. python_ddd_framework/background_execution/lifecycle.py +22 -0
  41. python_ddd_framework/background_execution/local.py +127 -0
  42. python_ddd_framework/background_execution/locks.py +33 -0
  43. python_ddd_framework/background_execution/management.py +17 -0
  44. python_ddd_framework/background_execution/module.py +10 -0
  45. python_ddd_framework/background_execution/permissions.py +14 -0
  46. python_ddd_framework/background_execution/processes.py +331 -0
  47. python_ddd_framework/background_jobs/__init__.py +31 -0
  48. python_ddd_framework/background_jobs/catalog.py +340 -0
  49. python_ddd_framework/background_jobs/contracts.py +208 -0
  50. python_ddd_framework/background_jobs/declaration.py +67 -0
  51. python_ddd_framework/background_jobs/errors.py +28 -0
  52. python_ddd_framework/background_jobs/execution.py +203 -0
  53. python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
  54. python_ddd_framework/background_jobs/pgqueuer/__init__.py +6 -0
  55. python_ddd_framework/background_jobs/pgqueuer/enqueue.py +86 -0
  56. python_ddd_framework/background_jobs/pgqueuer/migrations/0001_pgqueuer_1_3_2.py +95 -0
  57. python_ddd_framework/background_jobs/pgqueuer/migrations/__init__.py +1 -0
  58. python_ddd_framework/background_jobs/pgqueuer/module.py +106 -0
  59. python_ddd_framework/background_jobs/pgqueuer/options.py +82 -0
  60. python_ddd_framework/background_jobs/pgqueuer/runtime.py +479 -0
  61. python_ddd_framework/background_jobs/pgqueuer/sql/pgqueuer_1_3_2_install.sql +118 -0
  62. python_ddd_framework/background_jobs/pgqueuer/supervision.py +201 -0
  63. python_ddd_framework/background_workers/__init__.py +22 -0
  64. python_ddd_framework/background_workers/catalog.py +118 -0
  65. python_ddd_framework/background_workers/contracts.py +152 -0
  66. python_ddd_framework/background_workers/errors.py +20 -0
  67. python_ddd_framework/background_workers/execution.py +133 -0
  68. python_ddd_framework/background_workers/runtime.py +219 -0
  69. python_ddd_framework/caching/__init__.py +11 -0
  70. python_ddd_framework/caching/catalog.py +73 -0
  71. python_ddd_framework/caching/contracts.py +73 -0
  72. python_ddd_framework/caching/errors.py +20 -0
  73. python_ddd_framework/cli/__init__.py +105 -0
  74. python_ddd_framework/cli/development.py +85 -0
  75. python_ddd_framework/cli/errors.py +5 -0
  76. python_ddd_framework/cli/inspection.py +96 -0
  77. python_ddd_framework/cli/project.py +69 -0
  78. python_ddd_framework/cli/runtime.py +56 -0
  79. python_ddd_framework/configuration/__init__.py +21 -0
  80. python_ddd_framework/configuration/composition.py +65 -0
  81. python_ddd_framework/configuration/contracts.py +68 -0
  82. python_ddd_framework/configuration/dotenv_source.py +51 -0
  83. python_ddd_framework/configuration/environment_source.py +38 -0
  84. python_ddd_framework/configuration/immutability.py +124 -0
  85. python_ddd_framework/configuration/input_shape.py +58 -0
  86. python_ddd_framework/configuration/merge.py +103 -0
  87. python_ddd_framework/configuration/root.py +215 -0
  88. python_ddd_framework/configuration/sources.py +519 -0
  89. python_ddd_framework/configuration/values.py +427 -0
  90. python_ddd_framework/configuration/yaml_source.py +51 -0
  91. python_ddd_framework/developer_kit/__init__.py +1 -0
  92. python_ddd_framework/developer_kit/generation.py +182 -0
  93. python_ddd_framework/developer_kit/project_metadata.py +45 -0
  94. python_ddd_framework/developer_kit/source.py +53 -0
  95. python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -0
  96. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +70 -0
  97. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/__init__.py.jinja +1 -0
  98. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/__init__.py.jinja +1 -0
  99. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +10 -0
  100. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +24 -0
  101. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +41 -0
  102. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +20 -0
  103. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options.py.jinja +7 -0
  104. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +83 -0
  105. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +84 -0
  106. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/__init__.py.jinja +1 -0
  107. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +10 -0
  108. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +35 -0
  109. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/__init__.py.jinja +1 -0
  110. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +10 -0
  111. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/orders.py.jinja +56 -0
  112. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +14 -0
  113. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +19 -0
  114. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings.py.jinja +16 -0
  115. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/__init__.py.jinja +1 -0
  116. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +24 -0
  117. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +7 -0
  118. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions.py.jinja +15 -0
  119. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/__init__.py.jinja +1 -0
  120. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/files.py.jinja +73 -0
  121. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +25 -0
  122. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +36 -0
  123. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/__init__.py.jinja +1 -0
  124. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -0
  125. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +7 -0
  126. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/orders.py.jinja +25 -0
  127. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +26 -0
  128. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/__init__.py.jinja +1 -0
  129. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +49 -0
  130. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +18 -0
  131. python_ddd_framework/developer_kit/templates/project/cookiecutter.json +6 -0
  132. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.dockerignore +9 -0
  133. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.gitignore +6 -0
  134. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.python-version +1 -0
  135. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +38 -0
  136. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/Dockerfile +22 -0
  137. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +130 -0
  138. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/app.development.yaml.jinja +30 -0
  139. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.dev.yaml.jinja +24 -0
  140. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.production.yaml.jinja +20 -0
  141. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +84 -0
  142. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +185 -0
  143. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/pyproject.toml.jinja +28 -0
  144. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/__init__.py.jinja +1 -0
  145. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/main.py.jinja +47 -0
  146. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/module.py.jinja +37 -0
  147. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/conftest.py.jinja +8 -0
  148. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/host/test_http.py.jinja +57 -0
  149. python_ddd_framework/developer_kit/wiring.py +162 -0
  150. python_ddd_framework/diagnostics/__init__.py +23 -0
  151. python_ddd_framework/diagnostics/journal.py +174 -0
  152. python_ddd_framework/diagnostics/model.py +83 -0
  153. python_ddd_framework/diagnostics/source.py +25 -0
  154. python_ddd_framework/distributed_lock/__init__.py +6 -0
  155. python_ddd_framework/distributed_lock/contracts.py +20 -0
  156. python_ddd_framework/distributed_lock/options.py +19 -0
  157. python_ddd_framework/domain/__init__.py +13 -0
  158. python_ddd_framework/domain/aggregates.py +64 -0
  159. python_ddd_framework/domain/entities.py +33 -0
  160. python_ddd_framework/domain/errors.py +12 -0
  161. python_ddd_framework/domain/value_objects.py +31 -0
  162. python_ddd_framework/errors/__init__.py +75 -0
  163. python_ddd_framework/errors/base.py +13 -0
  164. python_ddd_framework/errors/business.py +53 -0
  165. python_ddd_framework/errors/configuration.py +119 -0
  166. python_ddd_framework/errors/diagnostics.py +12 -0
  167. python_ddd_framework/errors/lifecycle.py +164 -0
  168. python_ddd_framework/errors/modularity.py +100 -0
  169. python_ddd_framework/errors/services.py +100 -0
  170. python_ddd_framework/events/__init__.py +25 -0
  171. python_ddd_framework/events/aggregate.py +39 -0
  172. python_ddd_framework/events/catalog.py +136 -0
  173. python_ddd_framework/events/contracts.py +39 -0
  174. python_ddd_framework/events/contribution.py +128 -0
  175. python_ddd_framework/events/discovery.py +105 -0
  176. python_ddd_framework/events/errors.py +48 -0
  177. python_ddd_framework/events/runtime.py +192 -0
  178. python_ddd_framework/fastapi/__init__.py +49 -0
  179. python_ddd_framework/fastapi/action.py +175 -0
  180. python_ddd_framework/fastapi/adapter.py +391 -0
  181. python_ddd_framework/fastapi/application_services.py +654 -0
  182. python_ddd_framework/fastapi/background.py +42 -0
  183. python_ddd_framework/fastapi/contracts.py +275 -0
  184. python_ddd_framework/fastapi/errors.py +32 -0
  185. python_ddd_framework/fastapi/filters.py +111 -0
  186. python_ddd_framework/fastapi/health.py +44 -0
  187. python_ddd_framework/fastapi/http.py +72 -0
  188. python_ddd_framework/fastapi/http_router.py +216 -0
  189. python_ddd_framework/fastapi/manual_action.py +105 -0
  190. python_ddd_framework/fastapi/middleware.py +93 -0
  191. python_ddd_framework/fastapi/parameters.py +97 -0
  192. python_ddd_framework/fastapi/realtime/__init__.py +13 -0
  193. python_ddd_framework/fastapi/realtime/authentication.py +181 -0
  194. python_ddd_framework/fastapi/realtime/connection.py +166 -0
  195. python_ddd_framework/fastapi/realtime/module.py +41 -0
  196. python_ddd_framework/fastapi/realtime/options.py +50 -0
  197. python_ddd_framework/fastapi/realtime/runtime.py +423 -0
  198. python_ddd_framework/fastapi/request_context.py +247 -0
  199. python_ddd_framework/fastapi/route_integrity.py +88 -0
  200. python_ddd_framework/fastapi/routing.py +424 -0
  201. python_ddd_framework/fastapi/server.py +182 -0
  202. python_ddd_framework/fastapi/settings.py +40 -0
  203. python_ddd_framework/fastapi/tracing.py +62 -0
  204. python_ddd_framework/fastapi/transfer.py +128 -0
  205. python_ddd_framework/fastapi/upload_limits.py +63 -0
  206. python_ddd_framework/fastapi/uploads.py +94 -0
  207. python_ddd_framework/hosted_services/__init__.py +29 -0
  208. python_ddd_framework/hosted_services/bridge.py +420 -0
  209. python_ddd_framework/hosted_services/catalog.py +265 -0
  210. python_ddd_framework/hosted_services/contracts.py +55 -0
  211. python_ddd_framework/hosted_services/errors.py +53 -0
  212. python_ddd_framework/hosted_services/options.py +17 -0
  213. python_ddd_framework/hosted_services/runtime.py +267 -0
  214. python_ddd_framework/hosted_services/state.py +40 -0
  215. python_ddd_framework/hosting/__init__.py +4 -0
  216. python_ddd_framework/hosting/instance.py +40 -0
  217. python_ddd_framework/identity/__init__.py +73 -0
  218. python_ddd_framework/identity/application.py +287 -0
  219. python_ddd_framework/identity/contracts.py +283 -0
  220. python_ddd_framework/identity/errors.py +28 -0
  221. python_ddd_framework/identity/http_api.py +90 -0
  222. python_ddd_framework/identity/module.py +49 -0
  223. python_ddd_framework/identity/passwords.py +34 -0
  224. python_ddd_framework/identity/permissions.py +14 -0
  225. python_ddd_framework/identity/services.py +108 -0
  226. python_ddd_framework/identity/sqlalchemy/__init__.py +5 -0
  227. python_ddd_framework/identity/sqlalchemy/migrations/0001_identity.py +143 -0
  228. python_ddd_framework/identity/sqlalchemy/migrations/0002_physical_delete.py +42 -0
  229. python_ddd_framework/identity/sqlalchemy/migrations/__init__.py +1 -0
  230. python_ddd_framework/identity/sqlalchemy/models.py +85 -0
  231. python_ddd_framework/identity/sqlalchemy/module.py +62 -0
  232. python_ddd_framework/identity/sqlalchemy/stores.py +526 -0
  233. python_ddd_framework/identity/tokens.py +106 -0
  234. python_ddd_framework/invocation/__init__.py +3 -0
  235. python_ddd_framework/invocation/callables.py +171 -0
  236. python_ddd_framework/invocation/contracts.py +33 -0
  237. python_ddd_framework/invocation/entries.py +54 -0
  238. python_ddd_framework/invocation/function_runtime.py +48 -0
  239. python_ddd_framework/invocation/interception.py +193 -0
  240. python_ddd_framework/lifecycle/__init__.py +25 -0
  241. python_ddd_framework/lifecycle/composition.py +35 -0
  242. python_ddd_framework/lifecycle/context.py +31 -0
  243. python_ddd_framework/lifecycle/runtime.py +43 -0
  244. python_ddd_framework/lifecycle/state.py +20 -0
  245. python_ddd_framework/modularity/__init__.py +15 -0
  246. python_ddd_framework/modularity/contracts.py +91 -0
  247. python_ddd_framework/modularity/discovery.py +157 -0
  248. python_ddd_framework/modularity/graph.py +334 -0
  249. python_ddd_framework/modularity/registry.py +100 -0
  250. python_ddd_framework/modularity/selection.py +16 -0
  251. python_ddd_framework/notifications/__init__.py +19 -0
  252. python_ddd_framework/notifications/catalog.py +46 -0
  253. python_ddd_framework/notifications/contracts.py +107 -0
  254. python_ddd_framework/observability/__init__.py +5 -0
  255. python_ddd_framework/observability/context.py +43 -0
  256. python_ddd_framework/observability/export.py +73 -0
  257. python_ddd_framework/observability/formatting.py +89 -0
  258. python_ddd_framework/observability/logging.py +118 -0
  259. python_ddd_framework/observability/options.py +45 -0
  260. python_ddd_framework/observability/tracing.py +80 -0
  261. python_ddd_framework/options/__init__.py +19 -0
  262. python_ddd_framework/options/aliases.py +331 -0
  263. python_ddd_framework/options/contribution.py +62 -0
  264. python_ddd_framework/options/immutability.py +48 -0
  265. python_ddd_framework/options/input_keys.py +214 -0
  266. python_ddd_framework/options/issues.py +335 -0
  267. python_ddd_framework/options/models.py +114 -0
  268. python_ddd_framework/options/registry.py +280 -0
  269. python_ddd_framework/options/schema.py +488 -0
  270. python_ddd_framework/options/validation.py +120 -0
  271. python_ddd_framework/py.typed +0 -0
  272. python_ddd_framework/realtime/__init__.py +12 -0
  273. python_ddd_framework/realtime/contracts.py +28 -0
  274. python_ddd_framework/realtime/diagnostics.py +37 -0
  275. python_ddd_framework/realtime/messages.py +113 -0
  276. python_ddd_framework/redis/__init__.py +6 -0
  277. python_ddd_framework/redis/distributed_lock.py +212 -0
  278. python_ddd_framework/redis/lease_lock.py +36 -0
  279. python_ddd_framework/redis/module.py +50 -0
  280. python_ddd_framework/redis/notification_runtime.py +182 -0
  281. python_ddd_framework/redis/notifications.py +25 -0
  282. python_ddd_framework/redis/options.py +35 -0
  283. python_ddd_framework/redis/runtime.py +116 -0
  284. python_ddd_framework/services/__init__.py +41 -0
  285. python_ddd_framework/services/application_bindings.py +221 -0
  286. python_ddd_framework/services/arbitration.py +381 -0
  287. python_ddd_framework/services/binding.py +102 -0
  288. python_ddd_framework/services/contribution.py +438 -0
  289. python_ddd_framework/services/convention.py +301 -0
  290. python_ddd_framework/services/convention_contracts.py +101 -0
  291. python_ddd_framework/services/exposure.py +23 -0
  292. python_ddd_framework/services/fixed_lifetime.py +52 -0
  293. python_ddd_framework/services/framework_provider.py +178 -0
  294. python_ddd_framework/services/native_graph.py +201 -0
  295. python_ddd_framework/services/provider.py +204 -0
  296. python_ddd_framework/services/registration.py +101 -0
  297. python_ddd_framework/services/repository.py +38 -0
  298. python_ddd_framework/services/runtime.py +302 -0
  299. python_ddd_framework/settings/__init__.py +30 -0
  300. python_ddd_framework/settings/application.py +95 -0
  301. python_ddd_framework/settings/binding.py +33 -0
  302. python_ddd_framework/settings/catalog.py +121 -0
  303. python_ddd_framework/settings/changes.py +14 -0
  304. python_ddd_framework/settings/contracts.py +115 -0
  305. python_ddd_framework/settings/definition_discovery.py +71 -0
  306. python_ddd_framework/settings/definitions.py +41 -0
  307. python_ddd_framework/settings/errors.py +22 -0
  308. python_ddd_framework/settings/handlers.py +43 -0
  309. python_ddd_framework/settings/management.py +57 -0
  310. python_ddd_framework/settings/manager.py +89 -0
  311. python_ddd_framework/settings/module.py +10 -0
  312. python_ddd_framework/settings/notifications.py +39 -0
  313. python_ddd_framework/settings/permissions.py +14 -0
  314. python_ddd_framework/settings/provider.py +74 -0
  315. python_ddd_framework/settings/refresh.py +140 -0
  316. python_ddd_framework/settings/refresh_module.py +45 -0
  317. python_ddd_framework/settings/sqlalchemy/__init__.py +5 -0
  318. python_ddd_framework/settings/sqlalchemy/migrations/0001_settings.py +31 -0
  319. python_ddd_framework/settings/sqlalchemy/migrations/0002_physical_delete.py +32 -0
  320. python_ddd_framework/settings/sqlalchemy/migrations/0003_version_tokens.py +39 -0
  321. python_ddd_framework/settings/sqlalchemy/migrations/__init__.py +1 -0
  322. python_ddd_framework/settings/sqlalchemy/models.py +36 -0
  323. python_ddd_framework/settings/sqlalchemy/module.py +36 -0
  324. python_ddd_framework/settings/sqlalchemy/store.py +86 -0
  325. python_ddd_framework/settings/store.py +16 -0
  326. python_ddd_framework/settings/values.py +39 -0
  327. python_ddd_framework/sqlalchemy/__init__.py +54 -0
  328. python_ddd_framework/sqlalchemy/alembic_runtime/__init__.py +1 -0
  329. python_ddd_framework/sqlalchemy/alembic_runtime/env.py +33 -0
  330. python_ddd_framework/sqlalchemy/alembic_runtime/script.py.mako +14 -0
  331. python_ddd_framework/sqlalchemy/auditing.py +48 -0
  332. python_ddd_framework/sqlalchemy/errors.py +85 -0
  333. python_ddd_framework/sqlalchemy/metadata.py +576 -0
  334. python_ddd_framework/sqlalchemy/migration.py +378 -0
  335. python_ddd_framework/sqlalchemy/migration_options.py +56 -0
  336. python_ddd_framework/sqlalchemy/module.py +51 -0
  337. python_ddd_framework/sqlalchemy/module_migration.py +151 -0
  338. python_ddd_framework/sqlalchemy/options.py +66 -0
  339. python_ddd_framework/sqlalchemy/repository.py +33 -0
  340. python_ddd_framework/sqlalchemy/runtime.py +85 -0
  341. python_ddd_framework/sqlalchemy/session_provider.py +45 -0
  342. python_ddd_framework/sqlalchemy/unit_of_work.py +97 -0
  343. python_ddd_framework/testing/__init__.py +5 -0
  344. python_ddd_framework/testing/runtime.py +112 -0
  345. python_ddd_framework/unit_of_work/__init__.py +14 -0
  346. python_ddd_framework/unit_of_work/contracts.py +223 -0
  347. python_ddd_framework/unit_of_work/errors.py +27 -0
  348. python_ddd_framework/unit_of_work/manager.py +210 -0
  349. python_ddd_framework/unit_of_work/options.py +92 -0
  350. python_ddd_framework-0.3.1.dist-info/METADATA +379 -0
  351. python_ddd_framework-0.3.1.dist-info/RECORD +355 -0
  352. python_ddd_framework-0.3.1.dist-info/WHEEL +4 -0
  353. python_ddd_framework-0.3.1.dist-info/entry_points.txt +9 -0
  354. python_ddd_framework-0.3.1.dist-info/licenses/LICENSE +7 -0
  355. python_ddd_framework-0.3.1.dist-info/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
@@ -0,0 +1,27 @@
1
+ """Unit of Work 公开失败契约与状态诊断。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ..errors.base import FrameworkError
8
+
9
+ if TYPE_CHECKING:
10
+ from .contracts import UnitOfWorkState
11
+
12
+
13
+ class UnitOfWorkStateError(FrameworkError):
14
+ def __init__(
15
+ self,
16
+ *,
17
+ operation: str,
18
+ current: UnitOfWorkState,
19
+ allowed: tuple[UnitOfWorkState, ...],
20
+ ) -> None:
21
+ self.operation = operation
22
+ self.current = current
23
+ self.allowed = allowed
24
+ allowed_values = ", ".join(item.value for item in allowed)
25
+ super().__init__(
26
+ f"UnitOfWork.{operation} requires state in ({allowed_values}), got {current.value}"
27
+ )
@@ -0,0 +1,210 @@
1
+ """Application-owned ambient UoW 与取消监督;不捕获 Session。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import AsyncIterator
7
+ from contextlib import asynccontextmanager
8
+ from contextvars import ContextVar
9
+ from typing import TYPE_CHECKING
10
+ from uuid import uuid4
11
+
12
+ from dishka import Scope
13
+
14
+ from ..invocation import ApplicationInvocationContext
15
+ from .contracts import UnitOfWork, _UnitOfWorkFactory, _UnitOfWorkParticipant
16
+ from .options import (
17
+ UnitOfWorkDeclaration,
18
+ UnitOfWorkOptions,
19
+ UnitOfWorkTransactionBehavior,
20
+ _ResolvedUnitOfWorkOptions,
21
+ )
22
+
23
+ if TYPE_CHECKING:
24
+ from ..application_services.invocation import _InvocationRuntime
25
+ from ..events import LocalEventCatalog
26
+
27
+
28
+ class UnitOfWorkManager:
29
+ def __init__(
30
+ self,
31
+ runtime: _InvocationRuntime,
32
+ factory: _UnitOfWorkFactory,
33
+ options: UnitOfWorkOptions,
34
+ catalog: LocalEventCatalog,
35
+ ) -> None:
36
+ self._runtime = runtime
37
+ self._factory = factory
38
+ self._options = options
39
+ self._catalog = catalog
40
+ self._ambient: ContextVar[UnitOfWork | None] = ContextVar(
41
+ f"python_ddd_framework_uow_{runtime.application_id}", default=None
42
+ )
43
+
44
+ @property
45
+ def current(self) -> UnitOfWork | None:
46
+ work = self._ambient.get()
47
+ if work is not None:
48
+ work._require_active("current", allow_completing=True)
49
+ return work
50
+
51
+ def require_current(self, *, transactional: bool = False) -> UnitOfWork:
52
+ work = self.current
53
+ if work is None:
54
+ raise RuntimeError("operation requires an active UnitOfWork")
55
+ if transactional and not work.is_transactional:
56
+ raise RuntimeError("operation requires a transactional UnitOfWork")
57
+ return work
58
+
59
+ def _resolve_options(
60
+ self, connection_name: str | None, declaration: UnitOfWorkDeclaration, method_name: str
61
+ ) -> _ResolvedUnitOfWorkOptions:
62
+ from ..application_services.contracts import (
63
+ _DEFAULT_CONNECTION_NAME,
64
+ _validate_connection_name,
65
+ )
66
+
67
+ ambient = self.current
68
+ execution = self._runtime.current()
69
+ connection = (
70
+ connection_name
71
+ if connection_name is not None
72
+ else (
73
+ ambient.connection_name
74
+ if ambient is not None
75
+ else (
76
+ execution.context.connection_name
77
+ if execution is not None
78
+ else _DEFAULT_CONNECTION_NAME
79
+ )
80
+ )
81
+ )
82
+ transactional = declaration.is_transactional
83
+ if transactional is None:
84
+ behavior = self._options.transaction_behavior
85
+ if behavior is UnitOfWorkTransactionBehavior.AUTO:
86
+ verb = self._runtime.http_method()
87
+ transactional = (
88
+ verb != "GET"
89
+ if verb is not None
90
+ else not (method_name == "get" or method_name.startswith("get_"))
91
+ )
92
+ else:
93
+ transactional = behavior is UnitOfWorkTransactionBehavior.ENABLED
94
+ return _ResolvedUnitOfWorkOptions(
95
+ _validate_connection_name(connection),
96
+ transactional,
97
+ declaration.isolation_level or self._options.isolation_level,
98
+ declaration.command_timeout_ms
99
+ if declaration.command_timeout_ms is not None
100
+ else self._options.command_timeout_ms,
101
+ )
102
+
103
+ @asynccontextmanager
104
+ async def begin(
105
+ self,
106
+ *,
107
+ requires_new: bool = False,
108
+ connection_name: str | None = None,
109
+ is_transactional: bool | None = None,
110
+ isolation_level: str | None = None,
111
+ command_timeout_ms: int | None = None,
112
+ ) -> AsyncIterator[UnitOfWork]:
113
+ declaration = UnitOfWorkDeclaration(
114
+ is_transactional=is_transactional,
115
+ isolation_level=isolation_level,
116
+ command_timeout_ms=command_timeout_ms,
117
+ )
118
+ options = self._resolve_options(connection_name, declaration, "begin")
119
+ async with self._begin(options, requires_new=requires_new) as work:
120
+ yield work
121
+
122
+ def _check_current(self, connection_name: str) -> UnitOfWork | None:
123
+ from ..application_services.errors import NestedConnectionError
124
+
125
+ outer = self.current
126
+ # disabled 只控制自动创建;参与外层仍须满足同 task、有效状态与同连接。
127
+ if outer is not None and outer.connection_name != connection_name:
128
+ raise NestedConnectionError(outer=outer.connection_name, requested=connection_name)
129
+ return outer
130
+
131
+ @asynccontextmanager
132
+ async def _begin(
133
+ self, options: _ResolvedUnitOfWorkOptions, *, requires_new: bool = False
134
+ ) -> AsyncIterator[UnitOfWork]:
135
+ outer = self.current if requires_new else self._check_current(options.connection_name)
136
+ if outer is not None and not requires_new:
137
+ participant = _UnitOfWorkParticipant(outer)
138
+ try:
139
+ yield participant
140
+ finally:
141
+ participant._released = True
142
+ return
143
+ async with self._runtime.outer_lease():
144
+ async with self._own_work(options) as work:
145
+ yield work
146
+
147
+ @asynccontextmanager
148
+ async def _own_work(self, options: _ResolvedUnitOfWorkOptions) -> AsyncIterator[UnitOfWork]:
149
+ from ..application_services.errors import ApplicationInvocationCleanupError
150
+ from ..events.runtime import _LocalEventQueue
151
+
152
+ execution = self._runtime.current()
153
+ container = (
154
+ execution.action_container
155
+ if execution is not None
156
+ else self._runtime.current_call_scope()
157
+ )
158
+ if container is None:
159
+ raise RuntimeError("UnitOfWork.begin requires a managed execution scope")
160
+ context = ApplicationInvocationContext(
161
+ application_id=self._runtime.application_id,
162
+ invocation_id=uuid4(),
163
+ parent_invocation_id=execution.context.invocation_id if execution else None,
164
+ connection_name=options.connection_name,
165
+ )
166
+ work = self._factory.create()
167
+ work._options = options
168
+ work._event_queue = _LocalEventQueue(container, context, self._catalog)
169
+
170
+ async def after_commit() -> None:
171
+ # 新服务调用既不能复用旧事务,也不能落入旧 ACTION 的对象缓存。
172
+ token = self._ambient.set(None)
173
+ parent = container.parent_container if container.scope is Scope.ACTION else container
174
+ assert parent is not None
175
+ try:
176
+ with self._runtime.detached_execution(parent):
177
+ assert work._event_queue is not None
178
+ await work._event_queue._dispatch_after_commit()
179
+ finally:
180
+ self._ambient.reset(token)
181
+
182
+ work._after_commit = after_commit
183
+ token = self._ambient.set(work)
184
+ primary: BaseException | None = None
185
+ try:
186
+ yield work
187
+ except BaseException as error:
188
+ primary = error
189
+ finally:
190
+ # 仅原生资源 I/O 移入受监督 task;ContextVar token 始终由原 task 恢复。
191
+ cleanup = asyncio.create_task(work._cleanup())
192
+ try:
193
+ while not cleanup.done():
194
+ try:
195
+ await asyncio.shield(cleanup)
196
+ except asyncio.CancelledError as cancellation:
197
+ if primary is None:
198
+ primary = cancellation
199
+ cleanup.result()
200
+ finally:
201
+ self._ambient.reset(token)
202
+ if work.cleanup_failures:
203
+ failure = ApplicationInvocationCleanupError(
204
+ primary=primary, cleanup_failures=work.cleanup_failures, committed=work.committed
205
+ )
206
+ if isinstance(primary, asyncio.CancelledError):
207
+ raise primary from failure
208
+ raise failure from (primary or work.cleanup_failures[0])
209
+ if primary is not None:
210
+ raise primary
@@ -0,0 +1,92 @@
1
+ """UoW 声明整项覆盖;默认值只来自不可变全局 Options。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+ from typing import TypeVar
9
+
10
+ from pydantic import ConfigDict, Field, field_validator
11
+
12
+ from ..options import BaseOptions
13
+
14
+ _UNIT_OF_WORK_ATTRIBUTE = "__python_ddd_framework_unit_of_work__"
15
+ _T = TypeVar("_T")
16
+
17
+
18
+ class UnitOfWorkTransactionBehavior(str, Enum):
19
+ AUTO = "auto"
20
+ ENABLED = "enabled"
21
+ DISABLED = "disabled"
22
+
23
+
24
+ class UnitOfWorkOptions(BaseOptions):
25
+ model_config = ConfigDict(extra="forbid")
26
+
27
+ transaction_behavior: UnitOfWorkTransactionBehavior = UnitOfWorkTransactionBehavior.AUTO
28
+ isolation_level: str | None = None
29
+ command_timeout_ms: int | None = Field(default=None, strict=True, ge=0)
30
+
31
+ @field_validator("isolation_level")
32
+ @classmethod
33
+ def _validate_isolation_level(cls, value: str | None) -> str | None:
34
+ if value is not None and (
35
+ not value or value != value.strip() or value.upper() == "AUTOCOMMIT"
36
+ ):
37
+ # 事务隔离沿用原生 provider;DBAPI AUTOCOMMIT 会破坏本框架的批次/回滚语义。
38
+ raise ValueError("isolation_level must name a database transaction isolation level")
39
+ return value
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class UnitOfWorkDeclaration:
44
+ disabled: bool = False
45
+ is_transactional: bool | None = None
46
+ isolation_level: str | None = None
47
+ command_timeout_ms: int | None = None
48
+
49
+ def __post_init__(self) -> None:
50
+ if not isinstance(self.disabled, bool):
51
+ raise TypeError("disabled must be bool")
52
+ if self.is_transactional is not None and not isinstance(self.is_transactional, bool):
53
+ raise TypeError("is_transactional must be bool or None")
54
+ UnitOfWorkOptions(
55
+ isolation_level=self.isolation_level, command_timeout_ms=self.command_timeout_ms
56
+ )
57
+
58
+
59
+ @dataclass(frozen=True, slots=True)
60
+ class _ResolvedUnitOfWorkOptions:
61
+ connection_name: str
62
+ is_transactional: bool
63
+ isolation_level: str | None
64
+ command_timeout_ms: int | None
65
+
66
+
67
+ def unit_of_work(
68
+ *,
69
+ disabled: bool = False,
70
+ is_transactional: bool | None = None,
71
+ isolation_level: str | None = None,
72
+ command_timeout_ms: int | None = None,
73
+ ) -> Callable[[_T], _T]:
74
+ declaration = UnitOfWorkDeclaration(
75
+ disabled, is_transactional, isolation_level, command_timeout_ms
76
+ )
77
+
78
+ def decorate(target: _T) -> _T:
79
+ setattr(target, _UNIT_OF_WORK_ATTRIBUTE, declaration)
80
+ return target
81
+
82
+ return decorate
83
+
84
+
85
+ def _declaration(implementation: type[object], method: object) -> UnitOfWorkDeclaration:
86
+ # 方法替换整项声明,未填写的字段只补全全局默认,不能泄漏类配置。
87
+ result = getattr(method, _UNIT_OF_WORK_ATTRIBUTE, None)
88
+ if result is None:
89
+ result = getattr(implementation, _UNIT_OF_WORK_ATTRIBUTE, UnitOfWorkDeclaration())
90
+ if not isinstance(result, UnitOfWorkDeclaration):
91
+ raise TypeError("invalid unit_of_work metadata")
92
+ return result