freebie freebie - 2 months ago 4x
Python Question

Unpacking Python's Type Annotations

I'm trying to generate some JavaScript based on the type annotations I have provided in on some Python functions by using the

function in the

This part works as I expect when the type is a simple builtin class:

import inspect

def my_function() -> dict:

signature = inspect.signature(my_function)
signature.return_annotation is dict # True

Though I'm not sure how to unwrap and inspect more complex annotations e.g:

from typing import List
import inspect

def my_function() -> List[int]:

signature = inspect.signature(my_function)
signature.return_annotation is List[int] # False

Again similar problem with forward referencing a custom class:

def my_function() -> List['User']:
signature.return_annotation # typing.List[_ForwardRef('User')]

What I'm looking to get out is something like this - so I can branch appropriately while generating the JavaScript:

type = signature.return_annotation... # list
member_type = signature.return_annotation... # int / 'User'



List is not a map of types to GenericMeta, despite the syntax. Each access to it generates a new instance:

>>> [ id(List[str]) for i in range(3) ]
[33105112, 33106872, 33046936]

This means that even List[int] is not List[int]. To compare two instances, you have multiple options:

  • Use ==, i.e., signature.return_annotation == List[int].
  • Store an instance of your type in a global variable and check against that, i.e.,

    a = List[int]
    def foo() -> a:
    inspect.signature(foo).return_annotation is a
  • Use issubclass. The typing module defines that. Note that this might do more than you'd like, make sure to read the _TypeAlias documentation if you use this.

  • Check against List only and read the contents yourself. Though the property is internal, it is unlikely that the implementation will change soon: List[int].__args__[0] contains the type argument starting from Python 3.5.2, and in earlier versions, its List[int].__parameters__[0].

If you'd like to write generic code for your exporter, then the last option is probably best. If you only need to cover a specific use case, I'd personally go with using ==.