Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up compat code #2200

Merged
merged 7 commits into from
Dec 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Some tips on good issue reporting:
* When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing.
* Search the issue list first for related items, and make sure you're running the latest version of REST framework before reporting an issue.
* If reporting a bug, then try to include a pull request with a failing test case. This will help us quickly identify if there is a valid issue, and make sure that it gets fixed more quickly if there is one.
* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintainence overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation.
* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation.
* Closing an issue doesn't necessarily mean the end of a discussion. If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened.

## Triaging issues
Expand Down Expand Up @@ -82,7 +82,7 @@ GitHub's documentation for working on pull requests is [available here][pull-req

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.

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 runnning 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.

![Travis status][travis-status]

Expand Down
2 changes: 1 addition & 1 deletion rest_framework/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def authenticate(self, request):
def authenticate_header(self, request):
"""
If permission is denied, return a '401 Unauthorized' response,
with an appropraite 'WWW-Authenticate' header.
with an appropriate 'WWW-Authenticate' header.
"""
return 'OAuth realm="%s"' % self.www_authenticate_realm

Expand Down
5 changes: 4 additions & 1 deletion rest_framework/authtoken/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import binascii
import os

from django.conf import settings
from django.db import models
from django.utils.encoding import python_2_unicode_compatible


# Prior to Django 1.5, the AUTH_USER_MODEL setting does not exist.
Expand All @@ -11,6 +13,7 @@
AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')


@python_2_unicode_compatible
class Token(models.Model):
"""
The default authorization token model.
Expand All @@ -35,5 +38,5 @@ def save(self, *args, **kwargs):
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()

def __unicode__(self):
def __str__(self):
return self.key
107 changes: 14 additions & 93 deletions rest_framework/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,14 @@
# flake8: noqa
from __future__ import unicode_literals

import inspect

from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import force_text
from django.utils.six.moves.urllib import parse as urlparse
from django.conf import settings
from django.utils import six
import django
import inspect


# Handle django.utils.encoding rename in 1.5 onwards.
# smart_unicode -> smart_text
# force_unicode -> force_text
try:
from django.utils.encoding import smart_text
except ImportError:
from django.utils.encoding import smart_unicode as smart_text
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text


# OrderedDict only available in Python 2.7.
Expand All @@ -32,7 +22,7 @@
# For Django <= 1.6 and Python 2.6 fall back to OrderedDict.
try:
from collections import OrderedDict
except:
except ImportError:
from django.utils.datastructures import SortedDict as OrderedDict


Expand All @@ -49,7 +39,6 @@
except ImportError:
django_filters = None


if django.VERSION >= (1, 6):
def clean_manytomany_helptext(text):
return text
Expand All @@ -72,30 +61,6 @@ def clean_manytomany_helptext(text):
pass


# cStringIO only if it's available, otherwise StringIO
try:
import cStringIO.StringIO as StringIO
except ImportError:
StringIO = six.StringIO

BytesIO = six.BytesIO


# urlparse compat import (Required because it changed in python 3.x)
try:
from urllib import parse as urlparse
except ImportError:
import urlparse

# UserDict moves in Python 3
try:
from UserDict import UserDict
from UserDict import DictMixin
except ImportError:
from collections import UserDict
from collections import MutableMapping as DictMixin


def get_model_name(model_cls):
try:
return model_cls._meta.model_name
Expand All @@ -104,14 +69,6 @@ def get_model_name(model_cls):
return model_cls._meta.module_name


def get_concrete_model(model_cls):
try:
return model_cls._meta.concrete_model
except AttributeError:
# 1.3 does not include concrete model
return model_cls


# View._allowed_methods only present from 1.5 onwards
if django.VERSION >= (1, 5):
from django.views.generic import View
Expand All @@ -123,7 +80,6 @@ def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]



# MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+
if django.VERSION >= (1, 8):
from django.core.validators import MinValueValidator, MaxValueValidator
Expand Down Expand Up @@ -187,33 +143,30 @@ def __init__(self, *args, **kwargs):
# RequestFactory only provides `generic` from 1.5 onwards
from django.test.client import RequestFactory as DjangoRequestFactory
from django.test.client import FakePayload

try:
# In 1.5 the test client uses force_bytes
from django.utils.encoding import force_bytes as force_bytes_or_smart_bytes
except ImportError:
# In 1.4 the test client just uses smart_str
from django.utils.encoding import smart_str as force_bytes_or_smart_bytes


class RequestFactory(DjangoRequestFactory):
def generic(self, method, path,
data='', content_type='application/octet-stream', **extra):
parsed = urlparse.urlparse(path)
data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': force_text(parsed[4]),
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': force_text(parsed[4]),
'REQUEST_METHOD': six.text_type(method),
}
if data:
r.update({
'CONTENT_LENGTH': len(data),
'CONTENT_TYPE': six.text_type(content_type),
'wsgi.input': FakePayload(data),
})
elif django.VERSION <= (1, 4):
# For 1.3 we need an empty WSGI payload
r.update({
'wsgi.input': FakePayload('')
'CONTENT_TYPE': six.text_type(content_type),
'wsgi.input': FakePayload(data),
})
r.update(extra)
return self.request(**r)
Expand Down Expand Up @@ -287,10 +240,12 @@ def check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp):
import provider as oauth2_provider
from provider import scope as oauth2_provider_scope
from provider import constants as oauth2_constants

if oauth2_provider.__version__ in ('0.2.3', '0.2.4'):
# 0.2.3 and 0.2.4 are supported version that do not support
# timezone aware datetimes
import datetime

provider_now = datetime.datetime.now
else:
# Any other supported version does use timezone aware datetimes
Expand All @@ -301,45 +256,11 @@ def check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp):
oauth2_constants = None
provider_now = None

# `seperators` argument to `json.dumps()` differs between 2.x and 3.x
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
# See: http://bugs.python.org/issue22767
if six.PY3:
SHORT_SEPARATORS = (',', ':')
LONG_SEPARATORS = (', ', ': ')
else:
SHORT_SEPARATORS = (b',', b':')
LONG_SEPARATORS = (b', ', b': ')


# Handle lazy strings across Py2/Py3
from django.utils.functional import Promise

if six.PY3:
def is_non_str_iterable(obj):
if (isinstance(obj, str) or
(isinstance(obj, Promise) and obj._delegate_text)):
return False
return hasattr(obj, '__iter__')
else:
def is_non_str_iterable(obj):
return hasattr(obj, '__iter__')


try:
from django.utils.encoding import python_2_unicode_compatible
except ImportError:
def python_2_unicode_compatible(klass):
"""
A decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing.

To support Python 2 and 3 with a single code base, define a __str__ method
returning text and apply this decorator to the class.
"""
if '__str__' not in klass.__dict__:
raise ValueError("@python_2_unicode_compatible cannot be applied "
"to %s because it doesn't define __str__()." %
klass.__name__)
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
2 changes: 1 addition & 1 deletion rest_framework/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
"""
from __future__ import unicode_literals
from django.utils.encoding import force_text

from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from rest_framework import status
from rest_framework.compat import force_text
import math


Expand Down
4 changes: 2 additions & 2 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from django.forms import ImageField as DjangoImageField
from django.utils import six, timezone
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.encoding import is_protected_type
from django.utils.encoding import is_protected_type, smart_text
from django.utils.translation import ugettext_lazy as _
from rest_framework import ISO_8601
from rest_framework.compat import (
smart_text, EmailValidator, MinValueValidator, MaxValueValidator,
EmailValidator, MinValueValidator, MaxValueValidator,
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict
)
from rest_framework.exceptions import ValidationError
Expand Down
7 changes: 4 additions & 3 deletions rest_framework/metadata.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"""
The metadata API is used to allow cusomization of how `OPTIONS` requests
The metadata API is used to allow customization of how `OPTIONS` requests
are handled. We currently provide a single default implementation that returns
some fairly ad-hoc information about the view.

Future implementations might use JSON schema or other definations in order
Future implementations might use JSON schema or other definitions in order
to return this information in a more standardized way.
"""
from __future__ import unicode_literals

from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_text
from rest_framework import exceptions, serializers
from rest_framework.compat import force_text, OrderedDict
from rest_framework.compat import OrderedDict
from rest_framework.request import clone_request
from rest_framework.utils.field_mapping import ClassLookupDict

Expand Down
4 changes: 3 additions & 1 deletion rest_framework/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
from django.utils import six
from rest_framework.compat import etree, yaml, force_text, urlparse
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.encoding import force_text
from rest_framework.compat import etree, yaml
from rest_framework.exceptions import ParseError
from rest_framework import renderers
import json
Expand Down
2 changes: 1 addition & 1 deletion rest_framework/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def has_object_permission(self, request, view, obj):
if not user.has_perms(perms, obj):
# If the user does not have permissions we need to determine if
# they have read permissions to see 403, or not, and simply see
# a 404 reponse.
# a 404 response.

if request.method in ('GET', 'OPTIONS', 'HEAD'):
# Read permissions already checked and failed, no need
Expand Down
5 changes: 3 additions & 2 deletions rest_framework/relations.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from rest_framework.compat import smart_text, urlparse
from django.utils.encoding import smart_text
from rest_framework.fields import get_attribute, empty, Field
from rest_framework.reverse import reverse
from rest_framework.utils import html
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch, Resolver404
from django.db.models.query import QuerySet
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _


Expand Down Expand Up @@ -141,7 +142,7 @@ def get_attribute(self, instance):
def get_iterable(self, instance, source_attrs):
# For consistency with `get_attribute` we're using `serializable_value()`
# here. Typically there won't be any difference, but some custom field
# types might return a non-primative value for the pk otherwise.
# types might return a non-primitive value for the pk otherwise.
#
# We could try to get smart with `values_list('pk', flat=True)`, which
# would be better in some case, but would actually end up with *more*
Expand Down
10 changes: 6 additions & 4 deletions rest_framework/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
from django.template import Context, RequestContext, loader, Template
from django.test.client import encode_multipart
from django.utils import six
from django.utils.encoding import smart_text
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.six.moves import StringIO
from rest_framework import exceptions, serializers, status, VERSION
from rest_framework.compat import (
SHORT_SEPARATORS, LONG_SEPARATORS, StringIO, smart_text, yaml
)
from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.request import is_form_media_type, override_method
Expand Down Expand Up @@ -282,7 +282,9 @@ def get_template_names(self, response, view):
return view.get_template_names()
elif hasattr(view, 'template_name'):
return [view.template_name]
raise ImproperlyConfigured('Returned a template response with no `template_name` attribute set on either the view or response')
raise ImproperlyConfigured(
'Returned a template response with no `template_name` attribute set on either the view or response'
)

def get_exception_template(self, response):
template_names = [name % {'status_code': response.status_code}
Expand Down
2 changes: 1 addition & 1 deletion rest_framework/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from django.http.multipartparser import parse_header
from django.utils.datastructures import MultiValueDict
from django.utils.datastructures import MergeDict as DjangoMergeDict
from django.utils.six import BytesIO
from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions
from rest_framework.compat import BytesIO
from rest_framework.settings import api_settings
import warnings

Expand Down
Loading