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.
- {quadkit-0.0.44 → quadkit-0.0.45}/PKG-INFO +3 -3
- {quadkit-0.0.44 → quadkit-0.0.45}/README.md +2 -2
- {quadkit-0.0.44 → quadkit-0.0.45}/pyproject.toml +1 -1
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/base.py +19 -1
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/channel.py +44 -18
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/orchestrator.py +18 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/resolver.py +20 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/base.py +134 -5
- quadkit-0.0.45/src/quadkit/domain/models/coercion.py +67 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/ambient.py +18 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/processors.py +53 -13
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/config.py +19 -1
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/core/mapper.py +97 -17
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/__init__.py +58 -1
- quadkit-0.0.45/src/quadkit/primitives/typing.py +239 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/domain.py +6 -2
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/coercion.py +16 -6
- quadkit-0.0.45/tests/unit/concurrency/test_channel_close_wakes_receivers.py +115 -0
- quadkit-0.0.45/tests/unit/domain/test_domain_hydration.py +388 -0
- quadkit-0.0.45/tests/unit/mapping/test_auto_map_dataclass.py +142 -0
- quadkit-0.0.45/tests/unit/primitives/test_public_surface_is_static.py +33 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_coercion.py +7 -4
- quadkit-0.0.44/src/quadkit/domain/models/coercion.py +0 -138
- quadkit-0.0.44/src/quadkit/primitives/typing.py +0 -123
- {quadkit-0.0.44 → quadkit-0.0.45}/.gitignore +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/LICENSE +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/NOTICE +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/example.yaml +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/asgi_lifespan.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config/models.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/config_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/events.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/factory.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/health_probes.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/injectable_provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/invoker.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/runner.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/secrets.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/standard.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/app/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/codegen/field_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/bridges/sync_bridge.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/channels/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/di/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/dispatcher.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/parallel.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/executors/task_manager.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/locks/rwlock.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/concurrency/task_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/merge.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/sources.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/lib/watcher.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/main.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/secrets.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/config/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/_graph.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/builder.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/config/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/config/models.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_resolution.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/container_runtime.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/registrar.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/resolver.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/scope.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/container/validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/context.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/decorators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/aop_interceptors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/interceptors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/strategies.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/extensions/validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/function_provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/integration/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/integration/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/markers.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/_graph_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/compiler.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/context.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/collect.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/detect_cycles.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/expand.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/order.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/protocol.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/validate.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/compiler/phases/visibility.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/dynamic.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/errors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/graph.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/metadata.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/module/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/events.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/health.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/orchestrator/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/provider_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/context.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/descriptor.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/diagnostics.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/injector.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/invoker.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/store.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/type_hints.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/resolution/validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/di/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/aggregate.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/entity.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/factory.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/models/value_object.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/domain/unit_of_work.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/ambient.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/hooks/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/identity/generator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/README.md +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/models.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/redaction.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/config/sampling.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/configurator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/debug.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/factory.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/redaction.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/logging/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/core/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/mapping/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/caching.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/di.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/observability.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/resilience.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/routing.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/builtins/validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/chain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/exception_filters.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/core/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/middleware/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/core.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/sub_providers/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/observability/di/sub_providers/observability.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/engine.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/plugins/state.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/builder.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/clock.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context_keys.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/context_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/data.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/lazy.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/core.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/registry/introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/trusted_html.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/primitives/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/core.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/control.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/time_ops.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/operators/transform.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/retry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/reactive/subjects.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/_pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/errors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/result/utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/canonical_json.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/saga/content_addressed.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/decorators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/encryption/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/encryption/service.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/events.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/chain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/guards/decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/ambient.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/digest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hashing/kdf.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/hooks.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/filename.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/html.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/sanitizer.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/sanitization/url.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/rotation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/store.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/secrets/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/security/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/json.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/backends/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/di/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/domain/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/registry/registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/schema/schema.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/serialization/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/decorators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/engine/validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/rules/rules.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/fields.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/schema/py.typed +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/src/quadkit/validation/types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/_fixtures.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_boot_level.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_cross_method.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_dependency_chain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_exports.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_importers.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_module_health_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_module_lifecycle_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_name_lookup.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/module/test_stub_enforcement_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/di/test_visibility_enforcement_e2e.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/mapping/test_mapping_lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/middleware/test_middleware_lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_corrected_patterns_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_database_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_full_pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/integration/test_monitor_config_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/test.md +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/test_import_diagnostic.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/aop/test_interceptor_timeout.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/clock/test_clock.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_generator_base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_staged_generation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/codegen/test_template_overrides.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_bounded_channel.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_channel.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_parallel.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_provider_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_concurrency_rwlock.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_config_pool.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_dispatcher.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_sync_bridge.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_task_manager.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/concurrency/test_task_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_config_sources.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_env_interpolation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_loader_fail_fast.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_logging_redaction_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_logging_sampling.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_profile_overlay.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_scoping.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_secrets_validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_strict_bool_env.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/config/test_strict_sections.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_context_correlation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_core_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_core_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_request_context_di.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/test_typing.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/core/validation/test_protocols.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/integration/test_provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_compiler.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_dynamic.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_errors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_global.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_graph_reflection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_health_graph.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_integration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_module_lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_reexport.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_stub_compiler.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_stub_decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/module/test_visibility.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_advanced_features.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_basics.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_builder.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_freeze.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_hardening.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_container_visibility.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_decorators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_descriptor.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_decorators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_di_provider_config_wiring.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_diagnostics.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_extensions_discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_extensions_validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_function_provider_boot.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_graph_validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_interceptor_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_invoker_unit.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_lifecycle_orchestrator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_visibility_boot.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_module_visibility_enforcement.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_named_binding.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_orchestrator_health.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_basics.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_config_injection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_conformance.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_orchestration.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_reset_state.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_provider_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_scope_concurrency.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_scope_manager.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_service_store.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_type_hints.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/di/test_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_aggregates.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_init.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_model_strict.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_domain_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_models_factory.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/domain/test_value_object.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/identity/test_identity.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/logging/test_logging_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_auto_map.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_mapper.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_mapping_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_protocol.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/mapping/test_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_builtins.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_chain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_config_alignment.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_exception_filters.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_chain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_ep_discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_routing.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_middleware_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_p2_retry_traceback.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_registry_defaults.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/middleware/test_registry_with_resilience.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_engine.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_state.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_state_hardening.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/plugins/test_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_primitives_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_registry_advanced.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_registry_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/primitives/test_trusted_html.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_control.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_core.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_facade.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_retry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_subjects.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_time_ops.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/reactive/test_transform.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_guards.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_hashing.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_package_hooks_p2.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secret_rotation_policy.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secrets_rotation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_secrets_store.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_api.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_components_p2.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/security/test_security_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_schema.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_di_provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_domain.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_init.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_json.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/serialization/test_serialization_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_boot_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_config_and_misc.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_discovery.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_health_baseline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_health_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_runner_and_lifespan.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_app_validation_and_misc.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application_base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_application_lifecycle.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_base_and_misc_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_clock_and_pipeline_primitives.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_codegen.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_compiler_graph_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_component_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_concurrency_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_concurrency_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_hardening.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_quadkit.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_requirements.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_sources.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_config_watcher.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_override.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_resolver_impl.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_container_runtime_mixin.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_context_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_contract_completeness.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_builder.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_lazy.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_core_pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_dependency_injector.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_descriptor.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_di_module_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_domain_model_base.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_dynamic_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_events_security_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_execution_result.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hatch_build_targets.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_health_probes.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_edge_cases.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_hook_registry_scenarios.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_identity_generators_and_ambient.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_infra_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_input_sanitizers.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_interceptors.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_json_serialization_backend.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_apply_redaction.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_bridging.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_debug.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_di_provider.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_factory.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_init.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_redaction.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_redaction_compound.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_logging_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_m13_m14_verification.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapper.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapper_enhanced.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_mapping.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_enhancements.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_middleware_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_scan_and_provide.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_module_system.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_no_db_imports.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_observability.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_orchestrator_parallel.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_p2_scope_log_level.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_context.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_data.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_primitives_registry.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_provider_boot_enhanced.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_provider_decorator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_quadkit_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_quadkit_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_repr_helpers.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_resilience_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_combinators.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_core.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_pipeline.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_pipeline_and_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_types_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_result_utils.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_round22_hardening.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_round41_hardening.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_saga_primitives.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_sampling_and_config.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_secret_stores_and_rotation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_serialization_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_serialization_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_service_descriptor.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_structured_logging.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_time_ops.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_types.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_exceptions.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_rules.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/test_validation_validator_engine.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/__init__.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/conftest.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_async_validator.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_coercion.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_helpers.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_constants.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_constants_new.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_fields.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_fields_extended.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_init.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_introspection.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_module.py +0 -0
- {quadkit-0.0.44 → quadkit-0.0.45}/tests/unit/validation/test_validation_types.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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:
|
|
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=
|
|
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
|
|