typing-inspection

raw JSON →
0.4.2 verified Tue May 12 auth: no python install: verified quickstart: verified

typing-inspection provides runtime tools to inspect Python type annotations at runtime. Maintained by the Pydantic team, it is split into two submodules: `typing_inspection.typing_objects` (predicate functions like `is_union`, `is_literal`, `is_any`, etc. that correctly handle both `typing` and `typing_extensions` variants) and `typing_inspection.introspection` (higher-level helpers such as `inspect_annotation`, `get_literal_values`, and `is_union_origin`). Current version is 0.4.2 (released 2025-10-01); the project has released frequently since its initial release in February 2025.

pip install typing-inspection
breaking The INFERRED sentinel was renamed to UNKNOWN in v0.3.0. Any code importing or comparing against INFERRED will get an ImportError or a broken identity check.
fix Replace `from typing_inspection.introspection import INFERRED` with `from typing_inspection.introspection import UNKNOWN` and update all `is INFERRED` checks to `is UNKNOWN`.
breaking v0.4.0 added a new `DATACLASS` value to the AnnotationSource enum to support `dataclasses.InitVar` as a type qualifier. Exhaustive match/if-elif chains over AnnotationSource members will silently miss it.
fix Add a branch for `AnnotationSource.DATACLASS` wherever you enumerate AnnotationSource values. Also import `dataclasses.InitVar` handling if you introspect dataclass fields.
gotcha Never use identity checks like `get_origin(x) is typing.Union` or `get_origin(x) is typing_extensions.Union`; typing_extensions may ship a different Union object than stdlib typing.
fix Use `from typing_inspection.typing_objects import is_union` and call `is_union(get_origin(x))`, which checks both variants automatically.
gotcha inspect_annotation() raises ForbiddenQualifier if a type qualifier is not allowed for the given AnnotationSource (e.g. using Required outside a TypedDict context). Not catching this causes unhandled exceptions at runtime.
fix Catch `ForbiddenQualifier` explicitly, or pass `annotation_source=AnnotationSource.ANY` if you want to permit all qualifiers regardless of context.
gotcha Using `type_expr.__args__` directly to get Literal values silently misses PEP 695 type aliases (Python 3.12+), which are lazily evaluated and may not be expanded. Additionally, `get_literal_values` returns a generator that must be iterated to retrieve values.
fix Always use `get_literal_values(type_expr)` from `typing_inspection.introspection` instead of accessing `.__args__` directly on Literal forms, and remember to iterate over the returned generator object (e.g., using `list()` or a for-loop) to extract the literal values.
gotcha When `unpack_type_aliases='eager'` is passed to inspect_annotation(), any undefined symbol in a PEP 695 type alias raises NameError at runtime. The default is 'skip' (aliases not expanded).
fix Use `unpack_type_aliases='lenient'` to fall back gracefully to skipping the alias if name resolution fails, instead of raising NameError.
deprecated `is_union_origin()` is effectively superseded on Python 3.14+, where both Union[t1,t2] and t1|t2 produce the same typing.Union class. The function remains available but its main use case disappears.
fix For codebases targeting Python 3.14+ exclusively, replace `is_union_origin(get_origin(x))` with `is_union(get_origin(x))` from `typing_inspection.typing_objects`.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.06s 18.2M
3.10 slim (glibc) - - 0.02s 19M
3.11 alpine (musl) - - 0.09s 20.1M
3.11 slim (glibc) - - 0.07s 21M
3.12 alpine (musl) - - 0.07s 11.9M
3.12 slim (glibc) - - 0.07s 12M
3.13 alpine (musl) - - 0.07s 11.6M
3.13 slim (glibc) - - 0.06s 12M
3.9 alpine (musl) - - 0.04s 17.7M
3.9 slim (glibc) - - 0.03s 18M

Inspect a ClassVar[Annotated[int, 'meta']] annotation, then use typing_objects predicates on the unwrapped type expression.

from typing import ClassVar, Union, get_origin
from typing import Annotated
from typing_inspection.introspection import (
    AnnotationSource,
    UNKNOWN,
    inspect_annotation,
    get_literal_values,
    is_union_origin,
)
from typing_inspection.typing_objects import is_any, is_literal, is_union

# --- Unwrap an annotation expression ---
result = inspect_annotation(
    ClassVar[Annotated[int, "meta"]],
    annotation_source=AnnotationSource.CLASS,
)
print(result)
# InspectedAnnotation(type=int, qualifiers={'class_var'}, metadata=['meta'])

# Check the UNKNOWN sentinel (bare ClassVar / Final with no inner type)
bare_result = inspect_annotation(ClassVar, annotation_source=AnnotationSource.CLASS)
if bare_result.type is UNKNOWN:
    print("No explicit inner type; infer from assignment or default to Any")

# --- typing_objects predicates handle both typing + typing_extensions ---
from typing import Literal
origin = get_origin(Literal[1, 2])
print(is_literal(origin))   # True

# Safe union detection across typing / typing_extensions / PEP 604
union_origin = get_origin(Union[int, str])
print(is_union_origin(union_origin))  # True

# Retrieve Literal values (expands PEP 695 type aliases correctly)
values = get_literal_values(Literal["a", "b", 1])
print(values)  # ('a', 'b', 1)