matrx-orm 3.1.52__tar.gz → 3.1.53__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.52 → matrx_orm-3.1.53}/CLAUDE.md +10 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/PKG-INFO +2 -2
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/runner.py +127 -8
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/pyproject.toml +1 -1
- matrx_orm-3.1.53/tests/schema_builder/test_host_models_aggregator.py +171 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/.gitignore +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/AGENTS.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/MODEL_API.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/docs/migrations.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/postgresql.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/scripts/release.sh +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/conftest.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.52 → matrx_orm-3.1.53}/tests/test_sync_twins_guard.py +0 -0
|
@@ -113,6 +113,16 @@ Tests must run against a Postgres instance the developer provides (or a testcont
|
|
|
113
113
|
Host generators may expose a focused schema pass by forwarding
|
|
114
114
|
`only_schemas={...}` to `run_schema_generation`; aidream's focused package-model
|
|
115
115
|
command is `uv run python db/generate.py --schema <schema> --models-only`.
|
|
116
|
+
🚨 **A scoped run must NEVER shrink the host models aggregator.** `db/models/__init__.py`
|
|
117
|
+
is the entire `from db.models import X` surface; it is always written from the FULL,
|
|
118
|
+
unfiltered `generate[]` list, never from the `only_schemas` subset — and model names for
|
|
119
|
+
schemas the run did not regenerate are read back from their modules on disk (AST, no
|
|
120
|
+
import) so cross-schema collision de-flattening stays complete. Writing it from a subset
|
|
121
|
+
once truncated it to one schema and broke imports repo-wide, silently at generation time
|
|
122
|
+
(hand-repaired in 79505fee8). `_write_host_models_aggregator` now RAISES
|
|
123
|
+
`AggregatorShrinkError` naming every schema that would be lost, and writes nothing.
|
|
124
|
+
Guard: `tests/schema_builder/test_host_models_aggregator.py`.
|
|
125
|
+
|
|
116
126
|
`models_only=True` skips unrelated schema/type/helper outputs but still performs
|
|
117
127
|
live introspection and writes the canonical generated Model file. The unfiltered
|
|
118
128
|
command remains the release-wide regeneration path. A package target may declare
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.53
|
|
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
|
|
@@ -8,7 +8,9 @@ Usage:
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import ast
|
|
11
12
|
import os
|
|
13
|
+
import re
|
|
12
14
|
import sys
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
from typing import Any
|
|
@@ -408,8 +410,17 @@ def run_schema_generation(
|
|
|
408
410
|
raise
|
|
409
411
|
|
|
410
412
|
if not models_only:
|
|
413
|
+
# ALWAYS pass the FULL, unfiltered generate[] list. A scoped run
|
|
414
|
+
# (only_schemas={"hindsight"}) regenerates one schema module, but the
|
|
415
|
+
# aggregator must keep re-exporting EVERY host schema — it is the whole
|
|
416
|
+
# `from db.models import X` surface. Handing it the filtered list once
|
|
417
|
+
# truncated db/models/__init__.py to a single schema and broke imports
|
|
418
|
+
# repo-wide, silently at generation time (repaired by hand in 79505fee8).
|
|
411
419
|
_write_host_models_aggregator(
|
|
412
|
-
|
|
420
|
+
cfg.get("generate", []) or [],
|
|
421
|
+
python_root,
|
|
422
|
+
output_config,
|
|
423
|
+
host_schema_model_names,
|
|
413
424
|
)
|
|
414
425
|
|
|
415
426
|
# Declarative host→package DB wiring: resolve each incorporated package's
|
|
@@ -438,8 +449,73 @@ def run_schema_generation(
|
|
|
438
449
|
_close_pools()
|
|
439
450
|
|
|
440
451
|
|
|
452
|
+
_AGGREGATOR_IMPORT_RE = re.compile(r"^from \.(\w+) import \*", re.MULTILINE)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class AggregatorShrinkError(RuntimeError):
|
|
456
|
+
"""The host models aggregator was about to lose schemas or exported names."""
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _existing_aggregator_schemas(init_path: str) -> list[str]:
|
|
460
|
+
"""Schemas currently re-exported by db/models/__init__.py (empty if absent)."""
|
|
461
|
+
try:
|
|
462
|
+
with open(init_path) as f:
|
|
463
|
+
return _AGGREGATOR_IMPORT_RE.findall(f.read())
|
|
464
|
+
except FileNotFoundError:
|
|
465
|
+
return []
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
_MODEL_BASES = {"Model", "MatrxEntity"}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _module_model_names(models_dir: str, schema: str) -> list[str]:
|
|
472
|
+
"""Read a generated schema module's MODEL class names WITHOUT importing it.
|
|
473
|
+
|
|
474
|
+
A scoped generation run only holds fresh names for the schema it regenerated;
|
|
475
|
+
every other schema's names come from its module on disk, so cross-schema
|
|
476
|
+
collision detection stays complete instead of scoped-down.
|
|
477
|
+
|
|
478
|
+
Mirrors ``Schema.model_export_names`` exactly: real Model/MatrxEntity classes
|
|
479
|
+
(tables, read-only views, the public ``Users`` stub) — NOT enums, NOT DTOs,
|
|
480
|
+
NOT managers, and NOT ``_unfetchable`` cross-schema FK stubs (a stub of a
|
|
481
|
+
sibling schema's table would otherwise fake a collision).
|
|
482
|
+
"""
|
|
483
|
+
path = os.path.join(models_dir, f"{schema}.py")
|
|
484
|
+
try:
|
|
485
|
+
with open(path) as f:
|
|
486
|
+
tree = ast.parse(f.read(), filename=path)
|
|
487
|
+
except FileNotFoundError:
|
|
488
|
+
return []
|
|
489
|
+
except SyntaxError as e:
|
|
490
|
+
# Unreadable module ⇒ its names go undetected ⇒ a colliding name would
|
|
491
|
+
# silently stay in the flat namespace and bind whichever schema imported
|
|
492
|
+
# last. Never let that pass quietly.
|
|
493
|
+
vcprint(
|
|
494
|
+
f"[MATRX ORM] Could NOT parse {path} ({e}) — its model names are invisible "
|
|
495
|
+
"to cross-schema collision detection. Fix or regenerate that module.",
|
|
496
|
+
color="red",
|
|
497
|
+
)
|
|
498
|
+
return []
|
|
499
|
+
names: list[str] = []
|
|
500
|
+
for node in tree.body:
|
|
501
|
+
if not isinstance(node, ast.ClassDef):
|
|
502
|
+
continue
|
|
503
|
+
if not any(isinstance(b, ast.Name) and b.id in _MODEL_BASES for b in node.bases):
|
|
504
|
+
continue
|
|
505
|
+
is_stub = any(
|
|
506
|
+
isinstance(stmt, ast.Assign)
|
|
507
|
+
and any(isinstance(t, ast.Name) and t.id == "_unfetchable" for t in stmt.targets)
|
|
508
|
+
and isinstance(stmt.value, ast.Constant)
|
|
509
|
+
and stmt.value.value is True
|
|
510
|
+
for stmt in node.body
|
|
511
|
+
)
|
|
512
|
+
if not is_stub:
|
|
513
|
+
names.append(node.name)
|
|
514
|
+
return names
|
|
515
|
+
|
|
516
|
+
|
|
441
517
|
def _write_host_models_aggregator(
|
|
442
|
-
|
|
518
|
+
all_generate_entries: list[dict[str, Any]],
|
|
443
519
|
python_root: str,
|
|
444
520
|
output_config: OutputConfig,
|
|
445
521
|
host_schema_model_names: dict[str, list[str]] | None = None,
|
|
@@ -464,6 +540,13 @@ def _write_host_models_aggregator(
|
|
|
464
540
|
single-file layout, so it is skipped here. ``public`` is imported first so
|
|
465
541
|
its canonical models win over any cross-schema FK stub registered with
|
|
466
542
|
skip_existing in a sibling schema.
|
|
543
|
+
|
|
544
|
+
``all_generate_entries`` MUST be the full, unfiltered ``generate[]`` list —
|
|
545
|
+
NEVER the subset selected by ``only_schemas``. This file is the entire
|
|
546
|
+
``from db.models import X`` surface; writing it from a scoped run's subset
|
|
547
|
+
truncates it to that one schema and breaks imports across the whole host
|
|
548
|
+
repo, with no error at generation time. The shrink guard below makes that
|
|
549
|
+
outcome impossible rather than merely discouraged.
|
|
467
550
|
"""
|
|
468
551
|
if not (output_config.python and output_config.save_direct):
|
|
469
552
|
return
|
|
@@ -471,7 +554,7 @@ def _write_host_models_aggregator(
|
|
|
471
554
|
host_schema_model_names = host_schema_model_names or {}
|
|
472
555
|
|
|
473
556
|
schemas: list[str] = []
|
|
474
|
-
for entry in
|
|
557
|
+
for entry in all_generate_entries:
|
|
475
558
|
output_block = entry.get("output") or {}
|
|
476
559
|
if output_block.get("python_root"):
|
|
477
560
|
continue # package tree — not part of the host db/models package
|
|
@@ -485,13 +568,52 @@ def _write_host_models_aggregator(
|
|
|
485
568
|
# public first (canonical models register before sibling stubs)
|
|
486
569
|
schemas.sort(key=lambda s: (s != "public", s))
|
|
487
570
|
|
|
488
|
-
|
|
571
|
+
models_dir = os.path.join(python_root, "db", "models")
|
|
572
|
+
os.makedirs(models_dir, exist_ok=True)
|
|
573
|
+
init_path = os.path.join(models_dir, "__init__.py")
|
|
574
|
+
|
|
575
|
+
# Detect model class names that exist in >1 host schema. Names for schemas
|
|
576
|
+
# this run did NOT regenerate (a scoped `--schema X` pass) are read from
|
|
577
|
+
# their modules on disk, so collision detection is never scoped-down either.
|
|
489
578
|
name_to_schemas: dict[str, set[str]] = {}
|
|
490
579
|
for schema_name in schemas:
|
|
491
|
-
|
|
580
|
+
model_names = host_schema_model_names.get(schema_name) or _module_model_names(
|
|
581
|
+
models_dir, schema_name
|
|
582
|
+
)
|
|
583
|
+
for model_name in model_names:
|
|
492
584
|
name_to_schemas.setdefault(model_name, set()).add(schema_name)
|
|
493
585
|
colliding = sorted(n for n, owners in name_to_schemas.items() if len(owners) > 1)
|
|
494
586
|
|
|
587
|
+
# -------------------------------------------------------------------------
|
|
588
|
+
# SHRINK GUARD — silent shrinkage of this file is repo-wide breakage.
|
|
589
|
+
# `from db.models import X` resolves through here; a lost schema import
|
|
590
|
+
# explodes far from generation time, at some unrelated module's import.
|
|
591
|
+
# -------------------------------------------------------------------------
|
|
592
|
+
previous_schemas = _existing_aggregator_schemas(init_path)
|
|
593
|
+
lost_schemas = [s for s in previous_schemas if s not in set(schemas)]
|
|
594
|
+
if lost_schemas:
|
|
595
|
+
vcprint(
|
|
596
|
+
"\n" + "=" * 80 + "\n"
|
|
597
|
+
"[MATRX ORM] REFUSING to shrink the host models aggregator.\n"
|
|
598
|
+
f" File: {init_path}\n"
|
|
599
|
+
f" Currently re-exports {len(previous_schemas)} schema(s); this run would "
|
|
600
|
+
f"write {len(schemas)}.\n"
|
|
601
|
+
f" Would LOSE: {', '.join(sorted(lost_schemas))}\n"
|
|
602
|
+
" Every `from db.models import X` for those schemas would break at import\n"
|
|
603
|
+
" time, far from here. The aggregator must always be written from the FULL\n"
|
|
604
|
+
" generate[] list in db/matrx_orm.yaml — never a scoped subset.\n"
|
|
605
|
+
" If a schema was intentionally removed from matrx_orm.yaml, delete its\n"
|
|
606
|
+
" db/models/<schema>.py and the stale import line by hand, then rerun.\n"
|
|
607
|
+
+ "=" * 80
|
|
608
|
+
+ "\n",
|
|
609
|
+
color="red",
|
|
610
|
+
)
|
|
611
|
+
raise AggregatorShrinkError(
|
|
612
|
+
f"host models aggregator would lose {len(lost_schemas)} schema(s): "
|
|
613
|
+
f"{', '.join(sorted(lost_schemas))} (had {len(previous_schemas)}, "
|
|
614
|
+
f"writing {len(schemas)})"
|
|
615
|
+
)
|
|
616
|
+
|
|
495
617
|
lines = [
|
|
496
618
|
"# File: db/models/__init__.py",
|
|
497
619
|
"# AUTO-GENERATED by matrx-orm schema generation — do not edit by hand.",
|
|
@@ -522,9 +644,6 @@ def _write_host_models_aggregator(
|
|
|
522
644
|
]
|
|
523
645
|
)
|
|
524
646
|
|
|
525
|
-
models_dir = os.path.join(python_root, "db", "models")
|
|
526
|
-
os.makedirs(models_dir, exist_ok=True)
|
|
527
|
-
init_path = os.path.join(models_dir, "__init__.py")
|
|
528
647
|
with open(init_path, "w") as f:
|
|
529
648
|
f.write("\n".join(lines))
|
|
530
649
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.53"
|
|
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" }
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""The host db/models/__init__.py aggregator must never shrink.
|
|
2
|
+
|
|
3
|
+
A scoped generation run (`--schema hindsight`) once rewrote the aggregator down
|
|
4
|
+
to that one schema, breaking `from db.models import X` across the entire host
|
|
5
|
+
repo — silently, with the explosion arriving later at some unrelated import.
|
|
6
|
+
These tests exercise the real writer, not a stand-in.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
from matrx_orm.schema_builder.common import OutputConfig
|
|
13
|
+
from matrx_orm.schema_builder.runner import (
|
|
14
|
+
AggregatorShrinkError,
|
|
15
|
+
_select_generation_targets,
|
|
16
|
+
_write_host_models_aggregator,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
FULL_GENERATE = [
|
|
20
|
+
{"database": "main", "schema": "public"},
|
|
21
|
+
{"database": "main", "schema": "agent"},
|
|
22
|
+
{"database": "main", "schema": "chat"},
|
|
23
|
+
{"database": "main", "schema": "hindsight"},
|
|
24
|
+
{"database": "main", "schema": "workflow"},
|
|
25
|
+
# A package-tree target: keeps its own layout, never joins the aggregator.
|
|
26
|
+
{"database": "main", "schema": "seo", "output": {"python_root": "packages/matrx-seo"}},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
HOST_SCHEMAS = {"public", "agent", "chat", "hindsight", "workflow"}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _output_config() -> OutputConfig:
|
|
33
|
+
return OutputConfig(python=True, save_direct=True, typescript=False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _seed_models_tree(python_root, schema_models: dict[str, list[str]]) -> None:
|
|
37
|
+
"""Write generated-shaped schema modules: real model classes, an enum, a DTO,
|
|
38
|
+
a manager, and an _unfetchable cross-schema FK stub — only the model classes
|
|
39
|
+
may ever count toward collision detection."""
|
|
40
|
+
models_dir = python_root / "db" / "models"
|
|
41
|
+
models_dir.mkdir(parents=True)
|
|
42
|
+
for schema, names in schema_models.items():
|
|
43
|
+
parts = [f'class {n}(Model):\n _table_name = "{n.lower()}"\n' for n in names]
|
|
44
|
+
parts.append('class Visibility(str, Enum):\n PUBLIC = "public"\n')
|
|
45
|
+
parts.append("class SomethingDTO(BaseDTO):\n id: str\n")
|
|
46
|
+
parts.append("class SomethingManager(BaseManager):\n pass\n")
|
|
47
|
+
parts.append(
|
|
48
|
+
'class ForeignStub(Model):\n _table_name = "foreign"\n _unfetchable = True\n'
|
|
49
|
+
)
|
|
50
|
+
exports = "\n".join(f' "{n}",' for n in [*names, "Visibility"])
|
|
51
|
+
(models_dir / f"{schema}.py").write_text(
|
|
52
|
+
"\n".join(parts) + f"\n__all__ = [\n{exports}\n]\n"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _write_aggregator(python_root, host_schema_model_names) -> str:
|
|
57
|
+
_write_host_models_aggregator(
|
|
58
|
+
FULL_GENERATE,
|
|
59
|
+
str(python_root),
|
|
60
|
+
_output_config(),
|
|
61
|
+
host_schema_model_names,
|
|
62
|
+
)
|
|
63
|
+
return (python_root / "db" / "models" / "__init__.py").read_text()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_scoped_generation_keeps_every_schema(tmp_path) -> None:
|
|
67
|
+
"""The failure that actually happened: `--schema hindsight` must not truncate."""
|
|
68
|
+
_seed_models_tree(
|
|
69
|
+
tmp_path,
|
|
70
|
+
{
|
|
71
|
+
"public": ["Conversation", "AgentRun"],
|
|
72
|
+
"agent": ["Definition"],
|
|
73
|
+
"chat": ["AgentRun"],
|
|
74
|
+
"hindsight": ["Enrollment", "Review"],
|
|
75
|
+
"workflow": ["Definition"],
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Full run first — establishes the complete aggregator.
|
|
80
|
+
full_text = _write_aggregator(
|
|
81
|
+
tmp_path,
|
|
82
|
+
{
|
|
83
|
+
"public": ["Conversation", "AgentRun"],
|
|
84
|
+
"agent": ["Definition"],
|
|
85
|
+
"chat": ["AgentRun"],
|
|
86
|
+
"hindsight": ["Enrollment", "Review"],
|
|
87
|
+
"workflow": ["Definition"],
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
for schema in HOST_SCHEMAS:
|
|
91
|
+
assert f"from .{schema} import *" in full_text
|
|
92
|
+
|
|
93
|
+
# Now the scoped pass, exactly as db/generate.py --schema hindsight runs it:
|
|
94
|
+
# only the hindsight entry is selected, and only its fresh model names exist.
|
|
95
|
+
selected, _ = _select_generation_targets(FULL_GENERATE, {"hindsight"})
|
|
96
|
+
assert [e["schema"] for e in selected] == ["hindsight"]
|
|
97
|
+
|
|
98
|
+
scoped_text = _write_aggregator(tmp_path, {"hindsight": ["Enrollment", "Review", "Replay"]})
|
|
99
|
+
|
|
100
|
+
for schema in HOST_SCHEMAS:
|
|
101
|
+
assert f"from .{schema} import *" in scoped_text, (
|
|
102
|
+
f"scoped run dropped schema {schema!r} from the aggregator"
|
|
103
|
+
)
|
|
104
|
+
assert scoped_text.count("import *") == len(HOST_SCHEMAS)
|
|
105
|
+
# A package-tree entry never joins the host aggregator.
|
|
106
|
+
assert "from .seo import *" not in scoped_text
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_scoped_generation_preserves_cross_schema_collisions(tmp_path) -> None:
|
|
110
|
+
"""Names for un-regenerated schemas come from disk, so de-flattening survives."""
|
|
111
|
+
_seed_models_tree(
|
|
112
|
+
tmp_path,
|
|
113
|
+
{
|
|
114
|
+
"public": ["Conversation", "AgentRun"],
|
|
115
|
+
"agent": ["Definition"],
|
|
116
|
+
"chat": ["AgentRun"],
|
|
117
|
+
"hindsight": ["Enrollment"],
|
|
118
|
+
"workflow": ["Definition"],
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
text = _write_aggregator(tmp_path, {"hindsight": ["Enrollment"]})
|
|
122
|
+
|
|
123
|
+
assert '"AgentRun",' in text # public/chat
|
|
124
|
+
assert '"Definition",' in text # agent/workflow
|
|
125
|
+
assert '"Enrollment",' not in text # unique — stays in the flat namespace
|
|
126
|
+
# Enums, DTOs, managers and _unfetchable FK stubs exist in EVERY seeded
|
|
127
|
+
# module; counting them would fabricate collisions across all 5 schemas.
|
|
128
|
+
for non_model in ('"Visibility",', '"SomethingDTO",', '"SomethingManager",', '"ForeignStub",'):
|
|
129
|
+
assert non_model not in text
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_shrinking_the_aggregator_raises_and_names_the_losses(tmp_path) -> None:
|
|
133
|
+
"""A subset generate[] list is refused outright, not written."""
|
|
134
|
+
_seed_models_tree(tmp_path, {s: [f"Model{s.title()}"] for s in HOST_SCHEMAS})
|
|
135
|
+
_write_aggregator(tmp_path, {s: [f"Model{s.title()}"] for s in HOST_SCHEMAS})
|
|
136
|
+
|
|
137
|
+
init_path = tmp_path / "db" / "models" / "__init__.py"
|
|
138
|
+
before = init_path.read_text()
|
|
139
|
+
|
|
140
|
+
subset = [{"database": "main", "schema": "hindsight"}]
|
|
141
|
+
with pytest.raises(AggregatorShrinkError) as excinfo:
|
|
142
|
+
_write_host_models_aggregator(
|
|
143
|
+
subset,
|
|
144
|
+
str(tmp_path),
|
|
145
|
+
_output_config(),
|
|
146
|
+
{"hindsight": ["Enrollment"]},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
message = str(excinfo.value)
|
|
150
|
+
for lost in HOST_SCHEMAS - {"hindsight"}:
|
|
151
|
+
assert lost in message
|
|
152
|
+
# Refused means REFUSED — the file on disk is untouched.
|
|
153
|
+
assert init_path.read_text() == before
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_adding_a_new_schema_still_registers(tmp_path) -> None:
|
|
157
|
+
"""The guard blocks loss, never growth."""
|
|
158
|
+
_seed_models_tree(tmp_path, {s: [f"Model{s.title()}"] for s in HOST_SCHEMAS})
|
|
159
|
+
_write_aggregator(tmp_path, {s: [f"Model{s.title()}"] for s in HOST_SCHEMAS})
|
|
160
|
+
|
|
161
|
+
grown = FULL_GENERATE + [{"database": "main", "schema": "brandnew"}]
|
|
162
|
+
_write_host_models_aggregator(
|
|
163
|
+
grown,
|
|
164
|
+
str(tmp_path),
|
|
165
|
+
_output_config(),
|
|
166
|
+
{"brandnew": ["Shiny"]},
|
|
167
|
+
)
|
|
168
|
+
text = (tmp_path / "db" / "models" / "__init__.py").read_text()
|
|
169
|
+
assert "from .brandnew import *" in text
|
|
170
|
+
for schema in HOST_SCHEMAS:
|
|
171
|
+
assert f"from .{schema} import *" in 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
|
|
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
|