omlish 0.0.0.dev3__py3-none-any.whl → 0.0.0.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

omlish/__about__.py CHANGED
@@ -3,4 +3,4 @@ __url__ = 'https://github.com/wrmsr/omlish'
3
3
  __license__ = 'BSD-3-Clause'
4
4
  __requires_python__ = '>=3.12'
5
5
 
6
- __version__ = '0.0.0.dev3'
6
+ __version__ = '0.0.0.dev4'
omlish/__init__.py CHANGED
@@ -0,0 +1,8 @@
1
+ import sys as _sys
2
+
3
+
4
+ REQUIRED_PYTHON_VERSION = (3, 12)
5
+
6
+
7
+ if _sys.version_info < REQUIRED_PYTHON_VERSION:
8
+ raise EnvironmentError(f'Python version {_sys.version_info=} < {REQUIRED_PYTHON_VERSION}')
omlish/asyncs/__init__.py CHANGED
@@ -6,6 +6,23 @@ from .asyncs import ( # noqa
6
6
  syncable_iterable,
7
7
  )
8
8
 
9
+ from .flavors import ( # noqa
10
+ ContextManagerAdapter,
11
+ Flavor,
12
+ adapt,
13
+ adapt_context,
14
+ from_anyio,
15
+ from_anyio_context,
16
+ from_asyncio,
17
+ from_asyncio_context,
18
+ from_trio,
19
+ from_trio_context,
20
+ get_flavor,
21
+ mark_anyio,
22
+ mark_asyncio,
23
+ mark_flavor,
24
+ mark_trio,
25
+ )
9
26
 
10
27
  from .futures import ( # noqa
11
28
  FutureError,
@@ -15,3 +32,7 @@ from .futures import ( # noqa
15
32
  wait_dependent_futures,
16
33
  wait_futures,
17
34
  )
35
+
36
+ from .trio_asyncio import ( # noqa
37
+ with_trio_asyncio_loop,
38
+ )
@@ -0,0 +1,201 @@
1
+ """
2
+ TODO:
3
+ - 'get current'? -> sniffio..
4
+ - mark whole class / module?
5
+ - sync/greenlet bridge
6
+ """
7
+ import abc
8
+ import dataclasses as dc
9
+ import enum
10
+ import typing as ta
11
+
12
+ from .. import lang
13
+ from .trio_asyncio import check_trio_asyncio
14
+
15
+ if ta.TYPE_CHECKING:
16
+ import sniffio
17
+ import trio_asyncio
18
+ else:
19
+ sniffio = lang.proxy_import('sniffio')
20
+ trio_asyncio = lang.proxy_import('trio_asyncio')
21
+
22
+
23
+ T = ta.TypeVar('T')
24
+
25
+
26
+ ##
27
+
28
+
29
+ _FLAVOR_ATTR = '__async_flavor__'
30
+
31
+
32
+ class _MISSING(lang.Marker):
33
+ pass
34
+
35
+
36
+ class Flavor(enum.Enum):
37
+ ASYNCIO = enum.auto()
38
+ TRIO = enum.auto()
39
+ ANYIO = enum.auto()
40
+
41
+
42
+ def mark_flavor(f: Flavor):
43
+ if not isinstance(f, Flavor):
44
+ raise TypeError(f)
45
+
46
+ def inner(fn):
47
+ setattr(fn, _FLAVOR_ATTR, f)
48
+ return fn
49
+
50
+ return inner
51
+
52
+
53
+ mark_asyncio = mark_flavor(Flavor.ASYNCIO)
54
+ mark_anyio = mark_flavor(Flavor.ANYIO)
55
+ mark_trio = mark_flavor(Flavor.TRIO)
56
+
57
+ PACKAGE_FLAVORS: ta.MutableMapping[str, Flavor] = {
58
+ 'anyio': Flavor.ANYIO,
59
+ 'asyncio': Flavor.ASYNCIO,
60
+ 'trio': Flavor.TRIO,
61
+
62
+ 'sqlalchemy.ext.asyncio': Flavor.ASYNCIO,
63
+ }
64
+
65
+ _MODULE_FLAVOR_CACHE: dict[str, Flavor | None] = {}
66
+
67
+
68
+ def _get_module_flavor(p: str) -> Flavor | None:
69
+ try:
70
+ return _MODULE_FLAVOR_CACHE[p]
71
+ except KeyError:
72
+ pass
73
+ pf: Flavor | None = None
74
+ for cp, cf in PACKAGE_FLAVORS.items():
75
+ if p.startswith(cp) and (len(cp) == len(p) or p[len(cp)] == '.'):
76
+ pf = cf
77
+ break
78
+ _MODULE_FLAVOR_CACHE[p] = pf
79
+ return pf
80
+
81
+
82
+ def get_flavor(obj: ta.Any, default: ta.Union[Flavor, type[_MISSING], None] = _MISSING) -> Flavor:
83
+ u = lang.unwrap_func(obj)
84
+
85
+ try:
86
+ return getattr(u, _FLAVOR_ATTR)
87
+ except AttributeError:
88
+ pass
89
+
90
+ if (mn := getattr(u, '__module__', None)) is not None:
91
+ if (pf := _get_module_flavor(mn)):
92
+ return pf
93
+
94
+ if default is not _MISSING:
95
+ return default # type: ignore
96
+
97
+ raise TypeError(f'not marked with flavor: {obj}')
98
+
99
+
100
+ ##
101
+
102
+
103
+ class Adapter(lang.Abstract):
104
+ _FROM_METHODS_BY_FLAVOR: ta.ClassVar[ta.Mapping[Flavor, str]] = {
105
+ Flavor.ANYIO: 'from_anyio',
106
+ Flavor.ASYNCIO: 'from_asyncio',
107
+ Flavor.TRIO: 'from_trio',
108
+ }
109
+
110
+ def adapt(self, fn, fl=None):
111
+ if fl is None:
112
+ fl = get_flavor(fn)
113
+ return getattr(self, self._FROM_METHODS_BY_FLAVOR[fl])(fn)
114
+
115
+ #
116
+
117
+ def from_anyio(self, fn):
118
+ return fn
119
+
120
+ @abc.abstractmethod
121
+ def from_asyncio(self, fn):
122
+ raise NotImplementedError
123
+
124
+ @abc.abstractmethod
125
+ def from_trio(self, fn):
126
+ raise NotImplementedError
127
+
128
+
129
+ class AsyncioAdapter(Adapter):
130
+ def from_asyncio(self, fn):
131
+ return fn
132
+
133
+ def from_trio(self, fn):
134
+ check_trio_asyncio()
135
+ return trio_asyncio.trio_as_aio(fn)
136
+
137
+
138
+ class TrioAdapter(Adapter):
139
+ def from_asyncio(self, fn):
140
+ check_trio_asyncio()
141
+ return trio_asyncio.aio_as_trio(fn)
142
+
143
+ def from_trio(self, fn):
144
+ return fn
145
+
146
+
147
+ _ADAPTERS_BY_BACKEND: ta.Mapping[str, Adapter] = {
148
+ 'asyncio': AsyncioAdapter(),
149
+ 'trio': TrioAdapter(),
150
+ }
151
+
152
+
153
+ def get_adapter() -> Adapter:
154
+ return _ADAPTERS_BY_BACKEND[sniffio.current_async_library()]
155
+
156
+
157
+ def adapt(fn, fl=None):
158
+ return get_adapter().adapt(fn, fl)
159
+
160
+
161
+ def from_anyio(fn):
162
+ return get_adapter().from_anyio(fn)
163
+
164
+
165
+ def from_asyncio(fn):
166
+ return get_adapter().from_asyncio(fn)
167
+
168
+
169
+ def from_trio(fn):
170
+ return get_adapter().from_trio(fn)
171
+
172
+
173
+ ##
174
+
175
+
176
+ @dc.dataclass(frozen=True)
177
+ class ContextManagerAdapter(ta.Generic[T]):
178
+ obj: ta.AsyncContextManager[T]
179
+ adapt: ta.Callable[[ta.Callable], ta.Callable]
180
+
181
+ async def __aenter__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
182
+ return await self.adapt(self.obj.__aenter__)(*args, **kwargs)
183
+
184
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
185
+ return await self.adapt(self.obj.__aexit__)(exc_type, exc_val, exc_tb)
186
+
187
+
188
+ def adapt_context(obj):
189
+ return ContextManagerAdapter(obj, get_adapter().adapt)
190
+
191
+
192
+ def from_anyio_context(obj):
193
+ return ContextManagerAdapter(obj, get_adapter().from_anyio)
194
+
195
+
196
+ def from_asyncio_context(obj):
197
+ return ContextManagerAdapter(obj, get_adapter().from_asyncio)
198
+
199
+
200
+ def from_trio_context(obj):
201
+ return ContextManagerAdapter(obj, get_adapter().from_trio)
@@ -0,0 +1,41 @@
1
+ import functools
2
+ import typing as ta
3
+
4
+ from .. import lang
5
+
6
+ if ta.TYPE_CHECKING:
7
+ import asyncio
8
+ import sniffio
9
+ import trio_asyncio
10
+ else:
11
+ asyncio = lang.proxy_import('asyncio')
12
+ sniffio = lang.proxy_import('sniffio')
13
+ trio_asyncio = lang.proxy_import('trio_asyncio')
14
+
15
+
16
+ def check_trio_asyncio() -> None:
17
+ if trio_asyncio.current_loop.get() is None:
18
+ raise RuntimeError('trio_asyncio loop not running')
19
+
20
+
21
+ def with_trio_asyncio_loop(fn, *, wait=False):
22
+ @functools.wraps(fn)
23
+ async def inner(*args, **kwargs):
24
+ if trio_asyncio.current_loop.get() is not None:
25
+ await fn(*args, **kwargs)
26
+ return
27
+
28
+ if sniffio.current_async_library() != 'trio':
29
+ raise RuntimeError('trio loop not running')
30
+
31
+ loop: asyncio.BaseEventLoop
32
+ async with trio_asyncio.open_loop() as loop:
33
+ try:
34
+ await fn(*args, **kwargs)
35
+ finally:
36
+ if wait:
37
+ # FIXME: lol
38
+ while asyncio.all_tasks(loop):
39
+ await asyncio.sleep(.2)
40
+
41
+ return inner
omlish/dynamic.py CHANGED
@@ -49,7 +49,7 @@ class Var(ta.Generic[T]):
49
49
 
50
50
  def __init__(
51
51
  self,
52
- default: type[MISSING] | T = MISSING, # type: ignore
52
+ default: type[MISSING] | T = MISSING,
53
53
  *,
54
54
  new: ta.Callable[[], T] | type[MISSING] = MISSING,
55
55
  validate: ta.Callable[[T], None] | None = None,
@@ -138,7 +138,7 @@ class Var(ta.Generic[T]):
138
138
  frame = frame.f_back
139
139
 
140
140
  if self._new is not MISSING:
141
- yield self._new()
141
+ yield self._new() # type: ignore
142
142
 
143
143
  def __iter__(self) -> ta.Iterator[T]:
144
144
  return self.values
omlish/fnpairs.py CHANGED
@@ -1,3 +1,16 @@
1
+ """
2
+ TODO:
3
+ - objects
4
+ - csv
5
+ - csvloader
6
+ - cbor
7
+ - cloudpickle
8
+ - alt json backends
9
+ - compression
10
+ - snappy
11
+ - lz4
12
+ - wrapped (wait for usecase)
13
+ """
1
14
  import abc
2
15
  import codecs
3
16
  import dataclasses as dc
@@ -6,32 +19,41 @@ import typing as ta
6
19
  from . import lang
7
20
 
8
21
  if ta.TYPE_CHECKING:
9
- import bzip2 as _bzip2
22
+ import bz2 as _bz2
23
+ import cloudpickle as _cloudpickle
10
24
  import gzip as _gzip
11
25
  import json as _json
26
+ import lz4.frame as _lz4_frame
12
27
  import lzma as _lzma
13
28
  import pickle as _pickle
29
+ import snappy as _snappy
14
30
  import struct as _struct
15
31
  import tomllib as _tomllib
32
+ import yaml as _yaml
33
+ import zstd as _zstd
16
34
 
17
35
  else:
18
- _bzip2 = lang.proxy_import('bzip2')
36
+ _bz2 = lang.proxy_import('bz2')
37
+ _cloudpickle = lang.proxy_import('cloudpickle')
19
38
  _gzip = lang.proxy_import('gzip')
20
39
  _json = lang.proxy_import('json')
40
+ _lz4_frame = lang.proxy_import('lz4.frame')
21
41
  _lzma = lang.proxy_import('lzma')
22
42
  _pickle = lang.proxy_import('pickle')
43
+ _snappy = lang.proxy_import('snappy')
23
44
  _struct = lang.proxy_import('struct')
24
45
  _tomllib = lang.proxy_import('tomllib')
25
-
26
- _zstd = lang.proxy_import('zstd')
27
- _yaml = lang.proxy_import('yaml')
46
+ _yaml = lang.proxy_import('yaml')
47
+ _zstd = lang.proxy_import('zstd')
28
48
 
29
49
 
30
50
  ##
31
51
 
32
52
 
33
53
  F = ta.TypeVar('F')
54
+ F2 = ta.TypeVar('F2')
34
55
  T = ta.TypeVar('T')
56
+ T2 = ta.TypeVar('T2')
35
57
  U = ta.TypeVar('U')
36
58
 
37
59
 
@@ -44,6 +66,11 @@ class FnPair(ta.Generic[F, T], abc.ABC):
44
66
  def backward(self, t: T) -> F:
45
67
  raise NotImplementedError
46
68
 
69
+ ##
70
+
71
+ def __call__(self, f: F) -> T:
72
+ return self.forward(f)
73
+
47
74
  def invert(self) -> 'FnPair[T, F]':
48
75
  if isinstance(self, Inverted):
49
76
  return self.fp
@@ -76,6 +103,8 @@ Simple.__abstractmethods__ = frozenset() # noqa
76
103
 
77
104
  of = Simple
78
105
 
106
+ NOP: FnPair[ta.Any, ta.Any] = of(lang.identity, lang.identity)
107
+
79
108
 
80
109
  ##
81
110
 
@@ -91,9 +120,6 @@ class Inverted(FnPair[F, T]):
91
120
  return self.fp.forward(t)
92
121
 
93
122
 
94
- ##
95
-
96
-
97
123
  @dc.dataclass(frozen=True)
98
124
  class Composite(FnPair[F, T]):
99
125
  children: ta.Sequence[FnPair]
@@ -109,6 +135,14 @@ class Composite(FnPair[F, T]):
109
135
  return ta.cast(F, t)
110
136
 
111
137
 
138
+ def compose(*ps: FnPair) -> FnPair:
139
+ if not ps:
140
+ return NOP
141
+ if len(ps) == 1:
142
+ return ps[0]
143
+ return Composite(ps)
144
+
145
+
112
146
  ##
113
147
 
114
148
 
@@ -177,6 +211,10 @@ def _register_extension(*ss):
177
211
  return inner
178
212
 
179
213
 
214
+ def get_for_extension(ext: str) -> FnPair:
215
+ return compose(*[_EXTENSION_REGISTRY[p]() for p in ext.split('.')])
216
+
217
+
180
218
  ##
181
219
 
182
220
 
@@ -184,28 +222,28 @@ class Compression(FnPair[bytes, bytes], abc.ABC):
184
222
  pass
185
223
 
186
224
 
187
- @_register_extension('gz')
225
+ @_register_extension('bz2')
188
226
  @dc.dataclass(frozen=True)
189
- class Gzip(Compression):
227
+ class Bz2(Compression):
190
228
  compresslevel: int = 9
191
229
 
192
230
  def forward(self, f: bytes) -> bytes:
193
- return _gzip.compress(f, compresslevel=self.compresslevel)
231
+ return _bz2.compress(f, compresslevel=self.compresslevel)
194
232
 
195
233
  def backward(self, t: bytes) -> bytes:
196
- return _gzip.decompress(t)
234
+ return _bz2.decompress(t)
197
235
 
198
236
 
199
- @_register_extension('bz2')
237
+ @_register_extension('gz')
200
238
  @dc.dataclass(frozen=True)
201
- class Bzip2(Compression):
239
+ class Gzip(Compression):
202
240
  compresslevel: int = 9
203
241
 
204
242
  def forward(self, f: bytes) -> bytes:
205
- return _bzip2.compress(f, compresslevel=self.compresslevel)
243
+ return _gzip.compress(f, compresslevel=self.compresslevel)
206
244
 
207
245
  def backward(self, t: bytes) -> bytes:
208
- return _bzip2.decompress(t)
246
+ return _gzip.decompress(t)
209
247
 
210
248
 
211
249
  @_register_extension('lzma')
@@ -220,6 +258,27 @@ class Lzma(Compression):
220
258
  #
221
259
 
222
260
 
261
+ @_register_extension('lz4')
262
+ @dc.dataclass(frozen=True)
263
+ class Lz4(Compression):
264
+ compression_level: int = 0
265
+
266
+ def forward(self, f: bytes) -> bytes:
267
+ return _lz4_frame.compress(f, compression_level=self.compression_level)
268
+
269
+ def backward(self, t: bytes) -> bytes:
270
+ return _lz4_frame.decompress(t)
271
+
272
+
273
+ @_register_extension('snappy')
274
+ class Snappy(Compression):
275
+ def forward(self, f: bytes) -> bytes:
276
+ return _snappy.compress(f)
277
+
278
+ def backward(self, t: bytes) -> bytes:
279
+ return _snappy.decompress(t)
280
+
281
+
223
282
  @_register_extension('zstd')
224
283
  class Zstd(Compression):
225
284
  def forward(self, f: bytes) -> bytes:
@@ -243,9 +302,27 @@ class Struct(FnPair[tuple, bytes]):
243
302
  return _struct.unpack(self.fmt, t)
244
303
 
245
304
 
305
+ ##
306
+
307
+
308
+ class Object(FnPair[ta.Any, T], lang.Abstract): # noqa
309
+ pass
310
+
311
+
312
+ class ObjectStr(Object[str], lang.Abstract): # noqa
313
+ pass
314
+
315
+
316
+ class ObjectBytes(Object[bytes], lang.Abstract): # noqa
317
+ pass
318
+
319
+
320
+ #
321
+
322
+
246
323
  @_register_extension('pkl')
247
324
  @dc.dataclass(frozen=True)
248
- class Pickle(FnPair[ta.Any, bytes]):
325
+ class Pickle(ObjectBytes):
249
326
  protocol: int | None = None
250
327
 
251
328
  def forward(self, f: ta.Any) -> bytes:
@@ -257,7 +334,7 @@ class Pickle(FnPair[ta.Any, bytes]):
257
334
 
258
335
  @_register_extension('json')
259
336
  @dc.dataclass(frozen=True)
260
- class Json(FnPair[ta.Any, str]):
337
+ class Json(ObjectStr):
261
338
  indent: int | str | None = dc.field(default=None, kw_only=True)
262
339
  separators: tuple[str, str] | None = dc.field(default=None, kw_only=True)
263
340
 
@@ -283,7 +360,7 @@ class JsonLines(FnPair[ta.Sequence[ta.Any], str]):
283
360
 
284
361
 
285
362
  @_register_extension('toml')
286
- class Toml(FnPair[ta.Any, str]):
363
+ class Toml(ObjectStr):
287
364
  def forward(self, f: ta.Any) -> str:
288
365
  raise NotImplementedError
289
366
 
@@ -294,8 +371,20 @@ class Toml(FnPair[ta.Any, str]):
294
371
  #
295
372
 
296
373
 
374
+ @_register_extension('cpkl')
375
+ @dc.dataclass(frozen=True)
376
+ class Cloudpickle(ObjectBytes):
377
+ protocol: int | None = None
378
+
379
+ def forward(self, f: ta.Any) -> bytes:
380
+ return _cloudpickle.dumps(f, protocol=self.protocol)
381
+
382
+ def backward(self, t: bytes) -> ta.Any:
383
+ return _cloudpickle.loads(t)
384
+
385
+
297
386
  @_register_extension('yml', 'yaml')
298
- class Yaml(FnPair[ta.Any, str]):
387
+ class Yaml(ObjectStr):
299
388
  def forward(self, f: ta.Any) -> str:
300
389
  return _yaml.dump(f)
301
390
 
@@ -303,9 +392,9 @@ class Yaml(FnPair[ta.Any, str]):
303
392
  return _yaml.safe_load(t)
304
393
 
305
394
 
306
- class UnsafeYaml(FnPair[ta.Any, str]):
395
+ class YamlUnsafe(ObjectStr):
307
396
  def forward(self, f: ta.Any) -> str:
308
397
  return _yaml.dump(f)
309
398
 
310
399
  def backward(self, t: str) -> ta.Any:
311
- return _yaml.safe_load(t, loader=_yaml.FullLoader)
400
+ return _yaml.load(t, _yaml.FullLoader)
@@ -14,7 +14,7 @@ Element Types:
14
14
  - Overrides
15
15
  - Expose
16
16
  - Private
17
- - ScopeSeed
17
+ - ScopeBinding
18
18
  """
19
19
  import typing as ta
20
20
 
@@ -31,6 +31,7 @@ from ..overrides import Overrides
31
31
  from ..private import Expose
32
32
  from ..private import Private
33
33
  from ..scopes import ScopeBinding
34
+ from ..types import Scope
34
35
  from .bindings import BindingImpl
35
36
  from .providers import MultiProviderImpl
36
37
  from .providers import make_provider_impl
@@ -152,3 +153,14 @@ class ElementCollection(lang.Final):
152
153
  @lang.cached_function
153
154
  def binding_impl_map(self) -> ta.Mapping[Key, BindingImpl]:
154
155
  return self._build_binding_impl_map(self.element_multimap())
156
+
157
+ ##
158
+
159
+ @lang.cached_function
160
+ def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
161
+ bim = self.binding_impl_map()
162
+ ret: dict[Scope, list[Key]] = {}
163
+ for e in self.elements_of_type(Eager):
164
+ bi = bim[e.key]
165
+ ret.setdefault(bi.scope, []).append(e.key)
166
+ return ret
@@ -7,6 +7,8 @@ TODO:
7
7
  - config is probably shared with ElementCollection... but not 'bound', must be shared everywhere
8
8
  - InjectorRoot object?
9
9
  - ** eagers in any scope, on scope init/open
10
+ - injection listeners
11
+ - unions - raise on ambiguous - usecase: sql.AsyncEngineLike
10
12
  """
11
13
  import contextlib
12
14
  import typing as ta
@@ -14,7 +16,6 @@ import weakref
14
16
 
15
17
  from ... import check
16
18
  from ... import lang
17
- from ..eagers import Eager
18
19
  from ..elements import Elements
19
20
  from ..exceptions import CyclicDependencyError
20
21
  from ..exceptions import UnboundKeyError
@@ -52,6 +53,7 @@ class InjectorImpl(Injector, lang.Final):
52
53
  }
53
54
 
54
55
  self._bim = ec.binding_impl_map()
56
+ self._ekbs = ec.eager_keys_by_scope()
55
57
 
56
58
  self._cs: weakref.WeakSet[InjectorImpl] | None = None
57
59
  self._root: InjectorImpl = p._root if p is not None else self # noqa
@@ -66,13 +68,13 @@ class InjectorImpl(Injector, lang.Final):
66
68
  s: make_scope_impl(s) for s in ss
67
69
  }
68
70
 
69
- self._instantiate_eagers()
71
+ self._instantiate_eagers(Unscoped())
70
72
 
71
73
  _root: 'InjectorImpl'
72
74
 
73
- def _instantiate_eagers(self) -> None:
74
- for e in self._ec.elements_of_type(Eager):
75
- self.provide(e.key)
75
+ def _instantiate_eagers(self, sc: Scope) -> None:
76
+ for k in self._ekbs.get(sc, ()):
77
+ self.provide(k)
76
78
 
77
79
  def get_scope_impl(self, sc: Scope) -> ScopeImpl:
78
80
  return self._scopes[sc]
@@ -141,8 +141,8 @@ class SeededScopeImpl(ScopeImpl):
141
141
  def __init__(self, ss: SeededScope, i: Injector) -> None:
142
142
  super().__init__()
143
143
  self._ss = check.isinstance(ss, SeededScope)
144
- ii = check.isinstance(i, injector_.InjectorImpl)
145
- self._ssi = check.isinstance(ii.get_scope_impl(self._ss), SeededScopeImpl)
144
+ self._ii = check.isinstance(i, injector_.InjectorImpl)
145
+ self._ssi = check.isinstance(self._ii.get_scope_impl(self._ss), SeededScopeImpl)
146
146
 
147
147
  @contextlib.contextmanager
148
148
  def __call__(self, seeds: ta.Mapping[Key, ta.Any]) -> ta.Generator[None, None, None]:
@@ -150,6 +150,7 @@ class SeededScopeImpl(ScopeImpl):
150
150
  if self._ssi._st is not None: # noqa
151
151
  raise ScopeAlreadyOpenError(self._ss)
152
152
  self._ssi._st = SeededScopeImpl.State(dict(seeds)) # noqa
153
+ self._ii._instantiate_eagers(self._ss) # noqa
153
154
  yield
154
155
  finally:
155
156
  if self._ssi._st is None: # noqa
omlish/json.py CHANGED
@@ -1,7 +1,142 @@
1
+ """
2
+ json
3
+ dump
4
+ skipkeys=False
5
+ ensure_ascii=True
6
+ check_circular=True
7
+ allow_nan=True
8
+ cls=None
9
+ indent=None
10
+ separators=None
11
+ default=None
12
+ sort_keys=False
13
+ dumps
14
+ ^
15
+ load
16
+ cls=None
17
+ object_hook=None
18
+ parse_float=None
19
+ parse_int=None
20
+ parse_constant=None
21
+ object_pairs_hook=None
22
+ loads
23
+ ^
24
+
25
+ ujson
26
+ dump
27
+ ensure_ascii
28
+ encode_html_chars
29
+ escape_forward_slashes
30
+ sort_keys
31
+ indent
32
+ allow_nan
33
+ reject_bytes
34
+ default
35
+ separators
36
+ dumps
37
+ ^
38
+ load
39
+ loads
40
+
41
+ orjson
42
+ dumps
43
+ default
44
+ option
45
+ OPT_INDENT_2
46
+ OPT_NAIVE_UTC
47
+ OPT_NON_STR_KEYS
48
+ OPT_OMIT_MICROSECONDS
49
+ OPT_PASSTHROUGH_DATACLASS
50
+ OPT_PASSTHROUGH_DATETIME
51
+ OPT_PASSTHROUGH_SUBCLASS
52
+ OPT_SERIALIZE_DATACLASS
53
+ OPT_SERIALIZE_NUMPY
54
+ OPT_SERIALIZE_UUID
55
+ OPT_SORT_KEYS
56
+ OPT_STRICT_INTEGER
57
+ OPT_UTC_Z
58
+ loads
59
+
60
+ rapidjson
61
+ dump
62
+ skipkeys=False,
63
+ ensure_ascii=True,
64
+ write_mode=WM_COMPACT,
65
+ WM_COMPACT
66
+ WM_PRETTY
67
+ WM_SINGLE_LINE_ARRAY
68
+ indent=4,
69
+ default=None,
70
+ sort_keys=False,
71
+ number_mode=None,
72
+ NM_NONE
73
+ NM_DECIMAL
74
+ NM_NAN
75
+ NM_NATIVE
76
+ datetime_mode=None,
77
+ DM_NONE
78
+ DM_ISO8601
79
+ DM_UNIX_TIME
80
+ DM_ONLY_SECONDS
81
+ DM_IGNORE_TZ
82
+ DM_NAIVE_IS_UTC
83
+ DM_SHIFT_TO_UTC
84
+ uuid_mode=None,
85
+ UM_NONE
86
+ UM_CANONICAL
87
+ UM_HEX
88
+ bytes_mode=BM_UTF8,
89
+ BM_NONE
90
+ BM_UTF8
91
+ iterable_mode=IM_ANY_ITERABLE,
92
+ IM_ANY_ITERABLE
93
+ IM_ONLY_LISTS
94
+ mapping_mode=MM_ANY_MAPPING,
95
+ MM_ANY_MAPPING
96
+ MM_ONLY_DICTS
97
+ MM_COERCE_KEYS_TO_STRINGS
98
+ MM_SKIP_NON_STRING_KEYS
99
+ MM_SORT_KEYS
100
+ chunk_size
101
+ allow_nan=True
102
+ dumps
103
+ ^
104
+ -chunk_size
105
+ load
106
+ object_hook=None,
107
+ number_mode=None,
108
+ ^
109
+ datetime_mode=None,
110
+ ^
111
+ uuid_mode=None,
112
+ ^
113
+ parse_mode=None,
114
+ PM_NONE
115
+ PM_COMMENTS
116
+ PM_TRAILING_COMMAS
117
+ chunk_size=65536,
118
+ allow_nan=True
119
+ loads
120
+ ^
121
+ -chunk_size
122
+
123
+ """
1
124
  import functools
2
125
  import json as _json
3
126
  import typing as ta
4
127
 
128
+ from . import lang
129
+
130
+ if ta.TYPE_CHECKING:
131
+ import orjson as _orjson
132
+ import ujson as _ujson
133
+ else:
134
+ _orjson = lang.proxy_import('orjson')
135
+ _ujson = lang.proxy_import('ujson')
136
+
137
+
138
+ ##
139
+
5
140
 
6
141
  dump = _json.dump
7
142
  dumps = _json.dumps
@@ -11,6 +146,7 @@ detect_encoding = _json.detect_encoding
11
146
  load = _json.load
12
147
  loads = _json.loads
13
148
 
149
+
14
150
  ##
15
151
 
16
152
 
omlish/lang/__init__.py CHANGED
@@ -156,6 +156,10 @@ from .strings import ( # noqa
156
156
  snake_case,
157
157
  )
158
158
 
159
+ from .sys import ( # noqa
160
+ is_gil_enabled,
161
+ )
162
+
159
163
  from .timeouts import ( # noqa
160
164
  DeadlineTimeout,
161
165
  InfiniteTimeout,
@@ -89,7 +89,7 @@ class PackageSealed:
89
89
  class NotInstantiable(Abstract):
90
90
  __slots__ = ()
91
91
 
92
- def __new__(cls, *args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn: # noqa
92
+ def __new__(cls, *args, **kwargs):
93
93
  raise TypeError
94
94
 
95
95
 
omlish/lang/sys.py ADDED
@@ -0,0 +1,7 @@
1
+ import sys
2
+
3
+
4
+ def is_gil_enabled() -> bool:
5
+ if (fn := getattr(sys, '_is_gil_enabled', None)) is not None:
6
+ return fn()
7
+ return True
omlish/sql/__init__.py CHANGED
@@ -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
+ )
omlish/sql/asyncs.py ADDED
@@ -0,0 +1,148 @@
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 .. import asyncs as au
13
+
14
+
15
+ T = ta.TypeVar('T')
16
+ P = ta.ParamSpec('P')
17
+
18
+
19
+ AsyncEngineLike = ta.Union[saa.AsyncEngine, 'AsyncEngine']
20
+ AsyncConnectionLike = ta.Union[saa.AsyncConnection, 'AsyncConnection']
21
+ AsyncTransactionLike = ta.Union[saa.AsyncTransaction, 'AsyncTransaction']
22
+
23
+
24
+ class AsyncTransaction:
25
+ def __init__(self, underlying: saa.AsyncTransaction) -> None:
26
+ super().__init__()
27
+ self._underlying = underlying
28
+
29
+ @property
30
+ def underlying(self) -> saa.AsyncTransaction:
31
+ return self._underlying
32
+
33
+ ##
34
+
35
+ @au.mark_asyncio
36
+ async def close(self) -> None:
37
+ await au.from_asyncio(self._underlying.close)()
38
+
39
+ @au.mark_asyncio
40
+ async def rollback(self) -> None:
41
+ await au.from_asyncio(self._underlying.rollback)()
42
+
43
+ @au.mark_asyncio
44
+ async def commit(self) -> None:
45
+ await au.from_asyncio(self._underlying.commit)()
46
+
47
+
48
+ class AsyncConnection:
49
+ def __init__(self, underlying: saa.AsyncConnection) -> None:
50
+ super().__init__()
51
+ self._underlying = underlying
52
+
53
+ @property
54
+ def underlying(self) -> saa.AsyncConnection:
55
+ return self._underlying
56
+
57
+ ##
58
+
59
+ @contextlib.asynccontextmanager
60
+ @au.mark_asyncio
61
+ async def begin(self) -> ta.AsyncIterator[AsyncTransaction]:
62
+ async with au.from_asyncio_context(self._underlying.begin()) as u:
63
+ yield AsyncTransaction(u)
64
+
65
+ @au.mark_asyncio
66
+ async def execute(
67
+ self,
68
+ statement: ta.Any,
69
+ *args: ta.Any,
70
+ **kwargs: ta.Any,
71
+ ) -> sa.CursorResult[ta.Any]:
72
+ return await au.from_asyncio(self._underlying.execute)(statement, *args, **kwargs)
73
+
74
+ @au.mark_asyncio
75
+ async def run_sync(
76
+ self,
77
+ fn: ta.Callable[ta.Concatenate[sa.Connection, P], T],
78
+ *args: P.args,
79
+ **kwargs: P.kwargs,
80
+ ) -> T:
81
+ return await au.from_asyncio(self._underlying.run_sync)(fn, *args, **kwargs)
82
+
83
+
84
+ class AsyncEngine:
85
+ def __init__(self, underlying: saa.AsyncEngine) -> None:
86
+ super().__init__()
87
+ self._underlying = underlying
88
+
89
+ @property
90
+ def underlying(self) -> saa.AsyncEngine:
91
+ return self._underlying
92
+
93
+ ##
94
+
95
+ @contextlib.asynccontextmanager
96
+ @au.mark_asyncio
97
+ async def connect(self) -> ta.AsyncIterator[AsyncConnection]:
98
+ async with au.from_asyncio_context(self._underlying.connect()) as u:
99
+ yield AsyncConnection(u)
100
+
101
+ @au.mark_asyncio
102
+ async def dispose(self, close: bool = True) -> None:
103
+ await au.from_asyncio(self._underlying.dispose)(close)
104
+
105
+
106
+ ##
107
+
108
+
109
+ @ta.overload
110
+ def async_adapt(obj: AsyncEngine) -> AsyncEngine:
111
+ ...
112
+
113
+
114
+ @ta.overload
115
+ def async_adapt(obj: AsyncConnection) -> AsyncConnection:
116
+ ...
117
+
118
+
119
+ @ta.overload
120
+ def async_adapt(obj: AsyncTransaction) -> AsyncTransaction:
121
+ ...
122
+
123
+
124
+ @ta.overload
125
+ def async_adapt(obj: saa.AsyncEngine) -> AsyncEngine:
126
+ ...
127
+
128
+
129
+ @ta.overload
130
+ def async_adapt(obj: saa.AsyncConnection) -> AsyncConnection:
131
+ ...
132
+
133
+
134
+ @ta.overload
135
+ def async_adapt(obj: saa.AsyncTransaction) -> AsyncTransaction:
136
+ ...
137
+
138
+
139
+ def async_adapt(obj: ta.Any) -> ta.Any:
140
+ if isinstance(obj, (AsyncEngine, AsyncConnection, AsyncTransaction)):
141
+ return obj
142
+ if isinstance(obj, saa.AsyncTransaction):
143
+ return AsyncTransaction(obj)
144
+ if isinstance(obj, saa.AsyncConnection):
145
+ return AsyncConnection(obj)
146
+ if isinstance(obj, saa.AsyncEngine):
147
+ return AsyncEngine(obj)
148
+ raise TypeError(obj)
omlish/testing/pydevd.py CHANGED
@@ -87,6 +87,8 @@ def silence_subprocess_check() -> None:
87
87
 
88
88
  @lang.cached_function
89
89
  def patch_for_trio_asyncio() -> None:
90
+ """Fix for `trio a callable object was expected by call_soon(), got Task`"""
91
+
90
92
  try:
91
93
  import pydevd_nest_asyncio # noqa
92
94
  except ImportError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev3
3
+ Version: 0.0.0.dev4
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -24,6 +24,9 @@ Provides-Extra: sql
24
24
  Requires-Dist: sqlalchemy ; extra == 'sql'
25
25
  Provides-Extra: test
26
26
  Requires-Dist: pytest ; extra == 'test'
27
+ Provides-Extra: trio
28
+ Requires-Dist: trio ; extra == 'trio'
29
+ Requires-Dist: trio-asyncio ; extra == 'trio'
27
30
  Provides-Extra: wrapt
28
31
  Requires-Dist: wrapt ; extra == 'wrapt'
29
32
  Provides-Extra: yaml
@@ -1,15 +1,15 @@
1
- omlish/__about__.py,sha256=PDr-dayjZF7uNCY78Cmj-DplO_IRgBlaOdS27ftVXMk,153
2
- omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1
+ omlish/__about__.py,sha256=NmLFQDO4CRzemxQzOuD9dQdlaNKZkVw4DwBlKkI355I,153
2
+ omlish/__init__.py,sha256=WQMALSGEyPfcQUmuYQkwv3bC_A5v6IfQ6C6rOYETQHA,200
3
3
  omlish/argparse.py,sha256=QRQmX9G0-L_nATkFtGHvpd4qrpYzKATdjuFLbBqzJPM,6224
4
4
  omlish/c3.py,sha256=yhescciIJWoJ9nIWJSrUOtyqIu4uLyPjRHk8RbAJ3f4,8039
5
5
  omlish/cached.py,sha256=i7uWLRrUrE5N1ofzUf9D3OIsxf-dXkgu4OxCep0CCBk,197
6
6
  omlish/check.py,sha256=h66jWSwaiuaaIKPI6_pIfolWYYK3WTR43X1CBO5-PQA,5538
7
7
  omlish/defs.py,sha256=IvUL5i-iyBh5HUhf-gY6sonw3tIozL8066QL71CnoiY,4729
8
8
  omlish/docker.py,sha256=oLyJQg4Mu-k_WfDVP7PU9yK1cI-JCdcrTVjrQg2sQsE,4140
9
- omlish/dynamic.py,sha256=9LHfKegKnMFoczFfqIZuGpkR8xGxMxq_lAvNJTDJjWs,6541
10
- omlish/fnpairs.py,sha256=DgXK-AbcIFq7a-HI0a-7ctvKvoSAou4wW5B0fcorMIc,7252
9
+ omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
10
+ omlish/fnpairs.py,sha256=f9UfdkDCvfKLivN1H-eKbdOYe-8y1f2aPB6AuRGKLIM,9124
11
11
  omlish/iterators.py,sha256=YJUTyfqY_calFFOU40TEuptYBqKeavi6L6kc2gXn9s4,5524
12
- omlish/json.py,sha256=P2T8yigNNGDVdYpgKfaA7gtYdJC2EQJ10XaS89MROoQ,4549
12
+ omlish/json.py,sha256=ffsWL9o29ejtGoV0kzEMdDPn5gAI0Ezi3CSQasg41uY,6781
13
13
  omlish/libc.py,sha256=u0481imCiTFqP_e-v9g0pD-0WD249j5vYzhtn-fnNkY,15308
14
14
  omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
15
15
  omlish/os.py,sha256=T77P7_U7jShPXsm_byvXwPoumcmtA0a7KPRvE2GAOhw,950
@@ -17,12 +17,14 @@ omlish/reflect.py,sha256=3dJ9ZGrBan5RRW_Q0OTbl9--oi3B05-ZV4uu0VH-m1c,9891
17
17
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
18
18
  omlish/stats.py,sha256=uqjN-focDVssFZMagj22HqmyJ1TBO4Wt-XnHp8-EtVw,9927
19
19
  omlish/term.py,sha256=OmD3QXntycYHNDCZ-ZV5x2Rz9tw49CoYEn-rld7x_r8,5963
20
- omlish/asyncs/__init__.py,sha256=FIoKl3m_gSxEXL_JJU_dc4OAIKVqq9fLqom_byH3PGY,307
20
+ omlish/asyncs/__init__.py,sha256=e7cMiu0S-Rqg98TT7NBVNZ_DkScqRBqJLxI7Aeb5ToY,680
21
21
  omlish/asyncs/anyio.py,sha256=oYU7FGxMlzPhj5Aa1p7XSdKeD6icoyTPxZRWDc9-YGc,3226
22
22
  omlish/asyncs/asyncio.py,sha256=pSldCmtE9AgVATXb_Vnhjhhz0v2LtqgVXxe7-OFjH0g,414
23
23
  omlish/asyncs/asyncs.py,sha256=MmIz8Gw4PbZd0LUIro_u5kUjobR8i2qGCo6l2hb4U74,2014
24
+ omlish/asyncs/flavors.py,sha256=c8w0M_lTtpm38Vi78cy2jk6pRH7MH_DgR_H9z6PoCpE,4247
24
25
  omlish/asyncs/futures.py,sha256=3KkTj5TOPmvNOcuK9LtOQwmkuLzoEYQfcuaHTXSQ0Ts,5424
25
26
  omlish/asyncs/trio.py,sha256=GKG3wgelFr7gIKKHZhcflvMyCvxXHNZe862KB0Xw2uA,370
27
+ omlish/asyncs/trio_asyncio.py,sha256=-CV9hjjIl3JcLIDhcRNkhoNhbVLIyzoJFe-_dvyOyc0,1112
26
28
  omlish/collections/__init__.py,sha256=H1_J6VNBqdmdMb8vjc7LBJ8ULoiawxoz-1cR41twB2k,1526
27
29
  omlish/collections/_abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
28
30
  omlish/collections/_io_abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
@@ -106,13 +108,13 @@ omlish/inject/scopes.py,sha256=hecYiZwZ3H31cnTgfP01-duTM8OgllwxtvQKpzyHDSU,2033
106
108
  omlish/inject/types.py,sha256=CP3ltWxRtmR2Iqz1Qd7CvaTHQYV4st5FPiNDBN8tmn0,235
107
109
  omlish/inject/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
110
  omlish/inject/impl/bindings.py,sha256=8H586RCgmvwq53XBL9WMbb-1-Tdw_hh9zxIDCwcHA1c,414
109
- omlish/inject/impl/elements.py,sha256=MnBtTF9bx07s8KRYQ7xMtbB6WdtkAJ8Si59l-4Yxn_w,4532
110
- omlish/inject/impl/injector.py,sha256=1jSWr8uvsUPAWRFXEj_HHY6efprMrMBPy0tWvKLUYJw,5528
111
+ omlish/inject/impl/elements.py,sha256=f_eGd-AbyBO0g7Qsu8gA-IKPVKWSUOZcD2MbXxISXoE,4898
112
+ omlish/inject/impl/injector.py,sha256=DeweCOLcij_kYkY1g6i3gjcJ2FSTAe83fO4vnbX7Qmo,5639
111
113
  omlish/inject/impl/inspect.py,sha256=ZQScAStbjWi_5InA6M52ll3cdyfPRuLwYo6kc4kFsmM,2525
112
114
  omlish/inject/impl/private.py,sha256=MiYvs-EiFrla4FQnoqmqGpYx784ij33p0obAS57HOCQ,2611
113
115
  omlish/inject/impl/providers.py,sha256=m0c9AAY849P6egYBOsSCkSoSBc8So7Zbmlo6gZwzTIw,3302
114
- omlish/inject/impl/scopes.py,sha256=Lc2Bw1hK0TrAt0_JrXwSQGCixZdBcqZlEz3_n2Q1bCA,5636
115
- omlish/lang/__init__.py,sha256=mxCgLE8RAq6kQs75cOjT8POZXLrN_EKji2ZLKTnnJ78,2718
116
+ omlish/inject/impl/scopes.py,sha256=itwf_r8hhiYZVHHAzAvAEKRza4M2ST8LJJLOHrhSM2c,5711
117
+ omlish/lang/__init__.py,sha256=mqnnt6K54ROzSLWVJZrLz51ufcSM66RUy9jPtczjegI,2768
116
118
  omlish/lang/cached.py,sha256=MgBHfT83Rrl8tFr98hGBgEeA3ugse077_w6n3S35QYM,7603
117
119
  omlish/lang/clsdct.py,sha256=LXwLCULeI_8Np-7-pZkyNAHpUZLcQiBEQiHwKYQ0WRo,1742
118
120
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
@@ -127,11 +129,12 @@ omlish/lang/maybes.py,sha256=J2N3IDOnSSeGbrN342ln2adt0TDpz2SkyCIiZ0pVRDQ,3425
127
129
  omlish/lang/objects.py,sha256=TAD84PYsD0cGtaQpqo3fQSm99tP4-hckixkQ_K_Iaic,2820
128
130
  omlish/lang/resolving.py,sha256=UgrX-vxXtCGGEmnAMUYP4bUZ6-Ok0EcHVEKAZYbAS-o,1597
129
131
  omlish/lang/strings.py,sha256=wdLo3m9GkV76ZKak3QqtwiR6rWqUd1XlZ-divZppcek,2372
132
+ omlish/lang/sys.py,sha256=Q316Bzy9cC6bFwztkgpc8CKnsCaz4ldYvjIGpTXlxf4,145
130
133
  omlish/lang/timeouts.py,sha256=vECdWYhc_IZgcal1Ng1Y42wf2FV3KAx-i8As-MgGHIQ,1186
131
134
  omlish/lang/typing.py,sha256=pN7Eh8X-1iDqaZ_nZQJaheHBYdkuYTSs0yCN0LcKfDU,2341
132
135
  omlish/lang/classes/__init__.py,sha256=j1p0_uuMznKrY2EhMoj20uv6vx4LXljMzp7AaKe0mmU,530
133
136
  omlish/lang/classes/abstract.py,sha256=goIV14oY24EOs88eVe6E6NyrSPOOLMOcWTXTMuYKiqc,2304
134
- omlish/lang/classes/restrict.py,sha256=D3ICBbITr65xJpms3Q-KA-qygkZdb8FsrJAQBJuCzNg,3147
137
+ omlish/lang/classes/restrict.py,sha256=Fb18KwWo-o7N0OEXpomzsdQpQvJZIK_dAf4d4oAiTsk,3108
135
138
  omlish/lang/classes/simple.py,sha256=p30A4dJxXrtG3ztRF50uBZoBwt84hMqOt_c7cyNx5ec,2672
136
139
  omlish/lang/classes/virtual.py,sha256=hEVeosXKwG4sK_9dqBoNjkK2jF2mV9QoZGOtLqg7L_Q,3315
137
140
  omlish/logs/__init__.py,sha256=V8jEdb5rj9Dli0b9qtR0ZYstRoIlxWqbU38UmEOXbfY,419
@@ -162,11 +165,12 @@ omlish/marshal/standard.py,sha256=MPmtJH3B_LqeoSwQsxKjUm65-cq6albBc8z-gxyKAj4,24
162
165
  omlish/marshal/utils.py,sha256=puKJpwPpuDlMOIrKMcLTRLJyMiL6n_Xs-p59AuDEymA,543
163
166
  omlish/marshal/uuids.py,sha256=_htkeMt1a6FUNBlstZIy2rrB1-1DiavV5dZrjSQNYVM,952
164
167
  omlish/marshal/values.py,sha256=ssHiWdg_L6M17kAn8GiGdPW7UeQOm3RDikWkvwblf5I,263
165
- omlish/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
+ omlish/sql/__init__.py,sha256=pLv2EQmAwcGXN0Y3AE8K5JjyN_4GaHRMHyExaDFm5yU,181
166
169
  omlish/sql/_abc.py,sha256=HLhnnLZ7l0r_N99I-RCqJe6VHth-9iluU7cR-7-5jfs,1519
170
+ omlish/sql/asyncs.py,sha256=Wye3dwh7oZEGYz2Y4DZQSHtW4xjI2AH5qjW-BSS2IfU,3688
167
171
  omlish/sql/dbs.py,sha256=JxcJm3JIcsb7i-t9L0KU9WhAc26QsKUL6ZL6puy3IXI,1763
168
172
  omlish/testing/__init__.py,sha256=l13axR7dyVE9T1e6w1pMYtAkW3vS2J97nBcYQ1FUv18,136
169
- omlish/testing/pydevd.py,sha256=OV9RsVbzo7fYbA3EkoOdbxQ-VB4ex2Sm9jzG8poJHDk,6270
173
+ omlish/testing/pydevd.py,sha256=xOeAithe-XMiwhc0sqnmVe4FRfr2DOkKVgSH0jp5C0U,6352
170
174
  omlish/testing/testing.py,sha256=TaLELNrPx42_-EEqJygDYyQOtEdjXP4KqgLGu6juHmU,2470
171
175
  omlish/testing/pytest/__init__.py,sha256=diebkOX565JdY7HIDMJVLlj3pcFnlK-spv6YIlD77oA,176
172
176
  omlish/testing/pytest/helpers.py,sha256=DKZXQGqKpDiQAh0SuYrzvJ8usJbTnuVQFDpuDmQD_Oc,912
@@ -184,8 +188,8 @@ omlish/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
184
188
  omlish/text/delimit.py,sha256=gAdbVES35IT-maH_M084SJEaf1LRoyXG8Z3pIS6OSCI,4878
185
189
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
186
190
  omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
187
- omlish-0.0.0.dev3.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
188
- omlish-0.0.0.dev3.dist-info/METADATA,sha256=OIbDCU-yfhRj6eymUO_GHiLTfG0lhWtjQtRfh9mB2ys,953
189
- omlish-0.0.0.dev3.dist-info/WHEEL,sha256=rWxmBtp7hEUqVLOnTaDOPpR-cZpCDkzhhcBce-Zyd5k,91
190
- omlish-0.0.0.dev3.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
191
- omlish-0.0.0.dev3.dist-info/RECORD,,
191
+ omlish-0.0.0.dev4.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
192
+ omlish-0.0.0.dev4.dist-info/METADATA,sha256=nuRX2RRNrFfyR75hu2D3EK0MrAlueKOhq6dBz6khV-o,1058
193
+ omlish-0.0.0.dev4.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
194
+ omlish-0.0.0.dev4.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
195
+ omlish-0.0.0.dev4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.0.4)
2
+ Generator: setuptools (71.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5