diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 0efc7fa6c2..62ad957237 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -18,9 +18,9 @@ REST framework releases follow a formal deprecation policy, which is in line wit The timeline for deprecation of a feature present in version 1.0 would work as follows: -* Version 1.1 would remain **fully backwards compatible** with 1.0, but would raise `PendingDeprecationWarning` warnings if you use the feature that are due to be deprecated. These warnings are **silent by default**, but can be explicitly enabled when you're ready to start migrating any required changes. For example if you start running your tests using `python -Wd manage.py test`, you'll be warned of any API changes you need to make. +* Version 1.1 would remain **fully backwards compatible** with 1.0, but would raise `RemovedInDRF13Warning` warnings, subclassing `PendingDeprecationWarning`, if you use the feature that are due to be deprecated. These warnings are **silent by default**, but can be explicitly enabled when you're ready to start migrating any required changes. For example if you start running your tests using `python -Wd manage.py test`, you'll be warned of any API changes you need to make. -* Version 1.2 would escalate these warnings to `DeprecationWarning`, which is loud by default. +* Version 1.2 would escalate these warnings to subclass `DeprecationWarning`, which is loud by default. * Version 1.3 would remove the deprecated bits of API entirely. diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index b9da046ae5..eacc8dca04 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -23,3 +23,11 @@ ISO_8601 = 'iso-8601' default_app_config = 'rest_framework.apps.RestFrameworkConfig' + + +class RemovedInDRF310Warning(DeprecationWarning): + pass + + +class RemovedInDRF311Warning(PendingDeprecationWarning): + pass diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index f6d557d11f..30bfcc4e53 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -14,6 +14,7 @@ from django.forms.utils import pretty_name from django.utils import six +from rest_framework import RemovedInDRF310Warning from rest_framework.views import APIView @@ -225,7 +226,7 @@ def detail_route(methods=None, **kwargs): warnings.warn( "`detail_route` is deprecated and will be removed in 3.10 in favor of " "`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.", - DeprecationWarning, stacklevel=2 + RemovedInDRF310Warning, stacklevel=2 ) def decorator(func): @@ -243,7 +244,7 @@ def list_route(methods=None, **kwargs): warnings.warn( "`list_route` is deprecated and will be removed in 3.10 in favor of " "`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.", - DeprecationWarning, stacklevel=2 + RemovedInDRF310Warning, stacklevel=2 ) def decorator(func): diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 7989ace349..bb1b86586c 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -17,6 +17,7 @@ from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ +from rest_framework import RemovedInDRF310Warning from rest_framework.compat import ( coreapi, coreschema, distinct, is_guardian_installed ) @@ -299,7 +300,7 @@ def __init__(self): warnings.warn( "`DjangoObjectPermissionsFilter` has been deprecated and moved to " "the 3rd-party django-rest-framework-guardian package.", - DeprecationWarning, stacklevel=2 + RemovedInDRF310Warning, stacklevel=2 ) assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed' diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 2c24f90998..1cacea1812 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -25,7 +25,9 @@ from django.utils import six from django.utils.deprecation import RenameMethodsBase -from rest_framework import views +from rest_framework import ( + RemovedInDRF310Warning, RemovedInDRF311Warning, views +) from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.schemas import SchemaGenerator @@ -43,7 +45,7 @@ def __new__(cls, url, name, initkwargs): "`DynamicDetailRoute` is deprecated and will be removed in 3.10 " "in favor of `DynamicRoute`, which accepts a `detail` boolean. Use " "`DynamicRoute(url, name, True, initkwargs)` instead.", - DeprecationWarning, stacklevel=2 + RemovedInDRF310Warning, stacklevel=2 ) return DynamicRoute(url, name, True, initkwargs) @@ -54,7 +56,7 @@ def __new__(cls, url, name, initkwargs): "`DynamicListRoute` is deprecated and will be removed in 3.10 in " "favor of `DynamicRoute`, which accepts a `detail` boolean. Use " "`DynamicRoute(url, name, False, initkwargs)` instead.", - DeprecationWarning, stacklevel=2 + RemovedInDRF310Warning, stacklevel=2 ) return DynamicRoute(url, name, False, initkwargs) @@ -77,7 +79,7 @@ def flatten(list_of_lists): class RenameRouterMethods(RenameMethodsBase): renamed_methods = ( - ('get_default_base_name', 'get_default_basename', PendingDeprecationWarning), + ('get_default_base_name', 'get_default_basename', RemovedInDRF311Warning), ) @@ -88,7 +90,7 @@ def __init__(self): def register(self, prefix, viewset, basename=None, base_name=None): if base_name is not None: msg = "The `base_name` argument is pending deprecation in favor of `basename`." - warnings.warn(msg, PendingDeprecationWarning, 2) + warnings.warn(msg, RemovedInDRF311Warning, 2) assert not (basename and base_name), ( "Do not provide both the `basename` and `base_name` arguments.") diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 9c6a899bfa..13dd41ff3a 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -3,7 +3,7 @@ import pytest from django.test import TestCase -from rest_framework import status +from rest_framework import RemovedInDRF310Warning, status from rest_framework.authentication import BasicAuthentication from rest_framework.decorators import ( action, api_view, authentication_classes, detail_route, list_route, @@ -290,7 +290,7 @@ def test_action(): raise NotImplementedError def test_detail_route_deprecation(self): - with pytest.warns(DeprecationWarning) as record: + with pytest.warns(RemovedInDRF310Warning) as record: @detail_route() def view(request): raise NotImplementedError @@ -303,7 +303,7 @@ def view(request): ) def test_list_route_deprecation(self): - with pytest.warns(DeprecationWarning) as record: + with pytest.warns(RemovedInDRF310Warning) as record: @list_route() def view(request): raise NotImplementedError @@ -317,7 +317,7 @@ def view(request): def test_route_url_name_from_path(self): # pre-3.8 behavior was to base the `url_name` off of the `url_path` - with pytest.warns(DeprecationWarning): + with pytest.warns(RemovedInDRF310Warning): @list_route(url_path='foo_bar') def view(request): raise NotImplementedError diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 8070068586..2fabdfa05c 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -12,8 +12,8 @@ from django.urls import ResolverMatch from rest_framework import ( - HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, - status, views + HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics, + permissions, serializers, status, views ) from rest_framework.compat import PY36, is_guardian_installed, mock from rest_framework.filters import DjangoObjectPermissionsFilter @@ -427,7 +427,7 @@ def test_django_object_permissions_filter_deprecated(self): message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved " "to the 3rd-party django-rest-framework-guardian package.") self.assertEqual(len(w), 1) - self.assertIs(w[-1].category, DeprecationWarning) + self.assertIs(w[-1].category, RemovedInDRF310Warning) self.assertEqual(str(w[-1].message), message) def test_can_read_list_permissions(self): diff --git a/tests/test_routers.py b/tests/test_routers.py index c740553479..a3a731f939 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -10,7 +10,9 @@ from django.test import TestCase, override_settings from django.urls import resolve, reverse -from rest_framework import permissions, serializers, viewsets +from rest_framework import ( + RemovedInDRF311Warning, permissions, serializers, viewsets +) from rest_framework.compat import get_regex_pattern from rest_framework.decorators import action from rest_framework.response import Response @@ -508,7 +510,7 @@ def test_base_name_and_basename_assertion(self): def test_base_name_argument_deprecation(self): router = SimpleRouter() - with pytest.warns(PendingDeprecationWarning) as w: + with pytest.warns(RemovedInDRF311Warning) as w: warnings.simplefilter('always') router.register('mock', MockViewSet, base_name='mock') @@ -535,7 +537,7 @@ def test_get_default_base_name_deprecation(self): msg = "`CustomRouter.get_default_base_name` method should be renamed `get_default_basename`." # Class definition should raise a warning - with pytest.warns(PendingDeprecationWarning) as w: + with pytest.warns(RemovedInDRF311Warning) as w: warnings.simplefilter('always') class CustomRouter(SimpleRouter):
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: