| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
…`ForwardRef` before evaluation (https://bugs.python.org/issue41370)
There was a problem hiding this comment.
Looks pretty good! I have one small suggestion. This also needs a NEWS entry, which you can add with blurb.
Sorry, something went wrong.
There was a problem hiding this comment.
This is the code that I posted to the issue. But I also posted a few questions there, and those should at least be answered by tests:
But would that be enough? The algorithm would have to recursively dive into args to see if there's a string hidden deep inside, e.g. list[tuple[int, list["N"]]].
And what if the user writes something hybrid, like List[list["N"]]? What other cases would we need to cover?
And can we sell this as a bugfix for 3.10, or will this be a new feature in 3.11?
How will it interact with from future import annotations?
Sorry, something went wrong.
The tests already cover the sort of case you list here (there's a dict[int, list[List[list["T"]]] I think). We could add more variants though.
I think this can be considered a bugfix. get_type_hints() is supposed to evaluate forward references but it didn't in all cases.
That's another interesting test case. It would basically give us two levels of stringification, like x: 'list["N"]'. We should test that get_type_hints() resolves both levels of strings. |
Sorry, something went wrong.
|
Would also be good to test a recursive forward ref: X = list["X"] def f(x: X): ... get_type_hints(f) # hopefully no RecursionError We guard against this sort of thing already, but we should validate it works in this case too. |
Sorry, something went wrong.
|
You can add a NEWS entry using https://blurb-it.herokuapp.com/ as an alternative to the CLI :) |
Sorry, something went wrong.
Yes, sorry for not mentioning it. Same code, different location. I've added two more unit tests for the cases Guido and you mentioned. Thanks, added it. |
Sorry, something went wrong.
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
| if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)): | ||
| if isinstance(t, GenericAlias): | ||
| args = tuple( | ||
| ForwardRef(arg) if isinstance(arg, str) else arg |
There was a problem hiding this comment.
Do we need to pass globals and locals to ForwardRef here? 🤔
Sorry, something went wrong.
There was a problem hiding this comment.
I may not be knowledgable enough about the typing code base, but I don't see a globals/locals argument for ForwardRef. Do you mean the module? I couldn't find out how to use/what to pass to that argument exactly.
Sorry, something went wrong.
There was a problem hiding this comment.
It's too late for that anyway. The ForwardRef class has an optional module= argument which may be used to resolve references, but the best we could do at this point is passing globalns.get("__name__"), which would just end up with the same globalns as we are using anyway.
This suggests there could still be scenarios where this will fail, esp. when a type alias defined in one module is used in another. But that would only be solvable by recording the module at the time the alias is being defined, and we've already said we wouldn't go that far (since it would require a ForwardRef implementation in C).
Sorry, something went wrong.
|
Actually I'm not sure if we can find a good way to make the Annotated equality unit test work (test_get_type_hints_annotated) with this new behaviour. Maybe this is a side effect of this change that has to be accepted, but I'm happy to hear suggestions. |
Sorry, something went wrong.
We can just change those tests from assertIs to assertEquals. It doesn't seem important that the same object is returned. |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM.
I'll wait landing this until 3.11a6 is out, so as not to disturb the release process (which is already stressed by a last-minute blocker).
Sorry, something went wrong.
| if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)): | ||
| if isinstance(t, GenericAlias): | ||
| args = tuple( | ||
| ForwardRef(arg) if isinstance(arg, str) else arg |
There was a problem hiding this comment.
It's too late for that anyway. The ForwardRef class has an optional module= argument which may be used to resolve references, but the best we could do at this point is passing globalns.get("__name__"), which would just end up with the same globalns as we are using anyway.
This suggests there could still be scenarios where this will fail, esp. when a type alias defined in one module is used in another. But that would only be solvable by recording the module at the time the alias is being defined, and we've already said we wouldn't go that far (since it would require a ForwardRef implementation in C).
Sorry, something went wrong.
|
There's no C code here, and 3.11a6 seems to be still delayed, so I'm just landing this. Sorry for the confusion. |
Sorry, something went wrong.
|
Update: This didn't make 3.11a6. The release process was farther along than I realized. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This Pull Requests suggests a change to typing._eval_type() that considers strings in PEP 585 generics (ie. instances of types.GenericAlias) as forward references. This is necessary because ForwardRef is not and likely will not be implemented in C, and thus strings used as forward references in PEP 585 are not currently evaluated by typing.get_type_hints().
https://bugs.python.org/issue41370
A test case that uses assertIs() currently fails because in the current state _eval_type() creates a copy of the same generic alias with transformed arguments, thus for any PEP 585 generic alias x, the comparison x is typing.get_type_hints(func)['X'] will evaluate to False, assuming func has a field/argument annotation X: x. I think that this can be worked around, and happy to propose an updated implementation.
I created this PR as a proof of concept until a there is consent that in spirit this change to get_type_hints() is something that should happen. So for now I'll keep it as it is.
====================================================================== FAIL: test_get_type_hints_annotated (__main__.GetTypeHintTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/niklas/git/cpython/Lib/test/test_typing.py", line 3298, in test_get_type_hints_annotated self.assertIs( ^^^^^^^^^^^^^^ AssertionError: tuple[typing.Annotated[~T, (1, 0)], ...] is not tuple[typing.Annotated[~T, (1, 0)], ...]Open todos/questions
Co-Authored-By: Guido van Rossum guido@python.org
https://bugs.python.org/issue41370