classification
Title: get_type_hints exposes an instance of ForwardRef (internal class) in its result, with `from __future__ import annotations` enabled
Type: behavior Stage:
Components: Versions: Python 3.8, Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Lincoln Quirk, eric.smith, gvanrossum, levkivskyi, lukasz.langa, steven.daprano
Priority: normal Keywords:

Created on 2019-01-26 17:00 by Lincoln Quirk, last changed 2019-01-27 10:31 by levkivskyi.

Messages (4)
msg334396 - (view) Author: Lincoln Quirk (Lincoln Quirk) Date: 2019-01-26 17:00
Consider this code:

```
from __future__ import annotations

import typing

class A:
    f: 'Undef'

hints = typing.get_type_hints(A)
```

Since Undef is not defined, I should get an exception when calling get_type_hints, something like "NameError: name 'Undef' is not defined". But instead, get_type_hints returns {'f': ForwardRef('Undef')}.

If I remove the `from __future__ import annotations` line, get_type_hints correctly raises this exception.

I think the behavior should be to raise an exception in both cases.
msg334397 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2019-01-26 17:32
> Since Undef is not defined, I should get an exception when calling get_type_hints


One of the motives of PEP-563 is to make it easier to use forward references. I'm not sure, but it seems to me that given that, we should not get an exception. So I think the only issue here is that the ForwardReference class is not documented, and should be.

But I admit I'm not confident about my understanding of PEP-563 so I could be wrong.
msg334398 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2019-01-26 17:44
Wait, I just noticed that PEP563 says:

"Note: if an annotation was a string literal already, it will still be wrapped in a string."

https://www.python.org/dev/peps/pep-0563/#id5

In 3.8.0a I get this:

py> from __future__ import annotations
py>
py> class A:
...     f: 'Undef'
...
py> A.__annotations__
{'f': "'Undef'"}

which matches what the PEP says. So I expect that when calling get_type_hints it should return the unquoted string, rather than a ForwardReference.

get_type_hints(A)

expected {'f': 'Undef'}
actually got {'f': ForwardRef('Undef')}
msg334417 - (view) Author: Ivan Levkivskyi (levkivskyi) * (Python committer) Date: 2019-01-27 10:31
It looks like an opposite side of https://github.com/python/typing/issues/508 (I wanted to work on it, but never had time to, sorry).

This question appeared couple times before, and I think there are pros and cons for both returning a ForwardRef() and for raising a NameError. So as I proposed in the issue above, there should be a flag to `get_type_hints()` that controls what to do (the name of the flag, and the default are of course debatable).

Of course, we should try to make `get_type_hints()` behave as much similar as possible with and without PEP 563, regardless. But my point is that currently we have this "in-between" behavior, and it is harder to maintain this "in-between" behavior against PEP 563, than two clearly defined extremes.

> So I expect that when calling get_type_hints it should return the unquoted string, rather than a ForwardReference.

I think the values in the returned dictionary should always be types, either fully evaluated, or ForwardRefs (expecting two options is easier than expecting three).
History
Date User Action Args
2019-01-27 10:31:35levkivskyisetmessages: + msg334417
2019-01-26 18:13:30eric.smithsetnosy: + eric.smith, lukasz.langa
2019-01-26 17:52:11xtreaksetnosy: + gvanrossum, levkivskyi
2019-01-26 17:44:17steven.dapranosetmessages: + msg334398
2019-01-26 17:32:24steven.dapranosetnosy: + steven.daprano
messages: + msg334397
2019-01-26 17:00:18Lincoln Quirkcreate