omlish 0.0.0.dev409__py3-none-any.whl → 0.0.0.dev411__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.dev409'
2
- __revision__ = '9ee675d7bba914e838f5f8ce55dc0af32638a52c'
1
+ __version__ = '0.0.0.dev411'
2
+ __revision__ = 'ab397888a161157384b42de969a8c743c5a2c4b6'
3
3
 
4
4
 
5
5
  #
omlish/asyncs/all.py CHANGED
@@ -1,11 +1,3 @@
1
- from .sync import ( # noqa
2
- SyncableIterable,
3
- async_list,
4
- sync_await,
5
- sync_list,
6
- syncable_iterable,
7
- )
8
-
9
1
  from .bridge import ( # noqa
10
2
  a_to_s,
11
3
  is_in_bridge,
omlish/asyncs/bridge.py CHANGED
@@ -43,7 +43,6 @@ from .. import check
43
43
  from .. import lang
44
44
  from .. import sync
45
45
  from ..concurrent import threadlets
46
- from .sync import sync_await
47
46
 
48
47
 
49
48
  if ta.TYPE_CHECKING:
@@ -71,7 +70,7 @@ def trivial_s_to_a(fn):
71
70
 
72
71
  def trivial_a_to_s(fn):
73
72
  def inner(*args, **kwargs):
74
- return sync_await(fn, *args, **kwargs)
73
+ return lang.sync_await(fn, *args, **kwargs)
75
74
  return inner
76
75
 
77
76
 
omlish/lang/__init__.py CHANGED
@@ -1,3 +1,10 @@
1
+ from .asyncs import ( # noqa
2
+ async_list,
3
+
4
+ sync_await,
5
+ sync_async_list,
6
+ )
7
+
1
8
  from .attrs import ( # noqa
2
9
  AttrOps,
3
10
  AttributePresentError,
@@ -273,11 +280,11 @@ from .lazyglobals import ( # noqa
273
280
  )
274
281
 
275
282
  from .maysyncs import ( # noqa
276
- MaysyncP,
283
+ MaysyncFn,
284
+ MaysyncGeneratorFn,
277
285
 
278
286
  make_maysync,
279
287
  make_maysync_from_sync,
280
-
281
288
  maysync,
282
289
  )
283
290
 
@@ -420,8 +427,11 @@ just = Maybe.just
420
427
 
421
428
  from ..lite.maysyncs import ( # noqa
422
429
  Maywaitable,
423
- Maysync,
430
+ MaysyncGenerator,
431
+
424
432
  Maysync_,
433
+ MaysyncFn_,
434
+ MaysyncGeneratorFn_,
425
435
  )
426
436
 
427
437
  from ..lite.reprs import ( # noqa
omlish/lang/asyncs.py ADDED
@@ -0,0 +1,80 @@
1
+ import typing as ta
2
+
3
+
4
+ T = ta.TypeVar('T')
5
+ P = ta.ParamSpec('P')
6
+
7
+
8
+ ##
9
+
10
+
11
+ async def async_list(
12
+ fn: ta.Callable[P, ta.AsyncIterator[T]],
13
+ *args: P.args,
14
+ **kwargs: P.kwargs,
15
+ ) -> list[T]:
16
+ """Simply eagerly reads the full contents of a function call returning an async iterator."""
17
+
18
+ return [v async for v in fn(*args, **kwargs)]
19
+
20
+
21
+ ##
22
+
23
+
24
+ def sync_await(
25
+ fn: ta.Callable[P, ta.Awaitable[T]],
26
+ *args: P.args,
27
+ **kwargs: P.kwargs,
28
+ ) -> T:
29
+ """
30
+ Allows for the synchronous execution of async functions which will never actually *externally* await anything. These
31
+ functions are allowed to await any number of other functions - including contextmanagers and generators - so long as
32
+ nothing ever actually 'leaks' out of the function, presumably to an event loop.
33
+ """
34
+
35
+ ret = missing = object()
36
+
37
+ async def gate():
38
+ nonlocal ret
39
+
40
+ ret = await fn(*args, **kwargs)
41
+
42
+ cr = gate()
43
+ try:
44
+ try:
45
+ cr.send(None)
46
+ except StopIteration:
47
+ pass
48
+
49
+ if ret is missing or cr.cr_await is not None or cr.cr_running:
50
+ raise TypeError('Not terminated')
51
+
52
+ finally:
53
+ cr.close()
54
+
55
+ return ta.cast(T, ret)
56
+
57
+
58
+ def sync_async_list(
59
+ fn: ta.Callable[P, ta.AsyncIterator[T]],
60
+ *args: P.args,
61
+ **kwargs: P.kwargs,
62
+ ) -> list[T]:
63
+ """
64
+ Uses `sync_await` to synchronously read the full contents of a function call returning an async iterator, given that
65
+ the function never externally awaits anything.
66
+ """
67
+
68
+ lst: list[T] | None = None
69
+
70
+ async def inner():
71
+ nonlocal lst
72
+
73
+ lst = [v async for v in fn(*args, **kwargs)]
74
+
75
+ sync_await(inner)
76
+
77
+ if not isinstance(lst, list):
78
+ raise TypeError(lst)
79
+
80
+ return lst
omlish/lang/maysyncs.py CHANGED
@@ -1,15 +1,22 @@
1
1
  import typing as ta
2
2
 
3
+ from ..lite.maysyncs import MaysyncGenerator
3
4
  from ..lite.maysyncs import Maywaitable
4
5
  from ..lite.maysyncs import make_maysync as _make_maysync
5
6
  from ..lite.maysyncs import maysync as _maysync
7
+ from ..lite.maysyncs import maysync_fn as _maysync_fn
8
+ from ..lite.maysyncs import maysync_generator_fn as _maysync_generator_fn
6
9
  from .functions import as_async
7
10
 
8
11
 
9
12
  T = ta.TypeVar('T')
13
+ O = ta.TypeVar('O')
14
+ I = ta.TypeVar('I')
15
+
10
16
  P = ta.ParamSpec('P')
11
17
 
12
- MaysyncP: ta.TypeAlias = ta.Callable[P, Maywaitable[T]]
18
+ MaysyncFn: ta.TypeAlias = ta.Callable[P, Maywaitable[T]]
19
+ MaysyncGeneratorFn: ta.TypeAlias = ta.Callable[P, MaysyncGenerator[O, I]]
13
20
 
14
21
 
15
22
  ##
@@ -18,8 +25,8 @@ MaysyncP: ta.TypeAlias = ta.Callable[P, Maywaitable[T]]
18
25
  def make_maysync(
19
26
  s: ta.Callable[P, T],
20
27
  a: ta.Callable[P, ta.Awaitable[T]],
21
- ) -> MaysyncP[P, T]:
22
- return ta.cast('MaysyncP[P, T]', _make_maysync(
28
+ ) -> MaysyncFn[P, T]:
29
+ return ta.cast('MaysyncFn[P, T]', _make_maysync(
23
30
  s,
24
31
  a,
25
32
  ))
@@ -28,8 +35,8 @@ def make_maysync(
28
35
  def make_maysync_from_sync(
29
36
  s: ta.Callable[P, T],
30
37
  a: ta.Callable[P, ta.Awaitable[T]] | None = None,
31
- ) -> MaysyncP[P, T]:
32
- return ta.cast('MaysyncP[P, T]', _make_maysync(
38
+ ) -> MaysyncFn[P, T]:
39
+ return ta.cast('MaysyncFn[P, T]', _make_maysync(
33
40
  s,
34
41
  a if a is not None else as_async(s),
35
42
  ))
@@ -38,7 +45,31 @@ def make_maysync_from_sync(
38
45
  ##
39
46
 
40
47
 
41
- def maysync(m: ta.Callable[P, ta.Awaitable[T]]) -> MaysyncP[P, T]:
42
- return ta.cast('MaysyncP[P, T]', _maysync(
43
- m,
44
- ))
48
+ def maysync_fn(
49
+ m: ta.Callable[P, ta.Awaitable[T]],
50
+ ) -> MaysyncFn[P, T]:
51
+ return ta.cast('MaysyncFn[P, T]', _maysync_fn(m))
52
+
53
+
54
+ def maysync_generator_fn(
55
+ m: ta.Callable[P, ta.AsyncGenerator[O, I]],
56
+ ) -> MaysyncGeneratorFn[P, O, I]:
57
+ return ta.cast('MaysyncGeneratorFn[P, O, I]', _maysync_generator_fn(m))
58
+
59
+
60
+ @ta.overload
61
+ def maysync(
62
+ m: ta.Callable[P, ta.Awaitable[T]],
63
+ ) -> MaysyncFn[P, T]:
64
+ ...
65
+
66
+
67
+ @ta.overload
68
+ def maysync(
69
+ m: ta.Callable[P, ta.AsyncGenerator[O, I]],
70
+ ) -> MaysyncGeneratorFn[P, O, I]:
71
+ ...
72
+
73
+
74
+ def maysync(m):
75
+ return _maysync(m)
omlish/lite/maysyncs.py CHANGED
@@ -1,67 +1,230 @@
1
- # ruff: noqa: UP006 UP045
1
+ # ruff: noqa: UP006 UP007 UP043 UP045 UP046 UP047
2
2
  # @omlish-lite
3
3
  """
4
+ A system for writing a python function once which can then be effectively used in both sync and async contexts -
5
+ including IO, under any (or no) event loop.
6
+
7
+ Where an 'async fn' returns an 'awaitable', a 'maysync fn' returns a 'maywaitable', which is an object with two
8
+ nullary methods:
9
+
10
+ - `def s()` - to be called in sync contexts
11
+ - `async def a()` - to be called in async and maysync contexts
12
+
13
+ For example, a maysync function `m_inc_int(x: int) -> int` would be used as such:
14
+
15
+ - `assert m_inc_int(5).s() == 6` in sync contexts
16
+ - `assert await m_inc_int(5).a() == 6` in async and maysync contexts
17
+
18
+ Maysync fns may be constructed in two ways: either using `make_maysync`, providing an equivalent pair of sync and async
19
+ functions, or using the `@maysync` decorator to wrap a 'maysync flavored' async function. 'Maysync flavored' async
20
+ functions are ones which only call other maysync functions through their 'maysync context' - that is, they use the 'a'
21
+ methods on maywaitables - for example: `await m_foo().a()` - and the maysync machinery will ultimately call the
22
+ appropriate 'leaf' sync or async functions. Being regular python functions they are free to call whatever they like -
23
+ for example doing sync IO - but the point is to, ideally, route all IO through maysync functions such that the maysync
24
+ code is fully efficiently usable in any context.
25
+
26
+ Internally, it's not really correct to say that there is 'no event loop' in the maysync context - rather, each
27
+ 'entrypoint' call to a maysync fn runs within its own tiny event loop.
28
+
29
+ ===
30
+
4
31
  TODO:
5
32
  - __del__
33
+ - (test) maysync context managers
34
+ - CancelledError
35
+ - for debug, mask any running eventloop while running maysync code
36
+ - `[CO_ASYNC_GENERATOR] = {k for k, v in dis.COMPILER_FLAG_NAMES.items() if v == 'ASYNC_GENERATOR'}` ? inspect is big..
37
+ - works down to 3.8
38
+ - make_maysync_from_sync can run with asyncio.run_in_thread
39
+ - make_maysync_from_async can run with asyncio.run_soon
6
40
  """
7
41
  import abc
8
42
  import functools
43
+ import inspect
44
+ import sys
45
+ import threading
9
46
  import typing as ta
10
47
 
11
48
 
12
49
  T = ta.TypeVar('T')
13
50
  T_co = ta.TypeVar('T_co', covariant=True)
14
51
 
52
+ O = ta.TypeVar('O')
53
+ O_co = ta.TypeVar('O_co', covariant=True)
54
+
55
+ I = ta.TypeVar('I')
56
+ I_contra = ta.TypeVar('I_contra', contravariant=True)
57
+
15
58
  _MaysyncX = ta.TypeVar('_MaysyncX')
16
59
 
17
- _MaysyncGen = ta.Generator['_MaysyncOp', ta.Any, T] # ta.TypeAlias
60
+ _MaysyncRS = ta.TypeVar('_MaysyncRS')
61
+ _MaysyncRA = ta.TypeVar('_MaysyncRA')
18
62
 
19
63
 
20
64
  ##
21
65
 
22
66
 
23
67
  class Maywaitable(ta.Protocol[T_co]):
68
+ """
69
+ The maysync version of `Awaitable[T]`. Non-generator maysync functions return a `Maywaitable`, with the following
70
+ nullary methods:
71
+
72
+ - `def s()` - to be called in sync contexts
73
+ - `async def a()` - to be called in async and maysync contexts
74
+
75
+ Only the proper method should be called in each context.
76
+ """
77
+
24
78
  def s(self) -> T_co:
25
79
  ...
26
80
 
27
81
  def a(self) -> ta.Awaitable[T_co]:
28
82
  ...
29
83
 
30
- def m(self) -> ta.Awaitable[T_co]:
84
+
85
+ class MaysyncGenerator(ta.Protocol[O_co, I_contra]):
86
+ """
87
+ The maysync version of `AsyncGenerator[O, I]`. Generator maysync functions return a `MaysyncGenerator`, with the
88
+ following methods:
89
+
90
+ - `def s()` - to be called in sync contexts
91
+ - `async def a()` - to be called in async and maysync contexts
92
+
93
+ Only the proper method should be called in each context.
94
+ """
95
+
96
+ def s(self) -> ta.Generator[O_co, I_contra, None]:
97
+ ...
98
+
99
+ def a(self) -> ta.AsyncGenerator[O_co, I_contra]:
31
100
  ...
32
101
 
33
102
 
34
- Maysync = ta.Callable[..., Maywaitable[T]] # ta.TypeAlias # omlish-amalg-typing-no-move
103
+ # The maysync equivalent of an async function
104
+ MaysyncFn = ta.Callable[..., Maywaitable[T]] # ta.TypeAlias # omlish-amalg-typing-no-move
35
105
 
106
+ # The maysync equivalent of an async generator function
107
+ MaysyncGeneratorFn = ta.Callable[..., MaysyncGenerator[O, I]] # ta.TypeAlias # omlish-amalg-typing-no-move
36
108
 
37
- class Maysync_(abc.ABC, ta.Generic[T]): # noqa
38
- @ta.final
39
- def cast(self) -> Maysync[T]:
40
- return ta.cast('Maysync[T]', self)
109
+
110
+ class Maysync_(abc.ABC): # noqa
111
+ """
112
+ Abstract base class for maysync objects - either MaysyncFn's or MaysyncGeneratorFn's.
113
+
114
+ The concrete implementations are module-level implementation detail, and in general users should make a point to
115
+ only interact with the protocols defined above, but introspection can be necessary at times.
116
+ """
117
+
118
+ def __init_subclass__(cls, **kwargs):
119
+ if Maysync_ in cls.__bases__ and abc.ABC not in cls.__bases__:
120
+ raise TypeError(cls)
121
+
122
+ super().__init_subclass__(**kwargs)
41
123
 
42
124
  class FnPair(ta.NamedTuple):
43
125
  s: ta.Callable[..., ta.Any]
44
- a: ta.Callable[..., ta.Awaitable[ta.Any]]
126
+ a: ta.Callable[..., ta.Any]
45
127
 
46
128
  @abc.abstractmethod
47
129
  def fn_pair(self) -> ta.Optional[FnPair]:
130
+ """If this maysync object is backed by a (sync, async) pair of functions, returns the pair."""
131
+
132
+ raise NotImplementedError
133
+
134
+ @abc.abstractmethod
135
+ def cast(self):
136
+ pass
137
+
138
+ @abc.abstractmethod
139
+ def __call__(self, *args, **kwargs):
48
140
  raise NotImplementedError
49
141
 
142
+
143
+ class MaysyncFn_(Maysync_, abc.ABC, ta.Generic[T]): # noqa
144
+ @ta.final
145
+ def cast(self) -> MaysyncFn[T]:
146
+ return ta.cast('MaysyncFn[T]', self)
147
+
148
+
149
+ class MaysyncGeneratorFn_(Maysync_, abc.ABC, ta.Generic[O, I]): # noqa
150
+ @ta.final
151
+ def cast(self) -> MaysyncGenerator[O, I]:
152
+ return ta.cast('MaysyncGenerator[O, I]', self)
153
+
154
+
155
+ ##
156
+
157
+
158
+ class _MaysyncThreadLocal(threading.local):
159
+ context: ta.Optional['_MaysyncContext'] = None
160
+
161
+
162
+ class _MaysyncContext(abc.ABC):
163
+ mode: ta.ClassVar[ta.Literal['s', 'a']]
164
+
165
+ @classmethod
166
+ def current(cls) -> ta.Optional['_MaysyncContext']:
167
+ return _MaysyncThreadLocal.context
168
+
50
169
  @abc.abstractmethod
51
- def __call__(self, *args, **kwargs): # -> Maywaitable[T]
170
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
52
171
  raise NotImplementedError
53
172
 
54
173
 
174
+ @ta.final
175
+ class _SyncMaysyncContext(_MaysyncContext):
176
+ mode: ta.ClassVar[ta.Literal['s']] = 's'
177
+
178
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
179
+ prev = _MaysyncThreadLocal.context
180
+ _MaysyncThreadLocal.context = self
181
+
182
+ ph: ta.Any = sys.get_asyncgen_hooks()
183
+ if ph.firstiter is not None or ph.finalizer is not None:
184
+ sys.set_asyncgen_hooks(firstiter=None, finalizer=None)
185
+ else:
186
+ ph = None
187
+
188
+ try:
189
+ return fn(*args, **kwargs)
190
+
191
+ finally:
192
+ if ph is not None:
193
+ sys.set_asyncgen_hooks(*ph)
194
+
195
+ _MaysyncThreadLocal.context = prev
196
+
197
+
198
+ @ta.final
199
+ class _AsyncMaysyncContext(_MaysyncContext):
200
+ mode: ta.ClassVar[ta.Literal['a']] = 'a'
201
+
202
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
203
+ prev = _MaysyncThreadLocal.context
204
+ _MaysyncThreadLocal.context = self
205
+
206
+ try:
207
+ return fn(*args, **kwargs)
208
+
209
+ finally:
210
+ _MaysyncThreadLocal.context = prev
211
+
212
+
55
213
  ##
56
214
 
57
215
 
58
- class _Maywaitable(abc.ABC, ta.Generic[_MaysyncX, T]):
216
+ class _MaywaitableLike(
217
+ abc.ABC,
218
+ ta.Generic[_MaysyncX],
219
+ ):
220
+ """Abstract base class for the maysync versions of `Awaitable[T]` and `AsyncGenerator[O, I]`."""
221
+
59
222
  @ta.final
60
223
  def __init__(
61
224
  self,
62
225
  x: _MaysyncX,
63
- *args: ta.Any,
64
- **kwargs: ta.Any,
226
+ args: ta.Tuple[ta.Any, ...],
227
+ kwargs: ta.Mapping[str, ta.Any],
65
228
  ) -> None:
66
229
  self._x = x
67
230
  self._args = args
@@ -70,24 +233,36 @@ class _Maywaitable(abc.ABC, ta.Generic[_MaysyncX, T]):
70
233
  def __repr__(self) -> str:
71
234
  return f'{self.__class__.__name__}@{id(self):x}({self._x!r})'
72
235
 
73
- @ta.final
74
- def m(self) -> ta.Awaitable[T]:
75
- return _MaysyncFuture(_MaysyncOp(
76
- ta.cast(ta.Any, self._x),
77
- self._args,
78
- self._kwargs,
79
- ))
236
+
237
+ class _Maywaitable(
238
+ _MaywaitableLike[_MaysyncX],
239
+ abc.ABC,
240
+ ta.Generic[_MaysyncX, T],
241
+ ):
242
+ pass
243
+
244
+
245
+ class _MaysyncGenerator(
246
+ _MaywaitableLike[_MaysyncX],
247
+ abc.ABC,
248
+ ta.Generic[_MaysyncX, O, I],
249
+ ):
250
+ pass
80
251
 
81
252
 
82
253
  ##
83
254
 
84
255
 
85
- @ta.final
86
- class _FnMaysync(Maysync_, ta.Generic[T]):
256
+ class _FpMaysyncFnLike(
257
+ abc.ABC,
258
+ ta.Generic[_MaysyncRS, _MaysyncRA],
259
+ ):
260
+ """A maysync object backed by an underlying (sync, async) function pair."""
261
+
87
262
  def __init__(
88
263
  self,
89
- s: ta.Callable[..., T],
90
- a: ta.Callable[..., ta.Awaitable[T]],
264
+ s: ta.Callable[..., _MaysyncRS],
265
+ a: ta.Callable[..., _MaysyncRA],
91
266
  ) -> None:
92
267
  if s is None:
93
268
  raise TypeError(s)
@@ -106,39 +281,145 @@ class _FnMaysync(Maysync_, ta.Generic[T]):
106
281
  )
107
282
 
108
283
  def __get__(self, instance, owner=None):
109
- return _FnMaysync(
284
+ return self.__class__(
110
285
  self._s.__get__(instance, owner), # noqa
111
286
  self._a.__get__(instance, owner), # noqa
112
287
  )
113
288
 
289
+
290
+ @ta.final
291
+ class _FpMaysyncFn(
292
+ _FpMaysyncFnLike[T, ta.Awaitable[T]],
293
+ MaysyncFn_[T],
294
+ ta.Generic[T],
295
+ ):
114
296
  def __call__(self, *args, **kwargs):
115
- return _FnMaywaitable(self, *args, **kwargs)
297
+ return _FpMaywaitable(self, args, kwargs)
116
298
 
117
299
 
118
300
  @ta.final
119
- class _FnMaywaitable(_Maywaitable[_FnMaysync[T], T]):
301
+ class _FpMaywaitable(
302
+ _Maywaitable[_FpMaysyncFn[T], T],
303
+ ):
120
304
  def s(self) -> T:
121
305
  return self._x._s(*self._args, **self._kwargs) # noqa
122
306
 
123
- async def a(self) -> T:
124
- return await self._x._a(*self._args, **self._kwargs) # noqa
307
+ def a(self) -> ta.Awaitable[T]:
308
+ if _MaysyncContext.current() is not None:
309
+ return _MaysyncFuture(functools.partial(self._x, *self._args, **self._kwargs)) # noqa
310
+
311
+ return self._x._a(*self._args, **self._kwargs) # noqa
312
+
313
+
314
+ def make_maysync_fn(
315
+ s: ta.Callable[..., T],
316
+ a: ta.Callable[..., ta.Awaitable[T]],
317
+ ) -> MaysyncFn[T]:
318
+ """Constructs a MaysyncFn from a (sync, async) function pair."""
319
+
320
+ return _FpMaysyncFn(s, a)
321
+
322
+
323
+ @ta.final
324
+ class _FpMaysyncGeneratorFn(
325
+ _FpMaysyncFnLike[ta.Generator[O, I, None], ta.AsyncGenerator[O, I]],
326
+ MaysyncGeneratorFn_[O, I],
327
+ ta.Generic[O, I],
328
+ ):
329
+ def __call__(self, *args, **kwargs):
330
+ return _FpMaysyncGenerator(self, args, kwargs)
331
+
332
+
333
+ @ta.final
334
+ class _FpMaysyncGenerator(
335
+ _MaysyncGenerator[_FpMaysyncGeneratorFn[O, I], O, I],
336
+ ):
337
+ def s(self) -> ta.Generator[O, I, None]:
338
+ return self._x._s(*self._args, **self._kwargs) # noqa
339
+
340
+ def a(self) -> ta.AsyncGenerator[O, I]:
341
+ if (ctx := _MaysyncContext.current()) is not None and ctx.mode == 's':
342
+ async def inner():
343
+ g = self._x._s(*self._args, **self._kwargs) # noqa
344
+
345
+ i: ta.Any = None
346
+ e: ta.Any = None
125
347
 
348
+ while True:
349
+ try:
350
+ if e is not None:
351
+ o = g.throw(e)
352
+ else:
353
+ o = g.send(i)
354
+ except StopIteration as ex:
355
+ if ex.value is not None:
356
+ raise TypeError(ex) from None
357
+ return
358
+
359
+ i = None
360
+ e = None
126
361
 
362
+ try:
363
+ i = yield o
364
+ except StopIteration as ex: # noqa
365
+ raise NotImplementedError # noqa
366
+ except StopAsyncIteration as ex: # noqa
367
+ raise NotImplementedError # noqa
368
+ except BaseException as ex: # noqa
369
+ e = ex
370
+
371
+ return inner()
372
+
373
+ return self._x._a(*self._args, **self._kwargs) # noqa
374
+
375
+
376
+ def make_maysync_generator_fn(
377
+ s: ta.Callable[..., ta.Generator[O, I, None]],
378
+ a: ta.Callable[..., ta.AsyncGenerator[O, I]],
379
+ ) -> MaysyncGeneratorFn[O, I]:
380
+ """Constructs a MaysyncGeneratorFn from a (sync, async) generator function pair."""
381
+
382
+ return _FpMaysyncGeneratorFn(s, a)
383
+
384
+
385
+ @ta.overload
127
386
  def make_maysync(
128
387
  s: ta.Callable[..., T],
129
388
  a: ta.Callable[..., ta.Awaitable[T]],
130
- ) -> Maysync[T]:
131
- return _FnMaysync(s, a)
389
+ ) -> MaysyncFn[T]:
390
+ ...
391
+
392
+
393
+ @ta.overload
394
+ def make_maysync(
395
+ s: ta.Callable[..., ta.Generator[O, I, None]],
396
+ a: ta.Callable[..., ta.AsyncGenerator[O, I]],
397
+ ) -> MaysyncGeneratorFn[O, I]:
398
+ ...
399
+
400
+
401
+ def make_maysync(s, a):
402
+ """
403
+ Constructs a MaysyncFn or MaysyncGeneratorFn from a (sync, async) function pair, using `inspect.isasyncgenfunction`
404
+ to determine the type.
405
+ """
406
+
407
+ if inspect.isasyncgenfunction(a):
408
+ return make_maysync_generator_fn(s, a)
409
+ else:
410
+ return make_maysync_fn(s, a)
132
411
 
133
412
 
134
413
  ##
135
414
 
136
415
 
137
- @ta.final
138
- class _MgMaysync(Maysync_, ta.Generic[T]):
416
+ class _MgMaysyncFnLike(
417
+ abc.ABC,
418
+ ta.Generic[T],
419
+ ):
139
420
  def __init__(
140
421
  self,
141
- mg: ta.Callable[..., _MaysyncGen[T]],
422
+ mg: ta.Callable[..., T],
142
423
  ) -> None:
143
424
  self._mg = mg
144
425
 
@@ -151,141 +432,269 @@ class _MgMaysync(Maysync_, ta.Generic[T]):
151
432
  return None
152
433
 
153
434
  def __get__(self, instance, owner=None):
154
- return _MgMaysync(
435
+ return self.__class__(
155
436
  self._mg.__get__(instance, owner), # noqa
156
437
  )
157
438
 
439
+ @abc.abstractmethod # noqa
158
440
  def __call__(self, *args, **kwargs):
159
- return _MgMaywaitable(self, *args, **kwargs)
441
+ raise NotImplementedError
160
442
 
161
443
 
162
444
  @ta.final
163
- class _MgMaywaitable(_Maywaitable[_MgMaysync[T], T]):
164
- def s(self) -> T:
165
- g = self._x._mg(*self._args, **self._kwargs) # noqa
166
-
167
- i: ta.Any = None
168
- e: ta.Any = None
445
+ class _MgMaysyncFn(
446
+ _MgMaysyncFnLike[ta.Awaitable[T]],
447
+ MaysyncFn_[T],
448
+ ta.Generic[T],
449
+ ):
450
+ def __call__(self, *args, **kwargs):
451
+ return _MgMaywaitable(self, args, kwargs)
169
452
 
170
- while True:
171
- try:
172
- if e is not None:
173
- o = g.throw(e)
174
- else:
175
- o = g.send(i)
176
- except StopIteration as ex:
177
- return ex.value
178
453
 
179
- i = None
180
- e = None
454
+ @ta.final
455
+ class _MgMaysyncDriver:
456
+ def __init__(self, ctx: _MaysyncContext, mg: ta.Any) -> None:
457
+ self.ctx = ctx
458
+ self.mg = mg
181
459
 
182
- if not isinstance(o, _MaysyncOp):
183
- raise TypeError(o)
460
+ value: ta.Any
184
461
 
462
+ def __iter__(self) -> ta.Generator['_MaysyncFuture', None, None]:
463
+ try:
464
+ a = self.mg.__await__()
185
465
  try:
186
- i = o.x(*o.args, **o.kwargs).s()
187
- except BaseException as ex: # noqa
188
- e = ex
466
+ g = iter(a)
467
+ try:
468
+ while True:
469
+ try:
470
+ f = self.ctx.run(g.send, None)
471
+ except StopIteration as ex:
472
+ self.value = ex.value
473
+ return
189
474
 
190
- del o
475
+ if not isinstance(f, _MaysyncFuture):
476
+ raise TypeError(f)
191
477
 
192
- async def a(self) -> T:
193
- g = self._x._mg(*self._args, **self._kwargs) # noqa
478
+ yield f
479
+ del f
194
480
 
195
- i: ta.Any = None
196
- e: ta.Any = None
481
+ finally:
482
+ if g is not a:
483
+ self.ctx.run(g.close)
197
484
 
198
- while True:
199
- try:
200
- if e is not None:
201
- o = g.throw(e)
202
- else:
203
- o = g.send(i)
204
- except StopIteration as ex:
205
- return ex.value
485
+ finally:
486
+ self.ctx.run(a.close)
206
487
 
207
- i = None
208
- e = None
488
+ finally:
489
+ self.ctx.run(self.mg.close)
209
490
 
210
- if not isinstance(o, _MaysyncOp):
211
- raise TypeError(o)
212
491
 
213
- try:
214
- i = await o.x(*o.args, **o.kwargs).a()
215
- except BaseException as ex: # noqa
216
- e = ex
492
+ @ta.final
493
+ class _MgMaywaitable(
494
+ _Maywaitable[_MgMaysyncFn[T], T],
495
+ ):
496
+ def _driver(self, ctx: _MaysyncContext) -> _MgMaysyncDriver:
497
+ return _MgMaysyncDriver(ctx, self._x._mg(*self._args, **self._kwargs)) # noqa
217
498
 
218
- del o
499
+ def s(self) -> T:
500
+ for f in (drv := self._driver(_SyncMaysyncContext())):
501
+ f.s()
502
+ del f
219
503
 
504
+ return drv.value
220
505
 
221
- @ta.final
222
- class _MgMaysyncFn:
223
- def __init__(self, m):
224
- self._m = m
506
+ def a(self) -> ta.Awaitable[T]:
507
+ if (ctx := _MaysyncContext.current()) is None or ctx.mode == 'a':
508
+ return self._x._mg(*self._args, **self._kwargs) # noqa
225
509
 
226
- functools.update_wrapper(self, m, updated=())
510
+ async def inner():
511
+ for f in (drv := self._driver(_AsyncMaysyncContext())):
512
+ await f.a()
513
+ del f
227
514
 
228
- def __repr__(self) -> str:
229
- return f'{self.__class__.__name__}@{id(self):x}({self._m!r})'
515
+ return drv.value
230
516
 
231
- def __get__(self, instance, owner=None):
232
- return _MgMaysyncFn(
233
- self._m.__get__(instance, owner),
234
- )
517
+ return inner()
235
518
 
519
+
520
+ @ta.final
521
+ class _MgMaysyncGeneratorFn(
522
+ _MgMaysyncFnLike[ta.AsyncGenerator[O, I]],
523
+ MaysyncGeneratorFn_[O, I],
524
+ ta.Generic[O, I],
525
+ ):
236
526
  def __call__(self, *args, **kwargs):
237
- a = self._m(*args, **kwargs).__await__()
527
+ return _MgMaysyncGenerator(self, args, kwargs)
528
+
529
+
530
+ @ta.final
531
+ class _MgMaysyncGeneratorDriver:
532
+ def __init__(self, ctx: _MaysyncContext, ag: ta.Any) -> None:
533
+ self.ctx = ctx
534
+ self.ag = ag
535
+
536
+ def __iter__(self) -> ta.Generator[
537
+ ta.Union[
538
+ ta.Tuple[ta.Literal['f'], '_MaysyncFuture'],
539
+ ta.Tuple[ta.Literal['o'], ta.Any],
540
+ ],
541
+ ta.Union[
542
+ ta.Tuple[ta.Any, BaseException],
543
+ None,
544
+ ],
545
+ None,
546
+ ]:
238
547
  try:
239
- g = iter(a)
548
+ ai = self.ag.__aiter__()
240
549
  try:
550
+ i: ta.Any = None
551
+ e: ta.Any = None
552
+
241
553
  while True:
242
- try:
243
- o = g.send(None)
244
- except StopIteration as e:
245
- return e.value
554
+ if e is not None:
555
+ coro = ai.athrow(e)
556
+ else:
557
+ coro = ai.asend(i)
246
558
 
247
- if not isinstance(o, _MaysyncFuture):
248
- raise TypeError(o)
559
+ i = None
560
+ e = None
249
561
 
250
- if not o.done:
251
- try:
252
- o.result = yield o.op
253
- except BaseException as e: # noqa
254
- o.error = e
255
- o.done = True
562
+ for f in (drv := _MgMaysyncDriver(self.ctx, coro)):
563
+ if (x := (yield ('f', f))) is not None:
564
+ raise RuntimeError(x)
565
+
566
+ del f
256
567
 
257
- del o
568
+ i, e = yield ('o', drv.value) # type: ignore[misc]
258
569
 
259
570
  finally:
260
- g.close()
571
+ if ai is not self.ag:
572
+ for f in _MgMaysyncDriver(self.ctx, ai.aclose()):
573
+ yield ('f', f)
261
574
 
262
575
  finally:
263
- a.close()
576
+ for f in _MgMaysyncDriver(self.ctx, self.ag.aclose()):
577
+ yield ('f', f)
264
578
 
265
579
 
266
- def maysync(m: ta.Callable[..., ta.Awaitable[T]]) -> Maysync[T]:
267
- return _MgMaysync(_MgMaysyncFn(m))
580
+ @ta.final
581
+ class _MgMaysyncGenerator(
582
+ _MaysyncGenerator[_MgMaysyncGeneratorFn[O, I], O, I],
583
+ ):
584
+ def _driver(self, ctx: _MaysyncContext) -> _MgMaysyncGeneratorDriver:
585
+ return _MgMaysyncGeneratorDriver(ctx, self._x._mg(*self._args, **self._kwargs)) # noqa
268
586
 
587
+ def s(self) -> ta.Generator[O, I, None]:
588
+ di = iter(self._driver(_SyncMaysyncContext()))
269
589
 
270
- ##
590
+ ie: ta.Any = None
271
591
 
592
+ while True:
593
+ try:
594
+ t, x = di.send(ie)
595
+ except StopAsyncIteration:
596
+ return
597
+ except StopIteration:
598
+ raise RuntimeError from None
272
599
 
273
- @ta.final
274
- class _MaysyncOp:
275
- def __init__(
276
- self,
277
- x: Maysync[T],
278
- args: ta.Tuple[ta.Any, ...],
279
- kwargs: ta.Mapping[str, ta.Any],
280
- ) -> None:
281
- self.x = x
282
- self.args = args
283
- self.kwargs = kwargs
600
+ ie = None
601
+
602
+ if t == 'f':
603
+ x.s()
604
+
605
+ elif t == 'o':
606
+ try:
607
+ ie = ((yield x), None) # type: ignore[misc]
608
+ except BaseException as ex: # noqa
609
+ ie = (None, ex)
610
+
611
+ else:
612
+ raise RuntimeError((t, x))
613
+
614
+ del x
615
+
616
+ def a(self) -> ta.AsyncGenerator[O, I]:
617
+ if _MaysyncContext.current() is not None:
618
+ return self._x._mg(*self._args, **self._kwargs) # noqa
619
+
620
+ async def inner():
621
+ di = iter(self._driver(_AsyncMaysyncContext()))
622
+
623
+ ie: ta.Any = None
624
+
625
+ while True:
626
+ try:
627
+ t, x = di.send(ie)
628
+ except StopAsyncIteration:
629
+ return
630
+ except StopIteration:
631
+ raise RuntimeError from None
632
+
633
+ ie = None
634
+
635
+ if t == 'f':
636
+ await x.a()
637
+
638
+ elif t == 'o':
639
+ try:
640
+ ie = ((yield x), None)
641
+ except BaseException as ex: # noqa
642
+ ie = (None, ex)
643
+
644
+ else:
645
+ raise RuntimeError((t, x))
646
+
647
+ del x
648
+
649
+ return inner()
284
650
 
285
- def __repr__(self) -> str:
286
- return f'{self.__class__.__name__}@{id(self):x}({self.x!r})'
287
651
 
652
+ def maysync_fn(
653
+ m: ta.Callable[..., ta.Awaitable[T]],
654
+ ) -> MaysyncFn[T]:
655
+ """Constructs a MaysyncFn from a 'maysync flavored' async function."""
288
656
 
657
+ return _MgMaysyncFn(m)
658
+
659
+
660
+ def maysync_generator_fn(
661
+ m: ta.Callable[..., ta.AsyncGenerator[O, I]],
662
+ ) -> MaysyncGeneratorFn[O, I]:
663
+ """Constructs a MaysyncGeneratorFn from a 'maysync flavored' async generator function."""
664
+
665
+ return _MgMaysyncGeneratorFn(m)
666
+
667
+
668
+ @ta.overload
669
+ def maysync(
670
+ m: ta.Callable[..., ta.Awaitable[T]],
671
+ ) -> MaysyncFn[T]:
672
+ ...
673
+
674
+
675
+ @ta.overload
676
+ def maysync(
677
+ m: ta.Callable[..., ta.AsyncGenerator[O, I]],
678
+ ) -> MaysyncGeneratorFn[O, I]:
679
+ ...
680
+
681
+
682
+ def maysync(m):
683
+ """
684
+ Constructs a MaysyncFn or MaysyncGeneratorFn from 'maysync flavored' async function or async generator function,
685
+ using `inspect.isasyncgenfunction` to determine the type. Usable as a decorator.
686
+ """
687
+
688
+ if inspect.isasyncgenfunction(m):
689
+ return maysync_generator_fn(m)
690
+ else:
691
+ return maysync_fn(m)
692
+
693
+
694
+ ##
695
+
696
+
697
+ @ta.final
289
698
  class _MaysyncFutureNotAwaitedError(RuntimeError):
290
699
  pass
291
700
 
@@ -294,12 +703,12 @@ class _MaysyncFutureNotAwaitedError(RuntimeError):
294
703
  class _MaysyncFuture(ta.Generic[T]):
295
704
  def __init__(
296
705
  self,
297
- op: _MaysyncOp,
706
+ x: ta.Callable[[], Maywaitable[T]],
298
707
  ) -> None:
299
- self.op = op
708
+ self._x = x
300
709
 
301
710
  def __repr__(self) -> str:
302
- return f'{self.__class__.__name__}@{id(self):x}({self.op!r}, done={self.done!r})'
711
+ return f'{self.__class__.__name__}@{id(self):x}({self._x!r}, done={self.done!r})'
303
712
 
304
713
  done: bool = False
305
714
  result: T
@@ -314,3 +723,23 @@ class _MaysyncFuture(ta.Generic[T]):
314
723
  raise self.error
315
724
  else:
316
725
  return self.result
726
+
727
+ def s(self) -> None:
728
+ if self.done:
729
+ return
730
+
731
+ try:
732
+ self.result = self._x().s()
733
+ except BaseException as ex: # noqa
734
+ self.error = ex
735
+ self.done = True
736
+
737
+ async def a(self) -> None:
738
+ if self.done:
739
+ return
740
+
741
+ try:
742
+ self.result = await self._x().a()
743
+ except BaseException as ex: # noqa
744
+ self.error = ex
745
+ self.done = True
@@ -68,7 +68,7 @@ class AbstractMaysyncSubprocesses(BaseSubprocesses, abc.ABC):
68
68
  *cmd: str,
69
69
  **kwargs: ta.Any,
70
70
  ) -> str:
71
- return (await self.check_output(*cmd, **kwargs).m()).decode().strip()
71
+ return (await self.check_output(*cmd, **kwargs).a()).decode().strip()
72
72
 
73
73
  #
74
74
 
@@ -78,7 +78,7 @@ class AbstractMaysyncSubprocesses(BaseSubprocesses, abc.ABC):
78
78
  *cmd: str,
79
79
  **kwargs: ta.Any,
80
80
  ) -> bool:
81
- if isinstance(await self.async_try_fn(self.check_call(*cmd, **kwargs).m), Exception):
81
+ if isinstance(await self.async_try_fn(self.check_call(*cmd, **kwargs).a), Exception):
82
82
  return False
83
83
  else:
84
84
  return True
@@ -89,7 +89,7 @@ class AbstractMaysyncSubprocesses(BaseSubprocesses, abc.ABC):
89
89
  *cmd: str,
90
90
  **kwargs: ta.Any,
91
91
  ) -> ta.Optional[bytes]:
92
- if isinstance(ret := await self.async_try_fn(self.check_output(*cmd, **kwargs).m), Exception):
92
+ if isinstance(ret := await self.async_try_fn(self.check_output(*cmd, **kwargs).a), Exception):
93
93
  return None
94
94
  else:
95
95
  return ret
@@ -100,7 +100,7 @@ class AbstractMaysyncSubprocesses(BaseSubprocesses, abc.ABC):
100
100
  *cmd: str,
101
101
  **kwargs: ta.Any,
102
102
  ) -> ta.Optional[str]:
103
- if (ret := await self.try_output(*cmd, **kwargs).m()) is None:
103
+ if (ret := await self.try_output(*cmd, **kwargs).a()) is None:
104
104
  return None
105
105
  else:
106
106
  return ret.decode().strip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev409
3
+ Version: 0.0.0.dev411
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=FLw7xkPiSXuImZgqSP8BwrEib2R1doSzUPLUkc-QUIA,8410
2
- omlish/__about__.py,sha256=4QkSZDk0KGwNDUXL28RW4dosDTkKwvPxAM7iBzddM4g,3601
2
+ omlish/__about__.py,sha256=qRafTkrQl5EPJTSgaE1ncsq5b84uBIoagA3OZKLm0p4,3601
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -21,11 +21,10 @@ omlish/argparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  omlish/argparse/all.py,sha256=NeeMM5MIebY7XDAHaCxUzeesEoUYwsf5i9PrBUcO1cI,1057
22
22
  omlish/argparse/cli.py,sha256=60cfq_WFLwL3YsIQxGAQ7XDi-LzNjH33RavcKdRnhUU,8737
23
23
  omlish/asyncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- omlish/asyncs/all.py,sha256=MjW0P9K8XBrw6AaH2X_qD5Y7hEZOF0cktqFv5BDGH-k,649
25
- omlish/asyncs/bridge.py,sha256=QonuN2KgT7sRsmJEgRvLqtubXma9RI2xOs27X5JSQcA,10153
24
+ omlish/asyncs/all.py,sha256=cS7dTWR8l3vF0oWYYiV9JXp0YGxOecVyCuJ2fydAPU0,526
25
+ omlish/asyncs/bridge.py,sha256=iRUUrvw5-5YxBxhsy57TvhpM_DmH_JH1QceexkGmiBw,10129
26
26
  omlish/asyncs/buffers.py,sha256=_Ds4Aa1bUWQwQTGmcYsKLjcJ_d5HgbSkPTFrG9y-eMQ,1424
27
27
  omlish/asyncs/flavors.py,sha256=1mNxGNRVmjUHzA13K5ht8vdJv4CLEmzYTQ6BZXr1520,4866
28
- omlish/asyncs/sync.py,sha256=hS6VltWMkGnMgDjygls50RD_z-FBdpmlr4q8W_i0fCI,2040
29
28
  omlish/asyncs/trio.py,sha256=fmZ5b_lKdVV8NQ3euCUutWgnkqTFzSnOjvJSA_jvmrE,367
30
29
  omlish/asyncs/trio_asyncio.py,sha256=b6H5H32pB79Uz5xvWEmuhXTJgTAeKFHBHzocv_Rpt5A,1332
31
30
  omlish/asyncs/utils.py,sha256=-oaa6nheDxMOxVDTfRylWZGlyOja3UVJEV2IibJXQv4,369
@@ -426,7 +425,8 @@ omlish/iterators/iterators.py,sha256=RxW35yQ5ed8vBQ22IqpDXFx-i5JiLQdp7-pkMZXhJJ8
426
425
  omlish/iterators/recipes.py,sha256=wOwOZg-zWG9Zc3wcAxJFSe2rtavVBYwZOfG09qYEx_4,472
427
426
  omlish/iterators/tools.py,sha256=M16LXrJhMdsz5ea2qH0vws30ZvhQuQSCVFSLpRf_gTg,2096
428
427
  omlish/iterators/unique.py,sha256=BSE-eanva8byFCJi09Nt2zzTsVr8LnTqY1PIInGYRs0,1396
429
- omlish/lang/__init__.py,sha256=7Xe1YcE2PPgx-NI-V9VTsvUl4qZD0RPANxuDv9ddRXg,7231
428
+ omlish/lang/__init__.py,sha256=AiFCnMJZo2lXrEOqgU29UAjk8Ovnuc_lwMYuLy6Y748,7393
429
+ omlish/lang/asyncs.py,sha256=HQrf8oaaZnUYbFwIiVLYvsEshJarMvRNHZ3VN-RyqLY,1789
430
430
  omlish/lang/attrs.py,sha256=zFiVuGVOq88x45464T_LxDa-ZEq_RD9zJLq2zeVEBDc,5105
431
431
  omlish/lang/casing.py,sha256=cFUlbDdXLhwnWwcYx4qnM5c4zGX7hIRUfcjiZbxUD28,4636
432
432
  omlish/lang/clsdct.py,sha256=HAGIvBSbCefzRjXriwYSBLO7QHKRv2UsE78jixOb-fA,1828
@@ -441,7 +441,7 @@ omlish/lang/functions.py,sha256=aLdxhmqG0Pj9tBgsKdoCu_q15r82WIkNqDDSPQU19L8,5689
441
441
  omlish/lang/generators.py,sha256=a4D5HU_mySs2T2z3xCmE_s3t4QJkj0YRrK4-hhpGd0A,5197
442
442
  omlish/lang/iterables.py,sha256=y1SX2Co3VsOeX2wlfFF7K3rwLvF7Dtre7VY6EpfwAwA,3338
443
443
  omlish/lang/lazyglobals.py,sha256=G1hwpyIgM4PUkVJ_St3K-EdQkHQdWpFOcXao6I5LwyY,1435
444
- omlish/lang/maysyncs.py,sha256=kULOW2M9PFhLd1TdtmGGQstPgrJpl--JUv9FOHWl4f8,894
444
+ omlish/lang/maysyncs.py,sha256=Fa6jk8v0JQzoGvg7UbpCQXzrvWUuRqBG9XZCikbYvoQ,1646
445
445
  omlish/lang/objects.py,sha256=eOhFyFiwvxqpbLs5QTEkXU3rdSt_tQXDgHoWF5SA28E,6119
446
446
  omlish/lang/outcomes.py,sha256=0PqxoKaGbBXU9UYZ6AE2QSq94Z-gFDt6wYdp0KomNQw,8712
447
447
  omlish/lang/overrides.py,sha256=IBzK6ljfLX6TLgIyKTSjhqTLcuKRkQNVtEOnBLS4nuA,2095
@@ -490,7 +490,7 @@ omlish/lite/json.py,sha256=m0Ce9eqUZG23-H7-oOp8n1sf4fzno5vtK4AK_4Vc-Mg,706
490
490
  omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
491
491
  omlish/lite/marshal.py,sha256=K_wnZwfC8cftGILyE3RlmzQEYuZOfzkMLKey41zuwtM,20296
492
492
  omlish/lite/maybes.py,sha256=0p_fzb6yiOjEpvMKaQ53Q6CH1VPW1or7v7Lt1JIKcgM,4359
493
- omlish/lite/maysyncs.py,sha256=MT3zF5kQ5rNlWU7db9Q_uXSM8K2C8LK85PIgdBsJyGE,7251
493
+ omlish/lite/maysyncs.py,sha256=QqXV0thRHwevPFlkyELv4tHmXBM9cJ6uMjx0pZ0LbHc,20046
494
494
  omlish/lite/pycharm.py,sha256=FRHGcCDo42UzZXqNwW_DkhI-6kb_CmJKPiQ8F6mYkLA,1174
495
495
  omlish/lite/reflect.py,sha256=gI-Qlws9V-jND7kvCQFaIhBFrndVpDsikTQ7C6U2z3w,2434
496
496
  omlish/lite/reprs.py,sha256=2Bc7ukhKvYNTKmxPIuv9glZIph13C37y_W4fg9pBnu8,2006
@@ -765,7 +765,7 @@ omlish/subprocesses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
765
765
  omlish/subprocesses/asyncs.py,sha256=G9wj275s3r0ueftHFl73Lt4kMRBc2hJOKcoJQCDlBms,2663
766
766
  omlish/subprocesses/base.py,sha256=r60N3ad4ooSvdgFmT94L_xZEy7FMbMX6JcG2VgpHo6w,6139
767
767
  omlish/subprocesses/editor.py,sha256=xBrd7gY0akhRfDIBK5YIBrYMHECtl_8r499iKViyfpQ,2634
768
- omlish/subprocesses/maysyncs.py,sha256=XWWVC-jyjVJLcT-nWMZFf4jUQesRuBWWVhmwcG_fUWE,3818
768
+ omlish/subprocesses/maysyncs.py,sha256=sHReoZhT7RRlON_FjcbbHYLvOqK7nyWfkGi6fI26SGY,3818
769
769
  omlish/subprocesses/run.py,sha256=8EeMm2FdNEFmEmbhhzJyHXASUhCCMMRN_-8ybqFhgLI,4378
770
770
  omlish/subprocesses/sync.py,sha256=L-ZNj9RrZd69XjlKrXjt-EJ-XUpQF8E35Mh3b3SI3vc,3671
771
771
  omlish/subprocesses/utils.py,sha256=v5uEzxmbmRvXwOl_0DtBa5Il6yITKYRgmVSGHcLsT4o,402
@@ -909,9 +909,9 @@ omlish/typedvalues/marshal.py,sha256=AtBz7Jq-BfW8vwM7HSxSpR85JAXmxK2T0xDblmm1HI0
909
909
  omlish/typedvalues/of_.py,sha256=UXkxSj504WI2UrFlqdZJbu2hyDwBhL7XVrc2qdR02GQ,1309
910
910
  omlish/typedvalues/reflect.py,sha256=PAvKW6T4cW7u--iX80w3HWwZUS3SmIZ2_lQjT65uAyk,1026
911
911
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
912
- omlish-0.0.0.dev409.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
913
- omlish-0.0.0.dev409.dist-info/METADATA,sha256=fCzoIaINPTeVtCOvdUmzuUWQqKIGW40xgEmeKO4bIxg,18881
914
- omlish-0.0.0.dev409.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
915
- omlish-0.0.0.dev409.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
916
- omlish-0.0.0.dev409.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
917
- omlish-0.0.0.dev409.dist-info/RECORD,,
912
+ omlish-0.0.0.dev411.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
913
+ omlish-0.0.0.dev411.dist-info/METADATA,sha256=3MPeR2SuWXjWtGnh7xZs-LvdohVQarQEXwYO7Oh01ao,18881
914
+ omlish-0.0.0.dev411.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
915
+ omlish-0.0.0.dev411.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
916
+ omlish-0.0.0.dev411.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
917
+ omlish-0.0.0.dev411.dist-info/RECORD,,
omlish/asyncs/sync.py DELETED
@@ -1,80 +0,0 @@
1
- """
2
- TODO:
3
- - async<->sync greeenlet bridge
4
- In [5]: %timeit greenlet.greenlet(f).switch()
5
- 517 ns ± 13.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
6
- - injected io provider - sync vs greenlet aio trampolined
7
- - push/pull bridge?
8
- - move to lang
9
-
10
- https://github.com/sqlalchemy/sqlalchemy/blob/1e75c189da721395bc8c2d899c722a5b9a170404/lib/sqlalchemy/util/_concurrency_py3k.py#L83
11
- """
12
- import functools
13
- import typing as ta
14
-
15
-
16
- T = ta.TypeVar('T')
17
-
18
-
19
- ##
20
-
21
-
22
- def sync_await(fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
23
- ret = missing = object()
24
-
25
- async def gate():
26
- nonlocal ret
27
- ret = await fn(*args, **kwargs) # type: ignore
28
-
29
- cr = gate()
30
- try:
31
- try:
32
- cr.send(None)
33
- except StopIteration:
34
- pass
35
- if ret is missing or cr.cr_await is not None or cr.cr_running:
36
- raise TypeError('Not terminated')
37
- finally:
38
- cr.close()
39
-
40
- return ret # type: ignore
41
-
42
-
43
- def sync_list(fn: ta.Callable[..., ta.AsyncIterator[T]], *args, **kwargs) -> list[T]:
44
- lst = None
45
-
46
- async def inner():
47
- nonlocal lst
48
- lst = [v async for v in fn(*args, **kwargs)]
49
-
50
- sync_await(inner)
51
- if not isinstance(lst, list):
52
- raise TypeError(lst)
53
- return lst
54
-
55
-
56
- async def async_list(fn: ta.Callable[..., ta.AsyncIterator[T]], *args, **kwargs) -> list[T]:
57
- return [v async for v in fn(*args, **kwargs)]
58
-
59
-
60
- class SyncableIterable(ta.Generic[T]):
61
- def __init__(self, obj) -> None:
62
- super().__init__()
63
-
64
- self._obj = obj
65
-
66
- def __iter__(self) -> ta.Iterator[T]:
67
- async def inner():
68
- async for i in self._obj:
69
- yield i
70
- return iter(sync_list(inner))
71
-
72
- def __aiter__(self) -> ta.AsyncIterator[T]:
73
- return self._obj.__aiter__()
74
-
75
-
76
- def syncable_iterable(fn: ta.Callable[..., ta.AsyncIterator[T]]) -> ta.Callable[..., SyncableIterable[T]]:
77
- @functools.wraps(fn)
78
- def inner(*args, **kwargs):
79
- return SyncableIterable(fn(*args, **kwargs))
80
- return inner