deprecation.py 5.0 KB

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