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 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[Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]]
52
- type ShapedNDArray[_Shape: tuple[int, ...], _SCT: generic] = ndarray[_Shape, dtype[_SCT]]
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 = [f"{{{n}.__qualname__!r}}" for n in range(n_expected)]
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 = (', ' if n_expected > 2 else ' ').join([context.strip()] + name_slots).format(*expected)
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(is_matching_type(value, constraint) for constraint in typ.__constraints__)
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(field, typ, context=f'keyword argument {name!r} of type')
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(bitwise_or, __iterable)
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[..., tuple[Callable[[Iterable], NamedTuple], attrgetter]] = (
217
- _BoundedDict()
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 = UniqueAttrs._make, attrgetter(*attr_names) # noqa
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) | {p: i for i, p in enumerate(sig.parameters)}
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, _, _ = (s.translate(no_square_parens) for s in params.partition(')'))
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, key=lambda s: sum(1 for sub in s.split(', ') if sub in field_names)
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 = {idx: sorted_field_names.index(x) for idx, x in enumerate(field_names)}
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 = [attr_names[k] for k in map(transitions.__getitem__, range(len(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 for idx, elem in enumerate(args) if isinstance(elem, _LiteralGenericType)
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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.1'
21
- __version_tuple__ = version_tuple = (0, 2, 1)
20
+ __version__ = version = '0.2.3'
21
+ __version_tuple__ = version_tuple = (0, 2, 3)