continuo-python-runtime 0.11.0__tar.gz → 0.12.0__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.
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/image-build.yml +7 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/CHANGELOG.md +142 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/CLAUDE.md +5 -3
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/CONTRIBUTING.md +1 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/Dockerfile.duckdb +8 -21
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/Dockerfile.postgres +10 -33
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/Dockerfile.trino +10 -33
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/PKG-INFO +129 -12
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/README.md +126 -10
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/application/warehouse.py +2 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/pyproject.toml +2 -2
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_adapter_duckdb_wiring.py +4 -0
- continuo_python_runtime-0.12.0/adapters/duckdb/tests/test_integration_duckdb_entrypoint.py +185 -0
- continuo_python_runtime-0.12.0/adapters/duckdb/tests/test_integration_duckdb_seed_preflight.py +154 -0
- continuo_python_runtime-0.12.0/adapters/postgres/README.md +87 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/continuo_postgres_adapter/adapter.py +170 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/pyproject.toml +2 -2
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_adapter_postgres_validation.py +22 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_integration_postgres_conformance.py +5 -0
- continuo_python_runtime-0.12.0/adapters/postgres/tests/test_integration_postgres_dependents.py +701 -0
- continuo_python_runtime-0.12.0/adapters/postgres/tests/test_integration_postgres_entrypoint.py +209 -0
- continuo_python_runtime-0.12.0/adapters/postgres/tests/test_integration_postgres_seed_preflight.py +141 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/README.md +30 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/continuo_trino_adapter/adapter.py +111 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/pyproject.toml +7 -2
- continuo_python_runtime-0.12.0/adapters/trino/tests/test_adapter_trino_dependents.py +224 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_adapter_trino_validation.py +4 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_integration_trino_conformance.py +9 -0
- continuo_python_runtime-0.12.0/adapters/trino/tests/test_integration_trino_entrypoint.py +204 -0
- continuo_python_runtime-0.12.0/adapters/trino/tests/test_integration_trino_seed_preflight.py +105 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/cli.py +6 -0
- continuo_python_runtime-0.12.0/continuo_python_runtime/content_check.py +73 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/kinds.py +10 -2
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/loader.py +52 -2
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/merge.py +26 -45
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/model.py +1 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/paths.py +33 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/errors.py +13 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/harness.py +7 -1
- continuo_python_runtime-0.12.0/continuo_python_runtime/hash_inputs.py +63 -0
- continuo_python_runtime-0.12.0/continuo_python_runtime/hashing.py +93 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/lint.py +194 -6
- continuo_python_runtime-0.12.0/continuo_python_runtime/validation/preflight.py +410 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/validation/runner.py +20 -3
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/README.md +16 -5
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/port.py +70 -1
- continuo_python_runtime-0.12.0/contract/continuo_engine_contract/result.py +59 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/testing/conformance.py +119 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/pyproject.toml +1 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_conformance_suite.py +48 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_port.py +15 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_result.py +33 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_startup_conformance.py +13 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/docs/boundary-contract.md +63 -48
- continuo_python_runtime-0.12.0/image-requirements-duckdb.txt +3 -0
- continuo_python_runtime-0.12.0/image-requirements-postgres.txt +3 -0
- continuo_python_runtime-0.12.0/image-requirements-trino.txt +3 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/pyproject.toml +5 -2
- continuo_python_runtime-0.12.0/scripts/install_image_pins.py +137 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/scripts/verify_installed_packages.py +3 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/.github/workflows/release.yml +28 -3
- continuo_python_runtime-0.12.0/template/Dockerfile +17 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/Dockerfile.pip +6 -5
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/README.md +30 -7
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/test_loader.py +49 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/test_merge.py +172 -40
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/test_wire_contract_write_modes.py +4 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/fixtures/wire_contract_write_modes.yml +16 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/trino-stack/docker-compose.yml +4 -1
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_cli.py +10 -0
- continuo_python_runtime-0.12.0/tests/test_entrypoint_shims.py +23 -0
- continuo_python_runtime-0.12.0/tests/test_harness_content_check.py +176 -0
- continuo_python_runtime-0.12.0/tests/test_hashing.py +196 -0
- continuo_python_runtime-0.12.0/tests/test_hashing_vectors.py +35 -0
- continuo_python_runtime-0.12.0/tests/test_install_image_pins.py +148 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_kind_registries.py +2 -3
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_lint.py +77 -0
- continuo_python_runtime-0.12.0/tests/test_projection_vectors.py +34 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_release_pipeline_structure.py +13 -3
- continuo_python_runtime-0.12.0/tests/test_template.py +498 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_types.py +34 -0
- continuo_python_runtime-0.12.0/tests/test_validation_preflight.py +524 -0
- continuo_python_runtime-0.12.0/tests/test_verify_installed_packages.py +34 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/uv.lock +21 -5
- continuo_python_runtime-0.11.0/adapters/postgres/README.md +0 -43
- continuo_python_runtime-0.11.0/continuo_python_runtime/hashing.py +0 -94
- continuo_python_runtime-0.11.0/contract/continuo_engine_contract/result.py +0 -34
- continuo_python_runtime-0.11.0/image-requirements-duckdb.txt +0 -3
- continuo_python_runtime-0.11.0/image-requirements-postgres.txt +0 -3
- continuo_python_runtime-0.11.0/image-requirements-trino.txt +0 -3
- continuo_python_runtime-0.11.0/template/Dockerfile +0 -16
- continuo_python_runtime-0.11.0/tests/test_hashing.py +0 -195
- continuo_python_runtime-0.11.0/tests/test_template.py +0 -248
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.claude/agents/ddd-architecture-auditor.md +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.dockerignore +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/CODEOWNERS +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/dependabot.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/ci.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/dco.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/image-smoke.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/images.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/publish-pypi.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/release.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/security.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/verify-packages.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.gitignore +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.gitleaks.toml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.python-version +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/CODE_OF_CONDUCT.md +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/DCO +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/LICENSE +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/NOTICE +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/SECURITY.md +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/README.md +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/adapter.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/application/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/application/ports.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/domain/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/domain/columns.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/domain/identifiers.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/domain/layout.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/ddl.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/extensions.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/passfile.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/session.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/settings.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/conftest.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_adapter_duckdb_proxy.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_adapter_duckdb_runtime.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_adapter_duckdb_validation.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_domain_duckdb_columns.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_domain_duckdb_identifiers.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_domain_duckdb_layout.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_infra_duckdb_session.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_infra_duckdb_settings_ddl.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_conformance.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_layout.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_nullability_probe.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_runtime.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_stack.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/duckdb/tests/test_integration_duckdb_validation.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/continuo_postgres_adapter/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/conftest.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_adapter_postgres_connect.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_adapter_runtime_postgres.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_integration_postgres_validation.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/postgres/tests/test_integration_runtime_postgres.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/continuo_trino_adapter/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_adapter_runtime_trino.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_adapter_trino_connect.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_adapter_trino_writes.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_integration_runtime_trino.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_integration_trino_merge_probe.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/adapters/trino/tests/test_integration_trino_validation.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/closure.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/conform.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/context.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/contract/secret_ref.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/csv_loader.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/csv_readers/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/csv_readers/https.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/csv_readers/s3.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/csv_source.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/shape.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/types.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/validation/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/continuo_python_runtime/validation/s3.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/LICENSE +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/NOTICE +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/config.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/keys.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/sql.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/testing/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/continuo_engine_contract/types.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_config.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_contract_keys.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_contract_packaging.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_contract_types.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/contract/tests/test_sql.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/scripts/apply_rehearsal_version.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/scripts/bake_duckdb_extensions.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/scripts/check_version_bumps.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/scripts/security-scan.sh +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/contracts/example.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/contracts/example_csv.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/requirements.lock +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/template/scripts/example.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/conftest.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/__init__.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/test_kinds.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/contract/test_model.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/duckdb-stack/docker-compose.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/node_smoke/contracts/smoke.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/node_smoke/scripts/smoke.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/postgres-stack/docker-compose.yml +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/smoke/trino-stack/catalog/iceberg.properties +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_adapter_naming.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_check_version_bumps_tags.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_ci_test_coverage.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_closure.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_conform.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_context.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_contract_loader.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_contract_pin_consistency.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_csv_loader.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_csv_readers_https.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_csv_readers_integration.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_csv_source.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_errors.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_harness.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_harness_write_modes.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_image_requirements_sync.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_image_smoke_validation.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_no_legacy_names.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_package.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_python_version_consistency.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_rehearsal_version.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_shape.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_validation_runner.py +0 -0
- {continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/tests/test_validation_s3.py +0 -0
{continuo_python_runtime-0.11.0 → continuo_python_runtime-0.12.0}/.github/workflows/image-build.yml
RENAMED
|
@@ -89,7 +89,13 @@ jobs:
|
|
|
89
89
|
# once published. (The image build itself runs inside python:3.14-slim.)
|
|
90
90
|
python-version: "3.14"
|
|
91
91
|
# Ordering between the upload and this build is a `needs:` edge in
|
|
92
|
-
# publish-pypi.yml, but an index can lag its own upload, so poll.
|
|
92
|
+
# publish-pypi.yml, but an index can lag its own upload, so poll. A
|
|
93
|
+
# probe that succeeds does not guarantee the build's own request the
|
|
94
|
+
# same answer (two requests seconds apart can be served different
|
|
95
|
+
# copies of the index page), so the image build's install
|
|
96
|
+
# (scripts/install_image_pins.py) also retries while a pin is not
|
|
97
|
+
# served yet. This probe is the fast pre-check, and in a rehearsal it
|
|
98
|
+
# fetches the files compared byte for byte below. The
|
|
93
99
|
# probe runs with --no-cache-dir: the simple index is served with
|
|
94
100
|
# `Cache-Control: max-age=600`, which pip's HTTP cache honours, so one
|
|
95
101
|
# early stale fetch would otherwise be replayed for 10 minutes, exactly
|
|
@@ -5,6 +5,148 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.12.0] - 2026-11-11
|
|
9
|
+
|
|
10
|
+
**TL;DR**
|
|
11
|
+
|
|
12
|
+
- Hashes now come from continuo-contracts 0.1.0, the library continuo uses.
|
|
13
|
+
Every Python node's `content_hash` changes once, on its first release with
|
|
14
|
+
this version.
|
|
15
|
+
- A node can list the files its script reads in `assets`, and the lint refuses
|
|
16
|
+
five kinds of code the hash cannot see.
|
|
17
|
+
- The harness checks, when a node starts, that the image holds what the
|
|
18
|
+
release hashed.
|
|
19
|
+
- The validation runner has two new read-only ops, `seed_preflight` and
|
|
20
|
+
`probe_views`.
|
|
21
|
+
- Adapter authors: every adapter must now declare `drop_semantics`.
|
|
22
|
+
|
|
23
|
+
**Upgrade**
|
|
24
|
+
|
|
25
|
+
1. Build your domain image `FROM` the `v0.12.0` engine image and install
|
|
26
|
+
`continuo-python-runtime==0.12.0` in CI. The template's "Pin the runtime to
|
|
27
|
+
the image" step does this from the `FROM` line. A `Dockerfile.pip`
|
|
28
|
+
repository replaces that step as the template README says.
|
|
29
|
+
2. Move each Python service with continuo's rebaseline release, not an
|
|
30
|
+
ordinary one: its `content_hash` changes once.
|
|
31
|
+
3. `COPY` into the image every file a node lists in `assets`.
|
|
32
|
+
4. Bring-your-own adapter: declare `drop_semantics`. You may also implement
|
|
33
|
+
`DependentsIntrospection` and run `DependentsConformance`.
|
|
34
|
+
|
|
35
|
+
Versions: runtime 0.12.0 (pins continuo-contracts 0.1.0), engine contract 0.10.0, postgres adapter 0.4.0, trino adapter 0.5.0, duckdb adapter 0.3.0.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Every engine adapter declares `drop_semantics`, what dropping a table does
|
|
40
|
+
to the objects that read it: `"cascade"` (they are destroyed) or
|
|
41
|
+
`"late_binding"` (they survive and fail on their next read). The postgres
|
|
42
|
+
adapter declares `cascade`, the trino and duckdb adapters `late_binding`.
|
|
43
|
+
`StartupConformance` has a new case, `test_declares_a_drop_semantics`.
|
|
44
|
+
- `continuo_engine_contract.port` has an opt-in capability,
|
|
45
|
+
`DependentsIntrospection`, with `Dependent` and `Dependents` and the
|
|
46
|
+
vocabularies `DROP_SEMANTICS`, `COMPLETENESS` and `DEPENDENT_KINDS`. Its one
|
|
47
|
+
method, `dependents(schema, table)`, lists every object a drop of the table
|
|
48
|
+
would destroy or leave reading a missing table, and gives none for a missing
|
|
49
|
+
table or schema. The conformance class `DependentsConformance` checks it,
|
|
50
|
+
with an overridable `create_view` hook.
|
|
51
|
+
- `PostgresAdapter.dependents(schema, table)` lists, with `completeness="exact"`,
|
|
52
|
+
every object a `DROP TABLE ... CASCADE` of the table destroys or changes: views
|
|
53
|
+
and materialized views to any depth, SQL-standard functions and procedures,
|
|
54
|
+
foreign keys and policies on other tables, rules, inheriting tables, tables
|
|
55
|
+
with a column of the table's row type or of an array of it, the types built
|
|
56
|
+
on the table (a domain, a range, a composite type) and what reads them, and
|
|
57
|
+
what reads a partition of the table or a sequence of it (owned or identity).
|
|
58
|
+
Each object is listed once, at its shallowest depth, and the walk follows at
|
|
59
|
+
most 32 dependency levels. It reads `pg_depend`, so it sees what
|
|
60
|
+
`information_schema.view_table_usage` misses, and finds the table
|
|
61
|
+
case-insensitively. `TestPostgresDependents` plus a guard that snapshots the
|
|
62
|
+
schemas around a rolled-back cascade check it. The adapter's README lists
|
|
63
|
+
every kind.
|
|
64
|
+
- `TrinoAdapter.dependents(schema, table)` lists, with
|
|
65
|
+
`completeness="best_effort"`, the views whose SQL reads the table. It reads
|
|
66
|
+
`information_schema.views` of the adapter's catalog in one query and parses
|
|
67
|
+
each definition with sqlglot: an unqualified name is in the view's own
|
|
68
|
+
schema, a name without a catalog is in the adapter's, and a CTE of the same
|
|
69
|
+
statement is not the table. A definition that does not parse is matched by
|
|
70
|
+
the table's name as a whole identifier, with a WARNING naming the view. It
|
|
71
|
+
misses dynamic SQL, views in other catalogs, and views that read the table
|
|
72
|
+
only through another view. The adapter pins `sqlglot==30.23.0`, as the
|
|
73
|
+
engine contract does, and `TestTrinoDependents` runs the conformance cases
|
|
74
|
+
against Trino and the Iceberg REST catalog.
|
|
75
|
+
- The validation runner has two read-only ops, `seed_preflight` and `probe_views`
|
|
76
|
+
(`VALIDATION_OP`), which need no `DBT_TARGET_SCHEMA`. `seed_preflight` lists the
|
|
77
|
+
objects a reload of a seed's production table would destroy or leave reading
|
|
78
|
+
a missing table, compares them with `SEED_PREFLIGHT_ALLOWED` and
|
|
79
|
+
`SEED_PREFLIGHT_ACKNOWLEDGED`, and in `check` mode exits 1 with `reason`
|
|
80
|
+
`unowned_dependents`, `engine_mismatch` or `warehouse_unreachable` when the
|
|
81
|
+
reload must not run; `report` mode prints the same `reason` and exits 0.
|
|
82
|
+
`probe_views` reads each view of `PROBE_VIEWS` with a zero-row `SELECT` and
|
|
83
|
+
reports which no longer bind, without failing. Both print one result block
|
|
84
|
+
with their own keys after `reason`; `result_block` takes them as a keyword-only
|
|
85
|
+
`fields` mapping and refuses a key that shadows a standard one. The README
|
|
86
|
+
section "Seed preflight and view probe" has the inputs, the block and the
|
|
87
|
+
decision table.
|
|
88
|
+
- `continuo-runtime --version` prints `continuo-runtime <version>` to stdout and
|
|
89
|
+
exits 0. The template's `release.yml` has a "Pin the runtime to the image"
|
|
90
|
+
step that reads the version from the Dockerfile's
|
|
91
|
+
`FROM ghcr.io/carolsimone/continuo-python-runtime-<engine>:vX.Y.Z` line
|
|
92
|
+
(failing with an `::error::` when none matches), installs exactly that
|
|
93
|
+
runtime, and fails when the version `uv tool list` reports differs, so CI hashes
|
|
94
|
+
with the runtime the image runs.
|
|
95
|
+
- The harness checks, before it builds the adapter, that the image holds what
|
|
96
|
+
the release hashed. When `CONTENT_HASH` is set it recomputes the node's
|
|
97
|
+
`content_hash` from the files in the image, with the helpers `merge` uses,
|
|
98
|
+
and a difference, or a hashed file missing from the image, fails the node
|
|
99
|
+
with `ImageContractMismatch` (a `ContractError`) and exit 1. The message
|
|
100
|
+
names both hashes, the three parts, the unit paths and the runtime version.
|
|
101
|
+
An unset or empty `CONTENT_HASH` logs a warning and runs. The result block
|
|
102
|
+
carries an optional `reason` key after `unique_id`
|
|
103
|
+
(`image_contract_mismatch`); a block without one keeps its bytes, and
|
|
104
|
+
`continuo_engine_contract.result.result_block` takes it as a keyword-only
|
|
105
|
+
`reason`.
|
|
106
|
+
- `assets:` on a `python-node` or `python-api` node lists the repository files
|
|
107
|
+
its script reads. `merge` folds their bytes into `shared_code_hash`, so
|
|
108
|
+
editing one changes the node's `content_hash`, and writes `assets` on the
|
|
109
|
+
wire entry when a node declares any. A missing path, a non-file or a path
|
|
110
|
+
that resolves outside the repository fails `merge`; `python-csv` refuses the
|
|
111
|
+
key. The image must `COPY` the files.
|
|
112
|
+
- `lint` and `merge` refuse code the content hash cannot see: `runpy`, a change
|
|
113
|
+
to `sys.path`, a `sitecustomize` or `usercustomize` module, `compile()` and
|
|
114
|
+
`types.FunctionType`, and `pickle.load`, `pickle.loads` and
|
|
115
|
+
`pickle.Unpickler`. `merge` also refuses a `sitecustomize.py` or
|
|
116
|
+
`usercustomize.py` at the repository root. A file a script reads belongs in
|
|
117
|
+
`assets`.
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- **Breaking for adapter authors.** `WarehouseAdapter.drop_semantics` is a
|
|
122
|
+
required declaration with no default: an adapter that does not set it cannot
|
|
123
|
+
pass the new conformance case.
|
|
124
|
+
- A node's hashes come from continuo-contracts 0.1.0, the library
|
|
125
|
+
continuo's topology-controller uses too, so CI and continuo compute the
|
|
126
|
+
same values. `config_hash` hashes the node's code projection: `owner`,
|
|
127
|
+
`description`, `criticality` and `schedule` no longer move a node's hash,
|
|
128
|
+
a column type's spelling does not (`INT` and `INTEGER` hash alike), and a
|
|
129
|
+
read's SQL is hashed as written, apart from LF line endings and the
|
|
130
|
+
whitespace around it. `shared_code_hash` folds each import closure file
|
|
131
|
+
with its repo-relative path, so two files that swap their contents change
|
|
132
|
+
it. Every Python node's `content_hash` changes once, on the first release
|
|
133
|
+
built with this version. The wire entry keeps its keys and the fold.
|
|
134
|
+
- A node whose `config` holds a value the hash cannot write (an integer
|
|
135
|
+
beyond 2**53 - 1, NaN or an infinity, nesting deeper than 99 levels, a
|
|
136
|
+
lone surrogate) fails `merge` with a `ContractError` naming the node.
|
|
137
|
+
- The package gate no longer counts `continuo-contracts` as a first-party
|
|
138
|
+
distribution.
|
|
139
|
+
|
|
140
|
+
### Fixed
|
|
141
|
+
|
|
142
|
+
- A release's image build no longer fails when PyPI still serves an index
|
|
143
|
+
page from before the upload. The engine images install their pins through
|
|
144
|
+
`scripts/install_image_pins.py`, which retries an install while pip says a
|
|
145
|
+
pinned version is not served yet (up to 10 minutes) and fails any other
|
|
146
|
+
error at once. The v0.11.0 trino amd64 build hit this 40 s after the upload,
|
|
147
|
+
although the runner's own probe had seen the new version. No package
|
|
148
|
+
changes.
|
|
149
|
+
|
|
8
150
|
## [0.11.0] - 2026-11-04
|
|
9
151
|
|
|
10
152
|
**TL;DR**
|
|
@@ -28,11 +28,13 @@ merged PRs before adding anything new.
|
|
|
28
28
|
conformance suite for engine adapter authors. Its dependencies are installed
|
|
29
29
|
with `continuo-engine-contract[testing]`.
|
|
30
30
|
|
|
31
|
-
- Every adapter MUST satisfy the engine contract
|
|
32
|
-
(`from_env` waits out a warehouse that refuses
|
|
31
|
+
- Every adapter MUST satisfy the engine contract, declare a `drop_semantics`,
|
|
32
|
+
and pass `StartupConformance` (`from_env` waits out a warehouse that refuses
|
|
33
|
+
connections at first, and `drop_semantics` is declared) and the
|
|
33
34
|
shared conformance classes for every capability it declares:
|
|
34
35
|
`IntrospectionConformance` for `TableIntrospection`, `AppendConformance` for
|
|
35
|
-
`AppendCapable`,
|
|
36
|
+
`AppendCapable`, `UpsertConformance` for `UpsertCapable`, and
|
|
37
|
+
`DependentsConformance` for `DependentsIntrospection`. Append and
|
|
36
38
|
upsert adapters must also run the introspection class. An adapter need not
|
|
37
39
|
declare capabilities its engine cannot support.
|
|
38
40
|
- Each adapter's integration suite MUST subclass the applicable classes,
|
|
@@ -151,7 +151,7 @@ gh workflow run publish-pypi.yml --ref <branch> -f dry_run=true
|
|
|
151
151
|
|---|---|---|---|---|
|
|
152
152
|
| `prepare`, `verify-packages`, `preflight-*` | nothing | nothing | none | Fix, delete the tag (`git push --delete origin vX.Y.Z`), re-tag. |
|
|
153
153
|
| `publish-packages` | maybe some of the five files | nothing | none | Re-run failed jobs (`skip-existing` makes it resumable), or fix forward. |
|
|
154
|
-
| `candidate-build` | published | maybe untagged digests | none | Transient (index lag
|
|
154
|
+
| `candidate-build` | published | maybe untagged digests | none | The build waits up to 10 minutes on its own for the index to serve the new pins. Transient (a runner, a longer index lag): re-run failed jobs. Real defect: yank the files on pypi.org and **fix forward** with a new version. |
|
|
155
155
|
| `candidate-verify` | published | untagged candidates | none | Same: an image defect means yank and fix forward. |
|
|
156
156
|
| `promote` | published | verified digests, maybe some engines tagged | none | Re-run failed jobs; tagging is idempotent and refuses to move an existing tag to different content. |
|
|
157
157
|
|
|
@@ -12,30 +12,17 @@ FROM python:3.14.8-slim
|
|
|
12
12
|
# package names, so it is never an extra index.
|
|
13
13
|
# Any other value fails the build.
|
|
14
14
|
# The pinned versions live in image-requirements-duckdb.txt, kept equal to the
|
|
15
|
-
# repo's pyproject versions by tests/test_image_requirements_sync.py.
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
15
|
+
# repo's pyproject versions by tests/test_image_requirements_sync.py.
|
|
16
|
+
# scripts/install_image_pins.py runs the installs for each WHEEL_SOURCE (and
|
|
17
|
+
# says why the wheelhouse wheels are installed by exact file first). It
|
|
18
|
+
# retries an index install while the index does not serve a just-uploaded
|
|
19
|
+
# pin yet: a release builds its images right after the upload.
|
|
19
20
|
ARG WHEEL_SOURCE=pypi
|
|
20
21
|
COPY image-requirements-duckdb.txt /tmp/req.txt
|
|
21
22
|
COPY wheelhous[e] /tmp/wheelhouse
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/tmp/wheelhouse/continuo_engine_contract-*.whl \
|
|
26
|
-
/tmp/wheelhouse/continuo_python_runtime-*.whl \
|
|
27
|
-
/tmp/wheelhouse/continuo_duckdb_adapter-*.whl; \
|
|
28
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
29
|
-
elif [ "$WHEEL_SOURCE" = "testpypi" ]; then \
|
|
30
|
-
pip install --no-cache-dir --no-deps --only-binary=:all: --index-url https://test.pypi.org/simple/ \
|
|
31
|
-
-r /tmp/req.txt; \
|
|
32
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
33
|
-
elif [ "$WHEEL_SOURCE" = "pypi" ]; then \
|
|
34
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
35
|
-
else \
|
|
36
|
-
echo "unknown WHEEL_SOURCE=$WHEEL_SOURCE (pypi|testpypi|wheelhouse)" >&2; exit 1; \
|
|
37
|
-
fi; \
|
|
38
|
-
rm -rf /tmp/req.txt /tmp/wheelhouse
|
|
23
|
+
COPY scripts/install_image_pins.py /tmp/install_image_pins.py
|
|
24
|
+
RUN python /tmp/install_image_pins.py --source "$WHEEL_SOURCE" --adapter duckdb --requirements /tmp/req.txt --wheelhouse /tmp/wheelhouse \
|
|
25
|
+
&& rm -rf /tmp/req.txt /tmp/wheelhouse /tmp/install_image_pins.py
|
|
39
26
|
# The adapter LOADs ducklake/postgres/httpfs/aws from this directory. They are
|
|
40
27
|
# installed now, as root, because the runtime user below has no home directory
|
|
41
28
|
# and the container may have no network. HOME=/tmp gives DuckDB a writable
|
|
@@ -15,48 +15,25 @@ FROM python:3.14.8-slim
|
|
|
15
15
|
# Any other value fails the build.
|
|
16
16
|
# The wheelhouse holds only this repo's own first-party wheels (built via
|
|
17
17
|
# `uv build --all-packages`); third-party dependencies always resolve from
|
|
18
|
-
# PyPI in both branches, so the wheelhouse
|
|
19
|
-
# the requirements install
|
|
18
|
+
# PyPI in both branches, so the wheelhouse install never passes --no-index to
|
|
19
|
+
# the requirements install.
|
|
20
20
|
# The pinned versions live in image-requirements-postgres.txt, kept equal to the
|
|
21
21
|
# repo's pyproject versions by tests/test_image_requirements_sync.py.
|
|
22
22
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# name+version, and continuo-python-runtime / continuo-engine-contract are
|
|
28
|
-
# already published on PyPI under the version this repo's HEAD currently
|
|
29
|
-
# carries (a routine dependency bump lands between releases without a version
|
|
30
|
-
# bump). Left unpinned-by-file, pip silently installs the last PyPI release's
|
|
31
|
-
# transitive pins instead of the wheel this build just produced, defeating
|
|
32
|
-
# the point of testing an unreleased change. Installing the exact local files
|
|
33
|
-
# first (no index contacted at all for this step) is unambiguous; the second
|
|
34
|
-
# install then only has PyPI-only third-party deps left to resolve, and finds
|
|
35
|
-
# the three first-party packages already satisfied.
|
|
23
|
+
# scripts/install_image_pins.py runs the installs for each WHEEL_SOURCE (and
|
|
24
|
+
# says why the wheelhouse wheels are installed by exact file first). It
|
|
25
|
+
# retries an index install while the index does not serve a just-uploaded
|
|
26
|
+
# pin yet: a release builds its images right after the upload.
|
|
36
27
|
ARG WHEEL_SOURCE=pypi
|
|
37
28
|
COPY image-requirements-postgres.txt /tmp/req.txt
|
|
38
29
|
# Optional COPY: the bracket glob matches ./wheelhouse when it exists (the CI/PR
|
|
39
30
|
# WHEEL_SOURCE=wheelhouse path builds it) and no-ops when it is absent, so a
|
|
40
31
|
# release build or a plain `docker build -f Dockerfile.postgres .` needs no
|
|
41
|
-
# pre-created directory. Only the wheelhouse
|
|
32
|
+
# pre-created directory. Only the wheelhouse install reads its contents.
|
|
42
33
|
COPY wheelhous[e] /tmp/wheelhouse
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/tmp/wheelhouse/continuo_engine_contract-*.whl \
|
|
47
|
-
/tmp/wheelhouse/continuo_python_runtime-*.whl \
|
|
48
|
-
/tmp/wheelhouse/continuo_postgres_adapter-*.whl; \
|
|
49
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
50
|
-
elif [ "$WHEEL_SOURCE" = "testpypi" ]; then \
|
|
51
|
-
pip install --no-cache-dir --no-deps --only-binary=:all: --index-url https://test.pypi.org/simple/ \
|
|
52
|
-
-r /tmp/req.txt; \
|
|
53
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
54
|
-
elif [ "$WHEEL_SOURCE" = "pypi" ]; then \
|
|
55
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
56
|
-
else \
|
|
57
|
-
echo "unknown WHEEL_SOURCE=$WHEEL_SOURCE (pypi|testpypi|wheelhouse)" >&2; exit 1; \
|
|
58
|
-
fi; \
|
|
59
|
-
rm -rf /tmp/req.txt /tmp/wheelhouse
|
|
34
|
+
COPY scripts/install_image_pins.py /tmp/install_image_pins.py
|
|
35
|
+
RUN python /tmp/install_image_pins.py --source "$WHEEL_SOURCE" --adapter postgres --requirements /tmp/req.txt --wheelhouse /tmp/wheelhouse \
|
|
36
|
+
&& rm -rf /tmp/req.txt /tmp/wheelhouse /tmp/install_image_pins.py
|
|
60
37
|
# PYTHONPATH is belt-and-braces: the harness also inserts APP_ROOT and the
|
|
61
38
|
# node script's own directory at the front of sys.path before executing it.
|
|
62
39
|
ENV CONTRACT_DIR=/app/contracts APP_ROOT=/app PYTHONPATH=/app
|
|
@@ -15,48 +15,25 @@ FROM python:3.14.8-slim
|
|
|
15
15
|
# Any other value fails the build.
|
|
16
16
|
# The wheelhouse holds only this repo's own first-party wheels (built via
|
|
17
17
|
# `uv build --all-packages`); third-party dependencies always resolve from
|
|
18
|
-
# PyPI in both branches, so the wheelhouse
|
|
19
|
-
# the requirements install
|
|
18
|
+
# PyPI in both branches, so the wheelhouse install never passes --no-index to
|
|
19
|
+
# the requirements install.
|
|
20
20
|
# The pinned versions live in image-requirements-trino.txt, kept equal to the
|
|
21
21
|
# repo's pyproject versions by tests/test_image_requirements_sync.py.
|
|
22
22
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# name+version, and continuo-python-runtime / continuo-engine-contract are
|
|
28
|
-
# already published on PyPI under the version this repo's HEAD currently
|
|
29
|
-
# carries (a routine dependency bump lands between releases without a version
|
|
30
|
-
# bump). Left unpinned-by-file, pip silently installs the last PyPI release's
|
|
31
|
-
# transitive pins instead of the wheel this build just produced, defeating
|
|
32
|
-
# the point of testing an unreleased change. Installing the exact local files
|
|
33
|
-
# first (no index contacted at all for this step) is unambiguous; the second
|
|
34
|
-
# install then only has PyPI-only third-party deps left to resolve, and finds
|
|
35
|
-
# the three first-party packages already satisfied.
|
|
23
|
+
# scripts/install_image_pins.py runs the installs for each WHEEL_SOURCE (and
|
|
24
|
+
# says why the wheelhouse wheels are installed by exact file first). It
|
|
25
|
+
# retries an index install while the index does not serve a just-uploaded
|
|
26
|
+
# pin yet: a release builds its images right after the upload.
|
|
36
27
|
ARG WHEEL_SOURCE=pypi
|
|
37
28
|
COPY image-requirements-trino.txt /tmp/req.txt
|
|
38
29
|
# Optional COPY: the bracket glob matches ./wheelhouse when it exists (the CI/PR
|
|
39
30
|
# WHEEL_SOURCE=wheelhouse path builds it) and no-ops when it is absent, so a
|
|
40
31
|
# release build or a plain `docker build -f Dockerfile.trino .` needs no
|
|
41
|
-
# pre-created directory. Only the wheelhouse
|
|
32
|
+
# pre-created directory. Only the wheelhouse install reads its contents.
|
|
42
33
|
COPY wheelhous[e] /tmp/wheelhouse
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/tmp/wheelhouse/continuo_engine_contract-*.whl \
|
|
47
|
-
/tmp/wheelhouse/continuo_python_runtime-*.whl \
|
|
48
|
-
/tmp/wheelhouse/continuo_trino_adapter-*.whl; \
|
|
49
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
50
|
-
elif [ "$WHEEL_SOURCE" = "testpypi" ]; then \
|
|
51
|
-
pip install --no-cache-dir --no-deps --only-binary=:all: --index-url https://test.pypi.org/simple/ \
|
|
52
|
-
-r /tmp/req.txt; \
|
|
53
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
54
|
-
elif [ "$WHEEL_SOURCE" = "pypi" ]; then \
|
|
55
|
-
pip install --no-cache-dir -r /tmp/req.txt; \
|
|
56
|
-
else \
|
|
57
|
-
echo "unknown WHEEL_SOURCE=$WHEEL_SOURCE (pypi|testpypi|wheelhouse)" >&2; exit 1; \
|
|
58
|
-
fi; \
|
|
59
|
-
rm -rf /tmp/req.txt /tmp/wheelhouse
|
|
34
|
+
COPY scripts/install_image_pins.py /tmp/install_image_pins.py
|
|
35
|
+
RUN python /tmp/install_image_pins.py --source "$WHEEL_SOURCE" --adapter trino --requirements /tmp/req.txt --wheelhouse /tmp/wheelhouse \
|
|
36
|
+
&& rm -rf /tmp/req.txt /tmp/wheelhouse /tmp/install_image_pins.py
|
|
60
37
|
# PYTHONPATH is belt-and-braces: the harness also inserts APP_ROOT and the
|
|
61
38
|
# node script's own directory at the front of sys.path before executing it.
|
|
62
39
|
ENV CONTRACT_DIR=/app/contracts APP_ROOT=/app PYTHONPATH=/app
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: continuo-python-runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Runtime harness, contract tooling, and CI lint for Continuo python nodes.
|
|
5
5
|
Author: Simone Carolini
|
|
6
6
|
Maintainer: Simone Carolini
|
|
@@ -12,7 +12,8 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Python: >=3.14
|
|
14
14
|
Requires-Dist: boto3==1.43.114
|
|
15
|
-
Requires-Dist: continuo-
|
|
15
|
+
Requires-Dist: continuo-contracts==0.1.0
|
|
16
|
+
Requires-Dist: continuo-engine-contract==0.10.0
|
|
16
17
|
Requires-Dist: pyarrow==26.0.0
|
|
17
18
|
Requires-Dist: pyyaml==6.0.3
|
|
18
19
|
Requires-Dist: sqlglot==30.23.0
|
|
@@ -112,7 +113,12 @@ on every change.
|
|
|
112
113
|
last line of stdout, and Continuo's Go side parses it byte-for-byte
|
|
113
114
|
(`pkg/validationresult` in the `continuo` repository). That wire format — the
|
|
114
115
|
sentinel markers, the framing, and the field names inside the block — is
|
|
115
|
-
**frozen**.
|
|
116
|
+
**frozen**. The additions are an optional `reason` key, written after
|
|
117
|
+
`unique_id` only when an outcome has a machine-readable code (for example
|
|
118
|
+
`image_contract_mismatch`), and the op's own keys that the `seed_preflight` and
|
|
119
|
+
`probe_views` ops write after `reason`; a block without either keeps its bytes,
|
|
120
|
+
and the sentinels, `schema_version` and the five standard keys do not change. Reuse
|
|
121
|
+
it; never change it from this side alone. A change here that
|
|
116
122
|
the Go parser has not been taught is a production outage, not a refactor.
|
|
117
123
|
|
|
118
124
|
## Quickstart for domain teams
|
|
@@ -196,14 +202,37 @@ produced, never how it is checked or written.
|
|
|
196
202
|
script reads with `os.environ[...]`. The contract never holds a secret value
|
|
197
203
|
or env var name; the Secret itself is created in the cluster by the operator.
|
|
198
204
|
|
|
205
|
+
### Files a script reads (`assets`)
|
|
206
|
+
|
|
207
|
+
A `python-node` or `python-api` script that reads a file from the repository
|
|
208
|
+
(a model, a lookup table, a JSON config) lists it under `assets:`, one path
|
|
209
|
+
relative to the repository root per entry, written with forward slashes and
|
|
210
|
+
without a `..` segment:
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
- schema: analytics
|
|
214
|
+
table: scored_orders
|
|
215
|
+
kind: python-node
|
|
216
|
+
script: scripts/scored_orders.py
|
|
217
|
+
assets: [data/rates.json, models/scorer.bin]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`merge` reads each listed file and folds its bytes into `shared_code_hash`
|
|
221
|
+
under its path, so editing one changes the node's `content_hash` and starts a
|
|
222
|
+
validation. A path that is also an import of the script is one unit. `merge`
|
|
223
|
+
fails with a `ContractError` for a path that is missing, is not a file or
|
|
224
|
+
resolves outside the repository, and a path listed twice is refused. The image
|
|
225
|
+
must `COPY` the files, because `assets` only tells the hash about them. A
|
|
226
|
+
`python-csv` node has no script, so `assets` is refused on it.
|
|
227
|
+
|
|
199
228
|
### Task timeout
|
|
200
229
|
|
|
201
230
|
Any node may set `timeout_minutes:` — an integer from 1 to 10,080 (one week) —
|
|
202
231
|
to declare how long its task may run before continuo gives up on it. A value
|
|
203
232
|
outside that range, or one that is not an integer, fails
|
|
204
|
-
`continuo-runtime validate`/`merge`/`lint`.
|
|
205
|
-
|
|
206
|
-
`content_hash`.
|
|
233
|
+
`continuo-runtime validate`/`merge`/`lint`. Like `owner`, `schedule`,
|
|
234
|
+
`criticality` and `description`, it is outside the code projection that
|
|
235
|
+
`config_hash` hashes, so changing it never changes the node's `content_hash`.
|
|
207
236
|
|
|
208
237
|
## Write modes
|
|
209
238
|
|
|
@@ -282,7 +311,7 @@ def run(ctx):
|
|
|
282
311
|
example a polars DataFrame. Returning anything else raises `ScriptError`.
|
|
283
312
|
- The dataframe library is your choice. No dataframe library is baked into
|
|
284
313
|
the base image — the package's own runtime dependencies are `pyarrow`,
|
|
285
|
-
`PyYAML`,
|
|
314
|
+
`PyYAML`, `continuo-engine-contract` and `continuo-contracts`. Add whatever you script
|
|
286
315
|
against (pandas, polars, …) as a `RUN pip install` line in your own
|
|
287
316
|
`Dockerfile`, on top of the base image. The base image runs as non-root
|
|
288
317
|
(uid 65532), which pip cannot install under, so switch to root for the
|
|
@@ -298,7 +327,18 @@ def run(ctx):
|
|
|
298
327
|
read_sql as rs` then calling `rs(...)`),
|
|
299
328
|
- private/protected attribute access (`obj._x`) — except on `self`/`cls`,
|
|
300
329
|
so a script's own class-private helpers (`self._helper()`) aren't
|
|
301
|
-
flagged
|
|
330
|
+
flagged,
|
|
331
|
+
- code the content hash cannot see. The hash folds the script and the files
|
|
332
|
+
it imports, found by reading imports, so a node that runs anything else
|
|
333
|
+
can change without its `content_hash` changing. Lint refuses `runpy`, a
|
|
334
|
+
change to `sys.path` (`append`, `insert`, assignment, `del`, and
|
|
335
|
+
`site.addsitedir`), a `sitecustomize` or `usercustomize` module (and a file
|
|
336
|
+
of that name), `compile()` and `types.FunctionType`, and `pickle.load`,
|
|
337
|
+
`pickle.loads` and `pickle.Unpickler`. `merge` applies the same rules to
|
|
338
|
+
every file in a node's import closure, and refuses a `sitecustomize.py` or
|
|
339
|
+
`usercustomize.py` at the repository root, which Python imports before the
|
|
340
|
+
harness starts. A file a script reads, such as a model or a lookup table,
|
|
341
|
+
belongs in the node's `assets`, where its bytes are hashed.
|
|
302
342
|
|
|
303
343
|
The SQL-literal rule is best-effort: docstrings are exempt, but other
|
|
304
344
|
prose may still occasionally match. The hard guarantees enforced by lint
|
|
@@ -337,7 +377,7 @@ declared `output_columns` on the table `run()` returned, in this order:
|
|
|
337
377
|
|
|
338
378
|
## Error taxonomy
|
|
339
379
|
|
|
340
|
-
Every runtime failure is one of
|
|
380
|
+
Every runtime failure is one of six `HarnessError` subclasses
|
|
341
381
|
(`continuo_python_runtime/errors.py`). The sentinel result block's message
|
|
342
382
|
is prefixed `<ErrorClass>: ` so failures can be triaged without parsing free
|
|
343
383
|
text. The harness also logs the same message at ERROR on stderr, with the
|
|
@@ -348,6 +388,7 @@ the result block out of the log before storing it.
|
|
|
348
388
|
| Class | Meaning | Typical fix target |
|
|
349
389
|
| --- | --- | --- |
|
|
350
390
|
| `ContractError` | Contract missing or invalid, node not found for `NODE_ID`, or the declared script is missing/unreachable. | The contract yaml or the `script:` path. |
|
|
391
|
+
| `ImageContractMismatch` (a `ContractError`) | The image does not hold the contract, script, imports or assets the release hashed: the `content_hash` in `CONTENT_HASH` differs from the one the image's files give, or a file the release hashed is missing from the image. The block also carries `"reason":"image_contract_mismatch"`. | Rebuild the image from the released commit, with CI and the image on the same runtime. |
|
|
351
392
|
| `ReadError` | `ctx.read()` was called with an undeclared name, or a declared read failed at the warehouse. | The `reads:` map, or the upstream query/warehouse access. |
|
|
352
393
|
| `ScriptError` | `run()` raised, has no callable `run`, or returned a value that isn't Arrow-convertible. | The node script. |
|
|
353
394
|
| `ConformError` | Structural mismatch (extra/missing/duplicate columns), a strict-cast failure, a not-null violation, a VARCHAR/CHAR overflow, or an `upsert` run whose rows repeat a primary key. | The script's output shape, or the `output_columns` declaration. |
|
|
@@ -359,11 +400,11 @@ A domain repo picks its warehouse engine by which base image it builds
|
|
|
359
400
|
`FROM`:
|
|
360
401
|
|
|
361
402
|
```dockerfile
|
|
362
|
-
FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.
|
|
403
|
+
FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.12.0
|
|
363
404
|
# or
|
|
364
|
-
FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.
|
|
405
|
+
FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.12.0
|
|
365
406
|
# or
|
|
366
|
-
FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.
|
|
407
|
+
FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.12.0
|
|
367
408
|
```
|
|
368
409
|
|
|
369
410
|
The engine is part of the image **name**; the tag is the bare version, so
|
|
@@ -402,6 +443,82 @@ Both shapes end up running the same runtime and the same adapter version;
|
|
|
402
443
|
which one you pick only changes who controls the base OS layer underneath
|
|
403
444
|
them.
|
|
404
445
|
|
|
446
|
+
## Seed preflight and view probe
|
|
447
|
+
|
|
448
|
+
Two validation ops read the warehouse and change nothing. They run through the
|
|
449
|
+
same runner image as the other validation ops (`VALIDATION_OP`), need no
|
|
450
|
+
`DBT_TARGET_SCHEMA`, and print one result block as the last line of stdout.
|
|
451
|
+
|
|
452
|
+
A seed reload drops and recreates the seed's production table. On an engine where
|
|
453
|
+
a drop destroys the objects that read the table (`drop_semantics` `cascade`,
|
|
454
|
+
postgres) that takes every view, function and constraint built on the seed with
|
|
455
|
+
it, including objects continuo does not own. On an engine where those objects
|
|
456
|
+
survive and fail on their next read (`late_binding`, trino and duckdb) they
|
|
457
|
+
may go stale. `seed_preflight` lists what a reload would touch and compares it
|
|
458
|
+
with what the operator has allowed or acknowledged; `probe_views` tries named
|
|
459
|
+
views after a reload and says which no longer bind.
|
|
460
|
+
|
|
461
|
+
`seed_preflight` runs in two places. `check` runs in the seed task's own pod just
|
|
462
|
+
before dbt and stops the reload with exit 1 when the check concludes it must.
|
|
463
|
+
`report` runs in the release's seed-build Job and only reports what a `check`
|
|
464
|
+
would conclude; it exits 0 whatever it finds.
|
|
465
|
+
|
|
466
|
+
### `seed_preflight` inputs
|
|
467
|
+
|
|
468
|
+
| Variable | Meaning |
|
|
469
|
+
|---|---|
|
|
470
|
+
| `SEED_PREFLIGHT_MODE` | `check` or `report`. |
|
|
471
|
+
| `SEED_PREFLIGHT_ENGINE_FAMILY` | `schema_bound` or `late_binding`, the family the release plan expects of the engine. |
|
|
472
|
+
| `PROD_SCHEMA`, `TABLE_NAME` | The seed's production relation. |
|
|
473
|
+
| `SEED_PREFLIGHT_ALLOWED`, `SEED_PREFLIGHT_ACKNOWLEDGED` | JSON arrays of `"<schema>.<name>"`, default `[]`. An object is allowed when `<schema>.<name>`, in lower case, is in either list (entries are trimmed and lower-cased). |
|
|
474
|
+
| `NODE_ID` | Optional; the block's `unique_id`. Default `<PROD_SCHEMA>.<TABLE_NAME>`. |
|
|
475
|
+
|
|
476
|
+
The adapter's own variables (`POSTGRES_*`, `TRINO_*`, ...) are needed as for any
|
|
477
|
+
other op. An adapter's family is `schema_bound` when its `drop_semantics` is
|
|
478
|
+
`cascade`, and `late_binding` when it is `late_binding`.
|
|
479
|
+
|
|
480
|
+
### `seed_preflight` block
|
|
481
|
+
|
|
482
|
+
After the standard keys (and `reason` when one applies): `op` (`seed_preflight`),
|
|
483
|
+
`mode`, `checked` (the warehouse was asked and its listing came back), `engine_family` (as given),
|
|
484
|
+
`drop_semantics` (the adapter's), `completeness` (`exact`, `best_effort`, or
|
|
485
|
+
`none` when nothing was listed) and `dependents`, a list of
|
|
486
|
+
`{schema, name, kind, owner, depth, allowed}` sorted by depth, schema and name.
|
|
487
|
+
`status` is `error` for exit codes 1 and 2, and `failures` is 1 then.
|
|
488
|
+
|
|
489
|
+
`reason` is what a `check` concludes. `report` prints the same `reason` on a
|
|
490
|
+
`success` block.
|
|
491
|
+
|
|
492
|
+
| Situation | `reason` | `check` | `report` |
|
|
493
|
+
|---|---|---|---|
|
|
494
|
+
| a missing or malformed input, no adapter, missing adapter env | none | exit 2 | exit 2 |
|
|
495
|
+
| the adapter's family differs from `SEED_PREFLIGHT_ENGINE_FAMILY`, or it declares none | `engine_mismatch` | exit 1 | exit 0 |
|
|
496
|
+
| the adapter cannot list dependents, `schema_bound` | `unowned_dependents` | exit 1 | exit 0 |
|
|
497
|
+
| the adapter cannot list dependents, `late_binding` | none | exit 0 | exit 0 |
|
|
498
|
+
| the connection fails | `warehouse_unreachable` | exit 1 | exit 0 |
|
|
499
|
+
| the listing fails, `schema_bound` | none | exit 1 | exit 0 |
|
|
500
|
+
| the listing fails, `late_binding` | none | exit 0 | exit 0 |
|
|
501
|
+
| listed, `schema_bound`, an object is not allowed | `unowned_dependents` | exit 1 | exit 0 |
|
|
502
|
+
| anything else listed (on `late_binding` the objects not allowed are external views that may go stale, named in `message`) | none | exit 0 | exit 0 |
|
|
503
|
+
|
|
504
|
+
A seed table that does not exist yet has no dependents and passes. A stop's
|
|
505
|
+
`message` starts with its `reason` and names each object outside the set as
|
|
506
|
+
`<schema>.<name> (<kind>, owner <owner>)`.
|
|
507
|
+
|
|
508
|
+
### `probe_views`
|
|
509
|
+
|
|
510
|
+
`PROBE_VIEWS` is a JSON array of `"<schema>.<name>"`, split on the first dot;
|
|
511
|
+
`NODE_ID` is optional (default `probe_views`). Each view is read with
|
|
512
|
+
`SELECT * FROM "<schema>"."<name>" LIMIT 0`. The block has `op`
|
|
513
|
+
(`probe_views`), `checked` (the warehouse was reachable) and `views`, a list of
|
|
514
|
+
`{schema, name, ok, error}` in the order given, where `error` is the first line
|
|
515
|
+
of the failure, at most 500 characters, and empty when `ok`. A view that fails
|
|
516
|
+
its probe never fails the op: the exit code is 0, and `message` reads
|
|
517
|
+
`<n> of <total> views failed: <names>`. A warehouse that cannot be reached gives
|
|
518
|
+
exit 0, `checked` false, `views` empty and `reason` `warehouse_unreachable`.
|
|
519
|
+
Only a missing or malformed input, no adapter, missing adapter env or an
|
|
520
|
+
unexpected error is exit 2.
|
|
521
|
+
|
|
405
522
|
## Further reading
|
|
406
523
|
|
|
407
524
|
- `docs/superpowers/specs/2026-07-31-python-runtime-design.md` — this
|