Skip to content

introspection

High-level introspection utilities, used to inspect type annotations.

is_union_origin

is_union_origin(obj: Any) -> bool

Return whether the provided origin is the union form.

>>> is_union_origin(typing.Union)
True
>>> is_union_origin(get_origin(int | str))
True

get_literal_values

get_literal_values(annotation: Any, /, *, type_check: bool = False, unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager') -> Generator[Any]

Yield the values contained in the provided Literal special form.

Parameters:

Name Type Description Default
annotation Any

The Literal special form to unpack.

required
type_check bool

Whether to check if the literal values are legal parameters. Raises a TypeError otherwise.

False
unpack_type_aliases Literal['skip', 'lenient', 'eager']

What to do when encountering PEP 695 type aliases. Can be one of:

  • 'skip': Do not try to parse type aliases. Note that this can lead to incorrect results:

    >>> type MyAlias = Literal[1, 2]
    >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="skip"))
    [MyAlias, 3]
    

  • 'lenient': Try to parse type aliases, and fallback to 'skip' if the type alias can't be inspected (because of an undefined forward reference).

  • 'eager': Parse type aliases and raise any encountered NameError exceptions (the default):

    >>> type MyAlias = Literal[1, 2]
    >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="eager"))
    [1, 2, 3]
    

'eager'
Note

While None is equivalent to type(None), the runtime implementation of Literal does not de-duplicate them. This function makes sure this de-duplication is applied:

>>> list(get_literal_values(Literal[NoneType, None]))
[None]
Example
>>> type Ints = Literal[1, 2]
>>> list(get_literal_values(Literal[1, Ints], unpack_type_alias="skip"))
["a", Ints]
>>> list(get_literal_values(Literal[1, Ints]))
[1, 2]
>>> list(get_literal_values(Literal[1.0], type_check=True))
Traceback (most recent call last):
...
TypeError: 1.0 is not a valid literal value, must be one of: int, bytes, str, Enum, None.

inspect_annotation

inspect_annotation(annotation: Any, /, *, annotation_source: AnnotationSource, unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'skip') -> InspectedAnnotation

Inspect an annotation expression, extracting any type qualifier and metadata.

An annotation expression is a type expression optionally surrounded by one or more type qualifiers or by Annotated. This function will:

  • Unwrap the type expression, keeping track of the type qualifiers.
  • Unwrap Annotated forms, keeping track of the annotated metadata.

Parameters:

Name Type Description Default
annotation Any

The annotation expression to be inspected.

required
annotation_source AnnotationSource

The source of the annotation. Depending on the source (e.g. a class), different type qualifiers may be (dis)allowed. To allow any type qualifier, use AnnotationSource.ANY.

required
unpack_type_aliases Literal['skip', 'lenient', 'eager']

What to do when encountering PEP 695 type aliases. Can be one of:

  • 'skip': Do not try to parse type aliases (the default):

    >>> type MyInt = Annotated[int, 'meta']
    >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='skip')
    InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[])
    

  • 'lenient': Try to parse type aliases, and fallback to 'skip' if the type alias can't be inspected (because of an undefined forward reference):

    >>> type MyInt = Annotated[Undefined, 'meta']
    >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient')
    InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[])
    >>> Undefined = int
    >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient')
    InspectedAnnotation(type=int, qualifiers={}, metadata=['meta'])
    

  • 'eager': Parse type aliases and raise any encountered NameError exceptions.

'skip'

Returns:

Type Description
InspectedAnnotation

The result of the inspected annotation, where the type expression, used qualifiers and metadata is stored.

Example
>>> inspect_annotation(
...     Final[Annotated[ClassVar[Annotated[int, 'meta_1']], 'meta_2']],
...     annotation_source=AnnotationSource.CLASS,
... )
...
InspectedAnnotation(type=int, qualifiers={'class_var', 'final'}, metadata=['meta_1', 'meta_2'])

AnnotationSource

Bases: IntEnum

The source of an annotation, e.g. a class or a function.

Depending on the source, different type qualifiers may be (dis)allowed.

ASSIGNMENT_OR_VARIABLE

ASSIGNMENT_OR_VARIABLE = 1

An annotation used in an assignment or variable annotation:

x: Final[int] = 1
y: Final[str]

Allowed type qualifiers: Final.

CLASS

CLASS = 2

An annotation used in the body of a class:

class Test:
    x: Final[int] = 1
    y: ClassVar[str]

Allowed type qualifiers: ClassVar, Final.

TYPED_DICT

TYPED_DICT = 3

An annotation used in the body of a TypedDict:

class TD(TypedDict):
    x: Required[ReadOnly[int]]
    y: ReadOnly[NotRequired[str]]

Allowed type qualifiers: ReadOnly, Required, NotRequired.

NAMED_TUPLE

NAMED_TUPLE = 4

An annotation used in the body of a NamedTuple.

class NT(NamedTuple):
    x: int
    y: str

Allowed type qualifiers: none.

FUNCTION

FUNCTION = 5

An annotation used in a function, either for a parameter or the return value.

def func(a: int) -> str:
    ...

Allowed type qualifiers: none.

ANY

ANY = 6

An annotation that might come from any source.

Allowed type qualifiers: all.

BARE

BARE = 7

An annotation that is inspected as is.

Allowed type qualifiers: none.

allowed_qualifiers

allowed_qualifiers: set[Qualifier]

The allowed type qualifiers for this annotation source.

Qualifier

Qualifier: TypeAlias = Literal['required', 'not_required', 'read_only', 'class_var', 'final']

InspectedAnnotation

Bases: NamedTuple

The result of the inspected annotation.

type

type: Any | InferredType

The final type expression, with type qualifiers and annotated metadata stripped.

If no type expression is available, the INFERRED sentinel value is used instead. This is the case when a type qualifier is used with no type annotation:

ID: Final = 1

qualifiers

qualifiers: set[Qualifier]

The type qualifiers present on the annotation.

metadata

metadata: Sequence[Any]

The annotated metadata.

INFERRED

INFERRED = INFERRED

A sentinel value used when no type expression is used, indicating that the type should be inferred from the assigned value.

InferredType

InferredType: TypeAlias = Literal[INFERRED]

The type of the INFERRED sentinel value.

ForbiddenQualifier

ForbiddenQualifier(qualifier: Qualifier)

Bases: Exception

The provided type qualifier is forbidden.