deprecation.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import asyncio
  2. import inspect
  3. import warnings
  4. from asgiref.sync import sync_to_async
  5. class RemovedInDjango50Warning(DeprecationWarning):
  6. pass
  7. class RemovedInDjango51Warning(PendingDeprecationWarning):
  8. pass
  9. RemovedInNextVersionWarning = RemovedInDjango50Warning
  10. RemovedAfterNextVersionWarning = RemovedInDjango51Warning
  11. class warn_about_renamed_method:
  12. def __init__(
  13. self, class_name, old_method_name, new_method_name, deprecation_warning
  14. ):
  15. self.class_name = class_name
  16. self.old_method_name = old_method_name
  17. self.new_method_name = new_method_name
  18. self.deprecation_warning = deprecation_warning
  19. def __call__(self, f):
  20. def wrapped(*args, **kwargs):
  21. warnings.warn(
  22. "`%s.%s` is deprecated, use `%s` instead."
  23. % (self.class_name, self.old_method_name, self.new_method_name),
  24. self.deprecation_warning,
  25. 2,
  26. )
  27. return f(*args, **kwargs)
  28. return wrapped
  29. class RenameMethodsBase(type):
  30. """
  31. Handles the deprecation paths when renaming a method.
  32. It does the following:
  33. 1) Define the new method if missing and complain about it.
  34. 2) Define the old method if missing.
  35. 3) Complain whenever an old method is called.
  36. See #15363 for more details.
  37. """
  38. renamed_methods = ()
  39. def __new__(cls, name, bases, attrs):
  40. new_class = super().__new__(cls, name, bases, attrs)
  41. for base in inspect.getmro(new_class):
  42. class_name = base.__name__
  43. for renamed_method in cls.renamed_methods:
  44. old_method_name = renamed_method[0]
  45. old_method = base.__dict__.get(old_method_name)
  46. new_method_name = renamed_method[1]
  47. new_method = base.__dict__.get(new_method_name)
  48. deprecation_warning = renamed_method[2]
  49. wrapper = warn_about_renamed_method(class_name, *renamed_method)
  50. # Define the new method if missing and complain about it
  51. if not new_method and old_method:
  52. warnings.warn(
  53. "`%s.%s` method should be renamed `%s`."
  54. % (class_name, old_method_name, new_method_name),
  55. deprecation_warning,
  56. 2,
  57. )
  58. setattr(base, new_method_name, old_method)
  59. setattr(base, old_method_name, wrapper(old_method))
  60. # Define the old method as a wrapped call to the new method.
  61. if not old_method and new_method:
  62. setattr(base, old_method_name, wrapper(new_method))
  63. return new_class
  64. class DeprecationInstanceCheck(type):
  65. def __instancecheck__(self, instance):
  66. warnings.warn(
  67. "`%s` is deprecated, use `%s` instead." % (self.__name__, self.alternative),
  68. self.deprecation_warning,
  69. 2,
  70. )
  71. return super().__instancecheck__(instance)
  72. class MiddlewareMixin:
  73. sync_capable = True
  74. async_capable = True
  75. def __init__(self, get_response):
  76. if get_response is None:
  77. raise ValueError("get_response must be provided.")
  78. self.get_response = get_response
  79. self._async_check()
  80. super().__init__()
  81. def __repr__(self):
  82. return "<%s get_response=%s>" % (
  83. self.__class__.__qualname__,
  84. getattr(
  85. self.get_response,
  86. "__qualname__",
  87. self.get_response.__class__.__name__,
  88. ),
  89. )
  90. def _async_check(self):
  91. """
  92. If get_response is a coroutine function, turns us into async mode so
  93. a thread is not consumed during a whole request.
  94. """
  95. if asyncio.iscoroutinefunction(self.get_response):
  96. # Mark the class as async-capable, but do the actual switch
  97. # inside __call__ to avoid swapping out dunder methods
  98. self._is_coroutine = asyncio.coroutines._is_coroutine
  99. else:
  100. self._is_coroutine = None
  101. def __call__(self, request):
  102. # Exit out to async mode, if needed
  103. if self._is_coroutine:
  104. return self.__acall__(request)
  105. response = None
  106. if hasattr(self, "process_request"):
  107. response = self.process_request(request)
  108. response = response or self.get_response(request)
  109. if hasattr(self, "process_response"):
  110. response = self.process_response(request, response)
  111. return response
  112. async def __acall__(self, request):
  113. """
  114. Async version of __call__ that is swapped in when an async request
  115. is running.
  116. """
  117. response = None
  118. if hasattr(self, "process_request"):
  119. response = await sync_to_async(
  120. self.process_request,
  121. thread_sensitive=True,
  122. )(request)
  123. response = response or await self.get_response(request)
  124. if hasattr(self, "process_response"):
  125. response = await sync_to_async(
  126. self.process_response,
  127. thread_sensitive=True,
  128. )(request, response)
  129. return response