asynkit 0.19.1__tar.gz → 0.19.2__tar.gz
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.
- {asynkit-0.19.1/src/asynkit.egg-info → asynkit-0.19.2}/PKG-INFO +12 -1
- {asynkit-0.19.1 → asynkit-0.19.2}/README.md +11 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/pyproject.toml +2 -4
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/coroutine.py +57 -0
- {asynkit-0.19.1 → asynkit-0.19.2/src/asynkit.egg-info}/PKG-INFO +12 -1
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_coro.py +23 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_namespace.py +2 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/LICENSE +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/setup.cfg +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/setup.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/__init__.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/_cext/corostart.c +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/compat.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/experimental/__init__.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/experimental/anyio.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/experimental/interrupt.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/experimental/priority.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/__init__.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/default.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/eventloop.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/extensions.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/schedulingloop.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/loop/types.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/monitor.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/py.typed +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/scheduling.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit/tools.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit.egg-info/SOURCES.txt +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit.egg-info/dependency_links.txt +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit.egg-info/not-zip-safe +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit.egg-info/requires.txt +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/src/asynkit.egg-info/top_level.txt +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_anyio.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_compat_eager.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_df.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_eager_task_factory.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_eager_task_factory_context.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_implementation_params.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_loop.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_monitor.py +0 -0
- {asynkit-0.19.1 → asynkit-0.19.2}/tests/test_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asynkit
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.2
|
|
4
4
|
Summary: A toolkit for Python coroutines
|
|
5
5
|
Author-email: Kristján Valur Jónsson <sweskman@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -460,6 +460,17 @@ so that it is executed using `await_sync()`:
|
|
|
460
460
|
>>>
|
|
461
461
|
```
|
|
462
462
|
|
|
463
|
+
For methods, use `syncmethod()` when creating a class-body alias. It behaves
|
|
464
|
+
like `syncfunction()` at runtime, but exposes descriptor typing so type checkers
|
|
465
|
+
can distinguish bound and unbound method access:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
class Runner:
|
|
469
|
+
async def arun(self, *, yield_every: int | None = None) -> None: ...
|
|
470
|
+
|
|
471
|
+
run = asynkit.syncmethod(arun)
|
|
472
|
+
```
|
|
473
|
+
|
|
463
474
|
the `asyncfunction()` utility can be used when passing synchronous callbacks to async
|
|
464
475
|
code, to make them async. This, along with `syncfunction()` and `await_sync()`,
|
|
465
476
|
can be used to integrate synchronous code with async middleware:
|
|
@@ -432,6 +432,17 @@ so that it is executed using `await_sync()`:
|
|
|
432
432
|
>>>
|
|
433
433
|
```
|
|
434
434
|
|
|
435
|
+
For methods, use `syncmethod()` when creating a class-body alias. It behaves
|
|
436
|
+
like `syncfunction()` at runtime, but exposes descriptor typing so type checkers
|
|
437
|
+
can distinguish bound and unbound method access:
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
class Runner:
|
|
441
|
+
async def arun(self, *, yield_every: int | None = None) -> None: ...
|
|
442
|
+
|
|
443
|
+
run = asynkit.syncmethod(arun)
|
|
444
|
+
```
|
|
445
|
+
|
|
435
446
|
the `asyncfunction()` utility can be used when passing synchronous callbacks to async
|
|
436
447
|
code, to make them async. This, along with `syncfunction()` and `await_sync()`,
|
|
437
448
|
can be used to integrate synchronous code with async middleware:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "asynkit"
|
|
3
|
-
version = "0.19.
|
|
3
|
+
version = "0.19.2"
|
|
4
4
|
description = "A toolkit for Python coroutines"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Kristján Valur Jónsson", email = "sweskman@gmail.com"}
|
|
@@ -141,10 +141,8 @@ ignore = [
|
|
|
141
141
|
python-version = "3.10"
|
|
142
142
|
|
|
143
143
|
[tool.ty.src]
|
|
144
|
-
include = ["src/asynkit"]
|
|
144
|
+
include = ["src/asynkit", "tests/typing"]
|
|
145
145
|
exclude = [
|
|
146
|
-
"tests",
|
|
147
|
-
"examples",
|
|
148
146
|
"build",
|
|
149
147
|
"src/asynkit.egg-info",
|
|
150
148
|
]
|
|
@@ -22,6 +22,7 @@ from types import FrameType
|
|
|
22
22
|
from typing import (
|
|
23
23
|
TYPE_CHECKING,
|
|
24
24
|
Any,
|
|
25
|
+
Concatenate,
|
|
25
26
|
Generic,
|
|
26
27
|
TypeVar,
|
|
27
28
|
cast,
|
|
@@ -133,6 +134,8 @@ __all__ = [
|
|
|
133
134
|
"SynchronousAbort",
|
|
134
135
|
"asyncfunction",
|
|
135
136
|
"syncfunction",
|
|
137
|
+
"syncmethod",
|
|
138
|
+
"SyncMethod",
|
|
136
139
|
]
|
|
137
140
|
|
|
138
141
|
|
|
@@ -150,6 +153,7 @@ def get_current_context() -> Context:
|
|
|
150
153
|
|
|
151
154
|
T = TypeVar("T")
|
|
152
155
|
S = TypeVar("S")
|
|
156
|
+
SelfT = TypeVar("SelfT")
|
|
153
157
|
P = ParamSpec("P")
|
|
154
158
|
T_co = TypeVar("T_co", covariant=True)
|
|
155
159
|
Suspendable = (
|
|
@@ -1168,6 +1172,59 @@ def syncfunction(func: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, T]:
|
|
|
1168
1172
|
return helper
|
|
1169
1173
|
|
|
1170
1174
|
|
|
1175
|
+
class SyncMethod(Generic[SelfT, P, T]):
|
|
1176
|
+
"""Descriptor which makes an async method synchronous.
|
|
1177
|
+
|
|
1178
|
+
Unlike `syncfunction()`, this preserves method binding semantics when used
|
|
1179
|
+
for class-body aliases such as `run = syncmethod(arun)`.
|
|
1180
|
+
"""
|
|
1181
|
+
|
|
1182
|
+
def __init__(
|
|
1183
|
+
self,
|
|
1184
|
+
func: Callable[Concatenate[SelfT, P], Coroutine[Any, Any, T]],
|
|
1185
|
+
) -> None:
|
|
1186
|
+
self._func = func
|
|
1187
|
+
functools.update_wrapper(self, func)
|
|
1188
|
+
|
|
1189
|
+
@overload
|
|
1190
|
+
def __get__(
|
|
1191
|
+
self,
|
|
1192
|
+
instance: None,
|
|
1193
|
+
owner: type[SelfT],
|
|
1194
|
+
) -> Callable[Concatenate[SelfT, P], T]: ...
|
|
1195
|
+
|
|
1196
|
+
@overload
|
|
1197
|
+
def __get__(
|
|
1198
|
+
self,
|
|
1199
|
+
instance: SelfT,
|
|
1200
|
+
owner: type[SelfT] | None = None,
|
|
1201
|
+
) -> Callable[P, T]: ...
|
|
1202
|
+
|
|
1203
|
+
def __get__(
|
|
1204
|
+
self,
|
|
1205
|
+
instance: SelfT | None,
|
|
1206
|
+
owner: type[SelfT] | None = None,
|
|
1207
|
+
) -> Callable[..., T]:
|
|
1208
|
+
if instance is None:
|
|
1209
|
+
return self
|
|
1210
|
+
|
|
1211
|
+
@functools.wraps(self._func)
|
|
1212
|
+
def helper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
1213
|
+
return self(instance, *args, **kwargs)
|
|
1214
|
+
|
|
1215
|
+
return helper
|
|
1216
|
+
|
|
1217
|
+
def __call__(self, instance: SelfT, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
1218
|
+
return await_sync(self._func(instance, *args, **kwargs))
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def syncmethod(
|
|
1222
|
+
func: Callable[Concatenate[SelfT, P], Coroutine[Any, Any, T]],
|
|
1223
|
+
) -> SyncMethod[SelfT, P, T]:
|
|
1224
|
+
"""Make an async method synchronous while preserving descriptor typing."""
|
|
1225
|
+
return SyncMethod(func)
|
|
1226
|
+
|
|
1227
|
+
|
|
1171
1228
|
def asyncfunction(func: Callable[P, T]) -> Callable[P, Coroutine[Any, Any, T]]:
|
|
1172
1229
|
"""Make a regular function async, so that its result needs to be awaited.
|
|
1173
1230
|
Useful when providing synchronous callbacks to async code.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asynkit
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.2
|
|
4
4
|
Summary: A toolkit for Python coroutines
|
|
5
5
|
Author-email: Kristján Valur Jónsson <sweskman@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -460,6 +460,17 @@ so that it is executed using `await_sync()`:
|
|
|
460
460
|
>>>
|
|
461
461
|
```
|
|
462
462
|
|
|
463
|
+
For methods, use `syncmethod()` when creating a class-body alias. It behaves
|
|
464
|
+
like `syncfunction()` at runtime, but exposes descriptor typing so type checkers
|
|
465
|
+
can distinguish bound and unbound method access:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
class Runner:
|
|
469
|
+
async def arun(self, *, yield_every: int | None = None) -> None: ...
|
|
470
|
+
|
|
471
|
+
run = asynkit.syncmethod(arun)
|
|
472
|
+
```
|
|
473
|
+
|
|
463
474
|
the `asyncfunction()` utility can be used when passing synchronous callbacks to async
|
|
464
475
|
code, to make them async. This, along with `syncfunction()` and `await_sync()`,
|
|
465
476
|
can be used to integrate synchronous code with async middleware:
|
|
@@ -826,6 +826,9 @@ class TestCoroRun:
|
|
|
826
826
|
await self.sleep(0)
|
|
827
827
|
return "nullsleep"
|
|
828
828
|
|
|
829
|
+
async def blocks(self):
|
|
830
|
+
await drive_yield("blocked")
|
|
831
|
+
|
|
829
832
|
@asynkit.syncfunction
|
|
830
833
|
async def sync_simple(self):
|
|
831
834
|
return await self.simple()
|
|
@@ -838,6 +841,9 @@ class TestCoroRun:
|
|
|
838
841
|
async def sync_genexit(self):
|
|
839
842
|
return await self.genexit()
|
|
840
843
|
|
|
844
|
+
method_simple = asynkit.syncmethod(simple)
|
|
845
|
+
method_blocks = asynkit.syncmethod(blocks)
|
|
846
|
+
|
|
841
847
|
def test_simple(self):
|
|
842
848
|
assert asynkit.await_sync(self.simple()) == "simple"
|
|
843
849
|
|
|
@@ -852,6 +858,23 @@ class TestCoroRun:
|
|
|
852
858
|
def test_sync_simple(self):
|
|
853
859
|
assert self.sync_simple() == "simple"
|
|
854
860
|
|
|
861
|
+
def test_syncmethod_bound(self):
|
|
862
|
+
assert self.method_simple() == "simple"
|
|
863
|
+
|
|
864
|
+
def test_syncmethod_unbound(self):
|
|
865
|
+
assert type(self).method_simple(self) == "simple"
|
|
866
|
+
|
|
867
|
+
def test_syncmethod_descriptor(self):
|
|
868
|
+
descriptor = type(self).__dict__["method_simple"]
|
|
869
|
+
assert isinstance(descriptor, asynkit.SyncMethod)
|
|
870
|
+
assert type(self).method_simple.__name__ == "simple"
|
|
871
|
+
assert self.method_simple.__name__ == "simple"
|
|
872
|
+
|
|
873
|
+
def test_syncmethod_blocks(self):
|
|
874
|
+
with pytest.raises(asynkit.SynchronousError) as err:
|
|
875
|
+
self.method_blocks()
|
|
876
|
+
assert err.match("failed to complete synchronously")
|
|
877
|
+
|
|
855
878
|
def test_cleanup(self):
|
|
856
879
|
assert self.sync_cleanup() is None
|
|
857
880
|
|
|
@@ -20,6 +20,8 @@ def test_package_star_exports_public_api_only():
|
|
|
20
20
|
assert exports == set(asynkit.__all__)
|
|
21
21
|
assert "coro_start" in exports
|
|
22
22
|
assert "coro_drive" in exports
|
|
23
|
+
assert "syncmethod" in exports
|
|
24
|
+
assert "SyncMethod" in exports
|
|
23
25
|
assert "cancelling" in exports
|
|
24
26
|
assert "coroutine" not in exports
|
|
25
27
|
assert "loop" not in exports
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|