summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/core/handlers/base.py12
-rw-r--r--django/core/handlers/exception.py5
-rw-r--r--django/test/testcases.py5
-rw-r--r--django/test/utils.py5
-rw-r--r--django/utils/deprecation.py11
-rw-r--r--django/views/generic/base.py9
-rw-r--r--docs/internals/contributing/writing-code/unit-tests.txt2
-rw-r--r--docs/releases/4.2.txt3
-rw-r--r--docs/topics/async.txt6
-rw-r--r--docs/topics/http/middleware.txt6
-rw-r--r--setup.cfg2
-rw-r--r--tests/async/tests.py4
-rw-r--r--tests/deprecation/test_middleware_mixin.py7
-rw-r--r--tests/middleware_exceptions/middleware.py7
-rw-r--r--tests/requirements/py3.txt2
15 files changed, 42 insertions, 44 deletions
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index a934659186..8911543d4e 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -2,7 +2,7 @@ import asyncio
import logging
import types
-from asgiref.sync import async_to_sync, sync_to_async
+from asgiref.sync import async_to_sync, iscoroutinefunction, sync_to_async
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
@@ -119,7 +119,7 @@ class BaseHandler:
- Asynchronous methods are left alone
"""
if method_is_async is None:
- method_is_async = asyncio.iscoroutinefunction(method)
+ method_is_async = iscoroutinefunction(method)
if debug and not name:
name = name or "method %s()" % method.__qualname__
if is_async:
@@ -191,7 +191,7 @@ class BaseHandler:
if response is None:
wrapped_callback = self.make_view_atomic(callback)
# If it is an asynchronous view, run it in a subthread.
- if asyncio.iscoroutinefunction(wrapped_callback):
+ if iscoroutinefunction(wrapped_callback):
wrapped_callback = async_to_sync(wrapped_callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
@@ -245,7 +245,7 @@ class BaseHandler:
if response is None:
wrapped_callback = self.make_view_atomic(callback)
# If it is a synchronous view, run it in a subthread
- if not asyncio.iscoroutinefunction(wrapped_callback):
+ if not iscoroutinefunction(wrapped_callback):
wrapped_callback = sync_to_async(
wrapped_callback, thread_sensitive=True
)
@@ -278,7 +278,7 @@ class BaseHandler:
% (middleware_method.__self__.__class__.__name__,),
)
try:
- if asyncio.iscoroutinefunction(response.render):
+ if iscoroutinefunction(response.render):
response = await response.render()
else:
response = await sync_to_async(
@@ -346,7 +346,7 @@ class BaseHandler:
non_atomic_requests = getattr(view, "_non_atomic_requests", set())
for alias, settings_dict in connections.settings.items():
if settings_dict["ATOMIC_REQUESTS"] and alias not in non_atomic_requests:
- if asyncio.iscoroutinefunction(view):
+ if iscoroutinefunction(view):
raise RuntimeError(
"You cannot use ATOMIC_REQUESTS with async views."
)
diff --git a/django/core/handlers/exception.py b/django/core/handlers/exception.py
index 79577c2d0a..a0b1ba678a 100644
--- a/django/core/handlers/exception.py
+++ b/django/core/handlers/exception.py
@@ -1,9 +1,8 @@
-import asyncio
import logging
import sys
from functools import wraps
-from asgiref.sync import sync_to_async
+from asgiref.sync import iscoroutinefunction, sync_to_async
from django.conf import settings
from django.core import signals
@@ -34,7 +33,7 @@ def convert_exception_to_response(get_response):
no middleware leaks an exception and that the next middleware in the stack
can rely on getting a response instead of an exception.
"""
- if asyncio.iscoroutinefunction(get_response):
+ if iscoroutinefunction(get_response):
@wraps(get_response)
async def inner(request):
diff --git a/django/test/testcases.py b/django/test/testcases.py
index c78c2300a7..090a31e7c4 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -1,4 +1,3 @@
-import asyncio
import difflib
import inspect
import json
@@ -26,7 +25,7 @@ from urllib.parse import (
)
from urllib.request import url2pathname
-from asgiref.sync import async_to_sync
+from asgiref.sync import async_to_sync, iscoroutinefunction
from django.apps import apps
from django.conf import settings
@@ -401,7 +400,7 @@ class SimpleTestCase(unittest.TestCase):
)
# Convert async test methods.
- if asyncio.iscoroutinefunction(testMethod):
+ if iscoroutinefunction(testMethod):
setattr(self, self._testMethodName, async_to_sync(testMethod))
if not skipped:
diff --git a/django/test/utils.py b/django/test/utils.py
index 2b2b92c593..5e5649b0ac 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -1,4 +1,3 @@
-import asyncio
import collections
import logging
import os
@@ -14,6 +13,8 @@ from types import SimpleNamespace
from unittest import TestCase, skipIf, skipUnless
from xml.dom.minidom import Node, parseString
+from asgiref.sync import iscoroutinefunction
+
from django.apps import apps
from django.apps.registry import Apps
from django.conf import UserSettingsHolder, settings
@@ -440,7 +441,7 @@ class TestContextDecorator:
raise TypeError("Can only decorate subclasses of unittest.TestCase")
def decorate_callable(self, func):
- if asyncio.iscoroutinefunction(func):
+ if iscoroutinefunction(func):
# If the inner function is an async function, we must execute async
# as well so that the `with` statement executes at the right time.
@wraps(func)
diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py
index caed5b25d4..19379dfe58 100644
--- a/django/utils/deprecation.py
+++ b/django/utils/deprecation.py
@@ -1,8 +1,7 @@
-import asyncio
import inspect
import warnings
-from asgiref.sync import sync_to_async
+from asgiref.sync import iscoroutinefunction, markcoroutinefunction, sync_to_async
class RemovedInDjango50Warning(DeprecationWarning):
@@ -120,16 +119,14 @@ class MiddlewareMixin:
If get_response is a coroutine function, turns us into async mode so
a thread is not consumed during a whole request.
"""
- if asyncio.iscoroutinefunction(self.get_response):
+ if iscoroutinefunction(self.get_response):
# Mark the class as async-capable, but do the actual switch
# inside __call__ to avoid swapping out dunder methods
- self._is_coroutine = asyncio.coroutines._is_coroutine
- else:
- self._is_coroutine = None
+ markcoroutinefunction(self)
def __call__(self, request):
# Exit out to async mode, if needed
- if self._is_coroutine:
+ if iscoroutinefunction(self):
return self.__acall__(request)
response = None
if hasattr(self, "process_request"):
diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index 3a3afb0c73..8f8f9397e8 100644
--- a/django/views/generic/base.py
+++ b/django/views/generic/base.py
@@ -1,6 +1,7 @@
-import asyncio
import logging
+from asgiref.sync import iscoroutinefunction, markcoroutinefunction
+
from django.core.exceptions import ImproperlyConfigured
from django.http import (
HttpResponse,
@@ -68,8 +69,8 @@ class View:
]
if not handlers:
return False
- is_async = asyncio.iscoroutinefunction(handlers[0])
- if not all(asyncio.iscoroutinefunction(h) == is_async for h in handlers[1:]):
+ is_async = iscoroutinefunction(handlers[0])
+ if not all(iscoroutinefunction(h) == is_async for h in handlers[1:]):
raise ImproperlyConfigured(
f"{cls.__qualname__} HTTP handlers must either be all sync or all "
"async."
@@ -117,7 +118,7 @@ class View:
# Mark the callback if the view class is async.
if cls.view_is_async:
- view._is_coroutine = asyncio.coroutines._is_coroutine
+ markcoroutinefunction(view)
return view
diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
index 939abb5631..e9102110eb 100644
--- a/docs/internals/contributing/writing-code/unit-tests.txt
+++ b/docs/internals/contributing/writing-code/unit-tests.txt
@@ -278,7 +278,7 @@ dependencies:
* aiosmtpd_
* argon2-cffi_ 19.1.0+
-* asgiref_ 3.5.2+ (required)
+* asgiref_ 3.6.0+ (required)
* bcrypt_
* colorama_
* docutils_
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index 7e93df0702..7979e2359b 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -438,6 +438,9 @@ Miscellaneous
``DatabaseIntrospection.get_table_description()`` rather than
``internal_size`` for ``CharField``.
+* The minimum supported version of ``asgiref`` is increased from 3.5.2 to
+ 3.6.0.
+
.. _deprecated-features-4.2:
Features deprecated in 4.2
diff --git a/docs/topics/async.txt b/docs/topics/async.txt
index a3cc77aeba..39ca864655 100644
--- a/docs/topics/async.txt
+++ b/docs/topics/async.txt
@@ -28,10 +28,10 @@ class-based view, this means declaring the HTTP method handlers, such as
.. note::
- Django uses ``asyncio.iscoroutinefunction`` to test if your view is
+ Django uses ``asgiref.sync.iscoroutinefunction`` to test if your view is
asynchronous or not. If you implement your own method of returning a
- coroutine, ensure you set the ``_is_coroutine`` attribute of the view
- to ``asyncio.coroutines._is_coroutine`` so this function returns ``True``.
+ coroutine, ensure you use ``asgiref.sync.markcoroutinefunction`` so this
+ function returns ``True``.
Under a WSGI server, async views will run in their own, one-off event loop.
This means you can use async features, like concurrent async HTTP requests,
diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt
index 29f379889f..e1a3e95ebc 100644
--- a/docs/topics/http/middleware.txt
+++ b/docs/topics/http/middleware.txt
@@ -312,7 +312,7 @@ If your middleware has both ``sync_capable = True`` and
``async_capable = True``, then Django will pass it the request without
converting it. In this case, you can work out if your middleware will receive
async requests by checking if the ``get_response`` object you are passed is a
-coroutine function, using ``asyncio.iscoroutinefunction``.
+coroutine function, using ``asgiref.sync.iscoroutinefunction``.
The ``django.utils.decorators`` module contains
:func:`~django.utils.decorators.sync_only_middleware`,
@@ -331,13 +331,13 @@ at an additional performance penalty.
Here's an example of how to create a middleware function that supports both::
- import asyncio
+ from asgiref.sync import iscoroutinefunction
from django.utils.decorators import sync_and_async_middleware
@sync_and_async_middleware
def simple_middleware(get_response):
# One-time configuration and initialization goes here.
- if asyncio.iscoroutinefunction(get_response):
+ if iscoroutinefunction(get_response):
async def middleware(request):
# Do something here!
response = await get_response(request)
diff --git a/setup.cfg b/setup.cfg
index cc511c96e6..afef79c2ab 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -39,7 +39,7 @@ packages = find:
include_package_data = true
zip_safe = false
install_requires =
- asgiref >= 3.5.2
+ asgiref >= 3.6.0
backports.zoneinfo; python_version<"3.9"
sqlparse >= 0.2.2
tzdata; sys_platform == 'win32'
diff --git a/tests/async/tests.py b/tests/async/tests.py
index 559f21b8b1..6ca5c989b0 100644
--- a/tests/async/tests.py
+++ b/tests/async/tests.py
@@ -2,7 +2,7 @@ import asyncio
import os
from unittest import mock
-from asgiref.sync import async_to_sync
+from asgiref.sync import async_to_sync, iscoroutinefunction
from django.core.cache import DEFAULT_CACHE_ALIAS, caches
from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation
@@ -84,7 +84,7 @@ class ViewTests(SimpleTestCase):
with self.subTest(view_cls=view_cls, is_async=is_async):
self.assertIs(view_cls.view_is_async, is_async)
callback = view_cls.as_view()
- self.assertIs(asyncio.iscoroutinefunction(callback), is_async)
+ self.assertIs(iscoroutinefunction(callback), is_async)
def test_mixed_views_raise_error(self):
class MixedView(View):
diff --git a/tests/deprecation/test_middleware_mixin.py b/tests/deprecation/test_middleware_mixin.py
index 060c2f5f35..3b6ad6d8ee 100644
--- a/tests/deprecation/test_middleware_mixin.py
+++ b/tests/deprecation/test_middleware_mixin.py
@@ -1,7 +1,6 @@
-import asyncio
import threading
-from asgiref.sync import async_to_sync
+from asgiref.sync import async_to_sync, iscoroutinefunction
from django.contrib.admindocs.middleware import XViewMiddleware
from django.contrib.auth.middleware import (
@@ -101,11 +100,11 @@ class MiddlewareMixinTests(SimpleTestCase):
# Middleware appears as coroutine if get_function is
# a coroutine.
middleware_instance = middleware(async_get_response)
- self.assertIs(asyncio.iscoroutinefunction(middleware_instance), True)
+ self.assertIs(iscoroutinefunction(middleware_instance), True)
# Middleware doesn't appear as coroutine if get_function is not
# a coroutine.
middleware_instance = middleware(sync_get_response)
- self.assertIs(asyncio.iscoroutinefunction(middleware_instance), False)
+ self.assertIs(iscoroutinefunction(middleware_instance), False)
def test_sync_to_async_uses_base_thread_and_connection(self):
"""
diff --git a/tests/middleware_exceptions/middleware.py b/tests/middleware_exceptions/middleware.py
index 5a0c82afb1..f50aa61327 100644
--- a/tests/middleware_exceptions/middleware.py
+++ b/tests/middleware_exceptions/middleware.py
@@ -1,4 +1,4 @@
-import asyncio
+from asgiref.sync import iscoroutinefunction, markcoroutinefunction
from django.http import Http404, HttpResponse
from django.template import engines
@@ -15,9 +15,8 @@ log = []
class BaseMiddleware:
def __init__(self, get_response):
self.get_response = get_response
- if asyncio.iscoroutinefunction(self.get_response):
- # Mark the class as async-capable.
- self._is_coroutine = asyncio.coroutines._is_coroutine
+ if iscoroutinefunction(self.get_response):
+ markcoroutinefunction(self)
def __call__(self, request):
return self.get_response(request)
diff --git a/tests/requirements/py3.txt b/tests/requirements/py3.txt
index 6a88d17a8f..89209ca9e6 100644
--- a/tests/requirements/py3.txt
+++ b/tests/requirements/py3.txt
@@ -1,5 +1,5 @@
aiosmtpd
-asgiref >= 3.5.2
+asgiref >= 3.6.0
argon2-cffi >= 16.1.0
backports.zoneinfo; python_version < '3.9'
bcrypt