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 joperez
Recipients BTaskaya, eric.smith, gvanrossum, joperez, levkivskyi, lukasz.langa, vstinner
Date 2020-07-19.11:28:27
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1595158107.48.0.291386757001.issue41341@roundup.psfhosted.org>
In-reply-to
Content
(This issue is already broached in https://bugs.python.org/issue38605, and a in some way in https://bugs.python.org/issue35834, but only as a secondary subject, that's why I've opened a ticket on this particular issue)

ForwardRef of ForwardRef are not currently evaluated by get_type_hints, only the first level is, as illustrated in these examples:
```python
from typing import ForwardRef, Optional, get_type_hints
def func(a: "Optional[\"int\"]"):
    pass
assert get_type_hints(func)["a"] == Optional[ForwardRef("int")] 
# one would expect get_type_hints(func)["a"] == Optional[int] 
```
```python
from __future__ import annotations
from typing import ForwardRef, Optional, get_type_hints
def func(a: Optional["int"]):
    pass
assert get_type_hints(func)["a"] == Optional[ForwardRef("int")]
# one would expect get_type_hints(func)["a"] == Optional[int] (which is the case without the import of __future__.annotations!)
```
On the one hand I find this behavior quite counter-intuitive; I rather think ForwardRef as kind of internal (and wonder why there is no leading underscore, like _GenericAlias where it's used) and I don't understand the purpose of exposing it as the result of the public API get_type_hints. By the way, if ForwardRef can be obtained by retrieving annotations without get_type_hints, stringified annotations (especially since PEP 563) make get_type_hints kind of mandatory, and thus make ForwardRef disappeared (only at the first level so …)

On the other hand, the second example show that adoptions of postponed annotations can change the result of get_type_hints; several libraries relying of get_type_hints could be broken.

An other issue raised here is that if these ForwardRef are not evaluated by get_type_hints, how will be done their evaluatation by the user? It would require to retrieve some globalns/localns — too bad, it's exactly what is doing get_type_hints. And if the ForwardRef is in a class field, the class globalns/localns will have to be kept somewhere while waiting to encounter these random ForwardRef; that's feasible, but really tedious.

Agreeing with Guido Von Rossum (https://bugs.python.org/msg370232), this behavior could be easily "fixed" in get_type_hints.
Actually, there would be only one line to change in ForwardRef._evaluate:
```python
# from
self.__forward_value__ = _type_check(
    eval(self.__forward_code__, globalns, localns),
    "Forward references must evaluate to types.",
    is_argument=self.__forward_is_argument__)
# to
self.__forward_value__ = _eval_type(
    _type_check(
        eval(
            self.__forward_code__, globalns, localns),
            "Forward references must evaluate to types.",
            is_argument=self.__forward_is_argument__,
        ),
    globalns,
    localns,
)

And if this fix could solve the "double ForwardRef" issue mentionned in https://bugs.python.org/issue38605, it would also resolve https://bugs.python.org/issue35834 in the same time, raising NameError in case of unknown ForwardRef with postponed annotation.
History
Date User Action Args
2020-07-19 11:28:27joperezsetrecipients: + joperez, gvanrossum, vstinner, eric.smith, lukasz.langa, levkivskyi, BTaskaya
2020-07-19 11:28:27joperezsetmessageid: <1595158107.48.0.291386757001.issue41341@roundup.psfhosted.org>
2020-07-19 11:28:27joperezlinkissue41341 messages
2020-07-19 11:28:27joperezcreate