I'm trying to find a reliable / cross-version (3.5+) way of checking whether a type annotation is a "subclass" of a given generic type (i.e. get the generic type out of the type annotation object).
On Python 3.5 / 3.6, it works a breeze, as you would expect:
>>> from typing import List
>>> isinstance(List[str], type)
True
>>> issubclass(List[str], List)
True
While on 3.7, it looks like instances of generic types are no longer instances of type, so it will fail:
>>> from typing import List
>>> isinstance(List[str], type)
False
>>> issubclass(List[str], List)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.7/typing.py", line 716, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
Other ideas that come to mind are checking the actual instance type, but:
Python 3.6 / 3.5:
>>> type(List[str])
<class 'typing.GenericMeta'>
Python 3.7:
>>> type(List[str])
<class 'typing._GenericAlias'>
But that doesn't really give any further indication as to which is the actual generic type (might not be List); besides, it feels quite wrong to be doing the check this way, especially since the _GenericAlias now became a "private" type (notice the underscore).
Another thing one could check is the __origin__ argument on the type, but that doesn't feel like the right way to do it either.
And it still differs on 3.7:
>>> List[str].__origin__
<class 'list'>
while 3.5 / 3.6:
>>> List[str].__origin__
typing.List
I've been searching for the "right" way of doing this, but haven't found it in the Python docs / google search.
Now, I'm assuming there must be a clean way of doing this check, as tools like mypy would rely on it for doing type checks..?
Update: about the use case
Ok adding a bit more context here..
So, my use case for this is using introspection on function signatures (argument types / defaults, return type, docstring) to automatically generate a GraphQL schema for them (thus reducing the amount of boilerplate).
I'm still a bit torn on whether this would be a good idea or not.
I like it from the usability point of view (no need to learn yet another way to declare your function signature: just annotate your types the usual way); see the two code examples here to understand what I mean: https://github.com/rshk/pyql
I wonder if supporting generic types (lists, dicts, unions, ...) using types from typing this way adds too much "black magic", that could break in unexpected ways. (It's not a huge issue for now, but what about future Python versions, past 3.7? Is this going to become a maintenance nightmare?).
Of course the alternative would be to just use a custom type annotation that supports a more reliable / future-proof check, eg: https://github.com/rshk/pyql/blob/master/pyql/schema/types/core.py#L337-L339
..but on the downside, that would force people to remember they have to use the custom type annotation. Moreover, I'm not sure how would mypy deal with that (I assume there needs to be a declaration somewhere to say the custom type is fully compatible with typing.List..? Still sounds hackish).
(I'm mostly asking for suggestions on the two approaches, and most importantly any pros/cons of the two alternatives I might have missed. Hope this doesn't become "too broad" for SO..).

所有评论(0)