Skip to content

Commit b2ea8b8

Browse files
jalazizcarltongibson
authored and
Shi Feng
committed
Fix iscoroutine check for pubsub proxied methods (django#297)
* Fix iscoroutine check for pubsub proxied methods Adjust the proxying approach in `RedisPubSubChannelLayer` to ensure the proxied methods are properly marked as coroutines according to `inspect.iscoroutinefunction`. This can be issue during testing when trying to mock channel layer methods. The underlying issue is a [Python bug][1] where partial methods are not fully unwrapped to determine if they are coroutines. The workaround simply moves the proxy wrapper to a top-level async function, tricking `inspect.iscoroutinefunction` to correctly detecting the proxied async methods. [1]: https://bugs.python.org/issue38364 Co-authored-by: Carlton Gibson <[email protected]>
1 parent d1e3600 commit b2ea8b8

File tree

2 files changed

+18
-8
lines changed

2 files changed

+18
-8
lines changed

channels_redis/pubsub.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ def _wrapper(self, *args, **kwargs):
3434
loop.close = types.MethodType(_wrapper, loop)
3535

3636

37+
async def _async_proxy(obj, name, *args, **kwargs):
38+
# Must be defined as a function and not a method due to
39+
# https://bugs.python.org/issue38364
40+
layer = obj._get_layer()
41+
return await getattr(layer, name)(*args, **kwargs)
42+
43+
3744
class RedisPubSubChannelLayer:
3845
def __init__(self, *args, **kwargs) -> None:
3946
self._args = args
@@ -50,7 +57,7 @@ def __getattr__(self, name):
5057
"group_send",
5158
"flush",
5259
):
53-
return functools.partial(self._proxy, name)
60+
return functools.partial(_async_proxy, self, name)
5461
else:
5562
return getattr(self._get_layer(), name)
5663

@@ -82,13 +89,6 @@ def _get_layer(self):
8289

8390
return layer
8491

85-
def _proxy(self, name, *args, **kwargs):
86-
async def coro():
87-
layer = self._get_layer()
88-
return await getattr(layer, name)(*args, **kwargs)
89-
90-
return coro()
91-
9292

9393
class RedisPubSubLoopLayer:
9494
"""

tests/test_pubsub.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import asyncio
2+
import inspect
23
import random
4+
import sys
35

46
import async_timeout
57
import pytest
@@ -164,3 +166,11 @@ def test_multi_event_loop_garbage_collection(channel_layer):
164166
assert len(channel_layer._layers.values()) == 0
165167
async_to_sync(test_send_receive)(channel_layer)
166168
assert len(channel_layer._layers.values()) == 0
169+
170+
171+
@pytest.mark.asyncio
172+
async def test_proxied_methods_coroutine_check(channel_layer):
173+
# inspect.iscoroutinefunction does not work for partial functions
174+
# below Python 3.8.
175+
if sys.version_info >= (3, 8):
176+
assert inspect.iscoroutinefunction(channel_layer.send)

0 commit comments

Comments
 (0)