Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move variable initialization in AsyncToSync from __init__ to __call__ #439

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions asgiref/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,30 +153,30 @@ def __init__(
self.__self__ = self.awaitable.__self__ # type: ignore[union-attr]
except AttributeError:
pass
if force_new_loop:
# They have asked that we always run in a new sub-loop.
self.main_event_loop = None
else:
try:
self.main_event_loop = asyncio.get_running_loop()
except RuntimeError:
# There's no event loop in this thread. Look for the threadlocal if
# we're inside SyncToAsync
main_event_loop_pid = getattr(
SyncToAsync.threadlocal, "main_event_loop_pid", None
)
# We make sure the parent loop is from the same process - if
# they've forked, this is not going to be valid any more (#194)
if main_event_loop_pid and main_event_loop_pid == os.getpid():
self.main_event_loop = getattr(
SyncToAsync.threadlocal, "main_event_loop", None
)
else:
self.main_event_loop = None
self.force_new_loop = force_new_loop
self.main_event_loop = None
try:
self.main_event_loop = asyncio.get_running_loop()
except RuntimeError:
# There's no event loop in this thread.
pass

def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R:
__traceback_hide__ = True # noqa: F841

if not self.force_new_loop and not self.main_event_loop:
# There's no event loop in this thread. Look for the threadlocal if
# we're inside SyncToAsync
main_event_loop_pid = getattr(
SyncToAsync.threadlocal, "main_event_loop_pid", None
)
# We make sure the parent loop is from the same process - if
# they've forked, this is not going to be valid any more (#194)
if main_event_loop_pid and main_event_loop_pid == os.getpid():
self.main_event_loop = getattr(
SyncToAsync.threadlocal, "main_event_loop", None
)

# You can't call AsyncToSync from a thread with a running event loop
try:
event_loop = asyncio.get_running_loop()
Expand Down
50 changes: 50 additions & 0 deletions tests/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,56 @@ def sync_function():
assert result["thread"] == threading.current_thread()


@pytest.mark.asyncio
async def test_async_to_sync_to_async_decorator():
"""
Test async_to_sync as a function decorator uses the outer thread
when used inside sync_to_async.
"""
result = {}

# Define async function
@async_to_sync
async def inner_async_function():
result["worked"] = True
result["thread"] = threading.current_thread()
return 42

# Define sync function
@sync_to_async
def sync_function():
return inner_async_function()

# Check it works right
number = await sync_function()
assert number == 42
assert result["worked"]
# Make sure that it didn't needlessly make a new async loop
assert result["thread"] == threading.current_thread()

@pytest.mark.asyncio
async def test_async_to_sync_to_thread_decorator():
"""
Test async_to_sync as a function decorator uses the outer thread
when used inside another sync thread.
"""
result = {}

# Define async function
@async_to_sync
async def inner_async_function():
result["worked"] = True
result["thread"] = threading.current_thread()
return 42

# Check it works right
number = await asyncio.to_thread(inner_async_function)
assert number == 42
assert result["worked"]
# Make sure that it didn't needlessly make a new async loop
assert result["thread"] == threading.current_thread()


def test_async_to_sync_fail_non_function():
"""
async_to_sync raises a TypeError when applied to a non-function.
Expand Down
Loading