This commit is contained in:
Asif Saif Uddin 2019-05-02 12:55:27 +06:00
commit b37eb5f2fa
45 changed files with 103 additions and 144 deletions

View File

@ -59,7 +59,7 @@ Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recom
To run the tests, clone the repository, and then: To run the tests, clone the repository, and then:
# Setup the virtual environment # Setup the virtual environment
virtualenv env python3 -m venv env
source env/bin/activate source env/bin/activate
pip install django pip install django
pip install -r requirements.txt pip install -r requirements.txt
@ -115,7 +115,7 @@ It's also useful to remember that if you have an outstanding pull request then p
GitHub's documentation for working on pull requests is [available here][pull-requests]. GitHub's documentation for working on pull requests is [available here][pull-requests].
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django. Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible on all supported versions of Python and Django.
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect. Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.

View File

@ -354,7 +354,7 @@ The following third party packages are also available.
## Django OAuth Toolkit ## Django OAuth Toolkit
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support, and works with Python 2.7 and Python 3.3+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**. The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support and works with Python 3.4+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
#### Installation & configuration #### Installation & configuration

View File

@ -159,7 +159,7 @@ If you want the date field to be entirely hidden from the user, then use `Hidden
--- ---
**Note**: The `UniqueFor<Range>Validation` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input. **Note**: The `UniqueFor<Range>Validator` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input.
--- ---

View File

@ -65,7 +65,7 @@ Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recom
To run the tests, clone the repository, and then: To run the tests, clone the repository, and then:
# Setup the virtual environment # Setup the virtual environment
virtualenv env python3 -m venv env
source env/bin/activate source env/bin/activate
pip install django pip install django
pip install -r requirements.txt pip install -r requirements.txt
@ -121,7 +121,7 @@ It's also useful to remember that if you have an outstanding pull request then p
GitHub's documentation for working on pull requests is [available here][pull-requests]. GitHub's documentation for working on pull requests is [available here][pull-requests].
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django. Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible on all supported versions of Python and Django.
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect. Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.

View File

@ -84,7 +84,7 @@ continued development by **[signing up for a paid plan][funding]**.
REST framework requires the following: REST framework requires the following:
* 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

@ -14,18 +14,18 @@ The tutorial is fairly in-depth, so you should probably get a cookie and a cup o
## Setting up a new environment ## Setting up a new environment
Before we do anything else we'll create a new virtual environment, using [virtualenv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on. Before we do anything else we'll create a new virtual environment, using [venv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
virtualenv env python3 -m venv env
source env/bin/activate source env/bin/activate
Now that we're inside a virtualenv environment, we can install our package requirements. Now that we're inside a virtual environment, we can install our package requirements.
pip install django pip install django
pip install djangorestframework pip install djangorestframework
pip install pygments # We'll be using this for the code highlighting pip install pygments # We'll be using this for the code highlighting
**Note:** To exit the virtualenv environment at any time, just type `deactivate`. For more information see the [virtualenv documentation][virtualenv]. **Note:** To exit the virtual environment at any time, just type `deactivate`. For more information see the [venv documentation][venv].
## Getting started ## Getting started
@ -372,7 +372,7 @@ We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
[quickstart]: quickstart.md [quickstart]: quickstart.md
[repo]: https://github.com/encode/rest-framework-tutorial [repo]: https://github.com/encode/rest-framework-tutorial
[sandbox]: https://restframework.herokuapp.com/ [sandbox]: https://restframework.herokuapp.com/
[virtualenv]: http://www.virtualenv.org/en/latest/index.html [venv]: https://docs.python.org/3/library/venv.html
[tut-2]: 2-requests-and-responses.md [tut-2]: 2-requests-and-responses.md
[httpie]: https://github.com/jakubroztocil/httpie#installation [httpie]: https://github.com/jakubroztocil/httpie#installation
[curl]: https://curl.haxx.se/ [curl]: https://curl.haxx.se/

View File

@ -10,11 +10,11 @@ Create a new Django project named `tutorial`, then start a new app called `quick
mkdir tutorial mkdir tutorial
cd tutorial cd tutorial
# Create a virtualenv to isolate our package dependencies locally # Create a virtual environment to isolate our package dependencies locally
virtualenv env python3 -m venv env
source env/bin/activate # On Windows use `env\Scripts\activate` source env/bin/activate # On Windows use `env\Scripts\activate`
# Install Django and Django REST framework into the virtualenv # Install Django and Django REST framework into the virtual environment
pip install django pip install django
pip install djangorestframework pip install djangorestframework

View File

@ -6,7 +6,7 @@ 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.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import HTTP_HEADER_ENCODING, exceptions from rest_framework import HTTP_HEADER_ENCODING, exceptions

View File

@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class AuthTokenConfig(AppConfig): class AuthTokenConfig(AppConfig):

View File

@ -3,7 +3,7 @@ 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.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class Token(models.Model): class Token(models.Model):

View File

@ -1,5 +1,5 @@
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -3,7 +3,6 @@ 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.
""" """
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

View File

@ -8,8 +8,8 @@ import math
from django.http import JsonResponse from django.http import JsonResponse
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 gettext_lazy as _
from django.utils.translation import ungettext from django.utils.translation import ngettext
from rest_framework import status from rest_framework import status
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
@ -230,9 +230,9 @@ class Throttled(APIException):
wait = math.ceil(wait) wait = math.ceil(wait)
detail = ' '.join(( detail = ' '.join((
detail, detail,
force_text(ungettext(self.extra_detail_singular.format(wait=wait), force_text(ngettext(self.extra_detail_singular.format(wait=wait),
self.extra_detail_plural.format(wait=wait), self.extra_detail_plural.format(wait=wait),
wait)))) wait))))
self.wait = wait self.wait = wait
super().__init__(detail, code) super().__init__(detail, code)

View File

@ -6,6 +6,7 @@ import inspect
import re import re
import uuid import uuid
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Mapping
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@ -25,12 +26,12 @@ from django.utils.formats import localize_input, sanitize_separators
from django.utils.functional import lazy from django.utils.functional import lazy
from django.utils.ipv6 import clean_ipv6_address from django.utils.ipv6 import clean_ipv6_address
from django.utils.timezone import utc from django.utils.timezone import utc
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from pytz.exceptions import InvalidTimeError 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, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator, ProhibitNullCharactersValidator MinValueValidator, ProhibitNullCharactersValidator
) )
from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.exceptions import ErrorDetail, ValidationError
@ -1753,7 +1754,7 @@ class JSONField(Field):
try: try:
if self.binary or getattr(data, 'is_json_string', False): if self.binary or getattr(data, 'is_json_string', False):
if isinstance(data, bytes): if isinstance(data, bytes):
data = data.decode('utf-8') data = data.decode()
return json.loads(data) return json.loads(data)
else: else:
json.dumps(data) json.dumps(data)
@ -1764,10 +1765,7 @@ class JSONField(Field):
def to_representation(self, value): def to_representation(self, value):
if self.binary: if self.binary:
value = json.dumps(value) value = json.dumps(value)
# On python 2.x the return type for json.dumps() is underspecified. value = value.encode()
# On python 3.x json.dumps() returns unicode strings.
if isinstance(value, str):
value = bytes(value.encode('utf-8'))
return value return value

View File

@ -12,7 +12,7 @@ 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.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import RemovedInDRF310Warning from rest_framework import RemovedInDRF310Warning
from rest_framework.compat import ( from rest_framework.compat import (

View File

@ -29,7 +29,7 @@ class Command(BaseCommand):
renderer = self.get_renderer(options['format']) renderer = self.get_renderer(options['format'])
output = renderer.render(schema, renderer_context={}) output = renderer.render(schema, renderer_context={})
self.stdout.write(output.decode('utf-8')) self.stdout.write(output.decode())
def get_renderer(self, format): def get_renderer(self, format):
renderer_cls = { renderer_cls = {

View File

@ -10,7 +10,7 @@ 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.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.compat import coreapi, coreschema from rest_framework.compat import coreapi, coreschema
from rest_framework.exceptions import NotFound from rest_framework.exceptions import NotFound

View File

@ -80,8 +80,7 @@ class FormParser(BaseParser):
""" """
parser_context = parser_context or {} parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
data = QueryDict(stream.read(), encoding=encoding) return QueryDict(stream.read(), encoding=encoding)
return data
class MultiPartParser(BaseParser): class MultiPartParser(BaseParser):
@ -202,7 +201,7 @@ class FileUploadParser(BaseParser):
try: try:
meta = parser_context['request'].META meta = parser_context['request'].META
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8')) disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode())
filename_parm = disposition[1] filename_parm = disposition[1]
if 'filename*' in filename_parm: if 'filename*' in filename_parm:
return self.get_encoded_filename(filename_parm) return self.get_encoded_filename(filename_parm)

View File

@ -7,7 +7,7 @@ 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.encoding import smart_text, uri_to_iri from django.utils.encoding import smart_text, uri_to_iri
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.fields import ( from rest_framework.fields import (
Field, empty, get_attribute, is_simple_callable, iter_options Field, empty, get_attribute, is_simple_callable, iter_options

View File

@ -104,18 +104,11 @@ class JSONRenderer(BaseRenderer):
allow_nan=not self.strict, separators=separators allow_nan=not self.strict, separators=separators
) )
# On python 2.x json.dumps() returns bytestrings if ensure_ascii=True, # We always fully escape \u2028 and \u2029 to ensure we output JSON
# but if ensure_ascii=False, the return type is underspecified, # that is a strict javascript subset.
# and may (or may not) be unicode. # See: http://timelessrepo.com/json-isnt-a-javascript-subset
# On python 3.x json.dumps() returns unicode strings. ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
if isinstance(ret, str): return ret.encode()
# We always fully escape \u2028 and \u2029 to ensure we output JSON
# that is a strict javascript subset. If bytes were returned
# by json.dumps() then we don't have these characters in any case.
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
return bytes(ret.encode('utf-8'))
return ret
class TemplateHTMLRenderer(BaseRenderer): class TemplateHTMLRenderer(BaseRenderer):
@ -574,7 +567,7 @@ class BrowsableAPIRenderer(BaseRenderer):
data.pop(name, None) data.pop(name, None)
content = renderer.render(data, accepted, context) content = renderer.render(data, accepted, context)
# Renders returns bytes, but CharField expects a str. # Renders returns bytes, but CharField expects a str.
content = content.decode('utf-8') content = content.decode()
else: else:
content = None content = None
@ -681,7 +674,7 @@ class BrowsableAPIRenderer(BaseRenderer):
csrf_header_name = csrf_header_name[5:] csrf_header_name = csrf_header_name[5:]
csrf_header_name = csrf_header_name.replace('_', '-') csrf_header_name = csrf_header_name.replace('_', '-')
context = { return {
'content': self.get_content(renderer, data, accepted_media_type, renderer_context), 'content': self.get_content(renderer, data, accepted_media_type, renderer_context),
'code_style': pygments_css(self.code_style), 'code_style': pygments_css(self.code_style),
'view': view, 'view': view,
@ -717,7 +710,6 @@ class BrowsableAPIRenderer(BaseRenderer):
'csrf_cookie_name': csrf_cookie_name, 'csrf_cookie_name': csrf_cookie_name,
'csrf_header_name': csrf_header_name 'csrf_header_name': csrf_header_name
} }
return context
def render(self, data, accepted_media_type=None, renderer_context=None): def render(self, data, accepted_media_type=None, renderer_context=None):
""" """
@ -1032,7 +1024,7 @@ class OpenAPIRenderer(_BaseOpenAPIRenderer):
def render(self, data, media_type=None, renderer_context=None): def render(self, data, media_type=None, renderer_context=None):
structure = self.get_structure(data) structure = self.get_structure(data)
return yaml.dump(structure, default_flow_style=False).encode('utf-8') return yaml.dump(structure, default_flow_style=False).encode()
class JSONOpenAPIRenderer(_BaseOpenAPIRenderer): class JSONOpenAPIRenderer(_BaseOpenAPIRenderer):
@ -1045,4 +1037,4 @@ class JSONOpenAPIRenderer(_BaseOpenAPIRenderer):
def render(self, data, media_type=None, renderer_context=None): def render(self, data, media_type=None, renderer_context=None):
structure = self.get_structure(data) structure = self.get_structure(data)
return json.dumps(structure, indent=4).encode('utf-8') return json.dumps(structure, indent=4).encode()

View File

@ -184,9 +184,7 @@ class EndpointEnumerator:
) )
api_endpoints.extend(nested_endpoints) api_endpoints.extend(nested_endpoints)
api_endpoints = sorted(api_endpoints, key=endpoint_ordering) return sorted(api_endpoints, key=endpoint_ordering)
return api_endpoints
def get_path_from_regex(self, path_regex): def get_path_from_regex(self, path_regex):
""" """
@ -195,8 +193,7 @@ class EndpointEnumerator:
path = simplify_regex(path_regex) path = simplify_regex(path_regex)
# Strip Django 2.0 convertors as they are incompatible with uritemplate format # Strip Django 2.0 convertors as they are incompatible with uritemplate format
path = re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path) return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path)
return path
def should_include_endpoint(self, path, callback): def should_include_endpoint(self, path, callback):
""" """

View File

@ -11,7 +11,7 @@ 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.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions, serializers from rest_framework import exceptions, serializers
from rest_framework.compat import coreapi, coreschema, uritemplate from rest_framework.compat import coreapi, coreschema, uritemplate
@ -435,8 +435,7 @@ class AutoSchema(ViewInspector):
by_name = OrderedDict((f.name, f) for f in fields) by_name = OrderedDict((f.name, f) for f in fields)
for f in update_with: for f in update_with:
by_name[f.name] = f by_name[f.name] = f
fields = list(by_name.values()) return list(by_name.values())
return fields
def get_encoding(self, path, method): def get_encoding(self, path, method):
""" """

View File

@ -14,6 +14,7 @@ import copy
import inspect import inspect
import traceback import traceback
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Mapping
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
@ -23,9 +24,9 @@ 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 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 gettext_lazy as _
from rest_framework.compat import Mapping, postgres_fields from rest_framework.compat import 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

View File

@ -18,10 +18,9 @@ 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 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.module_loading import import_string
from rest_framework import ISO_8601 from rest_framework import ISO_8601
@ -175,11 +174,8 @@ def import_from_string(val, setting_name):
Attempt to import a class from a string representation. Attempt to import a class from a string representation.
""" """
try: try:
# Nod to tastypie's use of importlib. return import_string(val)
module_path, class_name = val.rsplit('.', 1) except ImportError as e:
module = import_module(module_path)
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e) msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
raise ImportError(msg) raise ImportError(msg)

View File

@ -12,7 +12,7 @@
<div class="modal-body"> <div class="modal-body">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<h4 class="text-center">You are logged in as {{ user.username }}.</h4> <h4 class="text-center">You are logged in as {{ user.get_username }}.</h4>
{% else %} {% else %}
<div class="text-center"> <div class="text-center">

View File

@ -47,7 +47,7 @@ class JSONEncoder(json.JSONEncoder):
return tuple(obj) return tuple(obj)
elif isinstance(obj, bytes): elif isinstance(obj, bytes):
# Best-effort for binary blobs. See #4187. # Best-effort for binary blobs. See #4187.
return obj.decode('utf-8') return obj.decode()
elif hasattr(obj, 'tolist'): elif hasattr(obj, 'tolist'):
# Numpy arrays and array scalars. # Numpy arrays and array scalars.
return obj.tolist() return obj.tolist()

View File

@ -41,9 +41,7 @@ def smart_repr(value):
# <django.core.validators.RegexValidator object at 0x1047af050> # <django.core.validators.RegexValidator object at 0x1047af050>
# Should be presented as # Should be presented as
# <django.core.validators.RegexValidator object> # <django.core.validators.RegexValidator object>
value = re.sub(' at 0x[0-9A-Fa-f]{4,32}>', '>', value) return re.sub(' at 0x[0-9A-Fa-f]{4,32}>', '>', value)
return value
def field_repr(field, force_many=False): def field_repr(field, force_many=False):

View File

@ -1,8 +1,8 @@
from collections import OrderedDict from collections import OrderedDict
from collections.abc import MutableMapping
from django.utils.encoding import force_text from django.utils.encoding import force_text
from rest_framework.compat import MutableMapping
from rest_framework.utils import json from rest_framework.utils import json

View File

@ -7,7 +7,7 @@ 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 django.db import DataError from django.db import DataError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
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

View File

@ -1,6 +1,6 @@
import re import re
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework.compat import unicode_http_header from rest_framework.compat import unicode_http_header

View File

@ -11,7 +11,7 @@ PYTEST_ARGS = {
FLAKE8_ARGS = ['rest_framework', 'tests'] FLAKE8_ARGS = ['rest_framework', 'tests']
ISORT_ARGS = ['--recursive', '--check-only', '--diff', '-o' 'uritemplate', '-p', 'tests', 'rest_framework', 'tests'] ISORT_ARGS = ['--recursive', '--check-only', '--diff', 'rest_framework', 'tests']
def exit_on_failure(ret, message=None): def exit_on_failure(ret, message=None):

View File

@ -14,8 +14,8 @@ skip=.tox
atomic=true atomic=true
multi_line_output=5 multi_line_output=5
known_standard_library=types known_standard_library=types
known_third_party=pytest,_pytest,django,pytz known_third_party=pytest,_pytest,django,pytz,uritemplate
known_first_party=rest_framework known_first_party=rest_framework,tests
[coverage:run] [coverage:run]
# NOTE: source is ignored with pytest-cov (but uses the same). # NOTE: source is ignored with pytest-cov (but uses the same).

View File

@ -183,7 +183,7 @@ class SessionAuthTests(TestCase):
cf. [#1810](https://github.com/encode/django-rest-framework/pull/1810) cf. [#1810](https://github.com/encode/django-rest-framework/pull/1810)
""" """
response = self.csrf_client.get('/auth/login/') response = self.csrf_client.get('/auth/login/')
content = response.content.decode('utf8') content = response.content.decode()
assert '<label for="id_username">Username:</label>' in content assert '<label for="id_username">Username:</label>' in content
def test_post_form_session_auth_failing_csrf(self): def test_post_form_session_auth_failing_csrf(self):

View File

@ -24,18 +24,18 @@ class DropdownWithAuthTests(TestCase):
def test_name_shown_when_logged_in(self): def test_name_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password) self.client.login(username=self.username, password=self.password)
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert 'john' in content assert 'john' in content
def test_logout_shown_when_logged_in(self): def test_logout_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password) self.client.login(username=self.username, password=self.password)
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert '>Log out<' in content assert '>Log out<' in content
def test_login_shown_when_logged_out(self): def test_login_shown_when_logged_out(self):
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert '>Log in<' in content assert '>Log in<' in content
@ -59,16 +59,16 @@ class NoDropdownWithoutAuthTests(TestCase):
def test_name_shown_when_logged_in(self): def test_name_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password) self.client.login(username=self.username, password=self.password)
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert 'john' in content assert 'john' in content
def test_dropdown_not_shown_when_logged_in(self): def test_dropdown_not_shown_when_logged_in(self):
self.client.login(username=self.username, password=self.password) self.client.login(username=self.username, password=self.password)
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert '<li class="dropdown">' not in content assert '<li class="dropdown">' not in content
def test_dropdown_not_shown_when_logged_out(self): def test_dropdown_not_shown_when_logged_out(self):
response = self.client.get('/') response = self.client.get('/')
content = response.content.decode('utf8') content = response.content.decode()
assert '<li class="dropdown">' not in content assert '<li class="dropdown">' not in content

View File

@ -34,7 +34,7 @@ class DropdownWithAuthTests(TestCase):
def test_login(self): def test_login(self):
response = self.client.get('/api/') response = self.client.get('/api/')
assert 200 == response.status_code assert 200 == response.status_code
content = response.content.decode('utf-8') content = response.content.decode()
assert 'form action="/api/"' in content assert 'form action="/api/"' in content
assert 'input name="nested.one"' in content assert 'input name="nested.one"' in content
assert 'input name="nested.two"' in content assert 'input name="nested.two"' in content

View File

@ -1,7 +1,7 @@
import uuid import uuid
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class RESTFrameworkModel(models.Model): class RESTFrameworkModel(models.Model):

View File

@ -202,8 +202,7 @@ class ActionDecoratorTestCase(TestCase):
def method(): def method():
raise NotImplementedError raise NotImplementedError
# Python 2.x compatibility - cast __name__ to str method.__name__ = name
method.__name__ = str(name)
getattr(test_action.mapping, name)(method) getattr(test_action.mapping, name)(method)
# ensure the mapping returns the correct method name # ensure the mapping returns the correct method name

View File

@ -1,6 +1,6 @@
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.utils import translation from django.utils import translation
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ( from rest_framework.exceptions import (
APIException, ErrorDetail, Throttled, _get_error_details, bad_request, APIException, ErrorDetail, Throttled, _get_error_details, bad_request,

View File

@ -164,7 +164,7 @@ class TestRootView(TestCase):
request = factory.post('/', data, HTTP_ACCEPT='text/html') request = factory.post('/', data, HTTP_ACCEPT='text/html')
response = self.view(request).render() response = self.view(request).render()
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>' expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
assert expected_error in response.rendered_content.decode('utf-8') assert expected_error in response.rendered_content.decode()
EXPECTED_QUERIES_FOR_PUT = 2 EXPECTED_QUERIES_FOR_PUT = 2
@ -311,7 +311,7 @@ class TestInstanceView(TestCase):
request = factory.put('/', data, HTTP_ACCEPT='text/html') request = factory.put('/', data, HTTP_ACCEPT='text/html')
response = self.view(request, pk=1).render() response = self.view(request, pk=1).render()
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>' expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
assert expected_error in response.rendered_content.decode('utf-8') assert expected_error in response.rendered_content.decode()
class TestFKInstanceView(TestCase): class TestFKInstanceView(TestCase):
@ -538,7 +538,7 @@ class TestFilterBackendAppliedToViews(TestCase):
view = DynamicSerializerView.as_view() view = DynamicSerializerView.as_view()
request = factory.get('/') request = factory.get('/')
response = view(request).render() response = view(request).render()
content = response.content.decode('utf8') content = response.content.decode()
assert 'field_b' in content assert 'field_b' in content
assert 'field_a' not in content assert 'field_a' not in content

View File

@ -40,9 +40,7 @@ class TestFileUploadParser(TestCase):
def setUp(self): def setUp(self):
class MockRequest: class MockRequest:
pass pass
self.stream = io.BytesIO( self.stream = io.BytesIO(b"Test text file")
"Test text file".encode('utf-8')
)
request = MockRequest() request = MockRequest()
request.upload_handlers = (MemoryFileUploadHandler(),) request.upload_handlers = (MemoryFileUploadHandler(),)
request.META = { request.META = {
@ -128,7 +126,7 @@ class TestFileUploadParser(TestCase):
class TestJSONParser(TestCase): class TestJSONParser(TestCase):
def bytes(self, value): def bytes(self, value):
return io.BytesIO(value.encode('utf-8')) return io.BytesIO(value.encode())
def test_float_strictness(self): def test_float_strictness(self):
parser = JSONParser() parser = JSONParser()

View File

@ -1,5 +1,6 @@
import re import re
from collections import OrderedDict from collections import OrderedDict
from collections.abc import MutableMapping
import pytest import pytest
from django.conf.urls import include, url from django.conf.urls import include, url
@ -9,10 +10,10 @@ 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.safestring import SafeText from django.utils.safestring import SafeText
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import permissions, serializers, status from rest_framework import permissions, serializers, status
from rest_framework.compat import MutableMapping, coreapi from rest_framework.compat import coreapi
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.renderers import ( from rest_framework.renderers import (
AdminRenderer, BaseRenderer, BrowsableAPIRenderer, DocumentationRenderer, AdminRenderer, BaseRenderer, BrowsableAPIRenderer, DocumentationRenderer,
@ -75,8 +76,7 @@ class MockView(APIView):
renderer_classes = (RendererA, RendererB) renderer_classes = (RendererA, RendererB)
def get(self, request, **kwargs): def get(self, request, **kwargs):
response = Response(DUMMYCONTENT, status=DUMMYSTATUS) return Response(DUMMYCONTENT, status=DUMMYSTATUS)
return response
class MockGETView(APIView): class MockGETView(APIView):
@ -303,14 +303,14 @@ class JSONRendererTests(TestCase):
o = DummyTestModel.objects.create(name='dummy') o = DummyTestModel.objects.create(name='dummy')
qs = DummyTestModel.objects.values('id', 'name') qs = DummyTestModel.objects.values('id', 'name')
ret = JSONRenderer().render(qs) ret = JSONRenderer().render(qs)
data = json.loads(ret.decode('utf-8')) data = json.loads(ret.decode())
self.assertEqual(data, [{'id': o.id, 'name': o.name}]) self.assertEqual(data, [{'id': o.id, 'name': o.name}])
def test_render_queryset_values_list(self): def test_render_queryset_values_list(self):
o = DummyTestModel.objects.create(name='dummy') o = DummyTestModel.objects.create(name='dummy')
qs = DummyTestModel.objects.values_list('id', 'name') qs = DummyTestModel.objects.values_list('id', 'name')
ret = JSONRenderer().render(qs) ret = JSONRenderer().render(qs)
data = json.loads(ret.decode('utf-8')) data = json.loads(ret.decode())
self.assertEqual(data, [[o.id, o.name]]) self.assertEqual(data, [[o.id, o.name]])
def test_render_dict_abc_obj(self): def test_render_dict_abc_obj(self):
@ -340,7 +340,7 @@ class JSONRendererTests(TestCase):
x['key'] = 'string value' x['key'] = 'string value'
x[2] = 3 x[2] = 3
ret = JSONRenderer().render(x) ret = JSONRenderer().render(x)
data = json.loads(ret.decode('utf-8')) data = json.loads(ret.decode())
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):
@ -380,7 +380,7 @@ class JSONRendererTests(TestCase):
renderer = JSONRenderer() renderer = JSONRenderer()
content = renderer.render(obj, 'application/json') content = renderer.render(obj, 'application/json')
# Fix failing test case which depends on version of JSON library. # Fix failing test case which depends on version of JSON library.
self.assertEqual(content.decode('utf-8'), _flat_repr) self.assertEqual(content.decode(), _flat_repr)
def test_with_content_type_args(self): def test_with_content_type_args(self):
""" """
@ -389,7 +389,7 @@ class JSONRendererTests(TestCase):
obj = {'foo': ['bar', 'baz']} obj = {'foo': ['bar', 'baz']}
renderer = JSONRenderer() renderer = JSONRenderer()
content = renderer.render(obj, 'application/json; indent=2') content = renderer.render(obj, 'application/json; indent=2')
self.assertEqual(strip_trailing_whitespace(content.decode('utf-8')), _indented_repr) self.assertEqual(strip_trailing_whitespace(content.decode()), _indented_repr)
class UnicodeJSONRendererTests(TestCase): class UnicodeJSONRendererTests(TestCase):
@ -400,7 +400,7 @@ class UnicodeJSONRendererTests(TestCase):
obj = {'countries': ['United Kingdom', 'France', 'España']} obj = {'countries': ['United Kingdom', 'France', 'España']}
renderer = JSONRenderer() renderer = JSONRenderer()
content = renderer.render(obj, 'application/json') content = renderer.render(obj, 'application/json')
self.assertEqual(content, '{"countries":["United Kingdom","France","España"]}'.encode('utf-8')) self.assertEqual(content, '{"countries":["United Kingdom","France","España"]}'.encode())
def test_u2028_u2029(self): def test_u2028_u2029(self):
# The \u2028 and \u2029 characters should be escaped, # The \u2028 and \u2029 characters should be escaped,
@ -409,7 +409,7 @@ class UnicodeJSONRendererTests(TestCase):
obj = {'should_escape': '\u2028\u2029'} obj = {'should_escape': '\u2028\u2029'}
renderer = JSONRenderer() renderer = JSONRenderer()
content = renderer.render(obj, 'application/json') content = renderer.render(obj, 'application/json')
self.assertEqual(content, '{"should_escape":"\\u2028\\u2029"}'.encode('utf-8')) self.assertEqual(content, '{"should_escape":"\\u2028\\u2029"}'.encode())
class AsciiJSONRendererTests(TestCase): class AsciiJSONRendererTests(TestCase):
@ -422,7 +422,7 @@ class AsciiJSONRendererTests(TestCase):
obj = {'countries': ['United Kingdom', 'France', 'España']} obj = {'countries': ['United Kingdom', 'France', 'España']}
renderer = AsciiJSONRenderer() renderer = AsciiJSONRenderer()
content = renderer.render(obj, 'application/json') content = renderer.render(obj, 'application/json')
self.assertEqual(content, '{"countries":["United Kingdom","France","Espa\\u00f1a"]}'.encode('utf-8')) self.assertEqual(content, '{"countries":["United Kingdom","France","Espa\\u00f1a"]}'.encode())
# Tests for caching issue, #346 # Tests for caching issue, #346
@ -653,9 +653,9 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
def test_extra_actions_dropdown(self): def test_extra_actions_dropdown(self):
resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html') resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html')
assert 'id="extra-actions-menu"' in resp.content.decode('utf-8') assert 'id="extra-actions-menu"' in resp.content.decode()
assert '/api/examples/list_action/' in resp.content.decode('utf-8') assert '/api/examples/list_action/' in resp.content.decode()
assert '>Extra list action<' in resp.content.decode('utf-8') assert '>Extra list action<' in resp.content.decode()
class AdminRendererTests(TestCase): class AdminRendererTests(TestCase):

View File

@ -438,13 +438,13 @@ class TestEmptyPrefix(URLPatternsTestCase, TestCase):
def test_empty_prefix_list(self): def test_empty_prefix_list(self):
response = self.client.get('/empty-prefix/') response = self.client.get('/empty-prefix/')
assert response.status_code == 200 assert response.status_code == 200
assert json.loads(response.content.decode('utf-8')) == [{'uuid': '111', 'text': 'First'}, assert json.loads(response.content.decode()) == [{'uuid': '111', 'text': 'First'},
{'uuid': '222', 'text': 'Second'}] {'uuid': '222', 'text': 'Second'}]
def test_empty_prefix_detail(self): def test_empty_prefix_detail(self):
response = self.client.get('/empty-prefix/1/') response = self.client.get('/empty-prefix/1/')
assert response.status_code == 200 assert response.status_code == 200
assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'} assert json.loads(response.content.decode()) == {'uuid': '111', 'text': 'First'}
class TestRegexUrlPath(URLPatternsTestCase, TestCase): class TestRegexUrlPath(URLPatternsTestCase, TestCase):
@ -456,14 +456,14 @@ class TestRegexUrlPath(URLPatternsTestCase, TestCase):
kwarg = '1234' kwarg = '1234'
response = self.client.get('/regex/list/{}/'.format(kwarg)) response = self.client.get('/regex/list/{}/'.format(kwarg))
assert response.status_code == 200 assert response.status_code == 200
assert json.loads(response.content.decode('utf-8')) == {'kwarg': kwarg} assert json.loads(response.content.decode()) == {'kwarg': kwarg}
def test_regex_url_path_detail(self): def test_regex_url_path_detail(self):
pk = '1' pk = '1'
kwarg = '1234' kwarg = '1234'
response = self.client.get('/regex/{}/detail/{}/'.format(pk, kwarg)) response = self.client.get('/regex/{}/detail/{}/'.format(pk, kwarg))
assert response.status_code == 200 assert response.status_code == 200
assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg} assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg}
class TestViewInitkwargs(URLPatternsTestCase, TestCase): class TestViewInitkwargs(URLPatternsTestCase, TestCase):

View File

@ -1358,4 +1358,4 @@ def test_schema_handles_exception():
response = schema_view(request) response = schema_view(request)
response.render() response.render()
assert response.status_code == 403 assert response.status_code == 403
assert "You do not have permission to perform this action." in str(response.content) assert b"You do not have permission to perform this action." in response.content

View File

@ -2,12 +2,12 @@ import inspect
import pickle import pickle
import re import re
from collections import ChainMap from collections import ChainMap
from collections.abc import Mapping
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
from rest_framework.fields import Field from rest_framework.fields import Field
from .models import ( from .models import (
@ -386,23 +386,6 @@ class TestIncorrectlyConfigured:
) )
class TestUnicodeRepr:
def test_repr(self):
class ExampleSerializer(serializers.Serializer):
example = serializers.CharField()
class ExampleObject:
def __init__(self):
self.example = '한국'
def __repr__(self):
return repr(self.example)
instance = ExampleObject()
serializer = ExampleSerializer(instance)
repr(serializer) # Should not error.
class TestNotRequiredOutput: class TestNotRequiredOutput:
def test_not_required_output_for_dict(self): def test_not_required_output_for_dict(self):
""" """

View File

@ -6,7 +6,7 @@ from django.shortcuts import render
def test_base_template_with_context(): def test_base_template_with_context():
context = {'request': True, 'csrf_token': 'TOKEN'} context = {'request': True, 'csrf_token': 'TOKEN'}
result = render({}, 'rest_framework/base.html', context=context) result = render({}, 'rest_framework/base.html', context=context)
assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode('utf-8')) assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode())
def test_base_template_with_no_context(): def test_base_template_with_no_context():
@ -14,4 +14,4 @@ def test_base_template_with_no_context():
# so it can be easily extended. # so it can be easily extended.
result = render({}, 'rest_framework/base.html') result = render({}, 'rest_framework/base.html')
# note that this response will not include a valid CSRF token # note that this response will not include a valid CSRF token
assert re.search(r'\bcsrfToken: ""', result.content.decode('utf-8')) assert re.search(r'\bcsrfToken: ""', result.content.decode())