mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Refs #34118 -- Adopted asgiref coroutine detection shims.
Thanks to Mariusz Felisiak for review.
This commit is contained in:
parent
a09d39f286
commit
32d70b2f55
@ -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."
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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"):
|
||||
|
@ -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
|
||||
|
||||
|
@ -278,7 +278,7 @@ dependencies:
|
||||
|
||||
* aiosmtpd_
|
||||
* argon2-cffi_ 19.1.0+
|
||||
* asgiref_ 3.5.2+ (required)
|
||||
* asgiref_ 3.6.0+ (required)
|
||||
* bcrypt_
|
||||
* colorama_
|
||||
* docutils_
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user