This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author n_rosenstein
Recipients AlexWaygood, BTaskaya, JelleZijlstra, eric.smith, gvanrossum, joperez, kj, levkivskyi, lukasz.langa, miss-islington, n_rosenstein, sobolevn
Date 2022-01-25.22:07:42
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1643148462.16.0.723370086753.issue41370@roundup.psfhosted.org>
In-reply-to
Content
Interesting! Representing the entire type hint as a string is something I haven't thought about, but it makes sense that it works.

It is my understanding that `get_type_hint()` already walks through the entire type hint recursively. If it weren't, it would not resolve `List['N']` to `List[__main__.N]` in the example below.


>>> from typing import get_type_hints, Mapping, List
>>>
>>> class N:
...   a: Mapping['str', list[List['N']]]
...
>>> get_type_hints(N)
{'a': typing.Mapping[str, list[typing.List[__main__.N]]]}


Upon closer inspection of the `typing` code, I can see that `_eval_type()` is doing that recursion. Applying the change your proposed in your previous message to that function seems to work at least in a trivial test case.


diff --git a/Lib/typing.py b/Lib/typing.py
index e3e098b1fc..ac56b545b4 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -331,6 +331,12 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
     if isinstance(t, ForwardRef):
         return t._evaluate(globalns, localns, recursive_guard)
     if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)):
+        if isinstance(t, GenericAlias):
+            args = tuple(
+                ForwardRef(arg) if isinstance(arg, str) else arg
+                for arg in t.__args__
+            )
+            t = t.__origin__[(*args,)]
         ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
         if ev_args == t.__args__:
             return t


Testcase:


>>> from typing import get_type_hints, Mapping, List
>>> class N:
...  a: Mapping['str', List[list['N']]]
...
>>> get_type_hints(N)
{'a': typing.Mapping[str, typing.List[list[__main__.N]]]}


I believe that this would be enough, but then again I haven't yet had enough time to crack at other implications this might have.


> How will it interact with from __future__ import annotations?

I've never used this future, but from my current, possibly limited, understanding it should have no side effects on how `get_type_hints()` will evaluate fully stringified annotations (as you have already shown, a fully stringified type hint actually works fine with PEP 585 generics).


> And can we sell this as a bugfix for 3.10, or will this be a new feature in 3.11?

I will throw in my personal opinion that this could be a bugfix, but I'm obviously biased as being on the "experiencing end" of this behaviour we're trying to change.
History
Date User Action Args
2022-01-25 22:07:42n_rosensteinsetrecipients: + n_rosenstein, gvanrossum, eric.smith, lukasz.langa, levkivskyi, JelleZijlstra, miss-islington, BTaskaya, sobolevn, joperez, kj, AlexWaygood
2022-01-25 22:07:42n_rosensteinsetmessageid: <1643148462.16.0.723370086753.issue41370@roundup.psfhosted.org>
2022-01-25 22:07:42n_rosensteinlinkissue41370 messages
2022-01-25 22:07:42n_rosensteincreate