Skip to content

[BUG](validation) Reject None in validate() and validate_json() - #721

Merged
Victor Schappert (vcschapp) merged 1 commit into
mainfrom
fix-validate-none-union-seed
Sep 16, 2026
Merged

Victor Schappert (vcschapp) merged 1 commit into
mainfrom
fix-validate-none-union-seed

Conversation

@sethfitz

@sethfitz Seth Fitzsimmons (sethfitz) commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Closes #720

validate(None) and validate_json("null") currently return successfully. They should raise ValidationError.

The non-discriminated union is built with reduce(or_, non_discriminated_models, None). The None initializer makes the resulting union None | Segment | ..., so None validates as a document. The equivalent code in overture-schema-cli guards the empty case instead, and rejects None correctly — this brings the validation package into line with it.

The non-discriminated bucket is non-empty in practice: Segment is an Annotated discriminated union rather than a class, so it fails _can_discriminate's isinstance(model_class, type) gate.

Three annotation details follow from the change: the generator is materialized into a tuple, because a generator's truthiness can't be tested for emptiness; non_discriminated_union widens to type[BaseModel] | UnionType | None, because reduce over a single-element tuple returns the bare model type; and model_union gets an explicit type[BaseModel] | UnionType declaration, because mypy infers a branch variable from its first assignment and so read the narrower UnionType off the both-present branch. mypy caught the second and third.

@sethfitz Seth Fitzsimmons (sethfitz) added the change type - minor 🤏 Minor schema change. See https://lf-overturemaps.atlassian.net/wiki/x/GgDa label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🗺️ Schema reference docs preview is live!

🌍 Preview https://staging.overturemaps.org/schema/pr/721/schema/index.html
🕐 Updated Sep 16, 2026 03:47 UTC
📝 Commit d7c0455
🔧 env SCHEMA_PREVIEW true

Note

♻️ This preview updates automatically with each push to this PR.

The non-discriminated union was built with

    reduce(or_, non_discriminated_models, None)

whose None initializer made the resulting union None | Segment | ..., so
validate(None) and validate_json("null") returned successfully instead
of raising ValidationError.

Guard the empty case instead, matching what the CLI's equivalent code in
overture-schema-cli already does. Three annotation details follow from
that: the generator is materialized into a tuple because a generator's
truthiness cannot be tested for emptiness; non_discriminated_union
widens to type[BaseModel] | UnionType | None because reduce over a
single-element tuple returns the bare model type; and model_union gets
an explicit type[BaseModel] | UnionType declaration, because mypy infers
a branch variable from its first assignment and so read the narrower
UnionType off the both-present branch.

The non-discriminated bucket is non-empty in practice: Segment is an
Annotated discriminated union rather than a class, so it fails
_can_discriminate's isinstance(model_class, type) gate.

Tests assert both directions -- the two null forms raise, and a genuine
feature still round-trips -- so a regression that broke the adapter into
rejecting everything could not pass the negative tests by accident.

Signed-off-by: Seth Fitzsimmons <seth@mojodna.net>
@vcschapp
Victor Schappert (vcschapp) merged commit 2092e8f into main Sep 16, 2026
27 checks passed
@vcschapp
Victor Schappert (vcschapp) deleted the fix-validate-none-union-seed branch September 16, 2026 03:50

This branch was successfully deployed

1 active deployment
staging d7c04551 Deployed Sep 16, 2026 by vcschapp via Deploy #553
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change type - minor 🤏 Minor schema change. See https://lf-overturemaps.atlassian.net/wiki/x/GgDa

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG](validation) validate(None) and validate_json("null") return successfully

3 participants