deceze
deceze

Reputation: 522522

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

Upvotes: 8

Views: 4893

Answers (2)

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160637

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.

Upvotes: 8

MSeifert
MSeifert

Reputation: 152775

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.

Upvotes: 3

Related Questions