Skip to content

Commit 0c5f0b9

Browse files
Add asynctools.sync (#46)
* added asynctools.sync * added unittest for argument TypeError case in asynctools.make_awaitable
1 parent b48fb63 commit 0c5f0b9

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

asyncstdlib/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
zip_longest,
3434
groupby,
3535
)
36-
from .asynctools import borrow, scoped_iter, await_each, apply
36+
from .asynctools import borrow, scoped_iter, await_each, apply, sync
3737

3838
__version__ = "3.9.2"
3939

@@ -82,4 +82,5 @@
8282
"scoped_iter",
8383
"await_each",
8484
"apply",
85+
"sync",
8586
]

asyncstdlib/asynctools.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from asyncio import iscoroutinefunction
2+
from functools import wraps
13
from typing import (
24
Union,
35
AsyncIterator,
@@ -324,3 +326,46 @@ async def compute_something_else() -> float:
324326
return __func(
325327
*[await arg for arg in args], **{k: await arg for k, arg in kwargs.items()}
326328
)
329+
330+
331+
def sync(function: Callable[..., T]) -> Callable[..., Awaitable[T]]:
332+
"""
333+
Wraps any Callable, which allows to use it as Awaitable object
334+
335+
:param function - can be any Callable
336+
337+
:raise TypeError if function argument is not Callable
338+
339+
Example:
340+
341+
.. code-block:: python3
342+
343+
import asyncstdlib as a
344+
345+
def test1_sync(x, y):
346+
...
347+
348+
async def test1_async(x):
349+
...
350+
351+
async def main():
352+
await a.sync(test1_sync)(x=1, y=2)
353+
await a.sync(test1_async)(x=8)
354+
await a.sync(lambda x: x ** 3)(x=5)
355+
356+
if __name__ == "__main__":
357+
asyncio.run(main())
358+
"""
359+
if not isinstance(function, Callable):
360+
raise TypeError("function argument should be Callable")
361+
362+
if iscoroutinefunction(function):
363+
return function
364+
365+
@wraps(function)
366+
async def async_wrapped(*args, **kwargs):
367+
result = function(*args, **kwargs)
368+
369+
return result
370+
371+
return async_wrapped

docs/source/api/asynctools.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Iterator lifetime
2323
Async transforming
2424
==================
2525

26+
.. autofunction:: sync(function: Callable[..., T]) -> Callable[..., Awaitable[T]]
27+
2628
.. autofunction:: await_each(awaitables: iter await T)
2729
:async-for: :T
2830

unittests/test_asynctools.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,23 @@ async def compute_something_else() -> int:
201201
)
202202

203203
assert result == 42 - 1984
204+
205+
206+
@sync
207+
async def test_sync():
208+
def check_3(x: int) -> int:
209+
return x + 10
210+
211+
async def check_4(x: int, y: int, z: int) -> int:
212+
return x + y + z + 100
213+
214+
t1 = await a.sync(check_3)(x=100)
215+
t2 = await a.sync(check_4)(x=5, y=5, z=10)
216+
t3 = await a.sync(lambda x: x ** 3)(x=5)
217+
218+
with pytest.raises(TypeError):
219+
a.sync("string")(10)
220+
221+
assert t1 == 110
222+
assert t2 == 120
223+
assert t3 == 125

0 commit comments

Comments
 (0)