Skip to content

Commit 36f37c9

Browse files
authoredDec 14, 2022
Added coroutine detection shims for Python 3.12 (#360)
1 parent 467c154 commit 36f37c9

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed
 

‎asgiref/compatibility.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import asyncio
21
import inspect
32

3+
from .sync import iscoroutinefunction
4+
45

56
def is_double_callable(application):
67
"""
@@ -18,10 +19,10 @@ def is_double_callable(application):
1819
if hasattr(application, "__call__"):
1920
# We only check to see if its __call__ is a coroutine function -
2021
# if it's not, it still might be a coroutine function itself.
21-
if asyncio.iscoroutinefunction(application.__call__):
22+
if iscoroutinefunction(application.__call__):
2223
return False
2324
# Non-classes we just check directly
24-
return not asyncio.iscoroutinefunction(application)
25+
return not iscoroutinefunction(application)
2526

2627

2728
def double_to_single_callable(application):

‎asgiref/sync.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,41 @@ def _restore_context(context):
2626
cvar.set(context.get(cvar))
2727

2828

29+
# Python 3.12 deprecates asyncio.iscoroutinefunction() as an alias for
30+
# inspect.iscoroutinefunction(), whilst also removing the _is_coroutine marker.
31+
# The latter is replaced with the inspect.markcoroutinefunction decorator.
32+
# Until 3.12 is the minimum supported Python version, provide a shim.
33+
# Django 4.0 only supports 3.8+, so don't concern with the _or_partial backport.
34+
35+
# Type hint: should be generic: whatever T it takes it returns. (Same id)
36+
def markcoroutinefunction(func: Any) -> Any:
37+
if hasattr(inspect, "markcoroutinefunction"):
38+
return inspect.markcoroutinefunction(func)
39+
else:
40+
func._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore
41+
return func
42+
43+
44+
def iscoroutinefunction(func: Any) -> bool:
45+
if hasattr(inspect, "markcoroutinefunction"):
46+
return inspect.iscoroutinefunction(func)
47+
else:
48+
return asyncio.iscoroutinefunction(func)
49+
50+
2951
def _iscoroutinefunction_or_partial(func: Any) -> bool:
3052
# Python < 3.8 does not correctly determine partially wrapped
3153
# coroutine functions are coroutine functions, hence the need for
3254
# this to exist. Code taken from CPython.
3355
if sys.version_info >= (3, 8):
34-
return asyncio.iscoroutinefunction(func)
56+
return iscoroutinefunction(func)
3557
else:
3658
while inspect.ismethod(func):
3759
func = func.__func__
3860
while isinstance(func, functools.partial):
3961
func = func.func
4062

41-
return asyncio.iscoroutinefunction(func)
63+
return iscoroutinefunction(func)
4264

4365

4466
class ThreadSensitiveContext:
@@ -356,7 +378,7 @@ def __init__(
356378
self.func = func
357379
functools.update_wrapper(self, func)
358380
self._thread_sensitive = thread_sensitive
359-
self._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore
381+
markcoroutinefunction(self)
360382
if thread_sensitive and executor is not None:
361383
raise TypeError("executor must not be set when thread_sensitive is True")
362384
self._executor = executor

‎tests/test_sync.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010

1111
import pytest
1212

13-
from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async
13+
from asgiref.sync import (
14+
ThreadSensitiveContext,
15+
async_to_sync,
16+
iscoroutinefunction,
17+
sync_to_async,
18+
)
1419
from asgiref.timeout import timeout
1520

1621

@@ -645,8 +650,8 @@ def test_sync_to_async_detected_as_coroutinefunction():
645650
def sync_func():
646651
return
647652

648-
assert not asyncio.iscoroutinefunction(sync_to_async)
649-
assert asyncio.iscoroutinefunction(sync_to_async(sync_func))
653+
assert not iscoroutinefunction(sync_to_async)
654+
assert iscoroutinefunction(sync_to_async(sync_func))
650655

651656

652657
async def async_process(queue):

0 commit comments

Comments
 (0)
Please sign in to comment.