omlish 0.0.0.dev408__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 +2 -2
- omlish/asyncs/all.py +0 -8
- omlish/asyncs/bridge.py +1 -2
- omlish/lang/__init__.py +14 -4
- omlish/lang/asyncs.py +80 -0
- omlish/lang/imports/conditional.py +1 -1
- omlish/lang/maysyncs.py +40 -9
- omlish/lite/maysyncs.py +550 -126
- omlish/manifests/loading.py +19 -33
- omlish/subprocesses/maysyncs.py +4 -4
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/RECORD +17 -17
- omlish/asyncs/sync.py +0 -80
- /omlish/lang/imports/{resolution.py → resolving.py} +0 -0
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev408.dist-info → omlish-0.0.0.dev410.dist-info}/top_level.txt +0 -0
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
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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.
|
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
|
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
|
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
|
-
|
64
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
86
|
-
|
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[...,
|
90
|
-
a: ta.Callable[...,
|
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
|
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
|
291
|
+
return _FpMaywaitable(self, args, kwargs)
|
116
292
|
|
117
293
|
|
118
294
|
@ta.final
|
119
|
-
class
|
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
|
-
|
124
|
-
|
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
|
-
) ->
|
131
|
-
|
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
|
-
|
138
|
-
|
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[...,
|
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
|
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
|
-
|
440
|
+
raise NotImplementedError
|
160
441
|
|
161
442
|
|
162
443
|
@ta.final
|
163
|
-
class
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
180
|
-
|
453
|
+
@ta.final
|
454
|
+
class _MgMaysyncDriver:
|
455
|
+
def __init__(self, ctx, mg):
|
456
|
+
self.ctx = ctx
|
457
|
+
self.mg = mg
|
181
458
|
|
182
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
474
|
+
if not isinstance(f, _MaysyncFuture):
|
475
|
+
raise TypeError(f)
|
191
476
|
|
192
|
-
|
193
|
-
|
477
|
+
yield f
|
478
|
+
del f
|
194
479
|
|
195
|
-
|
196
|
-
|
480
|
+
finally:
|
481
|
+
self.ctx.run(g.close)
|
197
482
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
211
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
497
|
+
def s(self) -> T:
|
498
|
+
for f in (drv := self._driver(_SyncMaysyncContext())):
|
499
|
+
f.s()
|
500
|
+
del f
|
217
501
|
|
218
|
-
|
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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
508
|
+
async def inner():
|
509
|
+
for f in (drv := self._driver(_AsyncMaysyncContext())):
|
510
|
+
await f.a()
|
511
|
+
del f
|
225
512
|
|
226
|
-
|
513
|
+
return drv.value
|
227
514
|
|
228
|
-
|
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
|
-
|
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
|
-
|
536
|
+
ai = self.ag.__aiter__()
|
240
537
|
try:
|
538
|
+
i: ta.Any = None
|
539
|
+
e: ta.Any = None
|
540
|
+
|
241
541
|
while True:
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
542
|
+
if e is not None:
|
543
|
+
coro = ai.athrow(e)
|
544
|
+
else:
|
545
|
+
coro = ai.asend(i)
|
246
546
|
|
247
|
-
|
248
|
-
|
547
|
+
i = None
|
548
|
+
e = None
|
249
549
|
|
250
|
-
|
251
|
-
|
252
|
-
|
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
|
-
|
554
|
+
del f
|
555
|
+
|
556
|
+
i, e = yield ('o', drv.value)
|
258
557
|
|
259
558
|
finally:
|
260
|
-
|
559
|
+
for f in _MgMaysyncDriver(self.ctx, ai.aclose()):
|
560
|
+
yield ('f', f)
|
261
561
|
|
262
562
|
finally:
|
263
|
-
|
563
|
+
for f in _MgMaysyncDriver(self.ctx, self.ag.aclose()):
|
564
|
+
yield ('f', f)
|
264
565
|
|
265
566
|
|
266
|
-
|
267
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
697
|
+
x: ta.Any,
|
698
|
+
args: ta.Tuple[ta.Any, ...],
|
699
|
+
kwargs: ta.Mapping[str, ta.Any],
|
298
700
|
) -> None:
|
299
|
-
self.
|
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.
|
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
|