Runtime inspection utilities for the Python typing module
npx @tessl/cli install tessl/pypi-typing-inspect@0.9.0Runtime inspection utilities for the Python typing module, enabling developers to programmatically analyze and introspect type annotations and generic types at runtime. This library provides comprehensive functions for testing and extracting information from complex type constructs across Python versions 2.7 and 3.5+.
pip install typing_inspectimport typing_inspectCommon imports for type checking functions:
from typing_inspect import (
is_generic_type, is_callable_type, is_union_type, is_optional_type,
get_origin, get_args, get_parameters
)from typing import Generic, TypeVar, Union, List, Optional
from typing_inspect import is_generic_type, is_union_type, get_origin, get_args
T = TypeVar('T')
# Test if types are generic
assert is_generic_type(List[int]) # True
assert is_generic_type(Generic[T]) # True
assert not is_generic_type(int) # False
# Test union types
assert is_union_type(Union[int, str]) # True
assert is_union_type(Optional[int]) # True (Optional[X] = Union[X, None])
# Extract type information
assert get_origin(List[int]) == list # In Python 3.7+
assert get_args(Union[int, str]) == (int, str)
assert get_args(Optional[int]) == (int, type(None))Functions to test various properties of types, determining whether a type represents a specific typing construct.
def is_generic_type(tp):
"""
Test if the given type is a generic type.
Includes Generic itself, but excludes special typing constructs
such as Union, Tuple, Callable, ClassVar.
Args:
tp: Type to test
Returns:
bool: True if tp is a generic type
"""
def is_callable_type(tp):
"""
Test if the type is a generic callable type.
Including subclasses excluding non-generic types and callables.
Args:
tp: Type to test
Returns:
bool: True if tp is a callable type
"""
def is_tuple_type(tp):
"""
Test if the type is a generic tuple type.
Including subclasses excluding non-generic classes.
Args:
tp: Type to test
Returns:
bool: True if tp is a tuple type
"""
def is_union_type(tp):
"""
Test if the type is a union type.
Args:
tp: Type to test
Returns:
bool: True if tp is a union type
"""
def is_optional_type(tp):
"""
Test if the type is type(None), or is a direct union with it.
Such as Optional[T]. Does not inspect nested unions or TypeVar bounds.
Args:
tp: Type to test
Returns:
bool: True if tp is an optional type
"""
def is_final_type(tp):
"""
Test if the type is a final type.
Args:
tp: Type to test
Returns:
bool: True if tp is a final type
"""
def is_literal_type(tp):
"""
Test if the type is a literal type.
Args:
tp: Type to test
Returns:
bool: True if tp is a literal type
"""
def is_typevar(tp):
"""
Test if the type represents a type variable.
Args:
tp: Type to test
Returns:
bool: True if tp is a TypeVar
"""
def is_classvar(tp):
"""
Test if the type represents a class variable.
Args:
tp: Type to test
Returns:
bool: True if tp is a ClassVar
"""
def is_new_type(tp):
"""
Tests if the type represents a distinct type (NewType).
Args:
tp: Type to test
Returns:
bool: True if tp is a NewType
"""
def is_forward_ref(tp):
"""
Tests if the type is a typing.ForwardRef.
Args:
tp: Type to test
Returns:
bool: True if tp is a ForwardRef
"""Functions to extract information from types, such as origins, arguments, parameters, and other metadata.
def get_origin(tp):
"""
Get the unsubscripted version of a type.
Supports generic types, Union, Callable, and Tuple.
Args:
tp: Type to get origin from
Returns:
The origin type, or None for unsupported types
"""
def get_last_origin(tp):
"""
Get the last base of (multiply) subscripted type.
Only supported in Python 3.6. Supports generic types, Union,
Callable, and Tuple.
Args:
tp: Type to get last origin from
Returns:
The last origin type, or None for unsupported types
Raises:
ValueError: If called in Python >= 3.7
"""
def get_parameters(tp):
"""
Return type parameters of a parameterizable type as a tuple.
Parameters are returned in lexicographic order. Parameterizable types
are generic types, unions, tuple types and callable types.
Args:
tp: Type to get parameters from
Returns:
tuple: Type parameters
"""
def get_args(tp, evaluate=None):
"""
Get type arguments with all substitutions performed.
For unions, basic simplifications used by Union constructor are performed.
In Python >= 3.7, evaluate parameter is ignored and always True.
Args:
tp: Type to get arguments from
evaluate (bool, optional): Whether to evaluate type parameters.
Only used in Python < 3.7
Returns:
tuple: Type arguments
Raises:
ValueError: If evaluate=False in Python >= 3.7
"""
def get_last_args(tp):
"""
Get last arguments of (multiply) subscripted type.
Only supported in Python 3.6. Parameters for Callable are flattened.
Args:
tp: Type to get last arguments from
Returns:
tuple: Last type arguments
Raises:
ValueError: If called in Python >= 3.7
"""
def get_bound(tp):
"""
Return the type bound to a TypeVar if any.
Args:
tp: TypeVar to get bound from
Returns:
The bound type, or None if no bound
Raises:
TypeError: If tp is not a TypeVar
"""
def get_constraints(tp):
"""
Returns the constraints of a TypeVar if any.
Args:
tp: TypeVar to get constraints from
Returns:
tuple: Constraint types, or empty tuple if no constraints
Raises:
TypeError: If tp is not a TypeVar
"""
def get_generic_type(obj):
"""
Get the generic type of an object if possible.
Otherwise returns runtime class.
Args:
obj: Object to get generic type from
Returns:
The generic type if available, otherwise type(obj)
"""
def get_generic_bases(tp):
"""
Get generic base types of a type or empty tuple if not possible.
Args:
tp: Type to get generic bases from
Returns:
tuple: Generic base types
"""
def typed_dict_keys(td):
"""
If td is a TypedDict class, return a dictionary mapping keys to types.
Args:
td: Type to check for TypedDict
Returns:
dict: Mapping of typed keys to types, or None if not a TypedDict
"""
def get_forward_arg(fr):
"""
If fr is a ForwardRef, return the string representation.
Args:
fr: Object to check for ForwardRef
Returns:
str: Forward reference string, or None if not a ForwardRef
"""Boolean constants indicating feature availability across different Python versions.
NEW_TYPING: bool
# True if Python version >= 3.7.0 (PEP 560)
WITH_FINAL: bool
# True if Final type is available
WITH_LITERAL: bool
# True if Literal type is available
WITH_CLASSVAR: bool
# True if ClassVar type is available
WITH_NEWTYPE: bool
# True if NewType is available
LEGACY_TYPING: bool
# True if using legacy typing module (very old versions)from typing import List, Dict, Generic, TypeVar
from typing_inspect import is_generic_type, get_origin, get_args
T = TypeVar('T')
K = TypeVar('K')
V = TypeVar('V')
class MyContainer(Generic[T]):
def __init__(self, item: T):
self.item = item
# Test generic types
assert is_generic_type(List[int])
assert is_generic_type(Dict[str, int])
assert is_generic_type(MyContainer[str])
# Extract type information
assert get_origin(List[int]) == list # Python 3.7+
assert get_args(Dict[str, int]) == (str, int)from typing import Union, Optional
from typing_inspect import is_union_type, is_optional_type, get_args
# Union types
StringOrInt = Union[str, int]
assert is_union_type(StringOrInt)
assert get_args(StringOrInt) == (str, int)
# Optional types (which are Union[T, None])
OptionalString = Optional[str]
assert is_optional_type(OptionalString)
assert is_union_type(OptionalString) # Optional is Union
assert get_args(OptionalString) == (str, type(None))from typing import TypeVar
from typing_inspect import is_typevar, get_bound, get_constraints
# Unconstrained TypeVar
T = TypeVar('T')
assert is_typevar(T)
assert get_bound(T) is None
assert get_constraints(T) == ()
# Bound TypeVar
NumberT = TypeVar('NumberT', bound=int)
assert is_typevar(NumberT)
assert get_bound(NumberT) == int
# Constrained TypeVar
StringOrBytesT = TypeVar('StringOrBytesT', str, bytes)
assert is_typevar(StringOrBytesT)
assert get_constraints(StringOrBytesT) == (str, bytes)from typing_inspect import NEW_TYPING, WITH_FINAL, WITH_LITERAL
if NEW_TYPING:
# Use Python 3.7+ typing features
from typing import get_origin as typing_get_origin
# typing_inspect.get_origin provides compatibility layer
if WITH_FINAL:
from typing_extensions import Final
from typing_inspect import is_final_type
FinalInt = Final[int]
assert is_final_type(FinalInt)
if WITH_LITERAL:
from typing_extensions import Literal
from typing_inspect import is_literal_type
Color = Literal['red', 'green', 'blue']
assert is_literal_type(Color)