chromatic-python 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chromatic/__init__.py +7 -4
- chromatic/_typing.py +291 -79
- chromatic/_version.py +2 -2
- chromatic/ascii/_array.py +90 -123
- chromatic/ascii/_curses.py +18 -18
- chromatic/ascii/_glyph_proc.py +6 -12
- chromatic/color/colorconv.py +25 -36
- chromatic/color/core.py +100 -170
- chromatic/color/core.pyi +32 -43
- chromatic/color/palette.py +139 -176
- chromatic/color/palette.pyi +62 -18
- chromatic/demo.py +10 -37
- chromatic_python-0.2.0.dist-info/METADATA +147 -0
- chromatic_python-0.2.0.dist-info/RECORD +27 -0
- {chromatic_python-0.1.1.dist-info → chromatic_python-0.2.0.dist-info}/WHEEL +1 -1
- chromatic_python-0.1.1.dist-info/METADATA +0 -56
- chromatic_python-0.1.1.dist-info/RECORD +0 -27
- {chromatic_python-0.1.1.dist-info → chromatic_python-0.2.0.dist-info}/LICENSE +0 -0
- {chromatic_python-0.1.1.dist-info → chromatic_python-0.2.0.dist-info}/top_level.txt +0 -0
chromatic/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ except ImportError:
|
|
|
5
5
|
|
|
6
6
|
from . import ascii, color, data
|
|
7
7
|
from .ascii import (
|
|
8
|
+
AnsiImage,
|
|
8
9
|
ansi2img,
|
|
9
10
|
ansi_quantize,
|
|
10
11
|
ascii2img,
|
|
@@ -23,16 +24,18 @@ from .ascii import (
|
|
|
23
24
|
)
|
|
24
25
|
from .ascii._glyph_proc import get_glyph_masks
|
|
25
26
|
from .color import (
|
|
26
|
-
ansicolor24Bit,
|
|
27
|
-
ansicolor4Bit,
|
|
28
|
-
ansicolor8Bit,
|
|
29
27
|
Back,
|
|
30
28
|
Color,
|
|
31
|
-
|
|
29
|
+
ColorNamespace,
|
|
32
30
|
ColorStr,
|
|
33
31
|
Fore,
|
|
34
32
|
SgrParameter,
|
|
35
33
|
Style,
|
|
34
|
+
ansicolor24Bit,
|
|
35
|
+
ansicolor4Bit,
|
|
36
|
+
ansicolor8Bit,
|
|
37
|
+
colorbytes,
|
|
38
|
+
named_color,
|
|
36
39
|
)
|
|
37
40
|
from .data import register_user_font
|
|
38
41
|
|
chromatic/_typing.py
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import inspect
|
|
4
|
+
import re
|
|
5
|
+
import types
|
|
6
|
+
from collections import OrderedDict, namedtuple
|
|
7
|
+
from collections.abc import Callable as ABC_Callable
|
|
8
|
+
from functools import reduce, wraps
|
|
4
9
|
from numbers import Number
|
|
5
|
-
from
|
|
10
|
+
from operator import attrgetter, or_ as bitwise_or
|
|
6
11
|
from typing import (
|
|
7
12
|
Any,
|
|
8
13
|
Callable,
|
|
9
14
|
Concatenate,
|
|
10
|
-
|
|
11
|
-
get_origin,
|
|
12
|
-
get_type_hints,
|
|
15
|
+
Hashable,
|
|
13
16
|
Iterable,
|
|
14
17
|
Literal,
|
|
18
|
+
NamedTuple,
|
|
19
|
+
Optional,
|
|
15
20
|
ParamSpec,
|
|
16
21
|
Protocol,
|
|
17
22
|
Sequence,
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
Type,
|
|
24
|
+
TypeAlias,
|
|
25
|
+
TypeAliasType,
|
|
26
|
+
TypeGuard,
|
|
20
27
|
TypeVar,
|
|
28
|
+
TypedDict,
|
|
21
29
|
Union,
|
|
30
|
+
Unpack,
|
|
31
|
+
cast,
|
|
32
|
+
get_args,
|
|
33
|
+
get_origin,
|
|
34
|
+
get_type_hints,
|
|
35
|
+
runtime_checkable,
|
|
22
36
|
)
|
|
23
37
|
|
|
24
|
-
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
25
|
-
from numpy._typing import _ArrayLike, NDArray
|
|
26
38
|
from PIL.Image import Image
|
|
27
39
|
from PIL.ImageFont import FreeTypeFont
|
|
40
|
+
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
41
|
+
from numpy._typing import NDArray, _ArrayLike
|
|
28
42
|
|
|
29
43
|
from chromatic.data import UserFont
|
|
30
44
|
|
|
@@ -34,45 +48,50 @@ _T_co = TypeVar('_T_co', covariant=True)
|
|
|
34
48
|
_T_contra = TypeVar('_T_contra', contravariant=True)
|
|
35
49
|
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class SupportsRoundAndDivMod(
|
|
41
|
-
SupportsRound[_T_co], SupportsDivMod[Any, _T_co], Protocol
|
|
42
|
-
): ...
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
type ArrayReducerFunc[_SCT: generic] = Callable[
|
|
46
|
-
Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]
|
|
47
|
-
]
|
|
48
|
-
type KeyFunction[_T] = Callable[[_T], SupportsRichComparison]
|
|
49
|
-
type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[
|
|
50
|
-
_Shape, dtype[_SCT]
|
|
51
|
-
]
|
|
51
|
+
type ArrayReducerFunc[_SCT: generic] = Callable[Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]]
|
|
52
|
+
type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[_Shape, dtype[_SCT]]
|
|
52
53
|
type MatrixLike[_SCT: generic] = ShapedNDArray[TupleOf2[int], _SCT]
|
|
53
54
|
type SquareMatrix[_I: int, _SCT: generic] = ShapedNDArray[TupleOf2[_I], _SCT]
|
|
54
55
|
type GlyphArray[_SCT: generic] = SquareMatrix[Literal[24], _SCT]
|
|
55
56
|
type TupleOf2[_T] = tuple[_T, _T]
|
|
56
57
|
type TupleOf3[_T] = tuple[_T, _T, _T]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
|
|
59
|
+
Float3Tuple: TypeAlias = TupleOf3[float]
|
|
60
|
+
Int3Tuple: TypeAlias = TupleOf3[int]
|
|
61
|
+
FloatSequence: TypeAlias = Sequence[float]
|
|
62
|
+
IntSequence: TypeAlias = Sequence[int]
|
|
63
|
+
GlyphBitmask: TypeAlias = GlyphArray[bool]
|
|
64
|
+
Bitmask: TypeAlias = MatrixLike[bool]
|
|
65
|
+
GreyscaleGlyphArray: TypeAlias = GlyphArray[float64]
|
|
66
|
+
GreyscaleArray: TypeAlias = MatrixLike[float64]
|
|
67
|
+
RGBArray: TypeAlias = ShapedNDArray[tuple[int, int, Literal[3]], uint8]
|
|
68
|
+
RGBPixel: TypeAlias = ShapedNDArray[tuple[Literal[3]], uint8]
|
|
69
|
+
|
|
70
|
+
RGBImageLike: TypeAlias = Image | RGBArray
|
|
71
|
+
RGBVectorLike: TypeAlias = Int3Tuple | IntSequence | RGBPixel
|
|
70
72
|
ColorDictKeys = Literal['fg', 'bg']
|
|
71
73
|
Ansi4BitAlias = Literal['4b']
|
|
72
74
|
Ansi8BitAlias = Literal['8b']
|
|
73
75
|
Ansi24BitAlias = Literal['24b']
|
|
74
76
|
AnsiColorAlias = Ansi4BitAlias | Ansi8BitAlias | Ansi24BitAlias
|
|
75
|
-
FontArgType =
|
|
77
|
+
FontArgType: TypeAlias = FreeTypeFont | UserFont | str | int
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def type_error_msg(err_obj, *expected, context: str = '', obj_repr=False):
|
|
81
|
+
n_expected = len(expected)
|
|
82
|
+
name_slots = [f"{{{n}.__qualname__!r}}" for n in range(n_expected)]
|
|
83
|
+
if name_slots and n_expected > 1:
|
|
84
|
+
name_slots[-1] = f"or {name_slots[-1]}"
|
|
85
|
+
names = (', ' if n_expected > 2 else ' ').join([context.strip()] + name_slots).format(*expected)
|
|
86
|
+
if not obj_repr:
|
|
87
|
+
if not isinstance(err_obj, type):
|
|
88
|
+
err_obj = type(err_obj)
|
|
89
|
+
oops = repr(err_obj.__qualname__)
|
|
90
|
+
elif not isinstance(err_obj, str):
|
|
91
|
+
oops = repr(err_obj)
|
|
92
|
+
else:
|
|
93
|
+
oops = err_obj
|
|
94
|
+
return f"expected {names}, got {oops} instead"
|
|
76
95
|
|
|
77
96
|
|
|
78
97
|
def is_matching_type(value, typ):
|
|
@@ -85,10 +104,7 @@ def is_matching_type(value, typ):
|
|
|
85
104
|
return value in args
|
|
86
105
|
elif isinstance(typ, TypeVar):
|
|
87
106
|
if typ.__constraints__:
|
|
88
|
-
return any(
|
|
89
|
-
is_matching_type(value, constraint)
|
|
90
|
-
for constraint in typ.__constraints__
|
|
91
|
-
)
|
|
107
|
+
return any(is_matching_type(value, constraint) for constraint in typ.__constraints__)
|
|
92
108
|
else:
|
|
93
109
|
return True
|
|
94
110
|
elif origin is type:
|
|
@@ -134,54 +150,250 @@ def is_matching_type(value, typ):
|
|
|
134
150
|
return False
|
|
135
151
|
|
|
136
152
|
|
|
153
|
+
def is_matching_typed_dict(__d: dict, typed_dict: type[dict]) -> tuple[bool, str]:
|
|
154
|
+
if TypedDict not in getattr(__d, '__orig_bases__', ()):
|
|
155
|
+
return False, type_error_msg(__d, dict)
|
|
156
|
+
expected = get_type_hints(typed_dict)
|
|
157
|
+
if unexpected := __d.keys() - expected:
|
|
158
|
+
return False, f"unexpected keyword arguments: {unexpected}"
|
|
159
|
+
if missing := set(getattr(typed_dict, '__required_keys__', expected)) - __d.keys():
|
|
160
|
+
return False, f"missing required keys: {missing}"
|
|
161
|
+
for name, typ in expected.items():
|
|
162
|
+
field = __d.get(name)
|
|
163
|
+
if field is None or is_matching_type(field, typ):
|
|
164
|
+
continue
|
|
165
|
+
return False, type_error_msg(field, typ, context=f'keyword argument {name!r} of type')
|
|
166
|
+
return True, ''
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def is_matching_callable(value, expected_type):
|
|
170
|
+
return callable(value) and value is expected_type
|
|
171
|
+
|
|
172
|
+
|
|
137
173
|
def deconstruct_type(tp):
|
|
138
174
|
origin = get_origin(tp) or tp
|
|
139
175
|
args = get_args(tp)
|
|
140
176
|
return origin, args
|
|
141
177
|
|
|
142
178
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return False
|
|
146
|
-
return id(value) == id(expected_type)
|
|
179
|
+
@runtime_checkable
|
|
180
|
+
class SupportsUnion(Protocol[_T_contra, _T_co]):
|
|
147
181
|
|
|
182
|
+
def __or__(self, x: _T_contra, /) -> _T_co: ...
|
|
148
183
|
|
|
149
|
-
def pseudo_union(ts: Iterable[type]) -> Union[type, UnionType]:
|
|
150
|
-
return reduce(lambda x, y: x | y, ts)
|
|
151
184
|
|
|
185
|
+
def unionize(__iterable: Iterable[SupportsUnion[_T_contra, _T_co]]) -> _T_co:
|
|
186
|
+
return reduce(bitwise_or, __iterable)
|
|
152
187
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
188
|
+
|
|
189
|
+
_GenericAlias = type(Type[...]) | types.GenericAlias
|
|
190
|
+
_UnionGenericType = type(Union[..., None])
|
|
191
|
+
_LiteralGenericType = type(Literal[''])
|
|
192
|
+
_CallableGenericType = type(Callable[[], ...]) | type(ABC_Callable[[], ...])
|
|
193
|
+
_CallableType = type(Callable) | ABC_Callable
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class _BoundedDict[_KT, _VT](OrderedDict[_KT, _VT]):
|
|
197
|
+
|
|
198
|
+
def __init__(self, *, maxsize: Optional[int] = 128):
|
|
199
|
+
"""Bounded OrderedDict, mimics FIFO behavior of `functools.lru_cache`"""
|
|
200
|
+
super().__init__()
|
|
201
|
+
if maxsize is not None:
|
|
202
|
+
maxsize = max(maxsize, 0)
|
|
203
|
+
|
|
204
|
+
@wraps(self.__setitem__)
|
|
205
|
+
def _fifo(_, key, value):
|
|
206
|
+
if maxsize <= len(self):
|
|
207
|
+
self.popitem(last=True)
|
|
208
|
+
super(_BoundedDict, self).__setitem__(key, value)
|
|
209
|
+
|
|
210
|
+
self.__setitem__ = _fifo
|
|
211
|
+
|
|
212
|
+
__repr__ = dict.__repr__
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
_SUBTYPE_CACHE: _BoundedDict[int, ...] = _BoundedDict()
|
|
216
|
+
_ATTR_GETTERS: _BoundedDict[..., tuple[Callable[[Iterable], NamedTuple], attrgetter]] = (
|
|
217
|
+
_BoundedDict()
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _unique_attrs(obj) -> Optional['NamedTuple']:
|
|
222
|
+
tp = type(obj)
|
|
223
|
+
tp_name: str = tp.__name__
|
|
224
|
+
if tp is type:
|
|
225
|
+
return
|
|
226
|
+
elif tp in _ATTR_GETTERS:
|
|
227
|
+
constructor, getter = _ATTR_GETTERS[tp]
|
|
228
|
+
return constructor(getter(obj))
|
|
229
|
+
ignored_attrs = frozenset({'__module__', '__slots__'})
|
|
230
|
+
attr_names = sorted(
|
|
231
|
+
s
|
|
232
|
+
for t in tp.mro()[:-1]
|
|
233
|
+
for s in set(dir(t)).difference(ignored_attrs, *map(dir, t.mro()[1:]))
|
|
234
|
+
if not callable(getattr(tp, s, None))
|
|
162
235
|
)
|
|
163
|
-
if
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
236
|
+
if '__dict__' in attr_names:
|
|
237
|
+
attr_names = sorted(cast(dict[str, ...], obj.__dict__).keys() - ignored_attrs)
|
|
238
|
+
if not attr_names:
|
|
239
|
+
return
|
|
240
|
+
attr_names, field_names = _sort_attrs(obj, tp_name, attr_names)
|
|
241
|
+
tup_name = (
|
|
242
|
+
re.sub(
|
|
243
|
+
r'\b[a-z0-9_]+\b',
|
|
244
|
+
lambda m: ''.join(s.capitalize() for s in m.group(0).split('_')),
|
|
245
|
+
tp_name.strip(),
|
|
246
|
+
)
|
|
247
|
+
+ 'Attrs'
|
|
248
|
+
).strip('_')
|
|
249
|
+
UniqueAttrs = cast(NamedTuple, namedtuple(tup_name, field_names))
|
|
250
|
+
_ATTR_GETTERS[tp] = constructor, getter = UniqueAttrs._make, attrgetter(*attr_names) # noqa
|
|
251
|
+
return constructor(getter(obj))
|
|
172
252
|
|
|
173
253
|
|
|
174
|
-
def
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
254
|
+
def _sort_attrs(obj, tp_name, attr_names):
|
|
255
|
+
field_names = [name.strip('_') for name in attr_names]
|
|
256
|
+
inf = float('inf')
|
|
257
|
+
try:
|
|
258
|
+
sig = inspect.signature(type(obj))
|
|
259
|
+
indices = (
|
|
260
|
+
dict.fromkeys(field_names, inf) | {p: i for i, p in enumerate(sig.parameters)}
|
|
261
|
+
).values()
|
|
262
|
+
for names in (attr_names, field_names):
|
|
263
|
+
names.sort(key=dict(zip(names, indices)).__getitem__)
|
|
264
|
+
return attr_names, field_names
|
|
265
|
+
except ValueError:
|
|
266
|
+
maybe_sigs: set[str | None] = set()
|
|
267
|
+
text_signature = '__text_signature__'
|
|
268
|
+
if text_signature in attr_names:
|
|
269
|
+
attr_names.remove(text_signature)
|
|
270
|
+
field_names.remove(text_signature.strip('_'))
|
|
271
|
+
maybe_sigs.add(getattr(obj, text_signature, None))
|
|
272
|
+
elif doc := getattr(obj, '__doc__', None):
|
|
273
|
+
lines = iter(inspect.cleandoc(doc).splitlines(keepends=True))
|
|
274
|
+
no_square_parens = {ord(c): None for c in '[]'}
|
|
275
|
+
sig_start = tp_name + '('
|
|
276
|
+
while True:
|
|
277
|
+
try:
|
|
278
|
+
line = next(lines)
|
|
279
|
+
while sig_start not in line:
|
|
280
|
+
line = next(lines)
|
|
281
|
+
_, _, params = line.partition(sig_start)
|
|
282
|
+
params, _, _ = (s.translate(no_square_parens) for s in params.partition(')'))
|
|
283
|
+
maybe_sigs.add(params)
|
|
284
|
+
except StopIteration:
|
|
285
|
+
break
|
|
286
|
+
maybe_sigs.discard(None)
|
|
287
|
+
if maybe_sigs:
|
|
288
|
+
if len(maybe_sigs) > 1:
|
|
289
|
+
sig = max(
|
|
290
|
+
maybe_sigs, key=lambda s: sum(1 for sub in s.split(', ') if sub in field_names)
|
|
186
291
|
)
|
|
187
|
-
|
|
292
|
+
else:
|
|
293
|
+
sig = maybe_sigs.pop()
|
|
294
|
+
positions = {x: i for i, x in enumerate(sig.split(', ')) if x}
|
|
295
|
+
sorted_field_names = sorted(field_names, key=lambda k: positions.get(k, inf))
|
|
296
|
+
transitions = {idx: sorted_field_names.index(x) for idx, x in enumerate(field_names)}
|
|
297
|
+
field_names = sorted_field_names
|
|
298
|
+
attr_names = [attr_names[k] for k in map(transitions.__getitem__, range(len(attr_names)))]
|
|
299
|
+
for Names in (attr_names, field_names):
|
|
300
|
+
name_attr = next((s for s in Names if s.strip('_') == 'name'), None)
|
|
301
|
+
if name_attr is not None:
|
|
302
|
+
Names.sort(key=name_attr.__eq__, reverse=True)
|
|
303
|
+
return attr_names, field_names
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def subtype[_T](typ: _T) -> _T:
|
|
307
|
+
type TypeVarDict = dict[TypeVar, ...]
|
|
308
|
+
|
|
309
|
+
def _serialize(__item: tuple[Any, Hashable]):
|
|
310
|
+
key, value = __item
|
|
311
|
+
return _unique_attrs(key), hash(value)
|
|
312
|
+
|
|
313
|
+
def _cache_key(tp, tvars: TypeVarDict):
|
|
314
|
+
if tvars:
|
|
315
|
+
value = tp, *sorted(tvars.items(), key=_serialize)
|
|
316
|
+
else:
|
|
317
|
+
value = tp
|
|
318
|
+
return value
|
|
319
|
+
|
|
320
|
+
def _inner(tp: ..., tvars: TypeVarDict):
|
|
321
|
+
key = _cache_key(tp, tvars)
|
|
322
|
+
recursive = lambda x: _inner(x, tvars)
|
|
323
|
+
try:
|
|
324
|
+
if key in _SUBTYPE_CACHE:
|
|
325
|
+
return _SUBTYPE_CACHE[key]
|
|
326
|
+
elif tp in tvars:
|
|
327
|
+
return tvars[tp]
|
|
328
|
+
except TypeError:
|
|
329
|
+
pass
|
|
330
|
+
if isinstance(tp, (_UnionGenericType, types.UnionType)):
|
|
331
|
+
args = tp.__args__
|
|
332
|
+
args_list = list(args)
|
|
333
|
+
if (
|
|
334
|
+
literals := [
|
|
335
|
+
idx for idx, elem in enumerate(args) if isinstance(elem, _LiteralGenericType)
|
|
336
|
+
]
|
|
337
|
+
) and len(literals) > 1:
|
|
338
|
+
|
|
339
|
+
def _next_args(__index: int):
|
|
340
|
+
idx = args_list.index(args[__index])
|
|
341
|
+
value = args_list.pop(idx)
|
|
342
|
+
return getattr(value, '__args__', ())
|
|
343
|
+
|
|
344
|
+
start = args[literals.pop(0)]
|
|
345
|
+
args_list[args_list.index(start)] = Literal[
|
|
346
|
+
*start.__args__,
|
|
347
|
+
*dict.fromkeys(arg for idx in literals for arg in _next_args(idx)),
|
|
348
|
+
]
|
|
349
|
+
try:
|
|
350
|
+
return unionize(map(recursive, args_list))
|
|
351
|
+
except TypeError:
|
|
352
|
+
return Union[*map(recursive, args_list)]
|
|
353
|
+
elif isinstance(tp, TypeAliasType):
|
|
354
|
+
result = recursive(tp.__value__)
|
|
355
|
+
elif isinstance(tp, _CallableGenericType):
|
|
356
|
+
ts, rtype = get_args(tp)
|
|
357
|
+
if isinstance(ts, list):
|
|
358
|
+
ts = list(map(recursive, ts))
|
|
359
|
+
result = ABC_Callable[ts, recursive(rtype)]
|
|
360
|
+
elif isinstance(tp, _GenericAlias):
|
|
361
|
+
origin, args = cast(tuple[types.GenericAlias, tuple], deconstruct_type(tp))
|
|
362
|
+
if origin_params := dict(zip(getattr(origin, '__parameters__', ()), args)):
|
|
363
|
+
if arg_match := origin_params.keys() & tvars:
|
|
364
|
+
_union = unionize(
|
|
365
|
+
origin[*map(f, arg_match)]
|
|
366
|
+
for f in (tvars.__getitem__, origin_params.__getitem__)
|
|
367
|
+
)
|
|
368
|
+
key = _cache_key(_union, {})
|
|
369
|
+
result = _SUBTYPE_CACHE[key] = _inner(_union, {})
|
|
370
|
+
return result
|
|
371
|
+
for param, arg in origin_params.items():
|
|
372
|
+
tvars[param] = recursive(arg)
|
|
373
|
+
if isinstance(origin, TypeAliasType):
|
|
374
|
+
result = recursive(origin)
|
|
375
|
+
elif (
|
|
376
|
+
isinstance(origin, type)
|
|
377
|
+
and issubclass(origin, tuple)
|
|
378
|
+
and len(args) == 2
|
|
379
|
+
and args[-1] is Ellipsis
|
|
380
|
+
):
|
|
381
|
+
result = origin[recursive(args[0]), ...]
|
|
382
|
+
else:
|
|
383
|
+
try:
|
|
384
|
+
result = origin[*map(recursive, args)]
|
|
385
|
+
except TypeError:
|
|
386
|
+
if origin is Unpack or origin is TypeGuard:
|
|
387
|
+
result = origin[recursive(args[0])]
|
|
388
|
+
else:
|
|
389
|
+
raise
|
|
390
|
+
elif tp is Ellipsis:
|
|
391
|
+
return Any
|
|
392
|
+
elif tp is None or tp is types.NoneType:
|
|
393
|
+
return None
|
|
394
|
+
else:
|
|
395
|
+
return tp
|
|
396
|
+
_SUBTYPE_CACHE[key] = result
|
|
397
|
+
return result
|
|
398
|
+
|
|
399
|
+
return _inner(typ, {})
|
chromatic/_version.py
CHANGED