omlish 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (147) hide show
  1. omlish/__about__.py +2 -3
  2. omlish/argparse.py +8 -8
  3. omlish/asyncs/__init__.py +2 -2
  4. omlish/asyncs/anyio.py +64 -1
  5. omlish/asyncs/asyncs.py +1 -3
  6. omlish/asyncs/futures.py +16 -15
  7. omlish/c3.py +5 -5
  8. omlish/check.py +8 -8
  9. omlish/collections/__init__.py +98 -63
  10. omlish/collections/_abc.py +2 -0
  11. omlish/collections/_io_abc.py +4 -2
  12. omlish/collections/cache/__init__.py +1 -1
  13. omlish/collections/cache/descriptor.py +12 -12
  14. omlish/collections/cache/impl.py +27 -20
  15. omlish/collections/cache/types.py +1 -1
  16. omlish/collections/coerce.py +44 -44
  17. omlish/collections/frozen.py +9 -9
  18. omlish/collections/identity.py +4 -5
  19. omlish/collections/mappings.py +5 -5
  20. omlish/collections/ordered.py +8 -8
  21. omlish/collections/skiplist.py +7 -7
  22. omlish/collections/sorted.py +4 -4
  23. omlish/collections/treap.py +42 -17
  24. omlish/collections/treapmap.py +59 -7
  25. omlish/collections/unmodifiable.py +25 -24
  26. omlish/collections/utils.py +1 -1
  27. omlish/configs/flattening.py +8 -7
  28. omlish/configs/props.py +3 -3
  29. omlish/dataclasses/__init__.py +1 -1
  30. omlish/dataclasses/impl/__init__.py +18 -0
  31. omlish/dataclasses/impl/api.py +15 -24
  32. omlish/dataclasses/impl/as_.py +4 -4
  33. omlish/dataclasses/impl/exceptions.py +1 -1
  34. omlish/dataclasses/impl/fields.py +8 -8
  35. omlish/dataclasses/impl/frozen.py +2 -2
  36. omlish/dataclasses/impl/init.py +6 -6
  37. omlish/dataclasses/impl/internals.py +16 -1
  38. omlish/dataclasses/impl/main.py +4 -4
  39. omlish/dataclasses/impl/metaclass.py +2 -2
  40. omlish/dataclasses/impl/metadata.py +1 -1
  41. omlish/dataclasses/impl/order.py +2 -2
  42. omlish/dataclasses/impl/params.py +4 -38
  43. omlish/dataclasses/impl/reflect.py +1 -7
  44. omlish/dataclasses/impl/replace.py +1 -1
  45. omlish/dataclasses/impl/repr.py +24 -6
  46. omlish/dataclasses/impl/simple.py +2 -2
  47. omlish/dataclasses/impl/slots.py +2 -2
  48. omlish/dataclasses/impl/utils.py +7 -7
  49. omlish/defs.py +13 -17
  50. omlish/diag/procfs.py +334 -0
  51. omlish/diag/ps.py +47 -0
  52. omlish/{replserver → diag/replserver}/console.py +26 -28
  53. omlish/{replserver → diag/replserver}/server.py +12 -12
  54. omlish/dispatch/dispatch.py +14 -16
  55. omlish/dispatch/functions.py +1 -1
  56. omlish/dispatch/methods.py +6 -7
  57. omlish/docker.py +8 -6
  58. omlish/dynamic.py +13 -13
  59. omlish/fnpairs.py +311 -0
  60. omlish/graphs/dot/items.py +1 -1
  61. omlish/graphs/trees.py +25 -31
  62. omlish/inject/__init__.py +7 -7
  63. omlish/inject/elements.py +2 -2
  64. omlish/inject/exceptions.py +8 -8
  65. omlish/inject/impl/elements.py +4 -4
  66. omlish/inject/impl/injector.py +6 -6
  67. omlish/inject/impl/inspect.py +3 -3
  68. omlish/inject/impl/scopes.py +9 -9
  69. omlish/inject/injector.py +1 -1
  70. omlish/inject/providers.py +2 -2
  71. omlish/inject/proxy.py +5 -5
  72. omlish/iterators.py +62 -26
  73. omlish/json.py +7 -6
  74. omlish/lang/__init__.py +172 -112
  75. omlish/lang/cached.py +15 -10
  76. omlish/lang/classes/__init__.py +35 -24
  77. omlish/lang/classes/abstract.py +3 -3
  78. omlish/lang/classes/restrict.py +14 -14
  79. omlish/lang/classes/simple.py +2 -2
  80. omlish/lang/classes/virtual.py +5 -5
  81. omlish/lang/clsdct.py +2 -2
  82. omlish/lang/cmp.py +2 -2
  83. omlish/lang/contextmanagers.py +31 -25
  84. omlish/lang/datetimes.py +1 -1
  85. omlish/lang/descriptors.py +51 -6
  86. omlish/lang/exceptions.py +2 -0
  87. omlish/lang/functions.py +101 -35
  88. omlish/lang/imports.py +25 -30
  89. omlish/lang/iterables.py +2 -2
  90. omlish/lang/maybes.py +2 -1
  91. omlish/lang/objects.py +17 -11
  92. omlish/lang/resolving.py +1 -1
  93. omlish/lang/strings.py +1 -1
  94. omlish/lang/timeouts.py +53 -0
  95. omlish/lang/typing.py +5 -5
  96. omlish/libc.py +15 -11
  97. omlish/logs/_abc.py +5 -1
  98. omlish/logs/filters.py +2 -0
  99. omlish/logs/formatters.py +6 -2
  100. omlish/logs/utils.py +1 -1
  101. omlish/marshal/base.py +9 -9
  102. omlish/marshal/dataclasses.py +2 -2
  103. omlish/marshal/enums.py +2 -2
  104. omlish/marshal/exceptions.py +1 -1
  105. omlish/marshal/factories.py +10 -10
  106. omlish/marshal/global_.py +10 -4
  107. omlish/marshal/iterables.py +2 -2
  108. omlish/marshal/mappings.py +2 -2
  109. omlish/marshal/objects.py +1 -2
  110. omlish/marshal/optionals.py +4 -4
  111. omlish/marshal/polymorphism.py +4 -4
  112. omlish/marshal/registries.py +3 -3
  113. omlish/marshal/standard.py +6 -6
  114. omlish/marshal/utils.py +3 -3
  115. omlish/marshal/values.py +1 -1
  116. omlish/math.py +9 -9
  117. omlish/os.py +13 -4
  118. omlish/reflect.py +5 -15
  119. omlish/sql/__init__.py +0 -0
  120. omlish/sql/_abc.py +65 -0
  121. omlish/sql/dbs.py +90 -0
  122. omlish/stats.py +7 -8
  123. omlish/term.py +1 -1
  124. omlish/testing/pydevd.py +30 -12
  125. omlish/testing/pytest/inject/__init__.py +7 -0
  126. omlish/testing/pytest/inject/harness.py +24 -2
  127. omlish/testing/pytest/plugins/__init__.py +1 -1
  128. omlish/testing/pytest/plugins/pydevd.py +12 -0
  129. omlish/testing/pytest/plugins/switches.py +3 -3
  130. omlish/testing/testing.py +5 -5
  131. omlish/text/delimit.py +3 -6
  132. omlish/text/parts.py +3 -3
  133. omlish-0.0.0.dev3.dist-info/METADATA +31 -0
  134. omlish-0.0.0.dev3.dist-info/RECORD +191 -0
  135. {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/WHEEL +1 -1
  136. omlish/lang/classes/test/test_abstract.py +0 -89
  137. omlish/lang/classes/test/test_restrict.py +0 -71
  138. omlish/lang/classes/test/test_simple.py +0 -58
  139. omlish/lang/classes/test/test_virtual.py +0 -72
  140. omlish/testing/pytest/plugins/pycharm.py +0 -54
  141. omlish-0.0.0.dev1.dist-info/METADATA +0 -17
  142. omlish-0.0.0.dev1.dist-info/RECORD +0 -187
  143. /omlish/{lang/classes/test → diag}/__init__.py +0 -0
  144. /omlish/{replserver → diag/replserver}/__init__.py +0 -0
  145. /omlish/{replserver → diag/replserver}/__main__.py +0 -0
  146. {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/LICENSE +0 -0
  147. {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@ from .dispatch import get_impl_func_cls_set
10
10
  USE_EXTENSION = False
11
11
 
12
12
 
13
- def function(func):
13
+ def function(func): # noqa
14
14
  disp = Dispatcher() # type: ignore
15
15
  disp.register(func, [object])
16
16
 
@@ -4,6 +4,7 @@ TODO:
4
4
  - ALT: A.f(super(), ... ? :/
5
5
  - classmethod/staticmethod
6
6
  """
7
+ import contextlib
7
8
  import functools
8
9
  import typing as ta
9
10
  import weakref
@@ -16,14 +17,14 @@ from .dispatch import get_impl_func_cls_set
16
17
  T = ta.TypeVar('T')
17
18
 
18
19
 
19
- def build_mro_dct(instance_cls: type, owner_cls: ta.Optional[type] = None) -> ta.Mapping[str, ta.Any]:
20
+ def build_mro_dct(instance_cls: type, owner_cls: type | None = None) -> ta.Mapping[str, ta.Any]:
20
21
  if owner_cls is None:
21
22
  owner_cls = instance_cls
22
23
  mro = instance_cls.__mro__[-2::-1]
23
24
  try:
24
25
  pos = mro.index(owner_cls)
25
26
  except ValueError:
26
- raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}')
27
+ raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
27
28
  dct: dict[str, ta.Any] = {}
28
29
  for cur_cls in mro[:pos + 1]:
29
30
  dct.update(cur_cls.__dict__)
@@ -55,10 +56,8 @@ class Method:
55
56
  def dispatch_func_cache_remove(k, self_ref=weakref.ref(self)):
56
57
  if (ref_self := self_ref()) is not None:
57
58
  cache = ref_self._dispatch_func_cache # noqa
58
- try:
59
+ with contextlib.suppress(KeyError):
59
60
  del cache[k]
60
- except KeyError:
61
- pass
62
61
 
63
62
  self._dispatch_func_cache_remove = dispatch_func_cache_remove
64
63
 
@@ -89,7 +88,7 @@ class Method:
89
88
 
90
89
  return impl
91
90
 
92
- def build_attr_dispatcher(self, instance_cls: type, owner_cls: ta.Optional[type] = None) -> Dispatcher[str]:
91
+ def build_attr_dispatcher(self, instance_cls: type, owner_cls: type | None = None) -> Dispatcher[str]:
93
92
  disp: Dispatcher[str] = Dispatcher()
94
93
 
95
94
  mro_dct = build_mro_dct(instance_cls, owner_cls)
@@ -158,5 +157,5 @@ class Method:
158
157
  return func.__get__(instance)(*args, **kwargs) # noqa
159
158
 
160
159
 
161
- def method(func):
160
+ def method(func): # noqa
162
161
  return Method(func)
omlish/docker.py CHANGED
@@ -118,28 +118,30 @@ class ComposeConfig:
118
118
  self,
119
119
  prefix: str,
120
120
  *,
121
- compose_path: str | None = None,
121
+ file_path: str | None = None,
122
122
  ) -> None:
123
123
  super().__init__()
124
124
 
125
125
  self._prefix = prefix
126
- self._compose_path = compose_path
126
+ self._file_path = file_path
127
127
 
128
128
  @lang.cached_function
129
129
  def get_config(self) -> ta.Mapping[str, ta.Any]:
130
- with open(check.not_none(self._compose_path), 'r') as f:
130
+ with open(check.not_none(self._file_path)) as f:
131
131
  buf = f.read()
132
- dct = yaml.safe_load(buf)
132
+ return yaml.safe_load(buf)
133
133
 
134
+ @lang.cached_function
135
+ def get_services(self) -> ta.Mapping[str, ta.Any]:
134
136
  ret = {}
135
- for n, c in dct['services'].items():
137
+ for n, c in self.get_config()['services'].items():
136
138
  check.state(n.startswith(self._prefix))
137
139
  ret[n[len(self._prefix):]] = c
138
140
 
139
141
  return ret
140
142
 
141
143
 
142
- def timebomb_payload(delay_s: int | float, name: str = 'omlish-timebomb') -> str:
144
+ def timebomb_payload(delay_s: float, name: str = 'omlish-docker-timebomb') -> str:
143
145
  return (
144
146
  '('
145
147
  f'echo {shlex.quote(name)} && '
omlish/dynamic.py CHANGED
@@ -25,7 +25,7 @@ _HOISTED_CODE_DEPTH: ta.MutableMapping[types.CodeType, int] = weakref.WeakKeyDic
25
25
  _MAX_HOIST_DEPTH = 0
26
26
 
27
27
 
28
- def hoist(depth=0):
28
+ def hoist(depth=0): # noqa
29
29
  def inner(fn):
30
30
  _HOISTED_CODE_DEPTH[fn.__code__] = depth
31
31
  global _MAX_HOIST_DEPTH
@@ -49,10 +49,10 @@ class Var(ta.Generic[T]):
49
49
 
50
50
  def __init__(
51
51
  self,
52
- default: ta.Union[type[MISSING], T] = MISSING, # type: ignore
52
+ default: type[MISSING] | T = MISSING, # type: ignore
53
53
  *,
54
- new: ta.Union[ta.Callable[[], T], type[MISSING]] = MISSING,
55
- validate: ta.Optional[ta.Callable[[T], None]] = None,
54
+ new: ta.Callable[[], T] | type[MISSING] = MISSING,
55
+ validate: ta.Callable[[T], None] | None = None,
56
56
  ) -> None:
57
57
  super().__init__()
58
58
 
@@ -60,7 +60,7 @@ class Var(ta.Generic[T]):
60
60
  raise TypeError('Cannot set both default and new')
61
61
  elif default is not MISSING:
62
62
  new = lambda: default # type: ignore
63
- self._new: ta.Union[type[MISSING], ta.Callable[[], T]] = new
63
+ self._new: type[MISSING] | ta.Callable[[], T] = new
64
64
  self._validate = validate
65
65
  self._bindings_by_frame: ta.MutableMapping[types.FrameType, ta.MutableMapping[int, Binding]] = weakref.WeakValueDictionary() # noqa
66
66
 
@@ -87,7 +87,7 @@ class Var(ta.Generic[T]):
87
87
  self._validate(self.value)
88
88
  return Binding(self, value, offset=offset)
89
89
 
90
- def with_binding(self, value):
90
+ def with_binding(self, value): # noqa
91
91
  def outer(fn):
92
92
  @functools.wraps(fn)
93
93
  def inner(*args, **kwargs):
@@ -96,7 +96,7 @@ class Var(ta.Generic[T]):
96
96
  return inner
97
97
  return outer
98
98
 
99
- def with_binding_fn(self, binding_fn):
99
+ def with_binding_fn(self, binding_fn): # noqa
100
100
  this = self
101
101
 
102
102
  def outer(fn):
@@ -104,7 +104,7 @@ class Var(ta.Generic[T]):
104
104
 
105
105
  @staticmethod
106
106
  @functools.wraps(fn)
107
- def __call__(*args, **kwargs):
107
+ def __call__(*args, **kwargs): # noqa
108
108
  with this.binding(binding_fn(*args, **kwargs)):
109
109
  return fn(*args, **kwargs)
110
110
 
@@ -119,7 +119,7 @@ class Var(ta.Generic[T]):
119
119
 
120
120
  return inner
121
121
 
122
- dct = dict((k, getattr(fn, k)) for k in functools.WRAPPER_ASSIGNMENTS)
122
+ dct: dict[str, ta.Any] = {k: getattr(fn, k) for k in functools.WRAPPER_ASSIGNMENTS}
123
123
  return lang.new_type(fn.__name__, (Descriptor,), dct)()
124
124
 
125
125
  return outer
@@ -133,7 +133,7 @@ class Var(ta.Generic[T]):
133
133
  except KeyError:
134
134
  pass
135
135
  else:
136
- for level, frame_binding in sorted(frame_bindings.items()):
136
+ for level, frame_binding in sorted(frame_bindings.items()): # noqa
137
137
  yield frame_binding._value # noqa
138
138
  frame = frame.f_back
139
139
 
@@ -148,7 +148,7 @@ class Var(ta.Generic[T]):
148
148
  try:
149
149
  return next(self.values)
150
150
  except StopIteration:
151
- raise UnboundVarError
151
+ raise UnboundVarError from None
152
152
 
153
153
 
154
154
  class Binding(ta.Generic[T]):
@@ -166,7 +166,7 @@ class Binding(ta.Generic[T]):
166
166
 
167
167
  def __enter__(self) -> T:
168
168
  frame = sys._getframe(self._offset).f_back # noqa
169
- lag_frame: ta.Optional[types.FrameType] = frame
169
+ lag_frame: types.FrameType | None = frame
170
170
  while lag_frame is not None:
171
171
  for cur_depth in range(_MAX_HOIST_DEPTH + 1):
172
172
  if lag_frame is None:
@@ -213,7 +213,7 @@ class _GeneratorContextManager(contextlib._GeneratorContextManager): # noqa
213
213
  return super().__enter__()
214
214
 
215
215
 
216
- def contextmanager(fn):
216
+ def contextmanager(fn): # noqa
217
217
  @functools.wraps(fn)
218
218
  def helper(*args, **kwds):
219
219
  return _GeneratorContextManager(fn, args, kwds)
omlish/fnpairs.py ADDED
@@ -0,0 +1,311 @@
1
+ import abc
2
+ import codecs
3
+ import dataclasses as dc
4
+ import typing as ta
5
+
6
+ from . import lang
7
+
8
+ if ta.TYPE_CHECKING:
9
+ import bzip2 as _bzip2
10
+ import gzip as _gzip
11
+ import json as _json
12
+ import lzma as _lzma
13
+ import pickle as _pickle
14
+ import struct as _struct
15
+ import tomllib as _tomllib
16
+
17
+ else:
18
+ _bzip2 = lang.proxy_import('bzip2')
19
+ _gzip = lang.proxy_import('gzip')
20
+ _json = lang.proxy_import('json')
21
+ _lzma = lang.proxy_import('lzma')
22
+ _pickle = lang.proxy_import('pickle')
23
+ _struct = lang.proxy_import('struct')
24
+ _tomllib = lang.proxy_import('tomllib')
25
+
26
+ _zstd = lang.proxy_import('zstd')
27
+ _yaml = lang.proxy_import('yaml')
28
+
29
+
30
+ ##
31
+
32
+
33
+ F = ta.TypeVar('F')
34
+ T = ta.TypeVar('T')
35
+ U = ta.TypeVar('U')
36
+
37
+
38
+ class FnPair(ta.Generic[F, T], abc.ABC):
39
+ @abc.abstractmethod
40
+ def forward(self, f: F) -> T:
41
+ raise NotImplementedError
42
+
43
+ @abc.abstractmethod
44
+ def backward(self, t: T) -> F:
45
+ raise NotImplementedError
46
+
47
+ def invert(self) -> 'FnPair[T, F]':
48
+ if isinstance(self, Inverted):
49
+ return self.fp
50
+ return Inverted(self)
51
+
52
+ def compose(self, nxt: 'FnPair[T, U]') -> 'FnPair[F, U]':
53
+ return Composite((self, nxt))
54
+
55
+
56
+ ##
57
+
58
+
59
+ @dc.dataclass(frozen=True)
60
+ class Simple(FnPair[F, T]):
61
+ forward: ta.Callable[[F], T] # type: ignore
62
+ backward: ta.Callable[[T], F] # type: ignore
63
+
64
+ def _forward(self, f: F) -> T:
65
+ return self.forward(f)
66
+
67
+ def _backward(self, t: T) -> F:
68
+ return self.backward(t)
69
+
70
+
71
+ # HACK: ABC workaround. Our dataclasses handle this with `override=True` but we don't want to dep that in here.
72
+ Simple.forward = Simple._forward # type: ignore # noqa
73
+ Simple.backward = Simple._backward # type: ignore # noqa
74
+ Simple.__abstractmethods__ = frozenset() # noqa
75
+
76
+
77
+ of = Simple
78
+
79
+
80
+ ##
81
+
82
+
83
+ @dc.dataclass(frozen=True)
84
+ class Inverted(FnPair[F, T]):
85
+ fp: FnPair[T, F]
86
+
87
+ def forward(self, f: F) -> T:
88
+ return self.fp.backward(f)
89
+
90
+ def backward(self, t: T) -> F:
91
+ return self.fp.forward(t)
92
+
93
+
94
+ ##
95
+
96
+
97
+ @dc.dataclass(frozen=True)
98
+ class Composite(FnPair[F, T]):
99
+ children: ta.Sequence[FnPair]
100
+
101
+ def forward(self, f: F) -> T:
102
+ for c in self.children:
103
+ f = c.forward(f)
104
+ return ta.cast(T, f)
105
+
106
+ def backward(self, t: T) -> F:
107
+ for c in reversed(self.children):
108
+ t = c.backward(t)
109
+ return ta.cast(F, t)
110
+
111
+
112
+ ##
113
+
114
+
115
+ @dc.dataclass(frozen=True)
116
+ class Text(FnPair[str, bytes]):
117
+ ci: codecs.CodecInfo
118
+ encode_errors: str = dc.field(default='strict', kw_only=True)
119
+ decode_errors: str = dc.field(default='strict', kw_only=True)
120
+
121
+ def forward(self, f: str) -> bytes:
122
+ # Python ignores the returned length:
123
+ # https://github.com/python/cpython/blob/7431c3799efbd06ed03ee70b64420f45e83b3667/Python/codecs.c#L424
124
+ t, _ = self.ci.encode(f, self.encode_errors)
125
+ return t
126
+
127
+ def backward(self, t: bytes) -> str:
128
+ f, _ = self.ci.decode(t, self.decode_errors)
129
+ return f
130
+
131
+
132
+ def text(name: str, *, encode_errors: str = 'strict', decode_errors: str = 'strict') -> Text:
133
+ ci = codecs.lookup(name)
134
+ if not ci._is_text_encoding: # noqa
135
+ raise TypeError(f'must be text codec: {name}')
136
+ return Text(ci, encode_errors=encode_errors, decode_errors=decode_errors)
137
+
138
+
139
+ UTF8 = text('utf-8')
140
+
141
+
142
+ #
143
+
144
+
145
+ @dc.dataclass(frozen=True)
146
+ class Optional(FnPair[ta.Optional[F], ta.Optional[T]]):
147
+ fp: FnPair[F, T]
148
+
149
+ def forward(self, f: ta.Optional[F]) -> ta.Optional[T]:
150
+ return None if f is None else self.fp.forward(f)
151
+
152
+ def backward(self, t: ta.Optional[T]) -> ta.Optional[F]:
153
+ return None if t is None else self.fp.backward(t)
154
+
155
+
156
+ class Lines(FnPair[ta.Sequence[str], str]):
157
+ def forward(self, f: ta.Sequence[str]) -> str:
158
+ return '\n'.join(f)
159
+
160
+ def backward(self, t: str) -> ta.Sequence[str]:
161
+ return t.splitlines()
162
+
163
+
164
+ ##
165
+
166
+
167
+ _EXTENSION_REGISTRY: dict[str, type[FnPair]] = {}
168
+
169
+
170
+ def _register_extension(*ss):
171
+ def inner(cls):
172
+ for s in ss:
173
+ if s in _EXTENSION_REGISTRY:
174
+ raise Exception(s)
175
+ _EXTENSION_REGISTRY[s] = cls
176
+ return cls
177
+ return inner
178
+
179
+
180
+ ##
181
+
182
+
183
+ class Compression(FnPair[bytes, bytes], abc.ABC):
184
+ pass
185
+
186
+
187
+ @_register_extension('gz')
188
+ @dc.dataclass(frozen=True)
189
+ class Gzip(Compression):
190
+ compresslevel: int = 9
191
+
192
+ def forward(self, f: bytes) -> bytes:
193
+ return _gzip.compress(f, compresslevel=self.compresslevel)
194
+
195
+ def backward(self, t: bytes) -> bytes:
196
+ return _gzip.decompress(t)
197
+
198
+
199
+ @_register_extension('bz2')
200
+ @dc.dataclass(frozen=True)
201
+ class Bzip2(Compression):
202
+ compresslevel: int = 9
203
+
204
+ def forward(self, f: bytes) -> bytes:
205
+ return _bzip2.compress(f, compresslevel=self.compresslevel)
206
+
207
+ def backward(self, t: bytes) -> bytes:
208
+ return _bzip2.decompress(t)
209
+
210
+
211
+ @_register_extension('lzma')
212
+ class Lzma(Compression):
213
+ def forward(self, f: bytes) -> bytes:
214
+ return _lzma.compress(f)
215
+
216
+ def backward(self, t: bytes) -> bytes:
217
+ return _lzma.decompress(t)
218
+
219
+
220
+ #
221
+
222
+
223
+ @_register_extension('zstd')
224
+ class Zstd(Compression):
225
+ def forward(self, f: bytes) -> bytes:
226
+ return _zstd.compress(f)
227
+
228
+ def backward(self, t: bytes) -> bytes:
229
+ return _zstd.decompress(t)
230
+
231
+
232
+ ##
233
+
234
+
235
+ @dc.dataclass(frozen=True)
236
+ class Struct(FnPair[tuple, bytes]):
237
+ fmt: str
238
+
239
+ def forward(self, f: tuple) -> bytes:
240
+ return _struct.pack(self.fmt, *f)
241
+
242
+ def backward(self, t: bytes) -> tuple:
243
+ return _struct.unpack(self.fmt, t)
244
+
245
+
246
+ @_register_extension('pkl')
247
+ @dc.dataclass(frozen=True)
248
+ class Pickle(FnPair[ta.Any, bytes]):
249
+ protocol: int | None = None
250
+
251
+ def forward(self, f: ta.Any) -> bytes:
252
+ return _pickle.dumps(f, protocol=self.protocol)
253
+
254
+ def backward(self, t: bytes) -> ta.Any:
255
+ return _pickle.loads(t)
256
+
257
+
258
+ @_register_extension('json')
259
+ @dc.dataclass(frozen=True)
260
+ class Json(FnPair[ta.Any, str]):
261
+ indent: int | str | None = dc.field(default=None, kw_only=True)
262
+ separators: tuple[str, str] | None = dc.field(default=None, kw_only=True)
263
+
264
+ def forward(self, f: ta.Any) -> str:
265
+ return _json.dumps(f, indent=self.indent, separators=self.separators)
266
+
267
+ def backward(self, t: str) -> ta.Any:
268
+ return _json.loads(t)
269
+
270
+
271
+ JSON = Json()
272
+ PRETTY_JSON = Json(indent=2)
273
+ COMPACT_JSON = Json(separators=(',', ':'))
274
+
275
+
276
+ @_register_extension('jsonl')
277
+ class JsonLines(FnPair[ta.Sequence[ta.Any], str]):
278
+ def forward(self, f: ta.Sequence[ta.Any]) -> str:
279
+ return '\n'.join(_json.dumps(e) for e in f)
280
+
281
+ def backward(self, t: str) -> ta.Sequence[ta.Any]:
282
+ return [_json.loads(l) for l in t.splitlines()]
283
+
284
+
285
+ @_register_extension('toml')
286
+ class Toml(FnPair[ta.Any, str]):
287
+ def forward(self, f: ta.Any) -> str:
288
+ raise NotImplementedError
289
+
290
+ def backward(self, t: str) -> ta.Any:
291
+ return _tomllib.loads(t)
292
+
293
+
294
+ #
295
+
296
+
297
+ @_register_extension('yml', 'yaml')
298
+ class Yaml(FnPair[ta.Any, str]):
299
+ def forward(self, f: ta.Any) -> str:
300
+ return _yaml.dump(f)
301
+
302
+ def backward(self, t: str) -> ta.Any:
303
+ return _yaml.safe_load(t)
304
+
305
+
306
+ class UnsafeYaml(FnPair[ta.Any, str]):
307
+ def forward(self, f: ta.Any) -> str:
308
+ return _yaml.dump(f)
309
+
310
+ def backward(self, t: str) -> ta.Any:
311
+ return _yaml.safe_load(t, loader=_yaml.FullLoader)
@@ -115,7 +115,7 @@ class Attrs(Item):
115
115
  coerce=lambda o: col.frozendict(
116
116
  (check.not_empty(check.isinstance(k, str)), Value.of(v)) # type: ignore
117
117
  for k, v in check.isinstance(o, ta.Mapping).items()
118
- )
118
+ ),
119
119
  )
120
120
 
121
121
  @classmethod
omlish/graphs/trees.py CHANGED
@@ -18,7 +18,7 @@ NodeWalker = ta.Callable[[NodeT], ta.Iterable[NodeT]]
18
18
  NodeGenerator = ta.Generator[NodeT, None, None]
19
19
 
20
20
 
21
- class NodeException(ta.Generic[NodeT], Exception):
21
+ class NodeError(ta.Generic[NodeT], Exception):
22
22
  def __init__(self, node: NodeT, msg: str, *args, **kwargs) -> None:
23
23
  super().__init__(msg, *args, **kwargs) # noqa
24
24
  self._node = node
@@ -28,12 +28,12 @@ class NodeException(ta.Generic[NodeT], Exception):
28
28
  return self._node
29
29
 
30
30
 
31
- class DuplicateNodeException(NodeException[NodeT]):
31
+ class DuplicateNodeError(NodeError[NodeT]):
32
32
  def __init__(self, node: NodeT, *args, **kwargs) -> None:
33
33
  super().__init__(node, f'Duplicate node: {node!r}', *args, **kwargs)
34
34
 
35
35
 
36
- class UnknownNodeException(NodeException[NodeT]):
36
+ class UnknownNodeError(NodeError[NodeT]):
37
37
  def __init__(self, node: NodeT, *args, **kwargs) -> None:
38
38
  super().__init__(node, f'Unknown node: {node!r}', *args, **kwargs)
39
39
 
@@ -55,19 +55,19 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
55
55
 
56
56
  self._set_fac: ta.Callable[..., ta.MutableSet[NodeT]] = col.IdentitySet if identity else set
57
57
  self._dict_fac: ta.Callable[..., ta.MutableMapping[NodeT, ta.Any]] = col.IdentityKeyDict if identity else dict
58
- self._idx_seq_fac: ta.Callable[..., col.IndexedSeq[NodeT]] = functools.partial(col.IndexedSeq, identity=identity) # noqa
58
+ self._idx_seq_fac: ta.Callable[..., col.IndexedSeq[NodeT]] = functools.partial(col.IndexedSeq, identity=identity) # type: ignore # noqa
59
59
 
60
- def walk(cur: NodeT, parent: ta.Optional[NodeT]) -> None:
60
+ def walk(cur: NodeT, parent: NodeT | None) -> None:
61
61
  check.not_none(cur)
62
62
  if cur in node_set:
63
- raise DuplicateNodeException(cur)
63
+ raise DuplicateNodeError(cur)
64
64
 
65
65
  nodes.append(cur)
66
66
  node_set.add(cur)
67
67
  if parent is None:
68
68
  check.state(cur is root)
69
69
  elif parent not in node_set:
70
- raise UnknownNodeException(parent)
70
+ raise UnknownNodeError(parent)
71
71
 
72
72
  parents_by_node[cur] = parent
73
73
 
@@ -77,22 +77,22 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
77
77
  walk(child, cur)
78
78
 
79
79
  nodes: list[NodeT] = []
80
- node_set: ta.MutableSet[NodeT] = self._set_fac() # type: ignore
81
- children_by_node: ta.MutableMapping[ta.Optional[NodeT], ta.Sequence[NodeT]] = self._dict_fac() # type: ignore
82
- child_sets_by_node: ta.MutableMapping[ta.Optional[NodeT], ta.AbstractSet[NodeT]] = self._dict_fac() # type: ignore # noqa
83
- parents_by_node: ta.MutableMapping[NodeT, ta.Optional[NodeT]] = self._dict_fac() # type: ignore
80
+ node_set: ta.MutableSet[NodeT] = self._set_fac()
81
+ children_by_node: ta.MutableMapping[NodeT | None, ta.Sequence[NodeT]] = self._dict_fac()
82
+ child_sets_by_node: ta.MutableMapping[ta.Optional[NodeT], ta.AbstractSet[NodeT]] = self._dict_fac()
83
+ parents_by_node: ta.MutableMapping[NodeT, NodeT | None] = self._dict_fac()
84
84
 
85
85
  children_by_node[None] = [root]
86
- child_sets_by_node[None] = self._set_fac([root]) # type: ignore
86
+ child_sets_by_node[None] = self._set_fac([root])
87
87
 
88
88
  walk(root, None)
89
89
 
90
- self._nodes = self._idx_seq_fac(nodes) # type: ignore
90
+ self._nodes = self._idx_seq_fac(nodes)
91
91
  self._node_set: ta.AbstractSet[NodeT] = node_set
92
- self._children_by_node: ta.Mapping[ta.Optional[NodeT], col.IndexedSeq[NodeT]] = self._dict_fac( # type: ignore
92
+ self._children_by_node: ta.Mapping[NodeT | None, col.IndexedSeq[NodeT]] = self._dict_fac(
93
93
  [(n, self._idx_seq_fac(cs)) for n, cs in children_by_node.items()])
94
- self._child_sets_by_node: ta.Mapping[ta.Optional[NodeT], ta.AbstractSet[NodeT]] = child_sets_by_node
95
- self._parents_by_node: ta.Mapping[NodeT, ta.Optional[NodeT]] = parents_by_node
94
+ self._child_sets_by_node: ta.Mapping[NodeT | None, ta.AbstractSet[NodeT]] = child_sets_by_node
95
+ self._parents_by_node: ta.Mapping[NodeT, NodeT | None] = parents_by_node
96
96
 
97
97
  @property
98
98
  def root(self) -> NodeT:
@@ -115,27 +115,24 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
115
115
  return self._node_set
116
116
 
117
117
  @property
118
- def children_by_node(self) -> ta.Mapping[ta.Optional[NodeT], col.IndexedSeq[NodeT]]:
118
+ def children_by_node(self) -> ta.Mapping[NodeT | None, col.IndexedSeq[NodeT]]:
119
119
  return self._children_by_node
120
120
 
121
121
  @property
122
- def child_sets_by_node(self) -> ta.Mapping[ta.Optional[NodeT], ta.AbstractSet[NodeT]]:
122
+ def child_sets_by_node(self) -> ta.Mapping[NodeT | None, ta.AbstractSet[NodeT]]:
123
123
  return self._child_sets_by_node
124
124
 
125
125
  @property
126
- def parents_by_node(self) -> ta.Mapping[NodeT, ta.Optional[NodeT]]:
126
+ def parents_by_node(self) -> ta.Mapping[NodeT, NodeT | None]:
127
127
  return self._parents_by_node
128
128
 
129
129
  @classmethod
130
130
  def from_parents(
131
131
  cls,
132
- src: ta.Union[
133
- ta.Mapping[NodeT, ta.Optional[NodeT]],
134
- ta.Iterable[tuple[NodeT, ta.Optional[NodeT]]],
135
- ],
132
+ src: ta.Mapping[NodeT, NodeT | None] | ta.Iterable[tuple[NodeT, NodeT | None]],
136
133
  *,
137
134
  identity: bool = False,
138
- **kwargs
135
+ **kwargs,
139
136
  ) -> 'BasicTreeAnalysis[NodeT]':
140
137
  pairs: ta.Sequence[tuple[NodeT, NodeT]]
141
138
  if isinstance(src, ta.Mapping):
@@ -166,13 +163,10 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
166
163
  @classmethod
167
164
  def from_children(
168
165
  cls,
169
- src: ta.Union[
170
- ta.Mapping[NodeT, ta.Iterable[NodeT]],
171
- ta.Iterable[tuple[NodeT, ta.Iterable[NodeT]]],
172
- ],
166
+ src: ta.Mapping[NodeT, ta.Iterable[NodeT]] | ta.Iterable[tuple[NodeT, ta.Iterable[NodeT]]],
173
167
  *,
174
168
  identity: bool = False,
175
- **kwargs
169
+ **kwargs,
176
170
  ) -> 'BasicTreeAnalysis[NodeT]':
177
171
  pairs: ta.Sequence[tuple[NodeT, ta.Sequence[NodeT]]]
178
172
  if isinstance(src, ta.Mapping):
@@ -222,7 +216,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
222
216
  return ret # type: ignore
223
217
 
224
218
  def iter_ancestors(self, node: NodeT) -> NodeGenerator[NodeT]:
225
- cur: ta.Optional[NodeT] = node
219
+ cur: NodeT | None = node
226
220
  while True:
227
221
  cur = self.parents_by_node.get(cur) # type: ignore
228
222
  if cur is None:
@@ -232,7 +226,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
232
226
  def get_lineage(self, node: NodeT) -> col.IndexedSeq[NodeT]:
233
227
  return self._idx_seq_fac(reversed([node, *self.iter_ancestors(node)]))
234
228
 
235
- def get_first_parent_of_type(self, node: NodeT, ty: type[T]) -> ta.Optional[T]:
229
+ def get_first_parent_of_type(self, node: NodeT, ty: type[T]) -> T | None:
236
230
  for cur in self.iter_ancestors(node):
237
231
  if isinstance(cur, ty):
238
232
  return cur
omlish/inject/__init__.py CHANGED
@@ -18,13 +18,13 @@ from .elements import ( # noqa
18
18
  )
19
19
 
20
20
  from .exceptions import ( # noqa
21
- CyclicDependencyException,
22
- DuplicateKeyException,
23
- KeyException,
24
- ScopeAlreadyOpenException,
25
- ScopeException,
26
- ScopeNotOpenException,
27
- UnboundKeyException,
21
+ BaseKeyError,
22
+ CyclicDependencyError,
23
+ DuplicateKeyError,
24
+ ScopeAlreadyOpenError,
25
+ ScopeError,
26
+ ScopeNotOpenError,
27
+ UnboundKeyError,
28
28
  )
29
29
 
30
30
  from .injector import ( # noqa