diff --git a/CHANGELOG.md b/CHANGELOG.md index fc4b11f..0a17dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Changed + +- The "mode_type not allowed for kind" contract error now quotes the offending + `mode_type` and `kind` (`repr`), matching the sibling "must be one of" error, + so an empty or whitespace value is legible. Docstring fixes only alongside: + the postgres `ensure_table` port-signature reference (0.6.0 → 0.8.0) and the + `ensure_unique_key` message-order claim (pyarrow grouping order is not + contractual). + ## [0.9.0] - 2026-10-11 ### Added diff --git a/adapters/postgres/continuo_postgres_adapter/adapter.py b/adapters/postgres/continuo_postgres_adapter/adapter.py index 2f45770..6c83ee9 100644 --- a/adapters/postgres/continuo_postgres_adapter/adapter.py +++ b/adapters/postgres/continuo_postgres_adapter/adapter.py @@ -706,7 +706,7 @@ def ensure_table( this engine's physical-layout vocabulary — ``indexes`` — validated by :func:`_validated_indexes` before any DDL runs, so a malformed config never leaves a half-built table behind (fail closed). Required and - keyword-only, matching the contract 0.6.0 port signature. The create + keyword-only, matching the contract 0.8.0 port signature. The create and every index either all land or none do, in the same explicit BEGIN/COMMIT/ROLLBACK shape build_empty_from_columns uses. diff --git a/continuo_python_runtime/contract/loader.py b/continuo_python_runtime/contract/loader.py index b37fd6b..911d515 100644 --- a/continuo_python_runtime/contract/loader.py +++ b/continuo_python_runtime/contract/loader.py @@ -302,7 +302,7 @@ def parse_node( ) if mode_type not in rules.mode_types: raise ContractError( - f"{label}: 'mode_type' {mode_type} is not allowed for kind {kind} " + f"{label}: 'mode_type' {mode_type!r} is not allowed for kind {kind!r} " f"(allowed: {sorted(rules.mode_types)})" ) diff --git a/contract/continuo_engine_contract/keys.py b/contract/continuo_engine_contract/keys.py index 1ccdfa4..3407ac4 100644 --- a/contract/continuo_engine_contract/keys.py +++ b/contract/continuo_engine_contract/keys.py @@ -29,9 +29,9 @@ def ensure_unique_key(data: "pyarrow.Table", primary_key: Sequence[str]) -> None Groups the rows by the key columns. NULL counts as a value here; the contract requires every key column to be ``nullable: false`` and the harness's ``conform()`` rejects NULLs in such columns before this runs. - The message names the first repeated keys, in order of first appearance, - with their row counts. Only methods of *data* are called, so this module - needs no pyarrow import. + The message names up to a fixed number of the repeated keys with their row + counts; their order follows pyarrow's grouping and is not contractual. Only + methods of *data* are called, so this module needs no pyarrow import. Raises: ValueError: If *primary_key* is empty, names a column *data* lacks, or diff --git a/tests/contract/test_loader.py b/tests/contract/test_loader.py index 675461f..f356747 100644 --- a/tests/contract/test_loader.py +++ b/tests/contract/test_loader.py @@ -786,7 +786,7 @@ def test_python_csv_only_allows_replace(mode): entry = _csv_entry(mode_type=mode) if mode == "upsert": entry["primary_key"] = ["order_id"] - with pytest.raises(ContractError, match=f"'mode_type' {mode} is not allowed for kind python-csv"): + with pytest.raises(ContractError, match=f"'mode_type' '{mode}' is not allowed for kind 'python-csv'"): parse_node(entry, "f.yml", check_reads=False)