Skip to content

[jsonschema] Annotate keyword validators as returning iterators - #16722

Open
Dhairya-007 wants to merge 1 commit into
python:mainfrom
Dhairya-007:jsonschema-keyword-iterators
Open

Dhairya-007 wants to merge 1 commit into
python:mainfrom
Dhairya-007:jsonschema-keyword-iterators

Conversation

@Dhairya-007

Copy link
Copy Markdown
Contributor

The keyword validation functions in _keywords and _legacy_keywords are generators that yield ValidationError, but 49 of them were declared -> None. Iterating the result — the only way to use them — was a type error:

for error in additionalProperties(validator, False, instance, schema):
    ...
# error: "None" has no attribute "__iter__" (not iterable)  [attr-defined]
# error: "additionalProperties" does not return a value  [func-returns-value]

A representative usage file reported 7 errors before this change and none after.

The correct type is already in these files

_legacy_keywords.pyi annotates two of its own functions, unevaluatedItems_draft2019 and unevaluatedProperties_draft2019, as Iterator[ValidationError], and protocols.pyi types iter_errors the same way. This just makes the remaining 49 consistent with both, and _legacy_keywords.pyi already imports Iterator and ValidationError for that reason.

Scope

Checked against jsonschema 4.26.0, matching the pinned ~=4.26.0. Every one of the 49 contains a yield or yield from outside any nested function, and at runtime inspect.isgeneratorfunction is True:

>>> list(additionalProperties(v, False, {"a": 1}, {"properties": {}}))
[<ValidationError: "Additional properties are not allowed (*a* was unexpected)">]
  • _keywords.pyi — all 36 functions, plus the two imports the file now needs
  • _legacy_keywords.pyi — 13 of the 18

The three non-generators in _legacy_keywords are deliberately untouched: ignore_ref_siblings, find_evaluated_item_indexes_by_schema and find_evaluated_property_keys_by_schema genuinely return list types and are already annotated as such.

Why stubtest did not catch this

stubtest does not compare return types. It reports the same 12 pre-existing _format errors before and after this change (optional format dependencies, unrelated) — the error sets diff identically. mypy --strict and pyright are both clean on the stubs, and ruff/black pass.

The keyword validation functions in `_keywords` and `_legacy_keywords`
are generators that yield `ValidationError`, but 49 of them were
declared `-> None`, so iterating their result was a type error:

    for error in additionalProperties(validator, False, instance, schema):
        ...
    # error: "None" has no attribute "__iter__" (not iterable)
    # error: "additionalProperties" does not return a value

`_legacy_keywords.pyi` already annotates two of its own functions,
`unevaluatedItems_draft2019` and `unevaluatedProperties_draft2019`, as
`Iterator[ValidationError]`, and `protocols.pyi` uses the same type for
`iter_errors`, so this makes the rest consistent with both.

Every function was checked against jsonschema 4.26.0: all 49 changed
here contain a `yield` or `yield from` outside any nested function. The
three non-generators in `_legacy_keywords` — `ignore_ref_siblings`,
`find_evaluated_item_indexes_by_schema` and
`find_evaluated_property_keys_by_schema` — already return list types and
are untouched.

stubtest reports the same 12 pre-existing `_format` errors before and
after (optional format dependencies, unrelated). mypy --strict and
pyright are clean on the stubs.
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant