Skip to content

feat: Allow keeping or rejecting extra columns - #422

Open
Mohith Gajjela (Mohith26) wants to merge 1 commit into
Quantco:mainfrom
Mohith26:feat/extra-columns
Open

Mohith Gajjela (Mohith26) wants to merge 1 commit into
Quantco:mainfrom
Mohith26:feat/extra-columns

Conversation

@Mohith26

Copy link
Copy Markdown

Motivation

Closes #100, closes #258. Wide frames (e.g. 100+ feature columns) can't be validated without declaring every column, because validate/filter/cast always 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

  • New extra_columns class keyword on dy.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_valid and cast take extra_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 with cast=True. filter keeps them in both the valid rows and failure.invalid().
  • "raise" raises a SchemaError listing the extra columns (on collect for lazy frames). is_valid returns False.
  • A kept column whose name matches a rule (including primary_key) or an internal __DATAFRAMELY_ column raises a SchemaError instead of being silently overwritten.
  • All of this lives in match_to_schema; filter now drops the internal columns instead of selecting the schema columns, so kept columns survive. With "drop", output is unchanged.
  • Docs: new "Extra columns" feature guide, API entry, and a short note in 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, mypy and prettier are 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error on extraneous columns Allow to pass through columns

2 participants