[BUG](system) Model-constraint decorators return the class they decorate - #761
Open
Seth Fitzsimmons (sethfitz) wants to merge 1 commit into
Open
Seth Fitzsimmons (sethfitz) wants to merge 1 commit into
Seth Fitzsimmons (sethfitz) wants to merge 1 commit into
Conversation
Seth Fitzsimmons (sethfitz)
requested review from
Roel Bollens (RoelBollens-TomTom) and
Victor Schappert (vcschapp)
September 23, 2026 16:07
🗺️ Schema reference docs preview is live!
Note ♻️ This preview updates automatically with each push to this PR. |
Collaborator
|
I'd suggest reusing |
Seth Fitzsimmons (sethfitz)
force-pushed
the
fix-model-constraint-decorator-types
branch
from
September 23, 2026 19:33
14e85e4 to
defbad3
Compare
Collaborator
Author
|
Roel Bollens (@RoelBollens-TomTom) thanks for the pointer. Updated! |
Roel Bollens (RoelBollens-TomTom)
requested changes
Sep 23, 2026
Every model-constraint decorator was annotated to return
type[BaseModel], and every decorator factory to return
Callable[[type[BaseModel]], type[BaseModel]]. pyright and ty honor a
class decorator's return type, so each decorated model became plain
BaseModel: its fields dropped out of hover and completion, attribute
access reported an error, and ty rejected the model in annotations as
invalid-type-form. mypy ignores class-decorator return types, so the
gate passed.
ModelT, the TypeVar create_model already declares, now carries the
decorated class through. ModelConstraint.decorate and no_extra_fields
take and return type[ModelT], and require_if, forbid_if, require_any_of,
require_any_true, radio_group and min_fields_set return
Callable[[type[ModelT]], type[ModelT]]. create_model returns
type[ModelT] for a type[ModelT] base, so decorate returns its result
without a cast.
mypy does check an ordinary call. test_decorator_types.py calls each
decorator as a function under assert_type(..., type[Model]), which puts
all eight return types in front of the mypy gate. Against the old
annotations it reports eight assert-type errors.
pyright and ty are not in the tree, so their results are recorded here.
Revealed type of a field on a model decorated at each of the eight
sites:
before after
pyright Unknown, "Cannot access attribute" the field's type
ty Unknown, invalid-type-form the field's type
mypy the field's type; decorate() called the field's type
as a function gave BaseModel
pyright 1.1.414, ty 0.0.81, mypy 2.3.1, pydantic 2.13.5.
Closes #760
Signed-off-by: Seth Fitzsimmons <seth@mojodna.net>
Seth Fitzsimmons (sethfitz)
force-pushed
the
fix-model-constraint-decorator-types
branch
from
September 23, 2026 22:48
defbad3 to
4c7fbac
Compare
Seth Fitzsimmons (sethfitz)
requested a review
from Roel Bollens (RoelBollens-TomTom)
September 23, 2026 22:52
Roel Bollens (RoelBollens-TomTom)
approved these changes
Sep 23, 2026
This branch was successfully 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.
Description
The model-constraint decorators were annotated to return
type[BaseModel], orCallable[[type[BaseModel]], type[BaseModel]]for the factories. pyright and ty honor a class decorator's return type, so every decorated model,DivisionandNamesamong them, became plainBaseModeland lost its fields in hover, completion and attribute access. mypy ignores class-decorator return types, somake checkpassed.The fix reuses
ModelT, the TypeVarcreate_modelalready declares, to carry the class through.ModelConstraint.decorateandno_extra_fieldstake and returntype[ModelT].require_if,forbid_if,require_any_of,require_any_true,radio_groupandmin_fields_setreturnCallable[[type[ModelT]], type[ModelT]].create_modelreturnstype[ModelT]for atype[ModelT]base, sodecoratereturns its result without a cast. Runtime behavior is unchanged.@scopedinoverture-schema-commonis out of scope; #760 says why.Reference
Testing
test_decorator_types.pycalls each decorator as a function underassert_type(..., type[Model]). mypy checks an ordinary call's return type even though it skips decorator syntax, so this puts all eight sites in front of the mypy gate. Against the old annotations it reports eightassert-typeerrors. It is the only added test.pyright and ty are not in the tree. Revealed type of a field on a model decorated at each of the eight sites:
Unknown, "Cannot access attribute"Unknown,invalid-type-formdecorate()called as a function gaveBaseModelOn the published models,
reveal_type(d.names)ford: Divisiongoes fromUnknowntoNamesunder pyright.Checklist
Checklist of tasks commonly-associated with schema pull requests. Please review the relevant checklists and ensure you do all the tasks that are required for the change you made.
Abut is not intended to test propertyA's validity, and you made a schema change that invalidates propertyAin that counterexample, fix the counterexample to align it with your schema change.Documentation website
Update the hyperlink below to put the pull request number in.
[Docs preview for this PR.](https://dfhx9f55j8eg5.cloudfront.net/pr/<PUT THE PR # HERE>)