Repository navigation
feat: Allow keeping or rejecting extra columns - #422
Open
Mohith Gajjela (Mohith26) wants to merge 1 commit into
Open
Mohith Gajjela (Mohith26) wants to merge 1 commit into
Mohith Gajjela (Mohith26) wants to merge 1 commit into
Conversation
Mohith Gajjela (Mohith26)
requested review from
Andreas Albert (AndreasAlbertQC),
Oliver Borchert (borchero) and
Daniel Elsner (delsner)
as code owners
November 4, 2026 16:58
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Kept extra columns can collide with collection validation markers, potentially accepting rejected rows and dropping columns that should be preserved.
3 open issues
What changed in this PR
Adds configurable handling of undeclared columns, enabling validation of wide frames without declaring every column and allowing schemas to reject extras.
Changes:
- Adds inherited schema-level
"drop","raise", and"keep"policies with per-call overrides. - Preserves kept columns through validation, casting, and filtering, with schema-level collision checks.
- Adds tests and documentation for the new behavior.
| File | Description |
|---|---|
tests/schema/test_extra_columns.py |
Tests policies, overrides, casting, filtering, and collisions. |
skills/SKILL.md |
Documents extra-column configuration and overrides. |
docs/guides/features/index.md |
Links the new feature guide. |
docs/guides/features/extra-columns.md |
Explains policies with usage examples. |
docs/api/schema/metadata.rst |
Adds the policy accessor to API documentation. |
dataframely/schema.py |
Exposes per-call overrides and preserves kept columns during filtering. |
dataframely/_match_to_schema.py |
Applies policies and checks schema-level name collisions. |
dataframely/_base_schema.py |
Defines the policy type, inherited setting, and accessor. |
Review effort: Balanced
Comment on lines
+91
to
+95
| reserved = set(target._validation_rules(with_cast=True)) | ||
| if clashes := [ | ||
| name | ||
| for name in extra | ||
| if name in reserved or name.startswith(INTERNAL_COLUMN_PREFIX) |
| Columns not defined in the schema are handled as configured by `extra_columns`, | ||
| falling back to the schema's setting. Kept columns come after the schema's columns. | ||
| """ | ||
| mode = extra_columns if extra_columns is not None else target.extra_columns() |
| "kept as they clash with names used for validation: " | ||
| + ", ".join(f"'{c}'" for c in clashes) | ||
| ) | ||
| return extra |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Motivation
Closes #100, closes #258. Wide frames (e.g. 100+ feature columns) can't be validated without declaring every column, because
validate/filter/castalways drop undeclared columns, and there's no way to reject them either. This implements the design from #100 (comment): a schema-level default, as Oliver Borchert (@borchero) preferred, plus a per-call override. Happy to change names or semantics.Changes
extra_columnsclass keyword ondy.Schema:"drop"(default, current behavior),"raise"or"keep". Subclasses inherit it and can override it.Schema.extra_columns()returns the setting; invalid values raise at class definition.validate,filter,is_validandcasttakeextra_columns=to override the schema setting for one call."keep"passes extra columns through unvalidated, after the schema columns in input order, for eager and lazy frames and withcast=True.filterkeeps them in both the valid rows andfailure.invalid()."raise"raises aSchemaErrorlisting the extra columns (on collect for lazy frames).is_validreturnsFalse.primary_key) or an internal__DATAFRAMELY_column raises aSchemaErrorinstead of being silently overwritten.match_to_schema;filternow drops the internal columns instead of selecting the schema columns, so kept columns survive. With"drop", output is unchanged.skills/SKILL.md.Collections don't get a separate option; a member schema's setting applies when the collection validates it.
Tests:
tests/schema/test_extra_columns.py(44 cases). Full suite passes locally (pytest tests --ignore=tests/benches: 2255 passed),ruff check,ruff format,mypyandprettierare clean on the changed files. I ran these in a plain venv with the native module from the 3.1.2 wheel rather than through pixi.