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
chromatic/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from . import ascii, color, data
|
|
2
|
+
from .ascii import (
|
|
3
|
+
ansi2img,
|
|
4
|
+
ansi_quantize,
|
|
5
|
+
ascii2img,
|
|
6
|
+
ascii_printable,
|
|
7
|
+
contrast_stretch,
|
|
8
|
+
cp437_printable,
|
|
9
|
+
equalize_white_point,
|
|
10
|
+
get_font_key,
|
|
11
|
+
get_font_object,
|
|
12
|
+
img2ansi,
|
|
13
|
+
img2ascii,
|
|
14
|
+
read_ans,
|
|
15
|
+
render_ans,
|
|
16
|
+
reshape_ansi,
|
|
17
|
+
to_sgr_array
|
|
18
|
+
)
|
|
19
|
+
from .ascii._glyph_proc import get_glyph_masks
|
|
20
|
+
from .color import (
|
|
21
|
+
ansicolor24Bit, ansicolor4Bit, ansicolor8Bit, Back, Color, colorbytes, ColorStr, Fore,
|
|
22
|
+
SgrParameter, Style
|
|
23
|
+
)
|
|
24
|
+
from .data import register_user_font
|
|
25
|
+
|
|
26
|
+
__all__ = []
|
chromatic/_typing.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from functools import reduce
|
|
5
|
+
from numbers import Number
|
|
6
|
+
from types import UnionType
|
|
7
|
+
from typing import (
|
|
8
|
+
Any, Callable, Concatenate, get_args, get_origin, get_type_hints, Iterable, Literal, ParamSpec,
|
|
9
|
+
Protocol, Sequence, SupportsRound, TYPE_CHECKING, TypeVar, Union
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
13
|
+
from numpy._typing import _ArrayLike, NDArray
|
|
14
|
+
from PIL.Image import Image
|
|
15
|
+
from PIL.ImageFont import FreeTypeFont
|
|
16
|
+
|
|
17
|
+
from chromatic.data import UserFont
|
|
18
|
+
|
|
19
|
+
_P = ParamSpec('_P')
|
|
20
|
+
_T = TypeVar('_T')
|
|
21
|
+
_T_co = TypeVar('_T_co', covariant=True)
|
|
22
|
+
_T_contra = TypeVar('_T_contra', contravariant=True)
|
|
23
|
+
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
24
|
+
|
|
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
|
+
type ArrayReducerFunc[_SCT: generic] = Callable[Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]]
|
|
37
|
+
type KeyFunction[_T] = Callable[[_T], SupportsRichComparison]
|
|
38
|
+
type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[_Shape, dtype[_SCT]]
|
|
39
|
+
type MatrixLike[_SCT: generic] = ShapedNDArray[TupleOf2[int], _SCT]
|
|
40
|
+
type SquareMatrix[_I: int, _SCT: generic] = ShapedNDArray[TupleOf2[_I], _SCT]
|
|
41
|
+
type GlyphArray[_SCT: generic] = SquareMatrix[Literal[24], _SCT]
|
|
42
|
+
type TupleOf2[_T] = tuple[_T, _T]
|
|
43
|
+
type TupleOf3[_T] = tuple[_T, _T, _T]
|
|
44
|
+
Float3Tuple = TupleOf3[float]
|
|
45
|
+
Int3Tuple = TupleOf3[int]
|
|
46
|
+
FloatSequence = Sequence[float]
|
|
47
|
+
IntSequence = Sequence[int]
|
|
48
|
+
GlyphBitmask = GlyphArray[bool]
|
|
49
|
+
Bitmask = MatrixLike[bool]
|
|
50
|
+
GreyscaleGlyphArray = GlyphArray[float64]
|
|
51
|
+
GreyscaleArray = MatrixLike[float64]
|
|
52
|
+
RGBArray = ShapedNDArray[tuple[int, int, Literal[3]], uint8]
|
|
53
|
+
RGBPixel = ShapedNDArray[tuple[Literal[3]], uint8]
|
|
54
|
+
|
|
55
|
+
RGBImageLike = Union[Image, RGBArray]
|
|
56
|
+
RGBVectorLike = Union[Int3Tuple, IntSequence, RGBPixel]
|
|
57
|
+
ColorDictKeys = Literal['fg', 'bg']
|
|
58
|
+
Ansi4BitAlias = Literal['4b']
|
|
59
|
+
Ansi8BitAlias = Literal['8b']
|
|
60
|
+
Ansi24BitAlias = Literal['24b']
|
|
61
|
+
AnsiColorAlias = Ansi4BitAlias | Ansi8BitAlias | Ansi24BitAlias
|
|
62
|
+
FontArgType = Union[FreeTypeFont | UserFont, str, int]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def is_matching_type(value, typ):
|
|
66
|
+
if typ is Any:
|
|
67
|
+
return True
|
|
68
|
+
origin, args = deconstruct_type(typ)
|
|
69
|
+
if origin is Union:
|
|
70
|
+
return any(is_matching_type(value, arg) for arg in args)
|
|
71
|
+
elif origin is Literal:
|
|
72
|
+
return value in args
|
|
73
|
+
elif isinstance(typ, TypeVar):
|
|
74
|
+
if typ.__constraints__:
|
|
75
|
+
return any(
|
|
76
|
+
is_matching_type(value, constraint) for constraint in typ.__constraints__)
|
|
77
|
+
else:
|
|
78
|
+
return True
|
|
79
|
+
elif origin is type:
|
|
80
|
+
if not isinstance(value, type):
|
|
81
|
+
return False
|
|
82
|
+
target_type = args[0]
|
|
83
|
+
target_origin = get_origin(target_type)
|
|
84
|
+
target_args = get_args(target_type)
|
|
85
|
+
if target_origin is Union:
|
|
86
|
+
return any(issubclass(value, t) for t in target_args)
|
|
87
|
+
else:
|
|
88
|
+
return issubclass(value, target_type)
|
|
89
|
+
elif origin is Callable:
|
|
90
|
+
return is_matching_callable(value, typ)
|
|
91
|
+
elif origin is list:
|
|
92
|
+
if not isinstance(value, list):
|
|
93
|
+
return False
|
|
94
|
+
if not args:
|
|
95
|
+
return True
|
|
96
|
+
return all(is_matching_type(item, args[0]) for item in value)
|
|
97
|
+
elif origin is dict:
|
|
98
|
+
if not isinstance(value, dict):
|
|
99
|
+
return False
|
|
100
|
+
if not args:
|
|
101
|
+
return True
|
|
102
|
+
key_type, val_type = args
|
|
103
|
+
return all(
|
|
104
|
+
is_matching_type(k, key_type) and is_matching_type(v, val_type) for k, v in
|
|
105
|
+
value.items())
|
|
106
|
+
elif origin is tuple:
|
|
107
|
+
if not isinstance(value, tuple):
|
|
108
|
+
return False
|
|
109
|
+
if len(args) == 2 and args[1] is ...:
|
|
110
|
+
return all(is_matching_type(item, args[0]) for item in value)
|
|
111
|
+
if len(value) != len(args):
|
|
112
|
+
return False
|
|
113
|
+
return all(is_matching_type(v, t) for v, t in zip(value, args))
|
|
114
|
+
else:
|
|
115
|
+
try:
|
|
116
|
+
return isinstance(value, typ)
|
|
117
|
+
except TypeError:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def deconstruct_type(tp):
|
|
122
|
+
origin = get_origin(tp) or tp
|
|
123
|
+
args = get_args(tp)
|
|
124
|
+
return origin, args
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def is_matching_callable(value, expected_type):
|
|
128
|
+
if not callable(value):
|
|
129
|
+
return False
|
|
130
|
+
return id(value) == id(expected_type)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def pseudo_union(ts: Iterable[type]) -> Union[type, UnionType]:
|
|
134
|
+
return reduce(lambda x, y: x | y, ts)
|
|
135
|
+
|
|
136
|
+
|
|
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
|
+
|
|
154
|
+
|
|
155
|
+
def is_matching_typed_dict(__d: dict, typed_dict: type[dict]) -> tuple[bool, str]:
|
|
156
|
+
if not isinstance(__d, dict):
|
|
157
|
+
return False, type_error_msg(__d, dict)
|
|
158
|
+
expected = get_type_hints(typed_dict)
|
|
159
|
+
if unexpected := __d.keys() - expected:
|
|
160
|
+
return False, f"unexpected keyword arguments: {unexpected}"
|
|
161
|
+
if missing := set(getattr(typed_dict, '__required_keys__', expected)) - __d.keys():
|
|
162
|
+
return False, f"missing required keys: {missing}"
|
|
163
|
+
for name, typ in expected.items():
|
|
164
|
+
if ((field := __d.get(name)) is not None) and not is_matching_type(field, typ):
|
|
165
|
+
return False, type_error_msg(
|
|
166
|
+
field, typ,
|
|
167
|
+
context=f'keyword argument {name!r} of type')
|
|
168
|
+
return True, ''
|