Repository: maxfischer2781/asyncstdlib

Issue:
Release 3.10
The [Python 3.10 release schedule](https://www.python.org/dev/peps/pep-0619/) assumes feature freeze for beta 1 @ Monday, 2021-05-03.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) Report
> Merging [#60](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) (f2240f3) into [master](https://codecov.io/gh/maxfischer2781/asyncstdlib/commit/a2e486c33257d59b3187ce597311570c26e75f7f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) (a2e486c) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60/graphs/tree.svg?width=650&height=150&src=pr&token=frT6svSwUb&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)

```diff
@@           Coverage Diff           @@
##           master      #60   +/-   ##
=======================================
  Coverage   99.19%   99.19%           
=======================================
  Files          17       17           
  Lines        1608     1617    +9     
  Branches      175      176    +1     
=======================================
+ Hits         1595     1604    +9     
  Misses          9        9           
  Partials        4        4           
```


| [Impacted Files](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) | Coverage Δ | |
|---|---|---|
| [asyncstdlib/asynctools.py](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer#diff-YXN5bmNzdGRsaWIvYXN5bmN0b29scy5weQ==) | `100.00% <100.00%> (ø)` | |
| [unittests/test\_asynctools.py](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer#diff-dW5pdHRlc3RzL3Rlc3RfYXN5bmN0b29scy5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer). Last update [a2e486c...f2240f3](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/60?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer).


Patch:
diff --git a/asyncstdlib/__init__.py b/asyncstdlib/__init__.py
index f972870..79ac930 100644
--- a/asyncstdlib/__init__.py
+++ b/asyncstdlib/__init__.py
@@ -35,7 +35,7 @@ from .itertools import (
 )
 from .asynctools import borrow, scoped_iter, await_each, apply, sync
 
-__version__ = "3.9.2"
+__version__ = "3.10.0"
 
 __all__ = [
     "anext",
diff --git a/asyncstdlib/asynctools.py b/asyncstdlib/asynctools.py
index 87b3c02..ee9b98f 100644
--- a/asyncstdlib/asynctools.py
+++ b/asyncstdlib/asynctools.py
@@ -326,7 +326,17 @@ async def apply(
     )
 
 
+@overload
+def sync(function: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
+    ...
+
+
+@overload
 def sync(function: Callable[..., T]) -> Callable[..., Awaitable[T]]:
+    ...
+
+
+def sync(function: Callable) -> Callable[..., Awaitable[T]]:
     r"""
     Wraps a callable to ensure its result can be ``await``\ ed
 
@@ -359,12 +369,13 @@ def sync(function: Callable[..., T]) -> Callable[..., Awaitable[T]]:
         raise TypeError("function argument should be Callable")
 
     if iscoroutinefunction(function):
-        return function  # type: ignore
+        return function
 
     @wraps(function)
     async def async_wrapped(*args, **kwargs):
         result = function(*args, **kwargs)
-
+        if isinstance(result, Awaitable):
+            return await result
         return result
 
     return async_wrapped
diff --git a/docs/conf.py b/docs/conf.py
index 57fc7bf..19b4d73 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -49,7 +49,6 @@ extensions = [
     'sphinx.ext.imgmath',
     'sphinx.ext.viewcode',
     'sphinxcontrib_trio',
-    'sphinxcontrib.contentui',
 ]
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/docs/source/api/asynctools.rst b/docs/source/api/asynctools.rst
index 31f774d..bac8931 100644
--- a/docs/source/api/asynctools.rst
+++ b/docs/source/api/asynctools.rst
@@ -23,8 +23,7 @@ Iterator lifetime
 Async transforming
 ==================
 
-.. autofunction:: sync(function:(...) -> T) -> (...) -> await T
-    :async:
+.. autofunction:: sync(function: (...) -> (await) T) -> (...) -> await T
 
     .. versionadded:: 3.9.3
 
diff --git a/docs/source/api/itertools.rst b/docs/source/api/itertools.rst
index 0a140ea..1685cf4 100644
--- a/docs/source/api/itertools.rst
+++ b/docs/source/api/itertools.rst
@@ -61,7 +61,7 @@ Iterator splitting
     :for: :(async iter T, ...)
 
 .. autofunction:: pairwise(iterable: (async) iter T)
-    :for: :(T, T)
+    :async-for: :(T, T)
 
     .. versionadded:: 3.10.0
 
diff --git a/pyproject.toml b/pyproject.toml
index 143b30a..7d3af30 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,7 +29,7 @@ test = [
     "pytest-cov",
     "mypy; implementation_name=='cpython'",
 ]
-doc = ["sphinx", "sphinxcontrib-contentui", "sphinxcontrib-trio"]
+doc = ["sphinx", "sphinxcontrib-trio"]
 
 [tool.flit.metadata.urls]
 Documentation = "https://asyncstdlib.readthedocs.io/en/latest/"


Test patch:
diff --git a/unittests/test_asynctools.py b/unittests/test_asynctools.py
index 088588c..fde3297 100644
--- a/unittests/test_asynctools.py
+++ b/unittests/test_asynctools.py
@@ -221,3 +221,17 @@ async def test_sync():
     assert t1 == 110
     assert t2 == 120
     assert t3 == 125
+
+
+@sync
+async def test_sync_awaitable():
+    """Test any (…) -> await T is recognised"""
+
+    @a.sync
+    def nocoro_async(value):
+        async def coro():
+            return value
+
+        return coro()
+
+    assert await nocoro_async(5) == 5