-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dropped Python 2 compatibility. #6615
Conversation
Hey @jdufresne. If you have bandwidth, could you throw your eye over this one? It's the sort of thing you're hot on. I think we got most of it, but no doubt there's some stuff we missed. Ta. 🙂 |
Ah, we are going to have to solve the mkdocs issue... (cc @tomchristie) https://travis-ci.org/encode/django-rest-framework/jobs/526144332 But it's not bad overall. Guardian still blocking Django master compat, but beyond that... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for bringing it all in a single PR. Looks good so far. Will try to have another look tomorrow if possible.
@@ -1,6 +1,6 @@ | |||
[tox] | |||
envlist = | |||
{py27,py34,py35,py36}-django111, | |||
{py34,py35,py36}-django111, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
py34 is already EOL :)
Running pyupgrade with the
(feel free to drop diffdiff --git a/rest_framework/authtoken/management/commands/drf_create_token.py b/rest_framework/authtoken/management/commands/drf_create_token.py
index 8e06812d..3d653924 100644
--- a/rest_framework/authtoken/management/commands/drf_create_token.py
+++ b/rest_framework/authtoken/management/commands/drf_create_token.py
@@ -38,8 +38,8 @@ class Command(BaseCommand):
token = self.create_user_token(username, reset_token)
except UserModel.DoesNotExist:
raise CommandError(
- 'Cannot create the Token: user {0} does not exist'.format(
+ 'Cannot create the Token: user {} does not exist'.format(
username)
)
self.stdout.write(
- 'Generated token {0} for user {1}'.format(token.key, username))
+ 'Generated token {} for user {}'.format(token.key, username))
diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py
index d93c2da4..8326e138 100644
--- a/rest_framework/exceptions.py
+++ b/rest_framework/exceptions.py
@@ -68,7 +68,7 @@ class ErrorDetail(str):
code = None
def __new__(cls, string, code=None):
- self = super(ErrorDetail, cls).__new__(cls, string)
+ self = super().__new__(cls, string)
self.code = code
return self
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 4418579d..ecbbbe0b 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -88,7 +88,7 @@ def get_attribute(instance, attrs):
# If we raised an Attribute or KeyError here it'd get treated
# as an omitted field in `Field.get_attribute()`. Instead we
# raise a ValueError to ensure the exception is not masked.
- raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc))
+ raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
return instance
@@ -598,7 +598,7 @@ class Field:
When a field is instantiated, we store the arguments that were used,
so that we can present a helpful representation of the object.
"""
- instance = super(Field, cls).__new__(cls)
+ instance = super().__new__(cls)
instance._args = args
instance._kwargs = kwargs
return instance
@@ -843,7 +843,7 @@ class UUIDField(Field):
if self.uuid_format not in self.valid_formats:
raise ValueError(
'Invalid format for uuid representation. '
- 'Must be one of "{0}"'.format('", "'.join(self.valid_formats))
+ 'Must be one of "{}"'.format('", "'.join(self.valid_formats))
)
super().__init__(**kwargs)
@@ -1104,7 +1104,7 @@ class DecimalField(Field):
if self.localize:
return localize_input(quantized)
- return '{0:f}'.format(quantized)
+ return '{:f}'.format(quantized)
def quantize(self, value):
"""
diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py
index 292c8f62..76113a82 100644
--- a/rest_framework/negotiation.py
+++ b/rest_framework/negotiation.py
@@ -64,7 +64,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
# Accepted media type is 'application/json'
full_media_type = ';'.join(
(renderer.media_type,) +
- tuple('{0}={1}'.format(
+ tuple('{}={}'.format(
key, value.decode(HTTP_HEADER_ENCODING))
for key, value in media_type_wrapper.params.items()))
return renderer, full_media_type
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index e4de4911..76c4d700 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -121,7 +121,7 @@ class RelatedField(Field):
# `ManyRelatedField` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
- return super(RelatedField, cls).__new__(cls, *args, **kwargs)
+ return super().__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
diff --git a/rest_framework/response.py b/rest_framework/response.py
index e031fc2a..db797777 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -62,7 +62,7 @@ class Response(SimpleTemplateResponse):
content_type = self.content_type
if content_type is None and charset is not None:
- content_type = "{0}; charset={1}".format(media_type, charset)
+ content_type = "{}; charset={}".format(media_type, charset)
elif content_type is None:
content_type = media_type
self['Content-Type'] = content_type
diff --git a/rest_framework/schemas/views.py b/rest_framework/schemas/views.py
index 73b4a807..fa5cdbdc 100644
--- a/rest_framework/schemas/views.py
+++ b/rest_framework/schemas/views.py
@@ -38,4 +38,4 @@ class SchemaView(APIView):
self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
neg = self.perform_content_negotiation(self.request, force=True)
self.request.accepted_renderer, self.request.accepted_media_type = neg
- return super(SchemaView, self).handle_exception(exc)
+ return super().handle_exception(exc)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index d36b866b..7edbc12f 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -113,14 +113,14 @@ class BaseSerializer(Field):
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
kwargs.pop('many', None)
- super(BaseSerializer, self).__init__(**kwargs)
+ super().__init__(**kwargs)
def __new__(cls, *args, **kwargs):
# We override this method in order to automagically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
- return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
+ return super().__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
@@ -313,7 +313,7 @@ class SerializerMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
- return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
+ return super().__new__(cls, name, bases, attrs)
def as_serializer_error(exc):
@@ -463,7 +463,7 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
to_validate.update(value)
else:
to_validate = value
- super(Serializer, self).run_validators(to_validate)
+ super().run_validators(to_validate)
def to_internal_value(self, data):
"""
@@ -557,12 +557,12 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
@property
def data(self):
- ret = super(Serializer, self).data
+ ret = super().data
return ReturnDict(ret, serializer=self)
@property
def errors(self):
- ret = super(Serializer, self).errors
+ ret = super().errors
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
# Edge case. Provide a more descriptive error than
# "this field may not be null", when no data is passed.
@@ -588,11 +588,11 @@ class ListSerializer(BaseSerializer):
self.allow_empty = kwargs.pop('allow_empty', True)
assert self.child is not None, '`child` is a required argument.'
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
- super(ListSerializer, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self)
def bind(self, field_name, parent):
- super(ListSerializer, self).bind(field_name, parent)
+ super().bind(field_name, parent)
self.partial = self.parent.partial
def get_initial(self):
@@ -762,12 +762,12 @@ class ListSerializer(BaseSerializer):
@property
def data(self):
- ret = super(ListSerializer, self).data
+ ret = super().data
return ReturnList(ret, serializer=self)
@property
def errors(self):
- ret = super(ListSerializer, self).errors
+ ret = super().errors
if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
# Edge case. Provide a more descriptive error than
# "this field may not be null", when no data is passed.
diff --git a/rest_framework/test.py b/rest_framework/test.py
index 54042d6e..852d4919 100644
--- a/rest_framework/test.py
+++ b/rest_framework/test.py
@@ -104,7 +104,7 @@ if requests is not None:
class RequestsClient(requests.Session):
def __init__(self, *args, **kwargs):
- super(RequestsClient, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
adapter = DjangoTestAdapter()
self.mount('http://', adapter)
self.mount('https://', adapter)
@@ -112,7 +112,7 @@ if requests is not None:
def request(self, method, url, *args, **kwargs):
if not url.startswith('http'):
raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url)
- return super(RequestsClient, self).request(method, url, *args, **kwargs)
+ return super().request(method, url, *args, **kwargs)
else:
def RequestsClient(*args, **kwargs):
@@ -124,7 +124,7 @@ if coreapi is not None:
def __init__(self, *args, **kwargs):
self._session = RequestsClient()
kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)]
- return super(CoreAPIClient, self).__init__(*args, **kwargs)
+ return super().__init__(*args, **kwargs)
@property
def session(self):
@@ -144,7 +144,7 @@ class APIRequestFactory(DjangoRequestFactory):
self.renderer_classes = {}
for cls in self.renderer_classes_list:
self.renderer_classes[cls.format] = cls
- super(APIRequestFactory, self).__init__(**defaults)
+ super().__init__(**defaults)
def _encode_data(self, data, format=None, content_type=None):
"""
@@ -166,7 +166,7 @@ class APIRequestFactory(DjangoRequestFactory):
format = format or self.default_format
assert format in self.renderer_classes, (
- "Invalid format '{0}'. Available formats are {1}. "
+ "Invalid format '{}'. Available formats are {}. "
"Set TEST_REQUEST_RENDERER_CLASSES to enable "
"extra request formats.".format(
format,
@@ -179,7 +179,7 @@ class APIRequestFactory(DjangoRequestFactory):
ret = renderer.render(data)
# Determine the content-type header from the renderer
- content_type = "{0}; charset={1}".format(
+ content_type = "{}; charset={}".format(
renderer.media_type, renderer.charset
)
@@ -228,11 +228,11 @@ class APIRequestFactory(DjangoRequestFactory):
if content_type is not None:
extra['CONTENT_TYPE'] = str(content_type)
- return super(APIRequestFactory, self).generic(
+ return super().generic(
method, path, data, content_type, secure, **extra)
def request(self, **kwargs):
- request = super(APIRequestFactory, self).request(**kwargs)
+ request = super().request(**kwargs)
request._dont_enforce_csrf_checks = not self.enforce_csrf_checks
return request
@@ -246,18 +246,18 @@ class ForceAuthClientHandler(ClientHandler):
def __init__(self, *args, **kwargs):
self._force_user = None
self._force_token = None
- super(ForceAuthClientHandler, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def get_response(self, request):
# This is the simplest place we can hook into to patch the
# request object.
force_authenticate(request, self._force_user, self._force_token)
- return super(ForceAuthClientHandler, self).get_response(request)
+ return super().get_response(request)
class APIClient(APIRequestFactory, DjangoClient):
def __init__(self, enforce_csrf_checks=False, **defaults):
- super(APIClient, self).__init__(**defaults)
+ super().__init__(**defaults)
self.handler = ForceAuthClientHandler(enforce_csrf_checks)
self._credentials = {}
@@ -280,17 +280,17 @@ class APIClient(APIRequestFactory, DjangoClient):
def request(self, **kwargs):
# Ensure that any credentials set get added to every request.
kwargs.update(self._credentials)
- return super(APIClient, self).request(**kwargs)
+ return super().request(**kwargs)
def get(self, path, data=None, follow=False, **extra):
- response = super(APIClient, self).get(path, data=data, **extra)
+ response = super().get(path, data=data, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response
def post(self, path, data=None, format=None, content_type=None,
follow=False, **extra):
- response = super(APIClient, self).post(
+ response = super().post(
path, data=data, format=format, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
@@ -298,7 +298,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def put(self, path, data=None, format=None, content_type=None,
follow=False, **extra):
- response = super(APIClient, self).put(
+ response = super().put(
path, data=data, format=format, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
@@ -306,7 +306,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def patch(self, path, data=None, format=None, content_type=None,
follow=False, **extra):
- response = super(APIClient, self).patch(
+ response = super().patch(
path, data=data, format=format, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
@@ -314,7 +314,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def delete(self, path, data=None, format=None, content_type=None,
follow=False, **extra):
- response = super(APIClient, self).delete(
+ response = super().delete(
path, data=data, format=format, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
@@ -322,7 +322,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def options(self, path, data=None, format=None, content_type=None,
follow=False, **extra):
- response = super(APIClient, self).options(
+ response = super().options(
path, data=data, format=format, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
@@ -336,7 +336,7 @@ class APIClient(APIRequestFactory, DjangoClient):
self.handler._force_token = None
if self.session:
- super(APIClient, self).logout()
+ super().logout()
class APITransactionTestCase(testcases.TransactionTestCase):
@@ -383,11 +383,11 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
cls._module.urlpatterns = cls.urlpatterns
cls._override.enable()
- super(URLPatternsTestCase, cls).setUpClass()
+ super().setUpClass()
@classmethod
def tearDownClass(cls):
- super(URLPatternsTestCase, cls).tearDownClass()
+ super().tearDownClass()
cls._override.disable()
if hasattr(cls, '_module_urlpatterns'):
diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py
index bc4ad886..0ba2ba66 100644
--- a/rest_framework/throttling.py
+++ b/rest_framework/throttling.py
@@ -230,7 +230,7 @@ class ScopedRateThrottle(SimpleRateThrottle):
self.num_requests, self.duration = self.parse_rate(self.rate)
# We can now proceed as normal.
- return super(ScopedRateThrottle, self).allow_request(request, view)
+ return super().allow_request(request, view)
def get_cache_key(self, request, view):
"""
diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py
index 8e27a925..0e56333e 100644
--- a/rest_framework/utils/serializer_helpers.py
+++ b/rest_framework/utils/serializer_helpers.py
@@ -101,7 +101,7 @@ class NestedBoundField(BoundField):
"""
def __init__(self, field, value, errors, prefix=''):
- if value is None or value is '':
+ if value is None or value == '':
value = {}
super().__init__(field, value, errors, prefix)
diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py
index d776df8c..0631a75c 100644
--- a/rest_framework/versioning.py
+++ b/rest_framework/versioning.py
@@ -84,7 +84,7 @@ class URLPathVersioning(BaseVersioning):
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version
- return super(URLPathVersioning, self).reverse(
+ return super().reverse(
viewname, args, kwargs, request, format, **extra
)
@@ -130,7 +130,7 @@ class NamespaceVersioning(BaseVersioning):
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
viewname = self.get_versioned_viewname(viewname, request)
- return super(NamespaceVersioning, self).reverse(
+ return super().reverse(
viewname, args, kwargs, request, format, **extra
)
@@ -176,7 +176,7 @@ class QueryParameterVersioning(BaseVersioning):
return version
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
- url = super(QueryParameterVersioning, self).reverse(
+ url = super().reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
diff --git a/rest_framework/views.py b/rest_framework/views.py
index 354e03ec..6ef7021d 100644
--- a/rest_framework/views.py
+++ b/rest_framework/views.py
@@ -135,7 +135,7 @@ class APIView(View):
)
cls.queryset._fetch_all = force_evaluation
- view = super(APIView, cls).as_view(**initkwargs)
+ view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py
index 90ae603e..ad563385 100644
--- a/rest_framework/viewsets.py
+++ b/rest_framework/viewsets.py
@@ -132,7 +132,7 @@ class ViewSetMixin:
"""
Set the `.action` attribute on the view, depending on the request method.
"""
- request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs)
+ request = super().initialize_request(request, *args, **kwargs)
method = request.method.lower()
if method == 'options':
# This is a special case as we always provide handling for the
diff --git a/tests/authentication/test_authentication.py b/tests/authentication/test_authentication.py
index e73a3438..f7e9fcf1 100644
--- a/tests/authentication/test_authentication.py
+++ b/tests/authentication/test_authentication.py
@@ -529,7 +529,7 @@ class BasicAuthenticationUnitTests(TestCase):
def test_basic_authentication_raises_error_if_user_not_active(self):
from rest_framework import authentication
- class MockUser(object):
+ class MockUser:
is_active = False
old_authenticate = authentication.authenticate
authentication.authenticate = lambda **kwargs: MockUser()
diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py
index 0e807dec..de04d2c0 100644
--- a/tests/test_atomic_requests.py
+++ b/tests/test_atomic_requests.py
@@ -36,7 +36,7 @@ class APIExceptionView(APIView):
class NonAtomicAPIExceptionView(APIView):
@transaction.non_atomic_requests
def dispatch(self, *args, **kwargs):
- return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs)
+ return super().dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
BasicModel.objects.all()
diff --git a/tests/test_bound_fields.py b/tests/test_bound_fields.py
index e588ae62..dc5ab542 100644
--- a/tests/test_bound_fields.py
+++ b/tests/test_bound_fields.py
@@ -28,7 +28,7 @@ class TestSimpleBoundField:
assert serializer['text'].value == 'abc'
assert serializer['text'].errors is None
assert serializer['text'].name == 'text'
- assert serializer['amount'].value is 123
+ assert serializer['amount'].value == 123
assert serializer['amount'].errors is None
assert serializer['amount'].name == 'amount'
@@ -43,7 +43,7 @@ class TestSimpleBoundField:
assert serializer['text'].value == 'x' * 1000
assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.']
assert serializer['text'].name == 'text'
- assert serializer['amount'].value is 123
+ assert serializer['amount'].value == 123
assert serializer['amount'].errors is None
assert serializer['amount'].name == 'amount'
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 3b9f3482..247f5b73 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -165,7 +165,7 @@ class TestEmpty:
"""
field = serializers.IntegerField(default=123)
output = field.run_validation()
- assert output is 123
+ assert output == 123
class TestSource:
@@ -752,7 +752,7 @@ class TestCharField(FieldValues):
def raise_exception(value):
raise exceptions.ValidationError('Raised error')
- for validators in ([raise_exception], (raise_exception,), set([raise_exception])):
+ for validators in ([raise_exception], (raise_exception,), {raise_exception}):
field = serializers.CharField(validators=validators)
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(value)
@@ -820,7 +820,7 @@ class TestSlugField(FieldValues):
validation_error = False
try:
- field.run_validation(u'slug-99-\u0420')
+ field.run_validation('slug-99-\u0420')
except serializers.ValidationError:
validation_error = True
@@ -1626,7 +1626,7 @@ class TestChoiceField(FieldValues):
]
)
field.choices = [1]
- assert field.run_validation(1) is 1
+ assert field.run_validation(1) == 1
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(2)
assert exc_info.value.detail == ['"2" is not a valid choice.']
diff --git a/tests/test_filters.py b/tests/test_filters.py
index bbe70c01..a52f4010 100644
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -161,7 +161,7 @@ class SearchFilterTests(TestCase):
def get_search_fields(self, view, request):
if request.query_params.get('title_only'):
return ('$title',)
- return super(CustomSearchFilter, self).get_search_fields(view, request)
+ return super().get_search_fields(view, request)
class SearchListView(generics.ListAPIView):
queryset = SearchFilterModel.objects.all()
diff --git a/tests/test_generics.py b/tests/test_generics.py
index 13539082..f41ebe6d 100644
--- a/tests/test_generics.py
+++ b/tests/test_generics.py
@@ -288,7 +288,7 @@ class TestInstanceView(TestCase):
"""
data = {'text': 'foo'}
filtered_out_pk = BasicModel.objects.filter(text='filtered out')[0].pk
- request = factory.put('/{0}'.format(filtered_out_pk), data, format='json')
+ request = factory.put('/{}'.format(filtered_out_pk), data, format='json')
response = self.view(request, pk=filtered_out_pk).render()
assert response.status_code == status.HTTP_404_NOT_FOUND
@@ -443,12 +443,12 @@ class TestM2MBrowsableAPI(TestCase):
assert response.status_code == status.HTTP_200_OK
-class InclusiveFilterBackend(object):
+class InclusiveFilterBackend:
def filter_queryset(self, request, queryset, view):
return queryset.filter(text='foo')
-class ExclusiveFilterBackend(object):
+class ExclusiveFilterBackend:
def filter_queryset(self, request, queryset, view):
return queryset.filter(text='other')
@@ -650,7 +650,7 @@ class ApiViewsTests(TestCase):
class GetObjectOr404Tests(TestCase):
def setUp(self):
- super(GetObjectOr404Tests, self).setUp()
+ super().setUp()
self.uuid_object = UUIDForeignKeyTarget.objects.create(name='bar')
def test_get_object_or_404_with_valid_uuid(self):
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index d339d507..2b9ab751 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -414,7 +414,7 @@ class TestGenericIPAddressFieldValidation(TestCase):
self.assertFalse(s.is_valid())
self.assertEqual(1, len(s.errors['address']),
'Unexpected number of validation errors: '
- '{0}'.format(s.errors))
+ '{}'.format(s.errors))
@pytest.mark.skipif('not postgres_fields')
diff --git a/tests/test_pagination.py b/tests/test_pagination.py
index 1044992a..3c581ddf 100644
--- a/tests/test_pagination.py
+++ b/tests/test_pagination.py
@@ -499,7 +499,7 @@ class TestLimitOffset:
content = self.get_paginated_content(queryset)
next_limit = self.pagination.default_limit
next_offset = self.pagination.default_limit
- next_url = 'http://testserver/?limit={0}&offset={1}'.format(next_limit, next_offset)
+ next_url = 'http://testserver/?limit={}&offset={}'.format(next_limit, next_offset)
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
assert content.get('next') == next_url
@@ -512,7 +512,7 @@ class TestLimitOffset:
content = self.get_paginated_content(queryset)
next_limit = self.pagination.default_limit
next_offset = self.pagination.default_limit
- next_url = 'http://testserver/?limit={0}&offset={1}'.format(next_limit, next_offset)
+ next_url = 'http://testserver/?limit={}&offset={}'.format(next_limit, next_offset)
assert queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
assert content.get('next') == next_url
@@ -528,9 +528,9 @@ class TestLimitOffset:
max_limit = self.pagination.max_limit
next_offset = offset + max_limit
prev_offset = offset - max_limit
- base_url = 'http://testserver/?limit={0}'.format(max_limit)
- next_url = base_url + '&offset={0}'.format(next_offset)
- prev_url = base_url + '&offset={0}'.format(prev_offset)
+ base_url = 'http://testserver/?limit={}'.format(max_limit)
+ next_url = base_url + '&offset={}'.format(next_offset)
+ prev_url = base_url + '&offset={}'.format(prev_offset)
assert queryset == list(range(51, 66))
assert content.get('next') == next_url
assert content.get('previous') == prev_url
diff --git a/tests/test_permissions.py b/tests/test_permissions.py
index 0ee3f1e0..c385eaef 100644
--- a/tests/test_permissions.py
+++ b/tests/test_permissions.py
@@ -329,14 +329,14 @@ class ObjectPermissionsIntegrationTests(TestCase):
everyone = Group.objects.create(name='everyone')
model_name = BasicPermModel._meta.model_name
app_label = BasicPermModel._meta.app_label
- f = '{0}_{1}'.format
+ f = '{}_{}'.format
perms = {
'view': f('view', model_name),
'change': f('change', model_name),
'delete': f('delete', model_name)
}
for perm in perms.values():
- perm = '{0}.{1}'.format(app_label, perm)
+ perm = '{}.{}'.format(app_label, perm)
assign_perm(perm, everyone)
everyone.user_set.add(*users.values())
diff --git a/tests/test_relations.py b/tests/test_relations.py
index 3c4b7d90..3281b7ea 100644
--- a/tests/test_relations.py
+++ b/tests/test_relations.py
@@ -26,7 +26,7 @@ class TestStringRelatedField(APISimpleTestCase):
assert representation == '<MockObject name=foo, pk=1>'
-class MockApiSettings(object):
+class MockApiSettings:
def __init__(self, cutoff, cutoff_text):
self.HTML_SELECT_CUTOFF = cutoff
self.HTML_SELECT_CUTOFF_TEXT = cutoff_text
diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py
index 3da3dc3e..0da9da89 100644
--- a/tests/test_relations_pk.py
+++ b/tests/test_relations_pk.py
@@ -559,7 +559,7 @@ class OneToOnePrimaryKeyTests(TestCase):
# When: Trying to create a second object
second_source = OneToOnePKSourceSerializer(data=data)
self.assertFalse(second_source.is_valid())
- expected = {'target': [u'one to one pk source with this target already exists.']}
+ expected = {'target': ['one to one pk source with this target already exists.']}
self.assertDictEqual(second_source.errors, expected)
def test_one_to_one_when_primary_key_does_not_exist(self):
diff --git a/tests/test_request.py b/tests/test_request.py
index 55268b8e..0f682deb 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -232,7 +232,7 @@ class TestUserSetter(TestCase):
This proves that when an AttributeError is raised inside of the request.user
property, that we can handle this and report the true, underlying error.
"""
- class AuthRaisesAttributeError(object):
+ class AuthRaisesAttributeError:
def authenticate(self, request):
self.MISSPELLED_NAME_THAT_DOESNT_EXIST
diff --git a/tests/test_response.py b/tests/test_response.py
index 0f74c097..d3a56d01 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -257,7 +257,7 @@ class Issue807Tests(TestCase):
"""
headers = {"HTTP_ACCEPT": RendererA.media_type}
resp = self.client.get('/', **headers)
- expected = "{0}; charset={1}".format(RendererA.media_type, 'utf-8')
+ expected = "{}; charset={}".format(RendererA.media_type, 'utf-8')
self.assertEqual(expected, resp['Content-Type'])
def test_if_there_is_charset_specified_on_renderer_it_gets_appended(self):
@@ -267,7 +267,7 @@ class Issue807Tests(TestCase):
"""
headers = {"HTTP_ACCEPT": RendererC.media_type}
resp = self.client.get('/', **headers)
- expected = "{0}; charset={1}".format(RendererC.media_type, RendererC.charset)
+ expected = "{}; charset={}".format(RendererC.media_type, RendererC.charset)
self.assertEqual(expected, resp['Content-Type'])
def test_content_type_set_explicitly_on_response(self):
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
index fc7a8302..1aad5d1d 100644
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -112,7 +112,7 @@ class ExampleViewSet(ModelViewSet):
def get_serializer(self, *args, **kwargs):
assert self.request
assert self.action
- return super(ExampleViewSet, self).get_serializer(*args, **kwargs)
+ return super().get_serializer(*args, **kwargs)
@action(methods=['get', 'post'], detail=False)
def documented_custom_action(self, request):
@@ -1303,7 +1303,7 @@ def test_head_and_options_methods_are_excluded():
@pytest.mark.skipif(not coreapi, reason='coreapi is not installed')
-class TestAutoSchemaAllowsFilters(object):
+class TestAutoSchemaAllowsFilters:
class MockAPIView(APIView):
filter_backends = [filters.OrderingFilter]
diff --git a/tests/test_serializer.py b/tests/test_serializer.py
index 8a1df04e..7feb5a87 100644
--- a/tests/test_serializer.py
+++ b/tests/test_serializer.py
@@ -168,7 +168,7 @@ class TestSerializer:
latitude = serializers.FloatField(source='y')
def to_internal_value(self, data):
- kwargs = super(NestedPointSerializer, self).to_internal_value(data)
+ kwargs = super().to_internal_value(data)
return Point(srid=4326, **kwargs)
serializer = NestedPointSerializer(data={'longitude': 6.958307, 'latitude': 50.941357})
@@ -198,7 +198,7 @@ class TestSerializer:
def raise_exception(value):
raise exceptions.ValidationError('Raised error')
- for validators in ([raise_exception], (raise_exception,), set([raise_exception])):
+ for validators in ([raise_exception], (raise_exception,), {raise_exception}):
class ExampleSerializer(serializers.Serializer):
char = serializers.CharField(validators=validators)
integer = serializers.IntegerField()
@@ -606,7 +606,7 @@ class Test2555Regression:
def test_serializer_context(self):
class NestedSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
- super(NestedSerializer, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
# .context should not cache
self.context
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index 796a7f67..12816088 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -222,7 +222,7 @@ class TemplateTagTests(TestCase):
assert result == ''
def test_get_pagination_html(self):
- class MockPager(object):
+ class MockPager:
def __init__(self):
self.called = False
@@ -337,7 +337,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['users']
flat_links = schema_links(section)
- assert len(flat_links) is 0
+ assert len(flat_links) == 0
def test_single_action(self):
schema = coreapi.Document(
@@ -355,7 +355,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['users']
flat_links = schema_links(section)
- assert len(flat_links) is 1
+ assert len(flat_links) == 1
assert 'list' in flat_links
def test_default_actions(self):
@@ -393,7 +393,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['users']
flat_links = schema_links(section)
- assert len(flat_links) is 4
+ assert len(flat_links) == 4
assert 'list' in flat_links
assert 'create' in flat_links
assert 'read' in flat_links
@@ -441,7 +441,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['users']
flat_links = schema_links(section)
- assert len(flat_links) is 5
+ assert len(flat_links) == 5
assert 'list' in flat_links
assert 'create' in flat_links
assert 'read' in flat_links
@@ -499,7 +499,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['users']
flat_links = schema_links(section)
- assert len(flat_links) is 6
+ assert len(flat_links) == 6
assert 'list' in flat_links
assert 'create' in flat_links
assert 'read' in flat_links
@@ -550,7 +550,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['animals']
flat_links = schema_links(section)
- assert len(flat_links) is 4
+ assert len(flat_links) == 4
assert 'cat > create' in flat_links
assert 'cat > list' in flat_links
assert 'dog > read' in flat_links
@@ -619,7 +619,7 @@ class SchemaLinksTests(TestCase):
)
section = schema['animals']
flat_links = schema_links(section)
- assert len(flat_links) is 4
+ assert len(flat_links) == 4
assert 'cat > create' in flat_links
assert 'cat > list' in flat_links
assert 'dog > read' in flat_links
@@ -627,6 +627,6 @@ class SchemaLinksTests(TestCase):
section = schema['farmers']
flat_links = schema_links(section)
- assert len(flat_links) is 2
+ assert len(flat_links) == 2
assert 'silo > list' in flat_links
assert 'silo > soy > list' in flat_links
diff --git a/tests/test_testing.py b/tests/test_testing.py
index b7fc7e30..8094bfd8 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -290,13 +290,13 @@ class TestUrlPatternTestCase(URLPatternsTestCase):
@classmethod
def setUpClass(cls):
assert urlpatterns is not cls.urlpatterns
- super(TestUrlPatternTestCase, cls).setUpClass()
+ super().setUpClass()
assert urlpatterns is cls.urlpatterns
@classmethod
def tearDownClass(cls):
assert urlpatterns is cls.urlpatterns
- super(TestUrlPatternTestCase, cls).tearDownClass()
+ super().tearDownClass()
assert urlpatterns is not cls.urlpatterns
def test_urlpatterns(self):
diff --git a/tests/test_validation.py b/tests/test_validation.py
index 9e0d6b96..6e00b48c 100644
--- a/tests/test_validation.py
+++ b/tests/test_validation.py
@@ -148,14 +148,14 @@ class TestMaxValueValidatorValidation(TestCase):
def test_max_value_validation_success(self):
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
- request = factory.patch('/{0}'.format(obj.pk), {'number_value': 98}, format='json')
+ request = factory.patch('/{}'.format(obj.pk), {'number_value': 98}, format='json')
view = UpdateMaxValueValidationModel().as_view()
response = view(request, pk=obj.pk).render()
assert response.status_code == status.HTTP_200_OK
def test_max_value_validation_fail(self):
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
- request = factory.patch('/{0}'.format(obj.pk), {'number_value': 101}, format='json')
+ request = factory.patch('/{}'.format(obj.pk), {'number_value': 101}, format='json')
view = UpdateMaxValueValidationModel().as_view()
response = view(request, pk=obj.pk).render()
assert response.content == b'{"number_value":["Ensure this value is less than or equal to 100."]}'
diff --git a/tests/test_validators.py b/tests/test_validators.py
index 4bbddb64..fe31ba23 100644
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -353,7 +353,7 @@ class TestUniquenessTogetherValidation(TestCase):
filter_queryset should add value from existing instance attribute
if it is not provided in attributes dict
"""
- class MockQueryset(object):
+ class MockQueryset:
def filter(self, **kwargs):
self.called_with = kwargs
@@ -558,19 +558,19 @@ class TestHiddenFieldUniquenessForDateValidation(TestCase):
class ValidatorsTests(TestCase):
def test_qs_exists_handles_type_error(self):
- class TypeErrorQueryset(object):
+ class TypeErrorQueryset:
def exists(self):
raise TypeError
assert qs_exists(TypeErrorQueryset()) is False
def test_qs_exists_handles_value_error(self):
- class ValueErrorQueryset(object):
+ class ValueErrorQueryset:
def exists(self):
raise ValueError
assert qs_exists(ValueErrorQueryset()) is False
def test_qs_exists_handles_data_error(self):
- class DataErrorQueryset(object):
+ class DataErrorQueryset:
def exists(self):
raise DataError
assert qs_exists(DataErrorQueryset()) is False
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index 526df829..d4e269df 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -319,7 +319,7 @@ class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase):
]
def setUp(self):
- super(TestHyperlinkedRelatedField, self).setUp()
+ super().setUp()
class MockQueryset:
def get(self, pk): |
This is great! Nice work. Sorry the if my review looks long. I've built up quite a list of things to check when dropping Python 2 🙂. Feel free to omit any cleanups if you think they aren't worth it or you disagree with them. I can provide a hand if you'd like, just let me know. The following fallbacks in
In
In
There are a few more instances of
One remaining encoding cookie that can be dropped:
(Edit: Nope, can't do this. https://docs.python.org/3/reference/datamodel.html#object.__ne__
You can add the trove classifier: The docs mention I would change Python 3 allows you to omit 'utf-8' from You can replace the Drop
From Consider replacing |
The docs still reference Python 2.7 in a few places. Worth verifying if it is still relevant:
|
Hey @jdufresne. Super thanks.
No. Exactly what I was hoping for. 🙂
Happy to take all this. If you have capacity a PR onto @auvipy :
Happy to drop Python 3.4 for the 3.10 release, but lets do it in a separate PR (after merging this). Thanks! |
1950896
to
6536b7f
Compare
700bfa3
to
ed16f40
Compare
OK, outstanding, but I'll leave for a follow-up:
And The docs still reference Python 2.7 in a few places. Worth verifying if it is still relevant:
|
Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <[email protected]> Co-authored-by: Rizwan Mansuri <[email protected]>
ed16f40
to
dda050e
Compare
Hey @jdufresne. Thanks. I'll pulled in most of your comments. Assuming CI is green I'll merge this and we can add other clean ups separately. |
yeah!! 👯♂️ |
Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <[email protected]> Co-authored-by: Rizwan Mansuri <[email protected]>
Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <[email protected]> Co-authored-by: Rizwan Mansuri <[email protected]>
Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <[email protected]> Co-authored-by: Rizwan Mansuri <[email protected]>
No description provided.