aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
authorTom Christie2015-01-19 15:23:08 +0000
committerTom Christie2015-01-19 15:23:08 +0000
commite5b94f7b7bbf2f6f35c5e33fb2723bdb0d33bad3 (patch)
treea156a4a29a26540c52e5580ffa555b14d6c63fbe /rest_framework
parentdbb684117f6fe0f9c34f98d5e914fc106090cdbc (diff)
parent3cc39ffbceffc5fdbb511d9a10e7732329e8baa4 (diff)
downloaddjango-rest-framework-e5b94f7b7bbf2f6f35c5e33fb2723bdb0d33bad3.tar.bz2
Merge branch 'version-3.1' into cursor-pagination
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/__init__.py2
-rw-r--r--rest_framework/compat.py16
-rw-r--r--rest_framework/pagination.py6
-rw-r--r--rest_framework/parsers.py2
-rw-r--r--rest_framework/relations.py5
-rw-r--r--rest_framework/renderers.py10
-rw-r--r--rest_framework/routers.py4
-rw-r--r--rest_framework/settings.py2
-rw-r--r--rest_framework/throttling.py10
-rw-r--r--rest_framework/utils/encoders.py6
-rw-r--r--rest_framework/utils/serializer_helpers.py6
-rw-r--r--rest_framework/versioning.py2
12 files changed, 45 insertions, 26 deletions
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index dec89b3e..fdcebb7b 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
-__version__ = '3.0.2'
+__version__ = '3.0.3'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2015 Tom Christie'
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index 7241da27..bd3802ad 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -8,7 +8,7 @@ from __future__ import unicode_literals
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
from django.utils.encoding import force_text
-from django.utils.six.moves.urllib import parse as urlparse
+from django.utils.six.moves.urllib.parse import urlparse as _urlparse
from django.utils import six
import django
import inspect
@@ -38,10 +38,18 @@ def unicode_http_header(value):
return value
+def total_seconds(timedelta):
+ # TimeDelta.total_seconds() is only available in Python 2.7
+ if hasattr(timedelta, 'total_seconds'):
+ return timedelta.total_seconds()
+ else:
+ return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0)
+
+
# OrderedDict only available in Python 2.7.
# This will always be the case in Django 1.7 and above, as these versions
# no longer support Python 2.6.
-# For Django <= 1.6 and Python 2.6 fall back to OrderedDict.
+# For Django <= 1.6 and Python 2.6 fall back to SortedDict.
try:
from collections import OrderedDict
except ImportError:
@@ -187,7 +195,7 @@ except ImportError:
class RequestFactory(DjangoRequestFactory):
def generic(self, method, path,
data='', content_type='application/octet-stream', **extra):
- parsed = urlparse.urlparse(path)
+ parsed = _urlparse(path)
data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET)
r = {
'PATH_INFO': self._get_path(parsed),
@@ -227,6 +235,8 @@ except ImportError:
if six.PY3:
SHORT_SEPARATORS = (',', ':')
LONG_SEPARATORS = (', ', ': ')
+ INDENT_SEPARATORS = (',', ': ')
else:
SHORT_SEPARATORS = (b',', b':')
LONG_SEPARATORS = (b', ', b': ')
+ INDENT_SEPARATORS = (b',', b': ')
diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py
index f56f55ce..5482788a 100644
--- a/rest_framework/pagination.py
+++ b/rest_framework/pagination.py
@@ -135,13 +135,13 @@ class BasePagination(object):
display_page_controls = False
def paginate_queryset(self, queryset, request, view=None): # pragma: no cover
- raise NotImplemented('paginate_queryset() must be implemented.')
+ raise NotImplementedError('paginate_queryset() must be implemented.')
def get_paginated_response(self, data): # pragma: no cover
- raise NotImplemented('get_paginated_response() must be implemented.')
+ raise NotImplementedError('get_paginated_response() must be implemented.')
def to_html(self): # pragma: no cover
- raise NotImplemented('to_html() must be implemented to display page controls.')
+ raise NotImplementedError('to_html() must be implemented to display page controls.')
class PageNumberPagination(BasePagination):
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index cb23423d..dd069840 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -179,7 +179,7 @@ class FileUploadParser(BaseParser):
for index, handler in enumerate(upload_handlers):
file_obj = handler.file_complete(counters[index])
if file_obj:
- return DataAndFiles(None, {'file': file_obj})
+ return DataAndFiles({}, {'file': file_obj})
raise ParseError("FileUpload parse error - "
"none of upload handlers can handle the stream")
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 05ac3d1c..a85edfec 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -7,6 +7,7 @@ from django.utils import six
from django.utils.encoding import smart_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _
+from rest_framework.compat import OrderedDict
from rest_framework.fields import get_attribute, empty, Field
from rest_framework.reverse import reverse
from rest_framework.utils import html
@@ -103,7 +104,7 @@ class RelatedField(Field):
@property
def choices(self):
- return dict([
+ return OrderedDict([
(
six.text_type(self.to_representation(item)),
six.text_type(item)
@@ -364,7 +365,7 @@ class ManyRelatedField(Field):
(item, self.child_relation.to_representation(item))
for item in iterable
]
- return dict([
+ return OrderedDict([
(
six.text_type(item_representation),
six.text_type(item) + ' - ' + six.text_type(item_representation)
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 4c46b049..f970a363 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -18,7 +18,7 @@ from django.template import Context, RequestContext, loader, Template
from django.test.client import encode_multipart
from django.utils import six
from rest_framework import exceptions, serializers, status, VERSION
-from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS
+from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, INDENT_SEPARATORS
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.request import is_form_media_type, override_method
@@ -43,7 +43,7 @@ class BaseRenderer(object):
render_style = 'text'
def render(self, data, accepted_media_type=None, renderer_context=None):
- raise NotImplemented('Renderer class requires .render() to be implemented')
+ raise NotImplementedError('Renderer class requires .render() to be implemented')
class JSONRenderer(BaseRenderer):
@@ -87,7 +87,11 @@ class JSONRenderer(BaseRenderer):
renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context)
- separators = SHORT_SEPARATORS if (indent is None and self.compact) else LONG_SEPARATORS
+
+ if indent is None:
+ separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS
+ else:
+ separators = INDENT_SEPARATORS
ret = json.dumps(
data, cls=self.encoder_class,
diff --git a/rest_framework/routers.py b/rest_framework/routers.py
index 61f3ccab..827da034 100644
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -65,13 +65,13 @@ class BaseRouter(object):
If `base_name` is not specified, attempt to automatically determine
it from the viewset.
"""
- raise NotImplemented('get_default_base_name must be overridden')
+ raise NotImplementedError('get_default_base_name must be overridden')
def get_urls(self):
"""
Return a list of URL patterns, given the registered viewsets.
"""
- raise NotImplemented('get_urls must be overridden')
+ raise NotImplementedError('get_urls must be overridden')
@property
def urls(self):
diff --git a/rest_framework/settings.py b/rest_framework/settings.py
index 3cce26b1..ca5af86e 100644
--- a/rest_framework/settings.py
+++ b/rest_framework/settings.py
@@ -176,7 +176,7 @@ class APISettings(object):
For example:
from rest_framework.settings import api_settings
- print api_settings.DEFAULT_RENDERER_CLASSES
+ print(api_settings.DEFAULT_RENDERER_CLASSES)
Any setting with string import paths will be automatically resolved
and return the class, rather than the string literal.
diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py
index 361dbddf..0f10136d 100644
--- a/rest_framework/throttling.py
+++ b/rest_framework/throttling.py
@@ -32,10 +32,10 @@ class BaseThrottle(object):
if num_proxies == 0 or xff is None:
return remote_addr
addrs = xff.split(',')
- client_addr = addrs[-min(num_proxies, len(xff))]
+ client_addr = addrs[-min(num_proxies, len(addrs))]
return client_addr.strip()
- return xff if xff else remote_addr
+ return ''.join(xff.split()) if xff else remote_addr
def wait(self):
"""
@@ -173,12 +173,6 @@ class AnonRateThrottle(SimpleRateThrottle):
if request.user.is_authenticated():
return None # Only throttle unauthenticated requests.
- ident = request.META.get('HTTP_X_FORWARDED_FOR')
- if ident is None:
- ident = request.META.get('REMOTE_ADDR')
- else:
- ident = ''.join(ident.split())
-
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py
index 0bd24939..2160d18b 100644
--- a/rest_framework/utils/encoders.py
+++ b/rest_framework/utils/encoders.py
@@ -6,9 +6,11 @@ from django.db.models.query import QuerySet
from django.utils import six, timezone
from django.utils.encoding import force_text
from django.utils.functional import Promise
+from rest_framework.compat import total_seconds
import datetime
import decimal
import json
+import uuid
class JSONEncoder(json.JSONEncoder):
@@ -38,10 +40,12 @@ class JSONEncoder(json.JSONEncoder):
representation = representation[:12]
return representation
elif isinstance(obj, datetime.timedelta):
- return six.text_type(obj.total_seconds())
+ return six.text_type(total_seconds(obj))
elif isinstance(obj, decimal.Decimal):
# Serializers will coerce decimals to strings by default.
return float(obj)
+ elif isinstance(obj, uuid.UUID):
+ return six.text_type(obj)
elif isinstance(obj, QuerySet):
return tuple(obj)
elif hasattr(obj, 'tolist'):
diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py
index 65a04d06..f9960603 100644
--- a/rest_framework/utils/serializer_helpers.py
+++ b/rest_framework/utils/serializer_helpers.py
@@ -16,6 +16,9 @@ class ReturnDict(OrderedDict):
def copy(self):
return ReturnDict(self, serializer=self.serializer)
+ def __repr__(self):
+ return dict.__repr__(self)
+
class ReturnList(list):
"""
@@ -27,6 +30,9 @@ class ReturnList(list):
self.serializer = kwargs.pop('serializer')
super(ReturnList, self).__init__(*args, **kwargs)
+ def __repr__(self):
+ return list.__repr__(self)
+
class BoundField(object):
"""
diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py
index e31c71e9..a07b629f 100644
--- a/rest_framework/versioning.py
+++ b/rest_framework/versioning.py
@@ -17,7 +17,7 @@ class BaseVersioning(object):
def determine_version(self, request, *args, **kwargs):
msg = '{cls}.determine_version() must be implemented.'
- raise NotImplemented(msg.format(
+ raise NotImplementedError(msg.format(
cls=self.__class__.__name__
))