1"""High-level introspection utilities, used to inspect type annotations.""" 
    2 
    3from __future__ import annotations 
    4 
    5import sys 
    6import types 
    7from collections.abc import Generator 
    8from dataclasses import InitVar 
    9from enum import Enum, IntEnum, auto 
    10from typing import Any, Literal, NamedTuple, cast 
    11 
    12from typing_extensions import TypeAlias, assert_never, get_args, get_origin 
    13 
    14from . import typing_objects 
    15 
    16__all__ = ( 
    17    'AnnotationSource', 
    18    'ForbiddenQualifier', 
    19    'InspectedAnnotation', 
    20    'Qualifier', 
    21    'get_literal_values', 
    22    'inspect_annotation', 
    23    'is_union_origin', 
    24) 
    25 
    26if sys.version_info >= (3, 14) or sys.version_info < (3, 10): 
    27 
    28    def is_union_origin(obj: Any, /) -> bool: 
    29        """Return whether the provided origin is the union form. 
    30 
    31        ```pycon 
    32        >>> is_union_origin(typing.Union) 
    33        True 
    34        >>> is_union_origin(get_origin(int | str)) 
    35        True 
    36        >>> is_union_origin(types.UnionType) 
    37        True 
    38        ``` 
    39 
    40        !!! note 
    41            Since Python 3.14, both `Union[<t1>, <t2>, ...]` and `<t1> | <t2> | ...` forms create instances 
    42            of the same [`typing.Union`][] class. As such, it is recommended to not use this function 
    43            anymore (provided that you only support Python 3.14 or greater), and instead use the 
    44            [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly: 
    45 
    46            ```python 
    47            from typing import Union, get_origin 
    48 
    49            from typing_inspection import typing_objects 
    50 
    51            typ = int | str  # Or Union[int, str] 
    52            origin = get_origin(typ) 
    53            if typing_objects.is_union(origin): 
    54                ... 
    55            ``` 
    56        """ 
    57        return typing_objects.is_union(obj) 
    58 
    59 
    60else: 
    61 
    62    def is_union_origin(obj: Any, /) -> bool: 
    63        """Return whether the provided origin is the union form. 
    64 
    65        ```pycon 
    66        >>> is_union_origin(typing.Union) 
    67        True 
    68        >>> is_union_origin(get_origin(int | str)) 
    69        True 
    70        >>> is_union_origin(types.UnionType) 
    71        True 
    72        ``` 
    73 
    74        !!! note 
    75            Since Python 3.14, both `Union[<t1>, <t2>, ...]` and `<t1> | <t2> | ...` forms create instances 
    76            of the same [`typing.Union`][] class. As such, it is recommended to not use this function 
    77            anymore (provided that you only support Python 3.14 or greater), and instead use the 
    78            [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly: 
    79 
    80            ```python 
    81            from typing import Union, get_origin 
    82 
    83            from typing_inspection import typing_objects 
    84 
    85            typ = int | str  # Or Union[int, str] 
    86            origin = get_origin(typ) 
    87            if typing_objects.is_union(origin): 
    88                ... 
    89            ``` 
    90        """ 
    91        return typing_objects.is_union(obj) or obj is types.UnionType 
    92 
    93 
    94def _literal_type_check(value: Any, /) -> None: 
    95    """Type check the provided literal value against the legal parameters.""" 
    96    if ( 
    97        not isinstance(value, (int, bytes, str, bool, Enum, typing_objects.NoneType)) 
    98        and value is not typing_objects.NoneType 
    99    ): 
    100        raise TypeError(f'{value} is not a valid literal value, must be one of: int, bytes, str, Enum, None.') 
    101 
    102 
    103def get_literal_values( 
    104    annotation: Any, 
    105    /, 
    106    *, 
    107    type_check: bool = False, 
    108    unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager', 
    109) -> Generator[Any]: 
    110    """Yield the values contained in the provided [`Literal`][typing.Literal] [special form][]. 
    111 
    112    Args: 
    113        annotation: The [`Literal`][typing.Literal] [special form][] to unpack. 
    114        type_check: Whether to check if the literal values are [legal parameters][literal-legal-parameters]. 
    115            Raises a [`TypeError`][] otherwise. 
    116        unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/) 
    117            [type aliases][type-aliases]. Can be one of: 
    118 
    119            - `'skip'`: Do not try to parse type aliases. Note that this can lead to incorrect results: 
    120              ```pycon 
    121              >>> type MyAlias = Literal[1, 2] 
    122              >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="skip")) 
    123              [MyAlias, 3] 
    124              ``` 
    125 
    126            - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias can't be inspected 
    127              (because of an undefined forward reference). 
    128 
    129            - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions (the default): 
    130              ```pycon 
    131              >>> type MyAlias = Literal[1, 2] 
    132              >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="eager")) 
    133              [1, 2, 3] 
    134              ``` 
    135 
    136    Note: 
    137        While `None` is [equivalent to][none] `type(None)`, the runtime implementation of [`Literal`][typing.Literal] 
    138        does not de-duplicate them. This function makes sure this de-duplication is applied: 
    139 
    140        ```pycon 
    141        >>> list(get_literal_values(Literal[NoneType, None])) 
    142        [None] 
    143        ``` 
    144 
    145    Example: 
    146        ```pycon 
    147        >>> type Ints = Literal[1, 2] 
    148        >>> list(get_literal_values(Literal[1, Ints], unpack_type_alias="skip")) 
    149        ["a", Ints] 
    150        >>> list(get_literal_values(Literal[1, Ints])) 
    151        [1, 2] 
    152        >>> list(get_literal_values(Literal[1.0], type_check=True)) 
    153        Traceback (most recent call last): 
    154        ... 
    155        TypeError: 1.0 is not a valid literal value, must be one of: int, bytes, str, Enum, None. 
    156        ``` 
    157    """ 
    158    # `literal` is guaranteed to be a `Literal[...]` special form, so use 
    159    # `__args__` directly instead of calling `get_args()`. 
    160 
    161    if unpack_type_aliases == 'skip': 
    162        _has_none = False 
    163        # `Literal` parameters are already deduplicated, no need to do it ourselves. 
    164        # (we only check for `None` and `NoneType`, which should be considered as duplicates). 
    165        for arg in annotation.__args__: 
    166            if type_check: 
    167                _literal_type_check(arg) 
    168            if arg is None or arg is typing_objects.NoneType: 
    169                if not _has_none: 
    170                    yield None 
    171                _has_none = True 
    172            else: 
    173                yield arg 
    174    else: 
    175        # We'll need to manually deduplicate parameters, see the `Literal` implementation in `typing`. 
    176        values_and_type: list[tuple[Any, type[Any]]] = [] 
    177 
    178        for arg in annotation.__args__: 
    179            # Note: we could also check for generic aliases with a type alias as an origin. 
    180            # However, it is very unlikely that this happens as type variables can't appear in 
    181            # `Literal` forms, so the only valid (but unnecessary) use case would be something like: 
    182            # `type Test[T] = Literal['a']` (and then use `Test[SomeType]`). 
    183            if typing_objects.is_typealiastype(arg): 
    184                try: 
    185                    alias_value = arg.__value__ 
    186                except NameError: 
    187                    if unpack_type_aliases == 'eager': 
    188                        raise 
    189                    # unpack_type_aliases == "lenient": 
    190                    if type_check: 
    191                        _literal_type_check(arg) 
    192                    values_and_type.append((arg, type(arg))) 
    193                else: 
    194                    sub_args = get_literal_values( 
    195                        alias_value, type_check=type_check, unpack_type_aliases=unpack_type_aliases 
    196                    ) 
    197                    values_and_type.extend((a, type(a)) for a in sub_args)  # pyright: ignore[reportUnknownArgumentType] 
    198            else: 
    199                if type_check: 
    200                    _literal_type_check(arg) 
    201                if arg is typing_objects.NoneType: 
    202                    values_and_type.append((None, typing_objects.NoneType)) 
    203                else: 
    204                    values_and_type.append((arg, type(arg)))  # pyright: ignore[reportUnknownArgumentType] 
    205 
    206        try: 
    207            dct = dict.fromkeys(values_and_type) 
    208        except TypeError: 
    209            # Unhashable parameters, the Python implementation allows them 
    210            yield from (p for p, _ in values_and_type) 
    211        else: 
    212            yield from (p for p, _ in dct) 
    213 
    214 
    215Qualifier: TypeAlias = Literal['required', 'not_required', 'read_only', 'class_var', 'init_var', 'final'] 
    216"""A [type qualifier][].""" 
    217 
    218_all_qualifiers: set[Qualifier] = set(get_args(Qualifier)) 
    219 
    220 
    221# TODO at some point, we could switch to an enum flag, so that multiple sources 
    222# can be combined. However, is there a need for this? 
    223class AnnotationSource(IntEnum): 
    224    # TODO if/when https://peps.python.org/pep-0767/ is accepted, add 'read_only' 
    225    # to CLASS and NAMED_TUPLE (even though for named tuples it is redundant). 
    226 
    227    """The source of an annotation, e.g. a class or a function. 
    228 
    229    Depending on the source, different [type qualifiers][type qualifier] may be (dis)allowed. 
    230    """ 
    231 
    232    ASSIGNMENT_OR_VARIABLE = auto() 
    233    """An annotation used in an assignment or variable annotation: 
    234 
    235    ```python 
    236    x: Final[int] = 1 
    237    y: Final[str] 
    238    ``` 
    239 
    240    **Allowed type qualifiers:** [`Final`][typing.Final]. 
    241    """ 
    242 
    243    CLASS = auto() 
    244    """An annotation used in the body of a class: 
    245 
    246    ```python 
    247    class Test: 
    248        x: Final[int] = 1 
    249        y: ClassVar[str] 
    250    ``` 
    251 
    252    **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final]. 
    253    """ 
    254 
    255    DATACLASS = auto() 
    256    """An annotation used in the body of a dataclass: 
    257 
    258    ```python 
    259    @dataclass 
    260    class Test: 
    261        x: Final[int] = 1 
    262        y: InitVar[str] = 'test' 
    263    ``` 
    264 
    265    **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final], [`InitVar`][dataclasses.InitVar]. 
    266    """  # noqa: E501 
    267 
    268    TYPED_DICT = auto() 
    269    """An annotation used in the body of a [`TypedDict`][typing.TypedDict]: 
    270 
    271    ```python 
    272    class TD(TypedDict): 
    273        x: Required[ReadOnly[int]] 
    274        y: ReadOnly[NotRequired[str]] 
    275    ``` 
    276 
    277    **Allowed type qualifiers:** [`ReadOnly`][typing.ReadOnly], [`Required`][typing.Required], 
    278    [`NotRequired`][typing.NotRequired]. 
    279    """ 
    280 
    281    NAMED_TUPLE = auto() 
    282    """An annotation used in the body of a [`NamedTuple`][typing.NamedTuple]. 
    283 
    284    ```python 
    285    class NT(NamedTuple): 
    286        x: int 
    287        y: str 
    288    ``` 
    289 
    290    **Allowed type qualifiers:** none. 
    291    """ 
    292 
    293    FUNCTION = auto() 
    294    """An annotation used in a function, either for a parameter or the return value. 
    295 
    296    ```python 
    297    def func(a: int) -> str: 
    298        ... 
    299    ``` 
    300 
    301    **Allowed type qualifiers:** none. 
    302    """ 
    303 
    304    ANY = auto() 
    305    """An annotation that might come from any source. 
    306 
    307    **Allowed type qualifiers:** all. 
    308    """ 
    309 
    310    BARE = auto() 
    311    """An annotation that is inspected as is. 
    312 
    313    **Allowed type qualifiers:** none. 
    314    """ 
    315 
    316    @property 
    317    def allowed_qualifiers(self) -> set[Qualifier]: 
    318        """The allowed [type qualifiers][type qualifier] for this annotation source.""" 
    319        # TODO use a match statement when Python 3.9 support is dropped. 
    320        if self is AnnotationSource.ASSIGNMENT_OR_VARIABLE: 
    321            return {'final'} 
    322        elif self is AnnotationSource.CLASS: 
    323            return {'final', 'class_var'} 
    324        elif self is AnnotationSource.DATACLASS: 
    325            return {'final', 'class_var', 'init_var'} 
    326        elif self is AnnotationSource.TYPED_DICT: 
    327            return {'required', 'not_required', 'read_only'} 
    328        elif self in (AnnotationSource.NAMED_TUPLE, AnnotationSource.FUNCTION, AnnotationSource.BARE): 
    329            return set() 
    330        elif self is AnnotationSource.ANY: 
    331            return _all_qualifiers 
    332        else:  # pragma: no cover 
    333            assert_never(self) 
    334 
    335 
    336class ForbiddenQualifier(Exception): 
    337    """The provided [type qualifier][] is forbidden.""" 
    338 
    339    qualifier: Qualifier 
    340    """The forbidden qualifier.""" 
    341 
    342    def __init__(self, qualifier: Qualifier, /) -> None: 
    343        self.qualifier = qualifier 
    344 
    345 
    346class _UnknownTypeEnum(Enum): 
    347    UNKNOWN = auto() 
    348 
    349    def __str__(self) -> str: 
    350        return 'UNKNOWN' 
    351 
    352    def __repr__(self) -> str: 
    353        return '<UNKNOWN>' 
    354 
    355 
    356UNKNOWN = _UnknownTypeEnum.UNKNOWN 
    357"""A sentinel value used when no [type expression][] is present.""" 
    358 
    359_UnkownType: TypeAlias = Literal[_UnknownTypeEnum.UNKNOWN] 
    360"""The type of the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel value.""" 
    361 
    362 
    363class InspectedAnnotation(NamedTuple): 
    364    """The result of the inspected annotation.""" 
    365 
    366    type: Any | _UnkownType 
    367    """The final [type expression][], with [type qualifiers][type qualifier] and annotated metadata stripped. 
    368 
    369    If no type expression is available, the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel 
    370    value is used instead. This is the case when a [type qualifier][] is used with no type annotation: 
    371 
    372    ```python 
    373    ID: Final = 1 
    374 
    375    class C: 
    376        x: ClassVar = 'test' 
    377    ``` 
    378    """ 
    379 
    380    qualifiers: set[Qualifier] 
    381    """The [type qualifiers][type qualifier] present on the annotation.""" 
    382 
    383    metadata: list[Any] 
    384    """The annotated metadata.""" 
    385 
    386 
    387def inspect_annotation(  # noqa: PLR0915 
    388    annotation: Any, 
    389    /, 
    390    *, 
    391    annotation_source: AnnotationSource, 
    392    unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'skip', 
    393) -> InspectedAnnotation: 
    394    """Inspect an [annotation expression][], extracting any [type qualifier][] and metadata. 
    395 
    396    An [annotation expression][] is a [type expression][] optionally surrounded by one or more 
    397    [type qualifiers][type qualifier] or by [`Annotated`][typing.Annotated]. This function will: 
    398 
    399    - Unwrap the type expression, keeping track of the type qualifiers. 
    400    - Unwrap [`Annotated`][typing.Annotated] forms, keeping track of the annotated metadata. 
    401 
    402    Args: 
    403        annotation: The annotation expression to be inspected. 
    404        annotation_source: The source of the annotation. Depending on the source (e.g. a class), different type 
    405            qualifiers may be (dis)allowed. To allow any type qualifier, use 
    406            [`AnnotationSource.ANY`][typing_inspection.introspection.AnnotationSource.ANY]. 
    407        unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/) 
    408            [type aliases][type-aliases]. Can be one of: 
    409 
    410            - `'skip'`: Do not try to parse type aliases (the default): 
    411              ```pycon 
    412              >>> type MyInt = Annotated[int, 'meta'] 
    413              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='skip') 
    414              InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[]) 
    415              ``` 
    416 
    417            - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias 
    418              can't be inspected (because of an undefined forward reference): 
    419              ```pycon 
    420              >>> type MyInt = Annotated[Undefined, 'meta'] 
    421              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient') 
    422              InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[]) 
    423              >>> Undefined = int 
    424              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient') 
    425              InspectedAnnotation(type=int, qualifiers={}, metadata=['meta']) 
    426              ``` 
    427 
    428            - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions. 
    429 
    430    Returns: 
    431        The result of the inspected annotation, where the type expression, used qualifiers and metadata is stored. 
    432 
    433    Example: 
    434        ```pycon 
    435        >>> inspect_annotation( 
    436        ...     Final[Annotated[ClassVar[Annotated[int, 'meta_1']], 'meta_2']], 
    437        ...     annotation_source=AnnotationSource.CLASS, 
    438        ... ) 
    439        ... 
    440        InspectedAnnotation(type=int, qualifiers={'class_var', 'final'}, metadata=['meta_1', 'meta_2']) 
    441        ``` 
    442    """ 
    443    allowed_qualifiers = annotation_source.allowed_qualifiers 
    444    qualifiers: set[Qualifier] = set() 
    445    metadata: list[Any] = [] 
    446 
    447    while True: 
    448        annotation, _meta = _unpack_annotated(annotation, unpack_type_aliases=unpack_type_aliases) 
    449        if _meta: 
    450            metadata = _meta + metadata 
    451            continue 
    452 
    453        origin = get_origin(annotation) 
    454        if origin is not None: 
    455            if typing_objects.is_classvar(origin): 
    456                if 'class_var' not in allowed_qualifiers: 
    457                    raise ForbiddenQualifier('class_var') 
    458                qualifiers.add('class_var') 
    459                annotation = annotation.__args__[0] 
    460            elif typing_objects.is_final(origin): 
    461                if 'final' not in allowed_qualifiers: 
    462                    raise ForbiddenQualifier('final') 
    463                qualifiers.add('final') 
    464                annotation = annotation.__args__[0] 
    465            elif typing_objects.is_required(origin): 
    466                if 'required' not in allowed_qualifiers: 
    467                    raise ForbiddenQualifier('required') 
    468                qualifiers.add('required') 
    469                annotation = annotation.__args__[0] 
    470            elif typing_objects.is_notrequired(origin): 
    471                if 'not_required' not in allowed_qualifiers: 
    472                    raise ForbiddenQualifier('not_required') 
    473                qualifiers.add('not_required') 
    474                annotation = annotation.__args__[0] 
    475            elif typing_objects.is_readonly(origin): 
    476                if 'read_only' not in allowed_qualifiers: 
    477                    raise ForbiddenQualifier('not_required') 
    478                qualifiers.add('read_only') 
    479                annotation = annotation.__args__[0] 
    480            else: 
    481                # origin is not None but not a type qualifier nor `Annotated` (e.g. `list[int]`): 
    482                break 
    483        elif isinstance(annotation, InitVar): 
    484            if 'init_var' not in allowed_qualifiers: 
    485                raise ForbiddenQualifier('init_var') 
    486            qualifiers.add('init_var') 
    487            annotation = cast(Any, annotation.type) 
    488        else: 
    489            break 
    490 
    491    # `Final`, `ClassVar` and `InitVar` are type qualifiers allowed to be used as a bare annotation: 
    492    if typing_objects.is_final(annotation): 
    493        if 'final' not in allowed_qualifiers: 
    494            raise ForbiddenQualifier('final') 
    495        qualifiers.add('final') 
    496        annotation = UNKNOWN 
    497    elif typing_objects.is_classvar(annotation): 
    498        if 'class_var' not in allowed_qualifiers: 
    499            raise ForbiddenQualifier('class_var') 
    500        qualifiers.add('class_var') 
    501        annotation = UNKNOWN 
    502    elif annotation is InitVar: 
    503        if 'init_var' not in allowed_qualifiers: 
    504            raise ForbiddenQualifier('init_var') 
    505        qualifiers.add('init_var') 
    506        annotation = UNKNOWN 
    507 
    508    return InspectedAnnotation(annotation, qualifiers, metadata) 
    509 
    510 
    511def _unpack_annotated_inner( 
    512    annotation: Any, unpack_type_aliases: Literal['lenient', 'eager'], check_annotated: bool 
    513) -> tuple[Any, list[Any]]: 
    514    origin = get_origin(annotation) 
    515    if check_annotated and typing_objects.is_annotated(origin): 
    516        annotated_type = annotation.__origin__ 
    517        metadata = list(annotation.__metadata__) 
    518 
    519        # The annotated type might be a PEP 695 type alias, so we need to recursively 
    520        # unpack it. Because Python already flattens `Annotated[Annotated[<type>, ...], ...]` forms, 
    521        # we can skip the `is_annotated()` check in the next call: 
    522        annotated_type, sub_meta = _unpack_annotated_inner( 
    523            annotated_type, unpack_type_aliases=unpack_type_aliases, check_annotated=False 
    524        ) 
    525        metadata = sub_meta + metadata 
    526        return annotated_type, metadata 
    527    elif typing_objects.is_typealiastype(annotation): 
    528        try: 
    529            value = annotation.__value__ 
    530        except NameError: 
    531            if unpack_type_aliases == 'eager': 
    532                raise 
    533        else: 
    534            typ, metadata = _unpack_annotated_inner( 
    535                value, unpack_type_aliases=unpack_type_aliases, check_annotated=True 
    536            ) 
    537            if metadata: 
    538                # Having metadata means the type alias' `__value__` was an `Annotated` form 
    539                # (or, recursively, a type alias to an `Annotated` form). It is important to check 
    540                # for this, as we don't want to unpack other type aliases (e.g. `type MyInt = int`). 
    541                return typ, metadata 
    542            return annotation, [] 
    543    elif typing_objects.is_typealiastype(origin): 
    544        # When parameterized, PEP 695 type aliases become generic aliases 
    545        # (e.g. with `type MyList[T] = Annotated[list[T], ...]`, `MyList[int]` 
    546        # is a generic alias). 
    547        try: 
    548            value = origin.__value__ 
    549        except NameError: 
    550            if unpack_type_aliases == 'eager': 
    551                raise 
    552        else: 
    553            # While Python already handles type variable replacement for simple `Annotated` forms, 
    554            # we need to manually apply the same logic for PEP 695 type aliases: 
    555            # - With `MyList = Annotated[list[T], ...]`, `MyList[int] == Annotated[list[int], ...]` 
    556            # - With `type MyList[T] = Annotated[list[T], ...]`, `MyList[int].__value__ == Annotated[list[T], ...]`. 
    557 
    558            try: 
    559                # To do so, we emulate the parameterization of the value with the arguments: 
    560                # with `type MyList[T] = Annotated[list[T], ...]`, to emulate `MyList[int]`, 
    561                # we do `Annotated[list[T], ...][int]` (which gives `Annotated[list[T], ...]`): 
    562                value = value[annotation.__args__] 
    563            except TypeError: 
    564                # Might happen if the type alias is parameterized, but its value doesn't have any 
    565                # type variables, e.g. `type MyInt[T] = int`. 
    566                pass 
    567            typ, metadata = _unpack_annotated_inner( 
    568                value, unpack_type_aliases=unpack_type_aliases, check_annotated=True 
    569            ) 
    570            if metadata: 
    571                return typ, metadata 
    572            return annotation, [] 
    573 
    574    return annotation, [] 
    575 
    576 
    577# This could eventually be made public: 
    578def _unpack_annotated( 
    579    annotation: Any, /, *, unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager' 
    580) -> tuple[Any, list[Any]]: 
    581    if unpack_type_aliases == 'skip': 
    582        if typing_objects.is_annotated(get_origin(annotation)): 
    583            return annotation.__origin__, list(annotation.__metadata__) 
    584        else: 
    585            return annotation, [] 
    586 
    587    return _unpack_annotated_inner(annotation, unpack_type_aliases=unpack_type_aliases, check_annotated=True)