omextra 0.0.0.dev471__py3-none-any.whl → 0.0.0.dev476__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.
@@ -0,0 +1,343 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import dataclasses as dc
5
+ from omlish import lang
6
+
7
+ from .base import Match
8
+ from .base import Parser
9
+ from .base import _Context
10
+
11
+
12
+ ##
13
+
14
+
15
+ class Literal(Parser, lang.Abstract):
16
+ def _match_repr(self) -> str:
17
+ return repr(self)
18
+
19
+
20
+ class StringLiteral(Literal):
21
+ def __init__(self, value: str) -> None:
22
+ super().__init__()
23
+
24
+ self._value = check.non_empty_str(value)
25
+
26
+ @property
27
+ def value(self) -> str:
28
+ return self._value
29
+
30
+ def __repr__(self) -> str:
31
+ return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
32
+
33
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
34
+ if start < len(ctx._source): # noqa
35
+ source = ctx._source[start : start + len(self._value)] # noqa
36
+ if source == self._value:
37
+ yield Match(self, start, start + len(source), ())
38
+
39
+
40
+ class CaseInsensitiveStringLiteral(Literal):
41
+ def __init__(self, value: str) -> None:
42
+ super().__init__()
43
+
44
+ self._value = check.non_empty_str(value).casefold()
45
+
46
+ @property
47
+ def value(self) -> str:
48
+ return self._value
49
+
50
+ def __repr__(self) -> str:
51
+ return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
52
+
53
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
54
+ if start < len(ctx._source): # noqa
55
+ source = ctx._source[start : start + len(self._value)].casefold() # noqa
56
+ if source == self._value:
57
+ yield Match(self, start, start + len(source), ())
58
+
59
+
60
+ class RangeLiteral(Literal):
61
+ @dc.dataclass(frozen=True)
62
+ class Range:
63
+ lo: str
64
+ hi: str
65
+
66
+ def __post_init__(self) -> None:
67
+ check.non_empty_str(self.lo)
68
+ check.non_empty_str(self.hi)
69
+ check.state(self.hi >= self.lo)
70
+
71
+ def __init__(self, value: Range) -> None:
72
+ super().__init__()
73
+
74
+ self._value = check.isinstance(value, RangeLiteral.Range)
75
+
76
+ @property
77
+ def value(self) -> Range:
78
+ return self._value
79
+
80
+ def __repr__(self) -> str:
81
+ return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
82
+
83
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
84
+ try:
85
+ source = ctx._source[start] # noqa
86
+ except IndexError:
87
+ return
88
+ # ranges are always case-sensitive
89
+ if (value := self._value).lo <= source <= value.hi:
90
+ yield Match(self, start, start + 1, ())
91
+
92
+
93
+ @ta.overload
94
+ def literal(s: str, *, case_sensitive: bool = False) -> StringLiteral:
95
+ ...
96
+
97
+
98
+ @ta.overload
99
+ def literal(lo: str, hi: str) -> RangeLiteral:
100
+ ...
101
+
102
+
103
+ def literal(*args, case_sensitive=None):
104
+ if not args:
105
+ raise TypeError
106
+ elif len(args) == 1:
107
+ s = check.isinstance(check.single(args), str)
108
+ if case_sensitive:
109
+ return StringLiteral(s)
110
+ else:
111
+ return CaseInsensitiveStringLiteral(s)
112
+ elif len(args) == 2:
113
+ check.none(case_sensitive)
114
+ return RangeLiteral(RangeLiteral.Range(*map(check.of_isinstance(str), args)))
115
+ else:
116
+ raise TypeError(args)
117
+
118
+
119
+ ##
120
+
121
+
122
+ class Concat(Parser):
123
+ def __init__(self, *children: Parser) -> None:
124
+ super().__init__()
125
+
126
+ for c in check.not_empty(children):
127
+ check.isinstance(c, Parser)
128
+ self._children = children
129
+
130
+ @property
131
+ def children(self) -> ta.Sequence[Parser]:
132
+ return self._children
133
+
134
+ def __repr__(self) -> str:
135
+ return f'{self.__class__.__name__}@{id(self):x}({", ".join(map(repr, self._children))})'
136
+
137
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
138
+ i = 0
139
+ match_tups: list[tuple[Match, ...]] = [()]
140
+ for cp in self._children:
141
+ next_match_tups: list[tuple[Match, ...]] = []
142
+ for mt in match_tups:
143
+ for cm in ctx.iter_parse(cp, mt[-1].end if mt else start):
144
+ next_match_tups.append((*mt, cm))
145
+ i += 1
146
+ if not next_match_tups:
147
+ return
148
+ match_tups = next_match_tups
149
+ if not i:
150
+ return
151
+ for mt in sorted(match_tups, key=len, reverse=True):
152
+ yield Match(self, start, mt[-1].end if mt else start, mt)
153
+
154
+
155
+ concat = Concat
156
+
157
+
158
+ ##
159
+
160
+
161
+ class Repeat(Parser):
162
+ @dc.dataclass(frozen=True)
163
+ class Times:
164
+ min: int = 0
165
+ max: int | None = None
166
+
167
+ def __post_init__(self) -> None:
168
+ if self.max is not None:
169
+ check.state(self.max >= self.min)
170
+
171
+ def __repr__(self) -> str:
172
+ return f'{self.__class__.__name__}({self.min}{f", {self.max!r}" if self.max is not None else ""})'
173
+
174
+ def __init__(self, times: Times, child: Parser) -> None:
175
+ super().__init__()
176
+
177
+ self._times = check.isinstance(times, Repeat.Times)
178
+ self._child = check.isinstance(child, Parser)
179
+
180
+ @property
181
+ def times(self) -> Times:
182
+ return self._times
183
+
184
+ @property
185
+ def child(self) -> Parser:
186
+ return self._child
187
+
188
+ def __repr__(self) -> str:
189
+ return f'{self.__class__.__name__}@{id(self):x}({self._times}, {self._child!r})'
190
+
191
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
192
+ match_tup_set: set[tuple[Match, ...]] = set()
193
+ last_match_tup_set: set[tuple[Match, ...]] = {()}
194
+ i = 0
195
+ while True:
196
+ if self._times.max is not None and i == self._times.max:
197
+ break
198
+ next_match_tup_set: set[tuple[Match, ...]] = set()
199
+ for mt in last_match_tup_set:
200
+ for cm in ctx.iter_parse(self._child, mt[-1].end if mt else start):
201
+ next_match_tup_set.add((*mt, cm))
202
+ if not next_match_tup_set or next_match_tup_set < match_tup_set:
203
+ break
204
+ i += 1
205
+ match_tup_set |= next_match_tup_set
206
+ last_match_tup_set = next_match_tup_set
207
+ if i < self._times.min:
208
+ return
209
+ for mt in sorted(match_tup_set or [()], key=len, reverse=True):
210
+ yield Match(self, start, mt[-1].end if mt else start, mt) # noqa
211
+
212
+
213
+ class Option(Repeat):
214
+ def __init__(self, child: Parser) -> None:
215
+ super().__init__(Repeat.Times(0, 1), child)
216
+
217
+ def __repr__(self) -> str:
218
+ return f'{self.__class__.__name__}@{id(self):x}({self._child!r})'
219
+
220
+
221
+ option = Option
222
+
223
+
224
+ @ta.overload
225
+ def repeat(child: Parser) -> Repeat: # noqa
226
+ ...
227
+
228
+
229
+ @ta.overload
230
+ def repeat(times: Repeat.Times, child: Parser) -> Repeat: # noqa
231
+ ...
232
+
233
+
234
+ @ta.overload
235
+ def repeat(min: int, child: Parser) -> Repeat: # noqa
236
+ ...
237
+
238
+
239
+ @ta.overload
240
+ def repeat(min: int, max: int | None, child: Parser) -> Repeat: # noqa
241
+ ...
242
+
243
+
244
+ def repeat(*args):
245
+ min: int # noqa
246
+ max: int | None # noqa
247
+
248
+ if len(args) < 2:
249
+ [child] = args
250
+ min, max = 0, None # noqa
251
+
252
+ elif len(args) > 2:
253
+ min, max, child = args # noqa
254
+
255
+ else:
256
+ ti, child = args # noqa
257
+
258
+ if isinstance(ti, Repeat.Times):
259
+ min, max = ti.min, ti.max # noqa
260
+
261
+ else:
262
+ min, max = ti, None # noqa
263
+
264
+ if (min, max) == (0, 1):
265
+ return Option(check.isinstance(child, Parser))
266
+
267
+ else:
268
+ return Repeat(
269
+ Repeat.Times(
270
+ check.isinstance(min, int),
271
+ check.isinstance(max, (int, None)),
272
+ ),
273
+ check.isinstance(child, Parser),
274
+ )
275
+
276
+
277
+ ##
278
+
279
+
280
+ class Either(Parser):
281
+ def __init__(self, *children: Parser, first_match: bool = False) -> None:
282
+ super().__init__()
283
+
284
+ for c in check.not_empty(children):
285
+ check.isinstance(c, Parser)
286
+ self._children = children
287
+ self._first_match = first_match
288
+
289
+ @property
290
+ def children(self) -> ta.Sequence[Parser]:
291
+ return self._children
292
+
293
+ @property
294
+ def first_match(self) -> bool:
295
+ return self._first_match
296
+
297
+ def __repr__(self) -> str:
298
+ return (
299
+ f'{self.__class__.__name__}@{id(self):x}('
300
+ f'{", ".join(map(repr, self._children))}'
301
+ f'{", first_match=True" if self._first_match else ""})'
302
+ )
303
+
304
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
305
+ for cp in self._children:
306
+ found = False
307
+ for cm in ctx.iter_parse(cp, start):
308
+ found = True
309
+ yield Match(self, start, cm.end, (cm,))
310
+ if found and self._first_match:
311
+ return
312
+
313
+
314
+ either = Either
315
+
316
+
317
+ ##
318
+
319
+
320
+ class RuleRef(Parser):
321
+ def __init__(self, name: str) -> None:
322
+ super().__init__()
323
+
324
+ self._name = check.non_empty_str(name)
325
+ self._name_f = name.casefold()
326
+
327
+ @property
328
+ def name(self) -> str:
329
+ return self._name
330
+
331
+ def __repr__(self) -> str:
332
+ return f'{self.__class__.__name__}@{id(self):x}({self._name!r})'
333
+
334
+ def _match_repr(self) -> str:
335
+ return repr(self)
336
+
337
+ def _iter_parse(self, ctx: _Context, start: int) -> ta.Iterator[Match]:
338
+ cp = ctx._grammar._rules_by_name_f[self._name_f].parser # noqa
339
+ for cm in ctx.iter_parse(cp, start):
340
+ yield Match(self, cm.start, cm.end, (cm,))
341
+
342
+
343
+ rule = RuleRef
@@ -0,0 +1,76 @@
1
+ import itertools
2
+ import textwrap
3
+ import typing as ta
4
+
5
+ from omlish import check
6
+
7
+ from .base import Grammar
8
+ from .base import Match
9
+ from .errors import AbnfIncompleteParseError
10
+ from .parsers import RuleRef
11
+
12
+
13
+ ##
14
+
15
+
16
+ def strip_insignificant_match_rules(m: Match, g: Grammar) -> Match:
17
+ def rec(c: Match) -> Match:
18
+ return c.flat_map_children(
19
+ lambda x: (
20
+ (rec(x),) if not (
21
+ isinstance((xp := x.parser), RuleRef) and
22
+ check.not_none(g.rule(xp.name)).insignificant
23
+ ) else ()
24
+ ),
25
+ )
26
+ return rec(m)
27
+
28
+
29
+ def only_match_rules(m: Match) -> Match:
30
+ def rec(c: Match) -> ta.Iterable[Match]:
31
+ if isinstance(c.parser, RuleRef):
32
+ return (c.flat_map_children(rec),)
33
+ else:
34
+ return itertools.chain.from_iterable(map(rec, c.children))
35
+ return m.flat_map_children(rec)
36
+
37
+
38
+ #
39
+
40
+
41
+ def parse_rules(
42
+ grammar: Grammar,
43
+ source: str,
44
+ root: str | None = None,
45
+ *,
46
+ start: int = 0,
47
+ complete: bool = False,
48
+ **kwargs: ta.Any,
49
+ ) -> Match | None:
50
+ if (match := grammar.parse(
51
+ source,
52
+ root,
53
+ start=start,
54
+ **kwargs,
55
+ )) is None:
56
+ return None
57
+
58
+ if complete and (match.start, match.end) != (start, len(source)):
59
+ raise AbnfIncompleteParseError
60
+
61
+ match = only_match_rules(match)
62
+ match = strip_insignificant_match_rules(match, grammar)
63
+
64
+ return match
65
+
66
+
67
+ ##
68
+
69
+
70
+ def fix_grammar_ws(s: str) -> str:
71
+ return (
72
+ textwrap.dedent(s)
73
+ .rstrip()
74
+ .replace('\r', '')
75
+ .replace('\n', '\r\n')
76
+ ) + '\r\n'
@@ -0,0 +1,55 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import collections as col
5
+ from omlish import dispatch
6
+ from omlish import lang
7
+
8
+ from .base import Match
9
+ from .base import Parser
10
+ from .parsers import RuleRef
11
+
12
+
13
+ T = ta.TypeVar('T')
14
+
15
+
16
+ ##
17
+
18
+
19
+ class ParserVisitor(lang.Abstract, ta.Generic[T]):
20
+ @dispatch.method()
21
+ def visit_parser(self, p: Parser, m: Match) -> T:
22
+ raise TypeError(p)
23
+
24
+ #
25
+
26
+ def visit_match(self, m: Match) -> T:
27
+ return self.visit_parser(m.parser, m)
28
+
29
+
30
+ ##
31
+
32
+
33
+ class RuleVisitor(lang.Abstract, ta.Generic[T]):
34
+ _registry = col.AttrRegistry[ta.Callable, str]()
35
+
36
+ @classmethod
37
+ def register(cls, name):
38
+ return cls._registry.register(name)
39
+
40
+ @lang.cached_function
41
+ @classmethod
42
+ def _registry_cache(cls) -> col.AttrRegistryCache[ta.Callable, str, ta.Mapping[str, str]]:
43
+ def prepare(_, dct):
44
+ return col.make_map(((n, a) for a, (_, n) in dct.items()), strict=True)
45
+
46
+ return col.AttrRegistryCache(cls._registry, prepare)
47
+
48
+ def visit_rule(self, name: str, m: Match) -> T:
49
+ att = self._registry_cache().get(self.__class__)[name]
50
+ return getattr(self, att)(m)
51
+
52
+ #
53
+
54
+ def visit_match(self, m: Match) -> T:
55
+ return self.visit_rule(check.isinstance(m.parser, RuleRef).name, m)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omextra
3
- Version: 0.0.0.dev471
3
+ Version: 0.0.0.dev476
4
4
  Summary: omextra
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev471
17
+ Requires-Dist: omlish==0.0.0.dev476
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -1,5 +1,5 @@
1
1
  omextra/.omlish-manifests.json,sha256=E1M9UJ31RfpLfsZ2MfbN0_GAh_Lu_vj9wVJKPgak8jQ,290
2
- omextra/__about__.py,sha256=UmONS0zKtygpY7Z9AD42Kn1cajMEdLKHzUwG00kaaFM,689
2
+ omextra/__about__.py,sha256=8R-v5C4UWEiLEyDv1CoxATr7YTxZxs6-7QRlva-gGWY,706
3
3
  omextra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omextra/defs.py,sha256=iMaDGj5VSG7QdYA1s7MvIYTbtTsXc1msINsuuZym1vs,4902
5
5
  omextra/dynamic.py,sha256=i3aJRWwHJOmsrXI9a2iWCLa7pUQXx5YbMvUOS5LZ_Bs,6531
@@ -14,7 +14,18 @@ omextra/asyncs/bluelet/events.py,sha256=PJ55CuXNmvzhYYhMmIDOUWegWTMcn9IkxL4dHcjd
14
14
  omextra/asyncs/bluelet/files.py,sha256=wpvzFPfQltqPcSKUgmAUZ1sjSSawX_nr5rZ4InLhsTM,2424
15
15
  omextra/asyncs/bluelet/runner.py,sha256=pE080AUs66AIxY5WBOWqh-9V1i1JYOvxaE2wB8t72Mw,15516
16
16
  omextra/asyncs/bluelet/sockets.py,sha256=nTVPdLbsmmjI0e6G9Di2DA-JoTTnvu8xc_Yv8N_7g48,6781
17
+ omextra/collections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ omextra/collections/hamt/LICENSE,sha256=82rcXdtBwd6vGLddjx9Jk09kkwPL6yqqv1NzGjPUCoM,2460
19
+ omextra/collections/hamt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ omextra/collections/hamt/_hamt.c,sha256=LDYWBuAl4685DbAZICrzO--EfIN8PjHZq0MUJYBAD74,100898
17
21
  omextra/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ omextra/formats/goyaml/LICENSE,sha256=qkavv9cjd59BFIFOMVMJMrC3FCEIELrBTA-GPhAFkoY,1071
23
+ omextra/formats/goyaml/__init__.py,sha256=Wf7WukYGHk2Pqep2ZGRzz2ZznXcay0oVVmrqvqgYIoo,1821
24
+ omextra/formats/goyaml/ast.py,sha256=55CyoNRiaDmu6YeKE_PRt51fE7zr35YcTWSDzVRjPRo,69984
25
+ omextra/formats/goyaml/errors.py,sha256=UQfuManw4kOJKnXrV6MjFqt0ZU6KkwZL75KMdQQ6ZyU,949
26
+ omextra/formats/goyaml/parsing.py,sha256=vZotOxOhLTrE9AGfXbSZcDrVC652PJ3K3ageu6aRT6Q,90558
27
+ omextra/formats/goyaml/scanning.py,sha256=X5hl5lhQAPtiqiKC0pGAo46D2rrB2nZOtJuQOAtaXDM,63208
28
+ omextra/formats/goyaml/tokens.py,sha256=fRshyMRU3XzZ5Pi9_C1F2BTzjtcrqIuIsHmQuqMu3Oo,29278
18
29
  omextra/formats/json/Json.g4,sha256=fYImsljcVPdlMTHBabM_rza2j4udqjfLQNxIz4S_TpY,1206
19
30
  omextra/formats/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
31
  omextra/formats/json/_antlr/JsonLexer.py,sha256=SYlMV8XKi_kJ9meEulNmm2B98hjdzz_xLyPsoqCyYoE,4957
@@ -52,6 +63,15 @@ omextra/sql/parsing/_antlr/MinisqlParser.py,sha256=htIMgNde8PqLj7mDV2cLo7kDiwn-a
52
63
  omextra/sql/parsing/_antlr/MinisqlVisitor.py,sha256=UPs2qA2VlNRP_LMwF0DpMxMir9tIS9w59vGTq07mmlI,10052
53
64
  omextra/sql/parsing/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
65
  omextra/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ omextra/text/abnf/LICENSE,sha256=UJzrhSePw9TVm0VfkVmx9NRHkPS1n6jx2UgajTb14ts,1054
67
+ omextra/text/abnf/__init__.py,sha256=4ckTD1WSkte_7LzO7vvhcZ17uEdSrNdnpkkuJBRJ2BY,1095
68
+ omextra/text/abnf/base.py,sha256=xDlMH9jaGXh2PN4DMR6AIMB-zOU_MVy20BcEd6Bpu4U,8128
69
+ omextra/text/abnf/core.py,sha256=VUD52hKgDsesvPjmhL1UZVXtpYr-EpWyBprdy722YTY,2303
70
+ omextra/text/abnf/errors.py,sha256=uj1oQvRVRlWkMG8mpmcRUg25kICeIJx5EWAVij3pmXc,142
71
+ omextra/text/abnf/meta.py,sha256=1i2AAGZBLEQhF2HUYQPr385qAMMYLEo0dZT-dcH2Gv0,14447
72
+ omextra/text/abnf/parsers.py,sha256=JKRff1lXbjbKokGL5IBG9tnPflrQ0Fd7X--MRcKTdtE,9209
73
+ omextra/text/abnf/utils.py,sha256=UXJ0MlTkigm3cWuKVU1Ao3bVjQKqy6sZgPni1aVgFgk,1667
74
+ omextra/text/abnf/visitors.py,sha256=kAWPmc3fAOLynLoVl_-ciG_TA3XG7HPca9_I1vI4rL0,1288
55
75
  omextra/text/antlr/__init__.py,sha256=88bMl_28cfSKslgOkMGYXqALgsHz3KC4LFvAVtzj7k8,89
56
76
  omextra/text/antlr/delimit.py,sha256=s0Jlu6oOamtuyWv_V98P0YC7HmgFJBF7GHVsDfMYTdI,3476
57
77
  omextra/text/antlr/dot.py,sha256=37ZBYpum-n6Xg9UGj1Wc0wyuR3gL_uX63KwrSzyWNmk,967
@@ -125,9 +145,9 @@ omextra/text/antlr/cli/__main__.py,sha256=ckYkj0drxabBVwWYewH2SS36TTeAxllZtS4xEl
125
145
  omextra/text/antlr/cli/cli.py,sha256=LW8pJNmySBOV3g8QxquPjUgxFv7YblzEyi555hHH3_M,1234
126
146
  omextra/text/antlr/cli/consts.py,sha256=HUYJP9j4RfeuuQv6HFd2oFMS0piWJ9Sq1tbeAs4OlBc,290
127
147
  omextra/text/antlr/cli/gen.py,sha256=HYleVptrpynwcl6GspX6O9_oMRSxwdYAQGuUFfDYse8,5236
128
- omextra-0.0.0.dev471.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
129
- omextra-0.0.0.dev471.dist-info/METADATA,sha256=KBJJ9lkZ0VYxiZhPUiJz-AwUIsrVJ1cnBJq70FxCO9g,1001
130
- omextra-0.0.0.dev471.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
131
- omextra-0.0.0.dev471.dist-info/entry_points.txt,sha256=-MFAMks5HgZ60Ore0Wl5lKVKk8z4kf1Ls3WY9E_OlCU,37
132
- omextra-0.0.0.dev471.dist-info/top_level.txt,sha256=o1nCNRejLMcayDngLuWMWwaeOucz33BXbpuoVvvzjPc,8
133
- omextra-0.0.0.dev471.dist-info/RECORD,,
148
+ omextra-0.0.0.dev476.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
149
+ omextra-0.0.0.dev476.dist-info/METADATA,sha256=IM7Sq4wSGV9B7Qsf09zG2Gg83UhbY43OS_4KgEu38Qo,1001
150
+ omextra-0.0.0.dev476.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
151
+ omextra-0.0.0.dev476.dist-info/entry_points.txt,sha256=-MFAMks5HgZ60Ore0Wl5lKVKk8z4kf1Ls3WY9E_OlCU,37
152
+ omextra-0.0.0.dev476.dist-info/top_level.txt,sha256=o1nCNRejLMcayDngLuWMWwaeOucz33BXbpuoVvvzjPc,8
153
+ omextra-0.0.0.dev476.dist-info/RECORD,,