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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With