omextra 0.0.0.dev514__py3-none-any.whl → 0.0.0.dev516__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.
omextra/asyncs/all.py ADDED
@@ -0,0 +1,18 @@
1
+ from .flavors import ( # noqa
2
+ ContextManagerAdapter,
3
+ Flavor,
4
+ adapt,
5
+ adapt_context,
6
+ from_anyio,
7
+ from_anyio_context,
8
+ from_asyncio,
9
+ from_asyncio_context,
10
+ from_trio,
11
+ from_trio_context,
12
+ get_flavor,
13
+ mark_anyio,
14
+ mark_asyncio,
15
+ mark_flavor,
16
+ mark_trio,
17
+ with_adapter_loop,
18
+ )
omextra/asyncs/bridge.py CHANGED
@@ -41,7 +41,7 @@ import weakref
41
41
 
42
42
  from omlish import check
43
43
  from omlish import lang
44
- from omlish import sync
44
+ from omlish.sync import all as sync
45
45
  from omlish.concurrent import threadlets
46
46
 
47
47
 
@@ -0,0 +1,229 @@
1
+ """
2
+ Tools for working with the different async loop implementations - currently anyio, asyncio, trio.
3
+
4
+ TODO:
5
+ - mark whole class / module?
6
+ """
7
+ import abc
8
+ import dataclasses as dc
9
+ import enum
10
+ import functools
11
+ import typing as ta
12
+
13
+ from omlish import lang
14
+ from omlish.asyncs.trio_asyncio import check_trio_asyncio
15
+ from omlish.asyncs.trio_asyncio import with_trio_asyncio_loop
16
+
17
+
18
+ if ta.TYPE_CHECKING:
19
+ import sniffio
20
+ import trio_asyncio
21
+ else:
22
+ sniffio = lang.proxy_import('sniffio')
23
+ trio_asyncio = lang.proxy_import('trio_asyncio')
24
+
25
+
26
+ T = ta.TypeVar('T')
27
+
28
+
29
+ ##
30
+
31
+
32
+ _FLAVOR_ATTR = '__async_flavor__'
33
+
34
+
35
+ class _MISSING(lang.Marker):
36
+ pass
37
+
38
+
39
+ class Flavor(enum.Enum):
40
+ ASYNCIO = enum.auto()
41
+ TRIO = enum.auto()
42
+ ANYIO = enum.auto()
43
+
44
+
45
+ def mark_flavor(f: Flavor):
46
+ if not isinstance(f, Flavor):
47
+ raise TypeError(f)
48
+
49
+ def inner(fn):
50
+ setattr(fn, _FLAVOR_ATTR, f)
51
+ return fn
52
+
53
+ return inner
54
+
55
+
56
+ mark_asyncio = mark_flavor(Flavor.ASYNCIO)
57
+ mark_anyio = mark_flavor(Flavor.ANYIO)
58
+ mark_trio = mark_flavor(Flavor.TRIO)
59
+
60
+ PACKAGE_FLAVORS: ta.MutableMapping[str, Flavor] = {
61
+ 'anyio': Flavor.ANYIO,
62
+ 'asyncio': Flavor.ASYNCIO,
63
+ 'trio': Flavor.TRIO,
64
+
65
+ 'sqlalchemy.ext.asyncio': Flavor.ASYNCIO,
66
+ }
67
+
68
+ _MODULE_FLAVOR_CACHE: dict[str, Flavor | None] = {}
69
+
70
+
71
+ def _get_module_flavor(p: str) -> Flavor | None:
72
+ try:
73
+ return _MODULE_FLAVOR_CACHE[p]
74
+ except KeyError:
75
+ pass
76
+
77
+ pf: Flavor | None = None
78
+ for cp, cf in PACKAGE_FLAVORS.items():
79
+ if p.startswith(cp) and (len(cp) == len(p) or p[len(cp)] == '.'):
80
+ pf = cf
81
+ break
82
+
83
+ _MODULE_FLAVOR_CACHE[p] = pf
84
+ return pf
85
+
86
+
87
+ def get_flavor(obj: ta.Any, default: Flavor | type[_MISSING] | None = _MISSING) -> Flavor:
88
+ u = lang.unwrap_func(obj)
89
+
90
+ try:
91
+ return getattr(u, _FLAVOR_ATTR)
92
+ except AttributeError:
93
+ pass
94
+
95
+ if (mn := getattr(u, '__module__', None)) is not None:
96
+ if (pf := _get_module_flavor(mn)):
97
+ return pf
98
+
99
+ if default is not _MISSING:
100
+ return default # type: ignore
101
+
102
+ raise TypeError(f'not marked with flavor: {obj}')
103
+
104
+
105
+ ##
106
+
107
+
108
+ def with_adapter_loop(*, wait=False):
109
+ def outer(fn):
110
+ @functools.wraps(fn)
111
+ async def inner(*args, **kwargs):
112
+ cur_lib = sniffio.current_async_library()
113
+
114
+ if cur_lib == 'asyncio':
115
+ await fn(*args, **kwargs)
116
+
117
+ elif cur_lib == 'trio':
118
+ await with_trio_asyncio_loop(wait=wait)(fn)(*args, **kwargs)
119
+
120
+ else:
121
+ raise RuntimeError(f'Unknown async library: {cur_lib}')
122
+
123
+ return inner
124
+
125
+ return outer
126
+
127
+
128
+ ##
129
+
130
+
131
+ class Adapter(lang.Abstract):
132
+ _FROM_METHODS_BY_FLAVOR: ta.ClassVar[ta.Mapping[Flavor, str]] = {
133
+ Flavor.ANYIO: 'from_anyio',
134
+ Flavor.ASYNCIO: 'from_asyncio',
135
+ Flavor.TRIO: 'from_trio',
136
+ }
137
+
138
+ def adapt(self, fn, fl=None):
139
+ if fl is None:
140
+ fl = get_flavor(fn)
141
+ return getattr(self, self._FROM_METHODS_BY_FLAVOR[fl])(fn)
142
+
143
+ #
144
+
145
+ def from_anyio(self, fn):
146
+ return fn
147
+
148
+ @abc.abstractmethod
149
+ def from_asyncio(self, fn):
150
+ raise NotImplementedError
151
+
152
+ @abc.abstractmethod
153
+ def from_trio(self, fn):
154
+ raise NotImplementedError
155
+
156
+
157
+ class AsyncioAdapter(Adapter):
158
+ def from_asyncio(self, fn):
159
+ return fn
160
+
161
+ def from_trio(self, fn):
162
+ check_trio_asyncio()
163
+ return trio_asyncio.trio_as_aio(fn)
164
+
165
+
166
+ class TrioAdapter(Adapter):
167
+ def from_asyncio(self, fn):
168
+ check_trio_asyncio()
169
+ return trio_asyncio.aio_as_trio(fn)
170
+
171
+ def from_trio(self, fn):
172
+ return fn
173
+
174
+
175
+ _ADAPTERS_BY_BACKEND: ta.Mapping[str, Adapter] = {
176
+ 'asyncio': AsyncioAdapter(),
177
+ 'trio': TrioAdapter(),
178
+ }
179
+
180
+
181
+ def get_adapter() -> Adapter:
182
+ return _ADAPTERS_BY_BACKEND[sniffio.current_async_library()]
183
+
184
+
185
+ def adapt(fn, fl=None):
186
+ return get_adapter().adapt(fn, fl)
187
+
188
+
189
+ def from_anyio(fn):
190
+ return get_adapter().from_anyio(fn)
191
+
192
+
193
+ def from_asyncio(fn):
194
+ return get_adapter().from_asyncio(fn)
195
+
196
+
197
+ def from_trio(fn):
198
+ return get_adapter().from_trio(fn)
199
+
200
+
201
+ ##
202
+
203
+
204
+ @dc.dataclass(frozen=True)
205
+ class ContextManagerAdapter(ta.Generic[T]):
206
+ obj: ta.AsyncContextManager[T]
207
+ adapt: ta.Callable[[ta.Callable], ta.Callable]
208
+
209
+ async def __aenter__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
210
+ return await self.adapt(self.obj.__aenter__)(*args, **kwargs)
211
+
212
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
213
+ return await self.adapt(self.obj.__aexit__)(exc_type, exc_val, exc_tb)
214
+
215
+
216
+ def adapt_context(obj):
217
+ return ContextManagerAdapter(obj, get_adapter().adapt)
218
+
219
+
220
+ def from_anyio_context(obj):
221
+ return ContextManagerAdapter(obj, get_adapter().from_anyio)
222
+
223
+
224
+ def from_asyncio_context(obj):
225
+ return ContextManagerAdapter(obj, get_adapter().from_asyncio)
226
+
227
+
228
+ def from_trio_context(obj):
229
+ return ContextManagerAdapter(obj, get_adapter().from_trio)
omextra/io/trampoline.py CHANGED
@@ -6,7 +6,7 @@ import typing as ta
6
6
  from omlish import check
7
7
  from omlish import lang
8
8
  from omlish.concurrent import threadlets as tls
9
- from omlish.sync import ConditionDeque
9
+ from omlish.sync.conddeque import ConditionDeque
10
10
 
11
11
 
12
12
  if ta.TYPE_CHECKING:
@@ -0,0 +1,9 @@
1
+ from .asyncs import ( # noqa
2
+ AsyncConnection,
3
+ AsyncConnectionLike,
4
+ AsyncEngine,
5
+ AsyncEngineLike,
6
+ AsyncTransaction,
7
+ AsyncTransactionLike,
8
+ async_adapt,
9
+ )
@@ -0,0 +1,154 @@
1
+ """
2
+ TODO:
3
+ - Maysync impls?
4
+ - base Protocol so adapters and real sa impls can be used interchangeably (if in asyncio ctx)?
5
+ """
6
+ import contextlib
7
+ import typing as ta
8
+
9
+ import sqlalchemy as sa
10
+ import sqlalchemy.ext.asyncio as saa
11
+
12
+ from ...asyncs import all as au
13
+
14
+
15
+ T = ta.TypeVar('T')
16
+ P = ta.ParamSpec('P')
17
+
18
+
19
+ AsyncEngineLike: ta.TypeAlias = ta.Union[saa.AsyncEngine, 'AsyncEngine']
20
+ AsyncConnectionLike: ta.TypeAlias = ta.Union[saa.AsyncConnection, 'AsyncConnection']
21
+ AsyncTransactionLike: ta.TypeAlias = ta.Union[saa.AsyncTransaction, 'AsyncTransaction']
22
+
23
+
24
+ ##
25
+
26
+
27
+ class AsyncTransaction:
28
+ def __init__(self, underlying: saa.AsyncTransaction) -> None:
29
+ super().__init__()
30
+
31
+ self._underlying = underlying
32
+
33
+ @property
34
+ def underlying(self) -> saa.AsyncTransaction:
35
+ return self._underlying
36
+
37
+ ##
38
+
39
+ @au.mark_asyncio
40
+ async def close(self) -> None:
41
+ await au.from_asyncio(self._underlying.close)()
42
+
43
+ @au.mark_asyncio
44
+ async def rollback(self) -> None:
45
+ await au.from_asyncio(self._underlying.rollback)()
46
+
47
+ @au.mark_asyncio
48
+ async def commit(self) -> None:
49
+ await au.from_asyncio(self._underlying.commit)()
50
+
51
+
52
+ class AsyncConnection:
53
+ def __init__(self, underlying: saa.AsyncConnection) -> None:
54
+ super().__init__()
55
+
56
+ self._underlying = underlying
57
+
58
+ @property
59
+ def underlying(self) -> saa.AsyncConnection:
60
+ return self._underlying
61
+
62
+ ##
63
+
64
+ @contextlib.asynccontextmanager
65
+ @au.mark_asyncio
66
+ async def begin(self) -> ta.AsyncIterator[AsyncTransaction]:
67
+ async with au.from_asyncio_context(self._underlying.begin()) as u:
68
+ yield AsyncTransaction(u)
69
+
70
+ @au.mark_asyncio
71
+ async def execute(
72
+ self,
73
+ statement: ta.Any,
74
+ *args: ta.Any,
75
+ **kwargs: ta.Any,
76
+ ) -> sa.CursorResult[ta.Any]:
77
+ return await au.from_asyncio(self._underlying.execute)(statement, *args, **kwargs)
78
+
79
+ @au.mark_asyncio
80
+ async def run_sync(
81
+ self,
82
+ fn: ta.Callable[ta.Concatenate[sa.Connection, P], T],
83
+ *args: P.args,
84
+ **kwargs: P.kwargs,
85
+ ) -> T:
86
+ return await au.from_asyncio(self._underlying.run_sync)(fn, *args, **kwargs)
87
+
88
+
89
+ class AsyncEngine:
90
+ def __init__(self, underlying: saa.AsyncEngine) -> None:
91
+ super().__init__()
92
+
93
+ self._underlying = underlying
94
+
95
+ @property
96
+ def underlying(self) -> saa.AsyncEngine:
97
+ return self._underlying
98
+
99
+ ##
100
+
101
+ @contextlib.asynccontextmanager
102
+ @au.mark_asyncio
103
+ async def connect(self) -> ta.AsyncIterator[AsyncConnection]:
104
+ async with au.from_asyncio_context(self._underlying.connect()) as u:
105
+ yield AsyncConnection(u)
106
+
107
+ @au.mark_asyncio
108
+ async def dispose(self, close: bool = True) -> None:
109
+ await au.from_asyncio(self._underlying.dispose)(close)
110
+
111
+
112
+ ##
113
+
114
+
115
+ @ta.overload
116
+ def async_adapt(obj: AsyncEngine) -> AsyncEngine:
117
+ ...
118
+
119
+
120
+ @ta.overload
121
+ def async_adapt(obj: AsyncConnection) -> AsyncConnection:
122
+ ...
123
+
124
+
125
+ @ta.overload
126
+ def async_adapt(obj: AsyncTransaction) -> AsyncTransaction:
127
+ ...
128
+
129
+
130
+ @ta.overload
131
+ def async_adapt(obj: saa.AsyncEngine) -> AsyncEngine:
132
+ ...
133
+
134
+
135
+ @ta.overload
136
+ def async_adapt(obj: saa.AsyncConnection) -> AsyncConnection:
137
+ ...
138
+
139
+
140
+ @ta.overload
141
+ def async_adapt(obj: saa.AsyncTransaction) -> AsyncTransaction:
142
+ ...
143
+
144
+
145
+ def async_adapt(obj: ta.Any) -> ta.Any:
146
+ if isinstance(obj, (AsyncEngine, AsyncConnection, AsyncTransaction)):
147
+ return obj
148
+ if isinstance(obj, saa.AsyncTransaction):
149
+ return AsyncTransaction(obj)
150
+ if isinstance(obj, saa.AsyncConnection):
151
+ return AsyncConnection(obj)
152
+ if isinstance(obj, saa.AsyncEngine):
153
+ return AsyncEngine(obj)
154
+ raise TypeError(obj)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omextra
3
- Version: 0.0.0.dev514
3
+ Version: 0.0.0.dev516
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.dev514
17
+ Requires-Dist: omlish==0.0.0.dev516
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -5,7 +5,9 @@ omextra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  omextra/defs.py,sha256=iMaDGj5VSG7QdYA1s7MvIYTbtTsXc1msINsuuZym1vs,4902
6
6
  omextra/dynamic.py,sha256=i3aJRWwHJOmsrXI9a2iWCLa7pUQXx5YbMvUOS5LZ_Bs,6531
7
7
  omextra/asyncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- omextra/asyncs/bridge.py,sha256=QHh4gtiSid5bjmV9lqQrGHHBkE0yPFFYGW_NbJ2KGhI,10162
8
+ omextra/asyncs/all.py,sha256=zE9zBNepDSczQ-QhnzwFz59IZIex3HuUqfKIgwbJLgY,329
9
+ omextra/asyncs/bridge.py,sha256=N-iJg2Yvk8zfEMh5hUB9V4dIIMosvJszoo54ZXydQ1Q,10174
10
+ omextra/asyncs/flavors.py,sha256=gFKfaKiuSDj4W5sopf9DtYQBDrBFXhsZX6_UJ5jgSNI,4896
9
11
  omextra/asyncs/bluelet/LICENSE,sha256=q5Kpj4s30qpi8H66tXFlh5v8_fkaKMFIzqdGfnN0Hz0,555
10
12
  omextra/asyncs/bluelet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
13
  omextra/asyncs/bluelet/all.py,sha256=aUV6PwnR8DqnEBS9wsZuPW_UtP6G9M8_KY-mmxZeVG0,1516
@@ -39,19 +41,7 @@ omextra/formats/json5/_antlr/Json5Parser.py,sha256=psacqZWRzPNEZq70XKpOnelZXHuXj
39
41
  omextra/formats/json5/_antlr/Json5Visitor.py,sha256=fIp5GhQ2Sd-bKEBBcBd31FOoCLiV1SZYPRof0Az6v6k,1455
40
42
  omextra/formats/json5/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
43
  omextra/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- omextra/io/trampoline.py,sha256=nzCXtL4qip3IjfHwSHqhWkh429iakUFikbYKgx66cLc,7221
43
- omextra/io/buffers/DESIGN.md,sha256=DAGa6EB_6B7MOvHybCmOegIFytYLxh2rZxPqua-ocb4,8301
44
- omextra/io/buffers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- omextra/io/buffers/adapters.py,sha256=C54vjLyJNcqdb_JVY5ZcJBHRH0yx9tIp4ZkoVRMMk8Y,9314
46
- omextra/io/buffers/all.py,sha256=Z3nxE1QcvfOO0UYfye2ELDeYggIQUzlcjWtNVdvv1X0,1051
47
- omextra/io/buffers/errors.py,sha256=38DvoYULPOyUpNLhuvZ9PG4qVdVdadTBXuVz3LKawpg,1523
48
- omextra/io/buffers/framing.py,sha256=dZd429gldmzcmE95oTL-DF_pJodWCn4JGO8fsEoS7rE,7389
49
- omextra/io/buffers/linear.py,sha256=wlMjrdQ9_bzeYVmlzZ7GHMipVeFQBViQlUn_P1hH1RI,6658
50
- omextra/io/buffers/reading.py,sha256=_EQWxbazE4uJhjZTQ1A5WtDVH1DjbbbDP5ITS3McJZ4,3485
51
- omextra/io/buffers/scanning.py,sha256=prq6Kdryfy0z-LK0K3h35HBavVuftllmnfIysxvf-6U,3049
52
- omextra/io/buffers/segmented.py,sha256=pTamrfBnqJGcHe02s2JmmRmFQPbFgGS38PTZ4vf5U7k,20191
53
- omextra/io/buffers/types.py,sha256=zbDRk8S9HquN5wk5bFFs5H0ptNtfQvS_ENvjc6Sl05E,11780
54
- omextra/io/buffers/utils.py,sha256=QxBN73g1rEUeHHq62NMS23IG0rPPC-5AY-UqwwxjFu8,1709
44
+ omextra/io/trampoline.py,sha256=oWEkwQonue4qk3lkpI5KE7_yR02Dyn2XN7Amngw3z4I,7231
55
45
  omextra/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
46
  omextra/specs/proto/Protobuf3.g4,sha256=chDrovFsuZaHf5W35WZNts3jOa1ssPwvWiJR4yVIgjw,5552
57
47
  omextra/specs/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -63,6 +53,8 @@ omextra/specs/proto/_antlr/Protobuf3Parser.py,sha256=iOeqyU9lUJ-kpiDj3lS86XjnE-L
63
53
  omextra/specs/proto/_antlr/Protobuf3Visitor.py,sha256=K5NxkXvMEJVsqnpXsHxMAHJOUFEYKSCSdsNR4RWgrE4,8804
64
54
  omextra/specs/proto/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
55
  omextra/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
+ omextra/sql/alchemy/__init__.py,sha256=pLv2EQmAwcGXN0Y3AE8K5JjyN_4GaHRMHyExaDFm5yU,181
57
+ omextra/sql/alchemy/asyncs.py,sha256=3rBxco1B7iC4gEw2pX7Jk9X7aFL0pe9LMHz3g7uz9Gk,3742
66
58
  omextra/sql/parsing/Minisql.g4,sha256=Jw8xT-8UI6ySHAgStyCg5QX9NTCinvTenLJReWiZIJU,4578
67
59
  omextra/sql/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
60
  omextra/sql/parsing/parsing.py,sha256=FkGpXcHoWzdEwxsxlSPhjAjpUip6U592rdFf0ymKenI,3374
@@ -163,9 +155,9 @@ omextra/text/antlr/cli/__main__.py,sha256=ckYkj0drxabBVwWYewH2SS36TTeAxllZtS4xEl
163
155
  omextra/text/antlr/cli/cli.py,sha256=LW8pJNmySBOV3g8QxquPjUgxFv7YblzEyi555hHH3_M,1234
164
156
  omextra/text/antlr/cli/consts.py,sha256=HUYJP9j4RfeuuQv6HFd2oFMS0piWJ9Sq1tbeAs4OlBc,290
165
157
  omextra/text/antlr/cli/gen.py,sha256=HYleVptrpynwcl6GspX6O9_oMRSxwdYAQGuUFfDYse8,5236
166
- omextra-0.0.0.dev514.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
167
- omextra-0.0.0.dev514.dist-info/METADATA,sha256=xsVoaoJsumnrTjy-kTUB8zC31UIS6k7rmYX8YClvMEo,1409
168
- omextra-0.0.0.dev514.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
169
- omextra-0.0.0.dev514.dist-info/entry_points.txt,sha256=-MFAMks5HgZ60Ore0Wl5lKVKk8z4kf1Ls3WY9E_OlCU,37
170
- omextra-0.0.0.dev514.dist-info/top_level.txt,sha256=o1nCNRejLMcayDngLuWMWwaeOucz33BXbpuoVvvzjPc,8
171
- omextra-0.0.0.dev514.dist-info/RECORD,,
158
+ omextra-0.0.0.dev516.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
159
+ omextra-0.0.0.dev516.dist-info/METADATA,sha256=GW_ZH-CkwAqAJ82STqQeRklcZXXgr7G0SL5J32mncHg,1409
160
+ omextra-0.0.0.dev516.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
161
+ omextra-0.0.0.dev516.dist-info/entry_points.txt,sha256=-MFAMks5HgZ60Ore0Wl5lKVKk8z4kf1Ls3WY9E_OlCU,37
162
+ omextra-0.0.0.dev516.dist-info/top_level.txt,sha256=o1nCNRejLMcayDngLuWMWwaeOucz33BXbpuoVvvzjPc,8
163
+ omextra-0.0.0.dev516.dist-info/RECORD,,