Skip to content

inspection

High-performance, Fine-grained runtime type inspections.

Typical Usage

>>> from typelib.py import inspection
>>> inspection.ismappingtype(dict)
True
>>> inspection.isfixedtupletype(tuple[int, str])
True

args

args(annotation: Any, *, evaluate: bool = False) -> Tuple[Any, ...]

Get the args supplied to an annotation, normalizing typing.TypeVar.

Note

TypeVar normalization follows this strategy:

-> If the TypeVar is bound
-----> return the bound type
-> Else If the TypeVar has constraints
-----> return a Union of the constraints
-> Else
-----> return Any

Examples:

>>> from typelib.py import inspection
>>> from typing import Dict, TypeVar, Any
>>> T = TypeVar("T")
>>> args(Dict)
()
>>> args(Dict[str, int])
(<class 'str'>, <class 'int'>)
>>> args(Dict[str, T])
(<class 'str'>, typing.Any)
Source code in src/typelib/py/inspection.py
def args(annotation: tp.Any, *, evaluate: bool = False) -> tp.Tuple[tp.Any, ...]:
    """Get the args supplied to an annotation, normalizing [`typing.TypeVar`][].

    Note:
        TypeVar normalization follows this strategy:

            -> If the TypeVar is bound
            -----> return the bound type
            -> Else If the TypeVar has constraints
            -----> return a Union of the constraints
            -> Else
            -----> return Any

    Examples:
        >>> from typelib.py import inspection
        >>> from typing import Dict, TypeVar, Any
        >>> T = TypeVar("T")
        >>> args(Dict)
        ()
        >>> args(Dict[str, int])
        (<class 'str'>, <class 'int'>)
        >>> args(Dict[str, T])
        (<class 'str'>, typing.Any)
    """
    a = tp.get_args(annotation)
    if not a:
        a = getattr(annotation, "__args__", a)

    if evaluate:
        a = (*(refs.evaluate(r) for r in a),)

    return (*_normalize_typevars(*a),)

get_type_hints

get_type_hints(obj: Union[type, Callable], exhaustive: bool = True) -> dict[str, type[Any]]

Wrapper for typing.get_type_hints.

If typing.get_type_hints raises ([NameError][], [TypeError][]), we will default to an empty dict.

Parameters:

  • obj (Union[type, Callable]) –

    The object to inspect.

  • exhaustive (bool, default: True ) –

    Whether to pull type hints from the signature of the object if none can be found via typing.get_type_hints. (defaults True)

Source code in src/typelib/py/inspection.py
def get_type_hints(
    obj: tp.Union[type, tp.Callable], exhaustive: bool = True
) -> dict[str, type[tp.Any]]:
    """Wrapper for [`typing.get_type_hints`][].

    If [`typing.get_type_hints`][] raises `([NameError][], [TypeError][])`, we will
    default to an empty dict.

    Args:
        obj: The object to inspect.
        exhaustive:
            Whether to pull type hints from the signature of the object if
            none can be found via [`typing.get_type_hints`][]. (defaults True)
    """
    try:
        hints = tp.get_type_hints(obj)
    except (NameError, TypeError):
        hints = {}
    # KW_ONLY is a special sentinel to denote kw-only params in a dataclass.
    #  We don't want to do anything with this hint/field. It's not real.
    hints = {f: t for f, t in hints.items() if t is not compat.KW_ONLY}
    if not hints and exhaustive:
        hints = _hints_from_signature(obj)
    return hints

isabstract

isabstract(o) -> TypeIs[ABC]

Test whether the given object is an abstract type.

Examples:

>>> import abc
>>> import numbers
>>>
>>> isabstract(numbers.Number)
True
>>>
>>> class MyABC(abc.ABC): ...
...
>>> isabstract(MyABC)
True
Source code in src/typelib/py/inspection.py
def isabstract(o) -> compat.TypeIs[abc.ABC]:
    """Test whether the given object is an abstract type.

    Examples:
        >>> import abc
        >>> import numbers

        >>>
        >>> isabstract(numbers.Number)
        True
        >>>
        >>> class MyABC(abc.ABC): ...
        ...
        >>> isabstract(MyABC)
        True

    """
    return inspect.isabstract(o) or o in _ABCS

isbuiltininstance

isbuiltininstance(o: Any) -> TypeIs[BuiltIntypeT]

Test whether an object is an instance of a builtin type.

Examples:

>>> isbuiltininstance("")
True
Source code in src/typelib/py/inspection.py
def isbuiltininstance(o: tp.Any) -> compat.TypeIs[BuiltIntypeT]:
    """Test whether an object is an instance of a builtin type.

    Examples:
        >>> isbuiltininstance("")
        True
    """
    return builtins.isinstance(o, BUILTIN_TYPES_TUPLE)

isbuiltinsubtype

isbuiltinsubtype(t: type) -> TypeIs[type[BuiltIntypeT]]

Check whether the provided type is a subclass of a builtin-type.

Examples:

>>> from typing import NewType, Mapping
>>> class SuperStr(str): ...
...
>>> isbuiltinsubtype(SuperStr)
True
>>> isbuiltinsubtype(NewType("MyStr", SuperStr))
True
>>> class Foo: ...
...
>>> isbuiltintype(Foo)
False
>>> isbuiltintype(Mapping)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isbuiltinsubtype(t: type) -> compat.TypeIs[type[BuiltIntypeT]]:
    """Check whether the provided type is a subclass of a builtin-type.

    Examples:
        >>> from typing import NewType, Mapping
        >>> class SuperStr(str): ...
        ...
        >>> isbuiltinsubtype(SuperStr)
        True
        >>> isbuiltinsubtype(NewType("MyStr", SuperStr))
        True
        >>> class Foo: ...
        ...
        >>> isbuiltintype(Foo)
        False
        >>> isbuiltintype(Mapping)
        False
    """
    return issubclass(resolve_supertype(t), BUILTIN_TYPES_TUPLE)

isbuiltintype

isbuiltintype(obj: type | FunctionType) -> TypeIs[type[BuiltIntypeT]]

Check whether the provided object is a builtin-type.

Note

Python stdlib and Python documentation have no "definitive list" of builtin-types, despite the fact that they are well-known. The closest we have is https://docs.python.org/3.7/library/functions.html, which clumps the builtin-types with builtin-functions. Despite clumping these types with functions in the documentation, these types eval as False when compared to types.BuiltinFunctionType, which is meant to be an alias for the builtin-functions listed in the documentation.

All this to say, here we are with a custom check to determine whether a type is a builtin.

Examples:

>>> from typing import NewType, Mapping
>>> isbuiltintype(str)
True
>>> isbuiltintype(NewType("MyStr", str))
True
>>> class Foo: ...
...
>>> isbuiltintype(Foo)
False
>>> isbuiltintype(Mapping)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isbuiltintype(
    obj: type | types.FunctionType,
) -> compat.TypeIs[type[BuiltIntypeT]]:
    """Check whether the provided object is a builtin-type.

    Note:
        Python stdlib and Python documentation have no "definitive list" of
        builtin-**types**, despite the fact that they are well-known. The closest we have
        is https://docs.python.org/3.7/library/functions.html, which clumps the
        builtin-types with builtin-functions. Despite clumping these types with functions
        in the documentation, these types eval as False when compared to
        [`types.BuiltinFunctionType`][], which is meant to be an alias for the
        builtin-functions listed in the documentation.

        All this to say, here we are with a custom check to determine whether a type is a
        builtin.

    Examples:
        >>> from typing import NewType, Mapping
        >>> isbuiltintype(str)
        True
        >>> isbuiltintype(NewType("MyStr", str))
        True
        >>> class Foo: ...
        ...
        >>> isbuiltintype(Foo)
        False
        >>> isbuiltintype(Mapping)
        False
    """
    return (
        resolve_supertype(obj) in BUILTIN_TYPES
        or resolve_supertype(type(obj)) in BUILTIN_TYPES
    )

isbytestype

isbytestype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]

Test whether the given type is a subclass of text or bytes.

Examples:

>>> class MyStr(str): ...
...
>>> istexttype(MyStr)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isbytestype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:
    """Test whether the given type is a subclass of text or bytes.

    Examples:
        >>> class MyStr(str): ...
        ...
        >>> istexttype(MyStr)
        True
    """
    return _safe_issubclass(t, (bytes, bytearray, memoryview))

iscallable

iscallable(t: Any) -> TypeIs[Callable]

Test whether the given type is a callable.

Examples:

>>> import typing
>>> import collections.abc
>>> iscallable(lambda: None)
True
>>> iscallable(typing.Callable)
True
>>> iscallable(collections.abc.Callable)
True
>>> iscallable(1)
False
Source code in src/typelib/py/inspection.py
@compat.cache  # type: ignore[arg-type]
def iscallable(t: tp.Any) -> compat.TypeIs[tp.Callable]:
    """Test whether the given type is a callable.

    Examples:
        >>> import typing
        >>> import collections.abc
        >>> iscallable(lambda: None)
        True
        >>> iscallable(typing.Callable)
        True
        >>> iscallable(collections.abc.Callable)
        True
        >>> iscallable(1)
        False
    """
    return inspect.isroutine(t) or t is tp.Callable or _safe_issubclass(t, abc_Callable)  # type: ignore[arg-type]

isclassvartype

isclassvartype(obj: type) -> bool

Test whether an annotation is a ClassVar annotation.

Examples:

>>> from typing import ClassVar, NewType
>>> isclassvartype(ClassVar[str])
True
>>> isclassvartype(NewType("Foo", ClassVar[str]))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isclassvartype(obj: type) -> bool:
    """Test whether an annotation is a ClassVar annotation.

    Examples:
        >>> from typing import ClassVar, NewType
        >>> isclassvartype(ClassVar[str])
        True
        >>> isclassvartype(NewType("Foo", ClassVar[str]))
        True
    """
    obj = resolve_supertype(obj)
    return getattr(obj, "__origin__", obj) is tp.ClassVar

iscollectiontype

iscollectiontype(obj: type) -> TypeIs[type[Collection]]

Test whether this annotation is a subclass of typing.Collection.

Includes builtins.

Examples:

>>> from typing import Collection, Mapping, NewType
>>> iscollectiontype(Collection)
True
>>> iscollectiontype(Mapping[str, str])
True
>>> iscollectiontype(str)
True
>>> iscollectiontype(list)
True
>>> iscollectiontype(NewType("Foo", dict))
True
>>> iscollectiontype(int)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def iscollectiontype(obj: type) -> compat.TypeIs[type[tp.Collection]]:
    """Test whether this annotation is a subclass of [`typing.Collection`][].

    Includes builtins.

    Examples:
        >>> from typing import Collection, Mapping, NewType
        >>> iscollectiontype(Collection)
        True
        >>> iscollectiontype(Mapping[str, str])
        True
        >>> iscollectiontype(str)
        True
        >>> iscollectiontype(list)
        True
        >>> iscollectiontype(NewType("Foo", dict))
        True
        >>> iscollectiontype(int)
        False
    """
    obj = origin(obj)
    return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Collection)

isdatetimetype

isdatetimetype(obj: type) -> TypeIs[type[Union[datetime, date]]]

Test whether this annotation is a a date/datetime object.

Examples:

>>> import datetime
>>> from typing import NewType
>>> isdatetype(datetime.datetime)
True
>>> isdatetype(datetime.date)
True
>>> isdatetype(NewType("Foo", datetime.datetime))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isdatetimetype(
    obj: type,
) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:
    """Test whether this annotation is a a date/datetime object.

    Examples:
        >>> import datetime
        >>> from typing import NewType
        >>> isdatetype(datetime.datetime)
        True
        >>> isdatetype(datetime.date)
        True
        >>> isdatetype(NewType("Foo", datetime.datetime))
        True
    """
    return builtins.issubclass(origin(obj), datetime.datetime)

isdatetype

isdatetype(obj: type) -> TypeIs[type[Union[datetime, date]]]

Test whether this annotation is a a date/datetime object.

Examples:

>>> import datetime
>>> from typing import NewType
>>> isdatetype(datetime.datetime)
True
>>> isdatetype(datetime.date)
True
>>> isdatetype(NewType("Foo", datetime.datetime))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isdatetype(
    obj: type,
) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:
    """Test whether this annotation is a a date/datetime object.

    Examples:
        >>> import datetime
        >>> from typing import NewType
        >>> isdatetype(datetime.datetime)
        True
        >>> isdatetype(datetime.date)
        True
        >>> isdatetype(NewType("Foo", datetime.datetime))
        True
    """
    return builtins.issubclass(origin(obj), datetime.date)

isdecimaltype

isdecimaltype(obj: type) -> TypeIs[type[Decimal]]

Test whether this annotation is a Decimal object.

Examples:

>>> import decimal
>>> from typing import NewType
>>> isdecimaltype(decimal.Decimal)
True
>>> isdecimaltype(NewType("Foo", decimal.Decimal))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isdecimaltype(obj: type) -> compat.TypeIs[type[decimal.Decimal]]:
    """Test whether this annotation is a Decimal object.

    Examples:
        >>> import decimal
        >>> from typing import NewType
        >>> isdecimaltype(decimal.Decimal)
        True
        >>> isdecimaltype(NewType("Foo", decimal.Decimal))
        True
    """
    return builtins.issubclass(origin(obj), decimal.Decimal)

isdescriptor

isdescriptor(obj) -> TypeIs[DescriptorT]

Test whether the given object is a types.GetSetDescriptorType

Examples:

>>> class StringDescriptor:
...     __slots__ = ("value",)
...
...     def __init__(self, default: str = "value"):
...         self.value = default
...
...     def __get__(self, instance: Any, value: str) -> str:
...         return self.value
...
>>> isdescriptor(StringDescriptor)
True
Source code in src/typelib/py/inspection.py
def isdescriptor(obj) -> compat.TypeIs[DescriptorT]:
    """Test whether the given object is a [`types.GetSetDescriptorType`][]

    Examples:
        >>> class StringDescriptor:
        ...     __slots__ = ("value",)
        ...
        ...     def __init__(self, default: str = "value"):
        ...         self.value = default
        ...
        ...     def __get__(self, instance: Any, value: str) -> str:
        ...         return self.value
        ...
        >>> isdescriptor(StringDescriptor)
        True
    """
    intersection = {*dir(obj)} & _DESCRIPTOR_METHODS
    return bool(intersection)

isenumtype

isenumtype(obj: type) -> TypeIs[type[Enum]]

Test whether this annotation is a subclass of enum.Enum

Examples:

>>> import enum
>>>
>>> class FooNum(enum.Enum): ...
...
>>> isenumtype(FooNum)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isenumtype(obj: type) -> compat.TypeIs[type[enum.Enum]]:
    """Test whether this annotation is a subclass of [`enum.Enum`][]

    Examples:
        >>> import enum
        >>>
        >>> class FooNum(enum.Enum): ...
        ...
        >>> isenumtype(FooNum)
        True
    """
    return _safe_issubclass(obj, enum.Enum)

isfinal

isfinal(obj: type) -> bool

Test whether an annotation is typing.Final.

Examples:

>>> from typing import NewType
>>> from typelib.py.compat import Final
>>> isfinal(Final[str])
True
>>> isfinal(NewType("Foo", Final[str]))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isfinal(obj: type) -> bool:
    """Test whether an annotation is [`typing.Final`][].

    Examples:
        >>> from typing import NewType
        >>> from typelib.py.compat import Final
        >>> isfinal(Final[str])
        True
        >>> isfinal(NewType("Foo", Final[str]))
        True
    """
    return origin(obj) is compat.Final

isfixedtupletype

isfixedtupletype(obj: type) -> TypeIs[type[tuple]]

Check whether an object is a "fixed" tuple, e.g., tuple[int, int].

Examples:

>>> from typing import Tuple
>>>
>>>
>>> isfixedtupletype(Tuple[str, int])
True
>>> isfixedtupletype(Tuple[str, ...])
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isfixedtupletype(obj: type) -> compat.TypeIs[type[tuple]]:
    """Check whether an object is a "fixed" tuple, e.g., `tuple[int, int]`.

    Examples:
        >>> from typing import Tuple
        >>>
        >>>
        >>> isfixedtupletype(Tuple[str, int])
        True
        >>> isfixedtupletype(Tuple[str, ...])
        False
    """
    a = args(obj)
    origin = tp.get_origin(obj)
    if not a or a[-1] is ...:
        return False
    return _safe_issubclass(origin, tuple)

isfloattype

isfloattype(t: type[Any]) -> TypeIs[type[float]]

Test whether t is a subclass of the numbers.Number protocol.

Examples:

>>> import decimal
>>> isnumbertype(int)
False
>>> isnumbertype(float)
True
>>> isnumbertype(decimal.Decimal)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isfloattype(t: type[tp.Any]) -> compat.TypeIs[type[float]]:
    """Test whether `t` is a subclass of the [`numbers.Number`][] protocol.

    Examples:
        >>> import decimal

        >>> isnumbertype(int)
        False
        >>> isnumbertype(float)
        True
        >>> isnumbertype(decimal.Decimal)
        False
    """
    return _safe_issubclass(t, float)

isforwardref

isforwardref(obj: Any) -> TypeIs[ForwardRef]

Tests whether the given object is a typing.ForwardRef.

Source code in src/typelib/py/inspection.py
def isforwardref(obj: tp.Any) -> compat.TypeIs[refs.ForwardRef]:
    """Tests whether the given object is a [`typing.ForwardRef`][]."""
    return obj.__class__ is refs.ForwardRef

isfractiontype

isfractiontype(obj: type) -> TypeIs[type[Fraction]]

Test whether this annotation is a Decimal object.

Examples:

>>> import fractions
>>> from typing import NewType
>>> isdecimaltype(fractions.Fraction)
True
>>> isdecimaltype(NewType("Foo", fractions.Fraction))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isfractiontype(obj: type) -> compat.TypeIs[type[fractions.Fraction]]:
    """Test whether this annotation is a Decimal object.

    Examples:
        >>> import fractions
        >>> from typing import NewType
        >>> isdecimaltype(fractions.Fraction)
        True
        >>> isdecimaltype(NewType("Foo", fractions.Fraction))
        True
    """
    return builtins.issubclass(origin(obj), fractions.Fraction)

isfromdictclass

isfromdictclass(obj: type) -> TypeIs[type[_FromDict]]

Test whether this annotation is a class with a from_dict() method.

Source code in src/typelib/py/inspection.py
@compat.cache
def isfromdictclass(obj: type) -> compat.TypeIs[type[_FromDict]]:
    """Test whether this annotation is a class with a `from_dict()` method."""
    return inspect.isclass(obj) and hasattr(obj, "from_dict")

isfrozendataclass

isfrozendataclass(obj: type) -> TypeIs[type[_FrozenDataclass]]

Test whether this is a dataclass and whether it's frozen.

Source code in src/typelib/py/inspection.py
@compat.cache
def isfrozendataclass(obj: type) -> compat.TypeIs[type[_FrozenDataclass]]:
    """Test whether this is a dataclass and whether it's frozen."""
    return getattr(getattr(obj, "__dataclass_params__", None), "frozen", False)

isgeneric

isgeneric(t: Any) -> bool

Test whether the given type is a typing generic.

Examples:

>>> from typing import Tuple, Generic, TypeVar
>>>
>>> isgeneric(Tuple)
True
>>> isgeneric(tuple)
False
>>> T = TypeVar("T")
>>> class MyGeneric(Generic[T]): ...
>>> isgeneric(MyGeneric[int])
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isgeneric(t: tp.Any) -> bool:
    """Test whether the given type is a typing generic.

    Examples:
        >>> from typing import Tuple, Generic, TypeVar

        >>>
        >>> isgeneric(Tuple)
        True
        >>> isgeneric(tuple)
        False
        >>> T = TypeVar("T")
        >>> class MyGeneric(Generic[T]): ...
        >>> isgeneric(MyGeneric[int])
        True
    """
    strobj = str(t)
    is_generic = (
        strobj.startswith("typing.")
        or strobj.startswith("typing_extensions.")
        or "[" in strobj
        or _safe_issubclass(t, tp.Generic)  # type: ignore[arg-type]
    )
    return is_generic

ishashable

ishashable(obj: Any) -> TypeIs[Hashable]

Check whether an object is hashable.

An order of magnitude faster than isinstance with typing.Hashable

Examples:

>>> ishashable(str())
True
>>> ishashable(frozenset())
True
>>> ishashable(list())
False
Source code in src/typelib/py/inspection.py
def ishashable(obj: tp.Any) -> compat.TypeIs[tp.Hashable]:
    """Check whether an object is hashable.

    An order of magnitude faster than [`isinstance`][] with
    [`typing.Hashable`][]

    Examples:
        >>> ishashable(str())
        True
        >>> ishashable(frozenset())
        True
        >>> ishashable(list())
        False
    """
    return __hashgetter(obj) is not None

isintegertype

isintegertype(t: type[Any]) -> TypeIs[type[int]]

Test whether t is a subclass of the numbers.Number protocol.

Examples:

>>> import decimal
>>> isnumbertype(int)
True
>>> isnumbertype(float)
False
>>> isnumbertype(decimal.Decimal)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isintegertype(t: type[tp.Any]) -> compat.TypeIs[type[int]]:
    """Test whether `t` is a subclass of the [`numbers.Number`][] protocol.

    Examples:
        >>> import decimal

        >>> isnumbertype(int)
        True
        >>> isnumbertype(float)
        False
        >>> isnumbertype(decimal.Decimal)
        False
    """
    return _safe_issubclass(t, int)

isiterabletype

isiterabletype(obj: type) -> TypeIs[type[Iterable]]

Test whether the given type is iterable.

Examples:

>>> from typing import Sequence, Collection
>>> isiterabletype(Sequence[str])
True
>>> isiterabletype(Collection)
True
>>> isiterabletype(str)
True
>>> isiterabletype(tuple)
True
>>> isiterabletype(int)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isiterabletype(obj: type) -> compat.TypeIs[type[tp.Iterable]]:
    """Test whether the given type is iterable.

    Examples:
        >>> from typing import Sequence, Collection
        >>> isiterabletype(Sequence[str])
        True
        >>> isiterabletype(Collection)
        True
        >>> isiterabletype(str)
        True
        >>> isiterabletype(tuple)
        True
        >>> isiterabletype(int)
        False
    """
    obj = origin(obj)
    return builtins.issubclass(obj, tp.Iterable)

isiteratortype

isiteratortype(obj: type) -> TypeIs[type[Iterator]]

Check whether the given object is a subclass of an Iterator.

Examples:

>>> def mygen(): yield 1
...
>>> isiteratortype(mygen().__class__)
True
>>> isiteratortype(iter([]).__class__)
True
>>> isiteratortype(mygen)
False
>>> isiteratortype(list)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isiteratortype(obj: type) -> compat.TypeIs[type[tp.Iterator]]:
    """Check whether the given object is a subclass of an Iterator.

    Examples:
        >>> def mygen(): yield 1
        ...
        >>> isiteratortype(mygen().__class__)
        True
        >>> isiteratortype(iter([]).__class__)
        True
        >>> isiteratortype(mygen)
        False
        >>> isiteratortype(list)
        False
    """
    obj = origin(obj)
    return builtins.issubclass(obj, tp.Iterator)

isliteral

isliteral(obj) -> bool

Test whether an annotation is typing.Literal.

Examples:

>>>
Source code in src/typelib/py/inspection.py
@compat.cache
def isliteral(obj) -> bool:
    """Test whether an annotation is [`typing.Literal`][].

    Examples:
        >>>
    """
    return origin(obj) is tp.Literal or (
        obj.__class__ is refs.ForwardRef and obj.__forward_arg__.startswith("Literal")
    )

ismappingtype

ismappingtype(obj: type) -> TypeIs[type[Mapping]]

Test whether this annotation is a subtype of typing.Mapping.

Examples:

>>> from typing import Mapping, Dict, DefaultDict, NewType
>>> ismappingtype(Mapping)
True
>>> ismappingtype(Dict[str, str])
True
>>> ismappingtype(DefaultDict)
True
>>> ismappingtype(dict)
True
>>> class MyDict(dict): ...
...
>>> ismappingtype(MyDict)
True
>>> class MyMapping(Mapping): ...
...
>>> ismappingtype(MyMapping)
True
>>> ismappingtype(NewType("Foo", dict))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def ismappingtype(obj: type) -> compat.TypeIs[type[tp.Mapping]]:
    """Test whether this annotation is a subtype of [`typing.Mapping`][].

    Examples:
        >>> from typing import Mapping, Dict, DefaultDict, NewType
        >>> ismappingtype(Mapping)
        True
        >>> ismappingtype(Dict[str, str])
        True
        >>> ismappingtype(DefaultDict)
        True
        >>> ismappingtype(dict)
        True
        >>> class MyDict(dict): ...
        ...
        >>> ismappingtype(MyDict)
        True
        >>> class MyMapping(Mapping): ...
        ...
        >>> ismappingtype(MyMapping)
        True
        >>> ismappingtype(NewType("Foo", dict))
        True
    """
    obj = origin(obj)
    return builtins.issubclass(obj, _MAPPING_TYPES) or builtins.issubclass(
        obj, tp.Mapping
    )

isnamedtuple

isnamedtuple(obj: type) -> TypeIs[type[NamedTuple]]

Check whether an object is a "named" tuple (collections.namedtuple).

Examples:

>>> from collections import namedtuple
>>>
>>> FooTup = namedtuple("FooTup", ["bar"])
>>> isnamedtuple(FooTup)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isnamedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:
    """Check whether an object is a "named" tuple ([`collections.namedtuple`][]).

    Examples:
        >>> from collections import namedtuple
        >>>
        >>> FooTup = namedtuple("FooTup", ["bar"])
        >>> isnamedtuple(FooTup)
        True
    """
    return inspect.isclass(obj) and issubclass(obj, tuple) and hasattr(obj, "_fields")

isnonetype

isnonetype(t: Any) -> TypeIs[None]

Detect if the given type is a types.NoneType.

Examples:

>>> isnonetype(None)
True
>>> isnonetype(type(None))
True
>>> isnonetype(1)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isnonetype(t: tp.Any) -> compat.TypeIs[None]:
    """Detect if the given type is a [`types.NoneType`][].

    Examples:
        >>> isnonetype(None)
        True
        >>> isnonetype(type(None))
        True
        >>> isnonetype(1)
        False
    """
    return t in (None, type(None))

isnumbertype

isnumbertype(t: type[Any]) -> TypeIs[type[Number]]

Test whether t is a subclass of the numbers.Number protocol.

Examples:

>>> import decimal
>>> isnumbertype(int)
True
>>> isnumbertype(float)
True
>>> isnumbertype(decimal.Decimal)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isnumbertype(t: type[tp.Any]) -> compat.TypeIs[type[numbers.Number]]:
    """Test whether `t` is a subclass of the [`numbers.Number`][] protocol.

    Examples:
        >>> import decimal

        >>> isnumbertype(int)
        True
        >>> isnumbertype(float)
        True
        >>> isnumbertype(decimal.Decimal)
        True
    """
    return _safe_issubclass(t, numbers.Number)

isoptionaltype

isoptionaltype(obj: type[_OT]) -> TypeIs[type[Optional[_OT]]]

Test whether an annotation is typing.Optional, or can be treated as.

typing.Optional is an alias for typing.Union[<T>, None], so both are "optional".

Examples:

>>> from typing import Optional, Union, Dict, Literal
>>> isoptionaltype(Optional[str])
True
>>> isoptionaltype(Union[str, None])
True
>>> isoptionaltype(Literal["", None])
True
>>> isoptionaltype(Dict[str, None])

False

Source code in src/typelib/py/inspection.py
@compat.cache
def isoptionaltype(obj: type[_OT]) -> compat.TypeIs[type[tp.Optional[_OT]]]:
    """Test whether an annotation is [`typing.Optional`][], or can be treated as.

    [`typing.Optional`][] is an alias for `typing.Union[<T>, None]`, so both are
    "optional".

    Examples:
        >>> from typing import Optional, Union, Dict, Literal
        >>> isoptionaltype(Optional[str])
        True
        >>> isoptionaltype(Union[str, None])
        True
        >>> isoptionaltype(Literal["", None])
        True
        >>> isoptionaltype(Dict[str, None])
    False
    """
    args = getattr(obj, "__args__", ())
    tname = name(origin(obj))
    nullarg = next((a for a in args if a in (type(None), None)), ...)
    isoptional = tname == "Optional" or (
        nullarg is not ... and tname in ("Union", "UnionType", "Literal")
    )
    return isoptional

ispathtype

ispathtype(t: Any) -> TypeIs[Path]

Detect if the given type is a pathlib.Path.

Examples:

>>> import pathlib
>>> ispathtype(pathlib.Path.cwd())
True
>>> ispathtype(".")
False
Source code in src/typelib/py/inspection.py
@compat.cache
def ispathtype(t: tp.Any) -> compat.TypeIs[pathlib.Path]:
    """Detect if the given type is a [`pathlib.Path`][].

    Examples:
        >>> import pathlib
        >>> ispathtype(pathlib.Path.cwd())
        True
        >>> ispathtype(".")
        False
    """
    return _safe_issubclass(t, pathlib.PurePath)

ispatterntype

ispatterntype(t: Any) -> TypeIs[Pattern]

Detect if the given type is a re.Pattern.

Examples:

>>> import re
>>> ispatterntype(re.compile(r"^[a-z]+$"))
True
>>> ispatterntype(r"^[a-z]+$")
False
Source code in src/typelib/py/inspection.py
@compat.cache
def ispatterntype(t: tp.Any) -> compat.TypeIs[re.Pattern]:
    """Detect if the given type is a [`re.Pattern`][].

    Examples:
        >>> import re
        >>> ispatterntype(re.compile(r"^[a-z]+$"))
        True
        >>> ispatterntype(r"^[a-z]+$")
        False
    """
    return _safe_issubclass(t, re.Pattern)

isproperty

isproperty(obj) -> TypeIs[DynamicClassAttribute]

Test whether the given object is an instance of [property] or functools.cached_property.

Examples:

>>> import functools
>>> class Foo:
...     @property
...     def prop(self) -> int:
...         return 1
...
...     @functools.cached_property
...     def cached(self) -> str:
...         return "foo"
...
>>> isproperty(Foo.prop)
True
>>> isproperty(Foo.cached)
True
Source code in src/typelib/py/inspection.py
def isproperty(obj) -> compat.TypeIs[types.DynamicClassAttribute]:
    """Test whether the given object is an instance of [`property`] or [`functools.cached_property`][].

    Examples:
        >>> import functools

        >>> class Foo:
        ...     @property
        ...     def prop(self) -> int:
        ...         return 1
        ...
        ...     @functools.cached_property
        ...     def cached(self) -> str:
        ...         return "foo"
        ...
        >>> isproperty(Foo.prop)
        True
        >>> isproperty(Foo.cached)
        True
    """

    return builtins.issubclass(obj.__class__, (property, functools.cached_property))

issequencetype

issequencetype(obj: type) -> TypeIs[type[Collection]]

Test whether this annotation is a subclass of typing.Collection.

Includes builtins.

Examples:

>>> from typing import Collection, Mapping, NewType, Sequence
>>> issequencetype(Sequence)
True
>>> issequencetype(Mapping[str, str])
True
>>> issequencetype(str)
True
>>> issequencetype(list)
True
>>> issequencetype(NewType("Foo", dict))
True
>>> issequencetype(int)
False
Source code in src/typelib/py/inspection.py
@compat.cache
def issequencetype(obj: type) -> compat.TypeIs[type[tp.Collection]]:
    """Test whether this annotation is a subclass of [`typing.Collection`][].

    Includes builtins.

    Examples:
        >>> from typing import Collection, Mapping, NewType, Sequence
        >>> issequencetype(Sequence)
        True
        >>> issequencetype(Mapping[str, str])
        True
        >>> issequencetype(str)
        True
        >>> issequencetype(list)
        True
        >>> issequencetype(NewType("Foo", dict))
        True
        >>> issequencetype(int)
        False
    """
    obj = origin(obj)
    return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Sequence)

issimpleattribute

issimpleattribute(v) -> bool

Test whether the given object is a static value

(e.g., not a function, class, or descriptor).

Examples:

>>> class MyOperator:
...     type = str
...
...     def operate(self, v) -> type:
...         return self.type(v)
...
...     @property
...     def default(self) -> type:
...         return self.type()
...
>>> issimpleattribute(MyOperator.type)
False
>>> issimpleattribute(MyOperator.operate)
False
>>> issimpleattribute(MyOperator.default)
False
Source code in src/typelib/py/inspection.py
def issimpleattribute(v) -> bool:
    """Test whether the given object is a static value

    (e.g., not a function, class, or descriptor).

    Examples:
        >>> class MyOperator:
        ...     type = str
        ...
        ...     def operate(self, v) -> type:
        ...         return self.type(v)
        ...
        ...     @property
        ...     def default(self) -> type:
        ...         return self.type()
        ...
        >>> issimpleattribute(MyOperator.type)
        False
        >>> issimpleattribute(MyOperator.operate)
        False
        >>> issimpleattribute(MyOperator.default)
        False
    """
    return not any(c(v) for c in _ATTR_CHECKS)

isstdlibinstance

isstdlibinstance(o: Any) -> TypeIs[STDLibtypeT]

Test whether an object is an instance of a type in the standard-lib.

Source code in src/typelib/py/inspection.py
def isstdlibinstance(o: tp.Any) -> compat.TypeIs[STDLibtypeT]:
    """Test whether an object is an instance of a type in the standard-lib."""
    return builtins.isinstance(o, STDLIB_TYPES_TUPLE)

isstdlibsubtype

isstdlibsubtype(t: type) -> TypeIs[type[STDLibtypeT]]

Test whether the given type is a subclass of a standard-lib type.

Examples:

>>> import datetime
>>> class MyDate(datetime.date): ...
...
>>> isstdlibsubtype(MyDate)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isstdlibsubtype(t: type) -> compat.TypeIs[type[STDLibtypeT]]:
    """Test whether the given type is a subclass of a standard-lib type.

    Examples:
        >>> import datetime

        >>> class MyDate(datetime.date): ...
        ...
        >>> isstdlibsubtype(MyDate)
        True
    """
    return _safe_issubclass(resolve_supertype(t), STDLIB_TYPES_TUPLE)

isstringtype

isstringtype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]

Test whether the given type is a subclass of text or bytes.

Examples:

>>> class MyStr(str): ...
...
>>> istexttype(MyStr)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isstringtype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:
    """Test whether the given type is a subclass of text or bytes.

    Examples:
        >>> class MyStr(str): ...
        ...
        >>> istexttype(MyStr)
        True
    """
    return _safe_issubclass(t, str)

isstructuredtype

isstructuredtype(t: type[Any]) -> bool

Test whether the given type has a fixed set of fields.

Examples:

>>> import dataclasses
>>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection
>>>
>>> isstructuredtype(Tuple[str, int])
True
>>> isstructuredtype(class MyDict(TypedDict): ...)
True
>>> isstructuredtype(class MyTup(NamedTuple): ...)
True
>>> isstructuredtype(class MyClass: ...)
True
>>> isstructuredtype(Union[str, int])
False
>>> isstructuredtype(Literal[1, 2])
False
>>> isstructuredtype(tuple)
False
>>> isstructuredtype(Collection[str])
False
Source code in src/typelib/py/inspection.py
@compat.cache
def isstructuredtype(t: type[tp.Any]) -> bool:
    """Test whether the given type has a fixed set of fields.

    Examples:
        >>> import dataclasses
        >>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection

        >>>
        >>> isstructuredtype(Tuple[str, int])
        True
        >>> isstructuredtype(class MyDict(TypedDict): ...)
        True
        >>> isstructuredtype(class MyTup(NamedTuple): ...)
        True
        >>> isstructuredtype(class MyClass: ...)
        True
        >>> isstructuredtype(Union[str, int])
        False
        >>> isstructuredtype(Literal[1, 2])
        False
        >>> isstructuredtype(tuple)
        False
        >>> isstructuredtype(Collection[str])
        False
    """
    return (
        isfixedtupletype(t)
        or isnamedtuple(t)
        or istypeddict(t)
        or (not isstdlibsubtype(origin(t)) and not isuniontype(t) and not isliteral(t))
    )

issubscriptedcollectiontype

issubscriptedcollectiontype(obj: type[Generic[_ArgsT]]) -> TypeIs[type[Collection[_ArgsT]]]

Test whether this annotation is a collection type and is subscripted.

Examples:

>>> from typing import Collection, Mapping, NewType
>>> issubscriptedcollectiontype(Collection)
False
>>> issubscriptedcollectiontype(Mapping[str, str])
True
>>> issubscriptedcollectiontype(str)
False
>>> issubscriptedcollectiontype(NewType("Foo", Collection[int]))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def issubscriptedcollectiontype(
    obj: type[tp.Generic[_ArgsT]],  # type: ignore[valid-type]
) -> compat.TypeIs[type[tp.Collection[_ArgsT]]]:
    """Test whether this annotation is a collection type and is subscripted.

    Examples:
        >>> from typing import Collection, Mapping, NewType
        >>> issubscriptedcollectiontype(Collection)
        False
        >>> issubscriptedcollectiontype(Mapping[str, str])
        True
        >>> issubscriptedcollectiontype(str)
        False
        >>> issubscriptedcollectiontype(NewType("Foo", Collection[int]))
        True
    """
    return iscollectiontype(obj) and issubscriptedgeneric(obj)

issubscriptedgeneric

issubscriptedgeneric(t: Any) -> bool

Test whether the given type is a typing generic.

Examples:

>>> from typing import Tuple, Generic, TypeVar
>>>
>>> isgeneric(Tuple)
True
>>> isgeneric(tuple)
False
>>> T = TypeVar("T")
>>> class MyGeneric(Generic[T]): ...
>>> isgeneric(MyGeneric[int])
True
Source code in src/typelib/py/inspection.py
@compat.cache
def issubscriptedgeneric(t: tp.Any) -> bool:
    """Test whether the given type is a typing generic.

    Examples:
        >>> from typing import Tuple, Generic, TypeVar

        >>>
        >>> isgeneric(Tuple)
        True
        >>> isgeneric(tuple)
        False
        >>> T = TypeVar("T")
        >>> class MyGeneric(Generic[T]): ...
        >>> isgeneric(MyGeneric[int])
        True
    """
    strobj = str(t)
    og = tp.get_origin(t) or t
    is_generic = isgeneric(og) or isgeneric(t)
    is_subscripted = "[" in strobj
    return is_generic and is_subscripted

istexttype

istexttype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]

Test whether the given type is a subclass of text or bytes.

Examples:

>>> class MyStr(str): ...
...
>>> istexttype(MyStr)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istexttype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:
    """Test whether the given type is a subclass of text or bytes.

    Examples:
        >>> class MyStr(str): ...
        ...
        >>> istexttype(MyStr)
        True
    """
    return _safe_issubclass(t, (str, bytes, bytearray, memoryview))

istimedeltatype

istimedeltatype(obj: type) -> TypeIs[type[timedelta]]

Test whether this annotation is a a date/datetime object.

Examples:

>>> import datetime
>>> from typing import NewType
>>> istimedeltatype(datetime.timedelta)
True
>>> istimedeltatype(NewType("Foo", datetime.timedelta))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istimedeltatype(obj: type) -> compat.TypeIs[type[datetime.timedelta]]:
    """Test whether this annotation is a a date/datetime object.

    Examples:
        >>> import datetime
        >>> from typing import NewType
        >>> istimedeltatype(datetime.timedelta)
        True
        >>> istimedeltatype(NewType("Foo", datetime.timedelta))
        True
    """
    return builtins.issubclass(origin(obj), datetime.timedelta)

istimetype

istimetype(obj: type) -> TypeIs[type[time]]

Test whether this annotation is a a date/datetime object.

Examples:

>>> import datetime
>>> from typing import NewType
>>> istimetype(datetime.time)
True
>>> istimetype(NewType("Foo", datetime.time))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istimetype(obj: type) -> compat.TypeIs[type[datetime.time]]:
    """Test whether this annotation is a a date/datetime object.

    Examples:
        >>> import datetime
        >>> from typing import NewType
        >>> istimetype(datetime.time)
        True
        >>> istimetype(NewType("Foo", datetime.time))
        True
    """
    return builtins.issubclass(origin(obj), datetime.time)

istupletype

istupletype(obj: Callable[..., Any] | type[Any]) -> TypeIs[type[tuple]]

Tests whether the given type is a subclass of tuple.

Examples:

>>> from typing import NamedTuple, Tuple
>>> class MyTup(NamedTuple):
...     field: int
...
>>> istupletype(tuple)
True
>>> istupletype(Tuple[str])
True
>>> istupletype(MyTup)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istupletype(
    obj: tp.Callable[..., tp.Any] | type[tp.Any],
) -> compat.TypeIs[type[tuple]]:
    """Tests whether the given type is a subclass of [`tuple`][].

    Examples:
        >>> from typing import NamedTuple, Tuple
        >>> class MyTup(NamedTuple):
        ...     field: int
        ...
        >>> istupletype(tuple)
        True
        >>> istupletype(Tuple[str])
        True
        >>> istupletype(MyTup)
        True
    """
    obj = origin(obj)
    return obj is tuple or issubclass(obj, tuple)  # type: ignore[arg-type]

istypealiastype

istypealiastype(t: Any) -> TypeIs[TypeAliasType]

Detect if the given object is a typing.TypeAliasType.

Examples:

>>> type IntList = list[int]
>>> istypealiastype(IntList)
True
>>> IntList = compat.TypeAliasType("IntList", list[int])
>>> istypealiastype(IntList)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istypealiastype(t: tp.Any) -> compat.TypeIs[compat.TypeAliasType]:
    """Detect if the given object is a [`typing.TypeAliasType`][].

    Examples:
        >>> type IntList = list[int]
        >>> istypealiastype(IntList)
        True
        >>> IntList = compat.TypeAliasType("IntList", list[int])
        >>> istypealiastype(IntList)
        True

    """
    return isinstance(t, compat.TypeAliasType)

istypeddict

istypeddict(obj: Any) -> bool

Check whether an object is a typing.TypedDict.

Examples:

>>> from typing import TypedDict
>>>
>>> class FooMap(TypedDict):
...     bar: str
...
>>> istypeddict(FooMap)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istypeddict(obj: tp.Any) -> bool:
    """Check whether an object is a [`typing.TypedDict`][].

    Examples:
        >>> from typing import TypedDict
        >>>
        >>> class FooMap(TypedDict):
        ...     bar: str
        ...
        >>> istypeddict(FooMap)
        True
    """
    return (
        inspect.isclass(obj)
        and dict in {*inspect.getmro(obj)}
        and hasattr(obj, "__total__")
    )

istypedtuple

istypedtuple(obj: type) -> TypeIs[type[NamedTuple]]

Check whether an object is a "typed" tuple (typing.NamedTuple).

Examples:

>>> from typing import NamedTuple
>>>
>>> class FooTup(NamedTuple):
...     bar: str
...
>>> istypedtuple(FooTup)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def istypedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:
    """Check whether an object is a "typed" tuple ([`typing.NamedTuple`][]).

    Examples:
        >>> from typing import NamedTuple
        >>>
        >>> class FooTup(NamedTuple):
        ...     bar: str
        ...
        >>> istypedtuple(FooTup)
        True
    """
    return (
        inspect.isclass(obj)
        and issubclass(obj, tuple)
        and bool(getattr(obj, "__annotations__", False))
    )

isunresolvable

isunresolvable(t: Any) -> bool

Test whether the given type is unresolvable.

Examples:

>>> import typing
>>> isunresolvable(int)
False
>>> isunresolvable(typ.Any)
True
>>> isunresolvable(...)
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isunresolvable(t: tp.Any) -> bool:
    """Test whether the given type is unresolvable.

    Examples:
        >>> import typing
        >>> isunresolvable(int)
        False
        >>> isunresolvable(typ.Any)
        True
        >>> isunresolvable(...)
        True
    """
    return t in _UNRESOLVABLE

isuuidtype

isuuidtype(obj: type) -> TypeIs[type[UUID]]

Test whether this annotation is a a date/datetime object.

Examples:

>>> import uuid
>>> from typing import NewType
>>> isuuidtype(uuid.UUID)
True
>>> class MyUUID(uuid.UUID): ...
...
>>> isuuidtype(MyUUID)
True
>>> isuuidtype(NewType("Foo", uuid.UUID))
True
Source code in src/typelib/py/inspection.py
@compat.cache
def isuuidtype(obj: type) -> compat.TypeIs[type[uuid.UUID]]:
    """Test whether this annotation is a a date/datetime object.

    Examples:
        >>> import uuid
        >>> from typing import NewType
        >>> isuuidtype(uuid.UUID)
        True
        >>> class MyUUID(uuid.UUID): ...
        ...
        >>> isuuidtype(MyUUID)
        True
        >>> isuuidtype(NewType("Foo", uuid.UUID))
        True
    """
    return builtins.issubclass(origin(obj), uuid.UUID)

name

name(obj: Union[type, ForwardRef, Callable]) -> str

Safely retrieve the name of either a standard object or a type annotation.

Examples:

>>> from typelib.py import inspection
>>> from typing import Dict, Any, TypeVar
>>> T = TypeVar("T")
>>> name(Dict)
'Dict'
>>> name(Dict[str, str])
'Dict'
>>> name(Any)
'Any'
>>> name(dict)
'dict'
Source code in src/typelib/py/inspection.py
@compat.cache
def name(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:
    """Safely retrieve the name of either a standard object or a type annotation.

    Examples:
        >>> from typelib.py import inspection
        >>> from typing import Dict, Any, TypeVar
        >>> T = TypeVar("T")
        >>> name(Dict)
        'Dict'
        >>> name(Dict[str, str])
        'Dict'
        >>> name(Any)
        'Any'
        >>> name(dict)
        'dict'
    """
    strobj = qualname(obj)
    return strobj.rsplit(".")[-1]

normalize_typevar

normalize_typevar(tvar: TypeVar) -> type[Any]

Reduce a TypeVar to a simple type.

Note

TypeVar normalization follows this strategy:

-> If the TypeVar is bound
-----> return the bound type
-> Else If the TypeVar has constraints
-----> return a Union of the constraints
-> Else
-----> return Any
Source code in src/typelib/py/inspection.py
@compat.cache
def normalize_typevar(tvar: tp.TypeVar) -> type[tp.Any]:
    """Reduce a TypeVar to a simple type.

    Note:
        TypeVar normalization follows this strategy:

            -> If the TypeVar is bound
            -----> return the bound type
            -> Else If the TypeVar has constraints
            -----> return a Union of the constraints
            -> Else
            -----> return Any
    """
    if tvar.__bound__:
        return tvar.__bound__
    elif tvar.__constraints__:
        return tp.Union[tvar.__constraints__]  # type: ignore[return-value]
    return tp.Any  # type: ignore[return-value]

origin

origin(annotation: Any) -> Any

Get the highest-order 'origin'-type for a given type definition.

Tip

For the purposes of this library, if we can resolve to a builtin type, we will.

Examples:

>>> from typelib.py import inspection
>>> from typing import Dict, Mapping, NewType, Optional
>>> origin(Dict)
<class 'dict'>
>>> origin(Mapping)
<class 'dict'>
>>> Registry = NewType('Registry', Dict)
>>> origin(Registry)
<class 'dict'>
>>> class Foo: ...
...
>>> origin(Foo)
<class 'typelib.Foo'>
Source code in src/typelib/py/inspection.py
@compat.cache
def origin(annotation: tp.Any) -> tp.Any:
    """Get the highest-order 'origin'-type for a given type definition.

    Tip:
        For the purposes of this library, if we can resolve to a builtin type, we will.

    Examples:
        >>> from typelib.py import inspection
        >>> from typing import Dict, Mapping, NewType, Optional
        >>> origin(Dict)
        <class 'dict'>
        >>> origin(Mapping)
        <class 'dict'>
        >>> Registry = NewType('Registry', Dict)
        >>> origin(Registry)
        <class 'dict'>
        >>> class Foo: ...
        ...
        >>> origin(Foo)
        <class 'typelib.Foo'>
    """
    # Resolve custom NewTypes.
    actual = resolve_supertype(annotation)

    # Unwrap optional/classvar
    if isclassvartype(actual):
        a = args(actual)
        actual = a[0] if a else actual

    if istypealiastype(actual):
        actual = actual.__value__

    actual = tp.get_origin(actual) or actual

    # provide defaults for generics
    if not isbuiltintype(actual):
        actual = _check_generics(actual)

    if iscallable(actual):
        actual = tp.Callable

    return actual

qualname

qualname(obj: Union[type, ForwardRef, Callable]) -> str

Safely retrieve the qualname of either a standard object or a type annotation.

Examples:

>>> from typelib.py import inspection
>>> from typing import Dict, Any, TypeVar
>>> T = TypeVar("T")
>>> qualname(Dict)
'typing.Dict'
>>> qualname(Dict[str, str])
'typing.Dict'
>>> qualname(Any)
'typing.Any'
>>> qualname(dict)
'dict'
Source code in src/typelib/py/inspection.py
@compat.cache
def qualname(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:
    """Safely retrieve the qualname of either a standard object or a type annotation.

    Examples:
        >>> from typelib.py import inspection
        >>> from typing import Dict, Any, TypeVar
        >>> T = TypeVar("T")
        >>> qualname(Dict)
        'typing.Dict'
        >>> qualname(Dict[str, str])
        'typing.Dict'
        >>> qualname(Any)
        'typing.Any'
        >>> qualname(dict)
        'dict'
    """
    strobj = str(obj)
    if isinstance(obj, refs.ForwardRef):
        strobj = str(obj.__forward_arg__)  # type: ignore[union-attr]
    is_generic = isgeneric(strobj)
    # We got a typing thing.
    if is_generic:
        # If this is a subscripted generic we should clean that up.
        return strobj.split("[", maxsplit=1)[0]
    # Easy-ish path, use name magix
    qname = getattr(obj, "__qualname__", None)
    nm = getattr(obj, "__name__", None)
    if qname is not None:
        return qname.replace("<locals>.", "")
    if nm is not None:  # pragma: no cover
        return nm
    return strobj

resolve_supertype

resolve_supertype(annotation: type[Any] | FunctionType) -> Any

Get the highest-order supertype for a NewType.

Examples:

>>> from typelib.py import inspection
>>> from typing import NewType
>>> UserID = NewType("UserID", int)
>>> AdminID = NewType("AdminID", UserID)
>>> resolve_supertype(AdminID)
<class 'int'>
Source code in src/typelib/py/inspection.py
@compat.cache
def resolve_supertype(annotation: type[tp.Any] | types.FunctionType) -> tp.Any:
    """Get the highest-order supertype for a NewType.

    Examples:
        >>> from typelib.py import inspection
        >>> from typing import NewType
        >>> UserID = NewType("UserID", int)
        >>> AdminID = NewType("AdminID", UserID)
        >>> resolve_supertype(AdminID)
        <class 'int'>
    """
    while hasattr(annotation, "__supertype__"):
        annotation = annotation.__supertype__  # type: ignore[union-attr]
    return annotation

safe_get_params

safe_get_params(obj: type) -> Mapping[str, Parameter]

Try to extract the parameters of the given object.

Return an empty mapping if we encounter an error.

Source code in src/typelib/py/inspection.py
@compat.cache
def safe_get_params(obj: type) -> tp.Mapping[str, inspect.Parameter]:
    """Try to extract the parameters of the given object.

    Return an empty mapping if we encounter an error.
    """
    params: tp.Mapping[str, inspect.Parameter]
    try:
        if ismappingtype(obj) and not istypeddict(obj):
            return {}
        params = cached_signature(obj).parameters
    except (ValueError, TypeError):  # pragma: nocover
        params = {}
    return params

should_unwrap

should_unwrap(obj: type) -> bool

Test whether we should use the args attr for resolving the type.

This is useful for determining what type to use at run-time for coercion.

Source code in src/typelib/py/inspection.py
@compat.cache
def should_unwrap(obj: type) -> bool:
    """Test whether we should use the __args__ attr for resolving the type.

    This is useful for determining what type to use at run-time for coercion.
    """
    return (not isliteral(obj)) and any(x(obj) for x in _UNWRAPPABLE)

signature

signature(obj: Callable[..., Any] | type[Any]) -> Signature

Get the signature of a type or callable.

Also supports TypedDict subclasses

Source code in src/typelib/py/inspection.py
def signature(obj: tp.Callable[..., tp.Any] | type[tp.Any]) -> inspect.Signature:
    """Get the signature of a type or callable.

    Also supports TypedDict subclasses
    """
    if inspect.isclass(obj) or isgeneric(obj):
        if istypeddict(obj):
            return typed_dict_signature(obj)
        if istupletype(obj) and not isnamedtuple(obj):
            return tuple_signature(obj)
    return inspect.signature(obj)

simple_attributes

simple_attributes(t: type) -> Tuple[str, ...]

Extract all public, static data-attributes for a given type.

Source code in src/typelib/py/inspection.py
def simple_attributes(t: type) -> tp.Tuple[str, ...]:
    """Extract all public, static data-attributes for a given type."""
    # If slots are defined, this is the best way to locate static attributes.
    if hasattr(t, "__slots__") and t.__slots__:
        return (
            *(
                f
                for f in t.__slots__
                if not f.startswith("_")
                # JIC - check if this is something fancy.
                and not isinstance(getattr(t, f, ...), _DYNAMIC_ATTRIBUTES)
            ),
        )
    # Otherwise we have to guess. This is inherently faulty, as attributes aren't
    #   always defined on a class before instantiation. The alternative is reverse
    #   engineering the constructor... yikes.
    return (
        *(
            x
            for x, y in inspect.getmembers(t, predicate=issimpleattribute)
            if not x.startswith("_") and not isinstance(y, _DYNAMIC_ATTRIBUTES)
        ),
    )

tuple_signature

tuple_signature(t: type[TupleT]) -> Signature

A little faker for getting the "signature" of a tuple.

Note

At runtime, tuples are just tuples, but we can make use of their type hints to define a predictable signature.

Source code in src/typelib/py/inspection.py
def tuple_signature(t: type[compat.TupleT]) -> inspect.Signature:
    """A little faker for getting the "signature" of a [`tuple`][].

    Note:
        At runtime, tuples are just tuples, but we can make use of their type hints to
        define a predictable signature.
    """
    a = args(t)
    if not a or a[-1] is ...:
        argt = tp.Any if not a else a[0]
        param = inspect.Parameter(
            name="args", kind=inspect.Parameter.VAR_POSITIONAL, annotation=argt
        )
        sig = inspect.Signature(parameters=(param,))
        return sig
    kind = inspect.Parameter.POSITIONAL_ONLY
    params = tuple(
        inspect.Parameter(name=f"arg{str(i)}", kind=kind, annotation=at)
        for i, at in enumerate(a)
    )
    sig = inspect.Signature(parameters=params)
    return sig

typed_dict_signature

typed_dict_signature(obj: Callable) -> Signature

A little faker for getting the "signature" of a typing.TypedDict.

Note

Technically, these are dicts at runtime, but we are enforcing a static shape, so we should be able to declare a matching signature for it.

Source code in src/typelib/py/inspection.py
def typed_dict_signature(obj: tp.Callable) -> inspect.Signature:
    """A little faker for getting the "signature" of a [`typing.TypedDict`][].

    Note:
        Technically, these are dicts at runtime, but we are enforcing a static shape,
        so we should be able to declare a matching signature for it.
    """
    hints = cached_type_hints(obj)
    total = getattr(obj, "__total__", True)
    default = inspect.Parameter.empty if total else ...
    return inspect.Signature(
        parameters=tuple(
            inspect.Parameter(
                name=x,
                kind=inspect.Parameter.KEYWORD_ONLY,
                annotation=y,
                default=getattr(obj, x, default),
            )
            for x, y in hints.items()
        )
    )