chromatic-python 0.1.0__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 +19 -3
- chromatic/_typing.py +304 -73
- chromatic/_version.py +16 -0
- chromatic/ascii/_array.py +466 -318
- chromatic/ascii/_curses.py +46 -55
- chromatic/ascii/_glyph_proc.py +28 -43
- chromatic/color/colorconv.py +32 -35
- chromatic/color/core.py +273 -258
- chromatic/color/core.pyi +350 -421
- chromatic/color/palette.py +197 -211
- chromatic/color/palette.pyi +54 -45
- chromatic/data/__init__.py +46 -37
- chromatic/demo.py +73 -121
- chromatic_python-0.2.0.dist-info/METADATA +147 -0
- chromatic_python-0.2.0.dist-info/RECORD +27 -0
- {chromatic_python-0.1.0.dist-info → chromatic_python-0.2.0.dist-info}/WHEEL +1 -1
- chromatic_python-0.1.0.dist-info/METADATA +0 -39
- chromatic_python-0.1.0.dist-info/RECORD +0 -26
- {chromatic_python-0.1.0.dist-info → chromatic_python-0.2.0.dist-info}/LICENSE +0 -0
- {chromatic_python-0.1.0.dist-info → chromatic_python-0.2.0.dist-info}/top_level.txt +0 -0
chromatic/__init__.py
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from ._version import version as __version__
|
|
3
|
+
except ImportError:
|
|
4
|
+
__version__ = "0.0.0"
|
|
5
|
+
|
|
1
6
|
from . import ascii, color, data
|
|
2
7
|
from .ascii import (
|
|
8
|
+
AnsiImage,
|
|
3
9
|
ansi2img,
|
|
4
10
|
ansi_quantize,
|
|
5
11
|
ascii2img,
|
|
@@ -14,12 +20,22 @@ from .ascii import (
|
|
|
14
20
|
read_ans,
|
|
15
21
|
render_ans,
|
|
16
22
|
reshape_ansi,
|
|
17
|
-
to_sgr_array
|
|
23
|
+
to_sgr_array,
|
|
18
24
|
)
|
|
19
25
|
from .ascii._glyph_proc import get_glyph_masks
|
|
20
26
|
from .color import (
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
Back,
|
|
28
|
+
Color,
|
|
29
|
+
ColorNamespace,
|
|
30
|
+
ColorStr,
|
|
31
|
+
Fore,
|
|
32
|
+
SgrParameter,
|
|
33
|
+
Style,
|
|
34
|
+
ansicolor24Bit,
|
|
35
|
+
ansicolor4Bit,
|
|
36
|
+
ansicolor8Bit,
|
|
37
|
+
colorbytes,
|
|
38
|
+
named_color,
|
|
23
39
|
)
|
|
24
40
|
from .data import register_user_font
|
|
25
41
|
|
chromatic/_typing.py
CHANGED
|
@@ -1,18 +1,44 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
5
9
|
from numbers import Number
|
|
6
|
-
from
|
|
10
|
+
from operator import attrgetter, or_ as bitwise_or
|
|
7
11
|
from typing import (
|
|
8
|
-
Any,
|
|
9
|
-
|
|
12
|
+
Any,
|
|
13
|
+
Callable,
|
|
14
|
+
Concatenate,
|
|
15
|
+
Hashable,
|
|
16
|
+
Iterable,
|
|
17
|
+
Literal,
|
|
18
|
+
NamedTuple,
|
|
19
|
+
Optional,
|
|
20
|
+
ParamSpec,
|
|
21
|
+
Protocol,
|
|
22
|
+
Sequence,
|
|
23
|
+
Type,
|
|
24
|
+
TypeAlias,
|
|
25
|
+
TypeAliasType,
|
|
26
|
+
TypeGuard,
|
|
27
|
+
TypeVar,
|
|
28
|
+
TypedDict,
|
|
29
|
+
Union,
|
|
30
|
+
Unpack,
|
|
31
|
+
cast,
|
|
32
|
+
get_args,
|
|
33
|
+
get_origin,
|
|
34
|
+
get_type_hints,
|
|
35
|
+
runtime_checkable,
|
|
10
36
|
)
|
|
11
37
|
|
|
12
|
-
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
13
|
-
from numpy._typing import _ArrayLike, NDArray
|
|
14
38
|
from PIL.Image import Image
|
|
15
39
|
from PIL.ImageFont import FreeTypeFont
|
|
40
|
+
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
41
|
+
from numpy._typing import NDArray, _ArrayLike
|
|
16
42
|
|
|
17
43
|
from chromatic.data import UserFont
|
|
18
44
|
|
|
@@ -22,44 +48,50 @@ _T_co = TypeVar('_T_co', covariant=True)
|
|
|
22
48
|
_T_contra = TypeVar('_T_contra', contravariant=True)
|
|
23
49
|
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
24
50
|
|
|
25
|
-
if TYPE_CHECKING:
|
|
26
|
-
from _typeshed import SupportsRichComparison, SupportsDivMod
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class SupportsRoundAndDivMod(
|
|
30
|
-
SupportsRound[_T_co],
|
|
31
|
-
SupportsDivMod[Any, _T_co],
|
|
32
|
-
Protocol
|
|
33
|
-
):
|
|
34
|
-
...
|
|
35
|
-
|
|
36
51
|
type ArrayReducerFunc[_SCT: generic] = Callable[Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]]
|
|
37
|
-
type KeyFunction[_T] = Callable[[_T], SupportsRichComparison]
|
|
38
52
|
type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[_Shape, dtype[_SCT]]
|
|
39
53
|
type MatrixLike[_SCT: generic] = ShapedNDArray[TupleOf2[int], _SCT]
|
|
40
54
|
type SquareMatrix[_I: int, _SCT: generic] = ShapedNDArray[TupleOf2[_I], _SCT]
|
|
41
55
|
type GlyphArray[_SCT: generic] = SquareMatrix[Literal[24], _SCT]
|
|
42
56
|
type TupleOf2[_T] = tuple[_T, _T]
|
|
43
57
|
type TupleOf3[_T] = tuple[_T, _T, _T]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
57
72
|
ColorDictKeys = Literal['fg', 'bg']
|
|
58
73
|
Ansi4BitAlias = Literal['4b']
|
|
59
74
|
Ansi8BitAlias = Literal['8b']
|
|
60
75
|
Ansi24BitAlias = Literal['24b']
|
|
61
76
|
AnsiColorAlias = Ansi4BitAlias | Ansi8BitAlias | Ansi24BitAlias
|
|
62
|
-
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"
|
|
63
95
|
|
|
64
96
|
|
|
65
97
|
def is_matching_type(value, typ):
|
|
@@ -72,8 +104,7 @@ def is_matching_type(value, typ):
|
|
|
72
104
|
return value in args
|
|
73
105
|
elif isinstance(typ, TypeVar):
|
|
74
106
|
if typ.__constraints__:
|
|
75
|
-
return any(
|
|
76
|
-
is_matching_type(value, constraint) for constraint in typ.__constraints__)
|
|
107
|
+
return any(is_matching_type(value, constraint) for constraint in typ.__constraints__)
|
|
77
108
|
else:
|
|
78
109
|
return True
|
|
79
110
|
elif origin is type:
|
|
@@ -101,8 +132,9 @@ def is_matching_type(value, typ):
|
|
|
101
132
|
return True
|
|
102
133
|
key_type, val_type = args
|
|
103
134
|
return all(
|
|
104
|
-
is_matching_type(k, key_type) and is_matching_type(v, val_type)
|
|
105
|
-
value.items()
|
|
135
|
+
is_matching_type(k, key_type) and is_matching_type(v, val_type)
|
|
136
|
+
for k, v in value.items()
|
|
137
|
+
)
|
|
106
138
|
elif origin is tuple:
|
|
107
139
|
if not isinstance(value, tuple):
|
|
108
140
|
return False
|
|
@@ -118,51 +150,250 @@ def is_matching_type(value, typ):
|
|
|
118
150
|
return False
|
|
119
151
|
|
|
120
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
|
+
|
|
121
173
|
def deconstruct_type(tp):
|
|
122
174
|
origin = get_origin(tp) or tp
|
|
123
175
|
args = get_args(tp)
|
|
124
176
|
return origin, args
|
|
125
177
|
|
|
126
178
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return False
|
|
130
|
-
return id(value) == id(expected_type)
|
|
179
|
+
@runtime_checkable
|
|
180
|
+
class SupportsUnion(Protocol[_T_contra, _T_co]):
|
|
131
181
|
|
|
182
|
+
def __or__(self, x: _T_contra, /) -> _T_co: ...
|
|
132
183
|
|
|
133
|
-
def pseudo_union(ts: Iterable[type]) -> Union[type, UnionType]:
|
|
134
|
-
return reduce(lambda x, y: x | y, ts)
|
|
135
184
|
|
|
185
|
+
def unionize(__iterable: Iterable[SupportsUnion[_T_contra, _T_co]]) -> _T_co:
|
|
186
|
+
return reduce(bitwise_or, __iterable)
|
|
136
187
|
|
|
137
|
-
def type_error_msg(err_obj, *expected, context: str = '', obj_repr=False):
|
|
138
|
-
n_expected = len(expected)
|
|
139
|
-
name_slots = [f"{{{n}.__qualname__!r}}" for n in range(n_expected)]
|
|
140
|
-
if name_slots and n_expected > 1:
|
|
141
|
-
name_slots[-1] = f"or {name_slots[-1]}"
|
|
142
|
-
names = (', ' if n_expected > 2 else ' ').join(
|
|
143
|
-
[context.strip()] + name_slots).format(*expected)
|
|
144
|
-
if not obj_repr:
|
|
145
|
-
if not isinstance(err_obj, type):
|
|
146
|
-
err_obj = type(err_obj)
|
|
147
|
-
oops = repr(err_obj.__qualname__)
|
|
148
|
-
elif not isinstance(err_obj, str):
|
|
149
|
-
oops = repr(err_obj)
|
|
150
|
-
else:
|
|
151
|
-
oops = err_obj
|
|
152
|
-
return f"expected {names}, got {oops} instead"
|
|
153
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
|
|
154
194
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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))
|
|
235
|
+
)
|
|
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))
|
|
252
|
+
|
|
253
|
+
|
|
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)
|
|
291
|
+
)
|
|
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
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.2.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 2, 0)
|