-
Notifications
You must be signed in to change notification settings - Fork 629
Support recursive forward references in from_type()
#4614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Support recursive forward references in from_type()
#4614
Conversation
|
(just noting that I'm very skeptical of this one at a glance, and would like to review before merge) |
Add support for self-referential type aliases like `A = list[Union["A", str]]` in from_type(). Previously such recursive types would raise ResolutionFailed, but now Hypothesis can resolve them by looking up forward references in the caller's namespace. Fixes HypothesisWorks#4542
With the new forward reference resolution feature (HypothesisWorks#4542), forward references like Optional["ConcreteFoo"] now resolve successfully when ConcreteFoo is in scope. Updated tests accordingly: - Split test_cannot_resolve_bare_forward_reference into two tests: - test_can_resolve_forward_reference_to_class for Optional/list/List - test_cannot_resolve_type_forward_reference for type/Type - Changed test_string_forward_ref_message to test_string_forward_ref_resolved since User with list["User"] now works on all Python versions
- Add return type annotation (typing.Any) - Add proper type annotation for frame variable - Import types module for FrameType
bddbc01 to
a6200aa
Compare
Explicitly tests that forward references can be resolved from local variables (f_locals path in _resolve_forward_ref_in_caller).
Move test_forward_ref_resolved_from_local_scope from nocover to cover directory so it's included in coverage runs and covers the f_locals lookup path in _resolve_forward_ref_in_caller.
|
I'm worried about false positives / namespace collisions here. If we stack walk looking for We want to find specifically the stack where the forward ref / type was defined. I'm not sure how to do that. This feels like something other libraries (pydantic / type checkers) have solved? I wonder how they approach this? |
|
some thoughts from an offline discussion between myself and Zac: we could perform this stack-walking only when it produces an unambiguous value in all frames. The downside is confused users when this breaks after a seemingly unrelated user code change. |
I don't think that sounds too bad, if we can provide an explanation and concrete guidance when it happens. I don't know what that guidance should be - wrap in |
To avoid false positives from namespace collisions, the stack-walking forward reference resolution now checks all frames and only returns a value if all frames that define the name have the same value. If different frames have different values for the same name, resolution returns None (ambiguous) and falls back to normal error handling. Added test for ambiguous forward reference case.
|
Agreed that we need some actionable advice to give to users in the failure case 👍 On 3.14+, we could advise the user to instantiate |
Add support for self-referential type aliases like
A = list[Union["A", str]]in from_type(). Previously such recursive types would raise ResolutionFailed, but now Hypothesis can resolve them by looking up forward references in the caller's namespace.Fixes #4542