matrx-orm 3.1.10__tar.gz → 3.1.11__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.
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/PKG-INFO +1 -1
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/__init__.py +8 -2
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/query/executor.py +6 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/__init__.py +8 -2
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/errors.py +3 -1
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/managed.py +77 -37
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/session.py +6 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/pyproject.toml +1 -1
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_managed_write_guard.py +78 -9
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/.gitignore +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/CLAUDE.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/MODEL_API.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/docs/migrations.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/postgresql.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/scripts/release.sh +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/conftest.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_execute_admin_sql.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_filter_raw.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.10 → matrx_orm-3.1.11}/tests/test_sync_twins_guard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.11
|
|
4
4
|
Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
|
|
5
5
|
Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
|
|
6
6
|
Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
|
|
@@ -282,13 +282,16 @@ from matrx_orm.session import (
|
|
|
282
282
|
SessionPhase,
|
|
283
283
|
WatchedLifecycleConfig,
|
|
284
284
|
WatchedLifecycleRegistry,
|
|
285
|
-
|
|
285
|
+
COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
|
|
286
|
+
allow_direct_coordinator_write,
|
|
287
|
+
coordinator_policy_tables,
|
|
286
288
|
current_session,
|
|
287
289
|
declare_managed,
|
|
288
290
|
drain_spilled_ops,
|
|
289
291
|
is_in_session,
|
|
290
292
|
record_error,
|
|
291
293
|
record_failures,
|
|
294
|
+
register_coordinator_owned_model,
|
|
292
295
|
)
|
|
293
296
|
from matrx_orm.session import (
|
|
294
297
|
run_sweeper as run_lifecycle_sweeper,
|
|
@@ -299,7 +302,10 @@ from matrx_orm.session import (
|
|
|
299
302
|
|
|
300
303
|
__all__ = [
|
|
301
304
|
"CoordinatorWriteViolation",
|
|
302
|
-
"
|
|
305
|
+
"COORDINATOR_BYPASS_ACKNOWLEDGEMENT",
|
|
306
|
+
"allow_direct_coordinator_write",
|
|
307
|
+
"coordinator_policy_tables",
|
|
308
|
+
"register_coordinator_owned_model",
|
|
303
309
|
# ── Configuration ──────────────────────────────────────────────────────────
|
|
304
310
|
"DatabaseProjectConfig",
|
|
305
311
|
"register_database",
|
|
@@ -957,6 +957,12 @@ class QueryExecutor:
|
|
|
957
957
|
(default ``BaseAdapter`` behaviour) so callers can fall through to the
|
|
958
958
|
SQL path.
|
|
959
959
|
"""
|
|
960
|
+
from matrx_orm.session.managed import assert_model_write_allowed
|
|
961
|
+
|
|
962
|
+
assert_model_write_allowed(
|
|
963
|
+
self.model,
|
|
964
|
+
str(query.get("operation") or "adapter write"),
|
|
965
|
+
)
|
|
960
966
|
return await self.db.execute_write_dict(query)
|
|
961
967
|
|
|
962
968
|
async def insert(self, query: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -67,9 +67,12 @@ from matrx_orm.session.lifecycle import (
|
|
|
67
67
|
sweep_once,
|
|
68
68
|
)
|
|
69
69
|
from matrx_orm.session.managed import (
|
|
70
|
-
|
|
70
|
+
COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
|
|
71
|
+
allow_direct_coordinator_write,
|
|
72
|
+
coordinator_policy_tables,
|
|
71
73
|
declare_managed,
|
|
72
74
|
install_managed_writes,
|
|
75
|
+
register_coordinator_owned_model,
|
|
73
76
|
)
|
|
74
77
|
from matrx_orm.session.op import (
|
|
75
78
|
OpType,
|
|
@@ -112,10 +115,13 @@ __all__ = [
|
|
|
112
115
|
"coalesce_ops",
|
|
113
116
|
"config_from_meta",
|
|
114
117
|
"current_session",
|
|
115
|
-
"
|
|
118
|
+
"COORDINATOR_BYPASS_ACKNOWLEDGEMENT",
|
|
119
|
+
"allow_direct_coordinator_write",
|
|
120
|
+
"coordinator_policy_tables",
|
|
116
121
|
"declare_managed",
|
|
117
122
|
"has_pending_in_stack",
|
|
118
123
|
"install_managed_writes",
|
|
124
|
+
"register_coordinator_owned_model",
|
|
119
125
|
"is_in_session",
|
|
120
126
|
"pending_ops_across_stack",
|
|
121
127
|
"make_delete",
|
|
@@ -66,7 +66,9 @@ class CoordinatorWriteViolation(ManagedWriteViolation):
|
|
|
66
66
|
f"Session scope is {actual_scope!r}. Queue this write through its "
|
|
67
67
|
"Coordinator so parent and child rows share one transactional DAG. "
|
|
68
68
|
"For a genuine out-of-request maintenance operation, use "
|
|
69
|
-
"
|
|
69
|
+
"allow_direct_coordinator_write(model, reason='...', "
|
|
70
|
+
"acknowledgement="
|
|
71
|
+
"'YES_I_KNOW_I_AM_SKIPPING_THE_COORDINATOR_ON_PURPOSE') explicitly.",
|
|
70
72
|
)
|
|
71
73
|
self.model_name = model_name
|
|
72
74
|
self.op_kind = op_kind
|
|
@@ -11,7 +11,7 @@ A Model opts into Session-aware writes by declaring
|
|
|
11
11
|
# OR
|
|
12
12
|
managed_writes = "strict" # raise ManagedWriteViolation if no Session
|
|
13
13
|
# OR
|
|
14
|
-
managed_writes = "coordinator" #
|
|
14
|
+
managed_writes = "coordinator" # audit writes lacking owner authority
|
|
15
15
|
|
|
16
16
|
When the Model's ``create`` / ``update`` / ``delete`` / ``bulk_*`` /
|
|
17
17
|
``upsert`` methods are called:
|
|
@@ -24,6 +24,9 @@ When the Model's ``create`` / ``update`` / ``delete`` / ``bulk_*`` /
|
|
|
24
24
|
was a direct write.
|
|
25
25
|
3. If NO active Session AND mode == ``"strict"``, raise
|
|
26
26
|
:class:`ManagedWriteViolation` — loud red banner, full stack trace.
|
|
27
|
+
4. Coordinator mode never blocks a write. A write without the Coordinator's
|
|
28
|
+
opaque authority (or an acknowledged exact-model override) continues and
|
|
29
|
+
emits a large red ERROR diagnostic. Authorized writes are silent.
|
|
27
30
|
|
|
28
31
|
For backward compatibility, this layer activates only when
|
|
29
32
|
``managed_writes`` is truthy. Unmanaged Models keep their current
|
|
@@ -61,7 +64,6 @@ from matrx_utils import vcprint
|
|
|
61
64
|
|
|
62
65
|
from matrx_orm.session.errors import (
|
|
63
66
|
MANAGED_WRITE_SKILL_HINT,
|
|
64
|
-
CoordinatorWriteViolation,
|
|
65
67
|
ManagedWriteViolation,
|
|
66
68
|
)
|
|
67
69
|
from matrx_orm.session.session import Session, SessionPhase, session_stack
|
|
@@ -95,10 +97,16 @@ _STRICT_TABLES: set[str] = set()
|
|
|
95
97
|
# merely opening a Session/transaction is not authority to write the table.
|
|
96
98
|
_COORDINATOR_TABLES: dict[str, str] = {}
|
|
97
99
|
|
|
100
|
+
_COORDINATOR_AUTHORITY = object()
|
|
101
|
+
|
|
102
|
+
COORDINATOR_BYPASS_ACKNOWLEDGEMENT = (
|
|
103
|
+
"YES_I_KNOW_I_AM_SKIPPING_THE_COORDINATOR_ON_PURPOSE"
|
|
104
|
+
)
|
|
105
|
+
|
|
98
106
|
|
|
99
107
|
@dataclass(frozen=True, slots=True)
|
|
100
108
|
class DirectWriteException:
|
|
101
|
-
|
|
109
|
+
model_cls: type
|
|
102
110
|
reason: str
|
|
103
111
|
|
|
104
112
|
|
|
@@ -109,22 +117,23 @@ _direct_write_exceptions: ContextVar[tuple[DirectWriteException, ...]] = Context
|
|
|
109
117
|
|
|
110
118
|
|
|
111
119
|
@contextmanager
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
def allow_direct_coordinator_write(
|
|
121
|
+
model_cls: type,
|
|
122
|
+
*,
|
|
123
|
+
reason: str,
|
|
124
|
+
acknowledgement: str,
|
|
125
|
+
) -> Iterator[None]:
|
|
126
|
+
"""Temporarily acknowledge one model's intentional Coordinator bypass."""
|
|
118
127
|
cleaned = reason.strip()
|
|
119
128
|
if not cleaned:
|
|
120
|
-
raise ValueError("
|
|
121
|
-
|
|
129
|
+
raise ValueError("allow_direct_coordinator_write requires a non-empty reason")
|
|
130
|
+
if acknowledgement != COORDINATOR_BYPASS_ACKNOWLEDGEMENT:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
"allow_direct_coordinator_write requires acknowledgement="
|
|
133
|
+
f"{COORDINATOR_BYPASS_ACKNOWLEDGEMENT!r}"
|
|
134
|
+
)
|
|
135
|
+
exception = DirectWriteException(model_cls, cleaned)
|
|
122
136
|
token = _direct_write_exceptions.set(_direct_write_exceptions.get() + (exception,))
|
|
123
|
-
logger.warning(
|
|
124
|
-
"DIRECT WRITE EXCEPTION entered for %s: %s",
|
|
125
|
-
model_cls.__name__,
|
|
126
|
-
cleaned,
|
|
127
|
-
)
|
|
128
137
|
try:
|
|
129
138
|
yield
|
|
130
139
|
finally:
|
|
@@ -132,7 +141,22 @@ def allow_direct_managed_write(model_cls: type, *, reason: str) -> Iterator[None
|
|
|
132
141
|
|
|
133
142
|
|
|
134
143
|
def _has_direct_exception(model_cls: type) -> bool:
|
|
135
|
-
return any(item.
|
|
144
|
+
return any(item.model_cls is model_cls for item in _direct_write_exceptions.get())
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _coordinator_session(*, database: str | None = None) -> Session:
|
|
148
|
+
"""Create the Session capability reserved for matrx-ai's Coordinator."""
|
|
149
|
+
from matrx_orm.session.session import Session as SessionClass
|
|
150
|
+
|
|
151
|
+
return SessionClass(
|
|
152
|
+
database=database,
|
|
153
|
+
write_scope="coordinator",
|
|
154
|
+
_write_authority=_COORDINATOR_AUTHORITY,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _has_coordinator_authority(session: Session | None) -> bool:
|
|
159
|
+
return session is not None and session._has_write_authority(_COORDINATOR_AUTHORITY)
|
|
136
160
|
|
|
137
161
|
# Leading INSERT INTO / UPDATE / DELETE FROM target — ORM-generated SQL only.
|
|
138
162
|
_WRITE_TARGET_RE = re.compile(
|
|
@@ -162,7 +186,20 @@ def _register_coordinator_table(model_cls: type, required_scope: str) -> None:
|
|
|
162
186
|
getattr(meta, "table_name", None),
|
|
163
187
|
):
|
|
164
188
|
if name:
|
|
165
|
-
|
|
189
|
+
normalized = str(name).strip('"')
|
|
190
|
+
_COORDINATOR_TABLES[normalized] = required_scope
|
|
191
|
+
_STRICT_TABLES.discard(normalized)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def register_coordinator_owned_model(entry: Any) -> None:
|
|
195
|
+
"""Host registrar consumed by matrx-ai's package-independent registry."""
|
|
196
|
+
model_cls = entry.model_cls
|
|
197
|
+
declare_managed(model_cls, mode="coordinator", write_scope="coordinator")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def coordinator_policy_tables() -> dict[str, str]:
|
|
201
|
+
"""Return installed table policies for startup/release diagnostics."""
|
|
202
|
+
return dict(_COORDINATOR_TABLES)
|
|
166
203
|
|
|
167
204
|
|
|
168
205
|
def _coordinator_scope_for_model(model_cls: type) -> str | None:
|
|
@@ -183,11 +220,11 @@ def assert_model_write_allowed(
|
|
|
183
220
|
op_kind: str,
|
|
184
221
|
*,
|
|
185
222
|
session: Session | None = None,
|
|
186
|
-
) ->
|
|
187
|
-
"""
|
|
223
|
+
) -> bool:
|
|
224
|
+
"""Warn on a Coordinator-owned Model write without Coordinator authority."""
|
|
188
225
|
required_scope = _coordinator_scope_for_model(model_cls)
|
|
189
226
|
if required_scope is None or _has_direct_exception(model_cls):
|
|
190
|
-
return
|
|
227
|
+
return True
|
|
191
228
|
active = session
|
|
192
229
|
if active is None:
|
|
193
230
|
for candidate in reversed(session_stack()):
|
|
@@ -195,19 +232,14 @@ def assert_model_write_allowed(
|
|
|
195
232
|
active = candidate
|
|
196
233
|
break
|
|
197
234
|
actual_scope = active.write_scope if active is not None else None
|
|
198
|
-
if
|
|
199
|
-
return
|
|
235
|
+
if _has_coordinator_authority(active):
|
|
236
|
+
return True
|
|
200
237
|
banner = _format_coordinator_banner(
|
|
201
238
|
model_cls.__name__, op_kind, required_scope, actual_scope
|
|
202
239
|
)
|
|
203
240
|
vcprint(banner, color="red")
|
|
204
241
|
logger.error("CoordinatorWriteViolation: %s", banner)
|
|
205
|
-
|
|
206
|
-
model_cls.__name__,
|
|
207
|
-
op_kind,
|
|
208
|
-
required_scope=required_scope,
|
|
209
|
-
actual_scope=actual_scope,
|
|
210
|
-
)
|
|
242
|
+
return False
|
|
211
243
|
|
|
212
244
|
|
|
213
245
|
def _has_active_open_session() -> bool:
|
|
@@ -248,9 +280,11 @@ def assert_managed_write_allowed(query: str) -> None:
|
|
|
248
280
|
else:
|
|
249
281
|
actual = _active_write_scope()
|
|
250
282
|
if actual != required_scope:
|
|
251
|
-
|
|
252
|
-
token, "write", required_scope
|
|
283
|
+
banner = _format_coordinator_banner(
|
|
284
|
+
token, "write", required_scope, actual
|
|
253
285
|
)
|
|
286
|
+
vcprint(banner, color="red", log_level="ERROR")
|
|
287
|
+
logger.error("CoordinatorWriteViolation: %s", banner)
|
|
254
288
|
return
|
|
255
289
|
# Governed contexts — always allowed:
|
|
256
290
|
if _has_active_open_session():
|
|
@@ -529,6 +563,7 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
|
|
|
529
563
|
|
|
530
564
|
@functools.wraps(original_func)
|
|
531
565
|
async def wrapped(cls, *args, **kwargs):
|
|
566
|
+
active_mode = _managed_mode(cls) or mode
|
|
532
567
|
# Reentrancy guard: when the flush executor calls bulk_create /
|
|
533
568
|
# bulk_update / bulk_delete to actually write rows, those calls
|
|
534
569
|
# must pass through unchanged. Without this check we'd recurse
|
|
@@ -552,7 +587,7 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
|
|
|
552
587
|
session = candidate
|
|
553
588
|
break
|
|
554
589
|
if session is not None:
|
|
555
|
-
if
|
|
590
|
+
if active_mode == "coordinator":
|
|
556
591
|
assert_model_write_allowed(cls, method_name, session=session)
|
|
557
592
|
# Defer to the active Session.
|
|
558
593
|
return await _defer_to_session(
|
|
@@ -565,10 +600,10 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
|
|
|
565
600
|
)
|
|
566
601
|
|
|
567
602
|
# No active Session.
|
|
568
|
-
if
|
|
603
|
+
if active_mode == "coordinator":
|
|
569
604
|
assert_model_write_allowed(cls, method_name)
|
|
570
605
|
|
|
571
|
-
if
|
|
606
|
+
if active_mode == "strict":
|
|
572
607
|
banner = _format_violation_banner(cls.__name__, method_name)
|
|
573
608
|
vcprint(banner, color="red")
|
|
574
609
|
logger.error(
|
|
@@ -750,15 +785,20 @@ def _format_coordinator_banner(
|
|
|
750
785
|
return (
|
|
751
786
|
"\n"
|
|
752
787
|
"████████████████████████████████████████████████████████████████████\n"
|
|
753
|
-
"██ COORDINATOR
|
|
788
|
+
"██ COORDINATOR OWNERSHIP BYPASS — WRITE CONTINUED ██\n"
|
|
754
789
|
"████████████████████████████████████████████████████████████████████\n"
|
|
755
790
|
f" Model : {model_name}\n"
|
|
756
791
|
f" Operation : {op_kind}\n"
|
|
757
792
|
f" Required : Session(write_scope={required_scope!r})\n"
|
|
758
793
|
f" Actual : Session(write_scope={actual_scope!r})\n"
|
|
794
|
+
" Result : the write was NOT blocked, but bypassed its owner.\n"
|
|
759
795
|
" Fix : queue this write through the registered Coordinator.\n"
|
|
760
|
-
"
|
|
761
|
-
"
|
|
796
|
+
" Override : allow_direct_coordinator_write(\n"
|
|
797
|
+
" Model, reason='...',\n"
|
|
798
|
+
" acknowledgement=\n"
|
|
799
|
+
f" {COORDINATOR_BYPASS_ACKNOWLEDGEMENT!r},\n"
|
|
800
|
+
" )\n"
|
|
801
|
+
" is reserved for intentional out-of-request writes.\n"
|
|
762
802
|
"████████████████████████████████████████████████████████████████████\n"
|
|
763
803
|
)
|
|
764
804
|
|
|
@@ -233,6 +233,7 @@ class Session:
|
|
|
233
233
|
"_opened_at",
|
|
234
234
|
"_stack_token",
|
|
235
235
|
"_write_scope",
|
|
236
|
+
"_write_authority",
|
|
236
237
|
)
|
|
237
238
|
|
|
238
239
|
def __init__(
|
|
@@ -243,6 +244,7 @@ class Session:
|
|
|
243
244
|
expected_writes: int | None = None,
|
|
244
245
|
max_age_secs: int | None = None,
|
|
245
246
|
write_scope: str | None = None,
|
|
247
|
+
_write_authority: object | None = None,
|
|
246
248
|
) -> None:
|
|
247
249
|
self._id = str(uuid.uuid4())
|
|
248
250
|
self._database = database
|
|
@@ -256,6 +258,7 @@ class Session:
|
|
|
256
258
|
self._opened_at = datetime.now(UTC)
|
|
257
259
|
self._stack_token: Any = None
|
|
258
260
|
self._write_scope = write_scope
|
|
261
|
+
self._write_authority = _write_authority
|
|
259
262
|
|
|
260
263
|
# ---------------------- properties / introspection -------------------
|
|
261
264
|
|
|
@@ -285,6 +288,9 @@ class Session:
|
|
|
285
288
|
"""
|
|
286
289
|
return self._write_scope
|
|
287
290
|
|
|
291
|
+
def _has_write_authority(self, authority: object) -> bool:
|
|
292
|
+
return self._write_authority is authority
|
|
293
|
+
|
|
288
294
|
@property
|
|
289
295
|
def report(self) -> FlushReport | None:
|
|
290
296
|
return self._report
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.11"
|
|
4
4
|
description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -109,7 +109,7 @@ class TestDeclareManagedRegistersStrictTable:
|
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
class TestCoordinatorOwnedGuard:
|
|
112
|
-
def
|
|
112
|
+
def test_plain_session_warns_but_still_defers(self, caplog):
|
|
113
113
|
model = _make_model(
|
|
114
114
|
"OwnedThing",
|
|
115
115
|
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
@@ -117,12 +117,12 @@ class TestCoordinatorOwnedGuard:
|
|
|
117
117
|
M.declare_managed(model, mode="coordinator", write_scope="request_writer")
|
|
118
118
|
|
|
119
119
|
from matrx_orm import Session
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
session = Session()
|
|
121
|
+
session.defer_insert(model, {"id": "abc"})
|
|
122
|
+
assert session.ops_count == 1
|
|
123
|
+
assert "COORDINATOR OWNERSHIP BYPASS" in caplog.text
|
|
124
124
|
|
|
125
|
-
def
|
|
125
|
+
def test_forged_scope_warns_but_still_defers(self, caplog):
|
|
126
126
|
model = _make_model(
|
|
127
127
|
"OwnedThing",
|
|
128
128
|
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
@@ -134,8 +134,22 @@ class TestCoordinatorOwnedGuard:
|
|
|
134
134
|
session = Session(write_scope="request_writer")
|
|
135
135
|
session.defer_insert(model, {"id": "abc"})
|
|
136
136
|
assert session.ops_count == 1
|
|
137
|
+
assert "COORDINATOR OWNERSHIP BYPASS" in caplog.text
|
|
138
|
+
|
|
139
|
+
def test_real_coordinator_authority_is_silent(self, caplog):
|
|
140
|
+
model = _make_model(
|
|
141
|
+
"OwnedThing",
|
|
142
|
+
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
143
|
+
)
|
|
144
|
+
M.declare_managed(model, mode="coordinator")
|
|
145
|
+
|
|
146
|
+
caplog.clear()
|
|
147
|
+
session = M._coordinator_session()
|
|
148
|
+
session.defer_insert(model, {"id": "abc"})
|
|
149
|
+
assert session.ops_count == 1
|
|
150
|
+
assert not [r for r in caplog.records if r.name == "matrx_orm.session.managed"]
|
|
137
151
|
|
|
138
|
-
def
|
|
152
|
+
def test_acknowledged_exception_is_silent(self, caplog):
|
|
139
153
|
model = _make_model(
|
|
140
154
|
"OwnedThing",
|
|
141
155
|
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
@@ -144,8 +158,37 @@ class TestCoordinatorOwnedGuard:
|
|
|
144
158
|
|
|
145
159
|
from matrx_orm import Session
|
|
146
160
|
|
|
147
|
-
|
|
161
|
+
caplog.clear()
|
|
162
|
+
with M.allow_direct_coordinator_write(
|
|
163
|
+
model,
|
|
164
|
+
reason="repair migration 0044",
|
|
165
|
+
acknowledgement=M.COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
|
|
166
|
+
):
|
|
148
167
|
Session().defer_insert(model, {"id": "abc"})
|
|
168
|
+
assert not [r for r in caplog.records if r.name == "matrx_orm.session.managed"]
|
|
169
|
+
|
|
170
|
+
def test_override_does_not_authorize_another_model(self, caplog):
|
|
171
|
+
allowed_model = _make_model(
|
|
172
|
+
"AllowedThing",
|
|
173
|
+
{"id": UUIDField(primary_key=True), "_table_name": "allowed_thing"},
|
|
174
|
+
)
|
|
175
|
+
other_model = _make_model(
|
|
176
|
+
"OtherThing",
|
|
177
|
+
{"id": UUIDField(primary_key=True), "_table_name": "other_thing"},
|
|
178
|
+
)
|
|
179
|
+
M.declare_managed(allowed_model, mode="coordinator")
|
|
180
|
+
M.declare_managed(other_model, mode="coordinator")
|
|
181
|
+
|
|
182
|
+
from matrx_orm import Session
|
|
183
|
+
|
|
184
|
+
caplog.clear()
|
|
185
|
+
with M.allow_direct_coordinator_write(
|
|
186
|
+
allowed_model,
|
|
187
|
+
reason="one-model maintenance",
|
|
188
|
+
acknowledgement=M.COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
|
|
189
|
+
):
|
|
190
|
+
Session().defer_insert(other_model, {"id": "abc"})
|
|
191
|
+
assert "COORDINATOR OWNERSHIP BYPASS" in caplog.text
|
|
149
192
|
|
|
150
193
|
def test_empty_exception_reason_is_rejected(self):
|
|
151
194
|
model = _make_model(
|
|
@@ -153,5 +196,31 @@ class TestCoordinatorOwnedGuard:
|
|
|
153
196
|
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
154
197
|
)
|
|
155
198
|
with pytest.raises(ValueError):
|
|
156
|
-
with M.
|
|
199
|
+
with M.allow_direct_coordinator_write(
|
|
200
|
+
model,
|
|
201
|
+
reason=" ",
|
|
202
|
+
acknowledgement=M.COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
|
|
203
|
+
):
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
def test_wrong_acknowledgement_is_rejected(self):
|
|
207
|
+
model = _make_model(
|
|
208
|
+
"OwnedThing",
|
|
209
|
+
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
210
|
+
)
|
|
211
|
+
with pytest.raises(ValueError):
|
|
212
|
+
with M.allow_direct_coordinator_write(
|
|
213
|
+
model,
|
|
214
|
+
reason="repair migration 0044",
|
|
215
|
+
acknowledgement="yes",
|
|
216
|
+
):
|
|
157
217
|
pass
|
|
218
|
+
|
|
219
|
+
def test_raw_write_warns_but_does_not_raise(self, caplog):
|
|
220
|
+
model = _make_model(
|
|
221
|
+
"OwnedThing",
|
|
222
|
+
{"id": UUIDField(primary_key=True), "_table_name": "owned_thing"},
|
|
223
|
+
)
|
|
224
|
+
M.declare_managed(model, mode="coordinator")
|
|
225
|
+
M.assert_managed_write_allowed("UPDATE owned_thing SET x=1")
|
|
226
|
+
assert "COORDINATOR OWNERSHIP BYPASS" in caplog.text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|