Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unpack Optional type annotation in Python 3.5.2

Given this example:

import typing

def foo(bar: int = None):
    pass

typing.get_type_hints(foo)

The type hint for bar is typing.Union[int, None]. How do I get int from that? Neither the __args__ nor __parameters__ property seems to work in Python 3.5.2.


More concretely, I'm trying to write a generic decorator that inspects the signature of a function and does specific things to the arguments. For that it needs to get the class from an annotation such as Optional[T] to then work with T:

annot = typing.Optional[T]
cls = # MAGIC?!
assert cls is T
like image 971
deceze Avatar asked Sep 13 '17 12:09

deceze


2 Answers

In 3.5.2, to get the parameters for a Union, you'll have to use __union_params__.

>>> from typing import Union
>>> d = Union[int, str]
>>> print(*d.__union_params__)
<class 'int'> <class 'str'>

This, unfortunately, seems to only apply until 3.5.2, it was changed in 3.5.3 to use __args__:

>>> from typing import Union
>>> t = Union[int, str]
>>> t.__union_params__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: '_Union' object has no attribute '__union_params__'
>>> print(*t.__args__)
<class 'int'> <class 'str'>

and has stayed __args__ in later versions (3.6 and 3.7).

This is due to the provisional status of the typing module. Many aspects of the internal API are changing between micro version so you'll probably have to deal with a lot of obscure changes.

like image 158
Dimitris Fasarakis Hilliard Avatar answered Oct 03 '22 01:10

Dimitris Fasarakis Hilliard


In these cases I prefer to simply consult the implementation. In 3.5.2 this is the __repr__ of Union:

def __repr__(self):
    r = super().__repr__()
    if self.__union_params__:
        r += '[%s]' % (', '.join(_type_repr(t)
                                 for t in self.__union_params__))
    return r

This suggests that the class is stored in the __union_params__ attribute:

typing.get_type_hints(foo)['bar'].__union_params__[0] is T

This was however changed to __args__ in the commit 5fc25a873cfdec27e46f71e62c9b65df5667c1b4 for 3.5.3.

like image 27
MSeifert Avatar answered Oct 03 '22 01:10

MSeifert