python-ddd-framework 0.3.2__py3-none-any.whl → 0.4.0__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 (334) hide show
  1. python_ddd_framework/__init__.py +139 -21
  2. python_ddd_framework/application/builder.py +23 -78
  3. python_ddd_framework/application/composition.py +4 -22
  4. python_ddd_framework/application/runtime.py +96 -291
  5. python_ddd_framework/application/state.py +7 -75
  6. python_ddd_framework/application_services/__init__.py +5 -21
  7. python_ddd_framework/{services/application_bindings.py → application_services/bindings.py} +52 -9
  8. python_ddd_framework/application_services/catalog.py +36 -288
  9. python_ddd_framework/application_services/contracts.py +14 -13
  10. python_ddd_framework/application_services/dispatcher.py +19 -269
  11. python_ddd_framework/application_services/invocation.py +126 -7
  12. python_ddd_framework/application_services/module.py +168 -0
  13. python_ddd_framework/application_services/pagination.py +21 -0
  14. python_ddd_framework/auditing/control.py +1 -1
  15. python_ddd_framework/auditing/module.py +15 -0
  16. python_ddd_framework/authorization/__init__.py +2 -0
  17. python_ddd_framework/authorization/contracts.py +7 -2
  18. python_ddd_framework/authorization/definitions.py +5 -0
  19. python_ddd_framework/authorization/module.py +40 -0
  20. python_ddd_framework/authorization/options.py +7 -0
  21. python_ddd_framework/background_execution/child.py +13 -3
  22. python_ddd_framework/background_execution/declarations.py +45 -0
  23. python_ddd_framework/background_execution/lifecycle.py +12 -0
  24. python_ddd_framework/background_execution/module.py +107 -1
  25. python_ddd_framework/background_jobs/catalog.py +59 -42
  26. python_ddd_framework/background_jobs/contracts.py +16 -19
  27. python_ddd_framework/background_jobs/pgqueuer/enqueue.py +4 -0
  28. python_ddd_framework/background_jobs/pgqueuer/module.py +2 -1
  29. python_ddd_framework/background_jobs/pgqueuer/runtime.py +7 -7
  30. python_ddd_framework/background_workers/catalog.py +23 -36
  31. python_ddd_framework/background_workers/contracts.py +3 -0
  32. python_ddd_framework/background_workers/execution.py +1 -1
  33. python_ddd_framework/background_workers/runtime.py +38 -1
  34. python_ddd_framework/caching/__init__.py +7 -6
  35. python_ddd_framework/caching/contracts.py +65 -67
  36. python_ddd_framework/caching/errors.py +0 -7
  37. python_ddd_framework/caching/module.py +18 -0
  38. python_ddd_framework/caching/naming.py +29 -0
  39. python_ddd_framework/caching/options.py +38 -0
  40. python_ddd_framework/caching/unit_of_work.py +46 -0
  41. python_ddd_framework/cli/__init__.py +31 -6
  42. python_ddd_framework/cli/project.py +68 -6
  43. python_ddd_framework/cli/runtime.py +1 -1
  44. python_ddd_framework/developer_kit/generation.py +75 -22
  45. python_ddd_framework/developer_kit/project_metadata.py +2 -4
  46. python_ddd_framework/developer_kit/templates/basic/cookiecutter.json +4 -0
  47. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/README.md +45 -0
  48. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/__init__.py.jinja +0 -0
  49. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/__init__.py.jinja +0 -0
  50. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/conversion_service.py.jinja +7 -0
  51. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/module.py.jinja +38 -0
  52. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja +0 -0
  53. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja +10 -0
  54. python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -1
  55. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +41 -49
  56. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja +1 -0
  57. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/__init__.py.jinja +1 -0
  58. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/handler.py.jinja +45 -0
  59. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/payload.py.jinja +8 -0
  60. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/__init__.py.jinja +1 -0
  61. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/handler.py.jinja +35 -0
  62. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/payload.py.jinja +5 -0
  63. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/schedule.py.jinja +15 -0
  64. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/__init__.py.jinja +1 -0
  65. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_maintenance_worker.py.jinja +28 -0
  66. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_statistics_worker.py.jinja +26 -0
  67. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/__init__.py.jinja +1 -0
  68. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/order_cache.py.jinja +9 -0
  69. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/statistics_cache.py.jinja +9 -0
  70. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/__init__.py.jinja +1 -0
  71. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/order_changed_handler.py.jinja +23 -0
  72. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/__init__.py.jinja +1 -0
  73. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{integration.py.jinja → hosted_services/order_integration_service.py.jinja} +8 -2
  74. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_observation_handler.py.jinja +16 -0
  75. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/__init__.py.jinja +1 -0
  76. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/order_reporting_service.py.jinja +16 -0
  77. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/__init__.py.jinja +1 -0
  78. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/order_timing_interceptor.py.jinja +17 -0
  79. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +105 -10
  80. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja +1 -0
  81. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{options.py.jinja → options/order_options.py.jinja} +1 -1
  82. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja +1 -0
  83. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_approval_service.py.jinja +65 -0
  84. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_management_service.py.jinja +35 -0
  85. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_query_service.py.jinja +41 -0
  86. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +1 -0
  87. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/approval_setting_observer.py.jinja +17 -0
  88. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja +1 -0
  89. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/approve_order.py.jinja +6 -0
  90. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/create_order.py.jinja +7 -0
  91. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja +1 -0
  92. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/order_reporting_service.py.jinja +7 -0
  93. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +39 -2
  94. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/__init__.py.jinja +1 -0
  95. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_approval_service.py.jinja +13 -0
  96. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_management_service.py.jinja +10 -0
  97. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_query_service.py.jinja +12 -0
  98. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/__init__.py.jinja +1 -0
  99. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_statistics_snapshot.py.jinja +9 -0
  100. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_view.py.jinja +12 -0
  101. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/entities/__init__.py.jinja +1 -0
  102. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{orders.py.jinja → entities/order.py.jinja} +10 -18
  103. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/__init__.py.jinja +1 -0
  104. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/order_changed.py.jinja +13 -0
  105. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +42 -3
  106. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/__init__.py.jinja +1 -0
  107. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/order_repository.py.jinja +17 -0
  108. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/__init__.py.jinja +1 -0
  109. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/order_seed_contributor.py.jinja +22 -0
  110. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/__init__.py.jinja +1 -0
  111. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/order_approval_service.py.jinja +23 -0
  112. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings/__init__.py.jinja +1 -0
  113. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{settings.py.jinja → settings/approval_settings.py.jinja} +3 -2
  114. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/__init__.py.jinja +1 -0
  115. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/order_title.py.jinja +10 -0
  116. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/__init__.py.jinja +1 -0
  117. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/order_constants.py.jinja +3 -0
  118. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/__init__.py.jinja +1 -0
  119. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/order_status.py.jinja +6 -0
  120. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/__init__.py.jinja +1 -0
  121. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/order_errors.py.jinja +11 -0
  122. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/__init__.py.jinja +1 -0
  123. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_messages.py.jinja +26 -0
  124. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_observation.py.jinja +13 -0
  125. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +40 -1
  126. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/__init__.py.jinja +1 -0
  127. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/{permissions.py.jinja → permissions/order_permission_provider.py.jinja} +6 -2
  128. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/order_permissions.py.jinja +7 -0
  129. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/__init__.py.jinja +1 -0
  130. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/money.py.jinja +18 -0
  131. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/__init__.py.jinja +1 -0
  132. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/export_filter.py.jinja +15 -0
  133. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/__init__.py.jinja +1 -0
  134. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/refresh_orders.py.jinja +5 -0
  135. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +85 -7
  136. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/routers/__init__.py.jinja +1 -0
  137. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/{files.py.jinja → routers/order_files.py.jinja} +23 -20
  138. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/__init__.py.jinja +1 -0
  139. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/order_socket.py.jinja +44 -0
  140. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -1
  141. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +1 -7
  142. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/base.py.jinja +7 -0
  143. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/{orders.py.jinja → order_model.py.jinja} +7 -3
  144. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +42 -5
  145. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/order_repository.py.jinja +79 -0
  146. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja +1 -0
  147. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +4 -3
  148. python_ddd_framework/developer_kit/templates/project/cookiecutter.json +1 -1
  149. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +16 -3
  150. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +29 -22
  151. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{app.development.yaml.jinja → backend/app.development.yaml.jinja} +2 -0
  152. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{pyproject.toml.jinja → backend/pyproject.toml.jinja} +1 -1
  153. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/main.py.jinja +2 -0
  154. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/module.py.jinja +40 -3
  155. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +40 -10
  156. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +134 -23
  157. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep +0 -0
  158. python_ddd_framework/distributed_lock/contracts.py +4 -1
  159. python_ddd_framework/domain/__init__.py +2 -0
  160. python_ddd_framework/domain/services.py +11 -0
  161. python_ddd_framework/errors/business.py +10 -1
  162. python_ddd_framework/errors/lifecycle.py +3 -31
  163. python_ddd_framework/events/__init__.py +5 -2
  164. python_ddd_framework/events/aggregate.py +3 -3
  165. python_ddd_framework/events/catalog.py +12 -11
  166. python_ddd_framework/events/contracts.py +6 -1
  167. python_ddd_framework/events/contribution.py +1 -52
  168. python_ddd_framework/events/entities.py +23 -0
  169. python_ddd_framework/events/module.py +97 -0
  170. python_ddd_framework/events/runtime.py +80 -29
  171. python_ddd_framework/events/subscription.py +36 -0
  172. python_ddd_framework/events/unit_of_work.py +25 -0
  173. python_ddd_framework/extensions/__init__.py +22 -0
  174. python_ddd_framework/extensions/catalog.py +198 -0
  175. python_ddd_framework/extensions/contracts.py +85 -0
  176. python_ddd_framework/extensions/models.py +142 -0
  177. python_ddd_framework/extensions/module.py +21 -0
  178. python_ddd_framework/fastapi/__init__.py +2 -0
  179. python_ddd_framework/fastapi/action.py +1 -2
  180. python_ddd_framework/fastapi/adapter.py +16 -0
  181. python_ddd_framework/fastapi/application_services.py +43 -25
  182. python_ddd_framework/fastapi/background.py +2 -1
  183. python_ddd_framework/fastapi/contracts.py +26 -8
  184. python_ddd_framework/fastapi/health.py +1 -1
  185. python_ddd_framework/fastapi/module.py +14 -0
  186. python_ddd_framework/fastapi/parameters.py +3 -18
  187. python_ddd_framework/fastapi/realtime/connection.py +10 -7
  188. python_ddd_framework/fastapi/realtime/module.py +3 -0
  189. python_ddd_framework/fastapi/realtime/runtime.py +10 -24
  190. python_ddd_framework/fastapi/request_context.py +2 -1
  191. python_ddd_framework/fastapi/routing.py +2 -1
  192. python_ddd_framework/fastapi/server.py +8 -0
  193. python_ddd_framework/fastapi/settings.py +2 -1
  194. python_ddd_framework/hosted_services/bridge.py +20 -1
  195. python_ddd_framework/hosted_services/catalog.py +6 -16
  196. python_ddd_framework/hosted_services/contracts.py +4 -0
  197. python_ddd_framework/hosted_services/declarations.py +15 -0
  198. python_ddd_framework/hosted_services/module.py +69 -0
  199. python_ddd_framework/hosted_services/runtime.py +30 -1
  200. python_ddd_framework/identity/__init__.py +24 -0
  201. python_ddd_framework/identity/application.py +38 -90
  202. python_ddd_framework/identity/contracts.py +181 -16
  203. python_ddd_framework/identity/http_api.py +79 -2
  204. python_ddd_framework/identity/management.py +166 -0
  205. python_ddd_framework/identity/module.py +12 -4
  206. python_ddd_framework/identity/services.py +29 -16
  207. python_ddd_framework/identity/sqlalchemy/migrations/0003_extra_properties.py +26 -0
  208. python_ddd_framework/identity/sqlalchemy/migrations/0004_concurrency_version.py +29 -0
  209. python_ddd_framework/identity/sqlalchemy/models.py +21 -2
  210. python_ddd_framework/identity/sqlalchemy/module.py +11 -2
  211. python_ddd_framework/identity/sqlalchemy/repository.py +458 -0
  212. python_ddd_framework/identity/sqlalchemy/session_security.py +88 -0
  213. python_ddd_framework/identity/sqlalchemy/stores.py +69 -290
  214. python_ddd_framework/identity/tokens.py +2 -5
  215. python_ddd_framework/{application_services/validation.py → invocation/arguments.py} +8 -6
  216. python_ddd_framework/invocation/contribution.py +61 -0
  217. python_ddd_framework/invocation/dispatcher.py +291 -0
  218. python_ddd_framework/invocation/entries.py +2 -25
  219. python_ddd_framework/invocation/entrypoints.py +126 -0
  220. python_ddd_framework/{application_services → invocation}/execution.py +8 -5
  221. python_ddd_framework/invocation/function_runtime.py +3 -1
  222. python_ddd_framework/invocation/interception.py +113 -42
  223. python_ddd_framework/{application_services → invocation}/interceptors.py +2 -2
  224. python_ddd_framework/invocation/managed_proxy.py +116 -0
  225. python_ddd_framework/invocation/managed_services.py +250 -0
  226. python_ddd_framework/invocation/methods.py +345 -0
  227. python_ddd_framework/invocation/module.py +113 -0
  228. python_ddd_framework/{application_services → invocation}/policies.py +8 -1
  229. python_ddd_framework/invocation/validation.py +25 -0
  230. python_ddd_framework/lifecycle/__init__.py +2 -0
  231. python_ddd_framework/lifecycle/composition.py +5 -2
  232. python_ddd_framework/lifecycle/participants.py +33 -0
  233. python_ddd_framework/messaging/__init__.py +25 -0
  234. python_ddd_framework/messaging/channel.py +205 -0
  235. python_ddd_framework/messaging/contracts.py +69 -0
  236. python_ddd_framework/messaging/module.py +101 -0
  237. python_ddd_framework/messaging/options.py +13 -0
  238. python_ddd_framework/messaging/receipt.py +27 -0
  239. python_ddd_framework/messaging/runtime.py +124 -0
  240. python_ddd_framework/modularity/__init__.py +11 -1
  241. python_ddd_framework/modularity/contracts.py +47 -16
  242. python_ddd_framework/modularity/discovery.py +21 -2
  243. python_ddd_framework/modularity/graph.py +45 -1
  244. python_ddd_framework/modularity/registry.py +83 -3
  245. python_ddd_framework/notifications/__init__.py +2 -0
  246. python_ddd_framework/notifications/catalog.py +3 -2
  247. python_ddd_framework/notifications/declarations.py +18 -0
  248. python_ddd_framework/notifications/module.py +21 -0
  249. python_ddd_framework/observability/logging.py +3 -0
  250. python_ddd_framework/observability/tracing.py +3 -0
  251. python_ddd_framework/options/contribution.py +12 -0
  252. python_ddd_framework/options/registry.py +44 -11
  253. python_ddd_framework/realtime/__init__.py +2 -1
  254. python_ddd_framework/realtime/contracts.py +3 -7
  255. python_ddd_framework/realtime/messages.py +26 -1
  256. python_ddd_framework/redis/cache.py +243 -0
  257. python_ddd_framework/redis/cache_scripts.py +40 -0
  258. python_ddd_framework/redis/distributed_lock.py +10 -2
  259. python_ddd_framework/redis/module.py +13 -4
  260. python_ddd_framework/redis/notifications.py +5 -2
  261. python_ddd_framework/redis/runtime.py +1 -79
  262. python_ddd_framework/seeding/__init__.py +13 -0
  263. python_ddd_framework/seeding/contracts.py +45 -0
  264. python_ddd_framework/seeding/module.py +73 -0
  265. python_ddd_framework/seeding/runtime.py +63 -0
  266. python_ddd_framework/services/arbitration.py +23 -0
  267. python_ddd_framework/services/binding.py +2 -3
  268. python_ddd_framework/services/composition.py +108 -0
  269. python_ddd_framework/services/contribution.py +44 -54
  270. python_ddd_framework/services/convention.py +53 -181
  271. python_ddd_framework/services/fixed_lifetime.py +12 -15
  272. python_ddd_framework/services/framework_provider.py +3 -158
  273. python_ddd_framework/services/native_graph.py +35 -60
  274. python_ddd_framework/services/provider.py +59 -4
  275. python_ddd_framework/services/repository.py +6 -0
  276. python_ddd_framework/services/runtime.py +129 -215
  277. python_ddd_framework/settings/catalog.py +5 -4
  278. python_ddd_framework/settings/declarations.py +18 -0
  279. python_ddd_framework/settings/definitions.py +5 -0
  280. python_ddd_framework/settings/manager.py +1 -1
  281. python_ddd_framework/settings/module.py +50 -1
  282. python_ddd_framework/settings/notifications.py +4 -2
  283. python_ddd_framework/settings/refresh_module.py +9 -2
  284. python_ddd_framework/settings/sqlalchemy/module.py +2 -1
  285. python_ddd_framework/sqlalchemy/__init__.py +12 -0
  286. python_ddd_framework/sqlalchemy/alembic_runtime/env.py +1 -0
  287. python_ddd_framework/sqlalchemy/auditing.py +7 -1
  288. python_ddd_framework/sqlalchemy/extensions.py +186 -0
  289. python_ddd_framework/sqlalchemy/metadata.py +110 -6
  290. python_ddd_framework/sqlalchemy/migration.py +61 -8
  291. python_ddd_framework/sqlalchemy/migration_operations.py +86 -0
  292. python_ddd_framework/sqlalchemy/module.py +14 -11
  293. python_ddd_framework/sqlalchemy/module_migration.py +71 -3
  294. python_ddd_framework/sqlalchemy/repository.py +217 -13
  295. python_ddd_framework/sqlalchemy/session_provider.py +14 -1
  296. python_ddd_framework/testing/runtime.py +5 -4
  297. python_ddd_framework/unit_of_work/__init__.py +2 -1
  298. python_ddd_framework/unit_of_work/contracts.py +6 -7
  299. python_ddd_framework/unit_of_work/errors.py +11 -0
  300. python_ddd_framework/unit_of_work/lifecycle.py +19 -0
  301. python_ddd_framework/unit_of_work/manager.py +86 -32
  302. python_ddd_framework/unit_of_work/module.py +61 -0
  303. python_ddd_framework/unit_of_work/options.py +7 -1
  304. python_ddd_framework/validation/__init__.py +1 -0
  305. python_ddd_framework/validation/models.py +37 -0
  306. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/METADATA +139 -44
  307. python_ddd_framework-0.4.0.dist-info/RECORD +488 -0
  308. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/WHEEL +1 -1
  309. python_ddd_framework/application_services/seeding.py +0 -72
  310. python_ddd_framework/caching/catalog.py +0 -73
  311. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +0 -10
  312. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +0 -24
  313. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +0 -83
  314. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +0 -84
  315. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +0 -35
  316. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +0 -14
  317. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +0 -19
  318. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +0 -24
  319. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +0 -36
  320. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +0 -49
  321. python_ddd_framework-0.3.2.dist-info/RECORD +0 -355
  322. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.dockerignore → backend/.dockerignore} +0 -0
  323. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.gitignore → backend/.gitignore} +0 -0
  324. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.python-version → backend/.python-version} +0 -0
  325. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{Dockerfile → backend/Dockerfile} +0 -0
  326. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.dev.yaml.jinja → backend/compose.dev.yaml.jinja} +0 -0
  327. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.production.yaml.jinja → backend/compose.production.yaml.jinja} +0 -0
  328. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/__init__.py.jinja +0 -0
  329. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/conftest.py.jinja +0 -0
  330. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/host/test_http.py.jinja +0 -0
  331. /python_ddd_framework/{application_services → invocation}/signature.py +0 -0
  332. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/entry_points.txt +0 -0
  333. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/LICENSE +0 -0
  334. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import logging
7
- from collections.abc import Mapping
8
7
  from dataclasses import dataclass
9
8
  from typing import cast
10
9
  from uuid import UUID, uuid4
@@ -17,14 +16,14 @@ from ...authorization import AccessTokenAuthenticator, CurrentUser, Unauthentica
17
16
  from ...options import Options
18
17
  from ...realtime import (
19
18
  RealtimeDeliveryReport,
20
- RealtimeEnvelope,
19
+ RealtimeMessage,
21
20
  RealtimePublisher,
22
21
  RealtimeSnapshot,
23
22
  )
24
- from ...realtime.messages import _encode_envelope
23
+ from ...realtime.messages import _encode_envelope, _message_envelope
25
24
  from .connection import (
26
- _FRAMEWORK_PROBLEM_MESSAGE_TYPE,
27
25
  WebSocketConnection,
26
+ _FrameworkProblemMessage,
28
27
  _WebSocketCloseCode,
29
28
  )
30
29
  from .options import FastApiRealtimeOptions
@@ -212,21 +211,17 @@ class _FastApiRealtimeRuntime:
212
211
  async def send_to_connection(
213
212
  self,
214
213
  connection_id: UUID,
215
- *,
216
- message_type: str,
217
- payload: Mapping[str, object],
214
+ message: RealtimeMessage,
218
215
  ) -> RealtimeDeliveryReport:
219
216
  async with self._lock:
220
217
  state = self._connections.get(connection_id)
221
218
  states = () if state is None else (state,)
222
- return await self._send(states, message_type=message_type, payload=payload)
219
+ return await self._send(states, message)
223
220
 
224
221
  async def send_to_user(
225
222
  self,
226
223
  user_id: UUID,
227
- *,
228
- message_type: str,
229
- payload: Mapping[str, object],
224
+ message: RealtimeMessage,
230
225
  ) -> RealtimeDeliveryReport:
231
226
  async with self._lock:
232
227
  connection_ids = tuple(self._user_connections.get(user_id, ()))
@@ -235,7 +230,7 @@ class _FastApiRealtimeRuntime:
235
230
  for connection_id in connection_ids
236
231
  if (state := self._connections.get(connection_id)) is not None
237
232
  )
238
- return await self._send(states, message_type=message_type, payload=payload)
233
+ return await self._send(states, message)
239
234
 
240
235
  async def snapshot(self) -> RealtimeSnapshot:
241
236
  async with self._lock:
@@ -277,18 +272,10 @@ class _FastApiRealtimeRuntime:
277
272
  async def _send(
278
273
  self,
279
274
  states: tuple[_ConnectionState, ...],
280
- *,
281
- message_type: str,
282
- payload: Mapping[str, object],
275
+ message: RealtimeMessage,
283
276
  ) -> RealtimeDeliveryReport:
284
277
  correlation_id = self._invocations.correlation_id() or str(uuid4())
285
- text = _encode_envelope(
286
- RealtimeEnvelope(
287
- message_type=message_type,
288
- correlation_id=correlation_id,
289
- payload=payload,
290
- )
291
- )
278
+ text = _encode_envelope(_message_envelope(message, correlation_id))
292
279
  if not states:
293
280
  return RealtimeDeliveryReport(matched=0, enqueued=0, dropped=0)
294
281
  enqueued = 0
@@ -327,8 +314,7 @@ class _FastApiRealtimeRuntime:
327
314
  ) -> RealtimeDeliveryReport:
328
315
  return await self._send(
329
316
  (state,),
330
- message_type=_FRAMEWORK_PROBLEM_MESSAGE_TYPE,
331
- payload={"code": code, "status": status},
317
+ _FrameworkProblemMessage(code=code, status=status),
332
318
  )
333
319
 
334
320
  async def _request_close(
@@ -26,6 +26,7 @@ from ..application_services.invocation import (
26
26
  from ..auditing import AuditSink, RequestAuditRecord
27
27
  from ..authorization import AccessTokenAuthenticator, CurrentUser, UnauthenticatedError
28
28
  from ..invocation.contracts import _is_safe_correlation_id
29
+ from ..invocation.entrypoints import http_request_lease
29
30
  from ..unit_of_work import UnitOfWorkManager
30
31
  from .errors import FastApiAdapterError
31
32
 
@@ -46,7 +47,7 @@ class _RequestScopeMiddleware:
46
47
  if scope["type"] != "http":
47
48
  await self._other(scope, receive, send)
48
49
  return
49
- async with self._application._http_request_lease_for_integration():
50
+ async with http_request_lease(self._application):
50
51
  request = Request(scope, receive, send)
51
52
  container = self._application._container_for_integration()(
52
53
  {Request: request}, scope=DIScope.REQUEST
@@ -15,6 +15,7 @@ from fastapi.routing import APIRoute, APIWebSocketRoute, RouteContext, iter_rout
15
15
  from starlette.routing import BaseRoute, WebSocketRoute
16
16
 
17
17
  from ..application import Application
18
+ from ..authorization.module import AuthorizationModule
18
19
  from ..diagnostics import SourceLocation
19
20
  from ..modularity import ModuleKey
20
21
  from .application_services import (
@@ -111,7 +112,7 @@ def _build_route_plan(
111
112
  unknown = tuple(
112
113
  permission
113
114
  for permission in route._definition.authorization.permissions
114
- if permission not in application.permission_catalog
115
+ if permission not in application.modules.instance(AuthorizationModule).catalog
115
116
  )
116
117
  if unknown:
117
118
  raise FastApiAdapterError(
@@ -14,6 +14,7 @@ from pathlib import Path
14
14
  from typing import TYPE_CHECKING
15
15
 
16
16
  from uvicorn import Config, Server
17
+ from uvicorn.supervisors.statreload import StatReload
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  from uvicorn.supervisors.basereload import BaseReload as ChangeReload
@@ -125,12 +126,19 @@ class _HostReload(ChangeReload):
125
126
  self.shutdown()
126
127
 
127
128
  def restart(self) -> None:
129
+ # 接管进程退出仍须保留原生 StatReload 的 mtime 重置;否则同一次修改会循环重启。
130
+ if isinstance(self, StatReload):
131
+ self.mtimes.clear()
128
132
  # 旧进程尚未退出就一直占位;不靠超时/PID 猜测,更不强杀后立即补位。
133
+ logger = logging.getLogger(__name__)
134
+ logger.info("Host reload requesting shutdown of process %s", self.process.pid)
129
135
  self._shutdown_requested.set()
130
136
  self.process.join()
137
+ logger.info("Host reload previous process %s exited", self.process.pid)
131
138
  self._shutdown_requested.clear()
132
139
  # 原生 startup 仍负责 spawn、信号接线和 socket 传递,不复制进程创建实现。
133
140
  self.startup()
141
+ logger.info("Host reload spawned replacement process %s", self.process.pid)
134
142
 
135
143
  def shutdown(self) -> None:
136
144
  self._shutdown_requested.set()
@@ -4,6 +4,7 @@ from ..modularity import AppModule
4
4
  from ..settings.management import SettingsManagementApplicationService
5
5
  from ..settings.module import SettingsManagementModule
6
6
  from . import ApplicationServiceRouteOverride, exposes_application_services
7
+ from .module import FastApiModule
7
8
 
8
9
 
9
10
  @exposes_application_services(
@@ -37,4 +38,4 @@ from . import ApplicationServiceRouteOverride, exposes_application_services
37
38
  ),
38
39
  )
39
40
  class SettingsManagementHttpApiModule(AppModule):
40
- dependencies = (SettingsManagementModule,)
41
+ dependencies = (FastApiModule,)
@@ -15,7 +15,11 @@ from uuid import uuid4
15
15
  from dishka import AsyncContainer
16
16
 
17
17
  from ..application_services import ApplicationServiceCatalog, ApplicationServiceContract
18
- from ..application_services.invocation import _InvocationRuntime, _ScopedApplicationServiceInvoker
18
+ from ..application_services.invocation import (
19
+ _InvocationReservation,
20
+ _InvocationRuntime,
21
+ _ScopedApplicationServiceInvoker,
22
+ )
19
23
  from ..authorization import CurrentUser
20
24
  from ..invocation.contracts import _is_safe_correlation_id
21
25
  from ..invocation.function_runtime import _invoke_callable
@@ -297,6 +301,21 @@ class _HostedServiceBridge:
297
301
  kwargs,
298
302
  )
299
303
 
304
+ async def invoke_reserved(
305
+ self,
306
+ reservation: _InvocationReservation,
307
+ function: Callable[..., Awaitable[_R]],
308
+ *args: object,
309
+ ) -> _R:
310
+ """兑现已接受工作的单次许可;不重新开放 bridge/public admission。"""
311
+ self._require_application_loop("invoke_reserved")
312
+ runtime = await self._container.get(_InvocationRuntime)
313
+ async with runtime.reserved_lease(reservation):
314
+ return cast(
315
+ _R,
316
+ await self._invoke_pipeline(str(uuid4()), function, args, {}, callable_entry=True),
317
+ )
318
+
300
319
  def _validate_call(
301
320
  self,
302
321
  operation: str,
@@ -12,8 +12,9 @@ from typing import TYPE_CHECKING
12
12
  from dishka import DEFAULT_COMPONENT, DependencyKey, Provider, Scope
13
13
 
14
14
  from ..diagnostics import SourceLocation
15
- from ..modularity import AppModule, ModuleKey, ModuleRegistry
15
+ from ..modularity import ModuleKey, ModuleRegistry
16
16
  from .contracts import HostedService, _validate_service_type
17
+ from .declarations import HostedServices
17
18
  from .errors import HostedServiceContractError, HostedServiceDefinitionError
18
19
 
19
20
  if TYPE_CHECKING:
@@ -36,19 +37,16 @@ class HostedServiceRegistration:
36
37
  class HostedServiceCatalog:
37
38
  """顺序来自 Module dependency order 与各 Module 的声明顺序。"""
38
39
 
39
- __slots__ = ("_by_name", "_declarations", "_fingerprint", "_registrations")
40
+ __slots__ = ("_by_name", "_fingerprint", "_registrations")
40
41
 
41
42
  def __init__(
42
43
  self,
43
44
  registrations: tuple[HostedServiceRegistration, ...],
44
- *,
45
- declarations: tuple[tuple[type[AppModule], tuple[type[HostedService], ...]], ...],
46
45
  ) -> None:
47
46
  self._registrations = registrations
48
47
  self._by_name: Mapping[str, HostedServiceRegistration] = MappingProxyType(
49
48
  {item.name: item for item in registrations}
50
49
  )
51
- self._declarations = declarations
52
50
  encoded = json.dumps(
53
51
  [
54
52
  {
@@ -100,26 +98,18 @@ class HostedServiceCatalog:
100
98
  service=item.name,
101
99
  reason="HostedService process metadata changed after build",
102
100
  )
103
- for module_type, declared in self._declarations:
104
- if module_type.__dict__.get("hosted_services", ()) is not declared:
105
- raise HostedServiceDefinitionError(
106
- service=f"{module_type.__module__}.{module_type.__qualname__}",
107
- reason="Module hosted_services metadata changed after build",
108
- )
109
101
 
110
102
 
111
103
  def _build_hosted_service_catalog(registry: ModuleRegistry) -> HostedServiceCatalog:
112
104
  registrations: list[HostedServiceRegistration] = []
113
- declarations: list[tuple[type[AppModule], tuple[type[HostedService], ...]]] = []
114
105
  names: dict[str, ModuleKey] = {}
115
- for descriptor in registry.dependency_order:
116
- declared = descriptor.module_type.__dict__.get("hosted_services", ())
106
+ for descriptor, declaration in registry.declarations(HostedServices):
107
+ declared = declaration.services
117
108
  if not isinstance(declared, tuple):
118
109
  raise HostedServiceDefinitionError(
119
110
  service=str(descriptor.key),
120
111
  reason="Module hosted_services must be an immutable tuple",
121
112
  )
122
- declarations.append((descriptor.module_type, declared))
123
113
  for declaration_index, candidate in enumerate(declared):
124
114
  _validate_service_type(candidate)
125
115
  name = f"{candidate.__module__}.{candidate.__qualname__}"
@@ -140,7 +130,7 @@ def _build_hosted_service_catalog(registry: ModuleRegistry) -> HostedServiceCata
140
130
  run_in_background_processes=candidate.run_in_background_processes,
141
131
  )
142
132
  )
143
- return HostedServiceCatalog(tuple(registrations), declarations=tuple(declarations))
133
+ return HostedServiceCatalog(tuple(registrations))
144
134
 
145
135
 
146
136
  def _contribute_hosted_service_defaults(
@@ -7,6 +7,8 @@ from abc import ABC, abstractmethod
7
7
  from enum import Enum
8
8
  from typing import TYPE_CHECKING, ClassVar
9
9
 
10
+ from dishka import Scope
11
+
10
12
  from .errors import HostedServiceDefinitionError
11
13
 
12
14
  if TYPE_CHECKING:
@@ -17,6 +19,8 @@ if TYPE_CHECKING:
17
19
  class HostedService(ABC):
18
20
  """APP 单例;start 返回即启动成功,stop 必须等待自身资源退出。"""
19
21
 
22
+ _fixed_service_scope: ClassVar[Scope] = Scope.APP
23
+
20
24
  run_in_background_processes: ClassVar[bool] = False
21
25
 
22
26
  @abstractmethod
@@ -0,0 +1,15 @@
1
+ """托管服务声明;顺序属于声明模块,解释和运行状态属于能力模块。"""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from ..modularity import ModuleDeclaration, ModuleDependency, ModuleRef
6
+ from .contracts import HostedService
7
+
8
+
9
+ @dataclass(frozen=True, slots=True)
10
+ class HostedServices(ModuleDeclaration):
11
+ services: tuple[type[HostedService], ...]
12
+
13
+ @property
14
+ def required_module(self) -> ModuleDependency:
15
+ return ModuleRef("python_ddd_framework.hosted_services.module:HostedServicesModule")
@@ -0,0 +1,69 @@
1
+ """托管服务配置、最终绑定与运行状态的模块 owner。"""
2
+
3
+ from dishka import Provider, Scope
4
+
5
+ from ..application_services.module import ApplicationServicesModule
6
+ from ..lifecycle import ConfigureContext
7
+ from ..modularity import AppModule
8
+ from ..services.composition import (
9
+ ServiceBindingContext,
10
+ ServiceComposition,
11
+ ServiceDiscoveryContext,
12
+ ServiceValidationContext,
13
+ )
14
+ from .catalog import (
15
+ HostedServiceCatalog,
16
+ _build_hosted_service_catalog,
17
+ _contribute_hosted_service_defaults,
18
+ _validate_hosted_service_drafts,
19
+ _validate_hosted_service_entries,
20
+ )
21
+ from .options import HOSTED_SERVICE_OPTIONS_SECTION, HostedServiceOptions
22
+ from .runtime import _HostedServiceCoordinator
23
+
24
+
25
+ class HostedServicesModule(AppModule):
26
+ dependencies = (ApplicationServicesModule,)
27
+ catalog: HostedServiceCatalog
28
+ coordinator: _HostedServiceCoordinator
29
+
30
+ def configure(self, context: ConfigureContext) -> None:
31
+ context.configure.default(HostedServiceOptions, section=HOSTED_SERVICE_OPTIONS_SECTION)
32
+ context.services.add_composition(
33
+ ServiceComposition(
34
+ discover=self._discover,
35
+ before_binding=lambda entries: _validate_hosted_service_entries(
36
+ entries, self.catalog
37
+ ),
38
+ bind=self._bind,
39
+ validate=self._validate,
40
+ )
41
+ )
42
+
43
+ def _discover(self, context: ServiceDiscoveryContext) -> None:
44
+ self.catalog = _build_hosted_service_catalog(context.modules)
45
+ _contribute_hosted_service_defaults(self.catalog, context.services)
46
+
47
+ def _bind(self, context: ServiceBindingContext) -> None:
48
+ _validate_hosted_service_drafts(context.arbitration.drafts, self.catalog)
49
+ application = context.application
50
+ self.coordinator = _HostedServiceCoordinator(
51
+ catalog=self.catalog,
52
+ options=context.options.get(HostedServiceOptions),
53
+ state_getter=lambda: application.state,
54
+ )
55
+ self.runtime = self.coordinator.runtime_participant()
56
+ provider = Provider()
57
+ provider.provide(lambda: self.catalog, provides=HostedServiceCatalog, scope=Scope.APP)
58
+ provider.provide(
59
+ lambda: self.coordinator,
60
+ provides=_HostedServiceCoordinator,
61
+ scope=Scope.APP,
62
+ )
63
+ context.add_provider(provider)
64
+
65
+ def _validate(self, context: ServiceValidationContext) -> None:
66
+ self.coordinator.attach_container(
67
+ context.container,
68
+ context.binding.modules.instance(ApplicationServicesModule).catalog,
69
+ )
@@ -10,7 +10,7 @@ from typing import cast
10
10
  from dishka import AsyncContainer
11
11
 
12
12
  from ..application_services import ApplicationServiceCatalog
13
- from ..lifecycle import ApplicationState, ShutdownReason
13
+ from ..lifecycle import ApplicationState, RuntimeParticipant, ShutdownReason
14
14
  from .bridge import HostedServiceContext, _HostedServiceBridge
15
15
  from .catalog import HostedServiceCatalog, HostedServiceRegistration
16
16
  from .contracts import HostedService, HostedServiceStatus
@@ -72,6 +72,35 @@ class _HostedServiceCoordinator:
72
72
  def readiness(self) -> ApplicationReadinessSnapshot:
73
73
  return self._state_owner.snapshot(self._state_getter())
74
74
 
75
+ def runtime_participant(self) -> RuntimeParticipant:
76
+ enabled = True
77
+
78
+ def prepare(run_services: bool) -> None:
79
+ nonlocal enabled
80
+ enabled = run_services
81
+
82
+ async def start() -> None:
83
+ if not enabled:
84
+ return
85
+ try:
86
+ await self.start()
87
+ except HostedServiceLifecycleError:
88
+ raise
89
+ except BaseException as error:
90
+ raise HostedServiceLifecycleError(
91
+ service=type(self).__qualname__,
92
+ operation="start",
93
+ error_type=type(error).__name__,
94
+ ) from error
95
+
96
+ return RuntimeParticipant(
97
+ prepare=prepare,
98
+ start=start,
99
+ stop=self.stop,
100
+ close_admission=self.close_admission,
101
+ ready=lambda: self.readiness().ready,
102
+ )
103
+
75
104
  def select_child(self) -> None:
76
105
  if self._started:
77
106
  raise RuntimeError("Hosted Service process selection must precede start")
@@ -1,11 +1,17 @@
1
+ from .application import (
2
+ DefaultAuthenticationApplicationService,
3
+ )
1
4
  from .contracts import (
2
5
  IDENTITY_ROLES_MANAGE,
6
+ IDENTITY_USER_EXTENSION,
3
7
  IDENTITY_USERS_MANAGE,
4
8
  AuthenticationApplicationService,
9
+ ChangePasswordCommand,
5
10
  CreateRoleCommand,
6
11
  CreateUserCommand,
7
12
  CurrentUserView,
8
13
  IdentityAccessStore,
14
+ IdentityConcurrencyCommand,
9
15
  IdentityLoginStore,
10
16
  IdentityManagementApplicationService,
11
17
  IdentityOptions,
@@ -19,10 +25,16 @@ from .contracts import (
19
25
  RefreshSessionStore,
20
26
  RefreshTokenCommand,
21
27
  RevokeTokenCommand,
28
+ RolePermissionsView,
22
29
  RoleView,
30
+ SetPasswordCommand,
23
31
  SetRolePermissionsCommand,
32
+ SetUserActiveCommand,
24
33
  SetUserRolesCommand,
25
34
  TokenPair,
35
+ UpdateRoleCommand,
36
+ UpdateUserCommand,
37
+ UserRolesView,
26
38
  UserView,
27
39
  )
28
40
  from .errors import (
@@ -31,6 +43,7 @@ from .errors import (
31
43
  IdentityResourceNotFoundError,
32
44
  InvalidCredentialsError,
33
45
  )
46
+ from .management import DefaultIdentityManagementApplicationService
34
47
  from .module import IdentityModule
35
48
  from .passwords import PasswordHasher
36
49
  from .services import IdentityAuthenticator
@@ -39,12 +52,17 @@ from .tokens import JwtTokenService
39
52
  __all__ = (
40
53
  "IDENTITY_ROLES_MANAGE",
41
54
  "IDENTITY_USERS_MANAGE",
55
+ "IDENTITY_USER_EXTENSION",
42
56
  "AuthenticationApplicationService",
57
+ "ChangePasswordCommand",
43
58
  "CreateRoleCommand",
44
59
  "CreateUserCommand",
45
60
  "CurrentUserView",
61
+ "DefaultAuthenticationApplicationService",
62
+ "DefaultIdentityManagementApplicationService",
46
63
  "IdentityAccessStore",
47
64
  "IdentityAuthenticator",
65
+ "IdentityConcurrencyCommand",
48
66
  "IdentityConflictError",
49
67
  "IdentityDefinitionError",
50
68
  "IdentityLoginStore",
@@ -65,9 +83,15 @@ __all__ = (
65
83
  "RefreshSessionStore",
66
84
  "RefreshTokenCommand",
67
85
  "RevokeTokenCommand",
86
+ "RolePermissionsView",
68
87
  "RoleView",
88
+ "SetPasswordCommand",
69
89
  "SetRolePermissionsCommand",
90
+ "SetUserActiveCommand",
70
91
  "SetUserRolesCommand",
71
92
  "TokenPair",
93
+ "UpdateRoleCommand",
94
+ "UpdateUserCommand",
95
+ "UserRolesView",
72
96
  "UserView",
73
97
  )