deprecation.py 5.1 KB

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