-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dev-python/asgiref: Bump to python 3.10 with a patch for warnings
Upstream PR: django/asgiref#262 Signed-off-by: Ekaterina Vaartis <vaartis@kotobank.ch> Signed-off-by: Michał Górny <mgorny@gentoo.org>
- Loading branch information
Showing
2 changed files
with
241 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
235 changes: 235 additions & 0 deletions
235
dev-python/asgiref/files/asgiref-3.3.4-py310-warnings.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
From 0c9e989f18b99ea24a1fb3ea2c8a66fd295c2178 Mon Sep 17 00:00:00 2001 | ||
From: Ekaterina Vaartis <vaartis@kotobank.ch> | ||
Date: Thu, 20 May 2021 19:44:15 +0300 | ||
Subject: [PATCH] Fix deprecation warnings for python 3.10 | ||
|
||
asyncio.get_event_loop was marked as deprecated, the documnetation | ||
now refers to asyncio.get_running_loop([1]) | ||
|
||
asyncio.ensure_future issues a deprecation warning if there is no | ||
running event loop([2]), so use asyncio.run which creates and destroys the | ||
loop itself | ||
|
||
asyncio.gather issues a warning if run outside of event | ||
loop (i.e. there is no running event loop)([3]), so wrap it into an | ||
async def | ||
|
||
explicit passing of coroutine objects to asyncio.wait is deprecated | ||
since 3.8([4]), so wrap them in asyncio.create_task | ||
|
||
plus, add 3.10 to tox.ini | ||
|
||
[1]: https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop | ||
[2]: https://docs.python.org/3.10/library/asyncio-future.html#asyncio.ensure_future | ||
[3]: https://docs.python.org/3.10/library/asyncio-task.html#asyncio.gather | ||
[4]: https://docs.python.org/3.10/library/asyncio-task.html#asyncio.wait | ||
--- | ||
asgiref/compatibility.py | 14 ++++++++++++++ | ||
asgiref/server.py | 8 ++++---- | ||
asgiref/sync.py | 15 ++++++++++----- | ||
tests/test_sync.py | 19 ++++++++++++++----- | ||
tests/test_sync_contextvars.py | 3 ++- | ||
tox.ini | 2 +- | ||
6 files changed, 45 insertions(+), 16 deletions(-) | ||
|
||
diff --git a/asgiref/compatibility.py b/asgiref/compatibility.py | ||
index eccaee0..614b2e6 100644 | ||
--- a/asgiref/compatibility.py | ||
+++ b/asgiref/compatibility.py | ||
@@ -1,5 +1,6 @@ | ||
import asyncio | ||
import inspect | ||
+import sys | ||
|
||
|
||
def is_double_callable(application): | ||
@@ -45,3 +46,16 @@ def guarantee_single_callable(application): | ||
if is_double_callable(application): | ||
application = double_to_single_callable(application) | ||
return application | ||
+ | ||
+ | ||
+if sys.version_info >= (3, 7): | ||
+ # these were introduced in 3.7 | ||
+ get_running_loop = asyncio.get_running_loop | ||
+ run_future = asyncio.run | ||
+ create_task = asyncio.create_task | ||
+else: | ||
+ # marked as deprecated in 3.10, did not exist before 3.7 | ||
+ get_running_loop = asyncio.get_event_loop | ||
+ run_future = asyncio.ensure_future | ||
+ # does nothing, this is fine for <3.7 | ||
+ create_task = lambda task: task | ||
diff --git a/asgiref/server.py b/asgiref/server.py | ||
index f975f78..fb1c394 100644 | ||
--- a/asgiref/server.py | ||
+++ b/asgiref/server.py | ||
@@ -3,7 +3,7 @@ import logging | ||
import time | ||
import traceback | ||
|
||
-from .compatibility import guarantee_single_callable | ||
+from .compatibility import get_running_loop, guarantee_single_callable, run_future | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
@@ -56,7 +56,7 @@ class StatelessServer: | ||
""" | ||
Runs the asyncio event loop with our handler loop. | ||
""" | ||
- event_loop = asyncio.get_event_loop() | ||
+ event_loop = get_running_loop() | ||
asyncio.ensure_future(self.application_checker()) | ||
try: | ||
event_loop.run_until_complete(self.handle()) | ||
@@ -88,12 +88,12 @@ class StatelessServer: | ||
input_queue = asyncio.Queue() | ||
application_instance = guarantee_single_callable(self.application) | ||
# Run it, and stash the future for later checking | ||
- future = asyncio.ensure_future( | ||
+ future = run_future( | ||
application_instance( | ||
scope=scope, | ||
receive=input_queue.get, | ||
send=lambda message: self.application_send(scope, message), | ||
- ) | ||
+ ), | ||
) | ||
self.application_instances[scope_id] = { | ||
"input_queue": input_queue, | ||
diff --git a/asgiref/sync.py b/asgiref/sync.py | ||
index 6b87c7e..9476e66 100644 | ||
--- a/asgiref/sync.py | ||
+++ b/asgiref/sync.py | ||
@@ -9,6 +9,7 @@ import weakref | ||
from concurrent.futures import Future, ThreadPoolExecutor | ||
from typing import Any, Callable, Dict, Optional, Union | ||
|
||
+from .compatibility import get_running_loop | ||
from .current_thread_executor import CurrentThreadExecutor | ||
from .local import Local | ||
|
||
@@ -132,7 +133,7 @@ class AsyncToSync: | ||
self.main_event_loop = None | ||
else: | ||
try: | ||
- self.main_event_loop = asyncio.get_event_loop() | ||
+ self.main_event_loop = get_running_loop() | ||
except RuntimeError: | ||
# There's no event loop in this thread. Look for the threadlocal if | ||
# we're inside SyncToAsync | ||
@@ -151,7 +152,7 @@ class AsyncToSync: | ||
def __call__(self, *args, **kwargs): | ||
# You can't call AsyncToSync from a thread with a running event loop | ||
try: | ||
- event_loop = asyncio.get_event_loop() | ||
+ event_loop = get_running_loop() | ||
except RuntimeError: | ||
pass | ||
else: | ||
@@ -238,7 +239,11 @@ class AsyncToSync: | ||
tasks = asyncio.Task.all_tasks(loop) | ||
for task in tasks: | ||
task.cancel() | ||
- loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True)) | ||
+ | ||
+ async def gather(): | ||
+ await asyncio.gather(*tasks, return_exceptions=True) | ||
+ | ||
+ loop.run_until_complete(gather()) | ||
for task in tasks: | ||
if task.cancelled(): | ||
continue | ||
@@ -320,7 +325,7 @@ class SyncToAsync: | ||
|
||
# If they've set ASGI_THREADS, update the default asyncio executor for now | ||
if "ASGI_THREADS" in os.environ: | ||
- loop = asyncio.get_event_loop() | ||
+ loop = get_running_loop() | ||
loop.set_default_executor( | ||
ThreadPoolExecutor(max_workers=int(os.environ["ASGI_THREADS"])) | ||
) | ||
@@ -370,7 +375,7 @@ class SyncToAsync: | ||
pass | ||
|
||
async def __call__(self, *args, **kwargs): | ||
- loop = asyncio.get_event_loop() | ||
+ loop = get_running_loop() | ||
|
||
# Work out what thread to run the code in | ||
if self._thread_sensitive: | ||
diff --git a/tests/test_sync.py b/tests/test_sync.py | ||
index cf0e0c5..8ed76a7 100644 | ||
--- a/tests/test_sync.py | ||
+++ b/tests/test_sync.py | ||
@@ -9,6 +9,7 @@ from unittest import TestCase | ||
|
||
import pytest | ||
|
||
+from asgiref.compatibility import create_task, get_running_loop | ||
from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async | ||
|
||
|
||
@@ -33,12 +34,17 @@ async def test_sync_to_async(): | ||
assert result == 42 | ||
assert end - start >= 1 | ||
# Set workers to 1, call it twice and make sure that works right | ||
- loop = asyncio.get_event_loop() | ||
- old_executor = loop._default_executor | ||
+ loop = get_running_loop() | ||
+ old_executor = loop._default_executor or ThreadPoolExecutor() | ||
loop.set_default_executor(ThreadPoolExecutor(max_workers=1)) | ||
try: | ||
start = time.monotonic() | ||
- await asyncio.wait([async_function(), async_function()]) | ||
+ await asyncio.wait( | ||
+ [ | ||
+ create_task(async_function()), | ||
+ create_task(async_function()), | ||
+ ] | ||
+ ) | ||
end = time.monotonic() | ||
# It should take at least 2 seconds as there's only one worker. | ||
assert end - start >= 2 | ||
@@ -428,7 +434,7 @@ async def test_thread_sensitive_outside_async(): | ||
result["thread"] = threading.current_thread() | ||
|
||
# Run it (in supposed parallel!) | ||
- await asyncio.wait([outer(result_1), inner(result_2)]) | ||
+ await asyncio.wait([create_task(outer(result_1)), create_task(inner(result_2))]) | ||
|
||
# They should not have run in the main thread, but in the same thread | ||
assert result_1["thread"] != threading.current_thread() | ||
@@ -449,7 +455,10 @@ async def test_thread_sensitive_with_context_matches(): | ||
async with ThreadSensitiveContext(): | ||
# Run it (in supposed parallel!) | ||
await asyncio.wait( | ||
- [store_thread_async(result_1), store_thread_async(result_2)] | ||
+ [ | ||
+ create_task(store_thread_async(result_1)), | ||
+ create_task(store_thread_async(result_2)), | ||
+ ] | ||
) | ||
|
||
await fn() | ||
diff --git a/tests/test_sync_contextvars.py b/tests/test_sync_contextvars.py | ||
index b1027aa..9665bf9 100644 | ||
--- a/tests/test_sync_contextvars.py | ||
+++ b/tests/test_sync_contextvars.py | ||
@@ -4,6 +4,7 @@ import time | ||
|
||
import pytest | ||
|
||
+from asgiref.compatibility import create_task | ||
from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async | ||
|
||
contextvars = pytest.importorskip("contextvars") | ||
@@ -25,7 +26,7 @@ async def test_thread_sensitive_with_context_different(): | ||
await store_thread(result) | ||
|
||
# Run it (in true parallel!) | ||
- await asyncio.wait([fn(result_1), fn(result_2)]) | ||
+ await asyncio.wait([create_task(fn(result_1)), create_task(fn(result_2))]) | ||
|
||
# They should not have run in the main thread, and on different threads | ||
assert result_1["thread"] != threading.current_thread() |