quadkit 0.0.44__tar.gz → 0.0.45__tar.gz

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 (673) hide show
  1. {quadkit-0.0.44 → quadkit-0.0.45}/PKG-INFO +3 -3
  2. {quadkit-0.0.44 → quadkit-0.0.45}/README.md +2 -2
  3. {quadkit-0.0.44 → quadkit-0.0.45}/pyproject.toml +1 -1
  4. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/base.py +19 -1
  5. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/channel.py +44 -18
  6. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/orchestrator.py +18 -0
  7. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/resolver.py +20 -0
  8. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/base.py +134 -5
  9. quadkit-0.0.45/src/quadkit/domain/models/coercion.py +67 -0
  10. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/ambient.py +18 -0
  11. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/processors.py +53 -13
  12. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/config.py +19 -1
  13. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/core/mapper.py +97 -17
  14. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/__init__.py +58 -1
  15. quadkit-0.0.45/src/quadkit/primitives/typing.py +239 -0
  16. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/domain.py +6 -2
  17. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/coercion.py +16 -6
  18. quadkit-0.0.45/tests/unit/concurrency/test_channel_close_wakes_receivers.py +115 -0
  19. quadkit-0.0.45/tests/unit/domain/test_domain_hydration.py +388 -0
  20. quadkit-0.0.45/tests/unit/mapping/test_auto_map_dataclass.py +142 -0
  21. quadkit-0.0.45/tests/unit/primitives/test_public_surface_is_static.py +33 -0
  22. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_coercion.py +7 -4
  23. quadkit-0.0.44/src/quadkit/domain/models/coercion.py +0 -138
  24. quadkit-0.0.44/src/quadkit/primitives/typing.py +0 -123
  25. {quadkit-0.0.44 → quadkit-0.0.45}/.gitignore +0 -0
  26. {quadkit-0.0.44 → quadkit-0.0.45}/LICENSE +0 -0
  27. {quadkit-0.0.44 → quadkit-0.0.45}/NOTICE +0 -0
  28. {quadkit-0.0.44 → quadkit-0.0.45}/example.yaml +0 -0
  29. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/__init__.py +0 -0
  30. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/__init__.py +0 -0
  31. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/asgi_lifespan.py +0 -0
  32. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/__init__.py +0 -0
  33. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/discovery.py +0 -0
  34. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/models.py +0 -0
  35. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config_validation.py +0 -0
  36. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/constants.py +0 -0
  37. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/di/__init__.py +0 -0
  38. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/di/provider.py +0 -0
  39. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/discovery.py +0 -0
  40. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/events.py +0 -0
  41. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/exceptions.py +0 -0
  42. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/factory.py +0 -0
  43. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/health_probes.py +0 -0
  44. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/injectable_provider.py +0 -0
  45. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/invoker.py +0 -0
  46. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/lifecycle.py +0 -0
  47. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/module.py +0 -0
  48. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/pipeline.py +0 -0
  49. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/protocols.py +0 -0
  50. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/py.typed +0 -0
  51. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/runner.py +0 -0
  52. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/secrets.py +0 -0
  53. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/standard.py +0 -0
  54. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/types.py +0 -0
  55. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/__init__.py +0 -0
  56. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/base.py +0 -0
  57. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/field_types.py +0 -0
  58. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/__init__.py +0 -0
  59. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/__init__.py +0 -0
  60. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/py.typed +0 -0
  61. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/sync_bridge.py +0 -0
  62. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/__init__.py +0 -0
  63. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/py.typed +0 -0
  64. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/config.py +0 -0
  65. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/constants.py +0 -0
  66. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/__init__.py +0 -0
  67. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/provider.py +0 -0
  68. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/py.typed +0 -0
  69. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/exceptions.py +0 -0
  70. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/__init__.py +0 -0
  71. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/dispatcher.py +0 -0
  72. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/parallel.py +0 -0
  73. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/py.typed +0 -0
  74. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/task_manager.py +0 -0
  75. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/__init__.py +0 -0
  76. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/py.typed +0 -0
  77. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/rwlock.py +0 -0
  78. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/module.py +0 -0
  79. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/py.typed +0 -0
  80. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/task_utils.py +0 -0
  81. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/__init__.py +0 -0
  82. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/base.py +0 -0
  83. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/constants.py +0 -0
  84. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/di/__init__.py +0 -0
  85. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/di/provider.py +0 -0
  86. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/exceptions.py +0 -0
  87. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/__init__.py +0 -0
  88. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/merge.py +0 -0
  89. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/registry.py +0 -0
  90. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/sources.py +0 -0
  91. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/validation.py +0 -0
  92. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/watcher.py +0 -0
  93. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/main.py +0 -0
  94. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/protocols.py +0 -0
  95. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/secrets.py +0 -0
  96. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/types.py +0 -0
  97. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/constants.py +0 -0
  98. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/__init__.py +0 -0
  99. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/_graph.py +0 -0
  100. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/builder.py +0 -0
  101. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/config/__init__.py +0 -0
  102. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/config/models.py +0 -0
  103. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/constants.py +0 -0
  104. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/__init__.py +0 -0
  105. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container.py +0 -0
  106. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_introspection.py +0 -0
  107. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_resolution.py +0 -0
  108. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_runtime.py +0 -0
  109. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/registrar.py +0 -0
  110. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/resolver.py +0 -0
  111. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/scope.py +0 -0
  112. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/validation.py +0 -0
  113. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/context.py +0 -0
  114. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/decorators.py +0 -0
  115. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/exceptions.py +0 -0
  116. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/__init__.py +0 -0
  117. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/aop_interceptors.py +0 -0
  118. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/discovery.py +0 -0
  119. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/interceptors.py +0 -0
  120. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/strategies.py +0 -0
  121. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/validator.py +0 -0
  122. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/function_provider.py +0 -0
  123. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/integration/__init__.py +0 -0
  124. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/integration/provider.py +0 -0
  125. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/markers.py +0 -0
  126. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/__init__.py +0 -0
  127. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/base.py +0 -0
  128. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/__init__.py +0 -0
  129. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/_graph_utils.py +0 -0
  130. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/compiler.py +0 -0
  131. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/context.py +0 -0
  132. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/__init__.py +0 -0
  133. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/collect.py +0 -0
  134. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/detect_cycles.py +0 -0
  135. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/expand.py +0 -0
  136. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/order.py +0 -0
  137. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/protocol.py +0 -0
  138. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/validate.py +0 -0
  139. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/visibility.py +0 -0
  140. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/constants.py +0 -0
  141. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/decorator.py +0 -0
  142. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/dynamic.py +0 -0
  143. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/errors.py +0 -0
  144. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/graph.py +0 -0
  145. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/introspection.py +0 -0
  146. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/metadata.py +0 -0
  147. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/registry.py +0 -0
  148. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/__init__.py +0 -0
  149. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/events.py +0 -0
  150. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/health.py +0 -0
  151. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/lifecycle.py +0 -0
  152. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/registry.py +0 -0
  153. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/protocols.py +0 -0
  154. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/provider.py +0 -0
  155. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/provider_utils.py +0 -0
  156. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/py.typed +0 -0
  157. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/__init__.py +0 -0
  158. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/context.py +0 -0
  159. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/descriptor.py +0 -0
  160. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/diagnostics.py +0 -0
  161. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/injector.py +0 -0
  162. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/invoker.py +0 -0
  163. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/registry.py +0 -0
  164. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/store.py +0 -0
  165. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/type_hints.py +0 -0
  166. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/validator.py +0 -0
  167. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/types.py +0 -0
  168. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/__init__.py +0 -0
  169. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/constants.py +0 -0
  170. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/exceptions.py +0 -0
  171. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/__init__.py +0 -0
  172. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/aggregate.py +0 -0
  173. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/entity.py +0 -0
  174. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/factory.py +0 -0
  175. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/py.typed +0 -0
  176. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/value_object.py +0 -0
  177. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/module.py +0 -0
  178. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/py.typed +0 -0
  179. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/types.py +0 -0
  180. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/unit_of_work.py +0 -0
  181. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/exceptions.py +0 -0
  182. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/__init__.py +0 -0
  183. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/ambient.py +0 -0
  184. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/registry.py +0 -0
  185. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/types.py +0 -0
  186. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/__init__.py +0 -0
  187. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/config.py +0 -0
  188. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/constants.py +0 -0
  189. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/di/__init__.py +0 -0
  190. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/di/provider.py +0 -0
  191. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/generator.py +0 -0
  192. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/README.md +0 -0
  193. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/__init__.py +0 -0
  194. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/__init__.py +0 -0
  195. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/models.py +0 -0
  196. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/redaction.py +0 -0
  197. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/sampling.py +0 -0
  198. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/configurator.py +0 -0
  199. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/constants.py +0 -0
  200. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/debug.py +0 -0
  201. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/di/__init__.py +0 -0
  202. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/di/provider.py +0 -0
  203. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/factory.py +0 -0
  204. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/redaction.py +0 -0
  205. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/types.py +0 -0
  206. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/__init__.py +0 -0
  207. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/constants.py +0 -0
  208. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/core/__init__.py +0 -0
  209. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/di/__init__.py +0 -0
  210. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/di/provider.py +0 -0
  211. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/exceptions.py +0 -0
  212. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/module.py +0 -0
  213. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/protocols.py +0 -0
  214. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/py.typed +0 -0
  215. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/types.py +0 -0
  216. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/__init__.py +0 -0
  217. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/__init__.py +0 -0
  218. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/caching.py +0 -0
  219. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/di.py +0 -0
  220. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/observability.py +0 -0
  221. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/resilience.py +0 -0
  222. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/routing.py +0 -0
  223. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/validation.py +0 -0
  224. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/config.py +0 -0
  225. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/constants.py +0 -0
  226. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/__init__.py +0 -0
  227. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/chain.py +0 -0
  228. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/exception_filters.py +0 -0
  229. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/registry.py +0 -0
  230. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/di/__init__.py +0 -0
  231. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/di/provider.py +0 -0
  232. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/exceptions.py +0 -0
  233. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/module.py +0 -0
  234. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/types.py +0 -0
  235. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/module.py +0 -0
  236. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/__init__.py +0 -0
  237. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/core.py +0 -0
  238. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/__init__.py +0 -0
  239. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/sub_providers/__init__.py +0 -0
  240. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/sub_providers/observability.py +0 -0
  241. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/__init__.py +0 -0
  242. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/discovery.py +0 -0
  243. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/engine.py +0 -0
  244. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/exceptions.py +0 -0
  245. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/module.py +0 -0
  246. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/state.py +0 -0
  247. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/builder.py +0 -0
  248. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/clock.py +0 -0
  249. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/constants.py +0 -0
  250. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context.py +0 -0
  251. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context_keys.py +0 -0
  252. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context_registry.py +0 -0
  253. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/data.py +0 -0
  254. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/di/__init__.py +0 -0
  255. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/di/provider.py +0 -0
  256. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/exceptions.py +0 -0
  257. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/lazy.py +0 -0
  258. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/pipeline.py +0 -0
  259. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/protocols.py +0 -0
  260. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/__init__.py +0 -0
  261. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/core.py +0 -0
  262. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/introspection.py +0 -0
  263. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/trusted_html.py +0 -0
  264. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/types.py +0 -0
  265. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/py.typed +0 -0
  266. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/__init__.py +0 -0
  267. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/core.py +0 -0
  268. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/exceptions.py +0 -0
  269. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/__init__.py +0 -0
  270. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/control.py +0 -0
  271. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/time_ops.py +0 -0
  272. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/transform.py +0 -0
  273. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/retry.py +0 -0
  274. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/subjects.py +0 -0
  275. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/__init__.py +0 -0
  276. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/_pipeline.py +0 -0
  277. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/errors.py +0 -0
  278. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/types.py +0 -0
  279. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/utils.py +0 -0
  280. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/__init__.py +0 -0
  281. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/base.py +0 -0
  282. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/canonical_json.py +0 -0
  283. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/content_addressed.py +0 -0
  284. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/__init__.py +0 -0
  285. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/config.py +0 -0
  286. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/constants.py +0 -0
  287. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/decorators.py +0 -0
  288. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/encryption/__init__.py +0 -0
  289. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/encryption/service.py +0 -0
  290. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/events.py +0 -0
  291. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/exceptions.py +0 -0
  292. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/__init__.py +0 -0
  293. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/chain.py +0 -0
  294. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/decorator.py +0 -0
  295. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/__init__.py +0 -0
  296. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/ambient.py +0 -0
  297. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/digest.py +0 -0
  298. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/kdf.py +0 -0
  299. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hooks.py +0 -0
  300. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/provider.py +0 -0
  301. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/py.typed +0 -0
  302. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/__init__.py +0 -0
  303. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/filename.py +0 -0
  304. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/html.py +0 -0
  305. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/sanitizer.py +0 -0
  306. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/url.py +0 -0
  307. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/__init__.py +0 -0
  308. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/rotation.py +0 -0
  309. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/store.py +0 -0
  310. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/types.py +0 -0
  311. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/types.py +0 -0
  312. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/__init__.py +0 -0
  313. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/__init__.py +0 -0
  314. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/json.py +0 -0
  315. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/py.typed +0 -0
  316. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/config.py +0 -0
  317. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/constants.py +0 -0
  318. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/__init__.py +0 -0
  319. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/provider.py +0 -0
  320. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/py.typed +0 -0
  321. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/__init__.py +0 -0
  322. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/py.typed +0 -0
  323. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/exceptions.py +0 -0
  324. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/module.py +0 -0
  325. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/py.typed +0 -0
  326. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/__init__.py +0 -0
  327. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/py.typed +0 -0
  328. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/registry.py +0 -0
  329. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/__init__.py +0 -0
  330. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/py.typed +0 -0
  331. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/schema.py +0 -0
  332. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/types.py +0 -0
  333. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/types.py +0 -0
  334. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/__init__.py +0 -0
  335. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/config.py +0 -0
  336. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/constants.py +0 -0
  337. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/decorators.py +0 -0
  338. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/__init__.py +0 -0
  339. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/py.typed +0 -0
  340. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/validator.py +0 -0
  341. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/exceptions.py +0 -0
  342. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/module.py +0 -0
  343. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/py.typed +0 -0
  344. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/__init__.py +0 -0
  345. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/py.typed +0 -0
  346. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/rules.py +0 -0
  347. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/__init__.py +0 -0
  348. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/fields.py +0 -0
  349. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/introspection.py +0 -0
  350. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/py.typed +0 -0
  351. {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/types.py +0 -0
  352. {quadkit-0.0.44 → quadkit-0.0.45}/tests/conftest.py +0 -0
  353. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/__init__.py +0 -0
  354. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/__init__.py +0 -0
  355. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/_fixtures.py +0 -0
  356. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/conftest.py +0 -0
  357. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_boot_level.py +0 -0
  358. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_cross_method.py +0 -0
  359. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_dependency_chain.py +0 -0
  360. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_exports.py +0 -0
  361. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_importers.py +0 -0
  362. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_module_health_integration.py +0 -0
  363. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_module_lifecycle_integration.py +0 -0
  364. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_name_lookup.py +0 -0
  365. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_stub_enforcement_integration.py +0 -0
  366. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/test_visibility_enforcement_e2e.py +0 -0
  367. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/mapping/test_mapping_lifecycle.py +0 -0
  368. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/middleware/test_middleware_lifecycle.py +0 -0
  369. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_corrected_patterns_integration.py +0 -0
  370. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_database_integration.py +0 -0
  371. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_full_pipeline.py +0 -0
  372. {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_monitor_config_integration.py +0 -0
  373. {quadkit-0.0.44 → quadkit-0.0.45}/tests/test.md +0 -0
  374. {quadkit-0.0.44 → quadkit-0.0.45}/tests/test_import_diagnostic.py +0 -0
  375. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/__init__.py +0 -0
  376. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/aop/test_interceptor_timeout.py +0 -0
  377. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/clock/test_clock.py +0 -0
  378. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/__init__.py +0 -0
  379. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_generator_base.py +0 -0
  380. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_staged_generation.py +0 -0
  381. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_template_overrides.py +0 -0
  382. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/conftest.py +0 -0
  383. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_bounded_channel.py +0 -0
  384. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_channel.py +0 -0
  385. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_config.py +0 -0
  386. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_constants.py +0 -0
  387. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_constants_new.py +0 -0
  388. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_exceptions.py +0 -0
  389. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_parallel.py +0 -0
  390. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_provider_config.py +0 -0
  391. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_rwlock.py +0 -0
  392. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_config_pool.py +0 -0
  393. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_dispatcher.py +0 -0
  394. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_sync_bridge.py +0 -0
  395. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_task_manager.py +0 -0
  396. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_task_utils.py +0 -0
  397. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_constants_new.py +0 -0
  398. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_protocols.py +0 -0
  399. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_sources.py +0 -0
  400. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_env_interpolation.py +0 -0
  401. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_loader_fail_fast.py +0 -0
  402. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_logging_redaction_config.py +0 -0
  403. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_logging_sampling.py +0 -0
  404. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_profile_overlay.py +0 -0
  405. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_scoping.py +0 -0
  406. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_secrets_validator.py +0 -0
  407. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_strict_bool_env.py +0 -0
  408. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_strict_sections.py +0 -0
  409. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_context_correlation.py +0 -0
  410. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_core_constants.py +0 -0
  411. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_core_types.py +0 -0
  412. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_request_context_di.py +0 -0
  413. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_typing.py +0 -0
  414. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/validation/test_protocols.py +0 -0
  415. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/__init__.py +0 -0
  416. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/integration/test_provider.py +0 -0
  417. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/__init__.py +0 -0
  418. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/conftest.py +0 -0
  419. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_compiler.py +0 -0
  420. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_decorator.py +0 -0
  421. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_dynamic.py +0 -0
  422. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_errors.py +0 -0
  423. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_global.py +0 -0
  424. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_graph_reflection.py +0 -0
  425. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_health_graph.py +0 -0
  426. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_integration.py +0 -0
  427. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_introspection.py +0 -0
  428. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_lifecycle.py +0 -0
  429. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_module_lifecycle.py +0 -0
  430. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_reexport.py +0 -0
  431. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_registry.py +0 -0
  432. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_stub_compiler.py +0 -0
  433. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_stub_decorator.py +0 -0
  434. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_visibility.py +0 -0
  435. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_advanced_features.py +0 -0
  436. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_basics.py +0 -0
  437. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_builder.py +0 -0
  438. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_freeze.py +0 -0
  439. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_hardening.py +0 -0
  440. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_visibility.py +0 -0
  441. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_decorators.py +0 -0
  442. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_descriptor.py +0 -0
  443. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_constants_new.py +0 -0
  444. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_decorators.py +0 -0
  445. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_provider_config_wiring.py +0 -0
  446. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_diagnostics.py +0 -0
  447. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_extensions_discovery.py +0 -0
  448. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_extensions_validator.py +0 -0
  449. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_function_provider_boot.py +0 -0
  450. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_graph_validator.py +0 -0
  451. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_interceptor_registry.py +0 -0
  452. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_invoker_unit.py +0 -0
  453. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_lifecycle_orchestrator.py +0 -0
  454. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_validation.py +0 -0
  455. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_visibility_boot.py +0 -0
  456. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_visibility_enforcement.py +0 -0
  457. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_named_binding.py +0 -0
  458. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_orchestrator_health.py +0 -0
  459. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_basics.py +0 -0
  460. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_config_injection.py +0 -0
  461. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_conformance.py +0 -0
  462. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_orchestration.py +0 -0
  463. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_reset_state.py +0 -0
  464. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_utils.py +0 -0
  465. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_scope_concurrency.py +0 -0
  466. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_scope_manager.py +0 -0
  467. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_service_store.py +0 -0
  468. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_type_hints.py +0 -0
  469. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_validation.py +0 -0
  470. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/conftest.py +0 -0
  471. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_aggregates.py +0 -0
  472. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain.py +0 -0
  473. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_constants.py +0 -0
  474. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_exceptions.py +0 -0
  475. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_init.py +0 -0
  476. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_model_strict.py +0 -0
  477. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_module.py +0 -0
  478. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_models_factory.py +0 -0
  479. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_value_object.py +0 -0
  480. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/identity/test_identity.py +0 -0
  481. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/logging/test_logging_constants_new.py +0 -0
  482. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/conftest.py +0 -0
  483. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_auto_map.py +0 -0
  484. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_exceptions.py +0 -0
  485. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_constants_new.py +0 -0
  486. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_exceptions.py +0 -0
  487. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_mapper.py +0 -0
  488. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_module.py +0 -0
  489. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_types.py +0 -0
  490. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_protocol.py +0 -0
  491. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_registry.py +0 -0
  492. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/conftest.py +0 -0
  493. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_builtins.py +0 -0
  494. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_chain.py +0 -0
  495. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_config_alignment.py +0 -0
  496. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_exception_filters.py +0 -0
  497. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_chain.py +0 -0
  498. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_constants.py +0 -0
  499. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_constants_new.py +0 -0
  500. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_ep_discovery.py +0 -0
  501. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_exceptions.py +0 -0
  502. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_module.py +0 -0
  503. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_routing.py +0 -0
  504. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_types.py +0 -0
  505. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_p2_retry_traceback.py +0 -0
  506. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_registry_defaults.py +0 -0
  507. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_registry_with_resilience.py +0 -0
  508. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_discovery.py +0 -0
  509. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_engine.py +0 -0
  510. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_state.py +0 -0
  511. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_state_hardening.py +0 -0
  512. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_validation.py +0 -0
  513. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_primitives_constants.py +0 -0
  514. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_registry_advanced.py +0 -0
  515. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_registry_introspection.py +0 -0
  516. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_trusted_html.py +0 -0
  517. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_control.py +0 -0
  518. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_core.py +0 -0
  519. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_facade.py +0 -0
  520. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_retry.py +0 -0
  521. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_subjects.py +0 -0
  522. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_time_ops.py +0 -0
  523. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_transform.py +0 -0
  524. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/__init__.py +0 -0
  525. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_exceptions.py +0 -0
  526. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_guards.py +0 -0
  527. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_hashing.py +0 -0
  528. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_package_hooks_p2.py +0 -0
  529. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secret_rotation_policy.py +0 -0
  530. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secrets_rotation.py +0 -0
  531. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secrets_store.py +0 -0
  532. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_api.py +0 -0
  533. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_components_p2.py +0 -0
  534. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_constants.py +0 -0
  535. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_exceptions.py +0 -0
  536. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_types.py +0 -0
  537. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/conftest.py +0 -0
  538. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_schema.py +0 -0
  539. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_constants.py +0 -0
  540. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_di_provider.py +0 -0
  541. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_domain.py +0 -0
  542. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_init.py +0 -0
  543. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_json.py +0 -0
  544. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_registry.py +0 -0
  545. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_types.py +0 -0
  546. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_boot_validation.py +0 -0
  547. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_config_and_misc.py +0 -0
  548. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_constants.py +0 -0
  549. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_discovery.py +0 -0
  550. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_health_baseline.py +0 -0
  551. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_health_config.py +0 -0
  552. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_runner_and_lifespan.py +0 -0
  553. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_types.py +0 -0
  554. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_validation_and_misc.py +0 -0
  555. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application.py +0 -0
  556. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application_base.py +0 -0
  557. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application_lifecycle.py +0 -0
  558. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_base_and_misc_exceptions.py +0 -0
  559. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_clock_and_pipeline_primitives.py +0 -0
  560. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_codegen.py +0 -0
  561. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_compiler_graph_utils.py +0 -0
  562. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_component_exceptions.py +0 -0
  563. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_concurrency_constants.py +0 -0
  564. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_concurrency_exceptions.py +0 -0
  565. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config.py +0 -0
  566. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_constants.py +0 -0
  567. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_hardening.py +0 -0
  568. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_quadkit.py +0 -0
  569. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_requirements.py +0 -0
  570. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_sources.py +0 -0
  571. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_types.py +0 -0
  572. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_watcher.py +0 -0
  573. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_exceptions.py +0 -0
  574. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_introspection.py +0 -0
  575. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_override.py +0 -0
  576. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_resolver_impl.py +0 -0
  577. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_runtime_mixin.py +0 -0
  578. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_context_registry.py +0 -0
  579. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_contract_completeness.py +0 -0
  580. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_builder.py +0 -0
  581. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_constants.py +0 -0
  582. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_lazy.py +0 -0
  583. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_module.py +0 -0
  584. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_pipeline.py +0 -0
  585. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_dependency_injector.py +0 -0
  586. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_constants.py +0 -0
  587. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_descriptor.py +0 -0
  588. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_module_constants.py +0 -0
  589. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_constants.py +0 -0
  590. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_exceptions.py +0 -0
  591. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_model_base.py +0 -0
  592. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_dynamic_module.py +0 -0
  593. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_events_security_exceptions.py +0 -0
  594. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_execution_result.py +0 -0
  595. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hatch_build_targets.py +0 -0
  596. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_health_probes.py +0 -0
  597. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry.py +0 -0
  598. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_edge_cases.py +0 -0
  599. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_introspection.py +0 -0
  600. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_scenarios.py +0 -0
  601. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_identity_generators_and_ambient.py +0 -0
  602. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_infra_exceptions.py +0 -0
  603. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_input_sanitizers.py +0 -0
  604. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_interceptors.py +0 -0
  605. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_json_serialization_backend.py +0 -0
  606. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_apply_redaction.py +0 -0
  607. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_bridging.py +0 -0
  608. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_constants.py +0 -0
  609. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_debug.py +0 -0
  610. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_di_provider.py +0 -0
  611. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_factory.py +0 -0
  612. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_init.py +0 -0
  613. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_redaction.py +0 -0
  614. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_redaction_compound.py +0 -0
  615. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_types.py +0 -0
  616. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_m13_m14_verification.py +0 -0
  617. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapper.py +0 -0
  618. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapper_enhanced.py +0 -0
  619. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapping.py +0 -0
  620. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_enhancements.py +0 -0
  621. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_exceptions.py +0 -0
  622. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_new.py +0 -0
  623. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_decorator.py +0 -0
  624. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_scan_and_provide.py +0 -0
  625. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_system.py +0 -0
  626. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_no_db_imports.py +0 -0
  627. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_observability.py +0 -0
  628. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_orchestrator_parallel.py +0 -0
  629. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_p2_scope_log_level.py +0 -0
  630. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_context.py +0 -0
  631. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_data.py +0 -0
  632. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_registry.py +0 -0
  633. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_provider_boot_enhanced.py +0 -0
  634. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_provider_decorator.py +0 -0
  635. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_quadkit_constants.py +0 -0
  636. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_quadkit_exceptions.py +0 -0
  637. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_repr_helpers.py +0 -0
  638. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_resilience_exceptions.py +0 -0
  639. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_combinators.py +0 -0
  640. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_core.py +0 -0
  641. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_pipeline.py +0 -0
  642. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_pipeline_and_utils.py +0 -0
  643. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_types_new.py +0 -0
  644. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_utils.py +0 -0
  645. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_round22_hardening.py +0 -0
  646. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_round41_hardening.py +0 -0
  647. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_saga_primitives.py +0 -0
  648. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_sampling_and_config.py +0 -0
  649. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_secret_stores_and_rotation.py +0 -0
  650. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_serialization_constants.py +0 -0
  651. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_serialization_exceptions.py +0 -0
  652. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_service_descriptor.py +0 -0
  653. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_structured_logging.py +0 -0
  654. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_time_ops.py +0 -0
  655. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_types.py +0 -0
  656. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_exceptions.py +0 -0
  657. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_rules.py +0 -0
  658. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_validator_engine.py +0 -0
  659. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/__init__.py +0 -0
  660. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/conftest.py +0 -0
  661. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_async_validator.py +0 -0
  662. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_coercion.py +0 -0
  663. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_helpers.py +0 -0
  664. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation.py +0 -0
  665. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_constants.py +0 -0
  666. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_constants_new.py +0 -0
  667. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_fields.py +0 -0
  668. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_fields_extended.py +0 -0
  669. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_init.py +0 -0
  670. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_introspection.py +0 -0
  671. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_module.py +0 -0
  672. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_types.py +0 -0
  673. {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: quadkit
3
- Version: 0.0.44
3
+ Version: 0.0.45
4
4
  Summary: Async-first DI/IoC framework for Python — core package
5
5
  Project-URL: Homepage, https://dbtinoy-.github.io/quadkit/
6
6
  Project-URL: Repository, https://github.com/dbtinoy-/quadkit
@@ -251,10 +251,10 @@ report vulnerabilities privately per
251
251
 
252
252
  ## Stability
253
253
 
254
- Version `0.0.44` in the `0.x` series, released in lockstep with the
254
+ Version `0.0.45` in the `0.x` series, released in lockstep with the
255
255
  other four distributions; APIs may change between minor versions until
256
256
  1.0 — pin an exact version
257
- (`quadkit==0.0.44`) or a tight range (`>=0.0.44,<0.1.0`). Full policy:
257
+ (`quadkit==0.0.45`) or a tight range (`>=0.0.45,<0.1.0`). Full policy:
258
258
  [stability and compatibility](https://dbtinoy-.github.io/quadkit/reference/stability/).
259
259
 
260
260
  ## Links
@@ -199,10 +199,10 @@ report vulnerabilities privately per
199
199
 
200
200
  ## Stability
201
201
 
202
- Version `0.0.44` in the `0.x` series, released in lockstep with the
202
+ Version `0.0.45` in the `0.x` series, released in lockstep with the
203
203
  other four distributions; APIs may change between minor versions until
204
204
  1.0 — pin an exact version
205
- (`quadkit==0.0.44`) or a tight range (`>=0.0.44,<0.1.0`). Full policy:
205
+ (`quadkit==0.0.45`) or a tight range (`>=0.0.45,<0.1.0`). Full policy:
206
206
  [stability and compatibility](https://dbtinoy-.github.io/quadkit/reference/stability/).
207
207
 
208
208
  ## Links
@@ -29,7 +29,7 @@ build-backend = "hatchling.build"
29
29
 
30
30
  [project]
31
31
  name = "quadkit"
32
- version = "0.0.44"
32
+ version = "0.0.45"
33
33
  description = "Async-first DI/IoC framework for Python — core package"
34
34
  readme = "README.md"
35
35
  requires-python = ">=3.11"
@@ -29,6 +29,7 @@ if TYPE_CHECKING:
29
29
  from collections.abc import AsyncIterator
30
30
 
31
31
  from quadkit.di.module.dynamic import DynamicModule
32
+ from quadkit.di.module.graph import CompiledModuleGraph
32
33
  from quadkit.di.provider import Provider
33
34
  from quadkit.logging import LoggerProtocol
34
35
 
@@ -73,7 +74,7 @@ class Application(SecretsMixin, HealthProbeMixin):
73
74
  self._state = AppState.CREATED
74
75
  self._container = Container()
75
76
  self._container.singleton(type(self), self)
76
- self._orchestrator = ProviderOrchestrator(self._container)
77
+ self._orchestrator: ProviderOrchestrator = ProviderOrchestrator(self._container)
77
78
 
78
79
  self._config = config or QuadkitConfig.from_env_profile()
79
80
  self._container.singleton(QuadkitConfig, self._config)
@@ -136,6 +137,23 @@ class Application(SecretsMixin, HealthProbeMixin):
136
137
  """DI container."""
137
138
  return self._container
138
139
 
140
+ @property
141
+ def module_graph(self) -> CompiledModuleGraph | None:
142
+ """The compiled module graph, or ``None`` before compilation.
143
+
144
+ Read-only view of what the boot sequence compiled: every module with
145
+ its providers, imports, and fully expanded exports. It exists so
146
+ boot-time guards can assert that what a module *declares* it exports is
147
+ what the container can actually resolve — the failure this graph is
148
+ otherwise blind to, because an export that nothing resolves during
149
+ boot only fails later, at ``QK_ERR_DI_004`` on first use, or never.
150
+
151
+ Returns:
152
+ The compiled graph, or ``None`` when the application has not been
153
+ compiled yet.
154
+ """
155
+ return self._orchestrator.compiled_graph
156
+
139
157
  @property
140
158
  def providers(self) -> list[Provider]:
141
159
  """Get all registered providers."""
@@ -65,6 +65,12 @@ class BoundedChannel(Generic[T]):
65
65
  )
66
66
  self._closed = False
67
67
  self._lock = asyncio.Lock()
68
+ #: Set whenever something a waiting receiver cares about happened:
69
+ #: an item arrived or the channel closed. Receivers clear it before
70
+ #: they wait. They never dequeue from a helper task, so an item is
71
+ #: either in the queue or in the consumer's hands — there is no tick
72
+ #: in between where ``is_empty`` lies.
73
+ self._wakeup = asyncio.Event()
68
74
 
69
75
  async def send(self, item: T) -> None:
70
76
  """Send an item, blocking if the channel is full.
@@ -91,6 +97,7 @@ class BoundedChannel(Generic[T]):
91
97
  # full, but that means the item was never actually enqueued.
92
98
  with suppress(asyncio.QueueFull):
93
99
  self._queue.put_nowait(wrapped)
100
+ self._wakeup.set()
94
101
  raise
95
102
  except asyncio.QueueFull:
96
103
  msg = "Channel is full"
@@ -98,6 +105,7 @@ class BoundedChannel(Generic[T]):
98
105
  msg,
99
106
  capacity=self.capacity,
100
107
  ) from None
108
+ self._wakeup.set()
101
109
 
102
110
  def send_nowait(self, item: T) -> None:
103
111
  """Send an item without blocking.
@@ -128,6 +136,7 @@ class BoundedChannel(Generic[T]):
128
136
  except asyncio.QueueFull:
129
137
  msg = "Channel is full"
130
138
  raise ChannelFullError(msg, capacity=self.capacity) from None
139
+ self._wakeup.set()
131
140
 
132
141
  async def receive(self) -> T:
133
142
  """Receive an item, blocking if the channel is empty.
@@ -138,29 +147,40 @@ class BoundedChannel(Generic[T]):
138
147
  Raises:
139
148
  ChannelClosedError: If the channel is closed and empty.
140
149
  """
141
- try:
142
- wrapped: _ContextualItem[T] = await self._queue.get()
143
- except asyncio.CancelledError:
144
- # If cancelled after the queue internally dequeued an item,
145
- # put it back so it is not lost. get_nowait will raise
146
- # QueueEmpty if nothing was actually dequeued yet.
147
- try:
148
- item_back = self._queue.get_nowait()
149
- self._queue.put_nowait(item_back)
150
- except asyncio.QueueEmpty:
151
- pass
152
- raise
153
- except asyncio.QueueEmpty:
154
- if self._closed:
155
- msg = "Channel is closed"
156
- raise ChannelClosedError(msg) from None
157
- raise
150
+ wrapped = await self._next_item()
158
151
  # Restore sender's context variables so OTel trace spans and other
159
152
  # context state are propagated to the consumer.
160
153
  for var, value in wrapped.ctx.items():
161
154
  var.set(value)
162
155
  return wrapped.item
163
156
 
157
+ async def _next_item(self) -> _ContextualItem[T]:
158
+ """Dequeue the next wrapped item, or raise once closed and drained.
159
+
160
+ The hot path — an item is already queued — is a plain
161
+ ``get_nowait``. Only an empty channel waits, and it waits on one
162
+ wake-up that both :meth:`send` and :meth:`close` raise. Without the
163
+ close half, ``close()`` could not end an ``async for`` over an empty
164
+ channel, and every consumer loop would outlive the thing it consumed.
165
+
166
+ The dequeue itself is always synchronous in the consumer's own task.
167
+ Pulling the item inside a helper task would open a window where the
168
+ queue is already empty but the consumer has not resumed yet — a
169
+ caller polling ``is_empty`` (the event bus's ``flush``) would
170
+ conclude the work was done one tick before it started.
171
+ """
172
+ while True:
173
+ try:
174
+ return self._queue.get_nowait()
175
+ except asyncio.QueueEmpty:
176
+ if self._closed:
177
+ msg = "Channel is closed"
178
+ raise ChannelClosedError(msg) from None
179
+ # Nothing queued and not closed: both facts were read without an
180
+ # intervening await, so clearing here cannot swallow a wake-up.
181
+ self._wakeup.clear()
182
+ await self._wakeup.wait()
183
+
164
184
  def receive_nowait(self) -> T:
165
185
  """Receive an item without blocking.
166
186
 
@@ -183,9 +203,15 @@ class BoundedChannel(Generic[T]):
183
203
  return wrapped.item
184
204
 
185
205
  async def close(self) -> None:
186
- """Close the channel. Remaining items can still be received."""
206
+ """Close the channel. Remaining items can still be received.
207
+
208
+ Receivers blocked on an empty channel are woken and get
209
+ :class:`ChannelClosedError` (``StopAsyncIteration`` under
210
+ ``async for``), so a consumer loop ends when its producer does.
211
+ """
187
212
  async with self._lock:
188
213
  self._closed = True
214
+ self._wakeup.set()
189
215
 
190
216
  @property
191
217
  def is_closed(self) -> bool:
@@ -110,6 +110,24 @@ class ProviderOrchestrator:
110
110
  """
111
111
  self._registry._compiled_graph = graph
112
112
 
113
+ @property
114
+ def compiled_graph(self) -> CompiledModuleGraph | None:
115
+ """The compiled module graph, or ``None`` before compilation.
116
+
117
+ The public companion to :meth:`set_compiled_graph`. The graph is what
118
+ knows which module declares which export, and the code that needs to
119
+ answer that question lives outside this class: health probes, boot-time
120
+ assertions, and test guards. Without a reader the only way to ask was
121
+ the private ``_compiled_graph``, so every such caller reached into the
122
+ orchestrator's internals — and a rename would have broken them without
123
+ a test to notice.
124
+
125
+ Returns:
126
+ The graph produced by :class:`ModuleCompiler`, or ``None`` when the
127
+ application has not been compiled yet.
128
+ """
129
+ return self._registry._compiled_graph
130
+
113
131
  def add(self, provider: Provider) -> None:
114
132
  """Add a provider to be orchestrated.
115
133
 
@@ -187,6 +187,26 @@ class ServiceResolver:
187
187
 
188
188
  return instance
189
189
 
190
+ async def resolve_optional(self, service_type: Any) -> Any | None:
191
+ """Resolve a service, returning ``None`` when it is not registered.
192
+
193
+ ``ContainerResolverProtocol`` has declared this method all along, and
194
+ the container implements it, but the resolver handed to provider
195
+ factories did not: a provider asking for an optional dependency had to
196
+ pair ``has()`` with ``resolve()`` and hope nothing moved in between.
197
+ Absent means absent — a registered service whose construction raises
198
+ still raises.
199
+
200
+ Args:
201
+ service_type: The type to resolve.
202
+
203
+ Returns:
204
+ The resolved instance, or ``None`` when nothing is registered for it.
205
+ """
206
+ if not self.can_resolve(service_type):
207
+ return None
208
+ return await self.resolve(service_type)
209
+
190
210
  async def _create_instance(self, descriptor: ServiceDescriptor) -> Any:
191
211
  """Create a service instance from its descriptor."""
192
212
  impl = descriptor.implementation
@@ -14,9 +14,9 @@ from typing import (
14
14
  get_type_hints,
15
15
  )
16
16
 
17
- from quadkit.domain.models.coercion import coerce_and_build, resolve_type_hints
17
+ from quadkit.domain.models.coercion import coerce_and_build
18
18
  from quadkit.identity import ambient as identity_ambient
19
- from quadkit.primitives.typing import type_allows_none
19
+ from quadkit.primitives.typing import resolve_type_hints, type_allows_none
20
20
 
21
21
  # Infrastructure imports (new locations)
22
22
  from quadkit.serialization.domain import (
@@ -116,6 +116,113 @@ def _convert_pydantic_fields_for_dataclass(cls: type) -> None:
116
116
  setattr(cls, attr_name, _dc.field(default=value.default, metadata=meta))
117
117
 
118
118
 
119
+ _GUARD_DONE_ATTR = "_quadkit_guard_done"
120
+
121
+
122
+ def run_field_guard(model: Any) -> None:
123
+ """Coerce and validate a dataclass-built model, once per instance.
124
+
125
+ ``@dataclass`` regenerates ``__init__`` on the subclass, so
126
+ ``DomainModel.__init__`` — the only place scalar coercion, ``Field(...)``
127
+ constraints and field validators ran — never executed for the documented
128
+ pattern::
129
+
130
+ @dataclass
131
+ class Product(DomainModel):
132
+ name: str = Field(default="", min_length=3)
133
+
134
+ A ``Field(min_length=3)`` there was documentation rather than a check, and
135
+ an ISO timestamp handed to the constructor stayed a string. Dataclasses
136
+ always call ``__post_init__``, so the guard is installed there (see
137
+ ``_install_field_guard``) and this function is what runs.
138
+
139
+ Idempotent by instance marker: ``DomainModel.__init__`` does this work
140
+ itself and then calls ``__post_init__``, so it marks the instance first.
141
+
142
+ Args:
143
+ model: The instance to coerce and validate in place.
144
+ """
145
+ if getattr(model, _GUARD_DONE_ATTR, False):
146
+ return
147
+ object.__setattr__(model, _GUARD_DONE_ATTR, True)
148
+
149
+ cls = type(model)
150
+ dc_fields = getattr(cls, "__dataclass_fields__", None)
151
+ if not dc_fields:
152
+ return
153
+
154
+ try:
155
+ type_hints = cls._cached_type_hints or resolve_type_hints(cls)
156
+ except (NameError, AttributeError, TypeError):
157
+ type_hints = {}
158
+
159
+ field_validators = getattr(cls, "_field_validators", None) or {}
160
+ values: dict[str, Any] = {}
161
+ for fname, f in dc_fields.items():
162
+ if not hasattr(model, fname):
163
+ continue
164
+ current = getattr(model, fname)
165
+ value = current
166
+
167
+ # `mode="before"` runs on the raw value: `@dataclass` has already
168
+ # assigned it, but coercion has not touched it yet, and a validator that
169
+ # declares the storage shape of a column (an epoch integer, a JSON
170
+ # document in a Text column) has to see that raw value.
171
+ # `DomainModel.__init__` dispatches the same mode the same way, and the
172
+ # two paths have to agree: one model, one behaviour.
173
+ for mode, vfn in field_validators.get(fname, []):
174
+ if mode == "before":
175
+ value = vfn(cls, value)
176
+
177
+ value = _coerce_value(fname, value, type_hints, f)
178
+ if value is not current:
179
+ object.__setattr__(model, fname, value)
180
+ values[fname] = value
181
+
182
+ cls._validate_field_constraints(dc_fields, values)
183
+
184
+ for fname, fval in list(values.items()):
185
+ for mode, vfn in field_validators.get(fname, []):
186
+ if mode == "after":
187
+ new_val = vfn(cls, fval)
188
+ if new_val is not None:
189
+ object.__setattr__(model, fname, new_val)
190
+ values[fname] = new_val
191
+
192
+ # ``getattr(..., None)`` is ``Any | None``, so the default has to be applied
193
+ # before the ``.get`` rather than inside it.
194
+ model_validators = getattr(cls, "_model_validators", None) or {}
195
+ for validator_fn in model_validators.get("after", []):
196
+ result = validator_fn(model)
197
+ if result is not None and result is not model:
198
+ for attr_k, attr_v in result.__dict__.items():
199
+ object.__setattr__(model, attr_k, attr_v)
200
+
201
+
202
+ def _install_field_guard(cls: type) -> None:
203
+ """Chain :func:`run_field_guard` onto the class's ``__post_init__``.
204
+
205
+ ``__post_init__`` is the one hook a generated ``__init__`` always calls, so
206
+ it is where a ``@dataclass`` subclass gets the same coercion and validation
207
+ a ``DomainModel``-initialised model does. A user-defined ``__post_init__``
208
+ is preserved and runs after the guard.
209
+
210
+ Args:
211
+ cls: The subclass being created.
212
+ """
213
+ existing = cls.__dict__.get("__post_init__")
214
+ if getattr(existing, "_quadkit_field_guard", False):
215
+ return
216
+
217
+ def __post_init__(self: Any) -> None: # noqa: N807 — the hook name is fixed by dataclasses
218
+ run_field_guard(self)
219
+ if existing is not None:
220
+ existing(self)
221
+
222
+ __post_init__._quadkit_field_guard = True # type: ignore[attr-defined]
223
+ cls.__post_init__ = __post_init__ # type: ignore[attr-defined]
224
+
225
+
119
226
  class DomainModel:
120
227
  """Mixin providing dataclass-like helpers for easy serialization.
121
228
 
@@ -155,6 +262,10 @@ class DomainModel:
155
262
  hints = resolve_type_hints(cls)
156
263
  cls._cached_type_hints = hints if hints else None
157
264
 
265
+ # `@dataclass` replaces `__init__`, so the coercion, constraint and
266
+ # validator work below has to be reachable from the generated one.
267
+ _install_field_guard(cls)
268
+
158
269
  @staticmethod
159
270
  def _ensure_dataclass(target_cls: type) -> None:
160
271
  """Auto-apply @dataclass if annotations have unconverted Field objects.
@@ -237,6 +348,9 @@ class DomainModel:
237
348
  for k in extra_keys:
238
349
  object.__setattr__(self, k, kwargs.pop(k))
239
350
 
351
+ # The guard has already run on this path; the marker keeps the chained
352
+ # `__post_init__` from repeating (and re-applying) the same work.
353
+ object.__setattr__(self, _GUARD_DONE_ATTR, True)
240
354
  if hasattr(self, "__post_init__"):
241
355
  self.__post_init__()
242
356
 
@@ -270,9 +384,13 @@ class DomainModel:
270
384
  val = _coerce_value(fname, val, type_hints, f)
271
385
  values[fname] = val
272
386
  elif f.default is not MISSING:
273
- values[fname] = f.default
387
+ # Coerced like a provided value: the guard path (a generated
388
+ # `__init__` followed by `__post_init__`) coerces whatever the
389
+ # default left on the instance, so this path has to as well or
390
+ # the two disagree about a defaulted field's type.
391
+ values[fname] = _coerce_value(fname, f.default, type_hints, f)
274
392
  elif f.default_factory is not MISSING:
275
- values[fname] = f.default_factory()
393
+ values[fname] = _coerce_value(fname, f.default_factory(), type_hints, f)
276
394
  elif fname == "id":
277
395
  values[fname] = None
278
396
  else:
@@ -314,7 +432,18 @@ class DomainModel:
314
432
  dc_fields: dict[str, Any],
315
433
  values: dict[str, Any],
316
434
  ) -> None:
317
- """Validate field values against metadata constraints."""
435
+ """Validate field values against metadata constraints.
436
+
437
+ **A default is not exempt.** ``name: str = Field(default="", min_length=3)``
438
+ is a contradiction the *declaration* contains, and the framework answers
439
+ it by refusing to construct the model — for a caller who omits the field
440
+ exactly as for one who passes the same value. Silently accepting the
441
+ default would let a constraint the model documents be violated by the
442
+ value the model itself supplies, which is how a `ge=0` counter ends up
443
+ negative in production. The failure is loud and points at the
444
+ declaration, and the two construction paths agree on it because both
445
+ check whatever value the field ended up holding.
446
+ """
318
447
  for fname, f in dc_fields.items():
319
448
  meta = getattr(f, "metadata", None)
320
449
  if not meta:
@@ -0,0 +1,67 @@
1
+ """Scalar-coercion helpers for domain models.
2
+
3
+ Hosts the implementation of ``DomainModel.model_validate`` (extracted from
4
+ :mod:`quadkit.domain.models.base`). The annotation resolver it relies on lives
5
+ in :mod:`quadkit.primitives.typing` and is re-exported here for the call sites
6
+ that have always imported it from this module.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, cast
12
+
13
+ # The annotation resolver lives in `quadkit.primitives.typing` because it is not
14
+ # a domain concern: `quadkit.mapping` needs the same answer ("what types does
15
+ # this model declare") to match a source object's fields against a dataclass
16
+ # destination, and two resolvers would eventually disagree.
17
+ from quadkit.primitives.typing import resolve_type_hints as resolve_type_hints
18
+
19
+ __all__ = ["coerce_and_build", "resolve_type_hints"]
20
+
21
+
22
+ def coerce_and_build(cls: type[Any], data: dict[str, Any]) -> Any:
23
+ """Build *cls* from *data* through the same guard as direct construction.
24
+
25
+ ``model_validate`` — the hydration path ``GenericRepository`` and every
26
+ ``from_dict``-shaped caller goes through — used to run a narrow coercion
27
+ table of its own (int/float/str/bool) and then ``cls(**coerced)``. On a
28
+ plain ``@dataclass`` subclass that bypasses everything ``DomainModel``
29
+ promises:
30
+
31
+ * a row's ISO timestamp stayed a ``str``, because ``datetime`` was never in
32
+ the table;
33
+ * an unknown column raised a bare ``TypeError: unexpected keyword
34
+ argument`` instead of landing in ``model_extra``;
35
+ * ``Field(...)`` constraints and the field/model validators never ran, so a
36
+ ``Field(min_length=...)`` declaration was documentation, not a check.
37
+
38
+ Delegating to ``DomainModel.__init__`` — the path a directly constructed
39
+ model already takes — fixes all three by deleting the second
40
+ implementation rather than extending it. One guard, one behaviour:
41
+ ``Model.model_validate(row)`` and ``Model(**row)`` now agree.
42
+
43
+ Args:
44
+ cls: The domain model class being validated.
45
+ data: Raw field values (e.g. a database row or a decoded JSON body).
46
+
47
+ Returns:
48
+ A new instance of *cls*.
49
+
50
+ Raises:
51
+ ValidationError: If required fields are missing or a declared
52
+ constraint or field validator rejects a value.
53
+ """
54
+ import dataclasses as _dc
55
+
56
+ if not _dc.is_dataclass(cls):
57
+ return cls(**data)
58
+
59
+ # Imported here: ``base`` imports this module at class-creation time.
60
+ from quadkit.domain.models.base import DomainModel
61
+
62
+ instance = cls.__new__(cls)
63
+ # ``cls`` is only known to be a dataclass here, so the freshly allocated
64
+ # instance has to be named as the ``DomainModel`` it is about to be
65
+ # initialised as before its ``__init__`` can be called directly.
66
+ DomainModel.__init__(cast("DomainModel", instance), **data)
67
+ return instance
@@ -66,6 +66,23 @@ def install(implementation: IdGeneratorProtocol) -> None:
66
66
  _ambient.set(implementation)
67
67
 
68
68
 
69
+ def current() -> IdGeneratorProtocol:
70
+ """Return the active identity generator.
71
+
72
+ The counterpart of ``quadkit.primitives.clock.current()``: an application
73
+ that publishes the ambient capability on its DI container binds *this*
74
+ resolver, not a captured instance, so ``install()`` and ``use()`` stay
75
+ visible to every consumer. Binding a snapshot would make a test's
76
+ ``use(...)`` override reach the code that calls ``new_uuid()`` directly
77
+ while missing the code that resolves the protocol — one process, two
78
+ generators.
79
+
80
+ Returns:
81
+ The ``IdGeneratorProtocol`` implementation currently installed.
82
+ """
83
+ return _ambient.get()
84
+
85
+
69
86
  def generate_for(entity_type: str) -> str:
70
87
  """Generate a time-ordered ID for the given entity type.
71
88
 
@@ -120,6 +137,7 @@ def use(implementation: IdGeneratorProtocol) -> Iterator[None]:
120
137
 
121
138
 
122
139
  __all__ = [
140
+ "current",
123
141
  "generate_for",
124
142
  "install",
125
143
  "new_uuid",
@@ -6,11 +6,24 @@ per-logger level filtering, and deterministic log sampling.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ from collections.abc import Callable, MutableMapping
9
10
  from dataclasses import dataclass, field
10
11
  import hashlib
11
12
  import threading
12
13
  from typing import Any
13
14
 
15
+ #: A structlog processor that takes and returns a mapping.
16
+ #:
17
+ #: ``structlog.typing.Processor`` is deliberately not used: its return union
18
+ #: also admits ``str``/``bytes``/``tuple``, none of which support the ``.items()``
19
+ #: this module performs on the result. The parameter and return are
20
+ #: ``MutableMapping`` rather than ``dict`` because that is what structlog's own
21
+ #: processors declare -- ``dict`` is invariant, so narrowing here would reject
22
+ #: every real processor, ``CallsiteParameterAdder`` included.
23
+ _MappingProcessor = Callable[
24
+ [Any, str, MutableMapping[str, Any]], MutableMapping[str, Any]
25
+ ]
26
+
14
27
  import structlog
15
28
 
16
29
  from quadkit.logging import get_logger
@@ -18,6 +31,13 @@ from quadkit.logging.redaction import get_redactor
18
31
 
19
32
  logger = get_logger(__name__)
20
33
  _otel_warned = [False]
34
+
35
+ #: Sentinel for "the opentelemetry import has not been attempted yet". Kept
36
+ #: distinct from ``None`` (import attempted, not available) so the negative
37
+ #: result is remembered instead of re-searched on every log event.
38
+ _OTEL_UNRESOLVED = object()
39
+ _otel_state: list[Any] = [_OTEL_UNRESOLVED]
40
+
21
41
  from quadkit.logging.types import _LOGLEVEL_TO_INT, LogLevel
22
42
 
23
43
  # ---------------------------------------------------------------------------
@@ -78,24 +98,37 @@ def _redact_processor(
78
98
  return get_redactor().redact_dict(event_dict)
79
99
 
80
100
 
101
+ def _resolve_otel_trace() -> Any | None:
102
+ """Return ``opentelemetry.trace`` once, or ``None`` when it is absent.
103
+
104
+ Resolution happens once per process and the answer — including "not
105
+ installed" — is remembered. A failed import is *not* cached by the import
106
+ system: every retry re-searches ``sys.path`` on disk, which measured as
107
+ roughly a third of the per-request cost of the health endpoint because
108
+ this processor runs for every log event. Losing the trace context is a
109
+ degraded mode, not a reason to stat the filesystem on each log line.
110
+ """
111
+ if _otel_state[0] is _OTEL_UNRESOLVED:
112
+ try:
113
+ # Dotted import: opentelemetry is a PEP-420 namespace package, so
114
+ # ``from opentelemetry import trace`` does not type-check against
115
+ # its stubs; PLR0402 prefers the from-form.
116
+ import opentelemetry.trace as trace # noqa: PLR0402
117
+
118
+ _otel_state[0] = trace
119
+ except ImportError:
120
+ _otel_state[0] = None
121
+ return _otel_state[0]
122
+
123
+
81
124
  def _otel_processor(
82
125
  logger: Any,
83
126
  method_name: str,
84
127
  event_dict: dict[str, Any],
85
128
  ) -> dict[str, Any]:
86
129
  """Inject OpenTelemetry trace and span IDs into the event dict."""
87
- try:
88
- # Dotted import: opentelemetry is a PEP-420 namespace package, so
89
- # ``from opentelemetry import trace`` does not type-check against its
90
- # stubs; PLR0402 prefers the from-form.
91
- import opentelemetry.trace as trace # noqa: PLR0402
92
-
93
- span = trace.get_current_span()
94
- if span and span.get_span_context().is_valid:
95
- ctx = span.get_span_context()
96
- event_dict["trace_id"] = format(ctx.trace_id, "032x")
97
- event_dict["span_id"] = format(ctx.span_id, "016x")
98
- except ImportError:
130
+ trace = _resolve_otel_trace()
131
+ if trace is None:
99
132
  if not _otel_warned[0]:
100
133
  _otel_warned[0] = True
101
134
  # No kwargs here: this warning can be the first log call of the
@@ -105,6 +138,13 @@ def _otel_processor(
105
138
  "opentelemetry_not_installed; install opentelemetry-api for "
106
139
  "trace_id/span_id injection into log events"
107
140
  )
141
+ return event_dict
142
+
143
+ span = trace.get_current_span()
144
+ if span and span.get_span_context().is_valid:
145
+ ctx = span.get_span_context()
146
+ event_dict["trace_id"] = format(ctx.trace_id, "032x")
147
+ event_dict["span_id"] = format(ctx.span_id, "016x")
108
148
  return event_dict
109
149
 
110
150
 
@@ -135,7 +175,7 @@ class _NamespacedCallsiteAdder:
135
175
  #: Keys structlog's adder may consult on the stdlib-bridge path.
136
176
  _PASSTHROUGH = ("_record", "_from_structlog")
137
177
 
138
- def __init__(self, inner: Any) -> None:
178
+ def __init__(self, inner: _MappingProcessor) -> None:
139
179
  self._inner = inner
140
180
 
141
181
  def __call__(
@@ -27,7 +27,25 @@ class MappingConfig(BaseConfig):
27
27
  )
28
28
  auto_map: bool = Field(
29
29
  default=True,
30
- description="Fall back to auto-mapping when no explicit mapper is registered.",
30
+ description=(
31
+ "Fall back to auto-mapping in map_or_auto() when no explicit mapper "
32
+ "is registered."
33
+ ),
34
+ )
35
+ ignore_unknown: bool = Field(
36
+ default=True,
37
+ description=(
38
+ "Drop source fields the destination does not declare. Set False to "
39
+ "raise MappingExecutionError instead, so a shape mismatch on either "
40
+ "side of the mapping fails loudly."
41
+ ),
42
+ )
43
+ case_sensitive: bool = Field(
44
+ default=True,
45
+ description=(
46
+ "Match source keys to destination fields case-sensitively. Set "
47
+ "False to match `Name` to `name`."
48
+ ),
31
49
  )
32
50
 
33
51