File: typing.py

package info (click to toggle)
python-xsdata 24.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,936 kB
  • sloc: python: 29,257; xml: 404; makefile: 27; sh: 6
file content (175 lines) | stat: -rw-r--r-- 4,200 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import sys
from typing import Any, Iterator, Tuple, Type, TypeVar, Union

from typing_extensions import get_args, get_origin

NONE_TYPE = type(None)


try:
    from types import UnionType  # type: ignore
except ImportError:
    UnionType = ()  # type: ignore


if (3, 9) <= sys.version_info[:2] <= (3, 10):
    # Backport this fix for python 3.9 and 3.10
    # https://github.com/python/cpython/pull/30900

    from types import GenericAlias
    from typing import ForwardRef
    from typing import _eval_type as __eval_type  # type: ignore

    def _eval_type(tp: Any, globalns: Any, localns: Any) -> Any:
        if isinstance(tp, GenericAlias):
            args = tuple(
                ForwardRef(arg) if isinstance(arg, str) else arg for arg in tp.__args__
            )
            tp = tp.__origin__[args]  # type: ignore

        return __eval_type(tp, globalns, localns)

else:
    from typing import _eval_type  # type: ignore


intern_typing = sys.intern("typing.")


def is_from_typing(tp: Any) -> bool:
    return str(tp).startswith(intern_typing)


def evaluate(
    tp: Any,
    globalns: Any = None,
    localns: Any = None,
) -> Tuple[Type, ...]:
    return tuple(_evaluate(_eval_type(tp, globalns, localns)))


def _evaluate(tp: Any) -> Iterator[Type]:
    if tp in (dict, list, tuple):
        origin = tp
    elif isinstance(tp, TypeVar):
        origin = TypeVar
    else:
        origin = get_origin(tp)

    if origin:
        try:
            yield from __evaluations__[origin](tp)
        except KeyError:
            raise TypeError()
    elif is_from_typing(tp):
        raise TypeError()
    else:
        yield tp


def _evaluate_type(tp: Any) -> Iterator[Type]:
    args = get_args(tp)
    if not args or isinstance(args[0], TypeVar):
        raise TypeError()
    yield from _evaluate(args[0])


def _evaluate_mapping(tp: Any) -> Iterator[Type]:
    yield dict
    args = get_args(tp)

    if not args:
        yield str
        yield str

    for arg in args:
        if isinstance(arg, TypeVar):
            try:
                next(_evaluate_typevar(arg))
            except TypeError:
                yield str
            else:
                raise TypeError()
        elif is_from_typing(arg) or get_origin(arg) is not None:
            raise TypeError()
        else:
            yield arg


def _evaluate_list(tp: Any) -> Iterator[Type]:
    yield list

    args = get_args(tp)
    if not args:
        yield str

    for arg in args:
        yield from _evaluate_array_arg(arg)


def _evaluate_array_arg(arg: Any) -> Iterator[Type]:
    if isinstance(arg, TypeVar):
        yield from _evaluate_typevar(arg)
    else:
        origin = get_origin(arg)

        if origin is None and not is_from_typing(arg):
            yield arg
        elif origin in (Union, UnionType, list, tuple):
            yield from __evaluations__[origin](arg)
        else:
            raise TypeError()


def _evaluate_tuple(tp: Any) -> Iterator[Type]:
    yield tuple

    args = get_args(tp)
    if not args:
        yield str

    for arg in args:
        if arg is Ellipsis:
            continue

        yield from _evaluate_array_arg(arg)


def _evaluate_union(tp: Any) -> Iterator[Type]:
    origin_locked = False
    for arg in get_args(tp):
        if arg is NONE_TYPE:
            continue

        if isinstance(arg, TypeVar):
            yield from _evaluate_typevar(arg)
        else:
            origin = get_origin(arg)
            if origin is list and not origin_locked:
                yield from _evaluate_list(arg)
                origin_locked = True
            elif origin is None and not is_from_typing(arg):
                yield arg
            else:
                raise TypeError()


def _evaluate_typevar(tp: TypeVar):
    if tp.__bound__:
        yield from _evaluate(tp.__bound__)
    elif tp.__constraints__:
        for arg in tp.__constraints__:
            yield from _evaluate(arg)
    else:
        raise TypeError()


__evaluations__ = {
    tuple: _evaluate_tuple,
    list: _evaluate_list,
    dict: _evaluate_mapping,
    Union: _evaluate_union,
    UnionType: _evaluate_union,
    type: _evaluate_type,
    TypeVar: _evaluate_typevar,
}