omlish 0.0.0.dev248__py3-none-any.whl → 0.0.0.dev250__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev248'
2
- __revision__ = 'c1cc77db1e3c02c65fabdb24e9696526d61559e3'
1
+ __version__ = '0.0.0.dev250'
2
+ __revision__ = '0e6e1316b474c6f882784e2474e809a478c54732'
3
3
 
4
4
 
5
5
  #
omlish/lang/__init__.py CHANGED
@@ -209,6 +209,19 @@ from .objects import ( # noqa
209
209
  super_meta,
210
210
  )
211
211
 
212
+ from .params import ( # noqa
213
+ ArgsParam,
214
+ KwOnlyParam,
215
+ KwargsParam,
216
+ Param,
217
+ ParamSeparator,
218
+ ParamSpec,
219
+ PosOnlyParam,
220
+ ValParam,
221
+ VarParam,
222
+ param_render,
223
+ )
224
+
212
225
  from .resolving import ( # noqa
213
226
  Resolvable,
214
227
  ResolvableClassNameError,
@@ -1,5 +1,7 @@
1
1
  """
2
2
  TODO:
3
+ - !! reconcile A().f() with A.f(A())
4
+ - unbound descriptor *should* still hit instance cache
3
5
  - integrate / expose with collections.cache
4
6
  - weakrefs (selectable by arg)
5
7
  - more rigorous descriptor pickling
@@ -18,11 +20,18 @@ from ..contextmanagers import DefaultLockable
18
20
  from ..contextmanagers import default_lock
19
21
  from ..descriptors import unwrap_func
20
22
  from ..descriptors import unwrap_func_with_partials
23
+ from ..params import KwargsParam
24
+ from ..params import Param
25
+ from ..params import ParamSeparator
26
+ from ..params import ParamSpec
27
+ from ..params import ValParam
28
+ from ..params import param_render
21
29
 
22
30
 
23
31
  P = ta.ParamSpec('P')
24
32
  T = ta.TypeVar('T')
25
33
  CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
34
+ CacheKeyMaker: ta.TypeAlias = ta.Callable[..., tuple]
26
35
 
27
36
 
28
37
  ##
@@ -32,66 +41,100 @@ def _nullary_cache_key_maker():
32
41
  return ()
33
42
 
34
43
 
35
- def _simple_cache_key_maker(*args, **kwargs):
36
- return (args, tuple(sorted(kwargs.items())))
44
+ def _self_cache_key_maker(self):
45
+ return (self,)
37
46
 
38
47
 
39
- def _make_cache_key_maker(fn, *, simple=False, bound=False):
40
- if simple:
41
- return _simple_cache_key_maker
48
+ _PRE_MADE_CACHE_KEY_MAKERS = [
49
+ _nullary_cache_key_maker,
50
+ _self_cache_key_maker,
51
+ ]
52
+
53
+
54
+ _PRE_MADE_CACHE_KEY_MAKERS_BY_PARAM_SPEC: ta.Mapping[ParamSpec, CacheKeyMaker] = {
55
+ ParamSpec.inspect(fn): fn # type: ignore
56
+ for fn in _PRE_MADE_CACHE_KEY_MAKERS
57
+ }
58
+
59
+
60
+ ##
42
61
 
43
- fn, partials = unwrap_func_with_partials(fn)
44
62
 
63
+ def _make_unwrapped_cache_key_maker(
64
+ fn: ta.Callable,
65
+ *,
66
+ bound: bool = False,
67
+ ) -> CacheKeyMaker:
45
68
  if inspect.isgeneratorfunction(fn) or inspect.iscoroutinefunction(fn):
46
69
  raise TypeError(fn)
47
70
 
48
- sig = inspect.signature(fn)
49
- sig_params = list(sig.parameters.values())[1 if bound else 0:]
50
- if not sig_params:
71
+ ps = ParamSpec.inspect(
72
+ fn,
73
+ offset=1 if bound else 0,
74
+ strip_annotations=True,
75
+ )
76
+
77
+ if not len(ps):
51
78
  return _nullary_cache_key_maker
52
79
 
53
- ns = {}
80
+ if not ps.has_defaults:
81
+ try:
82
+ return _PRE_MADE_CACHE_KEY_MAKERS_BY_PARAM_SPEC[ps]
83
+ except KeyError:
84
+ pass
85
+
86
+ builtin_pfx = '__cache_key_maker__'
87
+ ns = {
88
+ (builtin_tuple := builtin_pfx + 'tuple'): tuple,
89
+ (builtin_sorted := builtin_pfx + 'sorted'): sorted,
90
+ }
91
+
54
92
  src_params = []
55
93
  src_vals = []
56
94
  kwargs_name = None
57
- render_pos_only_separator = False
58
- render_kw_only_separator = True
59
- for p in sig_params:
60
- formatted = p.name
61
- if p.default is not inspect.Parameter.empty:
95
+ for p in ps:
96
+ if isinstance(p, ParamSeparator):
97
+ src_params.append(p.value)
98
+ continue
99
+
100
+ if not isinstance(p, Param):
101
+ raise TypeError(p)
102
+
103
+ if isinstance(p, ValParam) and p.default.present:
62
104
  ns[p.name] = p.default
63
- formatted = f'{formatted}={formatted}'
64
- kind = p.kind
65
- if kind == inspect.Parameter.VAR_POSITIONAL:
66
- formatted = '*' + formatted
67
- elif kind == inspect.Parameter.VAR_KEYWORD:
68
- formatted = '**' + formatted
69
- if kind == inspect.Parameter.POSITIONAL_ONLY:
70
- render_pos_only_separator = True
71
- elif render_pos_only_separator:
72
- src_params.append('/')
73
- render_pos_only_separator = False
74
- if kind == inspect.Parameter.VAR_POSITIONAL:
75
- render_kw_only_separator = False
76
- elif kind == inspect.Parameter.KEYWORD_ONLY and render_kw_only_separator:
77
- src_params.append('*')
78
- render_kw_only_separator = False
79
- src_params.append(formatted)
80
- if kind == inspect.Parameter.VAR_KEYWORD:
105
+
106
+ src_params.append(param_render(
107
+ p,
108
+ render_default=lambda _: p.name, # noqa
109
+ ))
110
+
111
+ if isinstance(p, KwargsParam):
81
112
  kwargs_name = p.name
82
113
  else:
83
114
  src_vals.append(p.name)
84
- if render_pos_only_separator:
85
- src_params.append('/')
86
115
 
87
- kwa = f', __builtins__.tuple(__builtins__.sorted({kwargs_name}.items()))' if kwargs_name else ''
116
+ if kwargs_name is not None:
117
+ src_vals.append(f'{builtin_tuple}({builtin_sorted}({kwargs_name}.items()))')
118
+
88
119
  rendered = (
89
120
  f'def __func__({", ".join(src_params)}):\n'
90
- f' return ({", ".join(src_vals)}{kwa})\n'
121
+ f' return ({", ".join(src_vals)}{"," if len(src_vals) == 1 else ""})\n'
91
122
  )
92
123
  exec(rendered, ns)
93
124
 
94
- kfn = ns['__func__']
125
+ kfn: CacheKeyMaker = ns['__func__'] # type: ignore[assignment]
126
+ return kfn
127
+
128
+
129
+ def _make_cache_key_maker(
130
+ fn: ta.Any,
131
+ *,
132
+ bound: bool = False,
133
+ ):
134
+ fn, partials = unwrap_func_with_partials(fn)
135
+
136
+ kfn = _make_unwrapped_cache_key_maker(fn, bound=bound)
137
+
95
138
  for part in partials[::-1]:
96
139
  kfn = functools.partial(kfn, *part.args, **part.keywords)
97
140
 
@@ -105,7 +148,6 @@ class _CachedFunction(ta.Generic[T], Abstract):
105
148
  @dc.dataclass(frozen=True)
106
149
  class Opts:
107
150
  map_maker: ta.Callable[[], ta.MutableMapping] = dict
108
- simple_key: bool = False
109
151
  lock: DefaultLockable = None
110
152
  transient: bool = False
111
153
 
@@ -122,7 +164,7 @@ class _CachedFunction(ta.Generic[T], Abstract):
122
164
 
123
165
  self._fn = (fn,)
124
166
  self._opts = opts
125
- self._key_maker = key_maker if key_maker is not None else _make_cache_key_maker(fn, simple=opts.simple_key)
167
+ self._key_maker = key_maker if key_maker is not None else _make_cache_key_maker(fn)
126
168
 
127
169
  self._lock = default_lock(opts.lock, False)() if opts.lock is not None else None
128
170
  self._values = values if values is not None else opts.map_maker()
@@ -278,7 +320,7 @@ class _DescriptorCachedFunction(_CachedFunction[T]):
278
320
  name = self._name
279
321
  bound_fn = fn.__get__(instance, owner)
280
322
  if self._bound_key_maker is None:
281
- self._bound_key_maker = _make_cache_key_maker(fn, simple=self._opts.simple_key, bound=True)
323
+ self._bound_key_maker = _make_cache_key_maker(fn, bound=True)
282
324
 
283
325
  bound = self.__class__(
284
326
  fn,
omlish/lang/params.py ADDED
@@ -0,0 +1,273 @@
1
+ """
2
+ TODO:
3
+ - check validity
4
+ """
5
+ import dataclasses as dc
6
+ import enum
7
+ import inspect
8
+ import typing as ta
9
+
10
+ from .classes.abstract import Abstract
11
+ from .classes.restrict import Final
12
+ from .classes.restrict import Sealed
13
+ from .maybes import Maybe
14
+ from .maybes import empty
15
+ from .maybes import just
16
+
17
+
18
+ T = ta.TypeVar('T')
19
+
20
+
21
+ ##
22
+
23
+
24
+ @dc.dataclass(frozen=True, unsafe_hash=True)
25
+ class Param(Sealed, Abstract):
26
+ name: str
27
+
28
+ annotation: Maybe = empty()
29
+
30
+ prefix: ta.ClassVar[str] = ''
31
+
32
+ @property
33
+ def name_with_prefix(self) -> str:
34
+ return f'{self.prefix}{self.name}'
35
+
36
+
37
+ #
38
+
39
+
40
+ @dc.dataclass(frozen=True, unsafe_hash=True)
41
+ class VarParam(Param, Abstract):
42
+ pass
43
+
44
+
45
+ @dc.dataclass(frozen=True, unsafe_hash=True)
46
+ class ArgsParam(VarParam, Final):
47
+ prefix: ta.ClassVar[str] = '*'
48
+
49
+
50
+ @dc.dataclass(frozen=True, unsafe_hash=True)
51
+ class KwargsParam(VarParam, Final):
52
+ prefix: ta.ClassVar[str] = '**'
53
+
54
+
55
+ #
56
+
57
+
58
+ @dc.dataclass(frozen=True, unsafe_hash=True)
59
+ class ValParam(Param):
60
+ default: Maybe = empty()
61
+
62
+
63
+ @dc.dataclass(frozen=True, unsafe_hash=True)
64
+ class PosOnlyParam(ValParam, Final):
65
+ pass
66
+
67
+
68
+ @dc.dataclass(frozen=True, unsafe_hash=True)
69
+ class KwOnlyParam(ValParam, Final):
70
+ pass
71
+
72
+
73
+ #
74
+
75
+
76
+ class ParamSeparator(enum.Enum):
77
+ POS_ONLY = '/'
78
+ KW_ONLY = '*'
79
+
80
+
81
+ ##
82
+
83
+
84
+ def _inspect_empty_to_maybe(o: T) -> Maybe[T]:
85
+ if o is inspect.Parameter.empty:
86
+ return empty()
87
+ else:
88
+ return just(o)
89
+
90
+
91
+ class ParamSpec(ta.Sequence[Param], Final):
92
+ def __init__(self, *ps: Param) -> None:
93
+ super().__init__()
94
+
95
+ self._ps = ps
96
+
97
+ self._hash: int | None = None
98
+
99
+ self._has_annotations: bool | None = None
100
+ self._has_defaults: bool | None = None
101
+
102
+ self._with_seps: tuple[Param | ParamSeparator, ...] | None = None
103
+
104
+ #
105
+
106
+ @classmethod
107
+ def of_signature(
108
+ cls,
109
+ sig: inspect.Signature,
110
+ *,
111
+ offset: int = 0,
112
+ strip_annotations: bool = False,
113
+ strip_defaults: bool = False,
114
+ ) -> 'ParamSpec':
115
+ ps: list[Param] = []
116
+
117
+ ip: inspect.Parameter
118
+ for i, ip in enumerate(sig.parameters.values()):
119
+ if i < offset:
120
+ continue
121
+
122
+ ann = _inspect_empty_to_maybe(ip.annotation) if not strip_annotations else empty()
123
+ dfl = _inspect_empty_to_maybe(ip.default) if not strip_defaults else empty()
124
+
125
+ if ip.kind == inspect.Parameter.POSITIONAL_ONLY:
126
+ ps.append(PosOnlyParam(ip.name, ann, dfl))
127
+
128
+ elif ip.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
129
+ ps.append(ValParam(ip.name, ann, dfl))
130
+
131
+ elif ip.kind == inspect.Parameter.VAR_POSITIONAL:
132
+ ps.append(ArgsParam(ip.name, ann))
133
+
134
+ elif ip.kind == inspect.Parameter.KEYWORD_ONLY:
135
+ ps.append(KwOnlyParam(ip.name, ann, dfl))
136
+
137
+ elif ip.kind == inspect.Parameter.VAR_KEYWORD:
138
+ ps.append(KwargsParam(ip.name, ann))
139
+
140
+ else:
141
+ raise ValueError(ip.kind)
142
+
143
+ return cls(*ps)
144
+
145
+ @classmethod
146
+ def inspect(
147
+ cls,
148
+ obj: ta.Any,
149
+ **kwargs: ta.Any,
150
+ ) -> 'ParamSpec':
151
+ return cls.of_signature(
152
+ inspect.signature(obj),
153
+ **kwargs,
154
+ )
155
+
156
+ #
157
+
158
+ def __repr__(self) -> str:
159
+ return f'{self.__class__.__name__}({", ".join(map(repr, self._ps))})'
160
+
161
+ def __hash__(self) -> int:
162
+ if (h := self._hash) is not None:
163
+ return h
164
+ self._hash = h = hash(self._ps)
165
+ return h
166
+
167
+ def __eq__(self, other: object) -> bool:
168
+ if type(other) is not ParamSpec:
169
+ raise TypeError(other)
170
+ return self._ps == other._ps # noqa
171
+
172
+ #
173
+
174
+ @property
175
+ def has_annotations(self) -> bool:
176
+ if (ha := self._has_annotations) is not None:
177
+ return ha
178
+ self._has_annotations = ha = any(
179
+ isinstance(p, (VarParam, ValParam)) and p.annotation.present
180
+ for p in self._ps
181
+ )
182
+ return ha
183
+
184
+ @property
185
+ def has_defaults(self) -> bool:
186
+ if (hd := self._has_defaults) is not None:
187
+ return hd
188
+ self._has_defaults = hd = any(
189
+ isinstance(p, ValParam) and p.default.present
190
+ for p in self._ps
191
+ )
192
+ return hd
193
+
194
+ #
195
+
196
+ @property
197
+ def with_seps(self) -> ta.Sequence[Param | ParamSeparator]:
198
+ if (ws := self._with_seps) is not None:
199
+ return ws
200
+
201
+ l: list[Param | ParamSeparator] = []
202
+ needs_pos_only = False
203
+ seen_kw_only = False
204
+ for p in self._ps:
205
+ if isinstance(p, PosOnlyParam):
206
+ needs_pos_only = True
207
+ elif needs_pos_only:
208
+ l.append(ParamSeparator.POS_ONLY)
209
+ needs_pos_only = False
210
+
211
+ if isinstance(p, KwOnlyParam) and not seen_kw_only:
212
+ l.append(ParamSeparator.KW_ONLY)
213
+ seen_kw_only = True
214
+ elif isinstance(p, KwargsParam):
215
+ seen_kw_only = True
216
+
217
+ l.append(p)
218
+
219
+ self._with_seps = ws = tuple(l)
220
+ return ws
221
+
222
+ #
223
+
224
+ @ta.overload
225
+ def __getitem__(self, index: int) -> Param: ...
226
+
227
+ @ta.overload
228
+ def __getitem__(self, index: slice) -> ta.Sequence[Param]: ...
229
+
230
+ def __getitem__(self, index):
231
+ return self._ps[index]
232
+
233
+ def __len__(self) -> int:
234
+ return len(self._ps)
235
+
236
+
237
+ ##
238
+
239
+
240
+ def param_render(
241
+ p: Param | ParamSeparator,
242
+ *,
243
+ render_annotation: ta.Callable[[ta.Any], str] | None = None,
244
+ render_default: ta.Callable[[ta.Any], str] | None = None,
245
+ ) -> str:
246
+ if isinstance(p, Param):
247
+ dfl_s: str | None = None
248
+ ann_s: str | None = None
249
+ if isinstance(p, (VarParam, ValParam)) and p.annotation.present:
250
+ if render_annotation is None:
251
+ raise ValueError(f'Param {p.name} has an annotation but no annotation renderer provided')
252
+ ann_s = render_annotation(p.annotation.must())
253
+
254
+ if isinstance(p, ValParam) and p.default.present:
255
+ if render_default is None:
256
+ raise ValueError(f'Param {p.name} has a default but no default renderer provided')
257
+ dfl_s = render_default(p.default.must())
258
+
259
+ if ann_s is not None:
260
+ if dfl_s is not None:
261
+ return f'{p.name_with_prefix}: {ann_s} = {dfl_s}'
262
+ else:
263
+ return f'{p.name_with_prefix}: {ann_s}'
264
+ elif dfl_s is not None:
265
+ return f'{p.name_with_prefix}={dfl_s}'
266
+ else:
267
+ return p.name_with_prefix
268
+
269
+ elif isinstance(p, ParamSeparator):
270
+ return p.value
271
+
272
+ else:
273
+ raise TypeError(p)
omlish/lang/resources.py CHANGED
@@ -1,9 +1,19 @@
1
1
  import dataclasses as dc
2
2
  import functools
3
- import importlib.resources
4
3
  import os.path
5
4
  import typing as ta
6
5
 
6
+ from .imports import proxy_import
7
+
8
+
9
+ if ta.TYPE_CHECKING:
10
+ import importlib.resources as importlib_resources
11
+ else:
12
+ importlib_resources = proxy_import('importlib.resources')
13
+
14
+
15
+ ##
16
+
7
17
 
8
18
  @dc.dataclass(frozen=True)
9
19
  class ReadableResource:
@@ -18,7 +28,7 @@ class ReadableResource:
18
28
  def get_package_resources(anchor: str) -> ta.Mapping[str, ReadableResource]:
19
29
  lst: list[ReadableResource] = []
20
30
 
21
- for pf in importlib.resources.files(anchor).iterdir():
31
+ for pf in importlib_resources.files(anchor).iterdir():
22
32
  lst.append(ReadableResource(
23
33
  name=pf.name,
24
34
  is_file=pf.is_file(),
@@ -65,7 +75,7 @@ def get_relative_resources(
65
75
  if num_up:
66
76
  pkg_parts = pkg_parts[:-num_up]
67
77
  anchor = '.'.join([*pkg_parts, *path_parts])
68
- for pf in importlib.resources.files(anchor).iterdir():
78
+ for pf in importlib_resources.files(anchor).iterdir():
69
79
  lst.append(ReadableResource(
70
80
  name=pf.name,
71
81
  is_file=pf.is_file(),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev248
3
+ Version: 0.0.0.dev250
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=x26AIwDzScUvnX-p4xlq6Zc5QYrAo0Vmgf1qHc1KL_M,8253
2
- omlish/__about__.py,sha256=vdUCdVH6QPJp23FjKOh9VC2Pxz8yofiz_tArVxvhOyU,3380
2
+ omlish/__about__.py,sha256=LCIKYHw0FYdPvCdmhqyF7FaJsL17sOU3m3dX5HFBUbg,3380
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -400,7 +400,7 @@ omlish/iterators/iterators.py,sha256=iTQQwBE6Wzoy36dnbPIws17zbjE3zNN4KwVw4Fzh-gY
400
400
  omlish/iterators/recipes.py,sha256=53mkexitMhkwXQZbL6DrhpT0WePQ_56uXd5Jaw3DfzI,467
401
401
  omlish/iterators/tools.py,sha256=Pi4ybXytUXVZ3xwK89xpPImQfYYId9p1vIFQvVqVLqA,2551
402
402
  omlish/iterators/unique.py,sha256=0jAX3kwzVfRNhe0Tmh7kVP_Q2WBIn8POo_O-rgFV0rQ,1390
403
- omlish/lang/__init__.py,sha256=7UEkGV5eWdCQRSqWvr3QNOfLKjkzLkF_ngm6fK0SUTk,4700
403
+ omlish/lang/__init__.py,sha256=TKxKq0L7Q34kdV0_UcxC81Umx64Uz_TPUgH6cy7Uzck,4892
404
404
  omlish/lang/attrs.py,sha256=fofCKN0X8TMu1yGqHpLpNLih9r9HWl3D3Vn3b6O791w,3891
405
405
  omlish/lang/clsdct.py,sha256=sJYadm-fwzti-gsi98knR5qQUxriBmOqQE_qz3RopNk,1743
406
406
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
@@ -415,13 +415,14 @@ omlish/lang/imports.py,sha256=Gdl6xCF89xiMOE1yDmdvKWamLq8HX-XPianO58Jdpmw,9218
415
415
  omlish/lang/iterables.py,sha256=HOjcxOwyI5bBApDLsxRAGGhTTmw7fdZl2kEckxRVl-0,1994
416
416
  omlish/lang/maybes.py,sha256=dAgrUoAhCgyrHRqa73CkaGnpXwGc-o9n-NIThrNXnbU,3416
417
417
  omlish/lang/objects.py,sha256=65XsD7UtblRdNe2ID1-brn_QvRkJhBIk5nyZWcQNeqU,4574
418
+ omlish/lang/params.py,sha256=AaUHI59qBLV7I-q1qPIEPTKGvgXzSRsmkNR2jTma5ZY,6688
418
419
  omlish/lang/resolving.py,sha256=OuN2mDTPNyBUbcrswtvFKtj4xgH4H4WglgqSKv3MTy0,1606
419
- omlish/lang/resources.py,sha256=N64KeVE-rYMxqBBRp91qzgVqpOVR2uX7k1WlS_bo5hM,2681
420
+ omlish/lang/resources.py,sha256=WKkAddC3ctMK1bvGw-elGe8ZxAj2IaUTKVSu2nfgHTo,2839
420
421
  omlish/lang/strings.py,sha256=egdv8PxLNG40-5V93agP5j2rBUDIsahCx048zV7uEbU,4690
421
422
  omlish/lang/sys.py,sha256=UoZz_PJYVKLQAKqYxxn-LHz1okK_38I__maZgnXMcxU,406
422
423
  omlish/lang/typing.py,sha256=Zdad9Zv0sa-hIaUXPrzPidT7sDVpRcussAI7D-j-I1c,3296
423
424
  omlish/lang/cached/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
424
- omlish/lang/cached/function.py,sha256=5pS3FCTxOradeMsrKg7JbXHsWhqdaa2C34apkXkwqqU,8785
425
+ omlish/lang/cached/function.py,sha256=3AeTVfpzovMMWbnjFuT5mM7kT1dBuk7fa66FOJKikYw,9188
425
426
  omlish/lang/cached/property.py,sha256=kzbao_35PlszdK_9oJBWrMmFFlVK_Xhx7YczHhTJ6cc,2764
426
427
  omlish/lang/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
427
428
  omlish/lang/classes/abstract.py,sha256=bIcuAetV_aChhpSURypmjjcqP07xi20uVYPKh1kvQNU,3710
@@ -734,9 +735,9 @@ omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
734
735
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
735
736
  omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
736
737
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
737
- omlish-0.0.0.dev248.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
738
- omlish-0.0.0.dev248.dist-info/METADATA,sha256=2MDwQ1CC7YMd2LkHobPoRqjE_TqGmuPaic_MlQcnojI,4176
739
- omlish-0.0.0.dev248.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
740
- omlish-0.0.0.dev248.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
741
- omlish-0.0.0.dev248.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
742
- omlish-0.0.0.dev248.dist-info/RECORD,,
738
+ omlish-0.0.0.dev250.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
739
+ omlish-0.0.0.dev250.dist-info/METADATA,sha256=I94K5DI1DH2JEs5HA15SkuwlyDsnTF5PYIeSr5rKrGo,4176
740
+ omlish-0.0.0.dev250.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
741
+ omlish-0.0.0.dev250.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
742
+ omlish-0.0.0.dev250.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
743
+ omlish-0.0.0.dev250.dist-info/RECORD,,