Can I use __qualname__ in a Python type hint with postponed annotation evaluation?
language-lawyer, python
Solution
`Foo.make` doesn't always return an instance of `Foo` (which is what `__qualname__` expands to). It returns an instance of the class it receives as an argument, which isn't necessarily `Foo`. Consider:
>>> class Foo:
... @classmethod
... def make(cls):
... return cls()
...
>>> class Bar(Foo):
... pass
...
>>> type(Bar.make())
<class '__main__.Bar'>
The correct type hint would be a type variable:
T = TypeVar("T", bound="Foo")
class Foo:
@classmethod
def make(cls: Type[T]) -> T:
return cls()
Problem
I like using `__qualname__` for the return-type annotation of factory-style class methods, because it doesn't hardcode the classname and therefore keeps working subclasses (cf. this answer). ``` class Foo: @classmethod def make(cls) -> __qualname__: return cls() ``` Currently this seems to work fine, but I am not sure whether this will still be possible with the postponed evaluation of annotations (PEP 563): the PEP says that Annotations can only use names present in the module scope as postponed evaluation using local names is not reliable (with the sole exception of class-level names resolved by `typing.get_type_hints()`). The PEP also says: The `get_type_hints()` function automatically resolves the correct value of `globalns` for functions and classes. It also automatically provides the correct `localns` for classes. However, the following code ``` from __future__ import annotations import typing class Foo(): @classmethod def make(cls) -> __qualname__: return cls() print(typing.get_type_hints(Foo.make)) ``` fails with ``` File "qualname_test.py", line 11, in <module> print(typing.get_type_hints(Foo.make)) File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 1004, in get_type_hints value = _eval_type(value, globalns, localns) File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 263, in _eval_type return t._evaluate(globalns, localns) File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 467, in _evaluate eval(self.__forward_code__, globalns, localns), File "<string>", line 1, in <module> NameError: name '__qualname__' is not defined ``` Commenting the `__future__` import makes the code work again, in that case it outputs ``` {'return': <class '__main__.Foo'>} ``` as expected. Is my use case `__qualname__` supported (which would mean that `get_type_hints` is buggy), or is this approach not possible with PEP 563?