omlish 0.0.0.dev27__py3-none-any.whl → 0.0.0.dev29__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.dev27'
2
- __revision__ = 'c62e03dc2f032aa532d2c2b9f0d71ff96158cdec'
1
+ __version__ = '0.0.0.dev29'
2
+ __revision__ = 'cde08614ca06e103fcae31adbb677c772002aef1'
3
3
 
4
4
 
5
5
  #
@@ -30,7 +30,7 @@ class Project(ProjectBase):
30
30
 
31
31
  optional_dependencies = {
32
32
  'async': [
33
- 'anyio ~= 4.4',
33
+ 'anyio ~= 4.5',
34
34
  'sniffio ~= 1.3',
35
35
 
36
36
  'greenlet ~= 3.1',
@@ -70,8 +70,6 @@ class Project(ProjectBase):
70
70
  ],
71
71
 
72
72
  'misc': [
73
- 'jinja2 ~= 3.1',
74
-
75
73
  'wrapt ~= 1.14',
76
74
  ],
77
75
 
@@ -81,7 +79,9 @@ class Project(ProjectBase):
81
79
 
82
80
  'sql': [
83
81
  'sqlalchemy[asyncio] ~= 2.0',
82
+ ],
84
83
 
84
+ 'sql-drivers': [
85
85
  'pg8000 ~= 1.31',
86
86
  # 'psycopg2 ~= 2.9',
87
87
  # 'psycopg ~= 3.2',
@@ -93,9 +93,7 @@ class Project(ProjectBase):
93
93
  'aiomysql ~= 0.2',
94
94
  'aiosqlite ~= 0.20',
95
95
  'asyncpg ~= 0.29; python_version < "3.13"',
96
- ],
97
96
 
98
- 'sqlx': [
99
97
  'sqlean.py ~= 3.45; python_version < "3.13"',
100
98
 
101
99
  'duckdb ~= 1.1',
@@ -17,7 +17,6 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER I
17
17
  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
18
  """
19
19
  # ruff: noqa: SLF001
20
-
21
20
  import typing as ta
22
21
 
23
22
 
@@ -36,6 +36,7 @@ def field( # noqa
36
36
  check_type: bool | None = None,
37
37
  override: bool = False,
38
38
  repr_fn: ta.Callable[[ta.Any], str | None] | None = None,
39
+ frozen: bool | None = None,
39
40
  ): # -> dc.Field
40
41
  if default is not MISSING and default_factory is not MISSING:
41
42
  raise ValueError('cannot specify both default and default_factory')
@@ -47,6 +48,7 @@ def field( # noqa
47
48
  check_type=check_type,
48
49
  override=override,
49
50
  repr_fn=repr_fn,
51
+ frozen=frozen,
50
52
  )
51
53
 
52
54
  md: ta.Mapping = {FieldExtras: fx}
@@ -178,6 +178,9 @@ def field_init(
178
178
  if fx.derive is not None:
179
179
  raise NotImplementedError
180
180
 
181
+ if fx.frozen:
182
+ raise NotImplementedError
183
+
181
184
  if fx.coerce is not None:
182
185
  cn = f'__dataclass_coerce__{f.name}__'
183
186
  locals[cn] = fx.coerce
@@ -42,12 +42,13 @@ from .metadata import METADATA_ATTR
42
42
 
43
43
  @dc.dataclass(frozen=True, kw_only=True)
44
44
  class FieldExtras(lang.Final):
45
- derive: ta.Callable[..., ta.Any] | None = None
45
+ derive: ta.Callable[..., ta.Any] | None = None # TODO
46
46
  coerce: bool | ta.Callable[[ta.Any], ta.Any] | None = None
47
47
  validate: ta.Callable[[ta.Any], bool] | None = None
48
48
  check_type: bool | None = None
49
49
  override: bool = False
50
50
  repr_fn: ta.Callable[[ta.Any], str | None] | None = None
51
+ frozen: bool | None = None # TODO
51
52
 
52
53
 
53
54
  DEFAULT_FIELD_EXTRAS = FieldExtras()
omlish/docker.py CHANGED
@@ -170,9 +170,9 @@ _LIKELY_IN_DOCKER_PATTERN = re.compile(r'^overlay / .*/docker/')
170
170
 
171
171
 
172
172
  def is_likely_in_docker() -> bool:
173
- if sys.platform != 'linux':
173
+ if getattr(sys, 'platform') != 'linux':
174
174
  return False
175
- with open('/proc/mounts') as f: # type: ignore
175
+ with open('/proc/mounts') as f:
176
176
  ls = f.readlines()
177
177
  return any(_LIKELY_IN_DOCKER_PATTERN.match(l) for l in ls)
178
178
 
omlish/lang/__init__.py CHANGED
@@ -98,6 +98,7 @@ from .functions import ( # noqa
98
98
  Args,
99
99
  VoidError,
100
100
  as_async,
101
+ coalesce,
101
102
  constant,
102
103
  finally_,
103
104
  identity,
@@ -107,6 +108,7 @@ from .functions import ( # noqa
107
108
  isinstance_of,
108
109
  issubclass_of,
109
110
  maybe_call,
111
+ opt_coalesce,
110
112
  periodically,
111
113
  raise_,
112
114
  raising,
@@ -57,11 +57,6 @@ def unwrap_method_descriptors(fn: ta.Callable) -> ta.Callable:
57
57
  ##
58
58
 
59
59
 
60
- def unwrap_func(fn: ta.Callable) -> ta.Callable:
61
- fn, _ = unwrap_func_with_partials(fn)
62
- return fn
63
-
64
-
65
60
  def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functools.partial]]:
66
61
  ps = []
67
62
  while True:
@@ -88,6 +83,11 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
88
83
  return fn, ps
89
84
 
90
85
 
86
+ def unwrap_func(fn: ta.Callable) -> ta.Callable:
87
+ uw, _ = unwrap_func_with_partials(fn)
88
+ return uw
89
+
90
+
91
91
  ##
92
92
 
93
93
 
omlish/lang/functions.py CHANGED
@@ -183,3 +183,20 @@ class Args:
183
183
 
184
184
  def __call__(self, fn: ta.Callable[..., T]) -> T:
185
185
  return fn(*self.args, **self.kwargs)
186
+
187
+
188
+ ##
189
+
190
+
191
+ def coalesce(*vs: T | None) -> T:
192
+ for v in vs:
193
+ if v is not None:
194
+ return v
195
+ raise ValueError('No value given')
196
+
197
+
198
+ def opt_coalesce(*vs: T | None) -> T | None:
199
+ for v in vs:
200
+ if v is not None:
201
+ return v
202
+ return None
omlish/lite/secrets.py CHANGED
@@ -20,3 +20,17 @@ class Secret:
20
20
 
21
21
  def reveal(self) -> str:
22
22
  return getattr(self, self._VALUE_ATTR)()
23
+
24
+ #
25
+
26
+ def __reduce__(self) -> ta.NoReturn:
27
+ raise TypeError
28
+
29
+ def __reduce_ex__(self, protocol) -> ta.NoReturn:
30
+ raise TypeError
31
+
32
+ def __getstate__(self) -> ta.NoReturn:
33
+ raise TypeError
34
+
35
+ def __setstate__(self, state) -> ta.NoReturn:
36
+ raise TypeError
@@ -55,8 +55,13 @@ from .helpers import ( # noqa
55
55
  )
56
56
 
57
57
  from .objects import ( # noqa
58
+ FieldInfo,
58
59
  FieldMetadata,
60
+ ObjectMarshaler,
59
61
  ObjectMetadata,
62
+ ObjectUnmarshaler,
63
+ SimpleObjectMarshalerFactory,
64
+ SimpleObjectUnmarshalerFactory,
60
65
  )
61
66
 
62
67
  from .polymorphism import ( # noqa
omlish/marshal/objects.py CHANGED
@@ -9,6 +9,7 @@ import typing as ta
9
9
 
10
10
  from .. import check
11
11
  from .. import dataclasses as dc
12
+ from .. import reflect as rfl
12
13
  from .base import MarshalContext
13
14
  from .base import Marshaler
14
15
  from .base import MarshalerFactory
@@ -87,6 +88,27 @@ class ObjectMarshaler(Marshaler):
87
88
  return ret
88
89
 
89
90
 
91
+ @dc.dataclass(frozen=True)
92
+ class SimpleObjectMarshalerFactory(MarshalerFactory):
93
+ dct: ta.Mapping[type, ta.Sequence[FieldInfo]]
94
+ unknown_field: str | None = None
95
+
96
+ def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
97
+ return isinstance(rty, type) and rty in self.dct
98
+
99
+ def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
100
+ ty = check.isinstance(rty, type)
101
+ flds = self.dct[ty]
102
+ fields = [
103
+ (fi, ctx.make(fi.type))
104
+ for fi in flds
105
+ ]
106
+ return ObjectMarshaler(
107
+ fields,
108
+ unknown_field=self.unknown_field,
109
+ )
110
+
111
+
90
112
  ##
91
113
 
92
114
 
@@ -123,3 +145,26 @@ class ObjectUnmarshaler(Unmarshaler):
123
145
  kw[fi.name] = u.unmarshal(ctx, mv)
124
146
 
125
147
  return self.cls(**kw)
148
+
149
+
150
+ @dc.dataclass(frozen=True)
151
+ class SimpleObjectUnmarshalerFactory(UnmarshalerFactory):
152
+ dct: ta.Mapping[type, ta.Sequence[FieldInfo]]
153
+ unknown_field: str | None = None
154
+
155
+ def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
156
+ return isinstance(rty, type) and rty in self.dct
157
+
158
+ def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
159
+ ty = check.isinstance(rty, type)
160
+ flds = self.dct[ty]
161
+ fields_by_unmarshal_name = {
162
+ n: (fi, ctx.make(fi.type))
163
+ for fi in flds
164
+ for n in fi.unmarshal_names
165
+ }
166
+ return ObjectUnmarshaler(
167
+ ty,
168
+ fields_by_unmarshal_name,
169
+ unknown_field=self.unknown_field,
170
+ )
File without changes
@@ -1,15 +1,10 @@
1
1
  import functools
2
- import struct
3
2
  import typing as ta
4
3
 
5
4
 
6
5
  ##
7
6
 
8
7
 
9
- def isclose(a: float, b: float, *, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> float:
10
- return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
11
-
12
-
13
8
  def get_bit(bit: int, value: int) -> int:
14
9
  return (value >> bit) & 1
15
10
 
@@ -29,14 +24,6 @@ def set_bits(bits_from: int, num_bits: int, bits_value: int, value: int) -> int:
29
24
  return value & ~(((1 << num_bits) - 1) << bits_from) | (bits_value << bits_from)
30
25
 
31
26
 
32
- def float_to_bytes(f: float) -> bytes:
33
- return struct.pack('>f', f)
34
-
35
-
36
- def bytes_to_float(b: bytes) -> float:
37
- return struct.unpack('>f', b)[0]
38
-
39
-
40
27
  ##
41
28
 
42
29
 
omlish/math/floats.py ADDED
@@ -0,0 +1,13 @@
1
+ import struct
2
+
3
+
4
+ def isclose(a: float, b: float, *, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> float:
5
+ return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
6
+
7
+
8
+ def float_to_bytes(f: float) -> bytes:
9
+ return struct.pack('>f', f)
10
+
11
+
12
+ def bytes_to_float(b: bytes) -> float:
13
+ return struct.unpack('>f', b)[0]
@@ -3,6 +3,7 @@ TODO:
3
3
  - reservoir
4
4
  - dep tdigest?
5
5
  - struct-of-arrays - array.array('f', ...) - backed SamplingHistogram
6
+ - https://docs.python.org/3/library/statistics.html
6
7
  """
7
8
  import bisect
8
9
  import collections
@@ -14,8 +15,8 @@ import random
14
15
  import time
15
16
  import typing as ta
16
17
 
17
- from . import cached
18
- from . import check
18
+ from .. import cached
19
+ from .. import check
19
20
 
20
21
 
21
22
  ##
omlish/text/asdl.py ADDED
@@ -0,0 +1,529 @@
1
+ """
2
+ https://github.com/python/cpython/blob/21d2a9ab2f4dcbf1be462d3b7f7a231a46bc1cb7/Parser/asdl.py
3
+
4
+ -------------------------------------------------------------------------------
5
+
6
+ Parser for ASDL [1] definition files. Reads in an ASDL description and parses it into an AST that describes it.
7
+
8
+ The EBNF we're parsing here: Figure 1 of the paper [1]. Extended to support modules and attributes after a product.
9
+ Words starting with Capital letters are terminals. Literal tokens are in "double quotes". Others are non-terminals. Id
10
+ is either TokenId or ConstructorId.
11
+
12
+ module ::= "module" Id "{" [definitions] "}"
13
+ definitions ::= { TypeId "=" type }
14
+ type ::= product | sum
15
+ product ::= fields ["attributes" fields]
16
+ fields ::= "(" { field, "," } field ")"
17
+ field ::= TypeId ["?" | "*"] [Id]
18
+ sum ::= constructor { "|" constructor } ["attributes" fields]
19
+ constructor ::= ConstructorId [fields]
20
+
21
+ [1] "The Zephyr Abstract Syntax Description Language" by Wang, et. al. See http://asdl.sourceforge.net/
22
+ """
23
+ # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
24
+ # --------------------------------------------
25
+ #
26
+ # 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization
27
+ # ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated
28
+ # documentation.
29
+ #
30
+ # 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive,
31
+ # royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative
32
+ # works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License
33
+ # Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001-2024 Python Software Foundation; All Rights
34
+ # Reserved" are retained in Python alone or in any derivative version prepared by Licensee.
35
+ #
36
+ # 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and
37
+ # wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in
38
+ # any such work a brief summary of the changes made to Python.
39
+ #
40
+ # 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES,
41
+ # EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
42
+ # OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY
43
+ # RIGHTS.
44
+ #
45
+ # 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
46
+ # DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF
47
+ # ADVISED OF THE POSSIBILITY THEREOF.
48
+ #
49
+ # 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
50
+ #
51
+ # 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint
52
+ # venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade
53
+ # name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
54
+ #
55
+ # 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
56
+ # License Agreement.
57
+ # ruff: noqa: N802
58
+ import abc
59
+ import dataclasses as dc
60
+ import enum
61
+ import re
62
+ import typing as ta
63
+
64
+ from .. import cached
65
+ from .. import check
66
+
67
+
68
+ ##
69
+
70
+
71
+ # The following classes define nodes into which the Asdl description is parsed. Note: this is a "meta-AST". Asdl files
72
+ # (such as Python.asdl) describe the AST structure used by a programming language. But Asdl files themselves need to be
73
+ # parsed. This module parses Asdl files and uses a simple AST to represent them. See the EBNF at the top of the file to
74
+ # understand the logical connection between the various node types.
75
+
76
+ class Ast(abc.ABC):
77
+ @abc.abstractmethod
78
+ def __repr__(self) -> str:
79
+ raise NotImplementedError
80
+
81
+
82
+ @dc.dataclass(frozen=True)
83
+ class Field(Ast):
84
+ type: str
85
+ name: str | None = None
86
+ seq: bool = False
87
+ opt: bool = False
88
+
89
+ def __str__(self) -> str:
90
+ if self.seq:
91
+ extra = '*'
92
+ elif self.opt:
93
+ extra = '?'
94
+ else:
95
+ extra = ''
96
+
97
+ return f'{self.type}{extra} {self.name}'
98
+
99
+ def __repr__(self) -> str:
100
+ if self.seq:
101
+ extra = ', seq=True'
102
+ elif self.opt:
103
+ extra = ', opt=True'
104
+ else:
105
+ extra = ''
106
+ if self.name is None:
107
+ return f'Field({self.type}{extra})'
108
+ else:
109
+ return f'Field({self.type}, {self.name}{extra})'
110
+
111
+
112
+ @dc.dataclass(frozen=True)
113
+ class Constructor(Ast):
114
+ name: str
115
+ fields: ta.Sequence[Field] | None = None
116
+
117
+ def __repr__(self) -> str:
118
+ return f'Constructor({self.name}, {self.fields})'
119
+
120
+
121
+ @dc.dataclass(frozen=True)
122
+ class Sum(Ast):
123
+ types: ta.Sequence[Constructor]
124
+ attributes: ta.Sequence[Field] | None = None
125
+
126
+ def __repr__(self) -> str:
127
+ if self.attributes:
128
+ return f'Sum({self.types}, {self.attributes})'
129
+ else:
130
+ return f'Sum({self.types})'
131
+
132
+
133
+ @dc.dataclass(frozen=True)
134
+ class Product(Ast):
135
+ fields: ta.Sequence[Field]
136
+ attributes: ta.Sequence[Field] | None = None
137
+
138
+ def __repr__(self) -> str:
139
+ if self.attributes:
140
+ return f'Product({self.fields}, {self.attributes})'
141
+ else:
142
+ return f'Product({self.fields})'
143
+
144
+
145
+ @dc.dataclass(frozen=True)
146
+ class Type(Ast):
147
+ name: str
148
+ value: Sum | Product
149
+
150
+ def __repr__(self) -> str:
151
+ return f'Type({self.name}, {self.value})'
152
+
153
+
154
+ @dc.dataclass(frozen=True)
155
+ class Module(Ast):
156
+ name: str
157
+ dfns: ta.Sequence[Type]
158
+
159
+ @cached.property
160
+ def types(self) -> ta.Mapping[str, Sum | Product]:
161
+ return {ty.name: ty.value for ty in self.dfns}
162
+
163
+ def __repr__(self) -> str:
164
+ return f'Module({self.name}, {self.dfns})'
165
+
166
+
167
+ ##
168
+
169
+
170
+ # A generic visitor for the meta-Ast that describes Asdl. This can be used by emitters. Note that this visitor does not
171
+ # provide a generic visit method, so a subclass needs to define visit methods from visitModule to as deep as the
172
+ # interesting node.
173
+ # We also define a Check visitor that makes sure the parsed Asdl is well-formed.
174
+
175
+ class VisitorBase:
176
+ """Generic tree visitor for Asts."""
177
+
178
+ def __init__(self) -> None:
179
+ super().__init__()
180
+ self.cache: dict[type, ta.Any] = {}
181
+
182
+ def visit(self, obj, *args):
183
+ klass = obj.__class__
184
+ meth = self.cache.get(klass)
185
+ if meth is None:
186
+ methname = 'visit' + klass.__name__
187
+ meth = getattr(self, methname, None)
188
+ self.cache[klass] = meth
189
+ if meth:
190
+ try:
191
+ meth(obj, *args)
192
+ except Exception as e:
193
+ raise Exception(f'Error visiting {obj!r}') from e
194
+
195
+
196
+ ##
197
+
198
+
199
+ BUILTIN_TYPES: ta.AbstractSet[str] = {
200
+ 'identifier',
201
+ 'string',
202
+ 'int',
203
+ 'constant',
204
+ }
205
+
206
+
207
+ ##
208
+
209
+
210
+ # Types for describing tokens in an Asdl specification.
211
+ class TokenKind(enum.IntEnum):
212
+ """TokenKind is provides a scope for enumerated token kinds."""
213
+
214
+ CONSTRUCTOR_ID = enum.auto()
215
+ TYPE_ID = enum.auto()
216
+ EQUALS = enum.auto()
217
+ COMMA = enum.auto()
218
+ QUESTION = enum.auto()
219
+ PIPE = enum.auto()
220
+ ASTERISK = enum.auto()
221
+ L_PAREN = enum.auto()
222
+ R_PAREN = enum.auto()
223
+ L_BRACE = enum.auto()
224
+ R_BRACE = enum.auto()
225
+
226
+
227
+ OPERATOR_TABLE: ta.Mapping[str, TokenKind] = {
228
+ '=': TokenKind.EQUALS,
229
+ ',': TokenKind.COMMA,
230
+ '?': TokenKind.QUESTION,
231
+ '|': TokenKind.PIPE,
232
+ '(': TokenKind.L_PAREN,
233
+ ')': TokenKind.R_PAREN,
234
+ '*': TokenKind.ASTERISK,
235
+ '{': TokenKind.L_BRACE,
236
+ '}': TokenKind.R_BRACE,
237
+ }
238
+
239
+
240
+ @dc.dataclass(frozen=True)
241
+ class Token:
242
+ kind: TokenKind
243
+ value: str
244
+ lineno: int
245
+
246
+
247
+ class AsdlSyntaxError(Exception):
248
+ def __init__(self, msg: str, lineno: int | None = None) -> None:
249
+ super().__init__()
250
+ self.msg = msg
251
+ self.lineno = lineno or '<unknown>'
252
+
253
+ def __str__(self) -> str:
254
+ return f'Syntax error on line {self.lineno}: {self.msg}'
255
+
256
+
257
+ def tokenize_asdl(buf: str) -> ta.Iterator[Token]:
258
+ """Tokenize the given buffer. Yield Token objects."""
259
+
260
+ for lineno, line in enumerate(buf.splitlines(), 1):
261
+ for m in re.finditer(r'\s*(\w+|--.*|.)', line.strip()):
262
+ c = m.group(1)
263
+ if c[0].isalpha():
264
+ # Some kind of identifier
265
+ if c[0].isupper():
266
+ yield Token(TokenKind.CONSTRUCTOR_ID, c, lineno)
267
+ else:
268
+ yield Token(TokenKind.TYPE_ID, c, lineno)
269
+ elif c[:2] == '--':
270
+ # Comment
271
+ break
272
+ else:
273
+ # Operators
274
+ try:
275
+ op_kind = OPERATOR_TABLE[c]
276
+ except KeyError:
277
+ raise AsdlSyntaxError(f'Invalid operator {c}', lineno) # noqa
278
+ yield Token(op_kind, c, lineno)
279
+
280
+
281
+ ##
282
+
283
+
284
+ class AsdlParser:
285
+ """
286
+ Parser for Asdl files.
287
+
288
+ Create, then call the parse method on a buffer containing Asdl. This is a simple recursive descent parser that uses
289
+ tokenize_asdl for the lexing.
290
+ """
291
+
292
+ def __init__(self) -> None:
293
+ super().__init__()
294
+ self._tokenizer: ta.Iterator[Token] | None = None
295
+ self.cur_token: Token | None = None
296
+
297
+ def cur(self) -> Token:
298
+ return check.not_none(self.cur_token)
299
+
300
+ def parse(self, buf: str) -> Module:
301
+ """Parse the Asdl in the buffer and return an Ast with a Module root."""
302
+
303
+ self._tokenizer = tokenize_asdl(buf)
304
+ self._advance()
305
+ return self._parse_module()
306
+
307
+ def _parse_module(self) -> Module:
308
+ if self._at_keyword('module'):
309
+ self._advance()
310
+ else:
311
+ raise AsdlSyntaxError(f'Expected "module" (found {self.cur().value})', self.cur().lineno) # noqa
312
+ name = self._match(self._id_kinds)
313
+ self._match(TokenKind.L_BRACE)
314
+ defs = self._parse_definitions()
315
+ self._match(TokenKind.R_BRACE)
316
+ return Module(name, defs)
317
+
318
+ def _parse_definitions(self) -> ta.Sequence[Type]:
319
+ defs = []
320
+ while self.cur().kind == TokenKind.TYPE_ID:
321
+ typename = check.non_empty_str(self._advance())
322
+ self._match(TokenKind.EQUALS)
323
+ ty = self._parse_type()
324
+ defs.append(Type(typename, ty))
325
+ return defs
326
+
327
+ def _parse_type(self) -> Sum | Product:
328
+ if self.cur().kind == TokenKind.L_PAREN:
329
+ # If we see a (, it's a product
330
+ return self._parse_product()
331
+ else:
332
+ # Otherwise it's a sum. Look for ConstructorId
333
+ sumlist = [Constructor(self._match(TokenKind.CONSTRUCTOR_ID), self._parse_optional_fields())]
334
+ while self.cur().kind == TokenKind.PIPE:
335
+ # More constructors
336
+ self._advance()
337
+ sumlist.append(Constructor(
338
+ self._match(TokenKind.CONSTRUCTOR_ID),
339
+ self._parse_optional_fields()),
340
+ )
341
+ return Sum(sumlist, self._parse_optional_attributes())
342
+
343
+ def _parse_product(self) -> Product:
344
+ return Product(self._parse_fields(), self._parse_optional_attributes())
345
+
346
+ def _parse_fields(self) -> ta.Sequence[Field]:
347
+ fields = []
348
+ self._match(TokenKind.L_PAREN)
349
+ while self.cur().kind == TokenKind.TYPE_ID:
350
+ typename = check.non_empty_str(self._advance())
351
+ is_seq, is_opt = self._parse_optional_field_quantifier()
352
+ id = self._advance() if self.cur().kind in self._id_kinds else None # noqa
353
+ fields.append(Field(typename, id, seq=is_seq, opt=is_opt))
354
+ if self.cur().kind == TokenKind.R_PAREN:
355
+ break
356
+ elif self.cur().kind == TokenKind.COMMA:
357
+ self._advance()
358
+ self._match(TokenKind.R_PAREN)
359
+ return fields
360
+
361
+ def _parse_optional_fields(self) -> ta.Sequence[Field] | None:
362
+ if self.cur().kind == TokenKind.L_PAREN:
363
+ return self._parse_fields()
364
+ else:
365
+ return None
366
+
367
+ def _parse_optional_attributes(self) -> ta.Sequence[Field] | None:
368
+ if self._at_keyword('attributes'):
369
+ self._advance()
370
+ return self._parse_fields()
371
+ else:
372
+ return None
373
+
374
+ def _parse_optional_field_quantifier(self) -> tuple[bool, bool]: # (seq, opt)
375
+ is_seq, is_opt = False, False
376
+ if self.cur().kind == TokenKind.ASTERISK:
377
+ is_seq = True
378
+ self._advance()
379
+ elif self.cur().kind == TokenKind.QUESTION:
380
+ is_opt = True
381
+ self._advance()
382
+ return is_seq, is_opt
383
+
384
+ def _advance(self) -> str | None:
385
+ """Return the value of the current token and read the next one into self.cur_token."""
386
+
387
+ cur_val = None if self.cur_token is None else self.cur_token.value
388
+ try:
389
+ self.cur_token = next(check.not_none(self._tokenizer))
390
+ except StopIteration:
391
+ self.cur_token = None
392
+ return cur_val
393
+
394
+ _id_kinds = (TokenKind.CONSTRUCTOR_ID, TokenKind.TYPE_ID)
395
+
396
+ def _match(self, kind: TokenKind | tuple[TokenKind, ...]) -> str:
397
+ """
398
+ The 'match' primitive of RD parsers.
399
+
400
+ * Verifies that the current token is of the given kind (kind can be a tuple, in which the kind must match one of
401
+ its members).
402
+ * Returns the value of the current token
403
+ * Reads in the next token
404
+ """
405
+
406
+ if isinstance(kind, tuple) and self.cur().kind in kind or self.cur().kind == kind:
407
+ value = self.cur().value
408
+ self._advance()
409
+ return value
410
+ else:
411
+ raise AsdlSyntaxError(
412
+ f'Unmatched {kind} (found {self.cur().kind})',
413
+ self.cur().lineno,
414
+ )
415
+
416
+ def _at_keyword(self, keyword: str) -> bool:
417
+ return self.cur().kind == TokenKind.TYPE_ID and self.cur().value == keyword
418
+
419
+
420
+ ##
421
+
422
+
423
+ FlatFieldArity: ta.TypeAlias = ta.Literal[1, '?', '*']
424
+
425
+
426
+ @dc.dataclass(frozen=True)
427
+ class FlatField:
428
+ name: str
429
+ type: str
430
+ n: FlatFieldArity = 1
431
+
432
+
433
+ @dc.dataclass(frozen=True)
434
+ class FlatNode(abc.ABC):
435
+ name: str
436
+ fields: ta.Sequence[FlatField] = dc.field(default=(), kw_only=True)
437
+ attributes: ta.Sequence[FlatField] = dc.field(default=(), kw_only=True)
438
+
439
+
440
+ @dc.dataclass(frozen=True)
441
+ class FlatSum(FlatNode):
442
+ constructors: ta.Sequence[str] = dc.field(default=(), kw_only=True)
443
+
444
+
445
+ @dc.dataclass(frozen=True)
446
+ class FlatProduct(FlatNode):
447
+ pass
448
+
449
+
450
+ @dc.dataclass(frozen=True)
451
+ class FlatConstructor(FlatNode):
452
+ sum: str = dc.field(kw_only=True)
453
+
454
+
455
+ def flatten(mod: Module) -> ta.Mapping[str, FlatNode]:
456
+ lst: list[FlatNode] = []
457
+
458
+ def mk_field(af: Field) -> FlatField:
459
+ return FlatField(
460
+ check.non_empty_str(af.name),
461
+ af.type,
462
+ n='*' if af.seq else '?' if af.opt else 1,
463
+ )
464
+
465
+ def mk_fields(afs: ta.Iterable[Field] | None) -> ta.Sequence[FlatField]:
466
+ return list(map(mk_field, afs or []))
467
+
468
+ for ty in mod.dfns:
469
+ v = ty.value
470
+
471
+ if isinstance(v, Sum):
472
+ lst.append(FlatSum(
473
+ ty.name,
474
+ attributes=mk_fields(v.attributes),
475
+ constructors=[c.name for c in v.types],
476
+ ))
477
+
478
+ for c in v.types:
479
+ lst.append(FlatConstructor(
480
+ c.name,
481
+ fields=mk_fields(c.fields),
482
+ attributes=mk_fields(v.attributes),
483
+ sum=ty.name,
484
+ ))
485
+
486
+ elif isinstance(v, Product):
487
+ lst.append(FlatProduct(
488
+ ty.name,
489
+ fields=mk_fields(v.fields),
490
+ attributes=mk_fields(v.attributes),
491
+ ))
492
+
493
+ else:
494
+ raise TypeError(v)
495
+
496
+ #
497
+
498
+ dct: dict[str, FlatNode] = {}
499
+
500
+ for n in lst:
501
+ if n.name in dct:
502
+ raise KeyError(n.name)
503
+ dct[n.name] = n
504
+
505
+ return dct
506
+
507
+
508
+ #
509
+
510
+
511
+ def build_fields_info(
512
+ nodes: ta.Mapping[str, FlatNode],
513
+ *,
514
+ exclude_builtins: bool = False,
515
+ ) -> ta.Mapping[str, ta.Mapping[str, tuple[str, FlatFieldArity]]]:
516
+ dct: dict[str, dict[str, tuple[str, FlatFieldArity]]] = {}
517
+ for n in nodes.values():
518
+ cur = {}
519
+ f: FlatField
520
+ for f in [*n.fields, *n.attributes]:
521
+ if f.type in BUILTIN_TYPES:
522
+ if exclude_builtins:
523
+ continue
524
+ elif f.type not in nodes:
525
+ raise KeyError(f.type)
526
+ cur[f.name] = (f.type, f.n)
527
+ if cur:
528
+ dct[n.name] = cur
529
+ return dct
omlish/text/parts.py CHANGED
@@ -30,7 +30,7 @@ def _check_part(o: PartT) -> PartT:
30
30
  return o
31
31
 
32
32
 
33
- def _check_optional_part(o: PartT | None) -> PartT | None:
33
+ def _check_opt_part(o: PartT | None) -> PartT | None:
34
34
  if o is None:
35
35
  return None
36
36
  return _check_part(o)
@@ -49,7 +49,7 @@ class Wrap(DataPart, lang.Final):
49
49
 
50
50
 
51
51
  class List(DataPart, lang.Final):
52
- parts: ta.Sequence[Part | None] = dc.xfield(coerce=col.seq_of(_check_optional_part))
52
+ parts: ta.Sequence[Part | None] = dc.xfield(coerce=col.seq_of(_check_opt_part))
53
53
  delimiter: str = dc.field(default=',') # FIXME: , check_type=str)
54
54
  trailer: bool = dc.field(default=False) # FIXME: , check_type=bool)
55
55
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev27
3
+ Version: 0.0.0.dev29
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -13,7 +13,7 @@ Classifier: Operating System :: POSIX
13
13
  Requires-Python: ~=3.12
14
14
  License-File: LICENSE
15
15
  Provides-Extra: all
16
- Requires-Dist: anyio ~=4.4 ; extra == 'all'
16
+ Requires-Dist: anyio ~=4.5 ; extra == 'all'
17
17
  Requires-Dist: sniffio ~=1.3 ; extra == 'all'
18
18
  Requires-Dist: greenlet ~=3.1 ; extra == 'all'
19
19
  Requires-Dist: trio ~=0.26 ; extra == 'all'
@@ -29,7 +29,6 @@ Requires-Dist: json5 ~=0.9 ; extra == 'all'
29
29
  Requires-Dist: pyyaml ~=5.0 ; extra == 'all'
30
30
  Requires-Dist: cloudpickle ~=3.0 ; extra == 'all'
31
31
  Requires-Dist: httpx[http2] ~=0.27 ; extra == 'all'
32
- Requires-Dist: jinja2 ~=3.1 ; extra == 'all'
33
32
  Requires-Dist: wrapt ~=1.14 ; extra == 'all'
34
33
  Requires-Dist: cryptography ~=43.0 ; extra == 'all'
35
34
  Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; extra == 'all'
@@ -43,7 +42,7 @@ Requires-Dist: python-snappy ~=0.7 ; (python_version < "3.13") and extra == 'all
43
42
  Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'all'
44
43
  Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'all'
45
44
  Provides-Extra: async
46
- Requires-Dist: anyio ~=4.4 ; extra == 'async'
45
+ Requires-Dist: anyio ~=4.5 ; extra == 'async'
47
46
  Requires-Dist: sniffio ~=1.3 ; extra == 'async'
48
47
  Requires-Dist: greenlet ~=3.1 ; extra == 'async'
49
48
  Requires-Dist: trio ~=0.26 ; extra == 'async'
@@ -65,20 +64,19 @@ Requires-Dist: cloudpickle ~=3.0 ; extra == 'formats'
65
64
  Provides-Extra: http
66
65
  Requires-Dist: httpx[http2] ~=0.27 ; extra == 'http'
67
66
  Provides-Extra: misc
68
- Requires-Dist: jinja2 ~=3.1 ; extra == 'misc'
69
67
  Requires-Dist: wrapt ~=1.14 ; extra == 'misc'
70
68
  Provides-Extra: secrets
71
69
  Requires-Dist: cryptography ~=43.0 ; extra == 'secrets'
72
70
  Provides-Extra: sql
73
71
  Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; extra == 'sql'
74
- Requires-Dist: pg8000 ~=1.31 ; extra == 'sql'
75
- Requires-Dist: pymysql ~=1.1 ; extra == 'sql'
76
- Requires-Dist: aiomysql ~=0.2 ; extra == 'sql'
77
- Requires-Dist: aiosqlite ~=0.20 ; extra == 'sql'
78
- Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'sql'
79
- Provides-Extra: sqlx
80
- Requires-Dist: duckdb ~=1.1 ; extra == 'sqlx'
81
- Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'sqlx'
72
+ Provides-Extra: sql-drivers
73
+ Requires-Dist: pg8000 ~=1.31 ; extra == 'sql-drivers'
74
+ Requires-Dist: pymysql ~=1.1 ; extra == 'sql-drivers'
75
+ Requires-Dist: aiomysql ~=0.2 ; extra == 'sql-drivers'
76
+ Requires-Dist: aiosqlite ~=0.20 ; extra == 'sql-drivers'
77
+ Requires-Dist: duckdb ~=1.1 ; extra == 'sql-drivers'
78
+ Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'sql-drivers'
79
+ Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'sql-drivers'
82
80
  Provides-Extra: testing
83
81
  Requires-Dist: pytest ~=8.0 ; extra == 'testing'
84
82
 
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
2
- omlish/__about__.py,sha256=zjwXesy8pai60AJpe7FV4t72dwRlRVraIytt0d1XmnE,2721
2
+ omlish/__about__.py,sha256=KzvTeQDmeZtJfbz_X82IPqzh5xJ44YhJRVMI4N3c2Ig,2698
3
3
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omlish/argparse.py,sha256=QRQmX9G0-L_nATkFtGHvpd4qrpYzKATdjuFLbBqzJPM,6224
5
5
  omlish/c3.py,sha256=W5EwYx9Por3rWYLkKUitJ6OoRMLLgVTfLTyroOz41Y0,8047
@@ -7,18 +7,16 @@ omlish/cached.py,sha256=UAizxlH4eMWHPzQtmItmyE6FEpFEUFzIkxaO2BHWZ5s,196
7
7
  omlish/check.py,sha256=3qp1_W8uRp23I26nWvG_c7YFxdTwJAZEFxmY8Bfw50Y,10078
8
8
  omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
9
  omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
10
- omlish/docker.py,sha256=GyzrHUTdD3rHeDBPGOI_mBzysVDtv5YAo1XN-HnMeo8,6231
10
+ omlish/docker.py,sha256=wipM7Xsx7rUv7-PoBTTT2v0uk3JVan0USljgm2QesXg,6227
11
11
  omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
12
12
  omlish/fnpairs.py,sha256=hVuLqQFdRNNze3FYH2cAQO3GC7nK5yQP_GWPUSbL7nE,10601
13
13
  omlish/genmachine.py,sha256=LCMiqvK32dAWtrlB6lKw9tXdQFiXC8rRdk4TMQYIroU,1603
14
14
  omlish/iterators.py,sha256=GGLC7RIT86uXMjhIIIqnff_Iu5SI_b9rXYywYGFyzmo,7292
15
15
  omlish/libc.py,sha256=u0481imCiTFqP_e-v9g0pD-0WD249j5vYzhtn-fnNkY,15308
16
16
  omlish/matchfns.py,sha256=ygqbqthRxgF9I1PJaw9Xl7FoZnAVMmnKBrYgimKQ0ag,6152
17
- omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
18
17
  omlish/multiprocessing.py,sha256=QZT4C7I-uThCAjaEY3xgUYb-5GagUlnE4etN01LDyU4,5186
19
18
  omlish/os.py,sha256=cz4nL2ujaxH_-XRq3JUD8af8mSe1JXGPIoXP9XAEd0M,2607
20
19
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
21
- omlish/stats.py,sha256=vJOJgYIW6DGDEgNm5AGWIp08GiLk61Cjld71fnC29fY,9998
22
20
  omlish/sync.py,sha256=AqwIfIuCMVHLwlJUa7dmaSjfA4sM5AYPCD5-nsz3XVQ,1516
23
21
  omlish/term.py,sha256=NEmxqAhicyInGtmFamZAizI2xdu819MzFYPEp0Fx97M,6111
24
22
  omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
@@ -50,7 +48,7 @@ omlish/collections/ordered.py,sha256=RzEC3fHvrDeJQSWThVDNYQKke263Vje1II5YwtDwT1Q
50
48
  omlish/collections/persistent.py,sha256=KG471s0bhhReQrjlmX0xaN9HeAIcrtT264ddZCxsExo,875
51
49
  omlish/collections/skiplist.py,sha256=xjuKZtSScp1VnOi9lpf7I090vGp1DnjA5ELjFhMeGps,5987
52
50
  omlish/collections/sorted.py,sha256=E5ZOdNn7Jju1EcQ7CX2Ltk9StIXsBOzqvh7EsT3ZA2U,3354
53
- omlish/collections/treap.py,sha256=wq9L5hvxq4QgPvIpHmueZMF8t7UrvX5vmlNN4BOqY4g,7720
51
+ omlish/collections/treap.py,sha256=A09ZPacGyJlyRB-Wi4TNj3tE0w-RpFNliPgpDaa6Vwg,7719
54
52
  omlish/collections/treapmap.py,sha256=TxOM-ZRF5PK2xe5wRIhESNt7DGh9b_MeZfE7HLkCOs4,5804
55
53
  omlish/collections/unmodifiable.py,sha256=QmUEi9IBXqiM_KGgH2rqg15VmkHJo1MZ6kwq2twEMho,4750
56
54
  omlish/collections/utils.py,sha256=9o9STwzAn5YjZRZ9ns1kuo7NgLXLaoVPFu6AJd-3JT8,4336
@@ -70,12 +68,12 @@ omlish/dataclasses/__init__.py,sha256=L2kRMvsWgsennXVw7VgZdczCtdLsQzyPcMFit2rBpb
70
68
  omlish/dataclasses/utils.py,sha256=qBKHyW2LSjnJYfPSAExEsVZ5EdmDBfp4napAhfWkZFs,3221
71
69
  omlish/dataclasses/impl/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
72
70
  omlish/dataclasses/impl/__init__.py,sha256=rQJRcE9fVsGEvRUOZ18r4DE0xiLbSRjspyJRXgbLS3I,348
73
- omlish/dataclasses/impl/api.py,sha256=PyBMuoamkp77r3OS7TIWKv27cy7laqN2iviq5JoihuE,6339
71
+ omlish/dataclasses/impl/api.py,sha256=UsCUtpHbElFEht0h-LiLKKOwlsrngmryz8jy9HDbNLw,6398
74
72
  omlish/dataclasses/impl/as_.py,sha256=CD-t7hkC1EP2F_jvZKIA_cVoDuwZ-Ln_xC4fJumPYX0,2598
75
73
  omlish/dataclasses/impl/copy.py,sha256=Tn8_n6Vohs-w4otbGdubBEvhd3TsSTaM3EfNGdS2LYo,591
76
74
  omlish/dataclasses/impl/descriptors.py,sha256=rEYE1Len99agTQCC25hSPMnM19BgPr0ZChABGi58Fdk,2476
77
75
  omlish/dataclasses/impl/exceptions.py,sha256=DeiM6rcjgncudn-XVuph9TDbVDEwBtyYb1bcbO3FFcA,193
78
- omlish/dataclasses/impl/fields.py,sha256=yh2mwXVZW-eOhPesY-BG9sVMWSdn0VqzXNHkbs4Jmok,5987
76
+ omlish/dataclasses/impl/fields.py,sha256=DrFPLlPeJ4dCbbRsuwfx-y47Fx3iSllJP0YM5eq6nIE,6040
79
77
  omlish/dataclasses/impl/frozen.py,sha256=x87DSM8FIMZ3c_BIUE8NooCkExFjPsabeqIueEP5qKs,2988
80
78
  omlish/dataclasses/impl/hashing.py,sha256=FKnHuXCg9ylrzK2TLGqO5yfRN4HX3F415CSLlVYXtYE,3190
81
79
  omlish/dataclasses/impl/init.py,sha256=IgxO9nwHaHF8jGrUAk-Y5xke9uV2OwzfEe-88McE1Wg,6161
@@ -84,7 +82,7 @@ omlish/dataclasses/impl/main.py,sha256=Ti0PKbFKraKvfmoPuR-G7nLVNzRC8mvEuXhCuC-M2
84
82
  omlish/dataclasses/impl/metaclass.py,sha256=dlQEIN9MHBirll7Nx3StpzxYxXjrqxJ-QsorMcCNt7w,2828
85
83
  omlish/dataclasses/impl/metadata.py,sha256=4veWwTr-aA0KP-Y1cPEeOcXHup9EKJTYNJ0ozIxtzD4,1401
86
84
  omlish/dataclasses/impl/order.py,sha256=zWvWDkSTym8cc7vO1cLHqcBhhjOlucHOCUVJcdh4jt0,1369
87
- omlish/dataclasses/impl/params.py,sha256=1kvL75eptGll9NXqBv66HN94ACwv_tucBRb1cDAvglM,2620
85
+ omlish/dataclasses/impl/params.py,sha256=KOeRuqVgYRa229tstv0whaYvT1SlXWEDLksiBCxxcQ4,2667
88
86
  omlish/dataclasses/impl/processing.py,sha256=DFxyFjL_h3awRyF_5eyTnB8QkuApx7Zc4QFnVoltlao,459
89
87
  omlish/dataclasses/impl/reflect.py,sha256=a19BbNxrmjNTbXzWuAl_794RCIQSMYyVqQ2Bf-DnNnM,5305
90
88
  omlish/dataclasses/impl/replace.py,sha256=wS9GHX4fIwaPv1JBJzIewdBfXyK3X3V7_t55Da87dYo,1217
@@ -164,15 +162,15 @@ omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCF
164
162
  omlish/inject/impl/providers.py,sha256=QnwhsujJFIHC0JTgd2Wlo1kP53i3CWTrj1nKU2DNxwg,2375
165
163
  omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1636
166
164
  omlish/inject/impl/scopes.py,sha256=ASfULXgP_ETlsAqFJfrZmyEaZt64Zr8tNn5ScA-EoXk,5900
167
- omlish/lang/__init__.py,sha256=WF4vYG4awiOAXezL4gwCjzYu4JpzqU57U-w3c6rzoLw,3500
165
+ omlish/lang/__init__.py,sha256=7GgNUH1ez1N68rkTN1-zKDaVOoDHW-BZ9V_H2LIFfqM,3532
168
166
  omlish/lang/cached.py,sha256=LwsgWQjQ5op618rBvI8vbASOEGWDTt_SKq6Tc1vlgZM,7680
169
167
  omlish/lang/clsdct.py,sha256=AjtIWLlx2E6D5rC97zQ3Lwq2SOMkbg08pdO_AxpzEHI,1744
170
168
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
171
169
  omlish/lang/contextmanagers.py,sha256=rzMSwJU7ObFXl46r6pGDbD45Zi_qZ9NHxDPnLNuux9o,9732
172
170
  omlish/lang/datetimes.py,sha256=ehI_DhQRM-bDxAavnp470XcekbbXc4Gdw9y1KpHDJT0,223
173
- omlish/lang/descriptors.py,sha256=tZzDyXSp3wMNzTytt9BScFFriBwMRpt0EeYP3CRKPX8,6602
171
+ omlish/lang/descriptors.py,sha256=OLM1qi14kY7PLGIJnvkd6CBEOzHgD9q8Cs2cB6Kzflk,6602
174
172
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
175
- omlish/lang/functions.py,sha256=yJxWwqlXEAT2gied4uTwiz5x1qXeuVubOSXyn9zy5aI,3624
173
+ omlish/lang/functions.py,sha256=kkPfcdocg-OmyN7skIqrFxNvqAv89Zc_kXKYAN8vw8g,3895
176
174
  omlish/lang/imports.py,sha256=04ugFC8NI5sbL7NH4V0r0q_nFsP_AMkHLz697CVkMtQ,6274
177
175
  omlish/lang/iterables.py,sha256=_q6rHbdFfW3VBqez0IV3rUABoNxsA_oBv_sykm5zsbQ,2243
178
176
  omlish/lang/maybes.py,sha256=NYHZDjqDtwPMheDrj2VtUVujxRPf8Qpgk4ZlZCTvBZc,3492
@@ -205,7 +203,7 @@ omlish/lite/logs.py,sha256=PeJZUJWd1K-UFErVs0wtD0zRMFW5-olUNFVVZAj_ScY,5549
205
203
  omlish/lite/marshal.py,sha256=u6jYUN_AndvI6__HJBvSw5ElHWC0CfHqgiDS28Vpqjg,8593
206
204
  omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
207
205
  omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
208
- omlish/lite/secrets.py,sha256=KZvGG7bFrjzIeLwohxsJ0Ctzpi42C-3A1PEMCIszJJs,521
206
+ omlish/lite/secrets.py,sha256=3Mz3V2jf__XU9qNHcH56sBSw95L3U2UPL24bjvobG0c,816
209
207
  omlish/lite/strings.py,sha256=9dO_A6EkhcTZ2xmOUGSOMT-mx9BnoOzYu1-ocSrDJaA,670
210
208
  omlish/lite/subprocesses.py,sha256=KuGV3ImehMjCUK0JoV3pUtG_7o5wei1lRDn9HxzByAg,3063
211
209
  omlish/logs/__init__.py,sha256=FbOyAW-lGH8gyBlSVArwljdYAU6RnwZLI5LwAfuNnrk,438
@@ -215,7 +213,7 @@ omlish/logs/formatters.py,sha256=q79nMnR2mRIStPyGrydQHpYTXgC5HHptt8lH3W2Wwbs,671
215
213
  omlish/logs/handlers.py,sha256=nyuFgmO05By_Xwq7es58ClzS51-F53lJL7gD0x5IqAg,228
216
214
  omlish/logs/noisy.py,sha256=8JORjI1dH38yU2MddM54OB6qt32Xozfocdb88vY4wro,335
217
215
  omlish/logs/utils.py,sha256=MgGovbP0zUrZ3FGD3qYNQWn-l0jy0Y0bStcQvv5BOmQ,391
218
- omlish/marshal/__init__.py,sha256=8r5nY0DMVT_kT4hARuTDsa8GOdshko40VEzOq0qNdS8,1628
216
+ omlish/marshal/__init__.py,sha256=RkZGfdp7x9KU8nEpb8hYBGANK-P_9AXMyjBXCrdYMmc,1757
219
217
  omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
220
218
  omlish/marshal/base.py,sha256=EIgrqsQ1OQ4mVUMuDH5zRBCwJpn8ijVS98Nmoka_Mrs,6025
221
219
  omlish/marshal/base64.py,sha256=F-3ogJdcFCtWINRgJgWT0rErqgx6f4qahhcg8OrkqhE,1089
@@ -232,7 +230,7 @@ omlish/marshal/mappings.py,sha256=zhLtyot7tzQtBNj7C4RBxjMELxA5r2q2Mth8Br7xkFs,28
232
230
  omlish/marshal/maybes.py,sha256=tKkVsJATERgbVcEfBnsHBK_2_LCQIVyBzca-cA-9KH0,2112
233
231
  omlish/marshal/naming.py,sha256=UCviMAXTTUpW1lyAGymybGP2rFUAW44P1X0zrIVbvi4,464
234
232
  omlish/marshal/numbers.py,sha256=oY_yMNJEnJhjfLh89gpPXvKqeUyhQcaTcQB6ecyHiG8,1704
235
- omlish/marshal/objects.py,sha256=T3prlm0HESpOLXHWcdsYekfmythJRZQOvaoL0is8J_o,3097
233
+ omlish/marshal/objects.py,sha256=R-NPCT1-UZhONTnrsrAvZvAtM2qyQsKZ8CPLfqkSg5g,4494
236
234
  omlish/marshal/optionals.py,sha256=r0XB5rqfasvgZJNrKYd6Unq2U4nHt3JURi26j0dYHlw,1499
237
235
  omlish/marshal/polymorphism.py,sha256=gyvNYUAkmQVhWrcXBLzXINxqx6RHyulf9n16Iv38PFI,5597
238
236
  omlish/marshal/primitives.py,sha256=wcvcs5GH_TWVmzAszh3dvyKibJgBxnXke-AlAXiwrrI,1107
@@ -242,6 +240,10 @@ omlish/marshal/unions.py,sha256=m9uVp2HrkZKY9lDyGoWQGXFgan8mRuBaKimtWNksl64,2635
242
240
  omlish/marshal/utils.py,sha256=puKJpwPpuDlMOIrKMcLTRLJyMiL6n_Xs-p59AuDEymA,543
243
241
  omlish/marshal/uuids.py,sha256=H4B7UX_EPNmP2tC8bubcKrPLTS4aQu98huvbXQ3Zv2g,910
244
242
  omlish/marshal/values.py,sha256=ssHiWdg_L6M17kAn8GiGdPW7UeQOm3RDikWkvwblf5I,263
243
+ omlish/math/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
+ omlish/math/bits.py,sha256=yip1l8agOYzT7bFyMGc0RR3XlnGCfHMpjw_SECLLh1I,3477
245
+ omlish/math/floats.py,sha256=UimhOT7KRl8LXTzOI5cQWoX_9h6WNWe_3vcOuO7-h_8,327
246
+ omlish/math/stats.py,sha256=MegzKVsmv2kra4jDWLOUgV0X7Ee2Tbl5u6ql1v4-dEY,10053
245
247
  omlish/reflect/__init__.py,sha256=iWDCNJNP4afPcv-MxZRJSIRQ4NRw6XYPyRHhBXb5YIA,667
246
248
  omlish/reflect/isinstance.py,sha256=x5T9S2634leinBT4hl3CZZkRttvdvvlxChkC_x9Qu2s,1176
247
249
  omlish/reflect/ops.py,sha256=RJ6jzrM4ieFsXzWyNXWV43O_WgzEaUvlHSc5N2ezW2A,2044
@@ -306,12 +308,13 @@ omlish/testing/pytest/plugins/spacing.py,sha256=JQQhi9q3c523Ro1a_K_9RGAb7HotiO74
306
308
  omlish/testing/pytest/plugins/switches.py,sha256=9FtN5qtPBoS-teEp54OHPF6jlZJakRJdq4pnLJpPj_A,3001
307
309
  omlish/testing/pytest/plugins/utils.py,sha256=L5C622UXcA_AUKDcvyh5IMiRfqSGGz0McdhwZWvfMlU,261
308
310
  omlish/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
311
+ omlish/text/asdl.py,sha256=3v5UocAfxan_d9drkGNdH3AMfx_FFBpQu3ULGL4M6VM,16865
309
312
  omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
310
313
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
311
314
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
312
- omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
313
- omlish-0.0.0.dev27.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
314
- omlish-0.0.0.dev27.dist-info/METADATA,sha256=y3f_PoZpAVj2Mpn51ruURaZtqJ0Tm8YWmtFacYsa0ck,3666
315
- omlish-0.0.0.dev27.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
316
- omlish-0.0.0.dev27.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
317
- omlish-0.0.0.dev27.dist-info/RECORD,,
315
+ omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
316
+ omlish-0.0.0.dev29.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
317
+ omlish-0.0.0.dev29.dist-info/METADATA,sha256=J9BcC0SJmQjneAgNGyWD756p2EyX8iSvs92bU2JAQH4,3636
318
+ omlish-0.0.0.dev29.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
319
+ omlish-0.0.0.dev29.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
320
+ omlish-0.0.0.dev29.dist-info/RECORD,,