diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 0efc7fa6c..62ad95723 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 b9da046ae..eacc8dca0 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -23,3 +23,11 @@ HTTP_HEADER_ENCODING = 'iso-8859-1' 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 f6d557d11..30bfcc4e5 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -14,6 +14,7 @@ import warnings 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 7989ace34..bb1b86586 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -17,6 +17,7 @@ from django.utils import six 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 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): 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 2c24f9099..1cacea181 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -25,7 +25,9 @@ from django.urls import NoReverseMatch 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 @@ class DynamicDetailRoute(object): "`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 @@ class DynamicListRoute(object): "`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 @@ class BaseRouter(six.with_metaclass(RenameRouterMethods)): 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 9c6a899bf..13dd41ff3 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals 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 @@ class ActionDecoratorTestCase(TestCase): 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 @@ class ActionDecoratorTestCase(TestCase): ) 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 @@ class ActionDecoratorTestCase(TestCase): 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 807006858..2fabdfa05 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -12,8 +12,8 @@ from django.test import TestCase 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 @@ class ObjectPermissionsIntegrationTests(TestCase): 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 c74055347..a3a731f93 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -10,7 +10,9 @@ from django.db import models 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 @@ class TestBaseNameRename(TestCase): 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 @@ class TestBaseNameRename(TestCase): 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):