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,39 @@
1
+ """Local Event 稳定 phase contract。"""
2
+
3
+ import inspect
4
+ from collections.abc import Callable
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from typing import TypeVar
8
+
9
+ from .errors import EventContributionError
10
+
11
+
12
+ class LocalEventPhase(str, Enum):
13
+ DOMAIN = "domain"
14
+ AFTER_COMMIT = "after_commit"
15
+
16
+
17
+ _EVENT_HANDLER_ATTRIBUTE = "__python_ddd_framework_local_event_handler__"
18
+ _THandler = TypeVar("_THandler")
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class _LocalEventHandlerDefinition:
23
+ phase: LocalEventPhase
24
+
25
+
26
+ def local_event_handler(*, phase: LocalEventPhase) -> Callable[[type[_THandler]], type[_THandler]]:
27
+ """只保存 class-local phase;事件类型在 Application build 由 handle 注解解释。"""
28
+ if not isinstance(phase, LocalEventPhase):
29
+ raise EventContributionError(reason="phase must be LocalEventPhase")
30
+
31
+ def decorate(handler: type[_THandler]) -> type[_THandler]:
32
+ if not isinstance(handler, type) or inspect.isabstract(handler):
33
+ raise EventContributionError(reason="local_event_handler requires a concrete class")
34
+ if _EVENT_HANDLER_ATTRIBUTE in handler.__dict__:
35
+ raise EventContributionError(reason="local_event_handler is already declared")
36
+ setattr(handler, _EVENT_HANDLER_ATTRIBUTE, _LocalEventHandlerDefinition(phase))
37
+ return handler
38
+
39
+ return decorate
@@ -0,0 +1,128 @@
1
+ """一次 Application composition 的 Local Event subscription collector。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Awaitable, Callable
7
+ from dataclasses import dataclass
8
+
9
+ from ..diagnostics import LifecycleStage, SourceLocation
10
+ from ..diagnostics.source import caller_source
11
+ from ..modularity import ModuleDescriptor, ModuleKey
12
+ from .contracts import LocalEventPhase
13
+ from .errors import EventContributionError
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class _EventContribution:
18
+ event_type: type[object]
19
+ handler_type: type[object] | Callable[..., Awaitable[None]]
20
+ phase: LocalEventPhase
21
+ owner: ModuleKey
22
+ source: SourceLocation
23
+ reason: str
24
+ declaration_index: int
25
+ automatic: bool = False
26
+
27
+
28
+ class EventContributor:
29
+ """只在当前 Module.configure 有效,避免 runtime 或 global subscription mutation。"""
30
+
31
+ __slots__ = ("_active", "_descriptor", "_registry", "_stage")
32
+
33
+ def __init__(
34
+ self,
35
+ registry: EventContributionRegistry,
36
+ *,
37
+ descriptor: ModuleDescriptor,
38
+ stage: LifecycleStage,
39
+ ) -> None:
40
+ self._registry = registry
41
+ self._descriptor = descriptor
42
+ self._stage = stage
43
+ self._active = True
44
+
45
+ def subscribe(
46
+ self,
47
+ event_type: type[object],
48
+ handler_type: type[object] | Callable[..., Awaitable[None]],
49
+ phase: LocalEventPhase,
50
+ *,
51
+ reason: str,
52
+ ) -> None:
53
+ if not self._active:
54
+ raise EventContributionError(
55
+ reason=f"event contributor is closed for {self._stage.value}"
56
+ )
57
+ self._registry.add(
58
+ event_type=event_type,
59
+ handler_type=handler_type,
60
+ phase=phase,
61
+ descriptor=self._descriptor,
62
+ source=caller_source(str(self._descriptor.key), depth=2),
63
+ reason=reason,
64
+ )
65
+
66
+ def _close(self) -> None:
67
+ self._active = False
68
+
69
+
70
+ class EventContributionRegistry:
71
+ """Application-owned append-only subscription state,freeze 后只读。"""
72
+
73
+ __slots__ = ("_counts", "_entries", "_frozen")
74
+
75
+ def __init__(self) -> None:
76
+ self._entries: list[_EventContribution] = []
77
+ self._counts: dict[ModuleKey, int] = {}
78
+ self._frozen = False
79
+
80
+ def contributor(
81
+ self,
82
+ *,
83
+ descriptor: ModuleDescriptor,
84
+ stage: LifecycleStage,
85
+ ) -> EventContributor:
86
+ return EventContributor(self, descriptor=descriptor, stage=stage)
87
+
88
+ def add(
89
+ self,
90
+ *,
91
+ event_type: type[object],
92
+ handler_type: type[object] | Callable[..., Awaitable[None]],
93
+ phase: LocalEventPhase,
94
+ descriptor: ModuleDescriptor,
95
+ source: SourceLocation,
96
+ reason: str,
97
+ automatic: bool = False,
98
+ ) -> None:
99
+ if self._frozen:
100
+ raise EventContributionError(reason="event contribution registry is already frozen")
101
+ if not isinstance(event_type, type) or not (
102
+ isinstance(handler_type, type) or inspect.iscoroutinefunction(handler_type)
103
+ ):
104
+ raise EventContributionError(
105
+ reason="event_type must be a class; handler must be a class or async function"
106
+ )
107
+ if not isinstance(phase, LocalEventPhase):
108
+ raise EventContributionError(reason="phase must be LocalEventPhase")
109
+ if not isinstance(reason, str) or not (normalized_reason := reason.strip()):
110
+ raise EventContributionError(reason="subscription reason must be a non-empty string")
111
+ declaration_index = self._counts.get(descriptor.key, 0)
112
+ self._counts[descriptor.key] = declaration_index + 1
113
+ self._entries.append(
114
+ _EventContribution(
115
+ event_type=event_type,
116
+ handler_type=handler_type,
117
+ phase=phase,
118
+ owner=descriptor.key,
119
+ source=source,
120
+ reason=normalized_reason,
121
+ declaration_index=declaration_index,
122
+ automatic=automatic,
123
+ )
124
+ )
125
+
126
+ def freeze(self) -> tuple[_EventContribution, ...]:
127
+ self._frozen = True
128
+ return tuple(self._entries)
@@ -0,0 +1,105 @@
1
+ """解释冻结发现结果,形成一次 Application 的有序自动订阅贡献。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ import types
7
+ from collections.abc import Callable
8
+ from typing import Any, Union, cast, get_args, get_origin
9
+
10
+ from dishka import Scope
11
+
12
+ from ..invocation.callables import _resolved_parameter_hints
13
+ from ..modularity import ModuleKey, ModuleRegistry
14
+ from ..modularity.discovery import _DiscoveredType
15
+ from .contracts import _EVENT_HANDLER_ATTRIBUTE, LocalEventPhase, _LocalEventHandlerDefinition
16
+ from .contribution import EventContributionRegistry
17
+ from .errors import EventContributionError
18
+
19
+
20
+ def _discover_event_handlers(
21
+ registry: ModuleRegistry,
22
+ discoveries: tuple[_DiscoveredType, ...],
23
+ contributions: EventContributionRegistry,
24
+ ) -> dict[type[object], Scope]:
25
+ positions = {item.key: index for index, item in enumerate(registry.dependency_order)}
26
+ owners: dict[type[object], ModuleKey] = {}
27
+ scopes: dict[type[object], Scope] = {}
28
+ for discovery in sorted(
29
+ discoveries,
30
+ key=lambda item: (
31
+ positions[item.owner],
32
+ f"{item.candidate.__module__}.{item.candidate.__qualname__}",
33
+ ),
34
+ ):
35
+ candidate = discovery.candidate
36
+ metadata = candidate.__dict__.get(_EVENT_HANDLER_ATTRIBUTE)
37
+ if metadata is None or inspect.isabstract(candidate):
38
+ continue
39
+ if not isinstance(metadata, _LocalEventHandlerDefinition):
40
+ raise EventContributionError(reason=f"{discovery.source}: invalid event metadata")
41
+ if candidate in owners:
42
+ if owners[candidate] != discovery.owner:
43
+ raise EventContributionError(
44
+ reason=f"{discovery.source}: handler is already owned by {owners[candidate]}"
45
+ )
46
+ continue
47
+ owners[candidate] = discovery.owner
48
+ scopes[candidate] = (
49
+ Scope.ACTION if metadata.phase is LocalEventPhase.DOMAIN else Scope.REQUEST
50
+ )
51
+ for event_type in _handler_event_types(candidate):
52
+ contributions.add(
53
+ event_type=event_type,
54
+ handler_type=candidate,
55
+ phase=metadata.phase,
56
+ descriptor=registry.descriptors[discovery.owner],
57
+ source=discovery.source,
58
+ reason="local_event_handler declaration",
59
+ automatic=True,
60
+ )
61
+ return scopes
62
+
63
+
64
+ def _handler_event_types(candidate: type[object]) -> tuple[type[object], ...]:
65
+ method = inspect.getattr_static(candidate, "handle", None)
66
+ if isinstance(method, (classmethod, staticmethod)) or not inspect.iscoroutinefunction(method):
67
+ raise EventContributionError(
68
+ reason=f"{candidate.__qualname__}.handle must be an instance async method"
69
+ )
70
+ parameters = tuple(inspect.signature(method).parameters.values())
71
+ if len(parameters) < 2 or parameters[1].kind not in (
72
+ inspect.Parameter.POSITIONAL_ONLY,
73
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
74
+ ):
75
+ raise EventContributionError(
76
+ reason=f"{candidate.__qualname__}.handle requires an event argument"
77
+ )
78
+ event = parameters[1]
79
+ try:
80
+ hints = _resolved_parameter_hints(
81
+ cast(Callable[..., object], method), {event.name: event.annotation}
82
+ )
83
+ except TypeError as error:
84
+ raise EventContributionError(
85
+ reason=f"{candidate.__qualname__}.handle: unresolved event annotation"
86
+ ) from error
87
+ annotation = hints[event.name]
88
+ choices = (
89
+ get_args(annotation)
90
+ if get_origin(annotation) in (Union, types.UnionType)
91
+ else (annotation,)
92
+ )
93
+ if any(
94
+ not isinstance(item, type)
95
+ or item in (Any, type(None), inspect.Parameter.empty)
96
+ or get_origin(item) is not None
97
+ for item in choices
98
+ ):
99
+ raise EventContributionError(
100
+ reason=(
101
+ f"{candidate.__qualname__}.handle requires concrete event types, "
102
+ "without Any/None/generics"
103
+ )
104
+ )
105
+ return cast(tuple[type[object], ...], choices)
@@ -0,0 +1,48 @@
1
+ """Local Event contribution、cycle 与 phase-specific failure contracts。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable
6
+
7
+ from ..errors.base import FrameworkError
8
+
9
+
10
+ class EventContributionError(FrameworkError):
11
+ def __init__(self, *, reason: str) -> None:
12
+ self.reason = reason
13
+ super().__init__(f"Invalid local event contribution: {reason}")
14
+
15
+
16
+ class EventCycleError(FrameworkError):
17
+ def __init__(self, *, event_type: type[object], ancestry: tuple[type[object], ...]) -> None:
18
+ self.event_type = event_type
19
+ self.ancestry = ancestry
20
+ rendered = " -> ".join(item.__qualname__ for item in (*ancestry, event_type))
21
+ super().__init__(f"Local event cycle detected: {rendered}")
22
+
23
+
24
+ class LocalEventHandlerError(FrameworkError):
25
+ def __init__(
26
+ self,
27
+ *,
28
+ event_type: type[object],
29
+ handler_type: type[object] | Callable[..., Awaitable[None]],
30
+ original_error: BaseException,
31
+ committed: bool,
32
+ ) -> None:
33
+ self.event_type = event_type
34
+ self.handler_type = handler_type
35
+ self.original_error = original_error
36
+ self.committed = committed
37
+ super().__init__(
38
+ f"Local event handler {handler_type.__module__}.{handler_type.__qualname__} failed "
39
+ f"for {event_type.__module__}.{event_type.__qualname__} "
40
+ f"(committed={committed}, error={type(original_error).__name__})"
41
+ )
42
+
43
+
44
+ class PostCommitEventError(FrameworkError):
45
+ def __init__(self, *, failures: tuple[LocalEventHandlerError, ...]) -> None:
46
+ self.failures = failures
47
+ self.committed = True
48
+ super().__init__(f"Post-commit local event dispatch failed ({len(failures)} failure(s))")
@@ -0,0 +1,192 @@
1
+ """UoW-owned FIFO envelopes 与 domain/post-commit dispatch。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import deque
6
+ from dataclasses import dataclass
7
+ from typing import Protocol
8
+ from uuid import UUID, uuid4
9
+
10
+ from dishka import AsyncContainer, Scope
11
+
12
+ from ..invocation import ApplicationInvocationContext
13
+ from ..unit_of_work.manager import UnitOfWorkManager
14
+ from .catalog import LocalEventCatalog, LocalEventSubscription
15
+ from .contracts import LocalEventPhase
16
+ from .errors import EventCycleError, LocalEventHandlerError, PostCommitEventError
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class _EventEnvelope:
21
+ envelope_id: UUID
22
+ invocation_id: UUID
23
+ parent_envelope_id: UUID | None
24
+ event: object
25
+ event_type: type[object]
26
+ ancestry: tuple[type[object], ...]
27
+
28
+
29
+ class _EventHandler(Protocol):
30
+ async def handle(self, event: object) -> None: ...
31
+
32
+
33
+ class _LocalEventQueue:
34
+ """队列只归一个具体 UoW;handler resolution 仍使用 Dishka 原生 scope。"""
35
+
36
+ __slots__ = (
37
+ "_action_container",
38
+ "_after_commit",
39
+ "_catalog",
40
+ "_context",
41
+ "_current",
42
+ "_domain",
43
+ )
44
+
45
+ def __init__(
46
+ self,
47
+ container: AsyncContainer,
48
+ context: ApplicationInvocationContext,
49
+ catalog: LocalEventCatalog,
50
+ ) -> None:
51
+ self._action_container = container
52
+ self._context = context
53
+ self._catalog = catalog
54
+ self._domain: deque[_EventEnvelope] = deque()
55
+ self._after_commit: deque[_EventEnvelope] = deque()
56
+ self._current: _EventEnvelope | None = None
57
+
58
+ def publish_domain(self, event: object) -> None:
59
+ self._publish(event, self._domain)
60
+
61
+ def publish_after_commit(self, event: object) -> None:
62
+ self._publish(event, self._after_commit)
63
+
64
+ async def _dispatch_domain(self) -> None:
65
+ request = (
66
+ self._action_container.parent_container
67
+ if self._action_container.scope is Scope.ACTION
68
+ else self._action_container
69
+ )
70
+ assert request is not None
71
+ # 每个 UoW 的 handler scope 独立;手动 UoW 的原调用 ACTION 可能已返回。
72
+ from ..application_services.invocation import _managed_action
73
+
74
+ async with _managed_action(request, self._context, committed=lambda: False) as action:
75
+ await self._drain_domain(action)
76
+
77
+ async def _drain_domain(self, action: AsyncContainer) -> None:
78
+ while self._domain:
79
+ envelope = self._domain.popleft()
80
+ await self._dispatch_envelope(
81
+ envelope,
82
+ action,
83
+ LocalEventPhase.DOMAIN,
84
+ committed=False,
85
+ )
86
+
87
+ async def _dispatch_after_commit(self) -> None:
88
+ request_container = (
89
+ self._action_container.parent_container
90
+ if self._action_container.scope is Scope.ACTION
91
+ else self._action_container
92
+ )
93
+ if request_container is None or request_container.registry.scope is not Scope.REQUEST:
94
+ raise RuntimeError("ACTION container has no REQUEST parent for post-commit dispatch")
95
+ failures: list[LocalEventHandlerError] = []
96
+ while self._after_commit:
97
+ envelope = self._after_commit.popleft()
98
+ for subscription in self._catalog.for_event(
99
+ envelope.event_type,
100
+ LocalEventPhase.AFTER_COMMIT,
101
+ ):
102
+ try:
103
+ await self._invoke_handler(
104
+ request_container,
105
+ subscription,
106
+ envelope,
107
+ committed=True,
108
+ )
109
+ except LocalEventHandlerError as failure:
110
+ failures.append(failure)
111
+ if failures:
112
+ raise PostCommitEventError(failures=tuple(failures))
113
+
114
+ def _publish(self, event: object, queue: deque[_EventEnvelope]) -> None:
115
+ event_type = type(event)
116
+ parent = self._current
117
+ ancestry = parent.ancestry if parent is not None else ()
118
+ if event_type in ancestry:
119
+ raise EventCycleError(event_type=event_type, ancestry=ancestry)
120
+ queue.append(
121
+ _EventEnvelope(
122
+ envelope_id=uuid4(),
123
+ invocation_id=self._context.invocation_id,
124
+ parent_envelope_id=parent.envelope_id if parent is not None else None,
125
+ event=event,
126
+ event_type=event_type,
127
+ ancestry=(*ancestry, event_type),
128
+ )
129
+ )
130
+
131
+ async def _dispatch_envelope(
132
+ self,
133
+ envelope: _EventEnvelope,
134
+ container: AsyncContainer,
135
+ phase: LocalEventPhase,
136
+ *,
137
+ committed: bool,
138
+ ) -> None:
139
+ for subscription in self._catalog.for_event(envelope.event_type, phase):
140
+ await self._invoke_handler(
141
+ container,
142
+ subscription,
143
+ envelope,
144
+ committed=committed,
145
+ )
146
+
147
+ async def _invoke_handler(
148
+ self,
149
+ container: AsyncContainer,
150
+ subscription: LocalEventSubscription,
151
+ envelope: _EventEnvelope,
152
+ *,
153
+ committed: bool,
154
+ ) -> None:
155
+ previous = self._current
156
+ self._current = envelope
157
+ try:
158
+ from ..application_services.invocation import _InvocationRuntime
159
+ from ..invocation.interception import _EntryInterceptionCatalog
160
+
161
+ entries = await container.get(_EntryInterceptionCatalog)
162
+ runtime = await container.get(_InvocationRuntime)
163
+ await entries.invoke(
164
+ runtime, container, subscription.handler_type, "handle", (envelope.event,)
165
+ )
166
+ except Exception as original_error:
167
+ error = LocalEventHandlerError(
168
+ event_type=envelope.event_type,
169
+ handler_type=subscription.handler_type,
170
+ original_error=original_error,
171
+ committed=committed,
172
+ )
173
+ raise error from original_error
174
+ finally:
175
+ self._current = previous
176
+
177
+
178
+ class LocalEventBus:
179
+ """发布能力解析具体 UoW 的队列;注入本身不创建 UoW 或资源。"""
180
+
181
+ def __init__(self, manager: UnitOfWorkManager) -> None:
182
+ self._manager = manager
183
+
184
+ def publish_domain(self, event: object) -> None:
185
+ work = self._manager.require_current(transactional=True)
186
+ assert work._event_queue is not None
187
+ work._event_queue.publish_domain(event)
188
+
189
+ def publish_after_commit(self, event: object) -> None:
190
+ work = self._manager.require_current(transactional=True)
191
+ assert work._event_queue is not None
192
+ work._event_queue.publish_after_commit(event)
@@ -0,0 +1,49 @@
1
+ """Optional FastAPI adapter public capability;不由 Kernel 根包重导出。"""
2
+
3
+ from . import http as http
4
+ from .adapter import FastApiAdapter
5
+ from .contracts import (
6
+ ApplicationServiceRouteOverride,
7
+ RouterCatalog,
8
+ RouterRegistration,
9
+ WebSocketRouteRegistration,
10
+ contributes_routers,
11
+ exposes_application_services,
12
+ )
13
+ from .errors import FastApiAdapterError, RouteConflictError
14
+ from .filters import ActionFilterContext, ServiceFilter, service_filter
15
+ from .http import remote_service
16
+ from .http_router import HttpRouter
17
+ from .middleware import DiMiddleware
18
+ from .realtime import (
19
+ FastApiRealtimeModule,
20
+ FastApiRealtimeOptions,
21
+ WebSocketConnection,
22
+ websocket_connection,
23
+ )
24
+ from .upload_limits import UploadLimits, upload_limits
25
+
26
+ __all__ = (
27
+ "ActionFilterContext",
28
+ "ApplicationServiceRouteOverride",
29
+ "DiMiddleware",
30
+ "FastApiAdapter",
31
+ "FastApiAdapterError",
32
+ "FastApiRealtimeModule",
33
+ "FastApiRealtimeOptions",
34
+ "HttpRouter",
35
+ "RouteConflictError",
36
+ "RouterCatalog",
37
+ "RouterRegistration",
38
+ "ServiceFilter",
39
+ "UploadLimits",
40
+ "WebSocketConnection",
41
+ "WebSocketRouteRegistration",
42
+ "contributes_routers",
43
+ "exposes_application_services",
44
+ "http",
45
+ "remote_service",
46
+ "service_filter",
47
+ "upload_limits",
48
+ "websocket_connection",
49
+ )