diff --git a/.travis.yml b/.travis.yml index 0dc878373..572e483b9 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 fc1691894..76ee4bd68 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 4da2aa625..7f5a75a3d 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 5508f6c05..ef11b85bb 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 998911e12..a66e1d7ce 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 43581ae9b..acaf8a719 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 149d64311..4a2b34a5c 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 dc0a73e66..c4f854eff 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 0a34abaa0..b3ee06902 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 1c28cd176..23e1da87d 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 be78c43ae..0a484c4a1 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 505994e28..4a333fb3e 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 4fb802a7c..3d3bcb3c4 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 5c77c55cd..13d03e62f 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 82fcdfe74..cbafbe0e6 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 1f17e8d28..ba2042cbb 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 446e23c07..0434f9009 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 146ad1e4c..72070a1ad 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 4799a04b5..fd01312aa 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 54096206d..34caa208b 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 8ab8a52fb..ffa39b1f3 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 6d137f68d..b4ad31661 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 0e129fae2..e81f0e423 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 3391e60af..4d00795ae 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 c1b4e624b..724053360 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 4b0324056..92b1bfd8d 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 875f4d422..453488d00 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 48b4f1ab9..a00626b57 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 7cd826565..f24325168 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 84fcb5dbb..1603f9725 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 ee7f3a546..3fc59eddd 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): diff --git a/setup.py b/setup.py index 26d072837..640bac4d9 100755 --- a/setup.py +++ b/setup.py @@ -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', ] )