data_type contract semantic change: update schema contracts documentation - #4353
data_type contract semantic change: update schema contracts documentation#4353ShreyasGS wants to merge 3 commits into
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 9617b6b | Commit Preview URL Branch Preview URL |
Aug 13 2026, 12:22 PM |
ShreyasGS
left a comment
There was a problem hiding this comment.
Reviewed against dlt/common/schema/typing.py and Schema.apply_schema_contract. The vocabulary is right, including timezone, and the corrections to data_types -> data_type and discard_rows/discard_columns -> discard_row/discard_value are real fixes — those were wrong before. Three comments inline: two changes and one question.
| 6. `is_variant` indicates that the column was generated as a variant of another column. | ||
|
|
||
| :::note | ||
| `data_type`, `nullable`, `precision`, `scale`, and `timezone` together make up the type of a column. The `data_type` [schema contract](schema-contracts.md#setting-up-the-contract) governs changes to them: with `data_type` set to `freeze`, `dlt` raises when a [variant column](#variant-columns) is created and also when any of those properties changes on a column that already has a `data_type`. |
There was a problem hiding this comment.
"on a column that already has a data_type" is narrower than the code. The condition in apply_schema_contract is not is_new_column and not is_variant and existing_col — so a pre-existing column that had hints but no data_type and now gains one also trips the contract, because existing_col.get(prop) is None and differs.
Please reword to describe the rule as it is: the contract governs a type-property change on any column that already existed and is not a variant. Do not overstate it either — if the precise boundary is awkward to phrase, say what holds and leave the corner case out rather than guessing.
There was a problem hiding this comment.
Reworded: the note now says the contract governs a change to one of those properties on a column that already exists in the table and is not a variant, without the "already has a data_type" qualifier.
On the specific case you raised — I ran it and a pre-existing column with hints but no data_type does not trip the data_type contract. is_new_column = not existing_col or not is_complete_column(existing_col), and is_complete_column requires a name and a data_type (dlt/common/schema/utils.py:403), so such a column is treated as new and falls under the columns contract instead. With {"columns": "evolve", "data_type": "freeze"}, {'name':'c','nullable':False} gaining data_type: bigint passed without raising. What does raise is a property change on an already complete column, including a property that was absent before (precision, timezone) or a flipped nullable — all DataValidationError. I left that boundary out of the page as the awkward corner, per your last sentence.
|
|
||
| ### **Lesson 7: Data Contracts** [](https://molab.marimo.io/github/dlt-hub/dlt/blob/master/docs/education/dlt-advanced-course/lesson_7_data_contracts.py) [](https://colab.research.google.com/github/dlt-hub/dlt/blob/master/docs/education/dlt-advanced-course/lesson_7_data_contracts.ipynb) [](https://github.com/dlt-hub/dlt/blob/master/docs/education/dlt-advanced-course/lesson_7_data_contracts.ipynb) | ||
| Define expectations on schema, enforce data types and behaviors, and lock down your schema evolution. Ensure reliable downstream use of your data. | ||
| Define expectations on schema, enforce data types and behaviors, and lock down your schema evolution. Ensure reliable downstream use of your data. Note that the `data_type` contract covers variant columns as well as changes to the type of an existing column (`data_type`, `nullable`, `precision`, `scale`, and `timezone`). See [schema contracts](../general-usage/schema-contracts.md#setting-up-the-contract) for the exact rules. |
There was a problem hiding this comment.
This makes Lesson 7's description about twice as long as every other lesson's, which breaks the rhythm of the index. The page is a course contents list, not a reference.
Please cut it to one short clause pointing at the contract rules, and let schema-contracts.md carry the property list.
There was a problem hiding this comment.
Cut. Lesson 7 is now the original two sentences plus one clause: "See schema contracts for what each contract mode covers." The property list stays in schema-contracts.md.
| ## How to control evolution | ||
|
|
||
| `dlt` allows schema evolution control via its schema and data contracts. Refer to our **[documentation](./schema-contracts)** for details. | ||
| `dlt` allows schema evolution control via its schema and data contracts. Refer to our **[documentation](./schema-contracts)** for details. |
There was a problem hiding this comment.
Question, not a change request: why is this line in the diff at all? It looks byte-identical to me and nothing in the issue asks for it. What actually differs here?
There was a problem hiding this comment.
Not byte-identical — the old line used non-breaking spaces (U+00A0) around the bold link: Refer to our\xa0**[documentation](./schema-contracts)**\xa0for details. The branch has ordinary U+0020 there. It was incidental, from the line being retyped rather than edited, and nothing in the issue asks for it. It renders the same and no line wrapping depends on it, but say the word and I will restore the two nbsp characters so the line drops out of the diff.
|
Applied the two changes and answered the question.
One thing worth flagging on the first point. I checked the case you described against the branch and it does not trip the contract: What does trip it, and what I could not have said with the old wording, is a type property appearing or changing on an already complete column — |
|
/revise My earlier comment on The note now ends "on a column that already exists in the table and is not a variant". That is looser than the rule: Restore that sense. Word it however reads best — the constraint is that the sentence must not imply the |
|
Restored the precision on the Re-confirmed against the branch before rewording: Ran it on the branch to check both sides of that boundary. A resource declaring |
Documentation for #4194: data_type contract semantic change: update schema contracts documentation
Closes #4194
What changed
general-usage/data-quality-lifecycle.md— editedgeneral-usage/schema-evolution.md— editedgeneral-usage/schema.md— editedtutorial/advanced-course.md— edited (3 attempts)intro.md— editedThe sidebar is unchanged: every page here already existed.
Named by the issue, and judged to need no change
The agent read these and decided they were already correct. That is a judgement, not a check — worth confirming:
general-usage/resource.md— Also touched by overhaul support for Pydantic data validation #3572. Its only contract text is the 'Put a contract on tables, columns, and data' section (line 71), which describes freeze generically and links to schema-contracts.md#setting-up-the-contract; the other flagged lines (83, 113, 120, 166) aredata_typecolumn hints in examples, not statements about the contract. Nothing there is made wrong by the change.general-usage/schema-contracts.md— Already updated by overhaul support for Pydantic data validation #3572 and correct. Line 25 reads:data_type"is applied when a data type property of an existing column changes. This includes variant columns ... as well as explicit changes todata_type,nullable,precision,scale, ortimezoneon a column that is already complete", and the Arrow/Pandas/Polars section (line 217) repeats the same property list. That matchesSchema.apply_schema_contractin dlt/common/schema/schema.py, includingtimezone. Nothing on the page still says the contract is variant-only.How this was checked
Third attempt on
tutorial/advanced-course.mdonly: the sentence is now split in two, with no em dash and no semicolon. Content unchanged from the verified claim.What I could not confirm
general-usage/schema-evolution.mdline 134 (Refer to our **[documentation]...**) contains two U+00A0 non-breaking spaces in the current file. I cannot emit that character, so they come back as ordinary spaces in the submitted text. Renders identically, but it shows up as a whitespace-only change on a line I was not asked to touch.schema-evolution.mdI also corrected the entity and mode names in the same sentence: it said entitiesdata_typesand modesdiscard_rows/discard_columns, none of which exist. The real names aredata_typeanddiscard_row/discard_value(verified fromTSchemaContractDict/TSchemaEvolutionMode).data-quality-lifecycle.md("Set your schema tofrozen") alone;frozenis not a real mode name (freezeis), but that row is about thecolumnscontract and outside this change.tutorial/advanced-course.mdonly links out to the Lesson 7 notebook (docs/education/dlt-advanced-course/lesson_7_data_contracts.*). I updated the page's description; the notebook content itself is not a documentation page and I did not check or change it — worth a look if it demonstratesdata_type: freeze.Draft opened by agentic-docs from the labelled issue.
Verification ran dlt's linters, the link and anchor checker and the style rules over all 5 pages. The code is in fenced blocks: type-checked, but never executed by CI. The agent ran it while writing — see above.
Note
Revised since this description was written. Everything above describes
the first draft — in particular How this was checked and What I could not
confirm, which may refer to text that has changed since. The reply on each
round says what it did and why.
general-usage/schema.md