chromatic-python 0.2.1__py3-none-any.whl → 0.2.3__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 +73 -0
- chromatic/_typing.py +44 -18
- chromatic/_version.py +2 -2
- chromatic/ascii/_array.py +185 -81
- chromatic/ascii/_curses.py +8 -4
- chromatic/ascii/_glyph_proc.py +9 -3
- chromatic/color/colorconv.py +28 -8
- chromatic/color/core.py +261 -201
- chromatic/color/core.pyi +72 -37
- chromatic/color/iterators.py +11 -5
- chromatic/color/palette.py +77 -52
- chromatic/color/palette.pyi +6 -14
- chromatic/demo.py +46 -15
- {chromatic_python-0.2.1.dist-info → chromatic_python-0.2.3.dist-info}/METADATA +8 -8
- chromatic_python-0.2.3.dist-info/RECORD +28 -0
- {chromatic_python-0.2.1.dist-info → chromatic_python-0.2.3.dist-info}/WHEEL +1 -1
- chromatic_python-0.2.1.dist-info/RECORD +0 -28
- {chromatic_python-0.2.1.dist-info → chromatic_python-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {chromatic_python-0.2.1.dist-info → chromatic_python-0.2.3.dist-info}/top_level.txt +0 -0
chromatic/__init__.py
CHANGED
|
@@ -7,6 +7,7 @@ from . import ascii, color, data
|
|
|
7
7
|
from .ascii import (
|
|
8
8
|
AnsiImage,
|
|
9
9
|
ansi2img,
|
|
10
|
+
ansify,
|
|
10
11
|
ansi_quantize,
|
|
11
12
|
ascii2img,
|
|
12
13
|
ascii_printable,
|
|
@@ -40,3 +41,75 @@ from .color import (
|
|
|
40
41
|
from .data import register_user_font
|
|
41
42
|
|
|
42
43
|
__all__ = []
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
import os
|
|
47
|
+
import sys
|
|
48
|
+
from functools import lru_cache, wraps
|
|
49
|
+
from types import ModuleType
|
|
50
|
+
|
|
51
|
+
def find_modules(path: str):
|
|
52
|
+
from setuptools import find_packages
|
|
53
|
+
from pkgutil import iter_modules
|
|
54
|
+
|
|
55
|
+
tree: dict[str, dict | ModuleType] = {
|
|
56
|
+
__name__: {'__module__': sys.modules[__name__]}
|
|
57
|
+
}
|
|
58
|
+
children = set()
|
|
59
|
+
for pkg in find_packages(path):
|
|
60
|
+
children.add(pkg)
|
|
61
|
+
pkg_path = path + '/' + pkg.replace('.', '/')
|
|
62
|
+
if sys.version_info.major == 2 or (
|
|
63
|
+
sys.version_info.major == 3 and sys.version_info.minor < 6
|
|
64
|
+
):
|
|
65
|
+
for _, name, ispkg in iter_modules([pkg_path]):
|
|
66
|
+
if not ispkg:
|
|
67
|
+
children.add(f"{pkg}.{name}")
|
|
68
|
+
else:
|
|
69
|
+
for info in iter_modules([pkg_path]):
|
|
70
|
+
if not info.ispkg:
|
|
71
|
+
children.add(f"{pkg}.{info.name}")
|
|
72
|
+
for child in children:
|
|
73
|
+
name = f"{__name__}.{child}"
|
|
74
|
+
depth = tree
|
|
75
|
+
for node in name.split('.'):
|
|
76
|
+
if node not in depth:
|
|
77
|
+
depth[node] = {}
|
|
78
|
+
depth = depth[node]
|
|
79
|
+
if name in sys.modules:
|
|
80
|
+
depth['__module__'] = sys.modules[name]
|
|
81
|
+
return tree
|
|
82
|
+
|
|
83
|
+
def publicize_modules(modulename: str, tree: dict[str, dict | ModuleType]):
|
|
84
|
+
def is_local(obj: object):
|
|
85
|
+
if isinstance(obj, ModuleType):
|
|
86
|
+
return obj.__spec__.parent == modulename
|
|
87
|
+
elif hasattr(obj, '__module__'):
|
|
88
|
+
return obj.__module__ == modulename
|
|
89
|
+
return obj is not None
|
|
90
|
+
|
|
91
|
+
for name, subtree in tree.items():
|
|
92
|
+
if name == '__module__' and isinstance(subtree, ModuleType):
|
|
93
|
+
submodule = subtree
|
|
94
|
+
init_dir = dir(submodule)
|
|
95
|
+
|
|
96
|
+
@wraps(submodule.__dir__)
|
|
97
|
+
def wrapped():
|
|
98
|
+
s = set(
|
|
99
|
+
attr
|
|
100
|
+
for attr in init_dir
|
|
101
|
+
if is_local(getattr(submodule, attr, None))
|
|
102
|
+
)
|
|
103
|
+
if hasattr(submodule, '__all__'):
|
|
104
|
+
s.update(submodule.__all__)
|
|
105
|
+
return list(s)
|
|
106
|
+
|
|
107
|
+
sys.modules[submodule.__name__].__dir__ = wrapped
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
publicize_modules(f"{modulename}.{name}", subtree)
|
|
111
|
+
|
|
112
|
+
publicize_modules(*find_modules(os.path.split(__file__)[0]).popitem())
|
|
113
|
+
|
|
114
|
+
finally:
|
|
115
|
+
del find_modules, publicize_modules
|
chromatic/_typing.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
import operator as op
|
|
4
5
|
import re
|
|
5
6
|
import types
|
|
6
7
|
from collections import OrderedDict, namedtuple
|
|
7
8
|
from collections.abc import Callable as ABC_Callable
|
|
8
9
|
from functools import reduce, wraps
|
|
9
10
|
from numbers import Number
|
|
10
|
-
from operator import attrgetter, or_ as bitwise_or
|
|
11
11
|
from typing import (
|
|
12
12
|
Any,
|
|
13
13
|
Callable,
|
|
@@ -48,8 +48,12 @@ _T_co = TypeVar('_T_co', covariant=True)
|
|
|
48
48
|
_T_contra = TypeVar('_T_contra', contravariant=True)
|
|
49
49
|
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
50
50
|
|
|
51
|
-
type ArrayReducerFunc[_SCT: generic] = Callable[
|
|
52
|
-
|
|
51
|
+
type ArrayReducerFunc[_SCT: generic] = Callable[
|
|
52
|
+
Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]
|
|
53
|
+
]
|
|
54
|
+
type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[
|
|
55
|
+
_Shape, dtype[_SCT]
|
|
56
|
+
]
|
|
53
57
|
type MatrixLike[_SCT: generic] = ShapedNDArray[TupleOf2[int], _SCT]
|
|
54
58
|
type SquareMatrix[_I: int, _SCT: generic] = ShapedNDArray[TupleOf2[_I], _SCT]
|
|
55
59
|
type GlyphArray[_SCT: generic] = SquareMatrix[Literal[24], _SCT]
|
|
@@ -79,10 +83,14 @@ FontArgType: TypeAlias = FreeTypeFont | UserFont | str | int
|
|
|
79
83
|
|
|
80
84
|
def type_error_msg(err_obj, *expected, context: str = '', obj_repr=False):
|
|
81
85
|
n_expected = len(expected)
|
|
82
|
-
name_slots = [
|
|
86
|
+
name_slots = ['{%d.__qualname__!r}' % n for n in range(n_expected)]
|
|
83
87
|
if name_slots and n_expected > 1:
|
|
84
88
|
name_slots[-1] = f"or {name_slots[-1]}"
|
|
85
|
-
names = (
|
|
89
|
+
names = (
|
|
90
|
+
(', ' if n_expected > 2 else ' ')
|
|
91
|
+
.join([context.strip()] + name_slots)
|
|
92
|
+
.format(*expected)
|
|
93
|
+
)
|
|
86
94
|
if not obj_repr:
|
|
87
95
|
if not isinstance(err_obj, type):
|
|
88
96
|
err_obj = type(err_obj)
|
|
@@ -104,7 +112,10 @@ def is_matching_type(value, typ):
|
|
|
104
112
|
return value in args
|
|
105
113
|
elif isinstance(typ, TypeVar):
|
|
106
114
|
if typ.__constraints__:
|
|
107
|
-
return any(
|
|
115
|
+
return any(
|
|
116
|
+
is_matching_type(value, constraint)
|
|
117
|
+
for constraint in typ.__constraints__
|
|
118
|
+
)
|
|
108
119
|
else:
|
|
109
120
|
return True
|
|
110
121
|
elif origin is type:
|
|
@@ -162,7 +173,9 @@ def is_matching_typed_dict(__d: dict, typed_dict: type[dict]) -> tuple[bool, str
|
|
|
162
173
|
field = __d.get(name)
|
|
163
174
|
if field is None or is_matching_type(field, typ):
|
|
164
175
|
continue
|
|
165
|
-
return False, type_error_msg(
|
|
176
|
+
return False, type_error_msg(
|
|
177
|
+
field, typ, context=f'keyword argument {name!r} of type'
|
|
178
|
+
)
|
|
166
179
|
return True, ''
|
|
167
180
|
|
|
168
181
|
|
|
@@ -183,7 +196,7 @@ class SupportsUnion(Protocol[_T_contra, _T_co]):
|
|
|
183
196
|
|
|
184
197
|
|
|
185
198
|
def unionize(__iterable: Iterable[SupportsUnion[_T_contra, _T_co]]) -> _T_co:
|
|
186
|
-
return reduce(
|
|
199
|
+
return reduce(op.or_, __iterable)
|
|
187
200
|
|
|
188
201
|
|
|
189
202
|
_GenericAlias = type(Type[...]) | types.GenericAlias
|
|
@@ -213,9 +226,9 @@ class _BoundedDict[_KT, _VT](OrderedDict[_KT, _VT]):
|
|
|
213
226
|
|
|
214
227
|
|
|
215
228
|
_SUBTYPE_CACHE: _BoundedDict[int, ...] = _BoundedDict()
|
|
216
|
-
_ATTR_GETTERS: _BoundedDict[
|
|
217
|
-
|
|
218
|
-
)
|
|
229
|
+
_ATTR_GETTERS: _BoundedDict[
|
|
230
|
+
..., tuple[Callable[[Iterable], NamedTuple], op.attrgetter]
|
|
231
|
+
] = _BoundedDict()
|
|
219
232
|
|
|
220
233
|
|
|
221
234
|
def _unique_attrs(obj) -> Optional['NamedTuple']:
|
|
@@ -247,7 +260,10 @@ def _unique_attrs(obj) -> Optional['NamedTuple']:
|
|
|
247
260
|
+ 'Attrs'
|
|
248
261
|
).strip('_')
|
|
249
262
|
UniqueAttrs = cast(NamedTuple, namedtuple(tup_name, field_names))
|
|
250
|
-
_ATTR_GETTERS[tp] = constructor, getter =
|
|
263
|
+
_ATTR_GETTERS[tp] = [constructor, getter] = [
|
|
264
|
+
UniqueAttrs._make,
|
|
265
|
+
op.attrgetter(*attr_names), # noqa
|
|
266
|
+
]
|
|
251
267
|
return constructor(getter(obj))
|
|
252
268
|
|
|
253
269
|
|
|
@@ -257,7 +273,8 @@ def _sort_attrs(obj, tp_name, attr_names):
|
|
|
257
273
|
try:
|
|
258
274
|
sig = inspect.signature(type(obj))
|
|
259
275
|
indices = (
|
|
260
|
-
dict.fromkeys(field_names, inf)
|
|
276
|
+
dict.fromkeys(field_names, inf)
|
|
277
|
+
| {p: i for i, p in enumerate(sig.parameters)}
|
|
261
278
|
).values()
|
|
262
279
|
for names in (attr_names, field_names):
|
|
263
280
|
names.sort(key=dict(zip(names, indices)).__getitem__)
|
|
@@ -279,7 +296,9 @@ def _sort_attrs(obj, tp_name, attr_names):
|
|
|
279
296
|
while sig_start not in line:
|
|
280
297
|
line = next(lines)
|
|
281
298
|
_, _, params = line.partition(sig_start)
|
|
282
|
-
params, _, _ = (
|
|
299
|
+
params, _, _ = (
|
|
300
|
+
s.translate(no_square_parens) for s in params.partition(')')
|
|
301
|
+
)
|
|
283
302
|
maybe_sigs.add(params)
|
|
284
303
|
except StopIteration:
|
|
285
304
|
break
|
|
@@ -287,15 +306,20 @@ def _sort_attrs(obj, tp_name, attr_names):
|
|
|
287
306
|
if maybe_sigs:
|
|
288
307
|
if len(maybe_sigs) > 1:
|
|
289
308
|
sig = max(
|
|
290
|
-
maybe_sigs,
|
|
309
|
+
maybe_sigs,
|
|
310
|
+
key=lambda s: sum(1 for sub in s.split(', ') if sub in field_names),
|
|
291
311
|
)
|
|
292
312
|
else:
|
|
293
313
|
sig = maybe_sigs.pop()
|
|
294
314
|
positions = {x: i for i, x in enumerate(sig.split(', ')) if x}
|
|
295
315
|
sorted_field_names = sorted(field_names, key=lambda k: positions.get(k, inf))
|
|
296
|
-
transitions = {
|
|
316
|
+
transitions = {
|
|
317
|
+
idx: sorted_field_names.index(x) for idx, x in enumerate(field_names)
|
|
318
|
+
}
|
|
297
319
|
field_names = sorted_field_names
|
|
298
|
-
attr_names = [
|
|
320
|
+
attr_names = [
|
|
321
|
+
attr_names[k] for k in map(transitions.__getitem__, range(len(attr_names)))
|
|
322
|
+
]
|
|
299
323
|
for Names in (attr_names, field_names):
|
|
300
324
|
name_attr = next((s for s in Names if s.strip('_') == 'name'), None)
|
|
301
325
|
if name_attr is not None:
|
|
@@ -332,7 +356,9 @@ def subtype[_T](typ: _T) -> _T:
|
|
|
332
356
|
args_list = list(args)
|
|
333
357
|
if (
|
|
334
358
|
literals := [
|
|
335
|
-
idx
|
|
359
|
+
idx
|
|
360
|
+
for idx, elem in enumerate(args)
|
|
361
|
+
if isinstance(elem, _LiteralGenericType)
|
|
336
362
|
]
|
|
337
363
|
) and len(literals) > 1:
|
|
338
364
|
|
chromatic/_version.py
CHANGED