chromatic-python 0.1.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 +26 -0
- chromatic/_typing.py +168 -0
- chromatic/ascii/__init__.py +5 -0
- chromatic/ascii/_array.py +1059 -0
- chromatic/ascii/_curses.py +153 -0
- chromatic/ascii/_glyph_proc.py +107 -0
- chromatic/color/__init__.py +6 -0
- chromatic/color/colorconv.py +316 -0
- chromatic/color/core.py +1677 -0
- chromatic/color/core.pyi +421 -0
- chromatic/color/palette.py +693 -0
- chromatic/color/palette.pyi +330 -0
- chromatic/data/__init__.py +189 -0
- chromatic/data/__init__.pyi +15 -0
- chromatic/data/fonts/IBM_VGA_437_8x16.ttf +0 -0
- chromatic/data/fonts/consolas.ttf +0 -0
- chromatic/data/images/butterfly.jpg +0 -0
- chromatic/data/images/escher.png +0 -0
- chromatic/data/images/goblin_virus.png +0 -0
- chromatic/data/images/hotdog.jpg +0 -0
- chromatic/demo.py +417 -0
- chromatic_python-0.1.0.dist-info/LICENSE +21 -0
- chromatic_python-0.1.0.dist-info/METADATA +39 -0
- chromatic_python-0.1.0.dist-info/RECORD +26 -0
- chromatic_python-0.1.0.dist-info/WHEEL +5 -0
- chromatic_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
from functools import lru_cache, update_wrapper, wraps
|
|
2
|
+
from inspect import getfullargspec, signature
|
|
3
|
+
from types import FunctionType, MappingProxyType
|
|
4
|
+
from typing import (
|
|
5
|
+
Callable, cast, dataclass_transform, Iterable, Iterator, Sequence, TYPE_CHECKING, TypedDict,
|
|
6
|
+
Union,
|
|
7
|
+
Unpack
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .colorconv import hex2rgb
|
|
11
|
+
from .core import (
|
|
12
|
+
AnsiColorFormat, Color, ColorStr, DEFAULT_ANSI, get_ansi_type, SgrParameter, SgrSequence
|
|
13
|
+
)
|
|
14
|
+
from .._typing import AnsiColorAlias, Int3Tuple
|
|
15
|
+
|
|
16
|
+
null = object()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Member[_T]:
|
|
20
|
+
|
|
21
|
+
def __init__(self, name, clsname, offset):
|
|
22
|
+
self.name = name
|
|
23
|
+
self.clsname = clsname
|
|
24
|
+
self.offset = offset
|
|
25
|
+
|
|
26
|
+
def __get__(self, instance, owner) -> _T:
|
|
27
|
+
if instance is None:
|
|
28
|
+
return self
|
|
29
|
+
value = instance.__members__[self.offset]
|
|
30
|
+
if value is null:
|
|
31
|
+
raise AttributeError(self.name)
|
|
32
|
+
try:
|
|
33
|
+
value.name = self.name
|
|
34
|
+
except AttributeError:
|
|
35
|
+
pass
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
def __set__(self, instance, value: _T):
|
|
39
|
+
instance.__members__[self.offset] = value
|
|
40
|
+
|
|
41
|
+
def __repr__(self):
|
|
42
|
+
return f"<{type(self).__name__} {self.name!r} of {self.clsname!r}>"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass_transform()
|
|
46
|
+
class DynamicNSMeta[_VT](type):
|
|
47
|
+
|
|
48
|
+
def __new__(mcls, clsname: str, bases: tuple[type, ...], mapping: dict[str, ...], **kwargs):
|
|
49
|
+
slot_names: dict[str, ...] = mapping.get('__annotations__', {})
|
|
50
|
+
member: Member[_VT]
|
|
51
|
+
for offset, name in enumerate(slot_names):
|
|
52
|
+
member = Member(name, clsname, offset)
|
|
53
|
+
mapping[name] = member
|
|
54
|
+
return type.__new__(mcls, clsname, bases, mapping, **kwargs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DynamicNamespace[_VT](metaclass=DynamicNSMeta[_VT]):
|
|
58
|
+
if TYPE_CHECKING:
|
|
59
|
+
__members__: list[_VT]
|
|
60
|
+
|
|
61
|
+
def __new__(cls, *args, **kwargs):
|
|
62
|
+
inst = super().__new__(cls)
|
|
63
|
+
if hasattr(cls, '__annotations__'):
|
|
64
|
+
is_member = _check_if_ns_member(cls)
|
|
65
|
+
slots = kwargs.pop('slots', list(filter(is_member, cls.__annotations__)))
|
|
66
|
+
empty_slots = [null] * len(slots)
|
|
67
|
+
object.__setattr__(inst, '__members__', empty_slots)
|
|
68
|
+
return inst
|
|
69
|
+
|
|
70
|
+
def __init__[_KT](self, **kwargs: dict[_KT, _VT]):
|
|
71
|
+
for name, value in kwargs.items():
|
|
72
|
+
self.__setattr__(name, value)
|
|
73
|
+
|
|
74
|
+
def __init_subclass__(cls, **kwargs):
|
|
75
|
+
if DynamicNamespace in cls.__bases__:
|
|
76
|
+
return super().__new__(cls)
|
|
77
|
+
factory: Callable[[...], _VT] | FunctionType = kwargs.get('factory')
|
|
78
|
+
if not callable(factory):
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"{cls.__name__!r} does not inherit {DynamicNamespace.__name__!r} as a "
|
|
81
|
+
f"base class "
|
|
82
|
+
f"and does not provide callable 'factory' keyword argument")
|
|
83
|
+
base: type[DynamicNamespace] = cast(
|
|
84
|
+
type[...], next(
|
|
85
|
+
(typ for typ in cls.mro()
|
|
86
|
+
if DynamicNamespace in typ.__bases__), null))
|
|
87
|
+
if base is null:
|
|
88
|
+
raise TypeError(
|
|
89
|
+
f"{cls.__qualname__!r} does not have any base classes of "
|
|
90
|
+
f"type {DynamicNamespace.__qualname__!r}") from None
|
|
91
|
+
d = dict(zip(base.__annotations__, map(factory, base().__members__)))
|
|
92
|
+
cls.__annotations__: dict[str, ...] = {k: type(v) for k, v in d.items()}
|
|
93
|
+
new = cls.__new__
|
|
94
|
+
|
|
95
|
+
@wraps(cls.__new__)
|
|
96
|
+
def wrapped_new(*args, **kw):
|
|
97
|
+
return new(*args, **(kw | dict(slots=d)))
|
|
98
|
+
|
|
99
|
+
@wraps(cls.__init__)
|
|
100
|
+
def wrapped_init(*args, **kw):
|
|
101
|
+
return DynamicNamespace.__init__(*args, **(kw | d))
|
|
102
|
+
|
|
103
|
+
cls.__new__ = wrapped_new
|
|
104
|
+
cls.__init__ = wrapped_init
|
|
105
|
+
return super().__new__(cls)
|
|
106
|
+
|
|
107
|
+
def __setattr__(self, name, value):
|
|
108
|
+
cls = type(self)
|
|
109
|
+
if hasattr(cls, '__annotations__') and name not in cls.__annotations__:
|
|
110
|
+
raise AttributeError(
|
|
111
|
+
f'{cls.__name__!r} object has no attribute {name!r}') from None
|
|
112
|
+
super().__setattr__(name, value)
|
|
113
|
+
|
|
114
|
+
def as_dict(self):
|
|
115
|
+
cls = type(self)
|
|
116
|
+
return dict(zip(cls.__annotations__, self.__members__))
|
|
117
|
+
|
|
118
|
+
def __iter__(self):
|
|
119
|
+
return iter(self.__members__)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _check_if_ns_member(cls: type) -> Callable[[str], bool]:
|
|
123
|
+
type_params = cls.__type_params__
|
|
124
|
+
if type_params and len(type_params) == 1:
|
|
125
|
+
anno_dict = cls.__annotations__
|
|
126
|
+
member_type = type_params[0]
|
|
127
|
+
|
|
128
|
+
def f(x: str):
|
|
129
|
+
return member_type == anno_dict.get(x)
|
|
130
|
+
|
|
131
|
+
return f
|
|
132
|
+
else:
|
|
133
|
+
return lambda _: False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _ns_from_iter[_KT, _VT](
|
|
137
|
+
__iter: Iterator[_KT] | Callable[[], Iterator[_KT]], member_type: _VT = null
|
|
138
|
+
) -> Callable[[type[DynamicNamespace[_VT]]], type[DynamicNamespace[_VT]]]:
|
|
139
|
+
def decorator(cls: type[DynamicNamespace[_VT]]):
|
|
140
|
+
anno = cls.__annotations__
|
|
141
|
+
type_params = cls.__type_params__
|
|
142
|
+
m_iter = __iter() if callable(__iter) else iter(__iter)
|
|
143
|
+
members: Iterator[_KT] = m_iter if member_type == null else map(member_type, m_iter)
|
|
144
|
+
d = dict(zip((k for k, v in anno.items() if v in type_params), members))
|
|
145
|
+
|
|
146
|
+
@wraps(cls.__init__)
|
|
147
|
+
def wrapped(*args, **kwargs):
|
|
148
|
+
return cls.__base__.__init__(*args, **(kwargs | d))
|
|
149
|
+
|
|
150
|
+
cls.__init__ = wrapped
|
|
151
|
+
return cls
|
|
152
|
+
|
|
153
|
+
return decorator
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _gen_named_color_values() -> Iterator[int]:
|
|
157
|
+
yield from [
|
|
158
|
+
0x000000, 0x696969, 0x808080, 0xa9a9a9, 0xc0c0c0, 0xd3d3d3, 0xf5f5f5, 0xffffff, 0x800000,
|
|
159
|
+
0x8b0000, 0xff0000, 0xb22222, 0xa52a2a, 0xcd5c5c, 0xf08080, 0xbc8f8f, 0xffe4e1, 0xfffafa,
|
|
160
|
+
0xa0522d, 0xff4500, 0xff6347, 0xea7e5d, 0xff7f50, 0xfa8072, 0xe9967a, 0xffa07a, 0xfff5ee,
|
|
161
|
+
0x8b4513, 0xd2691e, 0xcd853f, 0xf4a460, 0xffdab9, 0xfaf0e6, 0xff8c00, 0xdeb887, 0xffe4c4,
|
|
162
|
+
0xfaebd7, 0xffa500, 0xd2b48c, 0xf5deb3, 0xffdead, 0xffe4b5, 0xffebcd, 0xffefd5, 0xfdf5e6,
|
|
163
|
+
0xfffaf0, 0xb8860b, 0xdaa520, 0xfff8dc, 0xbdb76b, 0xffd700, 0xf0e68c, 0xeee8aa, 0xf5f5dc,
|
|
164
|
+
0xfafad2, 0xfffacd, 0x808000, 0xffff00, 0xffffe0, 0xfffff0, 0x006400, 0x008000, 0x556b2f,
|
|
165
|
+
0x228b22, 0x6b8e23, 0x32cd32, 0x8fbc8f, 0x00ff00, 0x9acd32, 0x7cfc00, 0x7fff00, 0x90ee90,
|
|
166
|
+
0xadff2f, 0x98fb98, 0xf0fff0, 0x2e8b57, 0x3cb371, 0x00ff7f, 0xf5fffa, 0x2f4f4f, 0x008080,
|
|
167
|
+
0x008b8b, 0x20b2aa, 0x48d1cc, 0x66cdaa, 0x40e0d0, 0x00fa9a, 0x00ffff, 0xafeeee, 0x7fffd4,
|
|
168
|
+
0xe0ffff, 0xf0ffff, 0x4682b4, 0x5f9ea0, 0x00bfff, 0x00ced1, 0x87ceeb, 0x87cefa, 0xadd8e6,
|
|
169
|
+
0xb0e0e6, 0xf0f8ff, 0x191970, 0x4169e1, 0x708090, 0x1e90ff, 0x778899, 0x6495ed, 0xb0c4de,
|
|
170
|
+
0xe6e6fa, 0x000080, 0x00008b, 0x0000cd, 0x0000ff, 0xf8f8ff, 0x4b0082, 0x9400d3, 0x483d8b,
|
|
171
|
+
0x663399, 0x8a2be2, 0x9932cc, 0x6a5acd, 0xba55d3, 0x7b68ee, 0x9370db, 0xd8bfd8, 0x800080,
|
|
172
|
+
0x8b008b, 0xc71585, 0xff00ff, 0xff1493, 0xda70d6, 0xff69b4, 0xee82ee, 0xdda0dd, 0xfff0f5,
|
|
173
|
+
0xdc143c, 0xdb7093, 0xffb6c1, 0xffc0cb]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@_ns_from_iter(_gen_named_color_values, Color)
|
|
177
|
+
class ColorNamespace[NamedColor: Color](DynamicNamespace[NamedColor]):
|
|
178
|
+
BLACK: NamedColor
|
|
179
|
+
DIM_GREY: NamedColor
|
|
180
|
+
GREY: NamedColor
|
|
181
|
+
DARK_GREY: NamedColor
|
|
182
|
+
SILVER: NamedColor
|
|
183
|
+
LIGHT_GREY: NamedColor
|
|
184
|
+
WHITE_SMOKE: NamedColor
|
|
185
|
+
WHITE: NamedColor
|
|
186
|
+
MAROON: NamedColor
|
|
187
|
+
DARK_RED: NamedColor
|
|
188
|
+
RED: NamedColor
|
|
189
|
+
FIREBRICK: NamedColor
|
|
190
|
+
BROWN: NamedColor
|
|
191
|
+
INDIAN_RED: NamedColor
|
|
192
|
+
LIGHT_CORAL: NamedColor
|
|
193
|
+
ROSY_BROWN: NamedColor
|
|
194
|
+
MISTY_ROSE: NamedColor
|
|
195
|
+
SNOW: NamedColor
|
|
196
|
+
SIENNA: NamedColor
|
|
197
|
+
ORANGE_RED: NamedColor
|
|
198
|
+
TOMATO: NamedColor
|
|
199
|
+
BURNT_SIENNA: NamedColor
|
|
200
|
+
CORAL: NamedColor
|
|
201
|
+
SALMON: NamedColor
|
|
202
|
+
DARK_SALMON: NamedColor
|
|
203
|
+
LIGHT_SALMON: NamedColor
|
|
204
|
+
SEASHELL: NamedColor
|
|
205
|
+
SADDLE_BROWN: NamedColor
|
|
206
|
+
CHOCOLATE: NamedColor
|
|
207
|
+
PERU: NamedColor
|
|
208
|
+
SANDY_BROWN: NamedColor
|
|
209
|
+
PEACH_PUFF: NamedColor
|
|
210
|
+
LINEN: NamedColor
|
|
211
|
+
DARK_ORANGE: NamedColor
|
|
212
|
+
BURLY_WOOD: NamedColor
|
|
213
|
+
BISQUE: NamedColor
|
|
214
|
+
ANTIQUE_WHITE: NamedColor
|
|
215
|
+
ORANGE: NamedColor
|
|
216
|
+
TAN: NamedColor
|
|
217
|
+
WHEAT: NamedColor
|
|
218
|
+
NAVAJO_WHITE: NamedColor
|
|
219
|
+
MOCCASIN: NamedColor
|
|
220
|
+
BLANCHED_ALMOND: NamedColor
|
|
221
|
+
PAPAYA_WHIP: NamedColor
|
|
222
|
+
OLD_LACE: NamedColor
|
|
223
|
+
FLORAL_WHITE: NamedColor
|
|
224
|
+
DARK_GOLDENROD: NamedColor
|
|
225
|
+
GOLDENROD: NamedColor
|
|
226
|
+
CORNSILK: NamedColor
|
|
227
|
+
DARK_KHAKI: NamedColor
|
|
228
|
+
GOLD: NamedColor
|
|
229
|
+
KHAKI: NamedColor
|
|
230
|
+
PALE_GOLDENROD: NamedColor
|
|
231
|
+
BEIGE: NamedColor
|
|
232
|
+
LIGHT_GOLDENROD_YELLOW: NamedColor
|
|
233
|
+
LEMON_CHIFFON: NamedColor
|
|
234
|
+
OLIVE: NamedColor
|
|
235
|
+
YELLOW: NamedColor
|
|
236
|
+
LIGHT_YELLOW: NamedColor
|
|
237
|
+
IVORY: NamedColor
|
|
238
|
+
DARK_GREEN: NamedColor
|
|
239
|
+
GREEN: NamedColor
|
|
240
|
+
DARK_OLIVE_GREEN: NamedColor
|
|
241
|
+
FOREST_GREEN: NamedColor
|
|
242
|
+
OLIVE_DRAB: NamedColor
|
|
243
|
+
LIME_GREEN: NamedColor
|
|
244
|
+
DARK_SEA_GREEN: NamedColor
|
|
245
|
+
LIME: NamedColor
|
|
246
|
+
YELLOW_GREEN: NamedColor
|
|
247
|
+
LAWN_GREEN: NamedColor
|
|
248
|
+
CHARTREUSE: NamedColor
|
|
249
|
+
LIGHT_GREEN: NamedColor
|
|
250
|
+
GREEN_YELLOW: NamedColor
|
|
251
|
+
PALE_GREEN: NamedColor
|
|
252
|
+
HONEYDEW: NamedColor
|
|
253
|
+
SEA_GREEN: NamedColor
|
|
254
|
+
MEDIUM_SEA_GREEN: NamedColor
|
|
255
|
+
SPRING_GREEN: NamedColor
|
|
256
|
+
MINT_CREAM: NamedColor
|
|
257
|
+
DARK_SLATE_GREY: NamedColor
|
|
258
|
+
TEAL: NamedColor
|
|
259
|
+
DARK_CYAN: NamedColor
|
|
260
|
+
LIGHT_SEA_GREEN: NamedColor
|
|
261
|
+
MEDIUM_TURQUOISE: NamedColor
|
|
262
|
+
MEDIUM_AQUAMARINE: NamedColor
|
|
263
|
+
TURQUOISE: NamedColor
|
|
264
|
+
MEDIUM_SPRING_GREEN: NamedColor
|
|
265
|
+
CYAN: NamedColor
|
|
266
|
+
PALE_TURQUOISE: NamedColor
|
|
267
|
+
AQUAMARINE: NamedColor
|
|
268
|
+
LIGHT_CYAN: NamedColor
|
|
269
|
+
AZURE: NamedColor
|
|
270
|
+
STEEL_BLUE: NamedColor
|
|
271
|
+
CADET_BLUE: NamedColor
|
|
272
|
+
DEEP_SKY_BLUE: NamedColor
|
|
273
|
+
DARK_TURQUOISE: NamedColor
|
|
274
|
+
SKY_BLUE: NamedColor
|
|
275
|
+
LIGHT_SKY_BLUE: NamedColor
|
|
276
|
+
LIGHT_BLUE: NamedColor
|
|
277
|
+
POWDER_BLUE: NamedColor
|
|
278
|
+
ALICE_BLUE: NamedColor
|
|
279
|
+
MIDNIGHT_BLUE: NamedColor
|
|
280
|
+
ROYAL_BLUE: NamedColor
|
|
281
|
+
SLATE_GREY: NamedColor
|
|
282
|
+
DODGER_BLUE: NamedColor
|
|
283
|
+
LIGHT_SLATE_GREY: NamedColor
|
|
284
|
+
CORNFLOWER_BLUE: NamedColor
|
|
285
|
+
LIGHT_STEEL_BLUE: NamedColor
|
|
286
|
+
LAVENDER: NamedColor
|
|
287
|
+
NAVY: NamedColor
|
|
288
|
+
DARK_BLUE: NamedColor
|
|
289
|
+
MEDIUM_BLUE: NamedColor
|
|
290
|
+
BLUE: NamedColor
|
|
291
|
+
GHOST_WHITE: NamedColor
|
|
292
|
+
INDIGO: NamedColor
|
|
293
|
+
DARK_VIOLET: NamedColor
|
|
294
|
+
DARK_SLATE_BLUE: NamedColor
|
|
295
|
+
REBECCA_PURPLE: NamedColor
|
|
296
|
+
BLUE_VIOLET: NamedColor
|
|
297
|
+
DARK_ORCHID: NamedColor
|
|
298
|
+
SLATE_BLUE: NamedColor
|
|
299
|
+
MEDIUM_ORCHID: NamedColor
|
|
300
|
+
MEDIUM_SLATE_BLUE: NamedColor
|
|
301
|
+
MEDIUM_PURPLE: NamedColor
|
|
302
|
+
THISTLE: NamedColor
|
|
303
|
+
PURPLE: NamedColor
|
|
304
|
+
DARK_MAGENTA: NamedColor
|
|
305
|
+
MEDIUM_VIOLET_RED: NamedColor
|
|
306
|
+
FUCHSIA: NamedColor
|
|
307
|
+
DEEP_PINK: NamedColor
|
|
308
|
+
ORCHID: NamedColor
|
|
309
|
+
HOT_PINK: NamedColor
|
|
310
|
+
VIOLET: NamedColor
|
|
311
|
+
PLUM: NamedColor
|
|
312
|
+
LAVENDER_BLUSH: NamedColor
|
|
313
|
+
CRIMSON: NamedColor
|
|
314
|
+
PALE_VIOLET_RED: NamedColor
|
|
315
|
+
LIGHT_PINK: NamedColor
|
|
316
|
+
PINK: NamedColor
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# noinspection PyTypedDict
|
|
320
|
+
class _ColorStrWrapperKwargs(TypedDict, total=False):
|
|
321
|
+
ansi_type: Union[AnsiColorAlias, type[AnsiColorFormat]]
|
|
322
|
+
bg: Union[Color, tuple[int, int, int], int]
|
|
323
|
+
fg: Union[Color, tuple[int, int, int], int]
|
|
324
|
+
sgr_params: Sequence[Union[int, SgrParameter]]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# noinspection PyUnresolvedReferences
|
|
328
|
+
class color_str_wrapper:
|
|
329
|
+
|
|
330
|
+
def __init__(self, **kwargs: Unpack[_ColorStrWrapperKwargs]):
|
|
331
|
+
self._rhs_ = kwargs.get('_rhs_', False)
|
|
332
|
+
self._concat_ = kwargs.get('_concat_', '')
|
|
333
|
+
if typ := kwargs.get('ansi_type'):
|
|
334
|
+
self._ansi_type_ = get_ansi_type(typ)
|
|
335
|
+
else:
|
|
336
|
+
self._ansi_type_ = DEFAULT_ANSI
|
|
337
|
+
if params := kwargs.get('sgr_params'):
|
|
338
|
+
self._sgr_ = SgrSequence(params)
|
|
339
|
+
else:
|
|
340
|
+
self._sgr_ = SgrSequence()
|
|
341
|
+
for k in kwargs.keys() & {'fg', 'bg'}:
|
|
342
|
+
if v := kwargs[k]:
|
|
343
|
+
if not isinstance(v, Iterable):
|
|
344
|
+
v = hex2rgb(v)
|
|
345
|
+
self._sgr_ += SgrSequence(self._ansi_type_.from_rgb({k: v}))
|
|
346
|
+
|
|
347
|
+
def __call__(self, __obj=None):
|
|
348
|
+
if type(self) is type(__obj):
|
|
349
|
+
new_sgr = self._sgr_ + __obj._sgr_
|
|
350
|
+
new_kwargs = {
|
|
351
|
+
'ansi_type': self._ansi_type_,
|
|
352
|
+
'_concat_': self.__dict__.get('_concat_', '').removesuffix(
|
|
353
|
+
str(self._sgr_)) + str(new_sgr),
|
|
354
|
+
'_rhs_': self.__dict__['_rhs_'],
|
|
355
|
+
**new_sgr.rgb_dict}
|
|
356
|
+
new_kwargs['sgr_params'] = [
|
|
357
|
+
int(v._value_) for v in new_sgr if not v.is_color()
|
|
358
|
+
]
|
|
359
|
+
return color_str_wrapper(**new_kwargs)
|
|
360
|
+
if isinstance(__obj, ColorStr):
|
|
361
|
+
if getattr(self, '_rhs_', False):
|
|
362
|
+
new_kwargs = {
|
|
363
|
+
'ansi_type': self._ansi_type_,
|
|
364
|
+
'sgr_params': list(self._sgr_),
|
|
365
|
+
'_concat_': (
|
|
366
|
+
getattr(self, '_concat_', '') + __obj
|
|
367
|
+
).removesuffix('[0m'),
|
|
368
|
+
'_rhs_': True,
|
|
369
|
+
**self._sgr_.rgb_dict
|
|
370
|
+
}
|
|
371
|
+
return color_str_wrapper(**new_kwargs)
|
|
372
|
+
new_params = [v for v in __obj._sgr_.values() if v not in self._sgr_.values()]
|
|
373
|
+
return ColorStr(
|
|
374
|
+
__obj.base_str,
|
|
375
|
+
color_spec=SgrSequence(new_params),
|
|
376
|
+
no_reset=__obj.no_reset,
|
|
377
|
+
ansi_type=self._ansi_type_)
|
|
378
|
+
if getattr(self, '_rhs_', False):
|
|
379
|
+
new_kwargs = {
|
|
380
|
+
'ansi_type': self._ansi_type_,
|
|
381
|
+
'sgr_params': list(self._sgr_),
|
|
382
|
+
'_concat_': (
|
|
383
|
+
getattr(self, '_concat_', '') + f"{__obj}"
|
|
384
|
+
).removesuffix('[0m'),
|
|
385
|
+
'_rhs_': True,
|
|
386
|
+
**self._sgr_.rgb_dict
|
|
387
|
+
}
|
|
388
|
+
return color_str_wrapper(**new_kwargs)
|
|
389
|
+
return ColorStr(__obj, color_spec=self._sgr_, ansi_type=self._ansi_type_)
|
|
390
|
+
|
|
391
|
+
def __add__(self, other):
|
|
392
|
+
return self.__call__(other)
|
|
393
|
+
|
|
394
|
+
def __radd__(self, other):
|
|
395
|
+
if getattr(self, '_rhs_') is False and type(other) is ColorStr:
|
|
396
|
+
setattr(self, '_rhs_', True)
|
|
397
|
+
return self.__call__(other)
|
|
398
|
+
|
|
399
|
+
def __str__(self):
|
|
400
|
+
return self.__dict__['_concat_'] + str(self._sgr_)
|
|
401
|
+
|
|
402
|
+
def __repr__(self):
|
|
403
|
+
return (f"{type(self).__name__}"
|
|
404
|
+
f"(sgr_params={self._sgr_.values()}, ansi_type={self._ansi_type_.__name__})")
|
|
405
|
+
|
|
406
|
+
def __getattr__(self, name):
|
|
407
|
+
if hasattr(str, name):
|
|
408
|
+
return getattr(self.__str__(), name)
|
|
409
|
+
raise AttributeError
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _style_wrappers():
|
|
413
|
+
yield from (
|
|
414
|
+
color_str_wrapper()
|
|
415
|
+
if x in {38, 48}
|
|
416
|
+
else color_str_wrapper(sgr_params=[x])
|
|
417
|
+
for x in SgrParameter
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@_ns_from_iter(_style_wrappers)
|
|
422
|
+
class AnsiStyle[StyleStr: color_str_wrapper](DynamicNamespace[StyleStr]):
|
|
423
|
+
RESET: StyleStr
|
|
424
|
+
BOLD: StyleStr
|
|
425
|
+
FAINT: StyleStr
|
|
426
|
+
ITALICS: StyleStr
|
|
427
|
+
SINGLE_UNDERLINE: StyleStr
|
|
428
|
+
SLOW_BLINK: StyleStr
|
|
429
|
+
RAPID_BLINK: StyleStr
|
|
430
|
+
NEGATIVE: StyleStr
|
|
431
|
+
CONCEALED_CHARS: StyleStr
|
|
432
|
+
CROSSED_OUT: StyleStr
|
|
433
|
+
PRIMARY: StyleStr
|
|
434
|
+
FIRST_ALT: StyleStr
|
|
435
|
+
SECOND_ALT: StyleStr
|
|
436
|
+
THIRD_ALT: StyleStr
|
|
437
|
+
FOURTH_ALT: StyleStr
|
|
438
|
+
FIFTH_ALT: StyleStr
|
|
439
|
+
SIXTH_ALT: StyleStr
|
|
440
|
+
SEVENTH_ALT: StyleStr
|
|
441
|
+
EIGHTH_ALT: StyleStr
|
|
442
|
+
NINTH_ALT: StyleStr
|
|
443
|
+
GOTHIC: StyleStr
|
|
444
|
+
DOUBLE_UNDERLINE: StyleStr
|
|
445
|
+
RESET_BOLD_AND_FAINT: StyleStr
|
|
446
|
+
RESET_ITALIC_AND_GOTHIC: StyleStr
|
|
447
|
+
RESET_UNDERLINES: StyleStr
|
|
448
|
+
RESET_BLINKING: StyleStr
|
|
449
|
+
POSITIVE: StyleStr
|
|
450
|
+
REVEALED_CHARS: StyleStr
|
|
451
|
+
RESET_CROSSED_OUT: StyleStr
|
|
452
|
+
BLACK_FG: StyleStr
|
|
453
|
+
RED_FG: StyleStr
|
|
454
|
+
GREEN_FG: StyleStr
|
|
455
|
+
YELLOW_FG: StyleStr
|
|
456
|
+
BLUE_FG: StyleStr
|
|
457
|
+
MAGENTA_FG: StyleStr
|
|
458
|
+
CYAN_FG: StyleStr
|
|
459
|
+
WHITE_FG: StyleStr
|
|
460
|
+
ANSI_256_SET_FG: StyleStr
|
|
461
|
+
DEFAULT_FG_COLOR: StyleStr
|
|
462
|
+
BLACK_BG: StyleStr
|
|
463
|
+
RED_BG: StyleStr
|
|
464
|
+
GREEN_BG: StyleStr
|
|
465
|
+
YELLOW_BG: StyleStr
|
|
466
|
+
BLUE_BG: StyleStr
|
|
467
|
+
MAGENTA_BG: StyleStr
|
|
468
|
+
CYAN_BG: StyleStr
|
|
469
|
+
WHITE_BG: StyleStr
|
|
470
|
+
ANSI_256_SET_BG: StyleStr
|
|
471
|
+
DEFAULT_BG_COLOR: StyleStr
|
|
472
|
+
FRAMED: StyleStr
|
|
473
|
+
ENCIRCLED: StyleStr
|
|
474
|
+
OVERLINED: StyleStr
|
|
475
|
+
NOT_FRAMED_OR_CIRCLED: StyleStr
|
|
476
|
+
IDEOGRAM_UNDER_OR_RIGHT: StyleStr
|
|
477
|
+
IDEOGRAM_2UNDER_OR_2RIGHT: StyleStr
|
|
478
|
+
IDEOGRAM_OVER_OR_LEFT: StyleStr
|
|
479
|
+
IDEOGRAM_2OVER_OR_2LEFT: StyleStr
|
|
480
|
+
CANCEL: StyleStr
|
|
481
|
+
BLACK_BRIGHT_FG: StyleStr
|
|
482
|
+
RED_BRIGHT_FG: StyleStr
|
|
483
|
+
GREEN_BRIGHT_FG: StyleStr
|
|
484
|
+
YELLOW_BRIGHT_FG: StyleStr
|
|
485
|
+
BLUE_BRIGHT_FG: StyleStr
|
|
486
|
+
MAGENTA_BRIGHT_FG: StyleStr
|
|
487
|
+
CYAN_BRIGHT_FG: StyleStr
|
|
488
|
+
WHITE_BRIGHT_FG: StyleStr
|
|
489
|
+
BLACK_BRIGHT_BG: StyleStr
|
|
490
|
+
RED_BRIGHT_BG: StyleStr
|
|
491
|
+
GREEN_BRIGHT_BG: StyleStr
|
|
492
|
+
YELLOW_BRIGHT_BG: StyleStr
|
|
493
|
+
BLUE_BRIGHT_BG: StyleStr
|
|
494
|
+
MAGENTA_BRIGHT_BG: StyleStr
|
|
495
|
+
CYAN_BRIGHT_BG: StyleStr
|
|
496
|
+
WHITE_BRIGHT_BG: StyleStr
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def _bg_wrapper_factory(__x: Color):
|
|
500
|
+
return color_str_wrapper(bg=__x, ansi_type='24b')
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _fg_wrapper_factory(__x: Color):
|
|
504
|
+
return color_str_wrapper(fg=__x, ansi_type='24b')
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class AnsiBack(ColorNamespace[color_str_wrapper], factory=_bg_wrapper_factory):
|
|
508
|
+
RESET = getattr(AnsiStyle(), 'DEFAULT_BG_COLOR')
|
|
509
|
+
|
|
510
|
+
def __call__(self, bg: Union[Color, int, tuple[int, int, int]]):
|
|
511
|
+
return color_str_wrapper(bg=bg)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
class AnsiFore(ColorNamespace[color_str_wrapper], factory=_fg_wrapper_factory):
|
|
515
|
+
RESET = getattr(AnsiStyle(), 'DEFAULT_FG_COLOR')
|
|
516
|
+
|
|
517
|
+
def __call__(self, fg: Union[Color, int, tuple[int, int, int]]):
|
|
518
|
+
return color_str_wrapper(fg=fg)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
class _color_ns_getter:
|
|
522
|
+
mapping = MappingProxyType(
|
|
523
|
+
{k.casefold(): v.rgb
|
|
524
|
+
for (k, v) in
|
|
525
|
+
ColorNamespace().as_dict().items()})
|
|
526
|
+
|
|
527
|
+
def __get__(self, instance, owner: type = None):
|
|
528
|
+
if instance:
|
|
529
|
+
return self
|
|
530
|
+
dummy = type.__new__(type, (cls_name := type(self).__name__), (), {})
|
|
531
|
+
dummy_str = f"<attr {cls_name!r} of {owner.__name__!r} objects>"
|
|
532
|
+
dummy.__str__ = lambda _: dummy_str
|
|
533
|
+
return dummy()
|
|
534
|
+
|
|
535
|
+
@staticmethod
|
|
536
|
+
@lru_cache
|
|
537
|
+
def _normalize_key(__key: str):
|
|
538
|
+
return __key.translate({0x20: 0x5f}).casefold()
|
|
539
|
+
|
|
540
|
+
def __contains__(self, __key):
|
|
541
|
+
if type(__key) is str:
|
|
542
|
+
return self._normalize_key(__key) in self.mapping
|
|
543
|
+
return False
|
|
544
|
+
|
|
545
|
+
def __getitem__(self, __key: str):
|
|
546
|
+
return self.mapping[self._normalize_key(__key)]
|
|
547
|
+
|
|
548
|
+
def __getattr__(self, __name):
|
|
549
|
+
try:
|
|
550
|
+
return getattr(self.mapping, __name)
|
|
551
|
+
except AttributeError as e:
|
|
552
|
+
raise AttributeError(
|
|
553
|
+
str(e).replace(
|
|
554
|
+
*map(
|
|
555
|
+
lambda x: type(x).__name__,
|
|
556
|
+
(self.mapping, self)))
|
|
557
|
+
) from None
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def _handle_singleton(__obj: ...):
|
|
561
|
+
return (__obj,) if isinstance(__obj, (str, int)) else __obj
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _scalar_union(s: set, value: object):
|
|
565
|
+
return s.union(_handle_singleton(value))
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
# noinspection PyUnresolvedReferences
|
|
569
|
+
class rgb_dispatch[** P, R]:
|
|
570
|
+
color_ns = cast(
|
|
571
|
+
MappingProxyType[str, Int3Tuple],
|
|
572
|
+
_color_ns_getter())
|
|
573
|
+
|
|
574
|
+
def __new__(cls, func: Callable[P, R] = None, /, *, args: Sequence[str | int] = ()):
|
|
575
|
+
args = _handle_singleton(args)
|
|
576
|
+
if func is None:
|
|
577
|
+
return lambda f, **kwargs: (
|
|
578
|
+
cls(f, args=tuple(_scalar_union(set(args), kwargs.get('args', ()))))
|
|
579
|
+
)
|
|
580
|
+
inst = super().__new__(cls)
|
|
581
|
+
setattr(inst, 'func', func)
|
|
582
|
+
getattr(inst, '_init_wrapper')(*args)
|
|
583
|
+
return inst
|
|
584
|
+
|
|
585
|
+
def _init_wrapper(
|
|
586
|
+
self,
|
|
587
|
+
*params: *tuple[str | int, ...]
|
|
588
|
+
):
|
|
589
|
+
if not callable(self.func):
|
|
590
|
+
raise ValueError
|
|
591
|
+
try:
|
|
592
|
+
argspec = getfullargspec(self.func)
|
|
593
|
+
sig = signature(self.func)
|
|
594
|
+
except TypeError:
|
|
595
|
+
if not (getattr(self.func, '__module__', '') == 'builtins'
|
|
596
|
+
or inspect.isbuiltin(self.func)):
|
|
597
|
+
raise
|
|
598
|
+
generic_spec = lambda *args, **kwargs: ...
|
|
599
|
+
argspec = getfullargspec(generic_spec)
|
|
600
|
+
sig = signature(generic_spec)
|
|
601
|
+
self.variadic = {argspec.varargs, argspec.varkw}
|
|
602
|
+
self.variadic.discard(None)
|
|
603
|
+
all_args = self.variadic.union(argspec.args + argspec.kwonlyargs)
|
|
604
|
+
self.rgb_args = all_args & {
|
|
605
|
+
*params,
|
|
606
|
+
*(v for (s, v) in zip(
|
|
607
|
+
('*' * x for x in range(1, 3)),
|
|
608
|
+
(argspec.varargs, argspec.varkw))
|
|
609
|
+
if s in params)
|
|
610
|
+
}
|
|
611
|
+
if not self.rgb_args:
|
|
612
|
+
keys = frozenset({'fg', 'bg'})
|
|
613
|
+
for arg in all_args:
|
|
614
|
+
if (arg[:2] in keys) or (arg[-2:] in keys):
|
|
615
|
+
self.rgb_args.add(arg)
|
|
616
|
+
self.variadic &= self.rgb_args
|
|
617
|
+
self.signature = sig.replace(
|
|
618
|
+
parameters=[
|
|
619
|
+
param.replace(
|
|
620
|
+
annotation=' | '.join(
|
|
621
|
+
{'str'}.union(filter(None, f"{param.annotation}".split(' | ')))))
|
|
622
|
+
if name in self.rgb_args and param.annotation is not param.empty
|
|
623
|
+
else param
|
|
624
|
+
for (name, param) in sig.parameters.items()
|
|
625
|
+
])
|
|
626
|
+
update_wrapper(self, self.func)
|
|
627
|
+
|
|
628
|
+
def __call__(
|
|
629
|
+
self,
|
|
630
|
+
*args: P.args,
|
|
631
|
+
**kwargs: P.kwargs
|
|
632
|
+
) -> R:
|
|
633
|
+
bound = self.signature.bind(*args, **kwargs)
|
|
634
|
+
bound.apply_defaults()
|
|
635
|
+
for arg, value in bound.arguments.items():
|
|
636
|
+
if arg not in self.rgb_args:
|
|
637
|
+
continue
|
|
638
|
+
if arg in self.variadic:
|
|
639
|
+
bound.arguments[arg] = (
|
|
640
|
+
tuple(
|
|
641
|
+
self.color_ns[v] if v in self.color_ns else v
|
|
642
|
+
for v in value)
|
|
643
|
+
if isinstance(value, tuple) else
|
|
644
|
+
{k: self.color_ns[v] if v in self.color_ns else v
|
|
645
|
+
for k, v in value.items()}
|
|
646
|
+
)
|
|
647
|
+
elif value in self.color_ns:
|
|
648
|
+
bound.arguments[arg] = self.color_ns[value]
|
|
649
|
+
return self.func(*bound.args, **bound.kwargs)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def display_named_colors():
|
|
653
|
+
return [ColorStr(name.replace('_', ' ').lower(), color_spec=color, ansi_type='24b')
|
|
654
|
+
for name, color in ColorNamespace().as_dict().items()]
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def display_ansi256_color_range():
|
|
658
|
+
from numpy import asarray
|
|
659
|
+
from chromatic.color.colorconv import ansi_8bit_to_rgb
|
|
660
|
+
|
|
661
|
+
ansi256_range = asarray(range(256)).reshape([16] * 2).tolist()
|
|
662
|
+
return [[ColorStr(
|
|
663
|
+
obj='###',
|
|
664
|
+
color_spec=ansi_8bit_to_rgb(v),
|
|
665
|
+
ansi_type='8b')
|
|
666
|
+
for v in arr]
|
|
667
|
+
for arr in ansi256_range]
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def __getattr__(name: ...) -> ...:
|
|
671
|
+
if name == 'Back':
|
|
672
|
+
return AnsiBack()
|
|
673
|
+
if name == 'Fore':
|
|
674
|
+
return AnsiFore()
|
|
675
|
+
if name == 'Style':
|
|
676
|
+
return AnsiStyle()
|
|
677
|
+
raise AttributeError(
|
|
678
|
+
f"Module {__name__!r} has no attribute {name!r}")
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
if TYPE_CHECKING:
|
|
682
|
+
Back: AnsiBack
|
|
683
|
+
Fore: AnsiFore
|
|
684
|
+
Style: AnsiStyle
|
|
685
|
+
|
|
686
|
+
__all__ = [
|
|
687
|
+
'Back',
|
|
688
|
+
'ColorNamespace',
|
|
689
|
+
'Fore',
|
|
690
|
+
'Style',
|
|
691
|
+
'color_str_wrapper',
|
|
692
|
+
'rgb_dispatch',
|
|
693
|
+
]
|