Install & Compatibility
Where this runs
tested against v0.4.4 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.056s · 18.2MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.7s · import 0.050s · 19MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
inspect_annotation
✓ from typing_inspection.introspection import inspect_annotation
✗ from typing_inspection import inspect_annotation
All public symbols live in the two explicit submodules (.introspection and .typing_objects); the top-level typing_inspection namespace does not re-export them.
AnnotationSource
✓ from typing_inspection.introspection import AnnotationSource
✗ from typing_inspection import AnnotationSource
AnnotationSource enum must be imported from the .introspection submodule directly.
UNKNOWN
✓ from typing_inspection.introspection import UNKNOWN
✗ from typing_inspection.introspection import INFERRED
The sentinel was renamed from INFERRED to UNKNOWN in v0.3.0; importing INFERRED raises ImportError.
is_union
✓ from typing_inspection.typing_objects import is_union
✗ from typing_inspection import is_union
All typing_objects predicate functions (is_literal, is_any, is_self, is_union, etc.) are in the .typing_objects submodule.
get_literal_values
✓ from typing_inspection.introspection import get_literal_values
Prefer get_literal_values() over direct __args__ access; it properly expands PEP 695 type aliases.
is_union_origin
✓ from typing_inspection.introspection import is_union_origin
Only needed for Python < 3.14; since Python 3.14 Union[t1,t2] and t1|t2 are the same class, so is_union() from typing_objects suffices.
ForbiddenQualifier
✓ from typing_inspection.introspection import ForbiddenQualifier
Exception raised when an invalid type qualifier is used for the given AnnotationSource; catch explicitly if you want to report it as a user error.
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)
Debug
Known issues
breakingThe 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.fixReplace `from typing_inspection.introspection import INFERRED` with `from typing_inspection.introspection import UNKNOWN` and update all `is INFERRED` checks to `is UNKNOWN`.
affects: <0.3.0
breakingv0.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.fixAdd a branch for `AnnotationSource.DATACLASS` wherever you enumerate AnnotationSource values. Also import `dataclasses.InitVar` handling if you introspect dataclass fields.
affects: <0.4.0
gotchaNever 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.fixUse `from typing_inspection.typing_objects import is_union` and call `is_union(get_origin(x))`, which checks both variants automatically.
affects: all
gotchainspect_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.fixCatch `ForbiddenQualifier` explicitly, or pass `annotation_source=AnnotationSource.ANY` if you want to permit all qualifiers regardless of context.
affects: all
gotchaUsing `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.fixAlways 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.
affects: all
gotchaWhen `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).fixUse `unpack_type_aliases='lenient'` to fall back gracefully to skipping the alias if name resolution fails, instead of raising NameError.
affects: >=0.4.0
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.fixFor codebases targeting Python 3.14+ exclusively, replace `is_union_origin(get_origin(x))` with `is_union(get_origin(x))` from `typing_inspection.typing_objects`.
affects: >=0.4.1 on Python 3.14+
Upgrade
Version history
0.4.4latest on PyPI · released Aug 12, 2026
Audit
Dependencies
typing_extensionsrequiredRequired at runtime; typing_objects predicates check against both stdlib typing and typing_extensions variants to handle version differences.