Dropped Python 2 compatibility. (#6615)

Thanks to Jon Dufresne (@jdufresne) for review.

Co-authored-by: Asif Saif Uddin <auvipy@gmail.com>
Co-authored-by: Rizwan Mansuri <Rizwan@webbyfox.com>
This commit is contained in:
Carlton Gibson 2019-04-30 17:53:44 +02:00 committed by GitHub
parent 5c992baf32
commit 0407a0df8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 473 additions and 795 deletions

View File

@ -4,7 +4,6 @@ dist: xenial
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
- { python: "2.7", env: DJANGO=1.11 }
- { python: "3.4", env: DJANGO=1.11 } - { python: "3.4", env: DJANGO=1.11 }
- { python: "3.4", env: DJANGO=2.0 } - { python: "3.4", env: DJANGO=2.0 }
@ -26,8 +25,8 @@ matrix:
- { python: "3.7", env: DJANGO=master } - { python: "3.7", env: DJANGO=master }
- { python: "3.7", env: TOXENV=base } - { python: "3.7", env: TOXENV=base }
- { python: "2.7", env: TOXENV=lint } - { python: "3.7", env: TOXENV=lint }
- { python: "2.7", env: TOXENV=docs } - { python: "3.7", env: TOXENV=docs }
- python: "3.7" - python: "3.7"
env: TOXENV=dist env: TOXENV=dist

View File

@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements # Requirements
* Python (2.7, 3.4, 3.5, 3.6, 3.7) * Python (3.4, 3.5, 3.6, 3.7)
* Django (1.11, 2.0, 2.1, 2.2) * Django (1.11, 2.0, 2.1, 2.2)
We **highly recommend** and only officially support the latest patch release of We **highly recommend** and only officially support the latest patch release of

View File

@ -629,7 +629,7 @@ Our `ColorField` class above currently does not perform any data validation.
To indicate invalid data, we should raise a `serializers.ValidationError`, like so: To indicate invalid data, we should raise a `serializers.ValidationError`, like so:
def to_internal_value(self, data): def to_internal_value(self, data):
if not isinstance(data, six.text_type): if not isinstance(data, str):
msg = 'Incorrect type. Expected a string, but got %s' msg = 'Incorrect type. Expected a string, but got %s'
raise ValidationError(msg % type(data).__name__) raise ValidationError(msg % type(data).__name__)
@ -653,7 +653,7 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me
} }
def to_internal_value(self, data): def to_internal_value(self, data):
if not isinstance(data, six.text_type): if not isinstance(data, str):
self.fail('incorrect_type', input_type=type(data).__name__) self.fail('incorrect_type', input_type=type(data).__name__)
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data): if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):

View File

@ -1,14 +1,11 @@
""" """
Provides various authentication policies. Provides various authentication policies.
""" """
from __future__ import unicode_literals
import base64 import base64
import binascii import binascii
from django.contrib.auth import authenticate, get_user_model from django.contrib.auth import authenticate, get_user_model
from django.middleware.csrf import CsrfViewMiddleware from django.middleware.csrf import CsrfViewMiddleware
from django.utils.six import text_type
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import HTTP_HEADER_ENCODING, exceptions from rest_framework import HTTP_HEADER_ENCODING, exceptions
@ -21,7 +18,7 @@ def get_authorization_header(request):
Hide some test client ickyness where the header can be unicode. Hide some test client ickyness where the header can be unicode.
""" """
auth = request.META.get('HTTP_AUTHORIZATION', b'') auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, text_type): if isinstance(auth, str):
# Work around django test client oddness # Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING) auth = auth.encode(HTTP_HEADER_ENCODING)
return auth return auth
@ -33,7 +30,7 @@ class CSRFCheck(CsrfViewMiddleware):
return reason return reason
class BaseAuthentication(object): class BaseAuthentication:
""" """
All authentication classes should extend BaseAuthentication. All authentication classes should extend BaseAuthentication.
""" """

View File

@ -38,8 +38,8 @@ class Command(BaseCommand):
token = self.create_user_token(username, reset_token) token = self.create_user_token(username, reset_token)
except UserModel.DoesNotExist: except UserModel.DoesNotExist:
raise CommandError( raise CommandError(
'Cannot create the Token: user {0} does not exist'.format( 'Cannot create the Token: user {} does not exist'.format(
username) username)
) )
self.stdout.write( self.stdout.write(
'Generated token {0} for user {1}'.format(token.key, username)) 'Generated token {} for user {}'.format(token.key, username))

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -3,11 +3,9 @@ import os
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@python_2_unicode_compatible
class Token(models.Model): class Token(models.Model):
""" """
The default authorization token model. The default authorization token model.
@ -32,7 +30,7 @@ class Token(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.key: if not self.key:
self.key = self.generate_key() self.key = self.generate_key()
return super(Token, self).save(*args, **kwargs) return super().save(*args, **kwargs)
def generate_key(self): def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode() return binascii.hexlify(os.urandom(20)).decode()

View File

@ -2,23 +2,13 @@
The `compat` module provides support for backwards compatibility with older The `compat` module provides support for backwards compatibility with older
versions of Django/Python, and compatibility wrappers around optional packages. versions of Django/Python, and compatibility wrappers around optional packages.
""" """
from __future__ import unicode_literals
import sys import sys
from collections.abc import Mapping, MutableMapping # noqa
from django.conf import settings from django.conf import settings
from django.core import validators from django.core import validators
from django.utils import six
from django.views.generic import View from django.views.generic import View
try:
# Python 3
from collections.abc import Mapping, MutableMapping # noqa
except ImportError:
# Python 2.7
from collections import Mapping, MutableMapping # noqa
try: try:
from django.urls import ( # noqa from django.urls import ( # noqa
URLPattern, URLPattern,
@ -36,11 +26,6 @@ try:
except ImportError: except ImportError:
ProhibitNullCharactersValidator = None ProhibitNullCharactersValidator = None
try:
from unittest import mock
except ImportError:
mock = None
def get_original_route(urlpattern): def get_original_route(urlpattern):
""" """
@ -89,23 +74,6 @@ def make_url_resolver(regex, urlpatterns):
return URLResolver(regex, urlpatterns) return URLResolver(regex, urlpatterns)
def unicode_repr(instance):
# Get the repr of an instance, but ensure it is a unicode string
# on both python 3 (already the case) and 2 (not the case).
if six.PY2:
return repr(instance).decode('utf-8')
return repr(instance)
def unicode_to_repr(value):
# Coerce a unicode string to the correct repr return type, depending on
# the Python version. We wrap all our `__repr__` implementations with
# this and then use unicode throughout internally.
if six.PY2:
return value.encode('utf-8')
return value
def unicode_http_header(value): def unicode_http_header(value):
# Coerce HTTP header value to unicode. # Coerce HTTP header value to unicode.
if isinstance(value, bytes): if isinstance(value, bytes):
@ -168,15 +136,6 @@ def is_guardian_installed():
""" """
django-guardian is optional and only imported if in INSTALLED_APPS. django-guardian is optional and only imported if in INSTALLED_APPS.
""" """
try:
import guardian
except ImportError:
guardian = None
if six.PY2 and (not guardian or guardian.VERSION >= (1, 5)):
# Guardian 1.5.0, for Django 2.2 is NOT compatible with Python 2.7.
# Remove when dropping PY2.
return False
return 'guardian' in settings.INSTALLED_APPS return 'guardian' in settings.INSTALLED_APPS
@ -289,17 +248,12 @@ except ImportError:
# `separators` argument to `json.dumps()` differs between 2.x and 3.x # `separators` argument to `json.dumps()` differs between 2.x and 3.x
# See: https://bugs.python.org/issue22767 # See: https://bugs.python.org/issue22767
if six.PY3: SHORT_SEPARATORS = (',', ':')
SHORT_SEPARATORS = (',', ':') LONG_SEPARATORS = (', ', ': ')
LONG_SEPARATORS = (', ', ': ') INDENT_SEPARATORS = (',', ': ')
INDENT_SEPARATORS = (',', ': ')
else:
SHORT_SEPARATORS = (b',', b':')
LONG_SEPARATORS = (b', ', b': ')
INDENT_SEPARATORS = (b',', b': ')
class CustomValidatorMessage(object): class CustomValidatorMessage:
""" """
We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`. We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`.
https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297 https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297
@ -309,7 +263,7 @@ class CustomValidatorMessage(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.message = kwargs.pop('message', self.message) self.message = kwargs.pop('message', self.message)
super(CustomValidatorMessage, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
class MinValueValidator(CustomValidatorMessage, validators.MinValueValidator): class MinValueValidator(CustomValidatorMessage, validators.MinValueValidator):

View File

@ -6,13 +6,10 @@ There are also various decorators for setting the API policies on function
based views, as well as the `@detail_route` and `@list_route` decorators, which are based views, as well as the `@detail_route` and `@list_route` decorators, which are
used to annotate methods on viewsets that should be included by routers. used to annotate methods on viewsets that should be included by routers.
""" """
from __future__ import unicode_literals
import types import types
import warnings import warnings
from django.forms.utils import pretty_name from django.forms.utils import pretty_name
from django.utils import six
from rest_framework import RemovedInDRF310Warning from rest_framework import RemovedInDRF310Warning
from rest_framework.views import APIView from rest_framework.views import APIView
@ -28,7 +25,7 @@ def api_view(http_method_names=None):
def decorator(func): def decorator(func):
WrappedAPIView = type( WrappedAPIView = type(
six.PY3 and 'WrappedAPIView' or b'WrappedAPIView', 'WrappedAPIView',
(APIView,), (APIView,),
{'__doc__': func.__doc__} {'__doc__': func.__doc__}
) )

View File

@ -4,18 +4,14 @@ Handled exceptions raised by REST framework.
In addition Django's built in 403 and 404 exceptions are handled. In addition Django's built in 403 and 404 exceptions are handled.
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`) (`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
""" """
from __future__ import unicode_literals
import math import math
from django.http import JsonResponse from django.http import JsonResponse
from django.utils import six
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext from django.utils.translation import ungettext
from rest_framework import status from rest_framework import status
from rest_framework.compat import unicode_to_repr
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
@ -64,19 +60,19 @@ def _get_full_details(detail):
} }
class ErrorDetail(six.text_type): class ErrorDetail(str):
""" """
A string-like object that can additionally have a code. A string-like object that can additionally have a code.
""" """
code = None code = None
def __new__(cls, string, code=None): def __new__(cls, string, code=None):
self = super(ErrorDetail, cls).__new__(cls, string) self = super().__new__(cls, string)
self.code = code self.code = code
return self return self
def __eq__(self, other): def __eq__(self, other):
r = super(ErrorDetail, self).__eq__(other) r = super().__eq__(other)
try: try:
return r and self.code == other.code return r and self.code == other.code
except AttributeError: except AttributeError:
@ -86,10 +82,10 @@ class ErrorDetail(six.text_type):
return not self.__eq__(other) return not self.__eq__(other)
def __repr__(self): def __repr__(self):
return unicode_to_repr('ErrorDetail(string=%r, code=%r)' % ( return 'ErrorDetail(string=%r, code=%r)' % (
six.text_type(self), str(self),
self.code, self.code,
)) )
def __hash__(self): def __hash__(self):
return hash(str(self)) return hash(str(self))
@ -113,7 +109,7 @@ class APIException(Exception):
self.detail = _get_error_details(detail, code) self.detail = _get_error_details(detail, code)
def __str__(self): def __str__(self):
return six.text_type(self.detail) return str(self.detail)
def get_codes(self): def get_codes(self):
""" """
@ -196,7 +192,7 @@ class MethodNotAllowed(APIException):
def __init__(self, method, detail=None, code=None): def __init__(self, method, detail=None, code=None):
if detail is None: if detail is None:
detail = force_text(self.default_detail).format(method=method) detail = force_text(self.default_detail).format(method=method)
super(MethodNotAllowed, self).__init__(detail, code) super().__init__(detail, code)
class NotAcceptable(APIException): class NotAcceptable(APIException):
@ -206,7 +202,7 @@ class NotAcceptable(APIException):
def __init__(self, detail=None, code=None, available_renderers=None): def __init__(self, detail=None, code=None, available_renderers=None):
self.available_renderers = available_renderers self.available_renderers = available_renderers
super(NotAcceptable, self).__init__(detail, code) super().__init__(detail, code)
class UnsupportedMediaType(APIException): class UnsupportedMediaType(APIException):
@ -217,7 +213,7 @@ class UnsupportedMediaType(APIException):
def __init__(self, media_type, detail=None, code=None): def __init__(self, media_type, detail=None, code=None):
if detail is None: if detail is None:
detail = force_text(self.default_detail).format(media_type=media_type) detail = force_text(self.default_detail).format(media_type=media_type)
super(UnsupportedMediaType, self).__init__(detail, code) super().__init__(detail, code)
class Throttled(APIException): class Throttled(APIException):
@ -238,7 +234,7 @@ class Throttled(APIException):
self.extra_detail_plural.format(wait=wait), self.extra_detail_plural.format(wait=wait),
wait)))) wait))))
self.wait = wait self.wait = wait
super(Throttled, self).__init__(detail, code) super().__init__(detail, code)
def server_error(request, *args, **kwargs): def server_error(request, *args, **kwargs):

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import copy import copy
import datetime import datetime
import decimal import decimal
@ -17,7 +15,7 @@ from django.core.validators import (
) )
from django.forms import FilePathField as DjangoFilePathField from django.forms import FilePathField as DjangoFilePathField
from django.forms import ImageField as DjangoImageField from django.forms import ImageField as DjangoImageField
from django.utils import six, timezone from django.utils import timezone
from django.utils.dateparse import ( from django.utils.dateparse import (
parse_date, parse_datetime, parse_duration, parse_time parse_date, parse_datetime, parse_duration, parse_time
) )
@ -33,8 +31,7 @@ from pytz.exceptions import InvalidTimeError
from rest_framework import ISO_8601 from rest_framework import ISO_8601
from rest_framework.compat import ( from rest_framework.compat import (
Mapping, MaxLengthValidator, MaxValueValidator, MinLengthValidator, Mapping, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator, ProhibitNullCharactersValidator, unicode_repr, MinValueValidator, ProhibitNullCharactersValidator
unicode_to_repr
) )
from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -51,39 +48,21 @@ class empty:
pass pass
if six.PY3: def is_simple_callable(obj):
def is_simple_callable(obj): """
""" True if the object is a callable that takes no arguments.
True if the object is a callable that takes no arguments. """
""" if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)): return False
return False
sig = inspect.signature(obj) sig = inspect.signature(obj)
params = sig.parameters.values() params = sig.parameters.values()
return all( return all(
param.kind == param.VAR_POSITIONAL or param.kind == param.VAR_POSITIONAL or
param.kind == param.VAR_KEYWORD or param.kind == param.VAR_KEYWORD or
param.default != param.empty param.default != param.empty
for param in params for param in params
) )
else:
def is_simple_callable(obj):
function = inspect.isfunction(obj)
method = inspect.ismethod(obj)
if not (function or method):
return False
if method:
is_unbound = obj.im_self is None
args, _, _, defaults = inspect.getargspec(obj)
len_args = len(args) if function or is_unbound else len(args) - 1
len_defaults = len(defaults) if defaults else 0
return len_args <= len_defaults
def get_attribute(instance, attrs): def get_attribute(instance, attrs):
@ -108,7 +87,7 @@ def get_attribute(instance, attrs):
# If we raised an Attribute or KeyError here it'd get treated # If we raised an Attribute or KeyError here it'd get treated
# as an omitted field in `Field.get_attribute()`. Instead we # as an omitted field in `Field.get_attribute()`. Instead we
# raise a ValueError to ensure the exception is not masked. # 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 return instance
@ -185,18 +164,18 @@ def iter_options(grouped_choices, cutoff=None, cutoff_text=None):
""" """
Helper function for options and option groups in templates. Helper function for options and option groups in templates.
""" """
class StartOptionGroup(object): class StartOptionGroup:
start_option_group = True start_option_group = True
end_option_group = False end_option_group = False
def __init__(self, label): def __init__(self, label):
self.label = label self.label = label
class EndOptionGroup(object): class EndOptionGroup:
start_option_group = False start_option_group = False
end_option_group = True end_option_group = True
class Option(object): class Option:
start_option_group = False start_option_group = False
end_option_group = False end_option_group = False
@ -251,7 +230,7 @@ def get_error_detail(exc_info):
} }
class CreateOnlyDefault(object): class CreateOnlyDefault:
""" """
This class may be used to provide default values that are only used This class may be used to provide default values that are only used
for create operations, but that do not return any value for update for create operations, but that do not return any value for update
@ -273,12 +252,10 @@ class CreateOnlyDefault(object):
return self.default return self.default
def __repr__(self): def __repr__(self):
return unicode_to_repr( return '%s(%s)' % (self.__class__.__name__, repr(self.default))
'%s(%s)' % (self.__class__.__name__, unicode_repr(self.default))
)
class CurrentUserDefault(object): class CurrentUserDefault:
def set_context(self, serializer_field): def set_context(self, serializer_field):
self.user = serializer_field.context['request'].user self.user = serializer_field.context['request'].user
@ -286,7 +263,7 @@ class CurrentUserDefault(object):
return self.user return self.user
def __repr__(self): def __repr__(self):
return unicode_to_repr('%s()' % self.__class__.__name__) return '%s()' % self.__class__.__name__
class SkipField(Exception): class SkipField(Exception):
@ -305,7 +282,7 @@ MISSING_ERROR_MESSAGE = (
) )
class Field(object): class Field:
_creation_counter = 0 _creation_counter = 0
default_error_messages = { default_error_messages = {
@ -618,7 +595,7 @@ class Field(object):
When a field is instantiated, we store the arguments that were used, When a field is instantiated, we store the arguments that were used,
so that we can present a helpful representation of the object. 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._args = args
instance._kwargs = kwargs instance._kwargs = kwargs
return instance return instance
@ -647,7 +624,7 @@ class Field(object):
This allows us to create descriptive representations for serializer This allows us to create descriptive representations for serializer
instances that show all the declared fields on the serializer. instances that show all the declared fields on the serializer.
""" """
return unicode_to_repr(representation.field_repr(self)) return representation.field_repr(self)
# Boolean types... # Boolean types...
@ -724,7 +701,7 @@ class NullBooleanField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.' assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
kwargs['allow_null'] = True kwargs['allow_null'] = True
super(NullBooleanField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
try: try:
@ -764,17 +741,14 @@ class CharField(Field):
self.trim_whitespace = kwargs.pop('trim_whitespace', True) self.trim_whitespace = kwargs.pop('trim_whitespace', True)
self.max_length = kwargs.pop('max_length', None) self.max_length = kwargs.pop('max_length', None)
self.min_length = kwargs.pop('min_length', None) self.min_length = kwargs.pop('min_length', None)
super(CharField, self).__init__(**kwargs) super().__init__(**kwargs)
if self.max_length is not None: if self.max_length is not None:
message = lazy( message = lazy(self.error_messages['max_length'].format, str)(max_length=self.max_length)
self.error_messages['max_length'].format,
six.text_type)(max_length=self.max_length)
self.validators.append( self.validators.append(
MaxLengthValidator(self.max_length, message=message)) MaxLengthValidator(self.max_length, message=message))
if self.min_length is not None: if self.min_length is not None:
message = lazy( message = lazy(
self.error_messages['min_length'].format, self.error_messages['min_length'].format, str)(min_length=self.min_length)
six.text_type)(min_length=self.min_length)
self.validators.append( self.validators.append(
MinLengthValidator(self.min_length, message=message)) MinLengthValidator(self.min_length, message=message))
@ -786,23 +760,23 @@ class CharField(Field):
# Test for the empty string here so that it does not get validated, # Test for the empty string here so that it does not get validated,
# and so that subclasses do not need to handle it explicitly # and so that subclasses do not need to handle it explicitly
# inside the `to_internal_value()` method. # inside the `to_internal_value()` method.
if data == '' or (self.trim_whitespace and six.text_type(data).strip() == ''): if data == '' or (self.trim_whitespace and str(data).strip() == ''):
if not self.allow_blank: if not self.allow_blank:
self.fail('blank') self.fail('blank')
return '' return ''
return super(CharField, self).run_validation(data) return super().run_validation(data)
def to_internal_value(self, data): def to_internal_value(self, data):
# We're lenient with allowing basic numerics to be coerced into strings, # We're lenient with allowing basic numerics to be coerced into strings,
# but other types should fail. Eg. unclear if booleans should represent as `true` or `True`, # but other types should fail. Eg. unclear if booleans should represent as `true` or `True`,
# and composites such as lists are likely user error. # and composites such as lists are likely user error.
if isinstance(data, bool) or not isinstance(data, six.string_types + six.integer_types + (float,)): if isinstance(data, bool) or not isinstance(data, (str, int, float,)):
self.fail('invalid') self.fail('invalid')
value = six.text_type(data) value = str(data)
return value.strip() if self.trim_whitespace else value return value.strip() if self.trim_whitespace else value
def to_representation(self, value): def to_representation(self, value):
return six.text_type(value) return str(value)
class EmailField(CharField): class EmailField(CharField):
@ -811,7 +785,7 @@ class EmailField(CharField):
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(EmailField, self).__init__(**kwargs) super().__init__(**kwargs)
validator = EmailValidator(message=self.error_messages['invalid']) validator = EmailValidator(message=self.error_messages['invalid'])
self.validators.append(validator) self.validators.append(validator)
@ -822,7 +796,7 @@ class RegexField(CharField):
} }
def __init__(self, regex, **kwargs): def __init__(self, regex, **kwargs):
super(RegexField, self).__init__(**kwargs) super().__init__(**kwargs)
validator = RegexValidator(regex, message=self.error_messages['invalid']) validator = RegexValidator(regex, message=self.error_messages['invalid'])
self.validators.append(validator) self.validators.append(validator)
@ -834,7 +808,7 @@ class SlugField(CharField):
} }
def __init__(self, allow_unicode=False, **kwargs): def __init__(self, allow_unicode=False, **kwargs):
super(SlugField, self).__init__(**kwargs) super().__init__(**kwargs)
self.allow_unicode = allow_unicode self.allow_unicode = allow_unicode
if self.allow_unicode: if self.allow_unicode:
validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode']) validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode'])
@ -849,7 +823,7 @@ class URLField(CharField):
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(URLField, self).__init__(**kwargs) super().__init__(**kwargs)
validator = URLValidator(message=self.error_messages['invalid']) validator = URLValidator(message=self.error_messages['invalid'])
self.validators.append(validator) self.validators.append(validator)
@ -866,16 +840,16 @@ class UUIDField(Field):
if self.uuid_format not in self.valid_formats: if self.uuid_format not in self.valid_formats:
raise ValueError( raise ValueError(
'Invalid format for uuid representation. ' '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(UUIDField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
if not isinstance(data, uuid.UUID): if not isinstance(data, uuid.UUID):
try: try:
if isinstance(data, six.integer_types): if isinstance(data, int):
return uuid.UUID(int=data) return uuid.UUID(int=data)
elif isinstance(data, six.string_types): elif isinstance(data, str):
return uuid.UUID(hex=data) return uuid.UUID(hex=data)
else: else:
self.fail('invalid', value=data) self.fail('invalid', value=data)
@ -900,12 +874,12 @@ class IPAddressField(CharField):
def __init__(self, protocol='both', **kwargs): def __init__(self, protocol='both', **kwargs):
self.protocol = protocol.lower() self.protocol = protocol.lower()
self.unpack_ipv4 = (self.protocol == 'both') self.unpack_ipv4 = (self.protocol == 'both')
super(IPAddressField, self).__init__(**kwargs) super().__init__(**kwargs)
validators, error_message = ip_address_validators(protocol, self.unpack_ipv4) validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
self.validators.extend(validators) self.validators.extend(validators)
def to_internal_value(self, data): def to_internal_value(self, data):
if not isinstance(data, six.string_types): if not isinstance(data, str):
self.fail('invalid', value=data) self.fail('invalid', value=data)
if ':' in data: if ':' in data:
@ -915,7 +889,7 @@ class IPAddressField(CharField):
except DjangoValidationError: except DjangoValidationError:
self.fail('invalid', value=data) self.fail('invalid', value=data)
return super(IPAddressField, self).to_internal_value(data) return super().to_internal_value(data)
# Number types... # Number types...
@ -933,22 +907,20 @@ class IntegerField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super(IntegerField, self).__init__(**kwargs) super().__init__(**kwargs)
if self.max_value is not None: if self.max_value is not None:
message = lazy( message = lazy(
self.error_messages['max_value'].format, self.error_messages['max_value'].format, str)(max_value=self.max_value)
six.text_type)(max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message))
if self.min_value is not None: if self.min_value is not None:
message = lazy( message = lazy(
self.error_messages['min_value'].format, self.error_messages['min_value'].format, str)(min_value=self.min_value)
six.text_type)(min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message))
def to_internal_value(self, data): def to_internal_value(self, data):
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH: if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
self.fail('max_string_length') self.fail('max_string_length')
try: try:
@ -973,23 +945,23 @@ class FloatField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super(FloatField, self).__init__(**kwargs) super().__init__(**kwargs)
if self.max_value is not None: if self.max_value is not None:
message = lazy( message = lazy(
self.error_messages['max_value'].format, self.error_messages['max_value'].format,
six.text_type)(max_value=self.max_value) str)(max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message))
if self.min_value is not None: if self.min_value is not None:
message = lazy( message = lazy(
self.error_messages['min_value'].format, self.error_messages['min_value'].format,
six.text_type)(min_value=self.min_value) str)(min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message))
def to_internal_value(self, data): def to_internal_value(self, data):
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH: if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
self.fail('max_string_length') self.fail('max_string_length')
try: try:
@ -1031,18 +1003,17 @@ class DecimalField(Field):
else: else:
self.max_whole_digits = None self.max_whole_digits = None
super(DecimalField, self).__init__(**kwargs) super().__init__(**kwargs)
if self.max_value is not None: if self.max_value is not None:
message = lazy( message = lazy(
self.error_messages['max_value'].format, self.error_messages['max_value'].format,
six.text_type)(max_value=self.max_value) str)(max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message))
if self.min_value is not None: if self.min_value is not None:
message = lazy( message = lazy(
self.error_messages['min_value'].format, self.error_messages['min_value'].format, str)(min_value=self.min_value)
six.text_type)(min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message))
@ -1121,7 +1092,7 @@ class DecimalField(Field):
coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING) coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING)
if not isinstance(value, decimal.Decimal): if not isinstance(value, decimal.Decimal):
value = decimal.Decimal(six.text_type(value).strip()) value = decimal.Decimal(str(value).strip())
quantized = self.quantize(value) quantized = self.quantize(value)
@ -1130,7 +1101,7 @@ class DecimalField(Field):
if self.localize: if self.localize:
return localize_input(quantized) return localize_input(quantized)
return '{0:f}'.format(quantized) return '{:f}'.format(quantized)
def quantize(self, value): def quantize(self, value):
""" """
@ -1167,7 +1138,7 @@ class DateTimeField(Field):
self.input_formats = input_formats self.input_formats = input_formats
if default_timezone is not None: if default_timezone is not None:
self.timezone = default_timezone self.timezone = default_timezone
super(DateTimeField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def enforce_timezone(self, value): def enforce_timezone(self, value):
""" """
@ -1226,7 +1197,7 @@ class DateTimeField(Field):
output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT) output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT)
if output_format is None or isinstance(value, six.string_types): if output_format is None or isinstance(value, str):
return value return value
value = self.enforce_timezone(value) value = self.enforce_timezone(value)
@ -1251,7 +1222,7 @@ class DateField(Field):
self.format = format self.format = format
if input_formats is not None: if input_formats is not None:
self.input_formats = input_formats self.input_formats = input_formats
super(DateField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_internal_value(self, value): def to_internal_value(self, value):
input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS) input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
@ -1288,7 +1259,7 @@ class DateField(Field):
output_format = getattr(self, 'format', api_settings.DATE_FORMAT) output_format = getattr(self, 'format', api_settings.DATE_FORMAT)
if output_format is None or isinstance(value, six.string_types): if output_format is None or isinstance(value, str):
return value return value
# Applying a `DateField` to a datetime value is almost always # Applying a `DateField` to a datetime value is almost always
@ -1317,7 +1288,7 @@ class TimeField(Field):
self.format = format self.format = format
if input_formats is not None: if input_formats is not None:
self.input_formats = input_formats self.input_formats = input_formats
super(TimeField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_internal_value(self, value): def to_internal_value(self, value):
input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS) input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS)
@ -1351,7 +1322,7 @@ class TimeField(Field):
output_format = getattr(self, 'format', api_settings.TIME_FORMAT) output_format = getattr(self, 'format', api_settings.TIME_FORMAT)
if output_format is None or isinstance(value, six.string_types): if output_format is None or isinstance(value, str):
return value return value
# Applying a `TimeField` to a datetime value is almost always # Applying a `TimeField` to a datetime value is almost always
@ -1378,24 +1349,24 @@ class DurationField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super(DurationField, self).__init__(**kwargs) super().__init__(**kwargs)
if self.max_value is not None: if self.max_value is not None:
message = lazy( message = lazy(
self.error_messages['max_value'].format, self.error_messages['max_value'].format,
six.text_type)(max_value=self.max_value) str)(max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message))
if self.min_value is not None: if self.min_value is not None:
message = lazy( message = lazy(
self.error_messages['min_value'].format, self.error_messages['min_value'].format,
six.text_type)(min_value=self.min_value) str)(min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message))
def to_internal_value(self, value): def to_internal_value(self, value):
if isinstance(value, datetime.timedelta): if isinstance(value, datetime.timedelta):
return value return value
parsed = parse_duration(six.text_type(value)) parsed = parse_duration(str(value))
if parsed is not None: if parsed is not None:
return parsed return parsed
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]') self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
@ -1420,21 +1391,21 @@ class ChoiceField(Field):
self.allow_blank = kwargs.pop('allow_blank', False) self.allow_blank = kwargs.pop('allow_blank', False)
super(ChoiceField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
if data == '' and self.allow_blank: if data == '' and self.allow_blank:
return '' return ''
try: try:
return self.choice_strings_to_values[six.text_type(data)] return self.choice_strings_to_values[str(data)]
except KeyError: except KeyError:
self.fail('invalid_choice', input=data) self.fail('invalid_choice', input=data)
def to_representation(self, value): def to_representation(self, value):
if value in ('', None): if value in ('', None):
return value return value
return self.choice_strings_to_values.get(six.text_type(value), value) return self.choice_strings_to_values.get(str(value), value)
def iter_options(self): def iter_options(self):
""" """
@ -1457,7 +1428,7 @@ class ChoiceField(Field):
# Allows us to deal with eg. integer choices while supporting either # Allows us to deal with eg. integer choices while supporting either
# integer or string input, but still get the correct datatype out. # integer or string input, but still get the correct datatype out.
self.choice_strings_to_values = { self.choice_strings_to_values = {
six.text_type(key): key for key in self.choices str(key): key for key in self.choices
} }
choices = property(_get_choices, _set_choices) choices = property(_get_choices, _set_choices)
@ -1473,7 +1444,7 @@ class MultipleChoiceField(ChoiceField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.allow_empty = kwargs.pop('allow_empty', True) self.allow_empty = kwargs.pop('allow_empty', True)
super(MultipleChoiceField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_value(self, dictionary): def get_value(self, dictionary):
if self.field_name not in dictionary: if self.field_name not in dictionary:
@ -1486,7 +1457,7 @@ class MultipleChoiceField(ChoiceField):
return dictionary.get(self.field_name, empty) return dictionary.get(self.field_name, empty)
def to_internal_value(self, data): def to_internal_value(self, data):
if isinstance(data, six.text_type) or not hasattr(data, '__iter__'): if isinstance(data, str) or not hasattr(data, '__iter__'):
self.fail('not_a_list', input_type=type(data).__name__) self.fail('not_a_list', input_type=type(data).__name__)
if not self.allow_empty and len(data) == 0: if not self.allow_empty and len(data) == 0:
self.fail('empty') self.fail('empty')
@ -1498,7 +1469,7 @@ class MultipleChoiceField(ChoiceField):
def to_representation(self, value): def to_representation(self, value):
return { return {
self.choice_strings_to_values.get(six.text_type(item), item) for item in value self.choice_strings_to_values.get(str(item), item) for item in value
} }
@ -1516,7 +1487,7 @@ class FilePathField(ChoiceField):
allow_folders=allow_folders, required=required allow_folders=allow_folders, required=required
) )
kwargs['choices'] = field.choices kwargs['choices'] = field.choices
super(FilePathField, self).__init__(**kwargs) super().__init__(**kwargs)
# File types... # File types...
@ -1535,7 +1506,7 @@ class FileField(Field):
self.allow_empty_file = kwargs.pop('allow_empty_file', False) self.allow_empty_file = kwargs.pop('allow_empty_file', False)
if 'use_url' in kwargs: if 'use_url' in kwargs:
self.use_url = kwargs.pop('use_url') self.use_url = kwargs.pop('use_url')
super(FileField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
try: try:
@ -1581,13 +1552,13 @@ class ImageField(FileField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField) self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField)
super(ImageField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
# Image validation is a bit grungy, so we'll just outright # Image validation is a bit grungy, so we'll just outright
# defer to Django's implementation so we don't need to # defer to Django's implementation so we don't need to
# consider it, or treat PIL as a test dependency. # consider it, or treat PIL as a test dependency.
file_object = super(ImageField, self).to_internal_value(data) file_object = super().to_internal_value(data)
django_field = self._DjangoImageField() django_field = self._DjangoImageField()
django_field.error_messages = self.error_messages django_field.error_messages = self.error_messages
return django_field.clean(file_object) return django_field.clean(file_object)
@ -1597,7 +1568,7 @@ class ImageField(FileField):
class _UnvalidatedField(Field): class _UnvalidatedField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(_UnvalidatedField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.allow_blank = True self.allow_blank = True
self.allow_null = True self.allow_null = True
@ -1630,7 +1601,7 @@ class ListField(Field):
"Remove `source=` from the field declaration." "Remove `source=` from the field declaration."
) )
super(ListField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self) self.child.bind(field_name='', parent=self)
if self.max_length is not None: if self.max_length is not None:
message = self.error_messages['max_length'].format(max_length=self.max_length) message = self.error_messages['max_length'].format(max_length=self.max_length)
@ -1660,7 +1631,7 @@ class ListField(Field):
""" """
if html.is_html_input(data): if html.is_html_input(data):
data = html.parse_html_list(data, default=[]) data = html.parse_html_list(data, default=[])
if isinstance(data, (six.text_type, Mapping)) or not hasattr(data, '__iter__'): if isinstance(data, (str, Mapping)) or not hasattr(data, '__iter__'):
self.fail('not_a_list', input_type=type(data).__name__) self.fail('not_a_list', input_type=type(data).__name__)
if not self.allow_empty and len(data) == 0: if not self.allow_empty and len(data) == 0:
self.fail('empty') self.fail('empty')
@ -1703,7 +1674,7 @@ class DictField(Field):
"Remove `source=` from the field declaration." "Remove `source=` from the field declaration."
) )
super(DictField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self) self.child.bind(field_name='', parent=self)
def get_value(self, dictionary): def get_value(self, dictionary):
@ -1725,7 +1696,7 @@ class DictField(Field):
def to_representation(self, value): def to_representation(self, value):
return { return {
six.text_type(key): self.child.to_representation(val) if val is not None else None str(key): self.child.to_representation(val) if val is not None else None
for key, val in value.items() for key, val in value.items()
} }
@ -1734,7 +1705,7 @@ class DictField(Field):
errors = OrderedDict() errors = OrderedDict()
for key, value in data.items(): for key, value in data.items():
key = six.text_type(key) key = str(key)
try: try:
result[key] = self.child.run_validation(value) result[key] = self.child.run_validation(value)
@ -1750,7 +1721,7 @@ class HStoreField(DictField):
child = CharField(allow_blank=True, allow_null=True) child = CharField(allow_blank=True, allow_null=True)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(HStoreField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
assert isinstance(self.child, CharField), ( assert isinstance(self.child, CharField), (
"The `child` argument must be an instance of `CharField`, " "The `child` argument must be an instance of `CharField`, "
"as the hstore extension stores values as strings." "as the hstore extension stores values as strings."
@ -1764,15 +1735,15 @@ class JSONField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.binary = kwargs.pop('binary', False) self.binary = kwargs.pop('binary', False)
super(JSONField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_value(self, dictionary): def get_value(self, dictionary):
if html.is_html_input(dictionary) and self.field_name in dictionary: if html.is_html_input(dictionary) and self.field_name in dictionary:
# When HTML form input is used, mark up the input # When HTML form input is used, mark up the input
# as being a JSON string, rather than a JSON primitive. # as being a JSON string, rather than a JSON primitive.
class JSONString(six.text_type): class JSONString(str):
def __new__(self, value): def __new__(self, value):
ret = six.text_type.__new__(self, value) ret = str.__new__(self, value)
ret.is_json_string = True ret.is_json_string = True
return ret return ret
return JSONString(dictionary[self.field_name]) return JSONString(dictionary[self.field_name])
@ -1795,7 +1766,7 @@ class JSONField(Field):
value = json.dumps(value) value = json.dumps(value)
# On python 2.x the return type for json.dumps() is underspecified. # On python 2.x the return type for json.dumps() is underspecified.
# On python 3.x json.dumps() returns unicode strings. # On python 3.x json.dumps() returns unicode strings.
if isinstance(value, six.text_type): if isinstance(value, str):
value = bytes(value.encode('utf-8')) value = bytes(value.encode('utf-8'))
return value return value
@ -1817,7 +1788,7 @@ class ReadOnlyField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
kwargs['read_only'] = True kwargs['read_only'] = True
super(ReadOnlyField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_representation(self, value): def to_representation(self, value):
return value return value
@ -1834,7 +1805,7 @@ class HiddenField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
assert 'default' in kwargs, 'default is a required argument.' assert 'default' in kwargs, 'default is a required argument.'
kwargs['write_only'] = True kwargs['write_only'] = True
super(HiddenField, self).__init__(**kwargs) super().__init__(**kwargs)
def get_value(self, dictionary): def get_value(self, dictionary):
# We always use the default value for `HiddenField`. # We always use the default value for `HiddenField`.
@ -1864,7 +1835,7 @@ class SerializerMethodField(Field):
self.method_name = method_name self.method_name = method_name
kwargs['source'] = '*' kwargs['source'] = '*'
kwargs['read_only'] = True kwargs['read_only'] = True
super(SerializerMethodField, self).__init__(**kwargs) super().__init__(**kwargs)
def bind(self, field_name, parent): def bind(self, field_name, parent):
# In order to enforce a consistent style, we error if a redundant # In order to enforce a consistent style, we error if a redundant
@ -1882,7 +1853,7 @@ class SerializerMethodField(Field):
if self.method_name is None: if self.method_name is None:
self.method_name = default_method_name self.method_name = default_method_name
super(SerializerMethodField, self).bind(field_name, parent) super().bind(field_name, parent)
def to_representation(self, value): def to_representation(self, value):
method = getattr(self.parent, self.method_name) method = getattr(self.parent, self.method_name)
@ -1905,11 +1876,10 @@ class ModelField(Field):
# The `max_length` option is supported by Django's base `Field` class, # The `max_length` option is supported by Django's base `Field` class,
# so we'd better support it here. # so we'd better support it here.
max_length = kwargs.pop('max_length', None) max_length = kwargs.pop('max_length', None)
super(ModelField, self).__init__(**kwargs) super().__init__(**kwargs)
if max_length is not None: if max_length is not None:
message = lazy( message = lazy(
self.error_messages['max_length'].format, self.error_messages['max_length'].format, str)(max_length=self.max_length)
six.text_type)(max_length=self.max_length)
self.validators.append( self.validators.append(
MaxLengthValidator(self.max_length, message=message)) MaxLengthValidator(self.max_length, message=message))

View File

@ -2,8 +2,6 @@
Provides generic filtering backends that can be used to filter the results Provides generic filtering backends that can be used to filter the results
returned by list views. returned by list views.
""" """
from __future__ import unicode_literals
import operator import operator
import warnings import warnings
from functools import reduce from functools import reduce
@ -13,7 +11,6 @@ from django.db import models
from django.db.models.constants import LOOKUP_SEP from django.db.models.constants import LOOKUP_SEP
from django.db.models.sql.constants import ORDER_PATTERN from django.db.models.sql.constants import ORDER_PATTERN
from django.template import loader from django.template import loader
from django.utils import six
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -24,7 +21,7 @@ from rest_framework.compat import (
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
class BaseFilterBackend(object): class BaseFilterBackend:
""" """
A base class from which all filter backend classes should inherit. A base class from which all filter backend classes should inherit.
""" """
@ -109,7 +106,7 @@ class SearchFilter(BaseFilterBackend):
return queryset return queryset
orm_lookups = [ orm_lookups = [
self.construct_search(six.text_type(search_field)) self.construct_search(str(search_field))
for search_field in search_fields for search_field in search_fields
] ]
@ -188,7 +185,7 @@ class OrderingFilter(BaseFilterBackend):
def get_default_ordering(self, view): def get_default_ordering(self, view):
ordering = getattr(view, 'ordering', None) ordering = getattr(view, 'ordering', None)
if isinstance(ordering, six.string_types): if isinstance(ordering, str):
return (ordering,) return (ordering,)
return ordering return ordering
@ -237,7 +234,7 @@ class OrderingFilter(BaseFilterBackend):
] ]
else: else:
valid_fields = [ valid_fields = [
(item, item) if isinstance(item, six.string_types) else item (item, item) if isinstance(item, str) else item
for item in valid_fields for item in valid_fields
] ]

View File

@ -1,8 +1,6 @@
""" """
Generic views that provide commonly needed behaviour. Generic views that provide commonly needed behaviour.
""" """
from __future__ import unicode_literals
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.http import Http404 from django.http import Http404

View File

@ -6,8 +6,6 @@ some fairly ad-hoc information about the view.
Future implementations might use JSON schema or other definitions in order Future implementations might use JSON schema or other definitions in order
to return this information in a more standardized way. to return this information in a more standardized way.
""" """
from __future__ import unicode_literals
from collections import OrderedDict from collections import OrderedDict
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@ -19,7 +17,7 @@ from rest_framework.request import clone_request
from rest_framework.utils.field_mapping import ClassLookupDict from rest_framework.utils.field_mapping import ClassLookupDict
class BaseMetadata(object): class BaseMetadata:
def determine_metadata(self, request, view): def determine_metadata(self, request, view):
""" """
Return a dictionary of metadata about the view. Return a dictionary of metadata about the view.

View File

@ -4,14 +4,12 @@ Basic building blocks for generic class based views.
We don't bind behaviour to http method handlers yet, We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways. which allows mixin classes to be composed in interesting ways.
""" """
from __future__ import unicode_literals
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
class CreateModelMixin(object): class CreateModelMixin:
""" """
Create a model instance. Create a model instance.
""" """
@ -32,7 +30,7 @@ class CreateModelMixin(object):
return {} return {}
class ListModelMixin(object): class ListModelMixin:
""" """
List a queryset. List a queryset.
""" """
@ -48,7 +46,7 @@ class ListModelMixin(object):
return Response(serializer.data) return Response(serializer.data)
class RetrieveModelMixin(object): class RetrieveModelMixin:
""" """
Retrieve a model instance. Retrieve a model instance.
""" """
@ -58,7 +56,7 @@ class RetrieveModelMixin(object):
return Response(serializer.data) return Response(serializer.data)
class UpdateModelMixin(object): class UpdateModelMixin:
""" """
Update a model instance. Update a model instance.
""" """
@ -84,7 +82,7 @@ class UpdateModelMixin(object):
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
class DestroyModelMixin(object): class DestroyModelMixin:
""" """
Destroy a model instance. Destroy a model instance.
""" """

View File

@ -2,8 +2,6 @@
Content negotiation deals with selecting an appropriate renderer given the Content negotiation deals with selecting an appropriate renderer given the
incoming request. Typically this will be based on the request's Accept header. incoming request. Typically this will be based on the request's Accept header.
""" """
from __future__ import unicode_literals
from django.http import Http404 from django.http import Http404
from rest_framework import HTTP_HEADER_ENCODING, exceptions from rest_framework import HTTP_HEADER_ENCODING, exceptions
@ -13,7 +11,7 @@ from rest_framework.utils.mediatypes import (
) )
class BaseContentNegotiation(object): class BaseContentNegotiation:
def select_parser(self, request, parsers): def select_parser(self, request, parsers):
raise NotImplementedError('.select_parser() must be implemented') raise NotImplementedError('.select_parser() must be implemented')
@ -66,7 +64,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
# Accepted media type is 'application/json' # Accepted media type is 'application/json'
full_media_type = ';'.join( full_media_type = ';'.join(
(renderer.media_type,) + (renderer.media_type,) +
tuple('{0}={1}'.format( tuple('{}={}'.format(
key, value.decode(HTTP_HEADER_ENCODING)) key, value.decode(HTTP_HEADER_ENCODING))
for key, value in media_type_wrapper.params.items())) for key, value in media_type_wrapper.params.items()))
return renderer, full_media_type return renderer, full_media_type

View File

@ -1,19 +1,15 @@
# coding: utf-8
""" """
Pagination serializers determine the structure of the output that should Pagination serializers determine the structure of the output that should
be used for paginated responses. be used for paginated responses.
""" """
from __future__ import unicode_literals
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
from urllib import parse
from django.core.paginator import InvalidPage from django.core.paginator import InvalidPage
from django.core.paginator import Paginator as DjangoPaginator from django.core.paginator import Paginator as DjangoPaginator
from django.template import loader from django.template import loader
from django.utils import six
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import coreapi, coreschema from rest_framework.compat import coreapi, coreschema
@ -133,7 +129,7 @@ PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break'])
PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True) PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True)
class BasePagination(object): class BasePagination:
display_page_controls = False display_page_controls = False
def paginate_queryset(self, queryset, request, view=None): # pragma: no cover def paginate_queryset(self, queryset, request, view=None): # pragma: no cover
@ -204,7 +200,7 @@ class PageNumberPagination(BasePagination):
self.page = paginator.page(page_number) self.page = paginator.page(page_number)
except InvalidPage as exc: except InvalidPage as exc:
msg = self.invalid_page_message.format( msg = self.invalid_page_message.format(
page_number=page_number, message=six.text_type(exc) page_number=page_number, message=str(exc)
) )
raise NotFound(msg) raise NotFound(msg)
@ -716,13 +712,13 @@ class CursorPagination(BasePagination):
'nearly-unique field on the model, such as "-created" or "pk".' 'nearly-unique field on the model, such as "-created" or "pk".'
) )
assert isinstance(ordering, (six.string_types, list, tuple)), ( assert isinstance(ordering, (str, list, tuple)), (
'Invalid ordering. Expected string or tuple, but got {type}'.format( 'Invalid ordering. Expected string or tuple, but got {type}'.format(
type=type(ordering).__name__ type=type(ordering).__name__
) )
) )
if isinstance(ordering, six.string_types): if isinstance(ordering, str):
return (ordering,) return (ordering,)
return tuple(ordering) return tuple(ordering)
@ -737,7 +733,7 @@ class CursorPagination(BasePagination):
try: try:
querystring = b64decode(encoded.encode('ascii')).decode('ascii') querystring = b64decode(encoded.encode('ascii')).decode('ascii')
tokens = urlparse.parse_qs(querystring, keep_blank_values=True) tokens = parse.parse_qs(querystring, keep_blank_values=True)
offset = tokens.get('o', ['0'])[0] offset = tokens.get('o', ['0'])[0]
offset = _positive_int(offset, cutoff=self.offset_cutoff) offset = _positive_int(offset, cutoff=self.offset_cutoff)
@ -763,7 +759,7 @@ class CursorPagination(BasePagination):
if cursor.position is not None: if cursor.position is not None:
tokens['p'] = cursor.position tokens['p'] = cursor.position
querystring = urlparse.urlencode(tokens, doseq=True) querystring = parse.urlencode(tokens, doseq=True)
encoded = b64encode(querystring.encode('ascii')).decode('ascii') encoded = b64encode(querystring.encode('ascii')).decode('ascii')
return replace_query_param(self.base_url, self.cursor_query_param, encoded) return replace_query_param(self.base_url, self.cursor_query_param, encoded)
@ -773,7 +769,7 @@ class CursorPagination(BasePagination):
attr = instance[field_name] attr = instance[field_name]
else: else:
attr = getattr(instance, field_name) attr = getattr(instance, field_name)
return six.text_type(attr) return str(attr)
def get_paginated_response(self, data): def get_paginated_response(self, data):
return Response(OrderedDict([ return Response(OrderedDict([

View File

@ -4,9 +4,8 @@ Parsers are used to parse the content of incoming HTTP requests.
They give us a generic way of being able to handle various media types They give us a generic way of being able to handle various media types
on the request, such as form content or json encoded data. on the request, such as form content or json encoded data.
""" """
from __future__ import unicode_literals
import codecs import codecs
from urllib import parse
from django.conf import settings from django.conf import settings
from django.core.files.uploadhandler import StopFutureHandlers from django.core.files.uploadhandler import StopFutureHandlers
@ -15,9 +14,7 @@ from django.http.multipartparser import ChunkIter
from django.http.multipartparser import \ from django.http.multipartparser import \
MultiPartParser as DjangoMultiPartParser MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError, parse_header from django.http.multipartparser import MultiPartParserError, parse_header
from django.utils import six
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.six.moves.urllib import parse as urlparse
from rest_framework import renderers from rest_framework import renderers
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
@ -25,13 +22,13 @@ from rest_framework.settings import api_settings
from rest_framework.utils import json from rest_framework.utils import json
class DataAndFiles(object): class DataAndFiles:
def __init__(self, data, files): def __init__(self, data, files):
self.data = data self.data = data
self.files = files self.files = files
class BaseParser(object): class BaseParser:
""" """
All parsers should extend `BaseParser`, specifying a `media_type` All parsers should extend `BaseParser`, specifying a `media_type`
attribute, and overriding the `.parse()` method. attribute, and overriding the `.parse()` method.
@ -67,7 +64,7 @@ class JSONParser(BaseParser):
parse_constant = json.strict_constant if self.strict else None parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant) return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc: except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc)) raise ParseError('JSON parse error - %s' % str(exc))
class FormParser(BaseParser): class FormParser(BaseParser):
@ -113,7 +110,7 @@ class MultiPartParser(BaseParser):
data, files = parser.parse() data, files = parser.parse()
return DataAndFiles(data, files) return DataAndFiles(data, files)
except MultiPartParserError as exc: except MultiPartParserError as exc:
raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) raise ParseError('Multipart form parse error - %s' % str(exc))
class FileUploadParser(BaseParser): class FileUploadParser(BaseParser):
@ -221,7 +218,7 @@ class FileUploadParser(BaseParser):
encoded_filename = force_text(filename_parm['filename*']) encoded_filename = force_text(filename_parm['filename*'])
try: try:
charset, lang, filename = encoded_filename.split('\'', 2) charset, lang, filename = encoded_filename.split('\'', 2)
filename = urlparse.unquote(filename) filename = parse.unquote(filename)
except (ValueError, LookupError): except (ValueError, LookupError):
filename = force_text(filename_parm['filename']) filename = force_text(filename_parm['filename'])
return filename return filename

View File

@ -1,10 +1,7 @@
""" """
Provides a set of pluggable permission policies. Provides a set of pluggable permission policies.
""" """
from __future__ import unicode_literals
from django.http import Http404 from django.http import Http404
from django.utils import six
from rest_framework import exceptions from rest_framework import exceptions
@ -101,8 +98,7 @@ class BasePermissionMetaclass(OperationHolderMixin, type):
pass pass
@six.add_metaclass(BasePermissionMetaclass) class BasePermission(metaclass=BasePermissionMetaclass):
class BasePermission(object):
""" """
A base class from which all permission classes should inherit. A base class from which all permission classes should inherit.
""" """

View File

@ -1,18 +1,12 @@
# coding: utf-8
from __future__ import unicode_literals
import sys import sys
from collections import OrderedDict from collections import OrderedDict
from urllib import parse
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.db.models import Manager from django.db.models import Manager
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve
from django.utils import six from django.utils.encoding import smart_text, uri_to_iri
from django.utils.encoding import (
python_2_unicode_compatible, smart_text, uri_to_iri
)
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.fields import ( from rest_framework.fields import (
@ -46,14 +40,14 @@ class ObjectTypeError(TypeError):
""" """
class Hyperlink(six.text_type): class Hyperlink(str):
""" """
A string like object that additionally has an associated name. A string like object that additionally has an associated name.
We use this for hyperlinked URLs that may render as a named link We use this for hyperlinked URLs that may render as a named link
in some contexts, or render as a plain URL in others. in some contexts, or render as a plain URL in others.
""" """
def __new__(self, url, obj): def __new__(self, url, obj):
ret = six.text_type.__new__(self, url) ret = str.__new__(self, url)
ret.obj = obj ret.obj = obj
return ret return ret
@ -65,13 +59,12 @@ class Hyperlink(six.text_type):
# This ensures that we only called `__str__` lazily, # This ensures that we only called `__str__` lazily,
# as in some cases calling __str__ on a model instances *might* # as in some cases calling __str__ on a model instances *might*
# involve a database lookup. # involve a database lookup.
return six.text_type(self.obj) return str(self.obj)
is_hyperlink = True is_hyperlink = True
@python_2_unicode_compatible class PKOnlyObject:
class PKOnlyObject(object):
""" """
This is a mock object, used for when we only need the pk of the object This is a mock object, used for when we only need the pk of the object
instance, but still want to return an object with a .pk attribute, instance, but still want to return an object with a .pk attribute,
@ -121,14 +114,14 @@ class RelatedField(Field):
) )
kwargs.pop('many', None) kwargs.pop('many', None)
kwargs.pop('allow_empty', None) kwargs.pop('allow_empty', None)
super(RelatedField, self).__init__(**kwargs) super().__init__(**kwargs)
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
# We override this method in order to automagically create # We override this method in order to automagically create
# `ManyRelatedField` classes instead when `many=True` is set. # `ManyRelatedField` classes instead when `many=True` is set.
if kwargs.pop('many', False): if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs) return cls.many_init(*args, **kwargs)
return super(RelatedField, cls).__new__(cls, *args, **kwargs) return super().__new__(cls, *args, **kwargs)
@classmethod @classmethod
def many_init(cls, *args, **kwargs): def many_init(cls, *args, **kwargs):
@ -157,7 +150,7 @@ class RelatedField(Field):
# We force empty strings to None values for relational fields. # We force empty strings to None values for relational fields.
if data == '': if data == '':
data = None data = None
return super(RelatedField, self).run_validation(data) return super().run_validation(data)
def get_queryset(self): def get_queryset(self):
queryset = self.queryset queryset = self.queryset
@ -189,7 +182,7 @@ class RelatedField(Field):
pass pass
# Standard case, return the object instance. # Standard case, return the object instance.
return super(RelatedField, self).get_attribute(instance) return super().get_attribute(instance)
def get_choices(self, cutoff=None): def get_choices(self, cutoff=None):
queryset = self.get_queryset() queryset = self.get_queryset()
@ -225,7 +218,7 @@ class RelatedField(Field):
) )
def display_value(self, instance): def display_value(self, instance):
return six.text_type(instance) return str(instance)
class StringRelatedField(RelatedField): class StringRelatedField(RelatedField):
@ -236,10 +229,10 @@ class StringRelatedField(RelatedField):
def __init__(self, **kwargs): def __init__(self, **kwargs):
kwargs['read_only'] = True kwargs['read_only'] = True
super(StringRelatedField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_representation(self, value): def to_representation(self, value):
return six.text_type(value) return str(value)
class PrimaryKeyRelatedField(RelatedField): class PrimaryKeyRelatedField(RelatedField):
@ -251,7 +244,7 @@ class PrimaryKeyRelatedField(RelatedField):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.pk_field = kwargs.pop('pk_field', None) self.pk_field = kwargs.pop('pk_field', None)
super(PrimaryKeyRelatedField, self).__init__(**kwargs) super().__init__(**kwargs)
def use_pk_only_optimization(self): def use_pk_only_optimization(self):
return True return True
@ -297,7 +290,7 @@ class HyperlinkedRelatedField(RelatedField):
# implicit `self` argument to be passed. # implicit `self` argument to be passed.
self.reverse = reverse self.reverse = reverse
super(HyperlinkedRelatedField, self).__init__(**kwargs) super().__init__(**kwargs)
def use_pk_only_optimization(self): def use_pk_only_optimization(self):
return self.lookup_field == 'pk' return self.lookup_field == 'pk'
@ -317,10 +310,10 @@ class HyperlinkedRelatedField(RelatedField):
return queryset.get(**lookup_kwargs) return queryset.get(**lookup_kwargs)
except ValueError: except ValueError:
exc = ObjectValueError(str(sys.exc_info()[1])) exc = ObjectValueError(str(sys.exc_info()[1]))
six.reraise(type(exc), exc, sys.exc_info()[2]) raise exc.with_traceback(sys.exc_info()[2])
except TypeError: except TypeError:
exc = ObjectTypeError(str(sys.exc_info()[1])) exc = ObjectTypeError(str(sys.exc_info()[1]))
six.reraise(type(exc), exc, sys.exc_info()[2]) raise exc.with_traceback(sys.exc_info()[2])
def get_url(self, obj, view_name, request, format): def get_url(self, obj, view_name, request, format):
""" """
@ -346,7 +339,7 @@ class HyperlinkedRelatedField(RelatedField):
if http_prefix: if http_prefix:
# If needed convert absolute URLs to relative path # If needed convert absolute URLs to relative path
data = urlparse.urlparse(data).path data = parse.urlparse(data).path
prefix = get_script_prefix() prefix = get_script_prefix()
if data.startswith(prefix): if data.startswith(prefix):
data = '/' + data[len(prefix):] data = '/' + data[len(prefix):]
@ -432,7 +425,7 @@ class HyperlinkedIdentityField(HyperlinkedRelatedField):
assert view_name is not None, 'The `view_name` argument is required.' assert view_name is not None, 'The `view_name` argument is required.'
kwargs['read_only'] = True kwargs['read_only'] = True
kwargs['source'] = '*' kwargs['source'] = '*'
super(HyperlinkedIdentityField, self).__init__(view_name, **kwargs) super().__init__(view_name, **kwargs)
def use_pk_only_optimization(self): def use_pk_only_optimization(self):
# We have the complete object instance already. We don't need # We have the complete object instance already. We don't need
@ -453,7 +446,7 @@ class SlugRelatedField(RelatedField):
def __init__(self, slug_field=None, **kwargs): def __init__(self, slug_field=None, **kwargs):
assert slug_field is not None, 'The `slug_field` argument is required.' assert slug_field is not None, 'The `slug_field` argument is required.'
self.slug_field = slug_field self.slug_field = slug_field
super(SlugRelatedField, self).__init__(**kwargs) super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
try: try:
@ -502,7 +495,7 @@ class ManyRelatedField(Field):
self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT)
) )
assert child_relation is not None, '`child_relation` is a required argument.' assert child_relation is not None, '`child_relation` is a required argument.'
super(ManyRelatedField, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.child_relation.bind(field_name='', parent=self) self.child_relation.bind(field_name='', parent=self)
def get_value(self, dictionary): def get_value(self, dictionary):
@ -518,7 +511,7 @@ class ManyRelatedField(Field):
return dictionary.get(self.field_name, empty) return dictionary.get(self.field_name, empty)
def to_internal_value(self, data): def to_internal_value(self, data):
if isinstance(data, six.text_type) or not hasattr(data, '__iter__'): if isinstance(data, str) or not hasattr(data, '__iter__'):
self.fail('not_a_list', input_type=type(data).__name__) self.fail('not_a_list', input_type=type(data).__name__)
if not self.allow_empty and len(data) == 0: if not self.allow_empty and len(data) == 0:
self.fail('empty') self.fail('empty')

View File

@ -6,10 +6,9 @@ on the response, such as JSON encoded data or HTML output.
REST framework also provides an HTML renderer that renders the browsable API. REST framework also provides an HTML renderer that renders the browsable API.
""" """
from __future__ import unicode_literals
import base64 import base64
from collections import OrderedDict from collections import OrderedDict
from urllib import parse
from django import forms from django import forms
from django.conf import settings from django.conf import settings
@ -19,9 +18,7 @@ from django.http.multipartparser import parse_header
from django.template import engines, loader from django.template import engines, loader
from django.test.client import encode_multipart from django.test.client import encode_multipart
from django.urls import NoReverseMatch from django.urls import NoReverseMatch
from django.utils import six
from django.utils.html import mark_safe from django.utils.html import mark_safe
from django.utils.six.moves.urllib import parse as urlparse
from rest_framework import VERSION, exceptions, serializers, status from rest_framework import VERSION, exceptions, serializers, status
from rest_framework.compat import ( from rest_framework.compat import (
@ -40,7 +37,7 @@ def zero_as_none(value):
return None if value == 0 else value return None if value == 0 else value
class BaseRenderer(object): class BaseRenderer:
""" """
All renderers should extend this class, setting the `media_type` All renderers should extend this class, setting the `media_type`
and `format` attributes, and override the `.render()` method. and `format` attributes, and override the `.render()` method.
@ -111,7 +108,7 @@ class JSONRenderer(BaseRenderer):
# but if ensure_ascii=False, the return type is underspecified, # but if ensure_ascii=False, the return type is underspecified,
# and may (or may not) be unicode. # and may (or may not) be unicode.
# On python 3.x json.dumps() returns unicode strings. # On python 3.x json.dumps() returns unicode strings.
if isinstance(ret, six.text_type): if isinstance(ret, str):
# We always fully escape \u2028 and \u2029 to ensure we output JSON # We always fully escape \u2028 and \u2029 to ensure we output JSON
# that is a strict javascript subset. If bytes were returned # that is a strict javascript subset. If bytes were returned
# by json.dumps() then we don't have these characters in any case. # by json.dumps() then we don't have these characters in any case.
@ -349,7 +346,7 @@ class HTMLFormRenderer(BaseRenderer):
# Get a clone of the field with text-only value representation. # Get a clone of the field with text-only value representation.
field = field.as_form_field() field = field.as_form_field()
if style.get('input_type') == 'datetime-local' and isinstance(field.value, six.text_type): if style.get('input_type') == 'datetime-local' and isinstance(field.value, str):
field.value = field.value.rstrip('Z') field.value = field.value.rstrip('Z')
if 'template' in style: if 'template' in style:
@ -791,7 +788,7 @@ class AdminRenderer(BrowsableAPIRenderer):
""" """
Render the HTML for the browsable API representation. Render the HTML for the browsable API representation.
""" """
context = super(AdminRenderer, self).get_context( context = super().get_context(
data, accepted_media_type, renderer_context data, accepted_media_type, renderer_context
) )
@ -995,14 +992,14 @@ class _BaseOpenAPIRenderer:
tag = None tag = None
for name, link in document.links.items(): for name, link in document.links.items():
path = urlparse.urlparse(link.url).path path = parse.urlparse(link.url).path
method = link.action.lower() method = link.action.lower()
paths.setdefault(path, {}) paths.setdefault(path, {})
paths[path][method] = self.get_operation(link, name, tag=tag) paths[path][method] = self.get_operation(link, name, tag=tag)
for tag, section in document.data.items(): for tag, section in document.data.items():
for name, link in section.links.items(): for name, link in section.links.items():
path = urlparse.urlparse(link.url).path path = parse.urlparse(link.url).path
method = link.action.lower() method = link.action.lower()
paths.setdefault(path, {}) paths.setdefault(path, {})
paths[path][method] = self.get_operation(link, name, tag=tag) paths[path][method] = self.get_operation(link, name, tag=tag)

View File

@ -8,8 +8,6 @@ The wrapped request then offers a richer API, in particular :
- full support of PUT method, including support for file uploads - full support of PUT method, including support for file uploads
- form overloading of HTTP method, content type and content - form overloading of HTTP method, content type and content
""" """
from __future__ import unicode_literals
import io import io
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
@ -18,7 +16,6 @@ from django.conf import settings
from django.http import HttpRequest, QueryDict from django.http import HttpRequest, QueryDict
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
from django.http.request import RawPostDataException from django.http.request import RawPostDataException
from django.utils import six
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from rest_framework import HTTP_HEADER_ENCODING, exceptions from rest_framework import HTTP_HEADER_ENCODING, exceptions
@ -34,7 +31,7 @@ def is_form_media_type(media_type):
base_media_type == 'multipart/form-data') base_media_type == 'multipart/form-data')
class override_method(object): class override_method:
""" """
A context manager that temporarily overrides the method on a request, A context manager that temporarily overrides the method on a request,
additionally setting the `view.request` attribute. additionally setting the `view.request` attribute.
@ -78,10 +75,10 @@ def wrap_attributeerrors():
except AttributeError: except AttributeError:
info = sys.exc_info() info = sys.exc_info()
exc = WrappedAttributeError(str(info[1])) exc = WrappedAttributeError(str(info[1]))
six.reraise(type(exc), exc, info[2]) raise exc.with_traceback(info[2])
class Empty(object): class Empty:
""" """
Placeholder for unset attributes. Placeholder for unset attributes.
Cannot use `None`, as that may be a valid value. Cannot use `None`, as that may be a valid value.
@ -126,7 +123,7 @@ def clone_request(request, method):
return ret return ret
class ForcedAuthentication(object): class ForcedAuthentication:
""" """
This authentication class is used if the test client or request factory This authentication class is used if the test client or request factory
forcibly authenticated the request. forcibly authenticated the request.
@ -140,7 +137,7 @@ class ForcedAuthentication(object):
return (self.force_user, self.force_token) return (self.force_user, self.force_token)
class Request(object): class Request:
""" """
Wrapper allowing to enhance a standard `HttpRequest` instance. Wrapper allowing to enhance a standard `HttpRequest` instance.

View File

@ -4,11 +4,9 @@ it is initialized with unrendered data, instead of a pre-rendered string.
The appropriate renderer is called during Django's template response rendering. The appropriate renderer is called during Django's template response rendering.
""" """
from __future__ import unicode_literals from http.client import responses
from django.template.response import SimpleTemplateResponse from django.template.response import SimpleTemplateResponse
from django.utils import six
from django.utils.six.moves.http_client import responses
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
@ -29,7 +27,7 @@ class Response(SimpleTemplateResponse):
Setting 'renderer' and 'media_type' will typically be deferred, Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`. For example being set automatically by the `APIView`.
""" """
super(Response, self).__init__(None, status=status) super().__init__(None, status=status)
if isinstance(data, Serializer): if isinstance(data, Serializer):
msg = ( msg = (
@ -45,7 +43,7 @@ class Response(SimpleTemplateResponse):
self.content_type = content_type self.content_type = content_type
if headers: if headers:
for name, value in six.iteritems(headers): for name, value in headers.items():
self[name] = value self[name] = value
@property @property
@ -64,13 +62,13 @@ class Response(SimpleTemplateResponse):
content_type = self.content_type content_type = self.content_type
if content_type is None and charset is not None: 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: elif content_type is None:
content_type = media_type content_type = media_type
self['Content-Type'] = content_type self['Content-Type'] = content_type
ret = renderer.render(self.data, accepted_media_type, context) ret = renderer.render(self.data, accepted_media_type, context)
if isinstance(ret, six.text_type): if isinstance(ret, str):
assert charset, ( assert charset, (
'renderer returned unicode, and did not specify ' 'renderer returned unicode, and did not specify '
'a charset value.' 'a charset value.'
@ -94,7 +92,7 @@ class Response(SimpleTemplateResponse):
""" """
Remove attributes from the response that shouldn't be cached. Remove attributes from the response that shouldn't be cached.
""" """
state = super(Response, self).__getstate__() state = super().__getstate__()
for key in ( for key in (
'accepted_renderer', 'renderer_context', 'resolver_match', 'accepted_renderer', 'renderer_context', 'resolver_match',
'client', 'request', 'json', 'wsgi_request' 'client', 'request', 'json', 'wsgi_request'

View File

@ -1,11 +1,8 @@
""" """
Provide urlresolver functions that return fully qualified URLs or view names Provide urlresolver functions that return fully qualified URLs or view names
""" """
from __future__ import unicode_literals
from django.urls import NoReverseMatch from django.urls import NoReverseMatch
from django.urls import reverse as django_reverse from django.urls import reverse as django_reverse
from django.utils import six
from django.utils.functional import lazy from django.utils.functional import lazy
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -66,4 +63,4 @@ def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extr
return url return url
reverse_lazy = lazy(reverse, six.text_type) reverse_lazy = lazy(reverse, str)

View File

@ -13,8 +13,6 @@ For example, you might have a `urls.py` that looks something like this:
urlpatterns = router.urls urlpatterns = router.urls
""" """
from __future__ import unicode_literals
import itertools import itertools
import warnings import warnings
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
@ -22,7 +20,6 @@ from collections import OrderedDict, namedtuple
from django.conf.urls import url from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.urls import NoReverseMatch from django.urls import NoReverseMatch
from django.utils import six
from django.utils.deprecation import RenameMethodsBase from django.utils.deprecation import RenameMethodsBase
from rest_framework import ( from rest_framework import (
@ -39,7 +36,7 @@ Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs'])
DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs']) DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs'])
class DynamicDetailRoute(object): class DynamicDetailRoute:
def __new__(cls, url, name, initkwargs): def __new__(cls, url, name, initkwargs):
warnings.warn( warnings.warn(
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 " "`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
@ -50,7 +47,7 @@ class DynamicDetailRoute(object):
return DynamicRoute(url, name, True, initkwargs) return DynamicRoute(url, name, True, initkwargs)
class DynamicListRoute(object): class DynamicListRoute:
def __new__(cls, url, name, initkwargs): def __new__(cls, url, name, initkwargs):
warnings.warn( warnings.warn(
"`DynamicListRoute` is deprecated and will be removed in 3.10 in " "`DynamicListRoute` is deprecated and will be removed in 3.10 in "
@ -83,7 +80,7 @@ class RenameRouterMethods(RenameMethodsBase):
) )
class BaseRouter(six.with_metaclass(RenameRouterMethods)): class BaseRouter(metaclass=RenameRouterMethods):
def __init__(self): def __init__(self):
self.registry = [] self.registry = []
@ -173,7 +170,7 @@ class SimpleRouter(BaseRouter):
def __init__(self, trailing_slash=True): def __init__(self, trailing_slash=True):
self.trailing_slash = '/' if trailing_slash else '' self.trailing_slash = '/' if trailing_slash else ''
super(SimpleRouter, self).__init__() super().__init__()
def get_default_basename(self, viewset): def get_default_basename(self, viewset):
""" """
@ -365,7 +362,7 @@ class DefaultRouter(SimpleRouter):
self.root_renderers = kwargs.pop('root_renderers') self.root_renderers = kwargs.pop('root_renderers')
else: else:
self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
super(DefaultRouter, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_api_root_view(self, api_urls=None): def get_api_root_view(self, api_urls=None):
""" """
@ -383,7 +380,7 @@ class DefaultRouter(SimpleRouter):
Generate the list of URL patterns, including a default root view Generate the list of URL patterns, including a default root view
for the API, and appending `.json` style format suffixes. for the API, and appending `.json` style format suffixes.
""" """
urls = super(DefaultRouter, self).get_urls() urls = super().get_urls()
if self.include_root_view: if self.include_root_view:
view = self.get_api_root_view(api_urls=urls) view = self.get_api_root_view(api_urls=urls)

View File

@ -11,7 +11,6 @@ from django.conf import settings
from django.contrib.admindocs.views import simplify_regex from django.contrib.admindocs.views import simplify_regex
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import Http404 from django.http import Http404
from django.utils import six
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework.compat import ( from rest_framework.compat import (
@ -68,7 +67,7 @@ class LinkNode(OrderedDict):
def __init__(self): def __init__(self):
self.links = [] self.links = []
self.methods_counter = Counter() self.methods_counter = Counter()
super(LinkNode, self).__init__() super().__init__()
def get_available_key(self, preferred_key): def get_available_key(self, preferred_key):
if preferred_key not in self: if preferred_key not in self:
@ -140,7 +139,7 @@ _PATH_PARAMETER_COMPONENT_RE = re.compile(
) )
class EndpointEnumerator(object): class EndpointEnumerator:
""" """
A class to determine the available API endpoints that a project exposes. A class to determine the available API endpoints that a project exposes.
""" """
@ -151,7 +150,7 @@ class EndpointEnumerator(object):
urlconf = settings.ROOT_URLCONF urlconf = settings.ROOT_URLCONF
# Load the given URLconf module # Load the given URLconf module
if isinstance(urlconf, six.string_types): if isinstance(urlconf, str):
urls = import_module(urlconf) urls = import_module(urlconf)
else: else:
urls = urlconf urls = urlconf
@ -232,7 +231,7 @@ class EndpointEnumerator(object):
return [method for method in methods if method not in ('OPTIONS', 'HEAD')] return [method for method in methods if method not in ('OPTIONS', 'HEAD')]
class SchemaGenerator(object): class SchemaGenerator:
# Map HTTP methods onto actions. # Map HTTP methods onto actions.
default_mapping = { default_mapping = {
'get': 'retrieve', 'get': 'retrieve',

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
inspectors.py # Per-endpoint view introspection inspectors.py # Per-endpoint view introspection
@ -7,11 +6,11 @@ See schemas.__init__.py for package overview.
import re import re
import warnings import warnings
from collections import OrderedDict from collections import OrderedDict
from urllib import parse
from weakref import WeakKeyDictionary from weakref import WeakKeyDictionary
from django.db import models from django.db import models
from django.utils.encoding import force_text, smart_text from django.utils.encoding import force_text, smart_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions, serializers from rest_framework import exceptions, serializers
@ -125,7 +124,7 @@ def get_pk_description(model, model_field):
) )
class ViewInspector(object): class ViewInspector:
""" """
Descriptor class on APIView. Descriptor class on APIView.
@ -207,7 +206,7 @@ class AutoSchema(ViewInspector):
* `manual_fields`: list of `coreapi.Field` instances that * `manual_fields`: list of `coreapi.Field` instances that
will be added to auto-generated fields, overwriting on `Field.name` will be added to auto-generated fields, overwriting on `Field.name`
""" """
super(AutoSchema, self).__init__() super().__init__()
if manual_fields is None: if manual_fields is None:
manual_fields = [] manual_fields = []
self._manual_fields = manual_fields self._manual_fields = manual_fields
@ -232,7 +231,7 @@ class AutoSchema(ViewInspector):
path = path[1:] path = path[1:]
return coreapi.Link( return coreapi.Link(
url=urlparse.urljoin(base_url, path), url=parse.urljoin(base_url, path),
action=method.lower(), action=method.lower(),
encoding=encoding, encoding=encoding,
fields=fields, fields=fields,
@ -475,7 +474,7 @@ class ManualSchema(ViewInspector):
* `fields`: list of `coreapi.Field` instances. * `fields`: list of `coreapi.Field` instances.
* `description`: String description for view. Optional. * `description`: String description for view. Optional.
""" """
super(ManualSchema, self).__init__() super().__init__()
assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances"
self._fields = fields self._fields = fields
self._description = description self._description = description
@ -487,7 +486,7 @@ class ManualSchema(ViewInspector):
path = path[1:] path = path[1:]
return coreapi.Link( return coreapi.Link(
url=urlparse.urljoin(base_url, path), url=parse.urljoin(base_url, path),
action=method.lower(), action=method.lower(),
encoding=self._encoding, encoding=self._encoding,
fields=self._fields, fields=self._fields,
@ -498,7 +497,7 @@ class ManualSchema(ViewInspector):
class DefaultSchema(ViewInspector): class DefaultSchema(ViewInspector):
"""Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting""" """Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting"""
def __get__(self, instance, owner): def __get__(self, instance, owner):
result = super(DefaultSchema, self).__get__(instance, owner) result = super().__get__(instance, owner)
if not isinstance(result, DefaultSchema): if not isinstance(result, DefaultSchema):
return result return result

View File

@ -17,7 +17,7 @@ class SchemaView(APIView):
public = False public = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SchemaView, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if self.renderer_classes is None: if self.renderer_classes is None:
self.renderer_classes = [ self.renderer_classes = [
renderers.OpenAPIRenderer, renderers.OpenAPIRenderer,
@ -38,4 +38,4 @@ class SchemaView(APIView):
self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
neg = self.perform_content_negotiation(self.request, force=True) neg = self.perform_content_negotiation(self.request, force=True)
self.request.accepted_renderer, self.request.accepted_media_type = neg self.request.accepted_renderer, self.request.accepted_media_type = neg
return super(SchemaView, self).handle_exception(exc) return super().handle_exception(exc)

View File

@ -10,8 +10,6 @@ python primitives.
2. The process of marshalling between python primitives and request and 2. The process of marshalling between python primitives and request and
response content is handled by parsers and renderers. response content is handled by parsers and renderers.
""" """
from __future__ import unicode_literals
import copy import copy
import inspect import inspect
import traceback import traceback
@ -23,11 +21,11 @@ from django.db import models
from django.db.models import DurationField as ModelDurationField from django.db.models import DurationField as ModelDurationField
from django.db.models.fields import Field as DjangoModelField from django.db.models.fields import Field as DjangoModelField
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
from django.utils import six, timezone from django.utils import timezone
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import Mapping, postgres_fields, unicode_to_repr from rest_framework.compat import Mapping, postgres_fields
from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.fields import get_error_detail, set_value from rest_framework.fields import get_error_detail, set_value
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -115,14 +113,14 @@ class BaseSerializer(Field):
self.partial = kwargs.pop('partial', False) self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {}) self._context = kwargs.pop('context', {})
kwargs.pop('many', None) kwargs.pop('many', None)
super(BaseSerializer, self).__init__(**kwargs) super().__init__(**kwargs)
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
# We override this method in order to automagically create # We override this method in order to automagically create
# `ListSerializer` classes instead when `many=True` is set. # `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False): if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs) return cls.many_init(*args, **kwargs)
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs) return super().__new__(cls, *args, **kwargs)
@classmethod @classmethod
def many_init(cls, *args, **kwargs): def many_init(cls, *args, **kwargs):
@ -315,7 +313,7 @@ class SerializerMetaclass(type):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
attrs['_declared_fields'] = cls._get_declared_fields(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): def as_serializer_error(exc):
@ -344,8 +342,7 @@ def as_serializer_error(exc):
} }
@six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
class Serializer(BaseSerializer):
default_error_messages = { default_error_messages = {
'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
} }
@ -466,7 +463,7 @@ class Serializer(BaseSerializer):
to_validate.update(value) to_validate.update(value)
else: else:
to_validate = value to_validate = value
super(Serializer, self).run_validators(to_validate) super().run_validators(to_validate)
def to_internal_value(self, data): def to_internal_value(self, data):
""" """
@ -535,7 +532,7 @@ class Serializer(BaseSerializer):
return attrs return attrs
def __repr__(self): def __repr__(self):
return unicode_to_repr(representation.serializer_repr(self, indent=1)) return representation.serializer_repr(self, indent=1)
# The following are used for accessing `BoundField` instances on the # The following are used for accessing `BoundField` instances on the
# serializer, for the purposes of presenting a form-like API onto the # serializer, for the purposes of presenting a form-like API onto the
@ -560,12 +557,12 @@ class Serializer(BaseSerializer):
@property @property
def data(self): def data(self):
ret = super(Serializer, self).data ret = super().data
return ReturnDict(ret, serializer=self) return ReturnDict(ret, serializer=self)
@property @property
def errors(self): 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': if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
# Edge case. Provide a more descriptive error than # Edge case. Provide a more descriptive error than
# "this field may not be null", when no data is passed. # "this field may not be null", when no data is passed.
@ -591,11 +588,11 @@ class ListSerializer(BaseSerializer):
self.allow_empty = kwargs.pop('allow_empty', True) self.allow_empty = kwargs.pop('allow_empty', True)
assert self.child is not None, '`child` is a required argument.' assert self.child is not None, '`child` is a required argument.'
assert not inspect.isclass(self.child), '`child` has not been instantiated.' 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) self.child.bind(field_name='', parent=self)
def bind(self, field_name, parent): def bind(self, field_name, parent):
super(ListSerializer, self).bind(field_name, parent) super().bind(field_name, parent)
self.partial = self.parent.partial self.partial = self.parent.partial
def get_initial(self): def get_initial(self):
@ -758,19 +755,19 @@ class ListSerializer(BaseSerializer):
return not bool(self._errors) return not bool(self._errors)
def __repr__(self): def __repr__(self):
return unicode_to_repr(representation.list_repr(self, indent=1)) return representation.list_repr(self, indent=1)
# Include a backlink to the serializer class on return objects. # Include a backlink to the serializer class on return objects.
# Allows renderers such as HTMLFormRenderer to get the full field info. # Allows renderers such as HTMLFormRenderer to get the full field info.
@property @property
def data(self): def data(self):
ret = super(ListSerializer, self).data ret = super().data
return ReturnList(ret, serializer=self) return ReturnList(ret, serializer=self)
@property @property
def errors(self): 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': if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
# Edge case. Provide a more descriptive error than # Edge case. Provide a more descriptive error than
# "this field may not be null", when no data is passed. # "this field may not be null", when no data is passed.

View File

@ -18,13 +18,10 @@ This module provides the `api_setting` object, that is used to access
REST framework settings, checking for user settings first, then falling REST framework settings, checking for user settings first, then falling
back to the defaults. back to the defaults.
""" """
from __future__ import unicode_literals
from importlib import import_module from importlib import import_module
from django.conf import settings from django.conf import settings
from django.test.signals import setting_changed from django.test.signals import setting_changed
from django.utils import six
from rest_framework import ISO_8601 from rest_framework import ISO_8601
@ -166,7 +163,7 @@ def perform_import(val, setting_name):
""" """
if val is None: if val is None:
return None return None
elif isinstance(val, six.string_types): elif isinstance(val, str):
return import_from_string(val, setting_name) return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)): elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val] return [import_from_string(item, setting_name) for item in val]
@ -187,7 +184,7 @@ def import_from_string(val, setting_name):
raise ImportError(msg) raise ImportError(msg)
class APISettings(object): class APISettings:
""" """
A settings object, that allows API settings to be accessed as properties. A settings object, that allows API settings to be accessed as properties.
For example: For example:

View File

@ -5,7 +5,6 @@ See RFC 2616 - https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
And RFC 6585 - https://tools.ietf.org/html/rfc6585 And RFC 6585 - https://tools.ietf.org/html/rfc6585
And RFC 4918 - https://tools.ietf.org/html/rfc4918 And RFC 4918 - https://tools.ietf.org/html/rfc4918
""" """
from __future__ import unicode_literals
def is_informational(code): def is_informational(code):

View File

@ -1,12 +1,9 @@
from __future__ import absolute_import, unicode_literals
import re import re
from collections import OrderedDict from collections import OrderedDict
from django import template from django import template
from django.template import loader from django.template import loader
from django.urls import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.utils import six
from django.utils.encoding import force_text, iri_to_uri from django.utils.encoding import force_text, iri_to_uri
from django.utils.html import escape, format_html, smart_urlquote from django.utils.html import escape, format_html, smart_urlquote
from django.utils.safestring import SafeData, mark_safe from django.utils.safestring import SafeData, mark_safe
@ -187,7 +184,7 @@ def add_class(value, css_class):
In the case of REST Framework, the filter is used to add Bootstrap-specific In the case of REST Framework, the filter is used to add Bootstrap-specific
classes to the forms. classes to the forms.
""" """
html = six.text_type(value) html = str(value)
match = class_re.search(html) match = class_re.search(html)
if match: if match:
m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class, m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
@ -204,7 +201,7 @@ def add_class(value, css_class):
@register.filter @register.filter
def format_value(value): def format_value(value):
if getattr(value, 'is_hyperlink', False): if getattr(value, 'is_hyperlink', False):
name = six.text_type(value.obj) name = str(value.obj)
return mark_safe('<a href=%s>%s</a>' % (value, escape(name))) return mark_safe('<a href=%s>%s</a>' % (value, escape(name)))
if value is None or isinstance(value, bool): if value is None or isinstance(value, bool):
return mark_safe('<code>%s</code>' % {True: 'true', False: 'false', None: 'null'}[value]) return mark_safe('<code>%s</code>' % {True: 'true', False: 'false', None: 'null'}[value])
@ -219,7 +216,7 @@ def format_value(value):
template = loader.get_template('rest_framework/admin/dict_value.html') template = loader.get_template('rest_framework/admin/dict_value.html')
context = {'value': value} context = {'value': value}
return template.render(context) return template.render(context)
elif isinstance(value, six.string_types): elif isinstance(value, str):
if ( if (
(value.startswith('http:') or value.startswith('https:')) and not (value.startswith('http:') or value.startswith('https:')) and not
re.search(r'\s', value) re.search(r'\s', value)
@ -229,7 +226,7 @@ def format_value(value):
return mark_safe('<a href="mailto:{value}">{value}</a>'.format(value=escape(value))) return mark_safe('<a href="mailto:{value}">{value}</a>'.format(value=escape(value)))
elif '\n' in value: elif '\n' in value:
return mark_safe('<pre>%s</pre>' % escape(value)) return mark_safe('<pre>%s</pre>' % escape(value))
return six.text_type(value) return str(value)
@register.filter @register.filter

View File

@ -1,9 +1,5 @@
# -- coding: utf-8 --
# Note that we import as `DjangoRequestFactory` and `DjangoClient` in order # Note that we import as `DjangoRequestFactory` and `DjangoClient` in order
# to make it harder for the user to import the wrong thing without realizing. # to make it harder for the user to import the wrong thing without realizing.
from __future__ import unicode_literals
import io import io
from importlib import import_module from importlib import import_module
@ -14,7 +10,6 @@ from django.test import override_settings, testcases
from django.test.client import Client as DjangoClient from django.test.client import Client as DjangoClient
from django.test.client import ClientHandler from django.test.client import ClientHandler
from django.test.client import RequestFactory as DjangoRequestFactory from django.test.client import RequestFactory as DjangoRequestFactory
from django.utils import six
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.http import urlencode from django.utils.http import urlencode
@ -32,7 +27,7 @@ if requests is not None:
def get_all(self, key, default): def get_all(self, key, default):
return self.getheaders(key) return self.getheaders(key)
class MockOriginalResponse(object): class MockOriginalResponse:
def __init__(self, headers): def __init__(self, headers):
self.msg = HeaderDict(headers) self.msg = HeaderDict(headers)
self.closed = False self.closed = False
@ -109,7 +104,7 @@ if requests is not None:
class RequestsClient(requests.Session): class RequestsClient(requests.Session):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RequestsClient, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
adapter = DjangoTestAdapter() adapter = DjangoTestAdapter()
self.mount('http://', adapter) self.mount('http://', adapter)
self.mount('https://', adapter) self.mount('https://', adapter)
@ -117,7 +112,7 @@ if requests is not None:
def request(self, method, url, *args, **kwargs): def request(self, method, url, *args, **kwargs):
if not url.startswith('http'): if not url.startswith('http'):
raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url) 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: else:
def RequestsClient(*args, **kwargs): def RequestsClient(*args, **kwargs):
@ -129,7 +124,7 @@ if coreapi is not None:
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._session = RequestsClient() self._session = RequestsClient()
kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)] kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)]
return super(CoreAPIClient, self).__init__(*args, **kwargs) return super().__init__(*args, **kwargs)
@property @property
def session(self): def session(self):
@ -149,7 +144,7 @@ class APIRequestFactory(DjangoRequestFactory):
self.renderer_classes = {} self.renderer_classes = {}
for cls in self.renderer_classes_list: for cls in self.renderer_classes_list:
self.renderer_classes[cls.format] = cls self.renderer_classes[cls.format] = cls
super(APIRequestFactory, self).__init__(**defaults) super().__init__(**defaults)
def _encode_data(self, data, format=None, content_type=None): def _encode_data(self, data, format=None, content_type=None):
""" """
@ -171,7 +166,7 @@ class APIRequestFactory(DjangoRequestFactory):
format = format or self.default_format format = format or self.default_format
assert format in self.renderer_classes, ( 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 " "Set TEST_REQUEST_RENDERER_CLASSES to enable "
"extra request formats.".format( "extra request formats.".format(
format, format,
@ -184,12 +179,12 @@ class APIRequestFactory(DjangoRequestFactory):
ret = renderer.render(data) ret = renderer.render(data)
# Determine the content-type header from the renderer # Determine the content-type header from the renderer
content_type = "{0}; charset={1}".format( content_type = "{}; charset={}".format(
renderer.media_type, renderer.charset renderer.media_type, renderer.charset
) )
# Coerce text to bytes if required. # Coerce text to bytes if required.
if isinstance(ret, six.text_type): if isinstance(ret, str):
ret = bytes(ret.encode(renderer.charset)) ret = bytes(ret.encode(renderer.charset))
return ret, content_type return ret, content_type
@ -202,8 +197,7 @@ class APIRequestFactory(DjangoRequestFactory):
# Fix to support old behavior where you have the arguments in the # Fix to support old behavior where you have the arguments in the
# url. See #1461. # url. See #1461.
query_string = force_bytes(path.split('?')[1]) query_string = force_bytes(path.split('?')[1])
if six.PY3: query_string = query_string.decode('iso-8859-1')
query_string = query_string.decode('iso-8859-1')
r['QUERY_STRING'] = query_string r['QUERY_STRING'] = query_string
r.update(extra) r.update(extra)
return self.generic('GET', path, **r) return self.generic('GET', path, **r)
@ -234,11 +228,11 @@ class APIRequestFactory(DjangoRequestFactory):
if content_type is not None: if content_type is not None:
extra['CONTENT_TYPE'] = str(content_type) extra['CONTENT_TYPE'] = str(content_type)
return super(APIRequestFactory, self).generic( return super().generic(
method, path, data, content_type, secure, **extra) method, path, data, content_type, secure, **extra)
def request(self, **kwargs): def request(self, **kwargs):
request = super(APIRequestFactory, self).request(**kwargs) request = super().request(**kwargs)
request._dont_enforce_csrf_checks = not self.enforce_csrf_checks request._dont_enforce_csrf_checks = not self.enforce_csrf_checks
return request return request
@ -252,18 +246,18 @@ class ForceAuthClientHandler(ClientHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._force_user = None self._force_user = None
self._force_token = None self._force_token = None
super(ForceAuthClientHandler, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_response(self, request): def get_response(self, request):
# This is the simplest place we can hook into to patch the # This is the simplest place we can hook into to patch the
# request object. # request object.
force_authenticate(request, self._force_user, self._force_token) 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): class APIClient(APIRequestFactory, DjangoClient):
def __init__(self, enforce_csrf_checks=False, **defaults): def __init__(self, enforce_csrf_checks=False, **defaults):
super(APIClient, self).__init__(**defaults) super().__init__(**defaults)
self.handler = ForceAuthClientHandler(enforce_csrf_checks) self.handler = ForceAuthClientHandler(enforce_csrf_checks)
self._credentials = {} self._credentials = {}
@ -286,17 +280,17 @@ class APIClient(APIRequestFactory, DjangoClient):
def request(self, **kwargs): def request(self, **kwargs):
# Ensure that any credentials set get added to every request. # Ensure that any credentials set get added to every request.
kwargs.update(self._credentials) kwargs.update(self._credentials)
return super(APIClient, self).request(**kwargs) return super().request(**kwargs)
def get(self, path, data=None, follow=False, **extra): 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: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
return response return response
def post(self, path, data=None, format=None, content_type=None, def post(self, path, data=None, format=None, content_type=None,
follow=False, **extra): follow=False, **extra):
response = super(APIClient, self).post( response = super().post(
path, data=data, format=format, content_type=content_type, **extra) path, data=data, format=format, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
@ -304,7 +298,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def put(self, path, data=None, format=None, content_type=None, def put(self, path, data=None, format=None, content_type=None,
follow=False, **extra): follow=False, **extra):
response = super(APIClient, self).put( response = super().put(
path, data=data, format=format, content_type=content_type, **extra) path, data=data, format=format, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
@ -312,7 +306,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def patch(self, path, data=None, format=None, content_type=None, def patch(self, path, data=None, format=None, content_type=None,
follow=False, **extra): follow=False, **extra):
response = super(APIClient, self).patch( response = super().patch(
path, data=data, format=format, content_type=content_type, **extra) path, data=data, format=format, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
@ -320,7 +314,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def delete(self, path, data=None, format=None, content_type=None, def delete(self, path, data=None, format=None, content_type=None,
follow=False, **extra): follow=False, **extra):
response = super(APIClient, self).delete( response = super().delete(
path, data=data, format=format, content_type=content_type, **extra) path, data=data, format=format, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
@ -328,7 +322,7 @@ class APIClient(APIRequestFactory, DjangoClient):
def options(self, path, data=None, format=None, content_type=None, def options(self, path, data=None, format=None, content_type=None,
follow=False, **extra): follow=False, **extra):
response = super(APIClient, self).options( response = super().options(
path, data=data, format=format, content_type=content_type, **extra) path, data=data, format=format, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
@ -342,7 +336,7 @@ class APIClient(APIRequestFactory, DjangoClient):
self.handler._force_token = None self.handler._force_token = None
if self.session: if self.session:
super(APIClient, self).logout() super().logout()
class APITransactionTestCase(testcases.TransactionTestCase): class APITransactionTestCase(testcases.TransactionTestCase):
@ -389,11 +383,11 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
cls._module.urlpatterns = cls.urlpatterns cls._module.urlpatterns = cls.urlpatterns
cls._override.enable() cls._override.enable()
super(URLPatternsTestCase, cls).setUpClass() super().setUpClass()
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
super(URLPatternsTestCase, cls).tearDownClass() super().tearDownClass()
cls._override.disable() cls._override.disable()
if hasattr(cls, '_module_urlpatterns'): if hasattr(cls, '_module_urlpatterns'):

View File

@ -1,8 +1,6 @@
""" """
Provides various throttling policies. Provides various throttling policies.
""" """
from __future__ import unicode_literals
import time import time
from django.core.cache import cache as default_cache from django.core.cache import cache as default_cache
@ -11,7 +9,7 @@ from django.core.exceptions import ImproperlyConfigured
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
class BaseThrottle(object): class BaseThrottle:
""" """
Rate throttling of requests. Rate throttling of requests.
""" """
@ -232,7 +230,7 @@ class ScopedRateThrottle(SimpleRateThrottle):
self.num_requests, self.duration = self.parse_rate(self.rate) self.num_requests, self.duration = self.parse_rate(self.rate)
# We can now proceed as normal. # 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): def get_cache_key(self, request, view):
""" """

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import include, url from django.conf.urls import include, url
from rest_framework.compat import ( from rest_framework.compat import (

View File

@ -11,8 +11,6 @@ your API requires authentication:
You should make sure your authentication settings include `SessionAuthentication`. You should make sure your authentication settings include `SessionAuthentication`.
""" """
from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from django.contrib.auth import views from django.contrib.auth import views

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.urls import get_script_prefix, resolve from django.urls import get_script_prefix, resolve

View File

@ -1,15 +1,13 @@
""" """
Helper classes for parsers. Helper classes for parsers.
""" """
from __future__ import absolute_import, unicode_literals
import datetime import datetime
import decimal import decimal
import json # noqa import json # noqa
import uuid import uuid
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils import six, timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import Promise from django.utils.functional import Promise
@ -39,12 +37,12 @@ class JSONEncoder(json.JSONEncoder):
representation = obj.isoformat() representation = obj.isoformat()
return representation return representation
elif isinstance(obj, datetime.timedelta): elif isinstance(obj, datetime.timedelta):
return six.text_type(obj.total_seconds()) return str(obj.total_seconds())
elif isinstance(obj, decimal.Decimal): elif isinstance(obj, decimal.Decimal):
# Serializers will coerce decimals to strings by default. # Serializers will coerce decimals to strings by default.
return float(obj) return float(obj)
elif isinstance(obj, uuid.UUID): elif isinstance(obj, uuid.UUID):
return six.text_type(obj) return str(obj)
elif isinstance(obj, QuerySet): elif isinstance(obj, QuerySet):
return tuple(obj) return tuple(obj)
elif isinstance(obj, bytes): elif isinstance(obj, bytes):
@ -65,4 +63,4 @@ class JSONEncoder(json.JSONEncoder):
pass pass
elif hasattr(obj, '__iter__'): elif hasattr(obj, '__iter__'):
return tuple(item for item in obj) return tuple(item for item in obj)
return super(JSONEncoder, self).default(obj) return super().default(obj)

View File

@ -16,7 +16,7 @@ NUMERIC_FIELD_TYPES = (
) )
class ClassLookupDict(object): class ClassLookupDict:
""" """
Takes a dictionary with classes as keys. Takes a dictionary with classes as keys.
Lookups against this object will traverses the object's inheritance Lookups against this object will traverses the object's inheritance

View File

@ -1,8 +1,6 @@
""" """
Utility functions to return a formatted name and description for a given view. Utility functions to return a formatted name and description for a given view.
""" """
from __future__ import unicode_literals
import re import re
from django.utils.encoding import force_text from django.utils.encoding import force_text

View File

@ -5,9 +5,6 @@ REST framework should always import this wrapper module in order to maintain
spec-compliant encoding/decoding. Support for non-standard features should be spec-compliant encoding/decoding. Support for non-standard features should be
handled by users at the renderer and parser layer. handled by users at the renderer and parser layer.
""" """
from __future__ import absolute_import
import functools import functools
import json # noqa import json # noqa

View File

@ -3,10 +3,7 @@ Handling of media types, as found in HTTP Content-Type and Accept headers.
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
""" """
from __future__ import unicode_literals
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
from django.utils.encoding import python_2_unicode_compatible
from rest_framework import HTTP_HEADER_ENCODING from rest_framework import HTTP_HEADER_ENCODING
@ -46,8 +43,7 @@ def order_by_precedence(media_type_lst):
return [media_types for media_types in ret if media_types] return [media_types for media_types in ret if media_types]
@python_2_unicode_compatible class _MediaType:
class _MediaType(object):
def __init__(self, media_type_str): def __init__(self, media_type_str):
self.orig = '' if (media_type_str is None) else media_type_str self.orig = '' if (media_type_str is None) else media_type_str
self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING)) self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING))

View File

@ -2,16 +2,12 @@
Helper functions for creating user-friendly representations Helper functions for creating user-friendly representations
of serializer classes and serializer fields. of serializer classes and serializer fields.
""" """
from __future__ import unicode_literals
import re import re
from django.db import models from django.db import models
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import Promise from django.utils.functional import Promise
from rest_framework.compat import unicode_repr
def manager_repr(value): def manager_repr(value):
model = value.model model = value.model
@ -34,7 +30,7 @@ def smart_repr(value):
if isinstance(value, Promise) and value._delegate_text: if isinstance(value, Promise) and value._delegate_text:
value = force_text(value) value = force_text(value)
value = unicode_repr(value) value = repr(value)
# Representations like u'help text' # Representations like u'help text'
# should simply be presented as 'help text' # should simply be presented as 'help text'

View File

@ -1,10 +1,8 @@
from __future__ import unicode_literals
from collections import OrderedDict from collections import OrderedDict
from django.utils.encoding import force_text from django.utils.encoding import force_text
from rest_framework.compat import MutableMapping, unicode_to_repr from rest_framework.compat import MutableMapping
from rest_framework.utils import json from rest_framework.utils import json
@ -17,7 +15,7 @@ class ReturnDict(OrderedDict):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.serializer = kwargs.pop('serializer') self.serializer = kwargs.pop('serializer')
super(ReturnDict, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def copy(self): def copy(self):
return ReturnDict(self, serializer=self.serializer) return ReturnDict(self, serializer=self.serializer)
@ -40,7 +38,7 @@ class ReturnList(list):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.serializer = kwargs.pop('serializer') self.serializer = kwargs.pop('serializer')
super(ReturnList, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def __repr__(self): def __repr__(self):
return list.__repr__(self) return list.__repr__(self)
@ -51,7 +49,7 @@ class ReturnList(list):
return (list, (list(self),)) return (list, (list(self),))
class BoundField(object): class BoundField:
""" """
A field object that also includes `.value` and `.error` properties. A field object that also includes `.value` and `.error` properties.
Returned when iterating over a serializer instance, Returned when iterating over a serializer instance,
@ -73,9 +71,9 @@ class BoundField(object):
return self._field.__class__ return self._field.__class__
def __repr__(self): def __repr__(self):
return unicode_to_repr('<%s value=%s errors=%s>' % ( return '<%s value=%s errors=%s>' % (
self.__class__.__name__, self.value, self.errors self.__class__.__name__, self.value, self.errors
)) )
def as_form_field(self): def as_form_field(self):
value = '' if (self.value is None or self.value is False) else self.value value = '' if (self.value is None or self.value is False) else self.value
@ -103,9 +101,9 @@ class NestedBoundField(BoundField):
""" """
def __init__(self, field, value, errors, prefix=''): def __init__(self, field, value, errors, prefix=''):
if value is None or value is '': if value is None or value == '':
value = {} value = {}
super(NestedBoundField, self).__init__(field, value, errors, prefix) super().__init__(field, value, errors, prefix)
def __iter__(self): def __iter__(self):
for field in self.fields.values(): for field in self.fields.values():

View File

@ -1,5 +1,6 @@
from urllib import parse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.six.moves.urllib import parse as urlparse
def replace_query_param(url, key, val): def replace_query_param(url, key, val):
@ -7,11 +8,11 @@ def replace_query_param(url, key, val):
Given a URL and a key/val pair, set or replace an item in the query Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL. parameters of the URL, and return the new URL.
""" """
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(force_str(url)) (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
query_dict = urlparse.parse_qs(query, keep_blank_values=True) query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict[force_str(key)] = [force_str(val)] query_dict[force_str(key)] = [force_str(val)]
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) return parse.urlunsplit((scheme, netloc, path, query, fragment))
def remove_query_param(url, key): def remove_query_param(url, key):
@ -19,8 +20,8 @@ def remove_query_param(url, key):
Given a URL and a key/val pair, remove an item in the query Given a URL and a key/val pair, remove an item in the query
parameters of the URL, and return the new URL. parameters of the URL, and return the new URL.
""" """
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(force_str(url)) (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
query_dict = urlparse.parse_qs(query, keep_blank_values=True) query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict.pop(key, None) query_dict.pop(key, None)
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) return parse.urlunsplit((scheme, netloc, path, query, fragment))

View File

@ -6,12 +6,9 @@ This gives us better separation of concerns, allows us to use single-step
object creation, and makes it possible to switch between using the implicit object creation, and makes it possible to switch between using the implicit
`ModelSerializer` class and an equivalent explicit `Serializer` class. `ModelSerializer` class and an equivalent explicit `Serializer` class.
""" """
from __future__ import unicode_literals
from django.db import DataError from django.db import DataError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import unicode_to_repr
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.utils.representation import smart_repr from rest_framework.utils.representation import smart_repr
@ -33,7 +30,7 @@ def qs_filter(queryset, **kwargs):
return queryset.none() return queryset.none()
class UniqueValidator(object): class UniqueValidator:
""" """
Validator that corresponds to `unique=True` on a model field. Validator that corresponds to `unique=True` on a model field.
@ -82,13 +79,13 @@ class UniqueValidator(object):
raise ValidationError(self.message, code='unique') raise ValidationError(self.message, code='unique')
def __repr__(self): def __repr__(self):
return unicode_to_repr('<%s(queryset=%s)>' % ( return '<%s(queryset=%s)>' % (
self.__class__.__name__, self.__class__.__name__,
smart_repr(self.queryset) smart_repr(self.queryset)
)) )
class UniqueTogetherValidator(object): class UniqueTogetherValidator:
""" """
Validator that corresponds to `unique_together = (...)` on a model class. Validator that corresponds to `unique_together = (...)` on a model class.
@ -170,14 +167,14 @@ class UniqueTogetherValidator(object):
raise ValidationError(message, code='unique') raise ValidationError(message, code='unique')
def __repr__(self): def __repr__(self):
return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % ( return '<%s(queryset=%s, fields=%s)>' % (
self.__class__.__name__, self.__class__.__name__,
smart_repr(self.queryset), smart_repr(self.queryset),
smart_repr(self.fields) smart_repr(self.fields)
)) )
class BaseUniqueForValidator(object): class BaseUniqueForValidator:
message = None message = None
missing_message = _('This field is required.') missing_message = _('This field is required.')
@ -236,12 +233,12 @@ class BaseUniqueForValidator(object):
}, code='unique') }, code='unique')
def __repr__(self): def __repr__(self):
return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % ( return '<%s(queryset=%s, field=%s, date_field=%s)>' % (
self.__class__.__name__, self.__class__.__name__,
smart_repr(self.queryset), smart_repr(self.queryset),
smart_repr(self.field), smart_repr(self.field),
smart_repr(self.date_field) smart_repr(self.date_field)
)) )
class UniqueForDateValidator(BaseUniqueForValidator): class UniqueForDateValidator(BaseUniqueForValidator):

View File

@ -1,6 +1,3 @@
# coding: utf-8
from __future__ import unicode_literals
import re import re
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -13,7 +10,7 @@ from rest_framework.templatetags.rest_framework import replace_query_param
from rest_framework.utils.mediatypes import _MediaType from rest_framework.utils.mediatypes import _MediaType
class BaseVersioning(object): class BaseVersioning:
default_version = api_settings.DEFAULT_VERSION default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM version_param = api_settings.VERSION_PARAM
@ -87,7 +84,7 @@ class URLPathVersioning(BaseVersioning):
kwargs = {} if (kwargs is None) else kwargs kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version kwargs[self.version_param] = request.version
return super(URLPathVersioning, self).reverse( return super().reverse(
viewname, args, kwargs, request, format, **extra viewname, args, kwargs, request, format, **extra
) )
@ -133,7 +130,7 @@ class NamespaceVersioning(BaseVersioning):
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None: if request.version is not None:
viewname = self.get_versioned_viewname(viewname, request) viewname = self.get_versioned_viewname(viewname, request)
return super(NamespaceVersioning, self).reverse( return super().reverse(
viewname, args, kwargs, request, format, **extra viewname, args, kwargs, request, format, **extra
) )
@ -179,7 +176,7 @@ class QueryParameterVersioning(BaseVersioning):
return version return version
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): 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 viewname, args, kwargs, request, format, **extra
) )
if request.version is not None: if request.version is not None:

View File

@ -1,8 +1,6 @@
""" """
Provides an APIView class that is the base of all views in REST framework. Provides an APIView class that is the base of all views in REST framework.
""" """
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import connection, models, transaction from django.db import connection, models, transaction
@ -137,7 +135,7 @@ class APIView(View):
) )
cls.queryset._fetch_all = force_evaluation cls.queryset._fetch_all = force_evaluation
view = super(APIView, cls).as_view(**initkwargs) view = super().as_view(**initkwargs)
view.cls = cls view.cls = cls
view.initkwargs = initkwargs view.initkwargs = initkwargs

View File

@ -16,8 +16,6 @@ automatically.
router.register(r'users', UserViewSet, 'user') router.register(r'users', UserViewSet, 'user')
urlpatterns = router.urls urlpatterns = router.urls
""" """
from __future__ import unicode_literals
from collections import OrderedDict from collections import OrderedDict
from functools import update_wrapper from functools import update_wrapper
from inspect import getmembers from inspect import getmembers
@ -34,7 +32,7 @@ def _is_extra_action(attr):
return hasattr(attr, 'mapping') return hasattr(attr, 'mapping')
class ViewSetMixin(object): class ViewSetMixin:
""" """
This is the magic. This is the magic.
@ -134,7 +132,7 @@ class ViewSetMixin(object):
""" """
Set the `.action` attribute on the view, depending on the request method. 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() method = request.method.lower()
if method == 'options': if method == 'options':
# This is a special case as we always provide handling for the # This is a special case as we always provide handling for the

View File

@ -1,6 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python3
from __future__ import print_function
import subprocess import subprocess
import sys import sys

View File

@ -1,6 +1,3 @@
[bdist_wheel]
universal = 1
[metadata] [metadata]
license_file = LICENSE.md license_file = LICENSE.md

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os import os
import re import re
import shutil import shutil
@ -8,6 +7,34 @@ from io import open
from setuptools import find_packages, setup from setuptools import find_packages, setup
CURRENT_PYTHON = sys.version_info[:2]
REQUIRED_PYTHON = (3, 4)
# This check and everything above must remain compatible with Python 2.7.
if CURRENT_PYTHON < REQUIRED_PYTHON:
sys.stderr.write("""
==========================
Unsupported Python version
==========================
This version of Django REST Framework requires Python {}.{}, but you're trying
to install it on Python {}.{}.
This may be because you are using a version of pip that doesn't
understand the python_requires classifier. Make sure you
have pip >= 9.0 and setuptools >= 24.2, then try again:
$ python -m pip install --upgrade pip setuptools
$ python -m pip install djangorestframework
This will install the latest version of Django REST Framework which works on
your version of Python. If you can't upgrade your pip (or Python), request
an older version of Django REST Framework:
$ python -m pip install "django<3.10"
""".format(*(REQUIRED_PYTHON + CURRENT_PYTHON)))
sys.exit(1)
def read(f): def read(f):
return open(f, 'r', encoding='utf-8').read() return open(f, 'r', encoding='utf-8').read()
@ -52,7 +79,7 @@ setup(
packages=find_packages(exclude=['tests*']), packages=find_packages(exclude=['tests*']),
include_package_data=True, include_package_data=True,
install_requires=[], install_requires=[],
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", python_requires=">=3.4",
zip_safe=False, zip_safe=False,
classifiers=[ classifiers=[
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',
@ -66,13 +93,12 @@ setup(
'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Programming Language :: Python', 'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP',
] ]
) )

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,3 @@
# coding: utf-8
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models

View File

@ -1,7 +1,3 @@
# coding: utf-8
from __future__ import unicode_literals
import base64 import base64
import pytest import pytest
@ -10,7 +6,6 @@ from django.conf.urls import include, url
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from rest_framework import ( from rest_framework import (
HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status
@ -253,7 +248,7 @@ class SessionAuthTests(TestCase):
assert response.status_code == status.HTTP_403_FORBIDDEN assert response.status_code == status.HTTP_403_FORBIDDEN
class BaseTokenAuthTests(object): class BaseTokenAuthTests:
"""Token authentication""" """Token authentication"""
model = None model = None
path = None path = None
@ -381,7 +376,7 @@ class TokenAuthTests(BaseTokenAuthTests, TestCase):
"""Ensure generate_key returns a string""" """Ensure generate_key returns a string"""
token = self.model() token = self.model()
key = token.generate_key() key = token.generate_key()
assert isinstance(key, six.string_types) assert isinstance(key, str)
def test_token_login_json(self): def test_token_login_json(self):
"""Ensure token login view using JSON POST works.""" """Ensure token login view using JSON POST works."""
@ -534,7 +529,7 @@ class BasicAuthenticationUnitTests(TestCase):
def test_basic_authentication_raises_error_if_user_not_active(self): def test_basic_authentication_raises_error_if_user_not_active(self):
from rest_framework import authentication from rest_framework import authentication
class MockUser(object): class MockUser:
is_active = False is_active = False
old_authenticate = authentication.authenticate old_authenticate = authentication.authenticate
authentication.authenticate = lambda **kwargs: MockUser() authentication.authenticate = lambda **kwargs: MockUser()

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import include, url from django.conf.urls import include, url
from .views import MockView from .views import MockView

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from .views import MockView from .views import MockView

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase, override_settings from django.test import TestCase, override_settings

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from rest_framework import authentication, renderers from rest_framework import authentication, renderers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView

View File

@ -1,14 +1,10 @@
from __future__ import unicode_literals
from django.contrib.contenttypes.fields import ( from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation GenericForeignKey, GenericRelation
) )
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Tag(models.Model): class Tag(models.Model):
""" """
Tags have a descriptive slug, and are attached to an arbitrary object. Tags have a descriptive slug, and are attached to an arbitrary object.
@ -22,7 +18,6 @@ class Tag(models.Model):
return self.tag return self.tag
@python_2_unicode_compatible
class Bookmark(models.Model): class Bookmark(models.Model):
""" """
A URL bookmark that may have multiple tags attached. A URL bookmark that may have multiple tags attached.
@ -34,7 +29,6 @@ class Bookmark(models.Model):
return 'Bookmark: %s' % self.url return 'Bookmark: %s' % self.url
@python_2_unicode_compatible
class Note(models.Model): class Note(models.Model):
""" """
A textual note that may have multiple tags attached. A textual note that may have multiple tags attached.

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from rest_framework import serializers from rest_framework import serializers

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import uuid import uuid
from django.db import models from django.db import models

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import os import os
import tempfile import tempfile
import unittest import unittest

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import unittest import unittest
from django.conf.urls import url from django.conf.urls import url
@ -38,7 +36,7 @@ class APIExceptionView(APIView):
class NonAtomicAPIExceptionView(APIView): class NonAtomicAPIExceptionView(APIView):
@transaction.non_atomic_requests @transaction.non_atomic_requests
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs) return super().dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
BasicModel.objects.all() BasicModel.objects.all()

View File

@ -1,9 +1,10 @@
from io import StringIO
import pytest import pytest
from django.contrib.admin import site from django.contrib.admin import site
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management import CommandError, call_command from django.core.management import CommandError, call_command
from django.test import TestCase from django.test import TestCase
from django.utils.six import StringIO
from rest_framework.authtoken.admin import TokenAdmin from rest_framework.authtoken.admin import TokenAdmin
from rest_framework.authtoken.management.commands.drf_create_token import \ from rest_framework.authtoken.management.commands.drf_create_token import \

View File

@ -28,7 +28,7 @@ class TestSimpleBoundField:
assert serializer['text'].value == 'abc' assert serializer['text'].value == 'abc'
assert serializer['text'].errors is None assert serializer['text'].errors is None
assert serializer['text'].name == 'text' 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'].errors is None
assert serializer['amount'].name == 'amount' assert serializer['amount'].name == 'amount'
@ -43,7 +43,7 @@ class TestSimpleBoundField:
assert serializer['text'].value == 'x' * 1000 assert serializer['text'].value == 'x' * 1000
assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.'] assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.']
assert serializer['text'].name == 'text' 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'].errors is None
assert serializer['amount'].name == 'amount' assert serializer['amount'].name == 'amount'

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import pytest import pytest
from django.test import TestCase from django.test import TestCase

View File

@ -1,9 +1,4 @@
# -- coding: utf-8 --
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from django.utils.encoding import python_2_unicode_compatible
from rest_framework.compat import apply_markdown from rest_framework.compat import apply_markdown
from rest_framework.utils.formatting import dedent from rest_framework.utils.formatting import dedent
@ -157,8 +152,8 @@ class TestViewNamesAndDescriptions(TestCase):
""" """
# use a mock object instead of gettext_lazy to ensure that we can't end # use a mock object instead of gettext_lazy to ensure that we can't end
# up with a test case string in our l10n catalog # up with a test case string in our l10n catalog
@python_2_unicode_compatible
class MockLazyStr(object): class MockLazyStr:
def __init__(self, string): def __init__(self, string):
self.s = string self.s = string

View File

@ -10,7 +10,7 @@ from rest_framework.compat import coreapi
from rest_framework.utils.encoders import JSONEncoder from rest_framework.utils.encoders import JSONEncoder
class MockList(object): class MockList:
def tolist(self): def tolist(self):
return [1, 2, 3] return [1, 2, 3]

View File

@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.utils import six, translation from django.utils import translation
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import ( from rest_framework.exceptions import (
@ -46,12 +43,12 @@ class ExceptionTestCase(TestCase):
exception = Throttled(wait=2) exception = Throttled(wait=2)
assert exception.get_full_details() == { assert exception.get_full_details() == {
'message': 'Request was throttled. Expected available in {} seconds.'.format(2 if six.PY3 else 2.), 'message': 'Request was throttled. Expected available in {} seconds.'.format(2),
'code': 'throttled'} 'code': 'throttled'}
exception = Throttled(wait=2, detail='Slow down!') exception = Throttled(wait=2, detail='Slow down!')
assert exception.get_full_details() == { assert exception.get_full_details() == {
'message': 'Slow down! Expected available in {} seconds.'.format(2 if six.PY3 else 2.), 'message': 'Slow down! Expected available in {} seconds.'.format(2),
'code': 'throttled'} 'code': 'throttled'}
@ -92,7 +89,7 @@ class TranslationTests(TestCase):
def test_message(self): def test_message(self):
# this test largely acts as a sanity test to ensure the translation files are present. # this test largely acts as a sanity test to ensure the translation files are present.
self.assertEqual(_('A server error occurred.'), 'Une erreur du serveur est survenue.') self.assertEqual(_('A server error occurred.'), 'Une erreur du serveur est survenue.')
self.assertEqual(six.text_type(APIException()), 'Une erreur du serveur est survenue.') self.assertEqual(str(APIException()), 'Une erreur du serveur est survenue.')
def test_server_error(): def test_server_error():

View File

@ -10,7 +10,6 @@ import pytz
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.http import QueryDict from django.http import QueryDict
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from django.utils.timezone import activate, deactivate, override, utc from django.utils.timezone import activate, deactivate, override, utc
import rest_framework import rest_framework
@ -167,7 +166,7 @@ class TestEmpty:
""" """
field = serializers.IntegerField(default=123) field = serializers.IntegerField(default=123)
output = field.run_validation() output = field.run_validation()
assert output is 123 assert output == 123
class TestSource: class TestSource:
@ -193,7 +192,7 @@ class TestSource:
class ExampleSerializer(serializers.Serializer): class ExampleSerializer(serializers.Serializer):
example_field = serializers.CharField(source='example_callable') example_field = serializers.CharField(source='example_callable')
class ExampleInstance(object): class ExampleInstance:
def example_callable(self): def example_callable(self):
return 'example callable value' return 'example callable value'
@ -204,7 +203,7 @@ class TestSource:
class ExampleSerializer(serializers.Serializer): class ExampleSerializer(serializers.Serializer):
example_field = serializers.CharField(source='example_callable', read_only=True) example_field = serializers.CharField(source='example_callable', read_only=True)
class ExampleInstance(object): class ExampleInstance:
def example_callable(self): def example_callable(self):
raise AttributeError('method call failed') raise AttributeError('method call failed')
@ -754,7 +753,7 @@ class TestCharField(FieldValues):
def raise_exception(value): def raise_exception(value):
raise exceptions.ValidationError('Raised error') 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) field = serializers.CharField(validators=validators)
with pytest.raises(serializers.ValidationError) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(value) field.run_validation(value)
@ -822,7 +821,7 @@ class TestSlugField(FieldValues):
validation_error = False validation_error = False
try: try:
field.run_validation(u'slug-99-\u0420') field.run_validation('slug-99-\u0420')
except serializers.ValidationError: except serializers.ValidationError:
validation_error = True validation_error = True
@ -1148,7 +1147,7 @@ class TestLocalizedDecimalField(TestCase):
def test_localize_forces_coerce_to_string(self): def test_localize_forces_coerce_to_string(self):
field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True) field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True)
assert isinstance(field.to_representation(Decimal('1.1')), six.string_types) assert isinstance(field.to_representation(Decimal('1.1')), str)
class TestQuantizedValueForDecimal(TestCase): class TestQuantizedValueForDecimal(TestCase):
@ -1219,7 +1218,7 @@ class TestDateField(FieldValues):
outputs = { outputs = {
datetime.date(2001, 1, 1): '2001-01-01', datetime.date(2001, 1, 1): '2001-01-01',
'2001-01-01': '2001-01-01', '2001-01-01': '2001-01-01',
six.text_type('2016-01-10'): '2016-01-10', str('2016-01-10'): '2016-01-10',
None: None, None: None,
'': None, '': None,
} }
@ -1286,7 +1285,7 @@ class TestDateTimeField(FieldValues):
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00Z', datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00Z',
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00Z', datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00Z',
'2001-01-01T00:00:00': '2001-01-01T00:00:00', '2001-01-01T00:00:00': '2001-01-01T00:00:00',
six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00', str('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
None: None, None: None,
'': None, '': None,
} }
@ -1628,7 +1627,7 @@ class TestChoiceField(FieldValues):
] ]
) )
field.choices = [1] field.choices = [1]
assert field.run_validation(1) is 1 assert field.run_validation(1) == 1
with pytest.raises(serializers.ValidationError) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(2) field.run_validation(2)
assert exc_info.value.detail == ['"2" is not a valid choice.'] assert exc_info.value.detail == ['"2" is not a valid choice.']

View File

@ -1,6 +1,5 @@
from __future__ import unicode_literals
import datetime import datetime
from importlib import reload as reload_module
import pytest import pytest
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -8,7 +7,6 @@ from django.db import models
from django.db.models.functions import Concat, Upper from django.db.models.functions import Concat, Upper
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.utils.six.moves import reload_module
from rest_framework import filters, generics, serializers from rest_framework import filters, generics, serializers
from rest_framework.compat import coreschema from rest_framework.compat import coreschema
@ -163,7 +161,7 @@ class SearchFilterTests(TestCase):
def get_search_fields(self, view, request): def get_search_fields(self, view, request):
if request.query_params.get('title_only'): if request.query_params.get('title_only'):
return ('$title',) return ('$title',)
return super(CustomSearchFilter, self).get_search_fields(view, request) return super().get_search_fields(view, request)
class SearchListView(generics.ListAPIView): class SearchListView(generics.ListAPIView):
queryset = SearchFilterModel.objects.all() queryset = SearchFilterModel.objects.all()

View File

@ -1,11 +1,10 @@
from __future__ import unicode_literals import io
import pytest import pytest
from django.conf.urls import url from django.conf.urls import url
from django.core.management import call_command from django.core.management import call_command
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.utils import six
from rest_framework.compat import coreapi from rest_framework.compat import coreapi
from rest_framework.utils import formatting, json from rest_framework.utils import formatting, json
@ -28,9 +27,8 @@ class GenerateSchemaTests(TestCase):
"""Tests for management command generateschema.""" """Tests for management command generateschema."""
def setUp(self): def setUp(self):
self.out = six.StringIO() self.out = io.StringIO()
@pytest.mark.skipif(six.PY2, reason='PyYAML unicode output is malformed on PY2.')
def test_renders_default_schema_with_custom_title_url_and_description(self): def test_renders_default_schema_with_custom_title_url_and_description(self):
expected_out = """info: expected_out = """info:
description: Sample description description: Sample description

View File

@ -1,11 +1,8 @@
from __future__ import unicode_literals
import pytest import pytest
from django.db import models from django.db import models
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import generics, renderers, serializers, status from rest_framework import generics, renderers, serializers, status
from rest_framework.response import Response from rest_framework.response import Response
@ -245,7 +242,7 @@ class TestInstanceView(TestCase):
with self.assertNumQueries(2): with self.assertNumQueries(2):
response = self.view(request, pk=1).render() response = self.view(request, pk=1).render()
assert response.status_code == status.HTTP_204_NO_CONTENT assert response.status_code == status.HTTP_204_NO_CONTENT
assert response.content == six.b('') assert response.content == b''
ids = [obj.id for obj in self.objects.all()] ids = [obj.id for obj in self.objects.all()]
assert ids == [2, 3] assert ids == [2, 3]
@ -291,7 +288,7 @@ class TestInstanceView(TestCase):
""" """
data = {'text': 'foo'} data = {'text': 'foo'}
filtered_out_pk = BasicModel.objects.filter(text='filtered out')[0].pk 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() response = self.view(request, pk=filtered_out_pk).render()
assert response.status_code == status.HTTP_404_NOT_FOUND assert response.status_code == status.HTTP_404_NOT_FOUND
@ -446,12 +443,12 @@ class TestM2MBrowsableAPI(TestCase):
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
class InclusiveFilterBackend(object): class InclusiveFilterBackend:
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
return queryset.filter(text='foo') return queryset.filter(text='foo')
class ExclusiveFilterBackend(object): class ExclusiveFilterBackend:
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
return queryset.filter(text='other') return queryset.filter(text='other')
@ -653,7 +650,7 @@ class ApiViewsTests(TestCase):
class GetObjectOr404Tests(TestCase): class GetObjectOr404Tests(TestCase):
def setUp(self): def setUp(self):
super(GetObjectOr404Tests, self).setUp() super().setUp()
self.uuid_object = UUIDForeignKeyTarget.objects.create(name='bar') self.uuid_object = UUIDForeignKeyTarget.objects.create(name='bar')
def test_get_object_or_404_with_valid_uuid(self): def test_get_object_or_404_with_valid_uuid(self):

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import django.template.loader import django.template.loader
import pytest import pytest
from django.conf.urls import url from django.conf.urls import url
@ -7,7 +5,6 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http import Http404 from django.http import Http404
from django.template import TemplateDoesNotExist, engines from django.template import TemplateDoesNotExist, engines
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from rest_framework import status from rest_framework import status
from rest_framework.decorators import api_view, renderer_classes from rest_framework.decorators import api_view, renderer_classes
@ -47,7 +44,7 @@ urlpatterns = [
@override_settings(ROOT_URLCONF='tests.test_htmlrenderer') @override_settings(ROOT_URLCONF='tests.test_htmlrenderer')
class TemplateHTMLRendererTests(TestCase): class TemplateHTMLRendererTests(TestCase):
def setUp(self): def setUp(self):
class MockResponse(object): class MockResponse:
template_name = None template_name = None
self.mock_response = MockResponse() self.mock_response = MockResponse()
self._monkey_patch_get_template() self._monkey_patch_get_template()
@ -85,13 +82,13 @@ class TemplateHTMLRendererTests(TestCase):
def test_not_found_html_view(self): def test_not_found_html_view(self):
response = self.client.get('/not_found') response = self.client.get('/not_found')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.content, six.b("404 Not Found")) self.assertEqual(response.content, b"404 Not Found")
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
def test_permission_denied_html_view(self): def test_permission_denied_html_view(self):
response = self.client.get('/permission_denied') response = self.client.get('/permission_denied')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(response.content, six.b("403 Forbidden")) self.assertEqual(response.content, b"403 Forbidden")
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
# 2 tests below are based on order of if statements in corresponding method # 2 tests below are based on order of if statements in corresponding method
@ -105,14 +102,14 @@ class TemplateHTMLRendererTests(TestCase):
def test_get_template_names_returns_view_template_name(self): def test_get_template_names_returns_view_template_name(self):
renderer = TemplateHTMLRenderer() renderer = TemplateHTMLRenderer()
class MockResponse(object): class MockResponse:
template_name = None template_name = None
class MockView(object): class MockView:
def get_template_names(self): def get_template_names(self):
return ['template from get_template_names method'] return ['template from get_template_names method']
class MockView2(object): class MockView2:
template_name = 'template from template_name attribute' template_name = 'template from template_name attribute'
template_name = renderer.get_template_names(self.mock_response, template_name = renderer.get_template_names(self.mock_response,
@ -156,12 +153,11 @@ class TemplateHTMLRendererExceptionTests(TestCase):
response = self.client.get('/not_found') response = self.client.get('/not_found')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertTrue(response.content in ( self.assertTrue(response.content in (
six.b("404: Not found"), six.b("404 Not Found"))) b"404: Not found", b"404 Not Found"))
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
def test_permission_denied_html_view_with_template(self): def test_permission_denied_html_view_with_template(self):
response = self.client.get('/permission_denied') response = self.client.get('/permission_denied')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertTrue(response.content in ( self.assertTrue(response.content in (b"403: Permission denied", b"403 Forbidden"))
six.b("403: Permission denied"), six.b("403 Forbidden")))
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import pytest import pytest
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models

View File

@ -22,7 +22,7 @@ urlpatterns = [
] ]
class RequestUserMiddleware(object): class RequestUserMiddleware:
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response
@ -34,7 +34,7 @@ class RequestUserMiddleware(object):
return response return response
class RequestPOSTMiddleware(object): class RequestPOSTMiddleware:
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response

View File

@ -5,8 +5,6 @@ shortcuts for automatically creating serializers based on a given model class.
These tests deal with ensuring that we correctly map the model fields onto These tests deal with ensuring that we correctly map the model fields onto
an appropriate set of serializer fields for each case. an appropriate set of serializer fields for each case.
""" """
from __future__ import unicode_literals
import datetime import datetime
import decimal import decimal
import sys import sys
@ -20,10 +18,9 @@ from django.core.validators import (
) )
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import serializers from rest_framework import serializers
from rest_framework.compat import postgres_fields, unicode_repr from rest_framework.compat import postgres_fields
from .models import NestedForeignKeySource from .models import NestedForeignKeySource
@ -193,7 +190,7 @@ class TestRegularFieldMappings(TestCase):
file_path_field = FilePathField(path='/tmp/') file_path_field = FilePathField(path='/tmp/')
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_field_options(self): def test_field_options(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -212,14 +209,7 @@ class TestRegularFieldMappings(TestCase):
descriptive_field = IntegerField(help_text='Some help text', label='A label') descriptive_field = IntegerField(help_text='Some help text', label='A label')
choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green'))) choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')))
""") """)
if six.PY2: self.assertEqual(repr(TestSerializer()), expected)
# This particular case is too awkward to resolve fully across
# both py2 and py3.
expected = expected.replace(
"('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')",
"(u'red', u'Red'), (u'blue', u'Blue'), (u'green', u'Green')"
)
self.assertEqual(unicode_repr(TestSerializer()), expected)
# merge this into test_regular_fields / RegularFieldsModel when # merge this into test_regular_fields / RegularFieldsModel when
# Django 2.1 is the minimum supported version # Django 2.1 is the minimum supported version
@ -238,7 +228,7 @@ class TestRegularFieldMappings(TestCase):
field = BooleanField(allow_null=True, required=False) field = BooleanField(allow_null=True, required=False)
""") """)
self.assertEqual(unicode_repr(NullableBooleanSerializer()), expected) self.assertEqual(repr(NullableBooleanSerializer()), expected)
def test_method_field(self): def test_method_field(self):
""" """
@ -382,7 +372,7 @@ class TestDurationFieldMapping(TestCase):
id = IntegerField(label='ID', read_only=True) id = IntegerField(label='ID', read_only=True)
duration_field = DurationField() duration_field = DurationField()
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_duration_field_with_validators(self): def test_duration_field_with_validators(self):
class ValidatedDurationFieldModel(models.Model): class ValidatedDurationFieldModel(models.Model):
@ -407,7 +397,7 @@ class TestDurationFieldMapping(TestCase):
id = IntegerField(label='ID', read_only=True) id = IntegerField(label='ID', read_only=True)
duration_field = DurationField(max_value=datetime.timedelta(days=3), min_value=datetime.timedelta(days=1)) duration_field = DurationField(max_value=datetime.timedelta(days=3), min_value=datetime.timedelta(days=1))
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
class TestGenericIPAddressFieldValidation(TestCase): class TestGenericIPAddressFieldValidation(TestCase):
@ -424,7 +414,7 @@ class TestGenericIPAddressFieldValidation(TestCase):
self.assertFalse(s.is_valid()) self.assertFalse(s.is_valid())
self.assertEqual(1, len(s.errors['address']), self.assertEqual(1, len(s.errors['address']),
'Unexpected number of validation errors: ' 'Unexpected number of validation errors: '
'{0}'.format(s.errors)) '{}'.format(s.errors))
@pytest.mark.skipif('not postgres_fields') @pytest.mark.skipif('not postgres_fields')
@ -442,7 +432,7 @@ class TestPosgresFieldsMapping(TestCase):
TestSerializer(): TestSerializer():
hstore_field = HStoreField() hstore_field = HStoreField()
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_array_field(self): def test_array_field(self):
class ArrayFieldModel(models.Model): class ArrayFieldModel(models.Model):
@ -457,7 +447,7 @@ class TestPosgresFieldsMapping(TestCase):
TestSerializer(): TestSerializer():
array_field = ListField(child=CharField(label='Array field', validators=[<django.core.validators.MaxLengthValidator object>])) array_field = ListField(child=CharField(label='Array field', validators=[<django.core.validators.MaxLengthValidator object>]))
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_json_field(self): def test_json_field(self):
class JSONFieldModel(models.Model): class JSONFieldModel(models.Model):
@ -472,7 +462,7 @@ class TestPosgresFieldsMapping(TestCase):
TestSerializer(): TestSerializer():
json_field = JSONField(style={'base_template': 'textarea.html'}) json_field = JSONField(style={'base_template': 'textarea.html'})
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
# Tests for relational field mappings. # Tests for relational field mappings.
@ -530,7 +520,7 @@ class TestRelationalFieldMappings(TestCase):
many_to_many = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all()) many_to_many = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all())
through = PrimaryKeyRelatedField(many=True, read_only=True) through = PrimaryKeyRelatedField(many=True, read_only=True)
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_nested_relations(self): def test_nested_relations(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -555,7 +545,7 @@ class TestRelationalFieldMappings(TestCase):
id = IntegerField(label='ID', read_only=True) id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_hyperlinked_relations(self): def test_hyperlinked_relations(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
@ -571,7 +561,7 @@ class TestRelationalFieldMappings(TestCase):
many_to_many = HyperlinkedRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail') many_to_many = HyperlinkedRelatedField(allow_empty=False, many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail') through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail')
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_nested_hyperlinked_relations(self): def test_nested_hyperlinked_relations(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
@ -596,7 +586,7 @@ class TestRelationalFieldMappings(TestCase):
url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail') url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_nested_hyperlinked_relations_starred_source(self): def test_nested_hyperlinked_relations_starred_source(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
@ -627,7 +617,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
self.maxDiff = None self.maxDiff = None
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_nested_unique_together_relations(self): def test_nested_unique_together_relations(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
@ -646,14 +636,7 @@ class TestRelationalFieldMappings(TestCase):
url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail') url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail')
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
if six.PY2: self.assertEqual(repr(TestSerializer()), expected)
# This case is also too awkward to resolve fully across both py2
# and py3. (See above)
expected = expected.replace(
"('foreign_key', 'one_to_one')",
"(u'foreign_key', u'one_to_one')"
)
self.assertEqual(unicode_repr(TestSerializer()), expected)
def test_pk_reverse_foreign_key(self): def test_pk_reverse_foreign_key(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -667,7 +650,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all()) reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_pk_reverse_one_to_one(self): def test_pk_reverse_one_to_one(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -681,7 +664,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all()) reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all())
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_pk_reverse_many_to_many(self): def test_pk_reverse_many_to_many(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -695,7 +678,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all()) reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_pk_reverse_through(self): def test_pk_reverse_through(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -709,7 +692,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_through = PrimaryKeyRelatedField(many=True, read_only=True) reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)
""") """)
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
class DisplayValueTargetModel(models.Model): class DisplayValueTargetModel(models.Model):
@ -1078,9 +1061,9 @@ class TestMetaInheritance(TestCase):
char_field = CharField(max_length=100) char_field = CharField(max_length=100)
non_model_field = CharField() non_model_field = CharField()
""") """)
self.assertEqual(unicode_repr(ChildSerializer()), child_expected) self.assertEqual(repr(ChildSerializer()), child_expected)
self.assertEqual(unicode_repr(TestSerializer()), test_expected) self.assertEqual(repr(TestSerializer()), test_expected)
self.assertEqual(unicode_repr(ChildSerializer()), child_expected) self.assertEqual(repr(ChildSerializer()), child_expected)
class OneToOneTargetTestModel(models.Model): class OneToOneTargetTestModel(models.Model):
@ -1149,14 +1132,14 @@ class Issue3674Test(TestCase):
title = CharField(max_length=64) title = CharField(max_length=64)
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all()) children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
""") """)
self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected) self.assertEqual(repr(TestParentModelSerializer()), parent_expected)
child_expected = dedent(""" child_expected = dedent("""
TestChildModelSerializer(): TestChildModelSerializer():
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>]) value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all()) parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
""") """)
self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected) self.assertEqual(repr(TestChildModelSerializer()), child_expected)
def test_nonID_PK_foreignkey_model_serializer(self): def test_nonID_PK_foreignkey_model_serializer(self):
@ -1248,7 +1231,7 @@ class TestFieldSource(TestCase):
number_field = IntegerField(source='integer_field') number_field = IntegerField(source='integer_field')
""") """)
self.maxDiff = None self.maxDiff = None
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
class Issue6110TestModel(models.Model): class Issue6110TestModel(models.Model):

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import pytest import pytest
from django.http import Http404 from django.http import Http404
from django.test import TestCase from django.test import TestCase
@ -80,7 +78,7 @@ class TestAcceptedMediaType(TestCase):
assert str(mediatype) == 'test/*; foo=bar' assert str(mediatype) == 'test/*; foo=bar'
def test_raise_error_if_no_suitable_renderers_found(self): def test_raise_error_if_no_suitable_renderers_found(self):
class MockRenderer(object): class MockRenderer:
format = 'xml' format = 'xml'
renderers = [MockRenderer()] renderers = [MockRenderer()]
with pytest.raises(Http404): with pytest.raises(Http404):

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase

View File

@ -1,11 +1,7 @@
# coding: utf-8
from __future__ import unicode_literals
import pytest import pytest
from django.core.paginator import Paginator as DjangoPaginator from django.core.paginator import Paginator as DjangoPaginator
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import ( from rest_framework import (
exceptions, filters, generics, pagination, serializers, status exceptions, filters, generics, pagination, serializers, status
@ -208,7 +204,7 @@ class TestPageNumberPagination:
] ]
} }
assert self.pagination.display_page_controls assert self.pagination.display_page_controls
assert isinstance(self.pagination.to_html(), six.text_type) assert isinstance(self.pagination.to_html(), str)
def test_second_page(self): def test_second_page(self):
request = Request(factory.get('/', {'page': 2})) request = Request(factory.get('/', {'page': 2}))
@ -314,7 +310,7 @@ class TestPageNumberPaginationOverride:
] ]
} }
assert not self.pagination.display_page_controls assert not self.pagination.display_page_controls
assert isinstance(self.pagination.to_html(), six.text_type) assert isinstance(self.pagination.to_html(), str)
def test_invalid_page(self): def test_invalid_page(self):
request = Request(factory.get('/', {'page': 'invalid'})) request = Request(factory.get('/', {'page': 'invalid'}))
@ -369,7 +365,7 @@ class TestLimitOffset:
] ]
} }
assert self.pagination.display_page_controls assert self.pagination.display_page_controls
assert isinstance(self.pagination.to_html(), six.text_type) assert isinstance(self.pagination.to_html(), str)
def test_pagination_not_applied_if_limit_or_default_limit_not_set(self): def test_pagination_not_applied_if_limit_or_default_limit_not_set(self):
class MockPagination(pagination.LimitOffsetPagination): class MockPagination(pagination.LimitOffsetPagination):
@ -503,7 +499,7 @@ class TestLimitOffset:
content = self.get_paginated_content(queryset) content = self.get_paginated_content(queryset)
next_limit = self.pagination.default_limit next_limit = self.pagination.default_limit
next_offset = 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 queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
assert content.get('next') == next_url assert content.get('next') == next_url
@ -516,7 +512,7 @@ class TestLimitOffset:
content = self.get_paginated_content(queryset) content = self.get_paginated_content(queryset)
next_limit = self.pagination.default_limit next_limit = self.pagination.default_limit
next_offset = 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 queryset == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
assert content.get('next') == next_url assert content.get('next') == next_url
@ -532,9 +528,9 @@ class TestLimitOffset:
max_limit = self.pagination.max_limit max_limit = self.pagination.max_limit
next_offset = offset + max_limit next_offset = offset + max_limit
prev_offset = offset - max_limit prev_offset = offset - max_limit
base_url = 'http://testserver/?limit={0}'.format(max_limit) base_url = 'http://testserver/?limit={}'.format(max_limit)
next_url = base_url + '&offset={0}'.format(next_offset) next_url = base_url + '&offset={}'.format(next_offset)
prev_url = base_url + '&offset={0}'.format(prev_offset) prev_url = base_url + '&offset={}'.format(prev_offset)
assert queryset == list(range(51, 66)) assert queryset == list(range(51, 66))
assert content.get('next') == next_url assert content.get('next') == next_url
assert content.get('previous') == prev_url assert content.get('previous') == prev_url
@ -632,7 +628,7 @@ class CursorPaginationTestsMixin:
assert current == [1, 1, 1, 1, 1] assert current == [1, 1, 1, 1, 1]
assert next == [1, 2, 3, 4, 4] assert next == [1, 2, 3, 4, 4]
assert isinstance(self.pagination.to_html(), six.text_type) assert isinstance(self.pagination.to_html(), str)
def test_cursor_pagination_with_page_size(self): def test_cursor_pagination_with_page_size(self):
(previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=20') (previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=20')
@ -799,11 +795,11 @@ class TestCursorPagination(CursorPaginationTestsMixin):
""" """
def setup(self): def setup(self):
class MockObject(object): class MockObject:
def __init__(self, idx): def __init__(self, idx):
self.created = idx self.created = idx
class MockQuerySet(object): class MockQuerySet:
def __init__(self, items): def __init__(self, items):
self.items = items self.items = items

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import io import io
import math import math
@ -11,7 +8,6 @@ from django.core.files.uploadhandler import (
) )
from django.http.request import RawPostDataException from django.http.request import RawPostDataException
from django.test import TestCase from django.test import TestCase
from django.utils.six import StringIO
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.parsers import ( from rest_framework.parsers import (
@ -34,7 +30,7 @@ class TestFormParser(TestCase):
""" Make sure the `QueryDict` works OK """ """ Make sure the `QueryDict` works OK """
parser = FormParser() parser = FormParser()
stream = StringIO(self.string) stream = io.StringIO(self.string)
data = parser.parse(stream) data = parser.parse(stream)
assert Form(data).is_valid() is True assert Form(data).is_valid() is True
@ -42,7 +38,7 @@ class TestFormParser(TestCase):
class TestFileUploadParser(TestCase): class TestFileUploadParser(TestCase):
def setUp(self): def setUp(self):
class MockRequest(object): class MockRequest:
pass pass
self.stream = io.BytesIO( self.stream = io.BytesIO(
"Test text file".encode('utf-8') "Test text file".encode('utf-8')

View File

@ -1,8 +1,7 @@
from __future__ import unicode_literals
import base64 import base64
import unittest import unittest
import warnings import warnings
from unittest import mock
import django import django
import pytest import pytest
@ -15,7 +14,7 @@ from rest_framework import (
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics, HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
permissions, serializers, status, views permissions, serializers, status, views
) )
from rest_framework.compat import PY36, is_guardian_installed, mock from rest_framework.compat import PY36, is_guardian_installed
from rest_framework.filters import DjangoObjectPermissionsFilter from rest_framework.filters import DjangoObjectPermissionsFilter
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
@ -331,14 +330,14 @@ class ObjectPermissionsIntegrationTests(TestCase):
everyone = Group.objects.create(name='everyone') everyone = Group.objects.create(name='everyone')
model_name = BasicPermModel._meta.model_name model_name = BasicPermModel._meta.model_name
app_label = BasicPermModel._meta.app_label app_label = BasicPermModel._meta.app_label
f = '{0}_{1}'.format f = '{}_{}'.format
perms = { perms = {
'view': f('view', model_name), 'view': f('view', model_name),
'change': f('change', model_name), 'change': f('change', model_name),
'delete': f('delete', model_name) 'delete': f('delete', model_name)
} }
for perm in perms.values(): for perm in perms.values():
perm = '{0}.{1}'.format(app_label, perm) perm = '{}.{}'.format(app_label, perm)
assign_perm(perm, everyone) assign_perm(perm, everyone)
everyone.user_set.add(*users.values()) everyone.user_set.add(*users.values())

View File

@ -26,7 +26,7 @@ class TestStringRelatedField(APISimpleTestCase):
assert representation == '<MockObject name=foo, pk=1>' assert representation == '<MockObject name=foo, pk=1>'
class MockApiSettings(object): class MockApiSettings:
def __init__(self, cutoff, cutoff_text): def __init__(self, cutoff, cutoff_text):
self.HTML_SELECT_CUTOFF = cutoff self.HTML_SELECT_CUTOFF = cutoff
self.HTML_SELECT_CUTOFF_TEXT = cutoff_text self.HTML_SELECT_CUTOFF_TEXT = cutoff_text

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from django.test import TestCase, override_settings from django.test import TestCase, override_settings

View File

@ -1,7 +1,4 @@
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import serializers from rest_framework import serializers
from tests.models import ( from tests.models import (
@ -263,7 +260,7 @@ class PKForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1) instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data) serializer = ForeignKeySourceSerializer(instance, data=data)
assert not serializer.is_valid() assert not serializer.is_valid()
assert serializer.errors == {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]} assert serializer.errors == {'target': ['Incorrect type. Expected pk value, received str.']}
def test_reverse_foreign_key_update(self): def test_reverse_foreign_key_update(self):
data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]} data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}
@ -562,7 +559,7 @@ class OneToOnePrimaryKeyTests(TestCase):
# When: Trying to create a second object # When: Trying to create a second object
second_source = OneToOnePKSourceSerializer(data=data) second_source = OneToOnePKSourceSerializer(data=data)
self.assertFalse(second_source.is_valid()) 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) self.assertDictEqual(second_source.errors, expected)
def test_one_to_one_when_primary_key_does_not_exist(self): def test_one_to_one_when_primary_key_does_not_exist(self):

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re import re
from collections import OrderedDict from collections import OrderedDict
@ -11,7 +8,6 @@ from django.db import models
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.template import loader from django.template import loader
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -175,7 +171,7 @@ class RendererEndToEndTests(TestCase):
resp = self.client.head('/') resp = self.client.head('/')
self.assertEqual(resp.status_code, DUMMYSTATUS) self.assertEqual(resp.status_code, DUMMYSTATUS)
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
self.assertEqual(resp.content, six.b('')) self.assertEqual(resp.content, b'')
def test_default_renderer_serializes_content_on_accept_any(self): def test_default_renderer_serializes_content_on_accept_any(self):
"""If the Accept header is set to */* the default renderer should serialize the response.""" """If the Accept header is set to */* the default renderer should serialize the response."""
@ -348,7 +344,7 @@ class JSONRendererTests(TestCase):
self.assertEqual(data, {'key': 'string value', '2': 3}) self.assertEqual(data, {'key': 'string value', '2': 3})
def test_render_obj_with_getitem(self): def test_render_obj_with_getitem(self):
class DictLike(object): class DictLike:
def __init__(self): def __init__(self):
self._dict = {} self._dict = {}
@ -647,7 +643,7 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
assert self.renderer.get_description({}, status_code=403) == '' assert self.renderer.get_description({}, status_code=403) == ''
def test_get_filter_form_returns_none_if_data_is_not_list_instance(self): def test_get_filter_form_returns_none_if_data_is_not_list_instance(self):
class DummyView(object): class DummyView:
get_queryset = None get_queryset = None
filter_backends = None filter_backends = None

View File

@ -1,8 +1,6 @@
""" """
Tests for content parsing, and form-overloaded content parsing. Tests for content parsing, and form-overloaded content parsing.
""" """
from __future__ import unicode_literals
import os.path import os.path
import tempfile import tempfile
@ -15,7 +13,6 @@ from django.contrib.sessions.middleware import SessionMiddleware
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.http.request import RawPostDataException from django.http.request import RawPostDataException
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from rest_framework import status from rest_framework import status
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
@ -82,7 +79,7 @@ class TestContentParsing(TestCase):
Ensure request.data returns content for POST request with Ensure request.data returns content for POST request with
non-form content. non-form content.
""" """
content = six.b('qwerty') content = b'qwerty'
content_type = 'text/plain' content_type = 'text/plain'
request = Request(factory.post('/', content, content_type=content_type)) request = Request(factory.post('/', content, content_type=content_type))
request.parsers = (PlainTextParser(),) request.parsers = (PlainTextParser(),)
@ -121,7 +118,7 @@ class TestContentParsing(TestCase):
Ensure request.data returns content for PUT request with Ensure request.data returns content for PUT request with
non-form content. non-form content.
""" """
content = six.b('qwerty') content = b'qwerty'
content_type = 'text/plain' content_type = 'text/plain'
request = Request(factory.put('/', content, content_type=content_type)) request = Request(factory.put('/', content, content_type=content_type))
request.parsers = (PlainTextParser(), ) request.parsers = (PlainTextParser(), )
@ -235,7 +232,7 @@ class TestUserSetter(TestCase):
This proves that when an AttributeError is raised inside of the request.user 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. property, that we can handle this and report the true, underlying error.
""" """
class AuthRaisesAttributeError(object): class AuthRaisesAttributeError:
def authenticate(self, request): def authenticate(self, request):
self.MISSPELLED_NAME_THAT_DOESNT_EXIST self.MISSPELLED_NAME_THAT_DOESNT_EXIST
@ -249,10 +246,6 @@ class TestUserSetter(TestCase):
with pytest.raises(WrappedAttributeError, match=expected): with pytest.raises(WrappedAttributeError, match=expected):
request.user request.user
# python 2 hasattr fails for *any* exception, not just AttributeError
if six.PY2:
return
with pytest.raises(WrappedAttributeError, match=expected): with pytest.raises(WrappedAttributeError, match=expected):
hasattr(request, 'user') hasattr(request, 'user')

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import unittest import unittest
from django.conf.urls import url from django.conf.urls import url

View File

@ -1,8 +1,5 @@
from __future__ import unicode_literals
from django.conf.urls import include, url from django.conf.urls import include, url
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six
from rest_framework import generics, routers, serializers, status, viewsets from rest_framework import generics, routers, serializers, status, viewsets
from rest_framework.parsers import JSONParser from rest_framework.parsers import JSONParser
@ -150,7 +147,7 @@ class RendererIntegrationTests(TestCase):
resp = self.client.head('/') resp = self.client.head('/')
self.assertEqual(resp.status_code, DUMMYSTATUS) self.assertEqual(resp.status_code, DUMMYSTATUS)
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
self.assertEqual(resp.content, six.b('')) self.assertEqual(resp.content, b'')
def test_default_renderer_serializes_content_on_accept_any(self): def test_default_renderer_serializes_content_on_accept_any(self):
"""If the Accept header is set to */* the default renderer should serialize the response.""" """If the Accept header is set to */* the default renderer should serialize the response."""
@ -260,7 +257,7 @@ class Issue807Tests(TestCase):
""" """
headers = {"HTTP_ACCEPT": RendererA.media_type} headers = {"HTTP_ACCEPT": RendererA.media_type}
resp = self.client.get('/', **headers) 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']) self.assertEqual(expected, resp['Content-Type'])
def test_if_there_is_charset_specified_on_renderer_it_gets_appended(self): def test_if_there_is_charset_specified_on_renderer_it_gets_appended(self):
@ -270,7 +267,7 @@ class Issue807Tests(TestCase):
""" """
headers = {"HTTP_ACCEPT": RendererC.media_type} headers = {"HTTP_ACCEPT": RendererC.media_type}
resp = self.client.get('/', **headers) 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']) self.assertEqual(expected, resp['Content-Type'])
def test_content_type_set_explicitly_on_response(self): def test_content_type_set_explicitly_on_response(self):

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.urls import NoReverseMatch from django.urls import NoReverseMatch
@ -19,7 +17,7 @@ urlpatterns = [
] ]
class MockVersioningScheme(object): class MockVersioningScheme:
def __init__(self, raise_error=False): def __init__(self, raise_error=False):
self.raise_error = raise_error self.raise_error = raise_error

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import warnings import warnings
from collections import namedtuple from collections import namedtuple

View File

@ -29,7 +29,7 @@ from .models import BasicModel, ForeignKeySource, ManyToManySource
factory = APIRequestFactory() factory = APIRequestFactory()
class MockUser(object): class MockUser:
def is_authenticated(self): def is_authenticated(self):
return True return True
@ -112,7 +112,7 @@ class ExampleViewSet(ModelViewSet):
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
assert self.request assert self.request
assert self.action assert self.action
return super(ExampleViewSet, self).get_serializer(*args, **kwargs) return super().get_serializer(*args, **kwargs)
@action(methods=['get', 'post'], detail=False) @action(methods=['get', 'post'], detail=False)
def documented_custom_action(self, request): 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') @pytest.mark.skipif(not coreapi, reason='coreapi is not installed')
class TestAutoSchemaAllowsFilters(object): class TestAutoSchemaAllowsFilters:
class MockAPIView(APIView): class MockAPIView(APIView):
filter_backends = [filters.OrderingFilter] filter_backends = [filters.OrderingFilter]

View File

@ -1,16 +1,13 @@
# coding: utf-8
from __future__ import unicode_literals
import inspect import inspect
import pickle import pickle
import re import re
import unittest from collections import ChainMap
import pytest import pytest
from django.db import models from django.db import models
from rest_framework import exceptions, fields, relations, serializers from rest_framework import exceptions, fields, relations, serializers
from rest_framework.compat import Mapping, unicode_repr from rest_framework.compat import Mapping
from rest_framework.fields import Field from rest_framework.fields import Field
from .models import ( from .models import (
@ -18,15 +15,9 @@ from .models import (
) )
from .utils import MockObject from .utils import MockObject
try:
from collections import ChainMap
except ImportError:
ChainMap = False
# Test serializer fields imports. # Test serializer fields imports.
# ------------------------------- # -------------------------------
class TestFieldImports: class TestFieldImports:
def is_field(self, name, value): def is_field(self, name, value):
return ( return (
@ -130,7 +121,6 @@ class TestSerializer:
assert not serializer.is_valid() assert not serializer.is_valid()
assert serializer.errors == {'non_field_errors': ['No data provided']} assert serializer.errors == {'non_field_errors': ['No data provided']}
@unittest.skipUnless(ChainMap, 'requires python 3.3')
def test_serialize_chainmap(self): def test_serialize_chainmap(self):
data = ChainMap({'char': 'abc'}, {'integer': 123}) data = ChainMap({'char': 'abc'}, {'integer': 123})
serializer = self.Serializer(data=data) serializer = self.Serializer(data=data)
@ -160,7 +150,7 @@ class TestSerializer:
to_internal_value() is expected to return a dict, but subclasses may to_internal_value() is expected to return a dict, but subclasses may
return application specific type. return application specific type.
""" """
class Point(object): class Point:
def __init__(self, srid, x, y): def __init__(self, srid, x, y):
self.srid = srid self.srid = srid
self.coords = (x, y) self.coords = (x, y)
@ -171,7 +161,7 @@ class TestSerializer:
latitude = serializers.FloatField(source='y') latitude = serializers.FloatField(source='y')
def to_internal_value(self, data): 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) return Point(srid=4326, **kwargs)
serializer = NestedPointSerializer(data={'longitude': 6.958307, 'latitude': 50.941357}) serializer = NestedPointSerializer(data={'longitude': 6.958307, 'latitude': 50.941357})
@ -201,7 +191,7 @@ class TestSerializer:
def raise_exception(value): def raise_exception(value):
raise exceptions.ValidationError('Raised error') 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): class ExampleSerializer(serializers.Serializer):
char = serializers.CharField(validators=validators) char = serializers.CharField(validators=validators)
integer = serializers.IntegerField() integer = serializers.IntegerField()
@ -397,7 +387,7 @@ class TestIncorrectlyConfigured:
class TestUnicodeRepr: class TestUnicodeRepr:
def test_unicode_repr(self): def test_repr(self):
class ExampleSerializer(serializers.Serializer): class ExampleSerializer(serializers.Serializer):
example = serializers.CharField() example = serializers.CharField()
@ -406,7 +396,7 @@ class TestUnicodeRepr:
self.example = '한국' self.example = '한국'
def __repr__(self): def __repr__(self):
return unicode_repr(self.example) return repr(self.example)
instance = ExampleObject() instance = ExampleObject()
serializer = ExampleSerializer(instance) serializer = ExampleSerializer(instance)
@ -609,7 +599,7 @@ class Test2555Regression:
def test_serializer_context(self): def test_serializer_context(self):
class NestedSerializer(serializers.Serializer): class NestedSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(NestedSerializer, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# .context should not cache # .context should not cache
self.context self.context

View File

@ -1,10 +1,7 @@
""" """
Tests to cover bulk create and update using serializers. Tests to cover bulk create and update using serializers.
""" """
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import serializers from rest_framework import serializers
@ -87,8 +84,7 @@ class BulkCreateSerializerTests(TestCase):
serializer = self.BookSerializer(data=data, many=True) serializer = self.BookSerializer(data=data, many=True)
assert serializer.is_valid() is False assert serializer.is_valid() is False
text_type_string = six.text_type.__name__ message = 'Invalid data. Expected a dictionary, but got str.'
message = 'Invalid data. Expected a dictionary, but got %s.' % text_type_string
expected_errors = [ expected_errors = [
{'non_field_errors': [message]}, {'non_field_errors': [message]},
{'non_field_errors': [message]}, {'non_field_errors': [message]},

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from rest_framework.settings import APISettings, api_settings from rest_framework.settings import APISettings, api_settings

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from rest_framework.status import ( from rest_framework.status import (

Some files were not shown because too many files have changed in this diff Show More