loom-kernel 2.19.0__tar.gz → 2.20.1__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.
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/CHANGELOG.md +62 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/PKG-INFO +1 -1
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/backend/core_model.py +6 -8
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/backend/sqlalchemy.py +43 -8
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/repository.py +64 -36
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/__init__.py +2 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/introspection.py +14 -5
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/projection/runtime.py +2 -9
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/cursor.py +32 -8
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/dynamodb/repository.py +17 -2
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/repository.py +17 -4
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/mixins.py +82 -36
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/compiler.py +21 -9
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/cursor.py +22 -11
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/autocrud.py +30 -6
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/in_memory.py +22 -1
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/.gitignore +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/LICENSE +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/README.md +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/pyproject.toml +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_concurrency.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_filters.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_paths.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_roles.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_startup.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_transport.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/_usecase.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/_binding.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/_handlers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/_rpc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/card.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/events.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/a2a/server.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/abc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/bootstrap.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/_compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/_fingerprint.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/_plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_capabilities.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_conversation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_feedable.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_hook.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_instructions.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_limits.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_model_role.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_output.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/compiler/phases/_state.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/declarative/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/declarative/_envelope.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/declarative/_schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/declarative/_v1.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/declarative/schemas/agent-spec-v1.schema.json +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/describe.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_a2a.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_capabilities.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_engine.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_escape.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_events.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_guards.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_history.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_instructions.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_limits.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_mcp.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_models.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_native.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_output.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_returns.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_schemas.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/_spec.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/extras/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/extras/a2a.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/extras/handlebars.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/extras/mcp.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/extras/skills.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/provider.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/_shared.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/anthropic.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/bedrock.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/gateway.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/openai.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/engines/pydantic_ai/providers/typesafe.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_batch.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_columns.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_frames.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_ledger.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_runner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/etl/_validation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/fastapi/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/fastapi/endpoints.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/fastapi/response.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/fastapi/streaming.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/inference.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/pricing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/remote_auth.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_bounded.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_chain.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_conversation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_grants.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_handle.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_health.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_hooks.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_lifecycle.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_limits.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/runtime/_mcp.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/validate/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/ai/validate/__main__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/auto.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/bootstrap.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/constants.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/runner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/celery/service.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/cli/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/cli/app.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/cli/schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/async_bridge.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/_decide.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/_grants.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/_roles.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/_scope.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/elevation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/authz/product.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/backend/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/backend/protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/backend/scoped_ddl.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/bootstrap/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/bootstrap/bootstrap.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/bootstrap/kernel.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/_batching.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/_single_flight.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/abc/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/abc/backend.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/abc/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/abc/dependency.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/calls.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/codec.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/decorators.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/dependency.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/gateway.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/keys.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/memory.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/result_codec.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/serializer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/cache/wiring.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/command/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/command/adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/command/base.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/command/field.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/command/introspection.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/_boto3.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/_includes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/_resolver_utils.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/binder.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/configurable.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/context.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/keys.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/loader.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/model.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/observability.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/resolver.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/secrets.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/config/ssm.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/contracts/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/contracts/manifest.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/di/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/di/container.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/di/scope.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/_utils.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/base.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/interfaces.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/manifest.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/discovery/modules.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/compilable.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/events.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/executor.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/metrics.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/engine/post_commit.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/errors/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/errors/codes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/errors/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/expr/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/expr/eval.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/expr/nodes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/expr/refs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/identity/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/identity/context.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/identity/identity.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/identity/issuer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/identity/wire.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/introspection/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/introspection/base.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/introspection/describe.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/callback.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/context.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/handle.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/job.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/job/service.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/locator.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/logger/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/logger/abc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/logger/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/logger/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/logger/structlogger.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/_loom_type.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/base.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/convert.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/enums.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/field.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/partition.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/privilege.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/projection.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/relation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/scoped.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/struct.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/timestamped.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/types.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/model/types_postgres.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/event.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/observer/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/observer/noop.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/observer/otel.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/observer/structlog.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/otel_ids.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/runtime.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/span.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/observability/topology.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/persistence/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/persistence/abc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/persistence/none.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/persistence/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/plugins/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/plugins/entrypoints.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/plugins/optional.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/projection/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/projection/loaders.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/query.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/query_compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/repo_for.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/repository.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/abc/session_scope.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/dynamodb/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/dynamodb/backend.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/dynamodb/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/dynamodb/uow.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/backend.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/ids.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/query_compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/uow.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mongo/values.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/mutation.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/registration.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/backend.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/integrity.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/loaders.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/migrations/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/migrations/env_template.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/migrations/hook.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/migrations/operations.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/migrations/runners.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/model.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/profile_loader.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/projection.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/filters.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/ordering.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/paths.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/query_compiler/subquery.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/repository.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/bootstrap.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/elevate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/guard/0001.sql +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/guard/0002.sql +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/guard/preflight.sql +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/guard_manifest.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/integrity.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/partitions.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/provider.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/sources.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/rls/verify.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/session_manager.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/session_settings.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/transactional.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/repository/sqlalchemy/uow.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/response/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/response/base.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/routing/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/routing/ref.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/routing/resolver.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/runner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/schema/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/schema/schema_mode.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/schema_names.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/abc/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/abc/contracts.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/abc/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/caller_bound.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/clickhouse/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/clickhouse/_client.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/clickhouse/executor.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/clickhouse/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/roles.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/sql/service.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/symbols.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/tracing/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/tracing/context.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/transaction.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/transport/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/transport/adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/uow/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/uow/abc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/uow/context.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/_predicates.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/agent_markers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/compute.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/constants.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/factory.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/field_ref.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/invoker.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/keys.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/markers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/mcp_markers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/rule.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/core/use_case/use_case.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/_uri.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_format_registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/_common.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/_log.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/_ops.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/_snapshot.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_historify/_transform.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_merge.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_path_template.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_predicate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_schema_aligner/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_schema_aligner/_aligner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_schema_aligner/_policy.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/_write_policy.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_dtype.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_file_writer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_frame.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_historify.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_predicate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_reader.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_schema_aligner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_streaming.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/_writer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/polars/provider.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_dtype.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_historify.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_reader.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_schema_aligner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/_writer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/backends/spark/provider.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_backends/_polars.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_backends/_spark.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_cleaners.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_options.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_paths.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_scope.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/checkpoint/_store.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_binding.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_filter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_validators.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_validators_agents.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_validators_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_validators_plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/compiler/_validators_step.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/_format.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/_read_options.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/_utils.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/_with_agent.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/_write_options.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/expr/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/expr/_params.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/expr/_predicate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/expr/_predicate_dialect.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/expr/_refs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_from.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_from_clickhouse.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_from_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_from_dynamodb.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_from_mongo.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/source/_specs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_client.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_file.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/_builder.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/_enums.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/_report.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_history/_spec.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_into.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_schema_mode.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_table.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/declarative/target/_temp.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/executor/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/executor/_agents.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/executor/_dispatcher.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/executor/_executor.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/_registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_clickhouse.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_dynamodb.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_dynamodb_items.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_dynamodb_predicate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_mongo.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_mongo_batch.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_mongo_bson.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_mongo_predicate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/sources/_serialization.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/targets/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/io/targets/_clickhouse.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/_observer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/_records.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/sinks/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/sinks/_protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/sinks/_table.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/lineage/sinks/_writer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/_builder.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/_ops.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/_protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/_runner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/_step.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/backends/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/maintenance/backends/_delta_rs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_generics.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_params.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_pipeline.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_process.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_sql.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_step.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_step_client.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/pipeline/_step_sql.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/_agents.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/_providers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/_wiring.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/config_loader.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/core.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runner/filtering.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runtime/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runtime/_config_values.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/runtime/contracts.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/schema/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/schema/_contract.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/schema/_schema.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/storage/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/storage/_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/storage/_file_locator.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/storage/_locator.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/storage/routing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/_result.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/_runners.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/_scenario.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/_stubs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/etl/testing/spark.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_async.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_ctx.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_flow_yaml.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_meta.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_placeholders.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/_summary.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/deploy/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/deploy/_discovery.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/deploy/_schedule.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/deploy/_yaml_etls.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/deploy/entrypoint.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_assemble.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_backfill.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_backfill_body.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_body.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_common.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_factory.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_hooks.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_maintenance.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_params.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_run_name.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_runtime.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_signature.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/flow/_stages.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/manifest/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/manifest/_model.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/manifest/_s3.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/manifest/_store.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_event.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_factory.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_format.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_port.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_slack.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/notify/_sns.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/observer/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/observer/_flow.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/observer/_logging_bridge.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/observer/_manifest.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prefect/observer/_task_run.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/_instruments.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/kafka.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/lifecycle.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/prometheus/middleware.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/py.typed +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/_body.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/auth/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/auth/abc.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/auth/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/auth/jwt.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/auth/middleware.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/constants.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/cors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/_exclusions.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/app.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/auto.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/health.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/openapi.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/response.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/router_runtime.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/fastapi/sql.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/middleware.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/model.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/rest/rest_adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_adapter.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_batch_spans.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_commit_tracker.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_dlq.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_error_boundary.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_operators.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_resource_manager.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_runtime_io.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/_sink_registry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/_shared.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/boundary.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/dispatcher.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/routing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/scopes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/shapes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/steps.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/handlers/storage.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/bytewax/runner.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/_bindings.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/_compiler.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/_plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/phases/build_plan.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/compiler/phases/validate.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/_exceptions.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/_message.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/_tracing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/core/_typing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/graph/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/graph/_flow.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_codec.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_errors.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_key_resolver.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_message.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_record.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/_wire.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/client/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/client/_consumer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/client/_producer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/client/_protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/client/_retry.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/message/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/message/_consumer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/message/_producer.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/kafka/message/_protocol.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/mongo/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/mongo/_bytewax_source.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/mongo/_config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/mongo/_event.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/mongo/_normalize.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_boundary.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_branches.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_broadcast.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_capabilities.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_decompose.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_expand_routes.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_expr_eval.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_fork.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_helpers.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_mongo.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_protocols.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_router.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_shape.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_sink.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_step.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_table/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_table/common.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_table/config.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/_with.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/nodes/refs.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/streaming/testing.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/__init__.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/agents.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/golden.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/http_harness.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/repository_harness.py +0 -0
- {loom_kernel-2.19.0 → loom_kernel-2.20.1}/src/loom/testing/runner.py +0 -0
|
@@ -375,6 +375,34 @@
|
|
|
375
375
|
|
|
376
376
|
### core
|
|
377
377
|
|
|
378
|
+
- **core:** on a model with a composite primary key, the SQLAlchemy
|
|
379
|
+
repository's `get_by_id`, `exists`, `update` and `delete` take the key as a
|
|
380
|
+
tuple of every key value in declaration order (`repo.get_by_id((account_id,
|
|
381
|
+
entry_id))`) and raise `ValueError` for anything else, a scalar included,
|
|
382
|
+
instead of matching the first key column. A `Load` step and a cached
|
|
383
|
+
repository's `get_by_id`, `update` and `delete` pass the id unchanged, so a
|
|
384
|
+
scalar id raises there too; a cached `@cache_query(scope="entity")` read
|
|
385
|
+
takes the tuple as its first argument and raises `TypeError` for anything
|
|
386
|
+
else. A cursor token issued for such a model before this release is refused with
|
|
387
|
+
`UnsupportedQuery`, `400 unsupported_query`; clients restart from the first
|
|
388
|
+
page. `decode_cursor` takes `id_count`, the number of key columns, and
|
|
389
|
+
`get_id_attributes` returns every key column name.
|
|
390
|
+
|
|
391
|
+
- **core:** `compile_all` raises `ValueError` for a model with a composite
|
|
392
|
+
primary key that declares a relation or a projection whose loader runs
|
|
393
|
+
`load_many`, naming the model, its key and those fields. The backend loads
|
|
394
|
+
them by the first key column only, so a many-to-one relation attached
|
|
395
|
+
another owner's row and a one-to-many relation grouped children by that
|
|
396
|
+
column; load them by hand until they are matched by the full key.
|
|
397
|
+
|
|
398
|
+
- **core:** the `RepositoryMongo`, `RepositoryDynamoDB` and
|
|
399
|
+
`loom.testing.InMemoryRepository` constructors raise `ConfigError` for a
|
|
400
|
+
model with a composite primary key, naming the model and its key. Each
|
|
401
|
+
keys an item by one field (Mongo's `_id` and the DynamoDB partition key by
|
|
402
|
+
the first key column, the in-memory store by its `id_field`), so a second
|
|
403
|
+
row sharing that field's value raised `Conflict` on Mongo and DynamoDB and
|
|
404
|
+
replaced the first row in memory.
|
|
405
|
+
|
|
378
406
|
- **core:** the miss path of a cached `@cache_query`/`@cache_call` read,
|
|
379
407
|
`McpHandle.call` and `CacheGateway.get_value`/`multi_get_values` now raise
|
|
380
408
|
`BoundaryValidationError` — a `ValueError` — on a stale or malformed cached
|
|
@@ -457,6 +485,10 @@
|
|
|
457
485
|
|
|
458
486
|
### rest
|
|
459
487
|
|
|
488
|
+
- **rest:** auto-CRUD mounts no `/{id}` route (get, update, delete) for a
|
|
489
|
+
model with a composite primary key, since one path segment cannot carry the
|
|
490
|
+
key; naming one of them in `include` raises `ValueError` when the routes are
|
|
491
|
+
built. Those routes matched the first key column only.
|
|
460
492
|
- **rest:** a JSON object body that fails `Command` validation now answers
|
|
461
493
|
`422 boundary_validation` with a `violations` list (`{field, message}`
|
|
462
494
|
pairs, `field` being the wire name the client sent, the same shape
|
|
@@ -570,6 +602,36 @@
|
|
|
570
602
|
|
|
571
603
|
### core
|
|
572
604
|
|
|
605
|
+
- **core:** the relation steps and the `load_many` projection steps of a read
|
|
606
|
+
profile run one after another on the request's session. They ran with
|
|
607
|
+
`asyncio.gather` on that one session: when one step failed while another was
|
|
608
|
+
still waiting, on Postgres with asyncpg the caller got `InterfaceError:
|
|
609
|
+
cannot perform operation: another operation is in progress` instead of the
|
|
610
|
+
step's error, and the pooled connection was left in a manually started
|
|
611
|
+
transaction, so the next request on it failed until `engine.dispose()`. The
|
|
612
|
+
number of statements is unchanged, at most one per step.
|
|
613
|
+
|
|
614
|
+
- **core:** cursor pagination and the by-id operations of the SQLAlchemy
|
|
615
|
+
repository use the full primary key of a model whose key is composite, such
|
|
616
|
+
as a `RowScoped` model keyed by its boundary column and its own id. They
|
|
617
|
+
used only the first key column: a cursor walk ordered and compared on it
|
|
618
|
+
alone and skipped the rows sharing that column's value with the last row of
|
|
619
|
+
each page, and `get_by_id`, `exists`, `update` and `delete` matched that
|
|
620
|
+
column only. The cursor now orders and compares on
|
|
621
|
+
every key column after the sort keys and carries every key value in the
|
|
622
|
+
token; offset pages and `list_paginated` order by every key column too.
|
|
623
|
+
Tokens of single-column keys are unchanged. A cached repository over such a
|
|
624
|
+
model keyed its entries by the first key column too, so a warm
|
|
625
|
+
`list_paginated` or first cursor page served the rows sharing a first key
|
|
626
|
+
value as copies of one of them; it now caches and reloads every row by its
|
|
627
|
+
full key, one `get_by_id` per missing row.
|
|
628
|
+
|
|
629
|
+
- **core:** a `MANY_TO_MANY` relation joins the target table on the column
|
|
630
|
+
the secondary table's foreign key references. It joined on the target
|
|
631
|
+
model's first key column, so a target with a composite key, or whose link
|
|
632
|
+
references a unique non-key column, loaded the rows whose first key column
|
|
633
|
+
held the linked value instead of the linked rows.
|
|
634
|
+
|
|
573
635
|
- **core:** the RLS guard refuses, in the same statement, a drop outside the
|
|
574
636
|
hatch that breaks its invariants. `DROP POLICY` or `DROP TRIGGER
|
|
575
637
|
loom_deny_owner_dml` on a scoped table used to commit, because drops are not
|
|
@@ -21,7 +21,6 @@ The ORM class (``ProductSA``) is kept exclusively for writes and Alembic.
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
-
import asyncio
|
|
25
24
|
from collections.abc import Sequence
|
|
26
25
|
from dataclasses import dataclass
|
|
27
26
|
from typing import TYPE_CHECKING, Any
|
|
@@ -61,7 +60,8 @@ class CoreRelationStep:
|
|
|
61
60
|
|
|
62
61
|
* ``MANY_TO_MANY``:
|
|
63
62
|
``fk_col`` = owner FK column in the secondary table.
|
|
64
|
-
``pk_col`` =
|
|
63
|
+
``pk_col`` = target column the secondary table's FK references (a key
|
|
64
|
+
or unique column).
|
|
65
65
|
``secondary_table`` / ``secondary_target_fk`` required.
|
|
66
66
|
"""
|
|
67
67
|
|
|
@@ -201,8 +201,9 @@ class CoreModel:
|
|
|
201
201
|
) -> list[Any]:
|
|
202
202
|
"""Execute ``stmt``, batch-load relations, run projections, return ``list[Struct]``.
|
|
203
203
|
|
|
204
|
-
Relations declared in the profile are
|
|
205
|
-
``
|
|
204
|
+
Relations declared in the profile are loaded one after another on
|
|
205
|
+
``session``, with at most one statement each. Projection steps run
|
|
206
|
+
after relation loading.
|
|
206
207
|
|
|
207
208
|
Args:
|
|
208
209
|
session: Active async SQLAlchemy session.
|
|
@@ -277,10 +278,7 @@ async def _load_relations(
|
|
|
277
278
|
steps: tuple[CoreRelationStep, ...],
|
|
278
279
|
rows: list[Any],
|
|
279
280
|
) -> dict[str, dict[Any, Any]]:
|
|
280
|
-
|
|
281
|
-
return {}
|
|
282
|
-
results = await asyncio.gather(*(_fetch_relation_batch(session, step, rows) for step in steps))
|
|
283
|
-
return {step.attr: data for step, data in zip(steps, results, strict=False)}
|
|
281
|
+
return {step.attr: await _fetch_relation_batch(session, step, rows) for step in steps}
|
|
284
282
|
|
|
285
283
|
|
|
286
284
|
def _make_adapter(
|
|
@@ -13,6 +13,7 @@ from sqlalchemy import (
|
|
|
13
13
|
BigInteger,
|
|
14
14
|
Boolean,
|
|
15
15
|
CheckConstraint,
|
|
16
|
+
Column,
|
|
16
17
|
Constraint,
|
|
17
18
|
DateTime,
|
|
18
19
|
Float,
|
|
@@ -57,6 +58,7 @@ from loom.core.model.introspection import (
|
|
|
57
58
|
extract_model_from_hint,
|
|
58
59
|
get_column_fields,
|
|
59
60
|
get_id_attribute,
|
|
61
|
+
get_id_attributes,
|
|
60
62
|
get_projections,
|
|
61
63
|
get_relations,
|
|
62
64
|
get_table_name,
|
|
@@ -879,6 +881,7 @@ def _compile_core_model(struct_cls: type, comp: _Compilation) -> None:
|
|
|
879
881
|
profile_plans = _build_profile_plans(
|
|
880
882
|
profiles, all_columns, id_attr, relations, relation_steps, projections, struct_cls
|
|
881
883
|
)
|
|
884
|
+
_refuse_composite_key_loads(struct_cls, profile_plans)
|
|
882
885
|
|
|
883
886
|
core_model = CoreModel(struct_cls, table, id_attr, profile_plans)
|
|
884
887
|
for name, col in zip(column_fields, all_columns, strict=False):
|
|
@@ -887,6 +890,37 @@ def _compile_core_model(struct_cls: type, comp: _Compilation) -> None:
|
|
|
887
890
|
comp.core[struct_cls] = core_model
|
|
888
891
|
|
|
889
892
|
|
|
893
|
+
def _refuse_composite_key_loads(struct_cls: type, plans: dict[str, CoreProfilePlan]) -> None:
|
|
894
|
+
"""Refuse relations and backend projections on a model with a composite primary key.
|
|
895
|
+
|
|
896
|
+
The core loaders match loaded rows to their owner by ``CoreProfilePlan.id_attr``,
|
|
897
|
+
the first primary-key column, so on a composite key they attach rows to the
|
|
898
|
+
wrong owner.
|
|
899
|
+
"""
|
|
900
|
+
key = get_id_attributes(struct_cls)
|
|
901
|
+
if len(key) == 1:
|
|
902
|
+
return
|
|
903
|
+
loads = _fields_loaded_by_key(plans)
|
|
904
|
+
if loads:
|
|
905
|
+
raise ValueError(
|
|
906
|
+
f"{struct_cls.__name__} has the composite primary key ({', '.join(key)}); the "
|
|
907
|
+
f"backend loads {', '.join(loads)} by its first key column only, so drop "
|
|
908
|
+
"them from the model and load them by hand"
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def _fields_loaded_by_key(plans: dict[str, CoreProfilePlan]) -> list[str]:
|
|
913
|
+
relations = {step.attr for plan in plans.values() for step in plan.relation_steps}
|
|
914
|
+
projections = {
|
|
915
|
+
step.name
|
|
916
|
+
for plan in plans.values()
|
|
917
|
+
for level in plan.projection_plan.levels
|
|
918
|
+
for step in level
|
|
919
|
+
if not step.prefer_memory
|
|
920
|
+
}
|
|
921
|
+
return sorted(relations | projections)
|
|
922
|
+
|
|
923
|
+
|
|
890
924
|
def _collect_profiles(
|
|
891
925
|
relations: dict[str, Any],
|
|
892
926
|
projections: dict[str, Any],
|
|
@@ -1167,12 +1201,12 @@ def _compile_many_to_many_step(
|
|
|
1167
1201
|
if secondary_table is None:
|
|
1168
1202
|
return None
|
|
1169
1203
|
owner_table_name = get_table_name(struct_cls)
|
|
1170
|
-
secondary_owner_fk, secondary_target_fk,
|
|
1204
|
+
secondary_owner_fk, secondary_target_fk, target_ref = _inspect_secondary(
|
|
1171
1205
|
secondary_table, owner_table_name
|
|
1172
1206
|
)
|
|
1173
|
-
if secondary_owner_fk is None or secondary_target_fk is None or
|
|
1207
|
+
if secondary_owner_fk is None or secondary_target_fk is None or target_ref is None:
|
|
1174
1208
|
return None
|
|
1175
|
-
target_sa = _find_target_sa_class(
|
|
1209
|
+
target_sa = _find_target_sa_class(target_ref.table.name, comp)
|
|
1176
1210
|
if target_sa is None:
|
|
1177
1211
|
return None
|
|
1178
1212
|
related_struct = getattr(target_sa, "__struct_cls__", None)
|
|
@@ -1188,7 +1222,7 @@ def _compile_many_to_many_step(
|
|
|
1188
1222
|
related_struct=related_struct,
|
|
1189
1223
|
owner_pk_col=get_id_attribute(struct_cls),
|
|
1190
1224
|
fk_col=secondary_owner_fk,
|
|
1191
|
-
pk_col=
|
|
1225
|
+
pk_col=target_ref.name,
|
|
1192
1226
|
secondary_table=secondary_table,
|
|
1193
1227
|
secondary_target_fk=secondary_target_fk,
|
|
1194
1228
|
)
|
|
@@ -1197,18 +1231,19 @@ def _compile_many_to_many_step(
|
|
|
1197
1231
|
def _inspect_secondary(
|
|
1198
1232
|
secondary_table: Any,
|
|
1199
1233
|
owner_table_name: str,
|
|
1200
|
-
) -> tuple[str | None, str | None,
|
|
1234
|
+
) -> tuple[str | None, str | None, Column[Any] | None]:
|
|
1235
|
+
"""Return the owner FK column, the target FK column and the target column it references."""
|
|
1201
1236
|
owner_fk: str | None = None
|
|
1202
1237
|
target_fk: str | None = None
|
|
1203
|
-
|
|
1238
|
+
target_ref: Column[Any] | None = None
|
|
1204
1239
|
for col in secondary_table.columns:
|
|
1205
1240
|
for fk in col.foreign_keys:
|
|
1206
1241
|
if fk.column.table.name == owner_table_name:
|
|
1207
1242
|
owner_fk = col.name
|
|
1208
1243
|
else:
|
|
1209
1244
|
target_fk = col.name
|
|
1210
|
-
|
|
1211
|
-
return owner_fk, target_fk,
|
|
1245
|
+
target_ref = fk.column
|
|
1246
|
+
return owner_fk, target_fk, target_ref
|
|
1212
1247
|
|
|
1213
1248
|
|
|
1214
1249
|
def _target_cols(related_struct: type, target_table: Any) -> tuple[Any, ...]:
|
|
@@ -7,6 +7,7 @@ import warnings
|
|
|
7
7
|
from collections.abc import Awaitable, Callable, Mapping, Sequence
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from functools import partial, wraps
|
|
10
|
+
from types import GenericAlias
|
|
10
11
|
from typing import Any, Generic, NamedTuple, TypeVar, cast
|
|
11
12
|
|
|
12
13
|
import msgspec
|
|
@@ -30,7 +31,7 @@ from loom.core.model import BoundaryValidationError, LoomType, is_pydantic_model
|
|
|
30
31
|
from loom.core.model.convert import to_struct
|
|
31
32
|
from loom.core.model.enums import Cardinality
|
|
32
33
|
from loom.core.model.introspection import (
|
|
33
|
-
|
|
34
|
+
get_id_attributes,
|
|
34
35
|
get_projections,
|
|
35
36
|
get_relations,
|
|
36
37
|
list_element_type,
|
|
@@ -127,37 +128,42 @@ class _DependencySpec:
|
|
|
127
128
|
class _PrimaryKey(NamedTuple):
|
|
128
129
|
"""Primary key of the cached model as the wrapper reads it.
|
|
129
130
|
|
|
131
|
+
A single-column key is its value; a composite key is the tuple of every
|
|
132
|
+
key value in declaration order, the form the SQLAlchemy repository's
|
|
133
|
+
``get_by_id`` takes.
|
|
134
|
+
|
|
130
135
|
Attributes:
|
|
131
|
-
|
|
132
|
-
repository declares no model or the
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
attributes: Names of the primary-key attributes in declaration order;
|
|
137
|
+
``("id",)`` when the wrapped repository declares no model or the
|
|
138
|
+
model declares no primary key.
|
|
139
|
+
python_types: Type of each key attribute, ``None`` where it is
|
|
140
|
+
unresolvable or not a plain class (``int | None``, for instance).
|
|
135
141
|
"""
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
attributes: tuple[str, ...]
|
|
144
|
+
python_types: tuple[type | None, ...]
|
|
139
145
|
|
|
140
146
|
|
|
141
|
-
_FALLBACK_PRIMARY_KEY = _PrimaryKey(
|
|
147
|
+
_FALLBACK_PRIMARY_KEY = _PrimaryKey(attributes=("id",), python_types=(None,))
|
|
142
148
|
|
|
143
149
|
|
|
144
150
|
def _resolve_primary_key(repository: Repository[Any, Any, Any, Any]) -> _PrimaryKey:
|
|
145
151
|
"""Resolve the primary key of the wrapped repository's model.
|
|
146
152
|
|
|
147
153
|
Falls back to the attribute ``id`` with no type for a repository without a
|
|
148
|
-
model or whose model declares no primary key; the type
|
|
149
|
-
when
|
|
150
|
-
used as it was decoded.
|
|
154
|
+
model or whose model declares no primary key; the type of one attribute
|
|
155
|
+
alone is ``None`` when its annotation is not a plain class.
|
|
151
156
|
"""
|
|
152
157
|
model = getattr(repository, "model", None)
|
|
153
158
|
if model is None:
|
|
154
159
|
return _FALLBACK_PRIMARY_KEY
|
|
155
160
|
try:
|
|
156
|
-
|
|
161
|
+
attributes = get_id_attributes(model)
|
|
157
162
|
except (TypeError, ValueError):
|
|
158
163
|
return _FALLBACK_PRIMARY_KEY
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
hints = resolve_type_hints(model)
|
|
165
|
+
types = tuple(hint if isinstance(hint, type) else None for hint in map(hints.get, attributes))
|
|
166
|
+
return _PrimaryKey(attributes, types)
|
|
161
167
|
|
|
162
168
|
|
|
163
169
|
class CachedRepository(
|
|
@@ -174,9 +180,11 @@ class CachedRepository(
|
|
|
174
180
|
single ``<primary key> IN (...)`` query, so the wrapped repository should
|
|
175
181
|
support :attr:`~loom.core.repository.abc.query.FilterOp.IN` on the primary
|
|
176
182
|
key; a repository whose ``allowed_filter_fields`` excludes the primary key
|
|
177
|
-
is detected and served with per-id reads instead
|
|
178
|
-
|
|
179
|
-
model declares
|
|
183
|
+
is detected and served with per-id reads instead, and so is a model with a
|
|
184
|
+
composite primary key, which a filter on one field cannot match. The key
|
|
185
|
+
is read by the attribute names the model declares (``id`` when there is no
|
|
186
|
+
model or the model declares no primary key); a composite key is the tuple
|
|
187
|
+
of its values in declaration order.
|
|
180
188
|
|
|
181
189
|
Concurrent misses of the same key on the entity read and on a
|
|
182
190
|
``@cache_query`` read are coalesced inside the process: the first caller
|
|
@@ -699,12 +707,15 @@ class CachedRepository(
|
|
|
699
707
|
return max(1, round(ttl + self._rng.uniform(-spread, spread)))
|
|
700
708
|
|
|
701
709
|
def _extract_entity_id(self, item: Any) -> object | None:
|
|
702
|
-
"""Read the primary key of *item* by the
|
|
710
|
+
"""Read the primary key of *item* by the attributes the model declares.
|
|
703
711
|
|
|
704
|
-
|
|
705
|
-
|
|
712
|
+
Returns the value of a single-column key and the tuple of every value
|
|
713
|
+
of a composite one, or ``None`` when a key value is missing.
|
|
706
714
|
"""
|
|
707
|
-
|
|
715
|
+
values = tuple(getattr(item, name, None) for name in self._primary_key.attributes)
|
|
716
|
+
if None in values:
|
|
717
|
+
return None
|
|
718
|
+
return values[0] if len(values) == 1 else values
|
|
708
719
|
|
|
709
720
|
async def _create_many(self, data: Sequence[msgspec.Struct]) -> tuple[OutputT, ...]:
|
|
710
721
|
if not data:
|
|
@@ -842,11 +853,12 @@ class CachedRepository(
|
|
|
842
853
|
"""Return the primary key a ``scope="entity"`` read is keyed by, or raise.
|
|
843
854
|
|
|
844
855
|
The key must be the first positional argument (a keyword does not
|
|
845
|
-
count) and must not be ``None
|
|
846
|
-
|
|
847
|
-
a
|
|
856
|
+
count) and must not be ``None``; a composite key must be a tuple of
|
|
857
|
+
one value per key attribute. Where a key attribute's type resolved
|
|
858
|
+
to a plain class, its value must be of that exact type: a
|
|
859
|
+
``datetime`` for a ``date`` key or a ``bool`` for an ``int`` key
|
|
848
860
|
would render a tag no mutation event ever bumps, so both are
|
|
849
|
-
rejected even though ``isinstance`` would accept them.
|
|
861
|
+
rejected even though ``isinstance`` would accept them. An attribute
|
|
850
862
|
declared ``int | None`` resolves no class and gets no type check.
|
|
851
863
|
Only this entry point is guarded: ``get_by_id``, ``update`` and
|
|
852
864
|
``delete`` are typed ``IdT`` and stay unchecked.
|
|
@@ -859,11 +871,18 @@ class CachedRepository(
|
|
|
859
871
|
f"{method_name}: @cache_query(scope='entity') needs the primary key"
|
|
860
872
|
" as first positional argument"
|
|
861
873
|
)
|
|
862
|
-
|
|
863
|
-
|
|
874
|
+
types = self._primary_key.python_types
|
|
875
|
+
composite = len(types) > 1
|
|
876
|
+
values = args[0] if composite and isinstance(args[0], tuple) else (args[0],)
|
|
877
|
+
if len(values) != len(types) or any(
|
|
878
|
+
expected is not None and type(value) is not expected
|
|
879
|
+
for value, expected in zip(values, types, strict=True)
|
|
880
|
+
):
|
|
881
|
+
names = ", ".join("Any" if t is None else t.__name__ for t in types)
|
|
864
882
|
raise TypeError(
|
|
865
883
|
f"{method_name}: @cache_query(scope='entity') received a"
|
|
866
|
-
f" {type(args[0]).__name__} as primary key,
|
|
884
|
+
f" {type(args[0]).__name__} as primary key,"
|
|
885
|
+
f" expected {f'tuple[{names}]' if composite else names}"
|
|
867
886
|
)
|
|
868
887
|
return args[0]
|
|
869
888
|
|
|
@@ -930,10 +949,12 @@ class CachedRepository(
|
|
|
930
949
|
A repository may restrict the filterable fields (``allowed_filter_fields``).
|
|
931
950
|
When the primary-key attribute is not among them the batched refill would
|
|
932
951
|
be rejected, so the wrapper degrades to per-id reads rather than failing
|
|
933
|
-
the page; the repository's declared policy is left untouched.
|
|
952
|
+
the page; the repository's declared policy is left untouched. A
|
|
953
|
+
composite key is always read per id, since a filter names one field.
|
|
934
954
|
"""
|
|
955
|
+
attributes = self._primary_key.attributes
|
|
935
956
|
allowed = getattr(self._repository, "allowed_filter_fields", None)
|
|
936
|
-
return not allowed or
|
|
957
|
+
return len(attributes) == 1 and (not allowed or attributes[0] in allowed)
|
|
937
958
|
|
|
938
959
|
async def _fetch_missing_one_by_one(
|
|
939
960
|
self,
|
|
@@ -966,7 +987,9 @@ class CachedRepository(
|
|
|
966
987
|
def _id_in_query(self, ids: Sequence[IdT]) -> QuerySpec:
|
|
967
988
|
return QuerySpec(
|
|
968
989
|
filters=FilterGroup(
|
|
969
|
-
filters=(
|
|
990
|
+
filters=(
|
|
991
|
+
FilterSpec(field=self._primary_key.attributes[0], op=FilterOp.IN, value=ids),
|
|
992
|
+
)
|
|
970
993
|
),
|
|
971
994
|
limit=len(ids),
|
|
972
995
|
)
|
|
@@ -1029,16 +1052,21 @@ class CachedRepository(
|
|
|
1029
1052
|
"""Restore the ids of a cached index to the model's primary-key type.
|
|
1030
1053
|
|
|
1031
1054
|
The index round-trips through msgpack, which renders a ``datetime``, a
|
|
1032
|
-
``date`` or a ``UUID`` as a string
|
|
1033
|
-
declared type: SQLAlchemy returns no rows for a
|
|
1034
|
-
filtered with strings
|
|
1035
|
-
|
|
1055
|
+
``date`` or a ``UUID`` as a string and a tuple as a list, while the
|
|
1056
|
+
repository binds the declared type: SQLAlchemy returns no rows for a
|
|
1057
|
+
``datetime`` column filtered with strings, and the SQLAlchemy
|
|
1058
|
+
repository takes a composite key only as a tuple. Converting here
|
|
1059
|
+
keeps every later use of the id — cache key, filter value, identity
|
|
1060
|
+
match — in the declared type.
|
|
1036
1061
|
|
|
1037
1062
|
Returns:
|
|
1038
1063
|
The converted ids, or ``None`` when they do not fit the current
|
|
1039
1064
|
primary-key type, which makes the index unusable.
|
|
1040
1065
|
"""
|
|
1041
|
-
|
|
1066
|
+
types = self._primary_key.python_types
|
|
1067
|
+
id_type: Any = types[0]
|
|
1068
|
+
if len(types) > 1:
|
|
1069
|
+
id_type = GenericAlias(tuple, tuple(Any if t is None else t for t in types))
|
|
1042
1070
|
if id_type is None:
|
|
1043
1071
|
return cast(list[IdT], raw_ids)
|
|
1044
1072
|
try:
|
|
@@ -22,6 +22,7 @@ from loom.core.model.introspection import (
|
|
|
22
22
|
declared_unique,
|
|
23
23
|
get_column_fields,
|
|
24
24
|
get_id_attribute,
|
|
25
|
+
get_id_attributes,
|
|
25
26
|
get_projections,
|
|
26
27
|
get_relations,
|
|
27
28
|
get_table_name,
|
|
@@ -107,6 +108,7 @@ __all__ = [
|
|
|
107
108
|
"UnsupportedBoundaryType",
|
|
108
109
|
"get_column_fields",
|
|
109
110
|
"get_id_attribute",
|
|
111
|
+
"get_id_attributes",
|
|
110
112
|
"get_projections",
|
|
111
113
|
"get_relations",
|
|
112
114
|
"get_table_name",
|
|
@@ -145,12 +145,21 @@ def get_projections(cls: type) -> dict[str, Projection]:
|
|
|
145
145
|
return _collect_inherited_dict_metadata(cls, "__loom_projections__")
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def get_id_attributes(cls: type) -> tuple[str, ...]:
|
|
149
|
+
"""Return the names of the primary-key fields, in declaration order.
|
|
150
|
+
|
|
151
|
+
Raises:
|
|
152
|
+
ValueError: If the model declares no primary-key field.
|
|
153
|
+
"""
|
|
154
|
+
names = tuple(name for name, info in get_column_fields(cls).items() if info.field.primary_key)
|
|
155
|
+
if not names:
|
|
156
|
+
raise ValueError(f"No primary key field found on {cls.__name__}")
|
|
157
|
+
return names
|
|
158
|
+
|
|
159
|
+
|
|
148
160
|
def get_id_attribute(cls: type) -> str:
|
|
149
|
-
"""Return the name of the primary
|
|
150
|
-
|
|
151
|
-
if info.field.primary_key:
|
|
152
|
-
return name
|
|
153
|
-
raise ValueError(f"No primary key field found on {cls.__name__}")
|
|
161
|
+
"""Return the name of the first primary-key field."""
|
|
162
|
+
return get_id_attributes(cls)[0]
|
|
154
163
|
|
|
155
164
|
|
|
156
165
|
def get_table_name(cls: type) -> str:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import inspect
|
|
5
4
|
from collections.abc import Mapping, Sequence
|
|
6
5
|
from dataclasses import dataclass
|
|
@@ -231,18 +230,12 @@ async def _execute_backend_steps(
|
|
|
231
230
|
parent_ids: Sequence[object],
|
|
232
231
|
values_by_index: dict[int, dict[str, Any]],
|
|
233
232
|
) -> None:
|
|
234
|
-
|
|
235
|
-
return
|
|
236
|
-
|
|
237
|
-
backend_results = await asyncio.gather(
|
|
238
|
-
*(step.loader.load_many(backend_context, parent_ids) for step in steps)
|
|
239
|
-
)
|
|
240
|
-
for step, rows in zip(steps, backend_results, strict=False):
|
|
233
|
+
for step in steps:
|
|
241
234
|
_merge_rows(
|
|
242
235
|
values_by_index=values_by_index,
|
|
243
236
|
parent_ids=parent_ids,
|
|
244
237
|
field_name=step.name,
|
|
245
|
-
rows=
|
|
238
|
+
rows=await step.loader.load_many(backend_context, parent_ids),
|
|
246
239
|
default=step.projection.default,
|
|
247
240
|
)
|
|
248
241
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Backend-neutral cursor tokens for keyset pagination.
|
|
2
2
|
|
|
3
3
|
A token is the base64url form of a msgspec-encoded record
|
|
4
|
-
``{"b": backend, "k": [sort key values...], "id": tie-breaker}
|
|
4
|
+
``{"b": backend, "k": [sort key values...], "id": tie-breaker}``, where the
|
|
5
|
+
tie-breaker is the primary-key value, or the list of its values when the key
|
|
6
|
+
is composite. Every
|
|
5
7
|
backend issues and consumes the same shape, so a token from another backend
|
|
6
8
|
or in a legacy format is rejected at decode time with
|
|
7
9
|
:class:`~loom.core.repository.abc.errors.UnsupportedQuery`.
|
|
@@ -28,7 +30,8 @@ class Cursor(LoomFrozenStruct, frozen=True):
|
|
|
28
30
|
Args:
|
|
29
31
|
backend: Name of the backend that issued the token.
|
|
30
32
|
keys: Sort key values of the last row on the previous page, in sort order.
|
|
31
|
-
tie_breaker: Primary key of that row
|
|
33
|
+
tie_breaker: Primary key of that row; a tuple in key order when the
|
|
34
|
+
key is composite.
|
|
32
35
|
"""
|
|
33
36
|
|
|
34
37
|
backend: str
|
|
@@ -58,7 +61,7 @@ _Key = int | float | str | bool | None | _DateTimeKey | _DateKey | _UuidKey | _D
|
|
|
58
61
|
class _Token(msgspec.Struct):
|
|
59
62
|
b: str
|
|
60
63
|
k: list[_Key]
|
|
61
|
-
id: _Key
|
|
64
|
+
id: _Key | list[_Key]
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
def _wrap(value: object) -> _Key:
|
|
@@ -89,7 +92,8 @@ def encode_cursor(backend: str, keys: Sequence[object], tie_breaker: object) ->
|
|
|
89
92
|
Args:
|
|
90
93
|
backend: Name of the issuing backend.
|
|
91
94
|
keys: Sort key values of the last row on the page, in sort order.
|
|
92
|
-
tie_breaker: Primary key of that row
|
|
95
|
+
tie_breaker: Primary key of that row; a tuple in key order when the
|
|
96
|
+
key is composite.
|
|
93
97
|
|
|
94
98
|
Returns:
|
|
95
99
|
base64url token that :func:`decode_cursor` accepts for ``backend``.
|
|
@@ -97,11 +101,18 @@ def encode_cursor(backend: str, keys: Sequence[object], tie_breaker: object) ->
|
|
|
97
101
|
Raises:
|
|
98
102
|
TypeError: If a key has a type the token format cannot carry.
|
|
99
103
|
"""
|
|
100
|
-
|
|
104
|
+
tie: _Key | list[_Key]
|
|
105
|
+
if isinstance(tie_breaker, tuple):
|
|
106
|
+
tie = [_wrap(value) for value in tie_breaker]
|
|
107
|
+
else:
|
|
108
|
+
tie = _wrap(tie_breaker)
|
|
109
|
+
token = _Token(b=backend, k=[_wrap(key) for key in keys], id=tie)
|
|
101
110
|
return base64.urlsafe_b64encode(msgspec.json.encode(token)).decode()
|
|
102
111
|
|
|
103
112
|
|
|
104
|
-
def decode_cursor(
|
|
113
|
+
def decode_cursor(
|
|
114
|
+
token: str, backend: str, model: str, *, key_count: int | None = None, id_count: int = 1
|
|
115
|
+
) -> Cursor:
|
|
105
116
|
"""Decode a token issued by :func:`encode_cursor` for ``backend``.
|
|
106
117
|
|
|
107
118
|
Args:
|
|
@@ -110,13 +121,17 @@ def decode_cursor(token: str, backend: str, model: str, *, key_count: int | None
|
|
|
110
121
|
model: Qualified model name, used in the error.
|
|
111
122
|
key_count: Number of sort keys the query orders by; when given, a
|
|
112
123
|
token carrying a different number is rejected.
|
|
124
|
+
id_count: Number of primary-key columns. A single-column key is
|
|
125
|
+
carried as a scalar and a composite key as a list of that many
|
|
126
|
+
values; any other token is rejected.
|
|
113
127
|
|
|
114
128
|
Returns:
|
|
115
129
|
The decoded :class:`Cursor`.
|
|
116
130
|
|
|
117
131
|
Raises:
|
|
118
132
|
UnsupportedQuery: If the token is undecodable, in a legacy format,
|
|
119
|
-
was issued by another backend or does not match the sort
|
|
133
|
+
was issued by another backend or does not match the sort or the
|
|
134
|
+
primary key.
|
|
120
135
|
"""
|
|
121
136
|
try:
|
|
122
137
|
record = msgspec.json.decode(base64.urlsafe_b64decode(token.encode()), type=_Token)
|
|
@@ -126,8 +141,17 @@ def decode_cursor(token: str, backend: str, model: str, *, key_count: int | None
|
|
|
126
141
|
raise UnsupportedQuery(backend, model, "cursor token was issued by another backend")
|
|
127
142
|
if key_count is not None and len(record.k) != key_count:
|
|
128
143
|
raise UnsupportedQuery(backend, model, "cursor token does not match the sort")
|
|
144
|
+
tie_breaker: object
|
|
145
|
+
if isinstance(record.id, list):
|
|
146
|
+
carried: int | None = len(record.id)
|
|
147
|
+
tie_breaker = tuple(_unwrap(key) for key in record.id)
|
|
148
|
+
else:
|
|
149
|
+
carried = None
|
|
150
|
+
tie_breaker = _unwrap(record.id)
|
|
151
|
+
if carried != (id_count if id_count > 1 else None):
|
|
152
|
+
raise UnsupportedQuery(backend, model, "cursor token does not match the primary key")
|
|
129
153
|
return Cursor(
|
|
130
154
|
backend=record.b,
|
|
131
155
|
keys=tuple(_unwrap(key) for key in record.k),
|
|
132
|
-
tie_breaker=
|
|
156
|
+
tie_breaker=tie_breaker,
|
|
133
157
|
)
|
|
@@ -23,10 +23,11 @@ from typing import Any, ClassVar, Generic, cast
|
|
|
23
23
|
|
|
24
24
|
import msgspec
|
|
25
25
|
|
|
26
|
+
from loom.core.config import ConfigError
|
|
26
27
|
from loom.core.errors import Conflict
|
|
27
28
|
from loom.core.logger import get_logger
|
|
28
29
|
from loom.core.model.convert import to_struct
|
|
29
|
-
from loom.core.model.introspection import get_column_fields,
|
|
30
|
+
from loom.core.model.introspection import get_column_fields, get_id_attributes, get_table_name
|
|
30
31
|
from loom.core.repository.abc import (
|
|
31
32
|
Creatable,
|
|
32
33
|
Deletable,
|
|
@@ -70,6 +71,9 @@ class RepositoryDynamoDB(
|
|
|
70
71
|
table_name: Name of the DynamoDB table backing this repository.
|
|
71
72
|
model: The Loom struct model this repository is bound to. Its
|
|
72
73
|
``primary_key`` field becomes the DynamoDB partition key.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ConfigError: If the model's primary key is composite.
|
|
73
77
|
"""
|
|
74
78
|
|
|
75
79
|
backend_name: ClassVar[str] = "dynamodb"
|
|
@@ -86,7 +90,7 @@ class RepositoryDynamoDB(
|
|
|
86
90
|
self._client = client
|
|
87
91
|
self._table_name = table_name
|
|
88
92
|
self._model = model
|
|
89
|
-
self._id_attr =
|
|
93
|
+
self._id_attr = _single_id_attribute(model)
|
|
90
94
|
self._column_fields = frozenset(get_column_fields(model).keys())
|
|
91
95
|
self._serializer = TypeSerializer()
|
|
92
96
|
self._deserializer = TypeDeserializer()
|
|
@@ -260,6 +264,17 @@ class RepositoryDynamoDB(
|
|
|
260
264
|
return {self._id_attr: self._encode_value(obj_id)}
|
|
261
265
|
|
|
262
266
|
|
|
267
|
+
def _single_id_attribute(model: type) -> str:
|
|
268
|
+
key = get_id_attributes(model)
|
|
269
|
+
if len(key) == 1:
|
|
270
|
+
return key[0]
|
|
271
|
+
raise ConfigError(
|
|
272
|
+
f"{model.__qualname__} has the composite primary key ({', '.join(key)}); "
|
|
273
|
+
"the repository keys each item by one partition key, so declare a single "
|
|
274
|
+
"primary key"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
263
278
|
def _is_conditional_check_failure(exc: Exception) -> bool:
|
|
264
279
|
"""Return whether *exc* is a DynamoDB conditional-check failure."""
|
|
265
280
|
response = getattr(exc, "response", None)
|