I'm trying to generate some JavaScript based on the type annotations I have provided in on some Python functions by using the signature()
function in the inspect
module.
This part works as I expect when the type is a simple builtin class:
import inspect
def my_function() -> dict:
pass
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]:
pass
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']:
pass
...
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'
Thanks.
List
is not a map of types toGenericMeta
, despite the syntax. Each access to it generates a new instance:This means that even
List[int] is not List[int]
. To compare two instances, you have multiple options:==
, i.e.,signature.return_annotation == List[int]
.Store an instance of your type in a global variable and check against that, i.e.,
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.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, itsList[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
==
.Take note, this applies to Python 3.5.1
For Python 3.5.2 take a look at phillip's answer.
You shouldn't be checking with the identity operator as Phillip stated, use equality to get this right.
To check if a hint is a subclass of a
list
you could useissubclass
checks (even though you should take note that this can be quirky in certain cases and is currently worked on):To get the members of a type hint you generally have two watch out for the cases involved.
If it has a simple type, as in
List[int]
the value of the argument is located in the__parameters__
value:Now, in more complex scenarios i.e a class supplied as an argument with
List[User]
you must again extract the__parameter__[0]
and then get the__forward_arg__
. This is because Python wraps the argument in a specialForwardRef
class:Take note, you don't need to actually use
inspect
here,typing
has a helper function namedget_type_hints
that returns the type hints as a dictionary (it uses the function objects__annotations__
attribute).Python 3.8 provides
typing.get_origin()
andtyping.get_args()
for this!See https://docs.python.org/3/library/typing.html#typing.get_origin