This code:
#!/usr/bin/env python
from typing import Optional, Type
class Foo(object):
pass
class Bar(Foo):
pass
def test_me() -> Optional[Type[Foo]]:
print("Hi there!")
return Bar
if __name__ == "__main__":
test_me()
will raise TypeError
on 3.5.2:
Traceback (most recent call last):
File "./test.py", line 11, in <module>
def test_me() -> Optional[Type[Foo]]:
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/typing.py", line 649, in __getitem__
return Union[arg, type(None)]
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/typing.py", line 552, in __getitem__
dict(self.__dict__), parameters, _root=True)
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/typing.py", line 512, in __new__
for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/typing.py", line 512, in <genexpr>
for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/typing.py", line 1077, in __subclasscheck__
if super().__subclasscheck__(cls):
File "/Users/mnot/.pyenv/versions/3.5.2/lib/python3.5/abc.py", line 225, in __subclasscheck__
for scls in cls.__subclasses__():
TypeError: descriptor '__subclasses__' of 'type' object needs an argument
whereas it runs fine on 3.6. Same problem if I spell out Optional as Union[None, Type[Foo]]
.
Is there any workaround for 3.5.2, while still accurately annotating the return type?
This is a bug in Python 3.5.2.
Optional[cls]
is a wrapper for Union[cls, type(None)]
, which uses __subclasses__()
to establish whether one class is a subclass of another.
However, Type
is a subclass of type
in Python 3.5.2, which means that
Union[Type[anything], anything_else]
will eventually call
type.__subclasses__()
… which is a problem, because type
is a metaclass, and so expects to be called with the class whose subclasses are being sought, in exactly the same way that calling an instance method on a regular class requires you to supply an instance of itself, e.g. str.upper('foo')
.
The problem is fixed in Python 3.5.3 (and, as you've noticed, 3.6) by making Type
no longer a subclass of type
.