omlish 0.0.0.dev409__py3-none-any.whl → 0.0.0.dev410__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.dev410'
2
+ __revision__ = 'fa58128e15cab568117709c80765dc8d633cb4e0'
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,224 @@
1
- # ruff: noqa: UP006 UP045
1
+ # ruff: noqa: UP006 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
6
38
  """
7
39
  import abc
8
40
  import functools
41
+ import inspect
42
+ import sys
43
+ import threading
9
44
  import typing as ta
10
45
 
11
46
 
12
47
  T = ta.TypeVar('T')
13
48
  T_co = ta.TypeVar('T_co', covariant=True)
14
49
 
50
+ O = ta.TypeVar('O')
51
+ O_co = ta.TypeVar('O_co', covariant=True)
52
+
53
+ I = ta.TypeVar('I')
54
+ I_contra = ta.TypeVar('I_contra', contravariant=True)
55
+
15
56
  _MaysyncX = ta.TypeVar('_MaysyncX')
16
57
 
17
- _MaysyncGen = ta.Generator['_MaysyncOp', ta.Any, T] # ta.TypeAlias
58
+ _MaysyncRS = ta.TypeVar('_MaysyncRS')
59
+ _MaysyncRA = ta.TypeVar('_MaysyncRA')
18
60
 
19
61
 
20
62
  ##
21
63
 
22
64
 
23
65
  class Maywaitable(ta.Protocol[T_co]):
66
+ """
67
+ The maysync version of `Awaitable[T]`. Non-generator maysync functions return a `Maywaitable`, with the following
68
+ nullary methods:
69
+
70
+ - `def s()` - to be called in sync contexts
71
+ - `async def a()` - to be called in async and maysync contexts
72
+
73
+ Only the proper method should be called in each context.
74
+ """
75
+
24
76
  def s(self) -> T_co:
25
77
  ...
26
78
 
27
79
  def a(self) -> ta.Awaitable[T_co]:
28
80
  ...
29
81
 
30
- def m(self) -> ta.Awaitable[T_co]:
82
+
83
+ class MaysyncGenerator(ta.Protocol[O_co, I_contra]):
84
+ """
85
+ The maysync version of `AsyncGenerator[O, I]`. Generator maysync functions return a `MaysyncGenerator`, with the
86
+ following methods:
87
+
88
+ - `def s()` - to be called in sync contexts
89
+ - `async def a()` - to be called in async and maysync contexts
90
+
91
+ Only the proper method should be called in each context.
92
+ """
93
+
94
+ def s(self) -> ta.Generator[O_co, I_contra, None]:
31
95
  ...
32
96
 
97
+ def a(self) -> ta.AsyncGenerator[O_co, I_contra]:
98
+ ...
33
99
 
34
- Maysync = ta.Callable[..., Maywaitable[T]] # ta.TypeAlias # omlish-amalg-typing-no-move
35
100
 
101
+ # The maysync equivalent of an async function
102
+ MaysyncFn = ta.Callable[..., Maywaitable[T]] # ta.TypeAlias # omlish-amalg-typing-no-move
36
103
 
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)
104
+ # The maysync equivalent of an async generator function
105
+ MaysyncGeneratorFn = ta.Callable[..., MaysyncGenerator[O, I]] # ta.TypeAlias # omlish-amalg-typing-no-move
106
+
107
+
108
+ class Maysync_(abc.ABC): # noqa
109
+ """
110
+ Abstract base class for maysync objects - either MaysyncFn's or MaysyncGeneratorFn's.
111
+
112
+ The concrete implementations are module-level implementation detail, and in general users should make a point to
113
+ only interact with the protocols defined above, but introspection can be necessary at times.
114
+ """
115
+
116
+ def __init_subclass__(cls, **kwargs):
117
+ if Maysync_ in cls.__bases__ and abc.ABC not in cls.__bases__:
118
+ raise TypeError(cls)
119
+
120
+ super().__init_subclass__(**kwargs)
41
121
 
42
122
  class FnPair(ta.NamedTuple):
43
123
  s: ta.Callable[..., ta.Any]
44
- a: ta.Callable[..., ta.Awaitable[ta.Any]]
124
+ a: ta.Callable[..., ta.Any]
45
125
 
46
126
  @abc.abstractmethod
47
127
  def fn_pair(self) -> ta.Optional[FnPair]:
128
+ """If this maysync object is backed by a (sync, async) pair of functions, returns the pair."""
129
+
48
130
  raise NotImplementedError
49
131
 
50
132
  @abc.abstractmethod
51
- def __call__(self, *args, **kwargs): # -> Maywaitable[T]
133
+ def cast(self):
134
+ pass
135
+
136
+ @abc.abstractmethod
137
+ def __call__(self, *args, **kwargs):
52
138
  raise NotImplementedError
53
139
 
54
140
 
141
+ class MaysyncFn_(Maysync_, abc.ABC, ta.Generic[T]): # noqa
142
+ @ta.final
143
+ def cast(self) -> MaysyncFn[T]:
144
+ return ta.cast('MaysyncFn[T]', self)
145
+
146
+
147
+ class MaysyncGeneratorFn_(Maysync_, abc.ABC, ta.Generic[O, I]): # noqa
148
+ @ta.final
149
+ def cast(self) -> MaysyncGenerator[O, I]:
150
+ return ta.cast('MaysyncGenerator[O, I]', self)
151
+
152
+
153
+ ##
154
+
155
+
156
+ class _MaysyncThreadLocal(threading.local):
157
+ context: ta.Optional['_MaysyncContext'] = None
158
+
159
+
160
+ class _MaysyncContext(abc.ABC):
161
+ mode: ta.ClassVar[ta.Literal['s', 'a']]
162
+
163
+ @classmethod
164
+ def current(cls) -> ta.Optional['_MaysyncContext']:
165
+ return _MaysyncThreadLocal.context
166
+
167
+ @abc.abstractmethod
168
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
169
+ raise NotImplementedError
170
+
171
+
172
+ @ta.final
173
+ class _SyncMaysyncContext(_MaysyncContext):
174
+ mode: ta.ClassVar[ta.Literal['s']] = 's'
175
+
176
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
177
+ prev = _MaysyncThreadLocal.context
178
+ _MaysyncThreadLocal.context = self
179
+
180
+ ph = sys.get_asyncgen_hooks()
181
+ sys.set_asyncgen_hooks(firstiter=None, finalizer=None)
182
+
183
+ try:
184
+ return fn(*args, **kwargs)
185
+
186
+ finally:
187
+ sys.set_asyncgen_hooks(*ph)
188
+
189
+ _MaysyncThreadLocal.context = prev
190
+
191
+
192
+ @ta.final
193
+ class _AsyncMaysyncContext(_MaysyncContext):
194
+ mode: ta.ClassVar[ta.Literal['a']] = 'a'
195
+
196
+ def run(self, fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
197
+ prev = _MaysyncThreadLocal.context
198
+ _MaysyncThreadLocal.context = self
199
+
200
+ try:
201
+ return fn(*args, **kwargs)
202
+
203
+ finally:
204
+ _MaysyncThreadLocal.context = prev
205
+
206
+
55
207
  ##
56
208
 
57
209
 
58
- class _Maywaitable(abc.ABC, ta.Generic[_MaysyncX, T]):
210
+ class _MaywaitableLike(
211
+ abc.ABC,
212
+ ta.Generic[_MaysyncX],
213
+ ):
214
+ """Abstract base class for the maysync versions of `Awaitable[T]` and `AsyncGenerator[O, I]`."""
215
+
59
216
  @ta.final
60
217
  def __init__(
61
218
  self,
62
219
  x: _MaysyncX,
63
- *args: ta.Any,
64
- **kwargs: ta.Any,
220
+ args: ta.Tuple[ta.Any, ...],
221
+ kwargs: ta.Mapping[str, ta.Any],
65
222
  ) -> None:
66
223
  self._x = x
67
224
  self._args = args
@@ -70,24 +227,36 @@ class _Maywaitable(abc.ABC, ta.Generic[_MaysyncX, T]):
70
227
  def __repr__(self) -> str:
71
228
  return f'{self.__class__.__name__}@{id(self):x}({self._x!r})'
72
229
 
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
- ))
230
+
231
+ class _Maywaitable(
232
+ _MaywaitableLike[_MaysyncX],
233
+ abc.ABC,
234
+ ta.Generic[_MaysyncX, T],
235
+ ):
236
+ pass
237
+
238
+
239
+ class _MaysyncGenerator(
240
+ _MaywaitableLike[_MaysyncX],
241
+ abc.ABC,
242
+ ta.Generic[_MaysyncX, O, I],
243
+ ):
244
+ pass
80
245
 
81
246
 
82
247
  ##
83
248
 
84
249
 
85
- @ta.final
86
- class _FnMaysync(Maysync_, ta.Generic[T]):
250
+ class _FpMaysyncFnLike(
251
+ abc.ABC,
252
+ ta.Generic[_MaysyncRS, _MaysyncRA],
253
+ ):
254
+ """A maysync object backed by an underlying (sync, async) function pair."""
255
+
87
256
  def __init__(
88
257
  self,
89
- s: ta.Callable[..., T],
90
- a: ta.Callable[..., ta.Awaitable[T]],
258
+ s: ta.Callable[..., _MaysyncRS],
259
+ a: ta.Callable[..., _MaysyncRA],
91
260
  ) -> None:
92
261
  if s is None:
93
262
  raise TypeError(s)
@@ -106,39 +275,150 @@ class _FnMaysync(Maysync_, ta.Generic[T]):
106
275
  )
107
276
 
108
277
  def __get__(self, instance, owner=None):
109
- return _FnMaysync(
278
+ return self.__class__(
110
279
  self._s.__get__(instance, owner), # noqa
111
280
  self._a.__get__(instance, owner), # noqa
112
281
  )
113
282
 
283
+
284
+ @ta.final
285
+ class _FpMaysyncFn(
286
+ _FpMaysyncFnLike[T, ta.Awaitable[T]],
287
+ MaysyncFn_[T],
288
+ ta.Generic[T],
289
+ ):
114
290
  def __call__(self, *args, **kwargs):
115
- return _FnMaywaitable(self, *args, **kwargs)
291
+ return _FpMaywaitable(self, args, kwargs)
116
292
 
117
293
 
118
294
  @ta.final
119
- class _FnMaywaitable(_Maywaitable[_FnMaysync[T], T]):
295
+ class _FpMaywaitable(
296
+ _Maywaitable[_FpMaysyncFn[T], T],
297
+ ):
120
298
  def s(self) -> T:
121
299
  return self._x._s(*self._args, **self._kwargs) # noqa
122
300
 
123
- async def a(self) -> T:
124
- return await self._x._a(*self._args, **self._kwargs) # noqa
301
+ def a(self) -> ta.Awaitable[T]:
302
+ if _MaysyncContext.current() is not None:
303
+ return _MaysyncFuture(self._x, self._args, self._kwargs)
304
+
305
+ return self._x._a(*self._args, **self._kwargs) # noqa
306
+
307
+
308
+ def make_maysync_fn(
309
+ s: ta.Callable[..., T],
310
+ a: ta.Callable[..., ta.Awaitable[T]],
311
+ ) -> MaysyncFn[T]:
312
+ """Constructs a MaysyncFn from a (sync, async) function pair."""
313
+
314
+ return _FpMaysyncFn(s, a)
315
+
316
+
317
+ @ta.final
318
+ class _FpMaysyncGeneratorFn(
319
+ _FpMaysyncFnLike[ta.Generator[O, I, None], ta.AsyncGenerator[O, I]],
320
+ MaysyncGeneratorFn_[O, I],
321
+ ta.Generic[O, I],
322
+ ):
323
+ def __call__(self, *args, **kwargs):
324
+ return _FpMaysyncGenerator(self, args, kwargs)
325
+
326
+
327
+ @ta.final
328
+ class _FpMaysyncGenerator(
329
+ _MaysyncGenerator[_FpMaysyncGeneratorFn[O, I], O, I],
330
+ ):
331
+ def s(self) -> ta.Generator[O, I, None]:
332
+ return self._x._s(*self._args, **self._kwargs) # noqa
333
+
334
+ def a(self) -> ta.AsyncGenerator[O, I]:
335
+ if (ctx := _MaysyncContext.current()) is not None and ctx.mode == 's':
336
+ async def inner():
337
+ g = self._x._s(*self._args, **self._kwargs) # noqa
338
+
339
+ i: ta.Any = None
340
+ e: ta.Any = None
341
+
342
+ while True:
343
+ try:
344
+ if e is not None:
345
+ o = g.throw(e)
346
+ else:
347
+ o = g.send(i)
348
+ except StopIteration as ex:
349
+ if ex.value is not None:
350
+ raise TypeError(ex) from None
351
+ return
352
+
353
+ i = None
354
+ e = None
355
+
356
+ try:
357
+ i = yield o
358
+ except StopIteration as ex: # noqa
359
+ raise NotImplementedError # noqa
360
+ except StopAsyncIteration as ex: # noqa
361
+ raise NotImplementedError # noqa
362
+ except BaseException as ex: # noqa
363
+ e = ex
364
+
365
+ return inner()
366
+
367
+ return self._x._a(*self._args, **self._kwargs) # noqa
368
+
125
369
 
370
+ def make_maysync_generator_fn(
371
+ s: ta.Callable[..., ta.Generator[O, I, None]],
372
+ a: ta.Callable[..., ta.AsyncGenerator[O, I]],
373
+ ) -> MaysyncGeneratorFn[O, I]:
374
+ """Constructs a MaysyncGeneratorFn from a (sync, async) generator function pair."""
126
375
 
376
+ return _FpMaysyncGeneratorFn(s, a)
377
+
378
+
379
+ @ta.overload
127
380
  def make_maysync(
128
381
  s: ta.Callable[..., T],
129
382
  a: ta.Callable[..., ta.Awaitable[T]],
130
- ) -> Maysync[T]:
131
- return _FnMaysync(s, a)
383
+ ) -> MaysyncFn[T]:
384
+ ...
385
+
386
+
387
+ @ta.overload
388
+ def make_maysync(
389
+ s: ta.Callable[..., ta.Generator[O, I, None]],
390
+ a: ta.Callable[..., ta.AsyncGenerator[O, I]],
391
+ ) -> MaysyncGeneratorFn[O, I]:
392
+ ...
393
+
394
+
395
+ def make_maysync(s, a):
396
+ """
397
+ Constructs a MaysyncFn or MaysyncGeneratorFn from a (sync, async) function pair, using `inspect.isasyncgenfunction`
398
+ to determine the type.
399
+ """
400
+
401
+ if inspect.isasyncgenfunction(a):
402
+ return make_maysync_generator_fn(s, a)
403
+ else:
404
+ return make_maysync_fn(s, a)
132
405
 
133
406
 
134
407
  ##
135
408
 
136
409
 
137
- @ta.final
138
- class _MgMaysync(Maysync_, ta.Generic[T]):
410
+ class _MgMaysyncFnLike(
411
+ abc.ABC,
412
+ ta.Generic[T],
413
+ ):
414
+ """
415
+ A maysync object backed by an underlying generator yielding _MaysyncOp's. The _MgDriver and _MgGeneratorDriver
416
+ classes produce such generators.
417
+ """
418
+
139
419
  def __init__(
140
420
  self,
141
- mg: ta.Callable[..., _MaysyncGen[T]],
421
+ mg: ta.Callable[..., T],
142
422
  ) -> None:
143
423
  self._mg = mg
144
424
 
@@ -151,141 +431,261 @@ class _MgMaysync(Maysync_, ta.Generic[T]):
151
431
  return None
152
432
 
153
433
  def __get__(self, instance, owner=None):
154
- return _MgMaysync(
434
+ return self.__class__(
155
435
  self._mg.__get__(instance, owner), # noqa
156
436
  )
157
437
 
438
+ @abc.abstractmethod # noqa
158
439
  def __call__(self, *args, **kwargs):
159
- return _MgMaywaitable(self, *args, **kwargs)
440
+ raise NotImplementedError
160
441
 
161
442
 
162
443
  @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
444
+ class _MgMaysyncFn(
445
+ _MgMaysyncFnLike[ta.Awaitable[T]],
446
+ MaysyncFn_[T],
447
+ ta.Generic[T],
448
+ ):
449
+ def __call__(self, *args, **kwargs):
450
+ return _MgMaywaitable(self, args, kwargs)
169
451
 
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
452
 
179
- i = None
180
- e = None
453
+ @ta.final
454
+ class _MgMaysyncDriver:
455
+ def __init__(self, ctx, mg):
456
+ self.ctx = ctx
457
+ self.mg = mg
181
458
 
182
- if not isinstance(o, _MaysyncOp):
183
- raise TypeError(o)
459
+ value: ta.Any
184
460
 
461
+ def __iter__(self):
462
+ try:
463
+ a = self.mg.__await__()
185
464
  try:
186
- i = o.x(*o.args, **o.kwargs).s()
187
- except BaseException as ex: # noqa
188
- e = ex
465
+ g = iter(a)
466
+ try:
467
+ while True:
468
+ try:
469
+ f = self.ctx.run(g.send, None)
470
+ except StopIteration as ex:
471
+ self.value = ex.value
472
+ return
189
473
 
190
- del o
474
+ if not isinstance(f, _MaysyncFuture):
475
+ raise TypeError(f)
191
476
 
192
- async def a(self) -> T:
193
- g = self._x._mg(*self._args, **self._kwargs) # noqa
477
+ yield f
478
+ del f
194
479
 
195
- i: ta.Any = None
196
- e: ta.Any = None
480
+ finally:
481
+ self.ctx.run(g.close)
197
482
 
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
483
+ finally:
484
+ self.ctx.run(a.close)
485
+
486
+ finally:
487
+ self.ctx.run(self.mg.close)
206
488
 
207
- i = None
208
- e = None
209
489
 
210
- if not isinstance(o, _MaysyncOp):
211
- raise TypeError(o)
490
+ @ta.final
491
+ class _MgMaywaitable(
492
+ _Maywaitable[_MgMaysyncFn[T], T],
493
+ ):
494
+ def _driver(self, ctx: _MaysyncContext) -> _MgMaysyncDriver:
495
+ return _MgMaysyncDriver(ctx, self._x._mg(*self._args, **self._kwargs)) # noqa
212
496
 
213
- try:
214
- i = await o.x(*o.args, **o.kwargs).a()
215
- except BaseException as ex: # noqa
216
- e = ex
497
+ def s(self) -> T:
498
+ for f in (drv := self._driver(_SyncMaysyncContext())):
499
+ f.s()
500
+ del f
217
501
 
218
- del o
502
+ return drv.value
219
503
 
504
+ def a(self) -> ta.Awaitable[T]:
505
+ if (ctx := _MaysyncContext.current()) is None or ctx.mode == 'a':
506
+ return self._x._mg(*self._args, **self._kwargs) # noqa
220
507
 
221
- @ta.final
222
- class _MgMaysyncFn:
223
- def __init__(self, m):
224
- self._m = m
508
+ async def inner():
509
+ for f in (drv := self._driver(_AsyncMaysyncContext())):
510
+ await f.a()
511
+ del f
225
512
 
226
- functools.update_wrapper(self, m, updated=())
513
+ return drv.value
227
514
 
228
- def __repr__(self) -> str:
229
- return f'{self.__class__.__name__}@{id(self):x}({self._m!r})'
515
+ return inner()
230
516
 
231
- def __get__(self, instance, owner=None):
232
- return _MgMaysyncFn(
233
- self._m.__get__(instance, owner),
234
- )
235
517
 
518
+ @ta.final
519
+ class _MgMaysyncGeneratorFn(
520
+ _MgMaysyncFnLike[ta.AsyncGenerator[O, I]],
521
+ MaysyncGeneratorFn_[O, I],
522
+ ta.Generic[O, I],
523
+ ):
236
524
  def __call__(self, *args, **kwargs):
237
- a = self._m(*args, **kwargs).__await__()
525
+ return _MgMaysyncGenerator(self, args, kwargs)
526
+
527
+
528
+ @ta.final
529
+ class _MgMaysyncGeneratorDriver:
530
+ def __init__(self, ctx, ag):
531
+ self.ctx = ctx
532
+ self.ag = ag
533
+
534
+ def __iter__(self):
238
535
  try:
239
- g = iter(a)
536
+ ai = self.ag.__aiter__()
240
537
  try:
538
+ i: ta.Any = None
539
+ e: ta.Any = None
540
+
241
541
  while True:
242
- try:
243
- o = g.send(None)
244
- except StopIteration as e:
245
- return e.value
542
+ if e is not None:
543
+ coro = ai.athrow(e)
544
+ else:
545
+ coro = ai.asend(i)
246
546
 
247
- if not isinstance(o, _MaysyncFuture):
248
- raise TypeError(o)
547
+ i = None
548
+ e = None
249
549
 
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
550
+ for f in (drv := _MgMaysyncDriver(self.ctx, coro)):
551
+ if (x := (yield ('f', f))) is not None:
552
+ raise RuntimeError(x)
256
553
 
257
- del o
554
+ del f
555
+
556
+ i, e = yield ('o', drv.value)
258
557
 
259
558
  finally:
260
- g.close()
559
+ for f in _MgMaysyncDriver(self.ctx, ai.aclose()):
560
+ yield ('f', f)
261
561
 
262
562
  finally:
263
- a.close()
563
+ for f in _MgMaysyncDriver(self.ctx, self.ag.aclose()):
564
+ yield ('f', f)
264
565
 
265
566
 
266
- def maysync(m: ta.Callable[..., ta.Awaitable[T]]) -> Maysync[T]:
267
- return _MgMaysync(_MgMaysyncFn(m))
567
+ @ta.final
568
+ class _MgMaysyncGenerator(
569
+ _MaysyncGenerator[_MgMaysyncGeneratorFn[O, I], O, I],
570
+ ):
571
+ def _driver(self, ctx: _MaysyncContext) -> _MgMaysyncGeneratorDriver:
572
+ return _MgMaysyncGeneratorDriver(ctx, self._x._mg(*self._args, **self._kwargs)) # noqa
268
573
 
574
+ def s(self) -> ta.Generator[O, I, None]:
575
+ drv = self._driver(_SyncMaysyncContext())
576
+ di = iter(drv)
269
577
 
270
- ##
578
+ ie: ta.Any = None
271
579
 
580
+ while True:
581
+ try:
582
+ t, x = di.send(ie)
583
+ except StopAsyncIteration:
584
+ return
585
+ except StopIteration:
586
+ raise RuntimeError from None
272
587
 
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
588
+ ie = None
589
+
590
+ if t == 'f':
591
+ x.s()
592
+
593
+ elif t == 'o':
594
+ try:
595
+ y = yield x
596
+ ie = (y, None)
597
+ except BaseException as ex: # noqa
598
+ ie = (None, ex)
599
+
600
+ else:
601
+ raise RuntimeError((t, x))
602
+
603
+ del x
604
+
605
+ def a(self) -> ta.AsyncGenerator[O, I]:
606
+ if _MaysyncContext.current() is not None:
607
+ return self._x._mg(*self._args, **self._kwargs) # noqa
608
+
609
+ async def inner():
610
+ drv = self._driver(_AsyncMaysyncContext())
611
+ di = iter(drv)
612
+
613
+ ie: ta.Any = None
614
+
615
+ while True:
616
+ try:
617
+ t, x = di.send(ie)
618
+ except StopAsyncIteration:
619
+ return
620
+ except StopIteration:
621
+ raise RuntimeError from None
622
+
623
+ ie = None
624
+
625
+ if t == 'f':
626
+ await x.a()
627
+
628
+ elif t == 'o':
629
+ try:
630
+ y = yield x
631
+ ie = (y, None)
632
+ except BaseException as ex: # noqa
633
+ ie = (None, ex)
634
+
635
+ else:
636
+ raise RuntimeError((t, x))
637
+
638
+ del x
639
+
640
+ return inner()
641
+
642
+
643
+ def maysync_fn(
644
+ m: ta.Callable[..., ta.Awaitable[T]],
645
+ ) -> MaysyncFn[T]:
646
+ """Constructs a MaysyncFn from a 'maysync flavored' async function."""
647
+
648
+ return _MgMaysyncFn(m)
284
649
 
285
- def __repr__(self) -> str:
286
- return f'{self.__class__.__name__}@{id(self):x}({self.x!r})'
287
650
 
651
+ def maysync_generator_fn(
652
+ m: ta.Callable[..., ta.AsyncGenerator[O, I]],
653
+ ) -> MaysyncGeneratorFn[O, I]:
654
+ """Constructs a MaysyncGeneratorFn from a 'maysync flavored' async generator function."""
288
655
 
656
+ return _MgMaysyncGeneratorFn(m)
657
+
658
+
659
+ @ta.overload
660
+ def maysync(
661
+ m: ta.Callable[..., ta.Awaitable[T]],
662
+ ) -> MaysyncFn[T]:
663
+ ...
664
+
665
+
666
+ @ta.overload
667
+ def maysync(
668
+ m: ta.Callable[..., ta.AsyncGenerator[O, I]],
669
+ ) -> MaysyncGeneratorFn[O, I]:
670
+ ...
671
+
672
+
673
+ def maysync(m):
674
+ """
675
+ Constructs a MaysyncFn or MaysyncGeneratorFn from 'maysync flavored' async function or async generator function,
676
+ using `inspect.isasyncgenfunction` to determine the type. Usable as a decorator.
677
+ """
678
+
679
+ if inspect.isasyncgenfunction(m):
680
+ return maysync_generator_fn(m)
681
+ else:
682
+ return maysync_fn(m)
683
+
684
+
685
+ ##
686
+
687
+
688
+ @ta.final
289
689
  class _MaysyncFutureNotAwaitedError(RuntimeError):
290
690
  pass
291
691
 
@@ -294,12 +694,16 @@ class _MaysyncFutureNotAwaitedError(RuntimeError):
294
694
  class _MaysyncFuture(ta.Generic[T]):
295
695
  def __init__(
296
696
  self,
297
- op: _MaysyncOp,
697
+ x: ta.Any,
698
+ args: ta.Tuple[ta.Any, ...],
699
+ kwargs: ta.Mapping[str, ta.Any],
298
700
  ) -> None:
299
- self.op = op
701
+ self.x = x
702
+ self.args = args
703
+ self.kwargs = kwargs
300
704
 
301
705
  def __repr__(self) -> str:
302
- return f'{self.__class__.__name__}@{id(self):x}({self.op!r}, done={self.done!r})'
706
+ return f'{self.__class__.__name__}@{id(self):x}({self.x!r}, done={self.done!r})'
303
707
 
304
708
  done: bool = False
305
709
  result: T
@@ -314,3 +718,23 @@ class _MaysyncFuture(ta.Generic[T]):
314
718
  raise self.error
315
719
  else:
316
720
  return self.result
721
+
722
+ def s(self) -> None:
723
+ if self.done:
724
+ return
725
+
726
+ try:
727
+ self.result = self.x(*self.args, **self.kwargs).s()
728
+ except BaseException as ex: # noqa
729
+ self.error = ex
730
+ self.done = True
731
+
732
+ async def a(self) -> None:
733
+ if self.done:
734
+ return
735
+
736
+ try:
737
+ self.result = await self.x(*self.args, **self.kwargs).a()
738
+ except BaseException as ex: # noqa
739
+ self.error = ex
740
+ 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.dev410
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=4TpD9f2NLOxe8wPIx2jkab-TAgd8pW7229kNrBPdko4,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=XZ-zbK3bES8Al6--lYcvGGhYKqfVg1zoHJ6GCwLbiv4,19663
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.dev410.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
913
+ omlish-0.0.0.dev410.dist-info/METADATA,sha256=tqNKk_ovprTJtbpkIsi_jZRS44y4hTlBreu5pTxzTRs,18881
914
+ omlish-0.0.0.dev410.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
915
+ omlish-0.0.0.dev410.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
916
+ omlish-0.0.0.dev410.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
917
+ omlish-0.0.0.dev410.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