summaryrefslogtreecommitdiff
path: root/django/utils
diff options
context:
space:
mode:
Diffstat (limited to 'django/utils')
-rw-r--r--django/utils/decorators.py27
-rw-r--r--django/utils/deprecation.py33
2 files changed, 60 insertions, 0 deletions
diff --git a/django/utils/decorators.py b/django/utils/decorators.py
index bb2e498e46..5c9a5d01c7 100644
--- a/django/utils/decorators.py
+++ b/django/utils/decorators.py
@@ -150,3 +150,30 @@ def make_middleware_decorator(middleware_class):
return _wrapped_view
return _decorator
return _make_decorator
+
+
+def sync_and_async_middleware(func):
+ """
+ Mark a middleware factory as returning a hybrid middleware supporting both
+ types of request.
+ """
+ func.sync_capable = True
+ func.async_capable = True
+ return func
+
+
+def sync_only_middleware(func):
+ """
+ Mark a middleware factory as returning a sync middleware.
+ This is the default.
+ """
+ func.sync_capable = True
+ func.async_capable = False
+ return func
+
+
+def async_only_middleware(func):
+ """Mark a middleware factory as returning an async middleware."""
+ func.sync_capable = False
+ func.async_capable = True
+ return func
diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py
index 81e7c3a15b..6336558a81 100644
--- a/django/utils/deprecation.py
+++ b/django/utils/deprecation.py
@@ -1,6 +1,9 @@
+import asyncio
import inspect
import warnings
+from asgiref.sync import sync_to_async
+
class RemovedInNextVersionWarning(DeprecationWarning):
pass
@@ -80,14 +83,31 @@ class DeprecationInstanceCheck(type):
class MiddlewareMixin:
+ sync_capable = True
+ async_capable = True
+
# RemovedInDjango40Warning: when the deprecation ends, replace with:
# def __init__(self, get_response):
def __init__(self, get_response=None):
self._get_response_none_deprecation(get_response)
self.get_response = get_response
+ self._async_check()
super().__init__()
+ def _async_check(self):
+ """
+ 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):
+ # 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
+
def __call__(self, request):
+ # Exit out to async mode, if needed
+ if asyncio.iscoroutinefunction(self.get_response):
+ return self.__acall__(request)
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
@@ -96,6 +116,19 @@ class MiddlewareMixin:
response = self.process_response(request, response)
return response
+ async def __acall__(self, request):
+ """
+ Async version of __call__ that is swapped in when an async request
+ is running.
+ """
+ response = None
+ if hasattr(self, 'process_request'):
+ response = await sync_to_async(self.process_request)(request)
+ response = response or await self.get_response(request)
+ if hasattr(self, 'process_response'):
+ response = await sync_to_async(self.process_response)(request, response)
+ return response
+
def _get_response_none_deprecation(self, get_response):
if get_response is None:
warnings.warn(