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 +2 -2
- omlish/asyncs/all.py +0 -8
- omlish/asyncs/bridge.py +1 -2
- omlish/lang/__init__.py +13 -3
- omlish/lang/asyncs.py +80 -0
- omlish/lang/maysyncs.py +40 -9
- omlish/lite/maysyncs.py +555 -126
- omlish/subprocesses/maysyncs.py +4 -4
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/RECORD +14 -14
- omlish/asyncs/sync.py +0 -80
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev409.dist-info → omlish-0.0.0.dev411.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/asyncs/all.py
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
) ->
|
22
|
-
return ta.cast('
|
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
|
-
) ->
|
32
|
-
return ta.cast('
|
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
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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.
|
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
|
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
|
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
|
-
|
64
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
86
|
-
|
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[...,
|
90
|
-
a: ta.Callable[...,
|
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
|
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
|
297
|
+
return _FpMaywaitable(self, args, kwargs)
|
116
298
|
|
117
299
|
|
118
300
|
@ta.final
|
119
|
-
class
|
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
|
-
|
124
|
-
|
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
|
-
) ->
|
131
|
-
|
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
|
-
|
138
|
-
|
416
|
+
class _MgMaysyncFnLike(
|
417
|
+
abc.ABC,
|
418
|
+
ta.Generic[T],
|
419
|
+
):
|
139
420
|
def __init__(
|
140
421
|
self,
|
141
|
-
mg: ta.Callable[...,
|
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
|
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
|
-
|
441
|
+
raise NotImplementedError
|
160
442
|
|
161
443
|
|
162
444
|
@ta.final
|
163
|
-
class
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
180
|
-
|
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
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
475
|
+
if not isinstance(f, _MaysyncFuture):
|
476
|
+
raise TypeError(f)
|
191
477
|
|
192
|
-
|
193
|
-
|
478
|
+
yield f
|
479
|
+
del f
|
194
480
|
|
195
|
-
|
196
|
-
|
481
|
+
finally:
|
482
|
+
if g is not a:
|
483
|
+
self.ctx.run(g.close)
|
197
484
|
|
198
|
-
|
199
|
-
|
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
|
-
|
208
|
-
|
488
|
+
finally:
|
489
|
+
self.ctx.run(self.mg.close)
|
209
490
|
|
210
|
-
if not isinstance(o, _MaysyncOp):
|
211
|
-
raise TypeError(o)
|
212
491
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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
|
-
|
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
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
510
|
+
async def inner():
|
511
|
+
for f in (drv := self._driver(_AsyncMaysyncContext())):
|
512
|
+
await f.a()
|
513
|
+
del f
|
227
514
|
|
228
|
-
|
229
|
-
return f'{self.__class__.__name__}@{id(self):x}({self._m!r})'
|
515
|
+
return drv.value
|
230
516
|
|
231
|
-
|
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
|
-
|
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
|
-
|
548
|
+
ai = self.ag.__aiter__()
|
240
549
|
try:
|
550
|
+
i: ta.Any = None
|
551
|
+
e: ta.Any = None
|
552
|
+
|
241
553
|
while True:
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
554
|
+
if e is not None:
|
555
|
+
coro = ai.athrow(e)
|
556
|
+
else:
|
557
|
+
coro = ai.asend(i)
|
246
558
|
|
247
|
-
|
248
|
-
|
559
|
+
i = None
|
560
|
+
e = None
|
249
561
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
568
|
+
i, e = yield ('o', drv.value) # type: ignore[misc]
|
258
569
|
|
259
570
|
finally:
|
260
|
-
|
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
|
-
|
576
|
+
for f in _MgMaysyncDriver(self.ctx, self.ag.aclose()):
|
577
|
+
yield ('f', f)
|
264
578
|
|
265
579
|
|
266
|
-
|
267
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
706
|
+
x: ta.Callable[[], Maywaitable[T]],
|
298
707
|
) -> None:
|
299
|
-
self.
|
708
|
+
self._x = x
|
300
709
|
|
301
710
|
def __repr__(self) -> str:
|
302
|
-
return f'{self.__class__.__name__}@{id(self):x}({self.
|
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
|
omlish/subprocesses/maysyncs.py
CHANGED
@@ -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).
|
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).
|
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).
|
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).
|
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,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=FLw7xkPiSXuImZgqSP8BwrEib2R1doSzUPLUkc-QUIA,8410
|
2
|
-
omlish/__about__.py,sha256=
|
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=
|
25
|
-
omlish/asyncs/bridge.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
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.
|
913
|
-
omlish-0.0.0.
|
914
|
-
omlish-0.0.0.
|
915
|
-
omlish-0.0.0.
|
916
|
-
omlish-0.0.0.
|
917
|
-
omlish-0.0.0.
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|