Python 3.7: check if type annotation is "subclass" of generic

Python 3.8 adds typing.get_origin() and typing.get_args() to support basic introspection.

These APIs have also been backported to Python >=3.5 in https://pypi.org/project/typing-compat/.

Be aware that the behavior of typing.get_args is still subtly different in 3.7 when called on the bare generics; in 3.8 typing.get_args(typing.Dict) is (), but in 3.7 it is (~KT, ~VT) (and analogously for the other generics), where ~KT and ~VT are objects of type typing.TypeVar.


First of all: There is no API defined to introspect type hinting objects as defined by the typing module. Type hinting tools are expected to deal with source code, so text, not with Python objects at runtime; mypy doesn't introspect List[str] objects, it instead deals a parsed Abstract Syntax Tree of your source code.

So, while you can always access attributes like __origin__, you are essentially dealing with implementation details (internal bookkeeping), and those implementation details can and will change from version to version.

That said, a core mypy / typing contributor has created the typing_inspect module to develop an introspection API for type hints. The project still documents itself as experimental, and you can expect that to change with time too until it isn't experimental any more. It won't solve your problem here, as it doesn't support Python 3.5, and it's get_origin() function returns the exact same values the __origin__ attribute provides.

With all those caveats out of the way, what you want to access on Python 3.5 / Python 3.6 is the __extra__ attribute; this is the base built-in type used to drive the issubclass() / isinstance() support that the library originally implemented (but since removed in 3.7):

def get_type_class(typ):
    try:
        # Python 3.5 / 3.6
        return typ.__extra__
    except AttributeError:
        # Python 3.7
        return typ.__origin__

This produces <class 'list'> in Python 3.5 and up, regardless. It still uses internal implementation details and may well break in future Python versions.