159 lines
5.1 KiB
Python
159 lines
5.1 KiB
Python
|
import asyncio
|
||
|
import inspect
|
||
|
import warnings
|
||
|
|
||
|
from asgiref.sync import sync_to_async
|
||
|
|
||
|
|
||
|
class RemovedInNextVersionWarning(DeprecationWarning):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class RemovedInDjango50Warning(PendingDeprecationWarning):
|
||
|
pass
|
||
|
|
||
|
|
||
|
RemovedAfterNextVersionWarning = RemovedInDjango50Warning
|
||
|
|
||
|
|
||
|
class warn_about_renamed_method:
|
||
|
def __init__(
|
||
|
self, class_name, old_method_name, new_method_name, deprecation_warning
|
||
|
):
|
||
|
self.class_name = class_name
|
||
|
self.old_method_name = old_method_name
|
||
|
self.new_method_name = new_method_name
|
||
|
self.deprecation_warning = deprecation_warning
|
||
|
|
||
|
def __call__(self, f):
|
||
|
def wrapped(*args, **kwargs):
|
||
|
warnings.warn(
|
||
|
"`%s.%s` is deprecated, use `%s` instead."
|
||
|
% (self.class_name, self.old_method_name, self.new_method_name),
|
||
|
self.deprecation_warning,
|
||
|
2,
|
||
|
)
|
||
|
return f(*args, **kwargs)
|
||
|
|
||
|
return wrapped
|
||
|
|
||
|
|
||
|
class RenameMethodsBase(type):
|
||
|
"""
|
||
|
Handles the deprecation paths when renaming a method.
|
||
|
|
||
|
It does the following:
|
||
|
1) Define the new method if missing and complain about it.
|
||
|
2) Define the old method if missing.
|
||
|
3) Complain whenever an old method is called.
|
||
|
|
||
|
See #15363 for more details.
|
||
|
"""
|
||
|
|
||
|
renamed_methods = ()
|
||
|
|
||
|
def __new__(cls, name, bases, attrs):
|
||
|
new_class = super().__new__(cls, name, bases, attrs)
|
||
|
|
||
|
for base in inspect.getmro(new_class):
|
||
|
class_name = base.__name__
|
||
|
for renamed_method in cls.renamed_methods:
|
||
|
old_method_name = renamed_method[0]
|
||
|
old_method = base.__dict__.get(old_method_name)
|
||
|
new_method_name = renamed_method[1]
|
||
|
new_method = base.__dict__.get(new_method_name)
|
||
|
deprecation_warning = renamed_method[2]
|
||
|
wrapper = warn_about_renamed_method(class_name, *renamed_method)
|
||
|
|
||
|
# Define the new method if missing and complain about it
|
||
|
if not new_method and old_method:
|
||
|
warnings.warn(
|
||
|
"`%s.%s` method should be renamed `%s`."
|
||
|
% (class_name, old_method_name, new_method_name),
|
||
|
deprecation_warning,
|
||
|
2,
|
||
|
)
|
||
|
setattr(base, new_method_name, old_method)
|
||
|
setattr(base, old_method_name, wrapper(old_method))
|
||
|
|
||
|
# Define the old method as a wrapped call to the new method.
|
||
|
if not old_method and new_method:
|
||
|
setattr(base, old_method_name, wrapper(new_method))
|
||
|
|
||
|
return new_class
|
||
|
|
||
|
|
||
|
class DeprecationInstanceCheck(type):
|
||
|
def __instancecheck__(self, instance):
|
||
|
warnings.warn(
|
||
|
"`%s` is deprecated, use `%s` instead." % (self.__name__, self.alternative),
|
||
|
self.deprecation_warning,
|
||
|
2,
|
||
|
)
|
||
|
return super().__instancecheck__(instance)
|
||
|
|
||
|
|
||
|
class MiddlewareMixin:
|
||
|
sync_capable = True
|
||
|
async_capable = True
|
||
|
|
||
|
def __init__(self, get_response):
|
||
|
if get_response is None:
|
||
|
raise ValueError("get_response must be provided.")
|
||
|
self.get_response = get_response
|
||
|
self._async_check()
|
||
|
super().__init__()
|
||
|
|
||
|
def __repr__(self):
|
||
|
return "<%s get_response=%s>" % (
|
||
|
self.__class__.__qualname__,
|
||
|
getattr(
|
||
|
self.get_response,
|
||
|
"__qualname__",
|
||
|
self.get_response.__class__.__name__,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
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
|
||
|
else:
|
||
|
self._is_coroutine = None
|
||
|
|
||
|
def __call__(self, request):
|
||
|
# Exit out to async mode, if needed
|
||
|
if self._is_coroutine:
|
||
|
return self.__acall__(request)
|
||
|
response = None
|
||
|
if hasattr(self, "process_request"):
|
||
|
response = self.process_request(request)
|
||
|
response = response or self.get_response(request)
|
||
|
if hasattr(self, "process_response"):
|
||
|
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,
|
||
|
thread_sensitive=True,
|
||
|
)(request)
|
||
|
response = response or await self.get_response(request)
|
||
|
if hasattr(self, "process_response"):
|
||
|
response = await sync_to_async(
|
||
|
self.process_response,
|
||
|
thread_sensitive=True,
|
||
|
)(request, response)
|
||
|
return response
|