With PEP 563, from __future__ import annotations
changes type annotations so that they are evaluated lazily, which provides a bunch of benefits like forward references.
However, this seems to play badly with other features, like dataclasses. For example, I have some code that inspects the type parameters of a class's __init__
method. (The real use case is to provide a default serializer for the class, but that is not important here.)
from dataclasses import dataclass
from typing import get_type_hints
class Foo:
pass
@dataclass
class Bar:
foo: Foo
print(get_type_hints(Bar.__init__))
In Python 3.6 and 3.7, this does what is expected; it prints {'foo': <class '__main__.Foo'>, 'return': <class 'NoneType'>}
.
However, if in Python 3.7, I add from __future__ import annotations
, then this fails with an error:
NameError: name 'Foo' is not defined
I think I understand why this is happening. The __init__
method is defined in dataclasses
which does not have the Foo
object in its environment, and the Foo
annotation is being passed to dataclass
and attached to __init__
as the string "Foo"
rather than as the original object Foo
, but get_type_hints
for the new annotations only does a name lookup in the module where __init__
is defined not where the annotation is defined.
I feel like I must be doing something wrong. I am surprised that these two new features play so poorly together. Is there a proper way to inspect the type hints of of an __init__
method so that it works on dataclasses like it does on normal classes?
When reported to the Python mailing list, this was considered a "good catch" by the BDFL. According to the issue, this has been fixed. The pull request for the fix was merged and backported. It works as expected starting in Python 3.7.6 and Python 3.8.1.