diff options
32 files changed, 422 insertions, 324 deletions
| diff --git a/.travis.yml b/.travis.yml index 0dc87837..572e483b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,27 @@ language: python  python:    - "2.6"    - "2.7" +  - "3.2"  env:    - DJANGO=https://github.com/django/django/zipball/master -  - DJANGO=django==1.4.3 --use-mirrors -  - DJANGO=django==1.3.5 --use-mirrors +  - DJANGO=https://www.djangoproject.com/download/1.5c1/tarball/ +  - DJANGO="django==1.4.3 --use-mirrors" +  - DJANGO="django==1.3.5 --use-mirrors"  install:    - pip install $DJANGO -  - pip install django-filter==0.5.4 --use-mirrors +  - "if [[ $TRAVIS_PYTHON_VERSION != '3.2' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi" +  - "if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi" +  - "if [[ $DJANGO == 'django==1.3.5 --use-mirrors' ]]; then pip install six --use-mirrors; fi"    - export PYTHONPATH=.  script:    - python rest_framework/runtests/runtests.py + +matrix: +  exclude: +    - python: "3.2" +      env: DJANGO="django==1.4.3 --use-mirrors" +    - python: "3.2" +      env: DJANGO="django==1.3.5 --use-mirrors" diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index fc169189..76ee4bd6 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -3,10 +3,12 @@ Provides a set of pluggable authentication policies.  """  from django.contrib.auth import authenticate -from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError +from django.utils.encoding import DjangoUnicodeDecodeError  from rest_framework import exceptions  from rest_framework.compat import CsrfViewMiddleware +from rest_framework.compat import smart_text  from rest_framework.authtoken.models import Token +from rest_framework.settings import api_settings  import base64 @@ -49,14 +51,15 @@ class BasicAuthentication(BaseAuthentication):          if len(auth) != 2:              raise exceptions.AuthenticationFailed('Invalid basic header') +        encoding = api_settings.HTTP_HEADER_ENCODING          try: -            auth_parts = base64.b64decode(auth[1]).partition(':') +            auth_parts = base64.b64decode(auth[1].encode(encoding)).partition(':')          except TypeError:              raise exceptions.AuthenticationFailed('Invalid basic header')          try: -            userid = smart_unicode(auth_parts[0]) -            password = smart_unicode(auth_parts[2]) +            userid = smart_text(auth_parts[0]) +            password = smart_text(auth_parts[2])          except DjangoUnicodeDecodeError:              raise exceptions.AuthenticationFailed('Invalid basic header') diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py index 4da2aa62..7f5a75a3 100644 --- a/rest_framework/authtoken/models.py +++ b/rest_framework/authtoken/models.py @@ -19,8 +19,8 @@ class Token(models.Model):          return super(Token, self).save(*args, **kwargs)      def generate_key(self): -        unique = str(uuid.uuid4()) -        return hmac.new(unique, digestmod=sha1).hexdigest() +        unique = uuid.uuid4() +        return hmac.new(unique.bytes, digestmod=sha1).hexdigest()      def __unicode__(self):          return self.key diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 5508f6c0..ef11b85b 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -3,14 +3,35 @@ The `compat` module provides support for backwards compatibility with older  versions of django/python, and compatibility wrappers around optional packages.  """  # flake8: noqa +from __future__ import unicode_literals +  import django +# Try to import six from Django, fallback to six itself (1.3.x) +try: +    from django.utils import six +except: +    import six +  # location of patterns, url, include changes in 1.4 onwards  try:      from django.conf.urls import patterns, url, include  except:      from django.conf.urls.defaults import patterns, url, include +# Handle django.utils.encoding rename: +# 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 + +  # django-filter is optional  try:      import django_filters @@ -20,9 +41,18 @@ except:  # cStringIO only if it's available, otherwise StringIO  try: -    import cStringIO as StringIO +    import cStringIO.StringIO as StringIO  except ImportError: -    import StringIO +    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  # Try to import PIL in either of the two ways it can end up installed. @@ -54,7 +84,7 @@ else:      try:          from django.contrib.auth.models import User      except ImportError: -        raise ImportError(u"User model is not to be found.") +        raise ImportError("User model is not to be found.")  # First implementation of Django class-based views did not include head method @@ -75,11 +105,11 @@ else:              # sanitize keyword arguments              for key in initkwargs:                  if key in cls.http_method_names: -                    raise TypeError(u"You tried to pass in the %s method name as a " -                                    u"keyword argument to %s(). Don't do that." +                    raise TypeError("You tried to pass in the %s method name as a " +                                    "keyword argument to %s(). Don't do that."                                      % (key, cls.__name__))                  if not hasattr(cls, key): -                    raise TypeError(u"%s() received an invalid keyword %r" % ( +                    raise TypeError("%s() received an invalid keyword %r" % (                          cls.__name__, key))              def view(request, *args, **kwargs): @@ -110,7 +140,6 @@ else:      import re      import random      import logging -    import urlparse      from django.conf import settings      from django.core.urlresolvers import get_callable @@ -152,7 +181,8 @@ else:          randrange = random.SystemRandom().randrange      else:          randrange = random.randrange -    _MAX_CSRF_KEY = 18446744073709551616L     # 2 << 63 + +    _MAX_CSRF_KEY = 18446744073709551616      # 2 << 63      REASON_NO_REFERER = "Referer checking failed - no Referer."      REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 998911e1..a66e1d7c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1,20 +1,23 @@ +from __future__ import unicode_literals +  import copy  import datetime  import inspect  import re  import warnings -from io import BytesIO -  from django.core import validators  from django.core.exceptions import ValidationError  from django.conf import settings  from django import forms  from django.forms import widgets -from django.utils.encoding import is_protected_type, smart_unicode +from django.utils.encoding import is_protected_type  from django.utils.translation import ugettext_lazy as _  from rest_framework.compat import parse_date, parse_datetime  from rest_framework.compat import timezone +from rest_framework.compat import BytesIO +from rest_framework.compat import six +from rest_framework.compat import smart_text  def is_simple_callable(obj): @@ -93,11 +96,11 @@ class Field(object):          if is_protected_type(value):              return value -        elif hasattr(value, '__iter__') and not isinstance(value, (dict, basestring)): +        elif hasattr(value, '__iter__') and not isinstance(value, (dict, six.string_types)):              return [self.to_native(item) for item in value]          elif isinstance(value, dict):              return dict(map(self.to_native, (k, v)) for k, v in value.items()) -        return smart_unicode(value) +        return smart_text(value)      def attributes(self):          """ @@ -258,7 +261,7 @@ class BooleanField(WritableField):      form_field_class = forms.BooleanField      widget = widgets.CheckboxInput      default_error_messages = { -        'invalid': _(u"'%s' value must be either True or False."), +        'invalid': _("'%s' value must be either True or False."),      }      empty = False @@ -298,9 +301,9 @@ class CharField(WritableField):              super(CharField, self).validate(value)      def from_native(self, value): -        if isinstance(value, basestring) or value is None: +        if isinstance(value, six.string_types) or value is None:              return value -        return smart_unicode(value) +        return smart_text(value)  class URLField(CharField): @@ -359,10 +362,10 @@ class ChoiceField(WritableField):              if isinstance(v, (list, tuple)):                  # This is an optgroup, so look inside the group for options                  for k2, v2 in v: -                    if value == smart_unicode(k2): +                    if value == smart_text(k2):                          return True              else: -                if value == smart_unicode(k) or value == k: +                if value == smart_text(k) or value == k:                      return True          return False @@ -402,7 +405,7 @@ class RegexField(CharField):          return self._regex      def _set_regex(self, regex): -        if isinstance(regex, basestring): +        if isinstance(regex, six.string_types):              regex = re.compile(regex)          self._regex = regex          if hasattr(self, '_regex_validator') and self._regex_validator in self.validators: @@ -425,10 +428,10 @@ class DateField(WritableField):      form_field_class = forms.DateField      default_error_messages = { -        'invalid': _(u"'%s' value has an invalid date format. It must be " -                     u"in YYYY-MM-DD format."), -        'invalid_date': _(u"'%s' value has the correct format (YYYY-MM-DD) " -                          u"but it is an invalid date."), +        'invalid': _("'%s' value has an invalid date format. It must be " +                     "in YYYY-MM-DD format."), +        'invalid_date': _("'%s' value has the correct format (YYYY-MM-DD) " +                          "but it is an invalid date."),      }      empty = None @@ -464,13 +467,13 @@ class DateTimeField(WritableField):      form_field_class = forms.DateTimeField      default_error_messages = { -        'invalid': _(u"'%s' value has an invalid format. It must be in " -                     u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."), -        'invalid_date': _(u"'%s' value has the correct format " -                          u"(YYYY-MM-DD) but it is an invalid date."), -        'invalid_datetime': _(u"'%s' value has the correct format " -                              u"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " -                              u"but it is an invalid date/time."), +        'invalid': _("'%s' value has an invalid format. It must be in " +                     "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."), +        'invalid_date': _("'%s' value has the correct format " +                          "(YYYY-MM-DD) but it is an invalid date."), +        'invalid_datetime': _("'%s' value has the correct format " +                              "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " +                              "but it is an invalid date/time."),      }      empty = None @@ -487,8 +490,8 @@ class DateTimeField(WritableField):                  # local time. This won't work during DST change, but we can't                  # do much about it, so we let the exceptions percolate up the                  # call stack. -                warnings.warn(u"DateTimeField received a naive datetime (%s)" -                              u" while time zone support is active." % value, +                warnings.warn("DateTimeField received a naive datetime (%s)" +                              " while time zone support is active." % value,                                RuntimeWarning)                  default_timezone = timezone.get_default_timezone()                  value = timezone.make_aware(value, default_timezone) diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 43581ae9..acaf8a71 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -4,6 +4,8 @@ Basic building blocks for generic class based views.  We don't bind behaviour to http method handlers yet,  which allows mixin classes to be composed in interesting ways.  """ +from __future__ import unicode_literals +  from django.http import Http404  from rest_framework import status  from rest_framework.response import Response @@ -41,7 +43,7 @@ class ListModelMixin(object):      List a queryset.      Should be mixed in with `MultipleObjectAPIView`.      """ -    empty_error = u"Empty list and '%(class_name)s.allow_empty' is False." +    empty_error = "Empty list and '%(class_name)s.allow_empty' is False."      def list(self, request, *args, **kwargs):          queryset = self.get_queryset() diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 149d6431..4a2b34a5 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -10,6 +10,7 @@ from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser  from django.http.multipartparser import MultiPartParserError  from rest_framework.compat import yaml, ETParseError  from rest_framework.exceptions import ParseError +from rest_framework.compat import six  from xml.etree import ElementTree as ET  from xml.parsers.expat import ExpatError  import json @@ -55,9 +56,10 @@ class JSONParser(BaseParser):          `files` will always be `None`.          """          try: -            return json.load(stream) -        except ValueError, exc: -            raise ParseError('JSON parse error - %s' % unicode(exc)) +            data = stream.read().decode('iso-8859-1') +            return json.loads(data) +        except ValueError as exc: +            raise ParseError('JSON parse error - %s' % six.text_type(exc))  class YAMLParser(BaseParser): @@ -75,9 +77,10 @@ class YAMLParser(BaseParser):          `files` will always be `None`.          """          try: -            return yaml.safe_load(stream) -        except (ValueError, yaml.parser.ParserError), exc: -            raise ParseError('YAML parse error - %s' % unicode(exc)) +            data = stream.read().decode('iso-8859-1') +            return yaml.safe_load(data) +        except (ValueError, yaml.parser.ParserError) as exc: +            raise ParseError('YAML parse error - %s' % six.u(exc))  class FormParser(BaseParser): @@ -121,8 +124,8 @@ class MultiPartParser(BaseParser):              parser = DjangoMultiPartParser(meta, stream, upload_handlers)              data, files = parser.parse()              return DataAndFiles(data, files) -        except MultiPartParserError, exc: -            raise ParseError('Multipart form parse error - %s' % unicode(exc)) +        except MultiPartParserError as exc: +            raise ParseError('Multipart form parse error - %s' % six.u(exc))  class XMLParser(BaseParser): @@ -135,8 +138,8 @@ class XMLParser(BaseParser):      def parse(self, stream, media_type=None, parser_context=None):          try:              tree = ET.parse(stream) -        except (ExpatError, ETParseError, ValueError), exc: -            raise ParseError('XML parse error - %s' % unicode(exc)) +        except (ExpatError, ETParseError, ValueError) as exc: +            raise ParseError('XML parse error - %s' % six.u(exc))          data = self._xml_convert(tree.getroot())          return data diff --git a/rest_framework/relations.py b/rest_framework/relations.py index dc0a73e6..c4f854ef 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,13 +1,16 @@ + +from __future__ import unicode_literals +  from django.core.exceptions import ObjectDoesNotExist, ValidationError  from django.core.urlresolvers import resolve, get_script_prefix  from django import forms  from django.forms import widgets  from django.forms.models import ModelChoiceIterator -from django.utils.encoding import smart_unicode  from django.utils.translation import ugettext_lazy as _  from rest_framework.fields import Field, WritableField  from rest_framework.reverse import reverse -from urlparse import urlparse +from rest_framework.compat import urlparse +from rest_framework.compat import smart_text  ##### Relational fields ##### @@ -60,8 +63,8 @@ class RelatedField(WritableField):          """          Return a readable representation for use with eg. select widgets.          """ -        desc = smart_unicode(obj) -        ident = smart_unicode(self.to_native(obj)) +        desc = smart_text(obj) +        ident = smart_text(self.to_native(obj))          if desc == ident:              return desc          return "%s - %s" % (desc, ident) @@ -188,8 +191,8 @@ class PrimaryKeyRelatedField(RelatedField):          """          Return a readable representation for use with eg. select widgets.          """ -        desc = smart_unicode(obj) -        ident = smart_unicode(self.to_native(obj.pk)) +        desc = smart_text(obj) +        ident = smart_text(self.to_native(obj.pk))          if desc == ident:              return desc          return "%s - %s" % (desc, ident) @@ -205,7 +208,7 @@ class PrimaryKeyRelatedField(RelatedField):          try:              return self.queryset.get(pk=data)          except ObjectDoesNotExist: -            msg = self.error_messages['does_not_exist'] % smart_unicode(data) +            msg = self.error_messages['does_not_exist'] % smart_text(data)              raise ValidationError(msg)          except (TypeError, ValueError):              received = type(data).__name__ @@ -246,8 +249,8 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):          """          Return a readable representation for use with eg. select widgets.          """ -        desc = smart_unicode(obj) -        ident = smart_unicode(self.to_native(obj.pk)) +        desc = smart_text(obj) +        ident = smart_text(self.to_native(obj.pk))          if desc == ident:              return desc          return "%s - %s" % (desc, ident) @@ -273,7 +276,7 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):          try:              return self.queryset.get(pk=data)          except ObjectDoesNotExist: -            msg = self.error_messages['does_not_exist'] % smart_unicode(data) +            msg = self.error_messages['does_not_exist'] % smart_text(data)              raise ValidationError(msg)          except (TypeError, ValueError):              received = type(data).__name__ @@ -404,7 +407,7 @@ class HyperlinkedRelatedField(RelatedField):          if http_prefix:              # If needed convert absolute URLs to relative path -            value = urlparse(value).path +            value = urlparse.urlparse(value).path              prefix = get_script_prefix()              if value.startswith(prefix):                  value = '/' + value[len(prefix):] diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 0a34abaa..b3ee0690 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -6,6 +6,8 @@ on the response, such as JSON encoded data or HTML output.  REST framework also provides an HTML renderer the renders the browsable API.  """ +from __future__ import unicode_literals +  import copy  import string  import json @@ -60,7 +62,7 @@ class JSONRenderer(BaseRenderer):          if accepted_media_type:              # If the media type looks like 'application/json; indent=4',              # then pretty print the result. -            base_media_type, params = parse_header(accepted_media_type) +            base_media_type, params = parse_header(accepted_media_type.encode('ascii'))              indent = params.get('indent', indent)              try:                  indent = max(min(int(indent), 8), 0) @@ -100,7 +102,7 @@ class JSONPRenderer(JSONRenderer):          callback = self.get_callback(renderer_context)          json = super(JSONPRenderer, self).render(data, accepted_media_type,                                                   renderer_context) -        return u"%s(%s);" % (callback, json) +        return "%s(%s);" % (callback, json)  class XMLRenderer(BaseRenderer): @@ -357,7 +359,7 @@ class BrowsableAPIRenderer(BaseRenderer):          # Creating an on the fly form see:          # http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python -        OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) +        OnTheFlyForm = type(str("OnTheFlyForm"), (forms.Form,), fields)          data = (obj is not None) and serializer.data or None          form_instance = OnTheFlyForm(data)          return form_instance diff --git a/rest_framework/request.py b/rest_framework/request.py index 1c28cd17..23e1da87 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -9,7 +9,7 @@ The wrapped request then offers a richer API, in particular :      - full support of PUT method, including support for file uploads      - form overloading of HTTP method, content type and content  """ -from StringIO import StringIO +from rest_framework.compat import BytesIO  from django.http.multipartparser import parse_header  from rest_framework import exceptions @@ -20,7 +20,7 @@ def is_form_media_type(media_type):      """      Return True if the media type is a valid form media type.      """ -    base_media_type, params = parse_header(media_type) +    base_media_type, params = parse_header(media_type.encode('iso-8859-1'))      return (base_media_type == 'application/x-www-form-urlencoded' or              base_media_type == 'multipart/form-data') @@ -242,7 +242,7 @@ class Request(object):          elif hasattr(self._request, 'read'):              self._stream = self._request          else: -            self._stream = StringIO(self.raw_post_data) +            self._stream = BytesIO(self.raw_post_data)      def _perform_form_overloading(self):          """ @@ -277,7 +277,7 @@ class Request(object):              self._CONTENT_PARAM in self._data and              self._CONTENTTYPE_PARAM in self._data):              self._content_type = self._data[self._CONTENTTYPE_PARAM] -            self._stream = StringIO(self._data[self._CONTENT_PARAM]) +            self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode('iso-8859-1'))              self._data, self._files = (Empty, Empty)      def _parse(self): diff --git a/rest_framework/response.py b/rest_framework/response.py index be78c43a..0a484c4a 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -1,6 +1,8 @@  from django.core.handlers.wsgi import STATUS_CODE_TEXT  from django.template.response import SimpleTemplateResponse +from rest_framework.compat import six +  class Response(SimpleTemplateResponse):      """ @@ -22,9 +24,9 @@ class Response(SimpleTemplateResponse):          self.data = data          self.template_name = template_name          self.exception = exception -         +          if headers: -            for name,value in headers.iteritems(): +            for name, value in six.iteritems(headers):                  self[name] = value      @property diff --git a/rest_framework/runtests/runtests.py b/rest_framework/runtests/runtests.py index 505994e2..4a333fb3 100755 --- a/rest_framework/runtests/runtests.py +++ b/rest_framework/runtests/runtests.py @@ -33,7 +33,7 @@ def main():      elif len(sys.argv) == 1:          test_case = ''      else: -        print usage() +        print(usage())          sys.exit(1)      failures = test_runner.run_tests(['tests' + test_case]) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 4fb802a7..3d3bcb3c 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -7,6 +7,7 @@ from django.db import models  from django.forms import widgets  from django.utils.datastructures import SortedDict  from rest_framework.compat import get_concrete_model +from rest_framework.compat import six  # Note: We do the following so that users of the framework can use this style:  # @@ -64,7 +65,7 @@ def _get_declared_fields(bases, attrs):      Note that all fields from the base classes are used.      """      fields = [(field_name, attrs.pop(field_name)) -              for field_name, obj in attrs.items() +              for field_name, obj in list(six.iteritems(attrs))                if isinstance(obj, Field)]      fields.sort(key=lambda x: x[1].creation_counter) @@ -73,7 +74,7 @@ def _get_declared_fields(bases, attrs):      # in order to maintain the correct order of fields.      for base in bases[::-1]:          if hasattr(base, 'base_fields'): -            fields = base.base_fields.items() + fields +            fields = list(base.base_fields.items()) + fields      return SortedDict(fields) @@ -359,8 +360,8 @@ class BaseSerializer(Field):          return self.object -class Serializer(BaseSerializer): -    __metaclass__ = SerializerMetaclass +class Serializer(six.with_metaclass(SerializerMetaclass, BaseSerializer)): +    pass  class ModelSerializerOptions(SerializerOptions): @@ -560,6 +561,12 @@ class ModelSerializer(Serializer):          else:              instance = self.opts.model(**attrs) +        try: +            instance.full_clean(exclude=self.get_validation_exclusions()) +        except ValidationError as err: +            self._errors = err.message_dict +            return None +          return instance      def from_native(self, data, files): diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 5c77c55c..13d03e62 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -19,6 +19,7 @@ back to the defaults.  """  from django.conf import settings  from django.utils import importlib +from rest_framework.compat import six  USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None) @@ -74,6 +75,9 @@ DEFAULTS = {      'URL_FORMAT_OVERRIDE': 'format',      'FORMAT_SUFFIX_KWARG': 'format', + +    # Header encoding (see RFC5987) +    'HTTP_HEADER_ENCODING': 'iso-8859-1',  } @@ -98,7 +102,7 @@ def perform_import(val, setting_name):      If the given setting is a string import notation,      then perform the necessary import or imports.      """ -    if isinstance(val, basestring): +    if isinstance(val, six.string_types):          return import_from_string(val, setting_name)      elif isinstance(val, (list, tuple)):          return [import_from_string(item, setting_name) for item in val] diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 82fcdfe7..cbafbe0e 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -1,10 +1,14 @@ +from __future__ import unicode_literals, absolute_import +  from django import template  from django.core.urlresolvers import reverse  from django.http import QueryDict -from django.utils.encoding import force_unicode  from django.utils.html import escape  from django.utils.safestring import SafeData, mark_safe -from urlparse import urlsplit, urlunsplit +from rest_framework.compat import urlparse +from rest_framework.compat import force_text +from rest_framework.compat import six +  import re  import string @@ -99,11 +103,11 @@ def replace_query_param(url, key, val):      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.      """ -    (scheme, netloc, path, query, fragment) = urlsplit(url) +    (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)      query_dict = QueryDict(query).copy()      query_dict[key] = val      query = query_dict.urlencode() -    return urlunsplit((scheme, netloc, path, query, fragment)) +    return urlparse.urlunsplit((scheme, netloc, path, query, fragment))  # Regex for adding classes to html snippets @@ -179,7 +183,7 @@ def add_class(value, css_class):      In the case of REST Framework, the filter is used to add Bootstrap-specific      classes to the forms.      """ -    html = unicode(value) +    html = six.text_type(value)      match = class_re.search(html)      if match:          m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class, @@ -213,7 +217,7 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru      """      trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x      safe_input = isinstance(text, SafeData) -    words = word_split_re.split(force_unicode(text)) +    words = word_split_re.split(force_text(text))      nofollow_attr = nofollow and ' rel="nofollow"' or ''      for i, word in enumerate(words):          match = None @@ -249,4 +253,4 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru              words[i] = mark_safe(word)          elif autoescape:              words[i] = escape(word) -    return mark_safe(u''.join(words)) +    return mark_safe(''.join(words)) diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 1f17e8d2..ba2042cb 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -1,7 +1,6 @@  from django.contrib.auth.models import User  from django.http import HttpResponse  from django.test import Client, TestCase -  from rest_framework import permissions  from rest_framework.authtoken.models import Token  from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication @@ -42,13 +41,13 @@ class BasicAuthTests(TestCase):      def test_post_form_passing_basic_auth(self):          """Ensure POSTing json over basic auth with correct credentials passes and does not require CSRF""" -        auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip() +        auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).encode('iso-8859-1').strip().decode('iso-8859-1')          response = self.csrf_client.post('/basic/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)          self.assertEqual(response.status_code, 200)      def test_post_json_passing_basic_auth(self):          """Ensure POSTing form over basic auth with correct credentials passes and does not require CSRF""" -        auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip() +        auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).encode('iso-8859-1').strip().decode('iso-8859-1')          response = self.csrf_client.post('/basic/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)          self.assertEqual(response.status_code, 200) @@ -159,7 +158,7 @@ class TokenAuthTests(TestCase):          response = client.post('/auth-token/',                                 json.dumps({'username': self.username, 'password': self.password}), 'application/json')          self.assertEqual(response.status_code, 200) -        self.assertEqual(json.loads(response.content)['token'], self.key) +        self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key)      def test_token_login_json_bad_creds(self):          """Ensure token login view using JSON POST fails if bad credentials are used.""" @@ -181,4 +180,4 @@ class TokenAuthTests(TestCase):          response = client.post('/auth-token/',                                 {'username': self.username, 'password': self.password})          self.assertEqual(response.status_code, 200) -        self.assertEqual(json.loads(response.content)['token'], self.key) +        self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key) diff --git a/rest_framework/tests/files.py b/rest_framework/tests/files.py index 446e23c0..0434f900 100644 --- a/rest_framework/tests/files.py +++ b/rest_framework/tests/files.py @@ -1,9 +1,10 @@ -import StringIO  import datetime  from django.test import TestCase  from rest_framework import serializers +from rest_framework.compat import BytesIO +from rest_framework.compat import six  class UploadedFile(object): @@ -27,9 +28,9 @@ class UploadedFileSerializer(serializers.Serializer):  class FileSerializerTests(TestCase):      def test_create(self):          now = datetime.datetime.now() -        file = StringIO.StringIO('stuff') +        file = BytesIO(six.b('stuff'))          file.name = 'stuff.txt' -        file.size = file.len +        file.size = len(file.getvalue())          serializer = UploadedFileSerializer(data={'created': now}, files={'file': file})          uploaded_file = UploadedFile(file=file, created=now)          self.assertTrue(serializer.is_valid()) diff --git a/rest_framework/tests/genericrelations.py b/rest_framework/tests/genericrelations.py index 146ad1e4..72070a1a 100644 --- a/rest_framework/tests/genericrelations.py +++ b/rest_framework/tests/genericrelations.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  from django.contrib.contenttypes.models import ContentType  from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey  from django.db import models @@ -63,8 +65,8 @@ class TestGenericRelations(TestCase):          serializer = BookmarkSerializer(self.bookmark)          expected = { -            'tags': [u'django', u'python'], -            'url': u'https://www.djangoproject.com/' +            'tags': ['django', 'python'], +            'url': 'https://www.djangoproject.com/'          }          self.assertEquals(serializer.data, expected) diff --git a/rest_framework/tests/generics.py b/rest_framework/tests/generics.py index 4799a04b..fd01312a 100644 --- a/rest_framework/tests/generics.py +++ b/rest_framework/tests/generics.py @@ -1,10 +1,11 @@ -import json +from __future__ import unicode_literals  from django.db import models  from django.test import TestCase  from rest_framework import generics, serializers, status  from rest_framework.tests.utils import RequestFactory  from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel - +from rest_framework.compat import six +import json  factory = RequestFactory() @@ -72,7 +73,7 @@ class TestRootView(TestCase):                                 content_type='application/json')          response = self.view(request).render()          self.assertEquals(response.status_code, status.HTTP_201_CREATED) -        self.assertEquals(response.data, {'id': 4, 'text': u'foobar'}) +        self.assertEquals(response.data, {'id': 4, 'text': 'foobar'})          created = self.objects.get(id=4)          self.assertEquals(created.text, 'foobar') @@ -127,7 +128,7 @@ class TestRootView(TestCase):                                 content_type='application/json')          response = self.view(request).render()          self.assertEquals(response.status_code, status.HTTP_201_CREATED) -        self.assertEquals(response.data, {'id': 4, 'text': u'foobar'}) +        self.assertEquals(response.data, {'id': 4, 'text': 'foobar'})          created = self.objects.get(id=4)          self.assertEquals(created.text, 'foobar') @@ -202,7 +203,7 @@ class TestInstanceView(TestCase):          request = factory.delete('/1')          response = self.view(request, pk=1).render()          self.assertEquals(response.status_code, status.HTTP_204_NO_CONTENT) -        self.assertEquals(response.content, '') +        self.assertEquals(response.content, six.b(''))          ids = [obj.id for obj in self.objects.all()]          self.assertEquals(ids, [2, 3]) diff --git a/rest_framework/tests/htmlrenderer.py b/rest_framework/tests/htmlrenderer.py index 54096206..34caa208 100644 --- a/rest_framework/tests/htmlrenderer.py +++ b/rest_framework/tests/htmlrenderer.py @@ -7,6 +7,7 @@ from rest_framework.compat import patterns, url  from rest_framework.decorators import api_view, renderer_classes  from rest_framework.renderers import TemplateHTMLRenderer  from rest_framework.response import Response +from rest_framework.compat import six  @api_view(('GET',)) @@ -68,13 +69,13 @@ class TemplateHTMLRendererTests(TestCase):      def test_not_found_html_view(self):          response = self.client.get('/not_found')          self.assertEquals(response.status_code, 404) -        self.assertEquals(response.content, "404 Not Found") +        self.assertEquals(response.content, six.b("404 Not Found"))          self.assertEquals(response['Content-Type'], 'text/html')      def test_permission_denied_html_view(self):          response = self.client.get('/permission_denied')          self.assertEquals(response.status_code, 403) -        self.assertEquals(response.content, "403 Forbidden") +        self.assertEquals(response.content, six.b("403 Forbidden"))          self.assertEquals(response['Content-Type'], 'text/html') @@ -105,11 +106,11 @@ class TemplateHTMLRendererExceptionTests(TestCase):      def test_not_found_html_view_with_template(self):          response = self.client.get('/not_found')          self.assertEquals(response.status_code, 404) -        self.assertEquals(response.content, "404: Not found") +        self.assertEquals(response.content, six.b("404: Not found"))          self.assertEquals(response['Content-Type'], 'text/html')      def test_permission_denied_html_view_with_template(self):          response = self.client.get('/permission_denied')          self.assertEquals(response.status_code, 403) -        self.assertEquals(response.content, "403: Permission denied") +        self.assertEquals(response.content, six.b("403: Permission denied"))          self.assertEquals(response['Content-Type'], 'text/html') diff --git a/rest_framework/tests/parsers.py b/rest_framework/tests/parsers.py index 8ab8a52f..ffa39b1f 100644 --- a/rest_framework/tests/parsers.py +++ b/rest_framework/tests/parsers.py @@ -131,7 +131,7 @@  #        self.assertEqual(data['key1'], 'val1')  #        self.assertEqual(files['file1'].read(), 'blablabla') -from StringIO import StringIO +from rest_framework.compat import StringIO  from django import forms  from django.test import TestCase  from rest_framework.parsers import FormParser diff --git a/rest_framework/tests/relations_hyperlink.py b/rest_framework/tests/relations_hyperlink.py index 6d137f68..b4ad3166 100644 --- a/rest_framework/tests/relations_hyperlink.py +++ b/rest_framework/tests/relations_hyperlink.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  from django.test import TestCase  from rest_framework import serializers  from rest_framework.compat import patterns, url @@ -74,9 +76,9 @@ class HyperlinkedManyToManyTests(TestCase):          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -                {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/']}, -                {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, -                {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']} +                {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/']}, +                {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, +                {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}          ]          self.assertEquals(serializer.data, expected) @@ -84,14 +86,14 @@ class HyperlinkedManyToManyTests(TestCase):          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']}, -            {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, -            {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']} +            {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']}, +            {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, +            {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']}          ]          self.assertEquals(serializer.data, expected)      def test_many_to_many_update(self): -        data = {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']} +        data = {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}          instance = ManyToManySource.objects.get(pk=1)          serializer = ManyToManySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -102,14 +104,14 @@ class HyperlinkedManyToManyTests(TestCase):          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -                {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}, -                {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, -                {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']} +                {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}, +                {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, +                {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}          ]          self.assertEquals(serializer.data, expected)      def test_reverse_many_to_many_update(self): -        data = {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/']} +        data = {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/']}          instance = ManyToManyTarget.objects.get(pk=1)          serializer = ManyToManyTargetSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -120,48 +122,48 @@ class HyperlinkedManyToManyTests(TestCase):          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/']}, -            {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, -            {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']} +            {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/']}, +            {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, +            {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']}          ]          self.assertEquals(serializer.data, expected)      def test_many_to_many_create(self): -        data = {'url': '/manytomanysource/4/', 'name': u'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']} +        data = {'url': '/manytomanysource/4/', 'name': 'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}          serializer = ManyToManySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is added, and everything else is as expected          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -            {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/']}, -            {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, -            {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}, -            {'url': '/manytomanysource/4/', 'name': u'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']} +            {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/']}, +            {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']}, +            {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}, +            {'url': '/manytomanysource/4/', 'name': 'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}          ]          self.assertEquals(serializer.data, expected)      def test_reverse_many_to_many_create(self): -        data = {'url': '/manytomanytarget/4/', 'name': u'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']} +        data = {'url': '/manytomanytarget/4/', 'name': 'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}          serializer = ManyToManyTargetSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'target-4') +        self.assertEqual(obj.name, 'target-4')          # Ensure target 4 is added, and everything else is as expected          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']}, -            {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, -            {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']}, -            {'url': '/manytomanytarget/4/', 'name': u'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']} +            {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']}, +            {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']}, +            {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']}, +            {'url': '/manytomanytarget/4/', 'name': 'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}          ]          self.assertEquals(serializer.data, expected) @@ -182,9 +184,9 @@ class HyperlinkedForeignKeyTests(TestCase):          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'} +            {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'}          ]          self.assertEquals(serializer.data, expected) @@ -192,13 +194,13 @@ class HyperlinkedForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']}, -            {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []}, +            {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']}, +            {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update(self): -        data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/2/'} +        data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/2/'}          instance = ForeignKeySource.objects.get(pk=1)          serializer = ForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -209,9 +211,9 @@ class HyperlinkedForeignKeyTests(TestCase):          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/2/'}, -            {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'} +            {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/2/'}, +            {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'}          ]          self.assertEquals(serializer.data, expected) @@ -223,7 +225,7 @@ class HyperlinkedForeignKeyTests(TestCase):          self.assertEquals(serializer.errors, {'target': [u'Incorrect type.  Expected url string, received int.']})      def test_reverse_foreign_key_update(self): -        data = {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']} +        data = {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}          instance = ForeignKeyTarget.objects.get(pk=2)          serializer = ForeignKeyTargetSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -232,8 +234,8 @@ class HyperlinkedForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          new_serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']}, -            {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []}, +            {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']}, +            {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},          ]          self.assertEquals(new_serializer.data, expected) @@ -244,54 +246,54 @@ class HyperlinkedForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/2/']}, -            {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}, +            {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/2/']}, +            {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_create(self): -        data = {'url': '/foreignkeysource/4/', 'name': u'source-4', 'target': '/foreignkeytarget/2/'} +        data = {'url': '/foreignkeysource/4/', 'name': 'source-4', 'target': '/foreignkeytarget/2/'}          serializer = ForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 1 is updated, and everything else is as expected          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'}, -            {'url': '/foreignkeysource/4/', 'name': u'source-4', 'target': '/foreignkeytarget/2/'}, +            {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'}, +            {'url': '/foreignkeysource/4/', 'name': 'source-4', 'target': '/foreignkeytarget/2/'},          ]          self.assertEquals(serializer.data, expected)      def test_reverse_foreign_key_create(self): -        data = {'url': '/foreignkeytarget/3/', 'name': u'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']} +        data = {'url': '/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}          serializer = ForeignKeyTargetSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'target-3') +        self.assertEqual(obj.name, 'target-3')          # Ensure target 4 is added, and everything else is as expected          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/2/']}, -            {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []}, -            {'url': '/foreignkeytarget/3/', 'name': u'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}, +            {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/2/']}, +            {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, +            {'url': '/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update_with_invalid_null(self): -        data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': None} +        data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': None}          instance = ForeignKeySource.objects.get(pk=1)          serializer = ForeignKeySourceSerializer(instance, data=data)          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'target': [u'Value may not be null']}) +        self.assertEquals(serializer.errors, {'target': ['Value may not be null']})  class HyperlinkedNullableForeignKeyTests(TestCase): @@ -310,28 +312,28 @@ class HyperlinkedNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None}, +            {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_create_with_valid_null(self): -        data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None} +        data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}          serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None}, -            {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None} +            {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, +            {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}          ]          self.assertEquals(serializer.data, expected) @@ -340,27 +342,27 @@ class HyperlinkedNullableForeignKeyTests(TestCase):          The emptystring should be interpreted as null in the context          of relationships.          """ -        data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': ''} -        expected_data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None} +        data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': ''} +        expected_data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}          serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, expected_data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None}, -            {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None} +            {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, +            {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update_with_valid_null(self): -        data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None} +        data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1)          serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -371,9 +373,9 @@ class HyperlinkedNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None}, -            {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None}, +            {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, +            {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},          ]          self.assertEquals(serializer.data, expected) @@ -382,8 +384,8 @@ class HyperlinkedNullableForeignKeyTests(TestCase):          The emptystring should be interpreted as null in the context          of relationships.          """ -        data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': ''} -        expected_data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None} +        data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': ''} +        expected_data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1)          serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -394,9 +396,9 @@ class HyperlinkedNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None}, -            {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'}, -            {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None}, +            {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, +            {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'}, +            {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},          ]          self.assertEquals(serializer.data, expected) @@ -405,7 +407,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase):      # and cannot be arbitrarily set.      # def test_reverse_foreign_key_update(self): -    #     data = {'id': 1, 'name': u'target-1', 'sources': [1]} +    #     data = {'id': 1, 'name': 'target-1', 'sources': [1]}      #     instance = ForeignKeyTarget.objects.get(pk=1)      #     serializer = ForeignKeyTargetSerializer(instance, data=data)      #     self.assertTrue(serializer.is_valid()) @@ -416,8 +418,8 @@ class HyperlinkedNullableForeignKeyTests(TestCase):      #     queryset = ForeignKeyTarget.objects.all()      #     serializer = ForeignKeyTargetSerializer(queryset)      #     expected = [ -    #         {'id': 1, 'name': u'target-1', 'sources': [1]}, -    #         {'id': 2, 'name': u'target-2', 'sources': []}, +    #         {'id': 1, 'name': 'target-1', 'sources': [1]}, +    #         {'id': 2, 'name': 'target-2', 'sources': []},      #     ]      #     self.assertEquals(serializer.data, expected) diff --git a/rest_framework/tests/relations_nested.py b/rest_framework/tests/relations_nested.py index 0e129fae..e81f0e42 100644 --- a/rest_framework/tests/relations_nested.py +++ b/rest_framework/tests/relations_nested.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals  from django.test import TestCase  from rest_framework import serializers  from rest_framework.tests.models import ForeignKeyTarget, ForeignKeySource, NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource @@ -53,9 +54,9 @@ class ReverseForeignKeyTests(TestCase):          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': {'id': 1, 'name': u'target-1'}}, -            {'id': 2, 'name': u'source-2', 'target': {'id': 1, 'name': u'target-1'}}, -            {'id': 3, 'name': u'source-3', 'target': {'id': 1, 'name': u'target-1'}}, +            {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, +            {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}}, +            {'id': 3, 'name': 'source-3', 'target': {'id': 1, 'name': 'target-1'}},          ]          self.assertEquals(serializer.data, expected) @@ -63,12 +64,12 @@ class ReverseForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [ -                {'id': 1, 'name': u'source-1', 'target': 1}, -                {'id': 2, 'name': u'source-2', 'target': 1}, -                {'id': 3, 'name': u'source-3', 'target': 1}, +            {'id': 1, 'name': 'target-1', 'sources': [ +                {'id': 1, 'name': 'source-1', 'target': 1}, +                {'id': 2, 'name': 'source-2', 'target': 1}, +                {'id': 3, 'name': 'source-3', 'target': 1},              ]}, -            {'id': 2, 'name': u'target-2', 'sources': [ +            {'id': 2, 'name': 'target-2', 'sources': [              ]}          ]          self.assertEquals(serializer.data, expected) @@ -88,9 +89,9 @@ class NestedNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': {'id': 1, 'name': u'target-1'}}, -            {'id': 2, 'name': u'source-2', 'target': {'id': 1, 'name': u'target-1'}}, -            {'id': 3, 'name': u'source-3', 'target': None}, +            {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, +            {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}}, +            {'id': 3, 'name': 'source-3', 'target': None},          ]          self.assertEquals(serializer.data, expected) diff --git a/rest_framework/tests/relations_pk.py b/rest_framework/tests/relations_pk.py index 3391e60a..4d00795a 100644 --- a/rest_framework/tests/relations_pk.py +++ b/rest_framework/tests/relations_pk.py @@ -1,3 +1,6 @@ +from __future__ import unicode_literals + +from django.db import models  from django.test import TestCase  from rest_framework import serializers  from rest_framework.tests.models import ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource @@ -56,9 +59,9 @@ class PKManyToManyTests(TestCase):          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -                {'id': 1, 'name': u'source-1', 'targets': [1]}, -                {'id': 2, 'name': u'source-2', 'targets': [1, 2]}, -                {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]} +                {'id': 1, 'name': 'source-1', 'targets': [1]}, +                {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, +                {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}          ]          self.assertEquals(serializer.data, expected) @@ -66,14 +69,14 @@ class PKManyToManyTests(TestCase):          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, -            {'id': 2, 'name': u'target-2', 'sources': [2, 3]}, -            {'id': 3, 'name': u'target-3', 'sources': [3]} +            {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, +            {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, +            {'id': 3, 'name': 'target-3', 'sources': [3]}          ]          self.assertEquals(serializer.data, expected)      def test_many_to_many_update(self): -        data = {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]} +        data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]}          instance = ManyToManySource.objects.get(pk=1)          serializer = ManyToManySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -84,14 +87,14 @@ class PKManyToManyTests(TestCase):          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -                {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]}, -                {'id': 2, 'name': u'source-2', 'targets': [1, 2]}, -                {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]} +                {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]}, +                {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, +                {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}          ]          self.assertEquals(serializer.data, expected)      def test_reverse_many_to_many_update(self): -        data = {'id': 1, 'name': u'target-1', 'sources': [1]} +        data = {'id': 1, 'name': 'target-1', 'sources': [1]}          instance = ManyToManyTarget.objects.get(pk=1)          serializer = ManyToManyTargetSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -102,47 +105,47 @@ class PKManyToManyTests(TestCase):          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [1]}, -            {'id': 2, 'name': u'target-2', 'sources': [2, 3]}, -            {'id': 3, 'name': u'target-3', 'sources': [3]} +            {'id': 1, 'name': 'target-1', 'sources': [1]}, +            {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, +            {'id': 3, 'name': 'target-3', 'sources': [3]}          ]          self.assertEquals(serializer.data, expected)      def test_many_to_many_create(self): -        data = {'id': 4, 'name': u'source-4', 'targets': [1, 3]} +        data = {'id': 4, 'name': 'source-4', 'targets': [1, 3]}          serializer = ManyToManySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is added, and everything else is as expected          queryset = ManyToManySource.objects.all()          serializer = ManyToManySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'targets': [1]}, -            {'id': 2, 'name': u'source-2', 'targets': [1, 2]}, -            {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]}, -            {'id': 4, 'name': u'source-4', 'targets': [1, 3]}, +            {'id': 1, 'name': 'source-1', 'targets': [1]}, +            {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, +            {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}, +            {'id': 4, 'name': 'source-4', 'targets': [1, 3]},          ]          self.assertEquals(serializer.data, expected)      def test_reverse_many_to_many_create(self): -        data = {'id': 4, 'name': u'target-4', 'sources': [1, 3]} +        data = {'id': 4, 'name': 'target-4', 'sources': [1, 3]}          serializer = ManyToManyTargetSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'target-4') +        self.assertEqual(obj.name, 'target-4')          # Ensure target 4 is added, and everything else is as expected          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, -            {'id': 2, 'name': u'target-2', 'sources': [2, 3]}, -            {'id': 3, 'name': u'target-3', 'sources': [3]}, -            {'id': 4, 'name': u'target-4', 'sources': [1, 3]} +            {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, +            {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, +            {'id': 3, 'name': 'target-3', 'sources': [3]}, +            {'id': 4, 'name': 'target-4', 'sources': [1, 3]}          ]          self.assertEquals(serializer.data, expected) @@ -161,9 +164,9 @@ class PKForeignKeyTests(TestCase):          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 1}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': 1} +            {'id': 1, 'name': 'source-1', 'target': 1}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': 1}          ]          self.assertEquals(serializer.data, expected) @@ -171,13 +174,13 @@ class PKForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, -            {'id': 2, 'name': u'target-2', 'sources': []}, +            {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, +            {'id': 2, 'name': 'target-2', 'sources': []},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update(self): -        data = {'id': 1, 'name': u'source-1', 'target': 2} +        data = {'id': 1, 'name': 'source-1', 'target': 2}          instance = ForeignKeySource.objects.get(pk=1)          serializer = ForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -188,9 +191,9 @@ class PKForeignKeyTests(TestCase):          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 2}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': 1} +            {'id': 1, 'name': 'source-1', 'target': 2}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': 1}          ]          self.assertEquals(serializer.data, expected) @@ -202,7 +205,7 @@ class PKForeignKeyTests(TestCase):          self.assertEquals(serializer.errors, {'target': [u'Incorrect type.  Expected pk value, received str.']})      def test_reverse_foreign_key_update(self): -        data = {'id': 2, 'name': u'target-2', 'sources': [1, 3]} +        data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}          instance = ForeignKeyTarget.objects.get(pk=2)          serializer = ForeignKeyTargetSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -211,8 +214,8 @@ class PKForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          new_serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, -            {'id': 2, 'name': u'target-2', 'sources': []}, +            {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, +            {'id': 2, 'name': 'target-2', 'sources': []},          ]          self.assertEquals(new_serializer.data, expected) @@ -223,54 +226,54 @@ class PKForeignKeyTests(TestCase):          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [2]}, -            {'id': 2, 'name': u'target-2', 'sources': [1, 3]}, +            {'id': 1, 'name': 'target-1', 'sources': [2]}, +            {'id': 2, 'name': 'target-2', 'sources': [1, 3]},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_create(self): -        data = {'id': 4, 'name': u'source-4', 'target': 2} +        data = {'id': 4, 'name': 'source-4', 'target': 2}          serializer = ForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is added, and everything else is as expected          queryset = ForeignKeySource.objects.all()          serializer = ForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 1}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': 1}, -            {'id': 4, 'name': u'source-4', 'target': 2}, +            {'id': 1, 'name': 'source-1', 'target': 1}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': 1}, +            {'id': 4, 'name': 'source-4', 'target': 2},          ]          self.assertEquals(serializer.data, expected)      def test_reverse_foreign_key_create(self): -        data = {'id': 3, 'name': u'target-3', 'sources': [1, 3]} +        data = {'id': 3, 'name': 'target-3', 'sources': [1, 3]}          serializer = ForeignKeyTargetSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'target-3') +        self.assertEqual(obj.name, 'target-3')          # Ensure target 3 is added, and everything else is as expected          queryset = ForeignKeyTarget.objects.all()          serializer = ForeignKeyTargetSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'target-1', 'sources': [2]}, -            {'id': 2, 'name': u'target-2', 'sources': []}, -            {'id': 3, 'name': u'target-3', 'sources': [1, 3]}, +            {'id': 1, 'name': 'target-1', 'sources': [2]}, +            {'id': 2, 'name': 'target-2', 'sources': []}, +            {'id': 3, 'name': 'target-3', 'sources': [1, 3]},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update_with_invalid_null(self): -        data = {'id': 1, 'name': u'source-1', 'target': None} +        data = {'id': 1, 'name': 'source-1', 'target': None}          instance = ForeignKeySource.objects.get(pk=1)          serializer = ForeignKeySourceSerializer(instance, data=data)          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'target': [u'Value may not be null']}) +        self.assertEquals(serializer.errors, {'target': ['Value may not be null']})  class PKNullableForeignKeyTests(TestCase): @@ -287,28 +290,28 @@ class PKNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 1}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': None}, +            {'id': 1, 'name': 'source-1', 'target': 1}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': None},          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_create_with_valid_null(self): -        data = {'id': 4, 'name': u'source-4', 'target': None} +        data = {'id': 4, 'name': 'source-4', 'target': None}          serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 1}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': None}, -            {'id': 4, 'name': u'source-4', 'target': None} +            {'id': 1, 'name': 'source-1', 'target': 1}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': None}, +            {'id': 4, 'name': 'source-4', 'target': None}          ]          self.assertEquals(serializer.data, expected) @@ -317,27 +320,27 @@ class PKNullableForeignKeyTests(TestCase):          The emptystring should be interpreted as null in the context          of relationships.          """ -        data = {'id': 4, 'name': u'source-4', 'target': ''} -        expected_data = {'id': 4, 'name': u'source-4', 'target': None} +        data = {'id': 4, 'name': 'source-4', 'target': ''} +        expected_data = {'id': 4, 'name': 'source-4', 'target': None}          serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, expected_data) -        self.assertEqual(obj.name, u'source-4') +        self.assertEqual(obj.name, 'source-4')          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': 1}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': None}, -            {'id': 4, 'name': u'source-4', 'target': None} +            {'id': 1, 'name': 'source-1', 'target': 1}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': None}, +            {'id': 4, 'name': 'source-4', 'target': None}          ]          self.assertEquals(serializer.data, expected)      def test_foreign_key_update_with_valid_null(self): -        data = {'id': 1, 'name': u'source-1', 'target': None} +        data = {'id': 1, 'name': 'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1)          serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -348,9 +351,9 @@ class PKNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': None}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': None} +            {'id': 1, 'name': 'source-1', 'target': None}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': None}          ]          self.assertEquals(serializer.data, expected) @@ -359,8 +362,8 @@ class PKNullableForeignKeyTests(TestCase):          The emptystring should be interpreted as null in the context          of relationships.          """ -        data = {'id': 1, 'name': u'source-1', 'target': ''} -        expected_data = {'id': 1, 'name': u'source-1', 'target': None} +        data = {'id': 1, 'name': 'source-1', 'target': ''} +        expected_data = {'id': 1, 'name': 'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1)          serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid()) @@ -371,9 +374,9 @@ class PKNullableForeignKeyTests(TestCase):          queryset = NullableForeignKeySource.objects.all()          serializer = NullableForeignKeySourceSerializer(queryset)          expected = [ -            {'id': 1, 'name': u'source-1', 'target': None}, -            {'id': 2, 'name': u'source-2', 'target': 1}, -            {'id': 3, 'name': u'source-3', 'target': None} +            {'id': 1, 'name': 'source-1', 'target': None}, +            {'id': 2, 'name': 'source-2', 'target': 1}, +            {'id': 3, 'name': 'source-3', 'target': None}          ]          self.assertEquals(serializer.data, expected) @@ -382,7 +385,7 @@ class PKNullableForeignKeyTests(TestCase):      # and cannot be arbitrarily set.      # def test_reverse_foreign_key_update(self): -    #     data = {'id': 1, 'name': u'target-1', 'sources': [1]} +    #     data = {'id': 1, 'name': 'target-1', 'sources': [1]}      #     instance = ForeignKeyTarget.objects.get(pk=1)      #     serializer = ForeignKeyTargetSerializer(instance, data=data)      #     self.assertTrue(serializer.is_valid()) @@ -393,8 +396,8 @@ class PKNullableForeignKeyTests(TestCase):      #     queryset = ForeignKeyTarget.objects.all()      #     serializer = ForeignKeyTargetSerializer(queryset)      #     expected = [ -    #         {'id': 1, 'name': u'target-1', 'sources': [1]}, -    #         {'id': 2, 'name': u'target-2', 'sources': []}, +    #         {'id': 1, 'name': 'target-1', 'sources': [1]}, +    #         {'id': 2, 'name': 'target-2', 'sources': []},      #     ]      #     self.assertEquals(serializer.data, expected) diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py index c1b4e624..72405336 100644 --- a/rest_framework/tests/renderers.py +++ b/rest_framework/tests/renderers.py @@ -14,7 +14,8 @@ from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \  from rest_framework.parsers import YAMLParser, XMLParser  from rest_framework.settings import api_settings -from StringIO import StringIO +from rest_framework.compat import StringIO +from rest_framework.compat import six  import datetime  from decimal import Decimal @@ -22,8 +23,8 @@ from decimal import Decimal  DUMMYSTATUS = status.HTTP_200_OK  DUMMYCONTENT = 'dummycontent' -RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x -RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x +RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii') +RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')  expected_results = [ @@ -140,7 +141,7 @@ class RendererEndToEndTests(TestCase):          resp = self.client.head('/')          self.assertEquals(resp.status_code, DUMMYSTATUS)          self.assertEquals(resp['Content-Type'], RendererA.media_type) -        self.assertEquals(resp.content, '') +        self.assertEquals(resp.content, six.b(''))      def test_default_renderer_serializes_content_on_accept_any(self):          """If the Accept header is set to */* the default renderer should serialize the response.""" @@ -267,7 +268,8 @@ class JSONPRendererTests(TestCase):                                 HTTP_ACCEPT='application/javascript')          self.assertEquals(resp.status_code, 200)          self.assertEquals(resp['Content-Type'], 'application/javascript') -        self.assertEquals(resp.content, 'callback(%s);' % _flat_repr) +        self.assertEquals(resp.content, +            ('callback(%s);' % _flat_repr).encode('ascii'))      def test_without_callback_without_json_renderer(self):          """ @@ -277,7 +279,8 @@ class JSONPRendererTests(TestCase):                                 HTTP_ACCEPT='application/javascript')          self.assertEquals(resp.status_code, 200)          self.assertEquals(resp['Content-Type'], 'application/javascript') -        self.assertEquals(resp.content, 'callback(%s);' % _flat_repr) +        self.assertEquals(resp.content, +            ('callback(%s);' % _flat_repr).encode('ascii'))      def test_with_callback(self):          """ @@ -288,7 +291,8 @@ class JSONPRendererTests(TestCase):                                 HTTP_ACCEPT='application/javascript')          self.assertEquals(resp.status_code, 200)          self.assertEquals(resp['Content-Type'], 'application/javascript') -        self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr)) +        self.assertEquals(resp.content, +            ('%s(%s);' % (callback_func, _flat_repr)).encode('ascii'))  if yaml: diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index 4b032405..92b1bfd8 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -20,6 +20,7 @@ from rest_framework.request import Request  from rest_framework.response import Response  from rest_framework.settings import api_settings  from rest_framework.views import APIView +from rest_framework.compat import six  factory = RequestFactory() @@ -79,14 +80,14 @@ class TestContentParsing(TestCase):          data = {'qwerty': 'uiop'}          request = Request(factory.post('/', data))          request.parsers = (FormParser(), MultiPartParser()) -        self.assertEqual(request.DATA.items(), data.items()) +        self.assertEqual(list(request.DATA.items()), list(data.items()))      def test_request_DATA_with_text_content(self):          """          Ensure request.DATA returns content for POST request with          non-form content.          """ -        content = 'qwerty' +        content = six.b('qwerty')          content_type = 'text/plain'          request = Request(factory.post('/', content, content_type=content_type))          request.parsers = (PlainTextParser(),) @@ -99,7 +100,7 @@ class TestContentParsing(TestCase):          data = {'qwerty': 'uiop'}          request = Request(factory.post('/', data))          request.parsers = (FormParser(), MultiPartParser()) -        self.assertEqual(request.POST.items(), data.items()) +        self.assertEqual(list(request.POST.items()), list(data.items()))      def test_standard_behaviour_determines_form_content_PUT(self):          """ @@ -117,14 +118,14 @@ class TestContentParsing(TestCase):              request = Request(factory.put('/', data))          request.parsers = (FormParser(), MultiPartParser()) -        self.assertEqual(request.DATA.items(), data.items()) +        self.assertEqual(list(request.DATA.items()), list(data.items()))      def test_standard_behaviour_determines_non_form_content_PUT(self):          """          Ensure request.DATA returns content for PUT request with          non-form content.          """ -        content = 'qwerty' +        content = six.b('qwerty')          content_type = 'text/plain'          request = Request(factory.put('/', content, content_type=content_type))          request.parsers = (PlainTextParser(), ) diff --git a/rest_framework/tests/response.py b/rest_framework/tests/response.py index 875f4d42..453488d0 100644 --- a/rest_framework/tests/response.py +++ b/rest_framework/tests/response.py @@ -9,6 +9,7 @@ from rest_framework.renderers import (      BrowsableAPIRenderer  )  from rest_framework.settings import api_settings +from rest_framework.compat import six  class MockPickleRenderer(BaseRenderer): @@ -22,8 +23,8 @@ class MockJsonRenderer(BaseRenderer):  DUMMYSTATUS = status.HTTP_200_OK  DUMMYCONTENT = 'dummycontent' -RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x -RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x +RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii') +RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')  class RendererA(BaseRenderer): @@ -92,7 +93,7 @@ class RendererIntegrationTests(TestCase):          resp = self.client.head('/')          self.assertEquals(resp.status_code, DUMMYSTATUS)          self.assertEquals(resp['Content-Type'], RendererA.media_type) -        self.assertEquals(resp.content, '') +        self.assertEquals(resp.content, six.b(''))      def test_default_renderer_serializes_content_on_accept_any(self):          """If the Accept header is set to */* the default renderer should serialize the response.""" diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 48b4f1ab..a00626b5 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  import datetime  import pickle  from django.test import TestCase @@ -200,12 +202,12 @@ class ValidationTests(TestCase):      def test_create(self):          serializer = CommentSerializer(data=self.data)          self.assertEquals(serializer.is_valid(), False) -        self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']}) +        self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})      def test_update(self):          serializer = CommentSerializer(self.comment, data=self.data)          self.assertEquals(serializer.is_valid(), False) -        self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']}) +        self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})      def test_update_missing_field(self):          data = { @@ -214,7 +216,7 @@ class ValidationTests(TestCase):          }          serializer = CommentSerializer(self.comment, data=data)          self.assertEquals(serializer.is_valid(), False) -        self.assertEquals(serializer.errors, {'email': [u'This field is required.']}) +        self.assertEquals(serializer.errors, {'email': ['This field is required.']})      def test_missing_bool_with_default(self):          """Make sure that a boolean value with a 'False' value is not @@ -268,7 +270,7 @@ class ValidationTests(TestCase):          serializer = CommentSerializerWithCrossFieldValidator(data=data)          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'non_field_errors': [u'Email address not in content']}) +        self.assertEquals(serializer.errors, {'non_field_errors': ['Email address not in content']})      def test_null_is_true_fields(self):          """ @@ -284,7 +286,7 @@ class ValidationTests(TestCase):          }          serializer = ActionItemSerializer(data=data)          self.assertEquals(serializer.is_valid(), False) -        self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']}) +        self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})      def test_modelserializer_max_length_exceeded_with_custom_restore(self):          """ @@ -307,7 +309,7 @@ class ValidationTests(TestCase):          }          serializer = ActionItemSerializer(data=data)          self.assertEquals(serializer.is_valid(), False) -        self.assertEquals(serializer.errors, {'info': [u'Ensure this value has at most 12 characters (it has 13).']}) +        self.assertEquals(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']})  class CustomValidationTests(TestCase): @@ -383,7 +385,7 @@ class ModelValidationTests(TestCase):          serializer.save()          second_serializer = AlbumsSerializer(data={'title': 'a'})          self.assertFalse(second_serializer.is_valid()) -        self.assertEqual(second_serializer.errors,  {'title': [u'Album with this Title already exists.']}) +        self.assertEqual(second_serializer.errors,  {'title': ['Album with this Title already exists.']})      def test_foreign_key_with_partial(self):          """ @@ -421,15 +423,15 @@ class RegexValidationTest(TestCase):      def test_create_failed(self):          serializer = BookSerializer(data={'isbn': '1234567890'})          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']}) +        self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})          serializer = BookSerializer(data={'isbn': '12345678901234'})          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']}) +        self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})          serializer = BookSerializer(data={'isbn': 'abcdefghijklm'})          self.assertFalse(serializer.is_valid()) -        self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']}) +        self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})      def test_create_success(self):          serializer = BookSerializer(data={'isbn': '1234567890123'}) @@ -743,11 +745,11 @@ class RelatedTraversalTest(TestCase):          serializer = BlogPostSerializer(instance=post)          expected = { -            'title': u'Test blog post', +            'title': 'Test blog post',              'comments': [{ -                'text': u'I love this blog post', +                'text': 'I love this blog post',                  'post_owner': { -                    "name": u"django", +                    "name": "django",                      "age": None                  }              }] @@ -782,8 +784,8 @@ class SerializerMethodFieldTests(TestCase):          serializer = self.serializer_class(source_data)          expected = { -            'beep': u'hello!', -            'boop': [u'a', u'b', u'c'], +            'beep': 'hello!', +            'boop': ['a', 'b', 'c'],              'boop_count': 3,          } @@ -882,8 +884,8 @@ class DepthTest(TestCase):                  depth = 1          serializer = BlogPostSerializer(instance=post) -        expected = {'id': 1, 'title': u'Test blog post', -                    'writer': {'id': 1, 'name': u'django', 'age': 1}} +        expected = {'id': 1, 'title': 'Test blog post', +                    'writer': {'id': 1, 'name': 'django', 'age': 1}}          self.assertEqual(serializer.data, expected) @@ -902,8 +904,8 @@ class DepthTest(TestCase):                  model = BlogPost          serializer = BlogPostSerializer(instance=post) -        expected = {'id': 1, 'title': u'Test blog post', -                    'writer': {'id': 1, 'name': u'django', 'age': 1}} +        expected = {'id': 1, 'title': 'Test blog post', +                    'writer': {'id': 1, 'name': 'django', 'age': 1}}          self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py index 7cd82656..f2432516 100644 --- a/rest_framework/tests/views.py +++ b/rest_framework/tests/views.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  import copy  from django.test import TestCase  from django.test.client import RequestFactory @@ -49,7 +51,7 @@ class ClassBasedViewIntegrationTests(TestCase):          request = factory.post('/', 'f00bar', content_type='application/json')          response = self.view(request)          expected = { -            'detail': u'JSON parse error - No JSON object could be decoded' +            'detail': 'JSON parse error - No JSON object could be decoded'          }          self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)          self.assertEquals(sanitise_json_error(response.data), expected) @@ -64,7 +66,7 @@ class ClassBasedViewIntegrationTests(TestCase):          request = factory.post('/', form_data)          response = self.view(request)          expected = { -            'detail': u'JSON parse error - No JSON object could be decoded' +            'detail': 'JSON parse error - No JSON object could be decoded'          }          self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)          self.assertEquals(sanitise_json_error(response.data), expected) @@ -78,7 +80,7 @@ class FunctionBasedViewIntegrationTests(TestCase):          request = factory.post('/', 'f00bar', content_type='application/json')          response = self.view(request)          expected = { -            'detail': u'JSON parse error - No JSON object could be decoded' +            'detail': 'JSON parse error - No JSON object could be decoded'          }          self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)          self.assertEquals(sanitise_json_error(response.data), expected) @@ -93,7 +95,7 @@ class FunctionBasedViewIntegrationTests(TestCase):          request = factory.post('/', form_data)          response = self.view(request)          expected = { -            'detail': u'JSON parse error - No JSON object could be decoded' +            'detail': 'JSON parse error - No JSON object could be decoded'          }          self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)          self.assertEquals(sanitise_json_error(response.data), expected) diff --git a/rest_framework/utils/__init__.py b/rest_framework/utils/__init__.py index 84fcb5db..1603f972 100644 --- a/rest_framework/utils/__init__.py +++ b/rest_framework/utils/__init__.py @@ -1,6 +1,7 @@ -from django.utils.encoding import smart_unicode  from django.utils.xmlutils import SimplerXMLGenerator  from rest_framework.compat import StringIO +from rest_framework.compat import six +from rest_framework.compat import smart_text  import re  import xml.etree.ElementTree as ET @@ -70,7 +71,7 @@ class XMLRenderer():                  xml.endElement("list-item")          elif isinstance(data, dict): -            for key, value in data.iteritems(): +            for key, value in six.iteritems(data):                  xml.startElement(key, {})                  self._to_xml(xml, value)                  xml.endElement(key) @@ -80,10 +81,10 @@ class XMLRenderer():              pass          else: -            xml.characters(smart_unicode(data)) +            xml.characters(smart_text(data))      def dict2xml(self, data): -        stream = StringIO.StringIO() +        stream = StringIO()          xml = SimplerXMLGenerator(stream, "utf-8")          xml.startDocument() diff --git a/rest_framework/utils/mediatypes.py b/rest_framework/utils/mediatypes.py index ee7f3a54..3fc59edd 100644 --- a/rest_framework/utils/mediatypes.py +++ b/rest_framework/utils/mediatypes.py @@ -47,7 +47,7 @@ class _MediaType(object):          if media_type_str is None:              media_type_str = ''          self.orig = media_type_str -        self.full_type, self.params = parse_header(media_type_str) +        self.full_type, self.params = parse_header(media_type_str.encode('iso-8859-1'))          self.main_type, sep, self.sub_type = self.full_type.partition('/')      def match(self, other): @@ -1,6 +1,8 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- +#from __future__ import unicode_literals +  from setuptools import setup  import re  import os @@ -45,9 +47,9 @@ version = get_version('rest_framework')  if sys.argv[-1] == 'publish':      os.system("python setup.py sdist upload") -    print "You probably want to also tag the version now:" -    print "  git tag -a %s -m 'version %s'" % (version, version) -    print "  git push --tags" +    print("You probably want to also tag the version now:") +    print("  git tag -a %s -m 'version %s'" % (version, version)) +    print("  git push --tags")      sys.exit() @@ -72,6 +74,7 @@ setup(          'License :: OSI Approved :: BSD License',          'Operating System :: OS Independent',          'Programming Language :: Python', +        'Programming Language :: Python :: 3',          'Topic :: Internet :: WWW/HTTP',      ]  ) | 
