Merge pull request #2279 from tomchristie/fix-serializer-repr-unicode-bug

Use unicode internally everywhere for 'repr'.
This commit is contained in:
Tom Christie 2014-12-15 12:04:46 +00:00
commit af53e34dd5
8 changed files with 66 additions and 21 deletions

View File

@ -16,6 +16,23 @@ from django.utils import six
import django
def unicode_repr(instance):
# Get the repr of an instance, but ensure it is a unicode string
# on both python 3 (already the case) and 2 (not the case).
if six.PY2:
repr(instance).decode('utf-8')
return repr(instance)
def unicode_to_repr(value):
# Coerce a unicode string to the correct repr return type, depending on
# the Python version. We wrap all our `__repr__` implementations with
# this and then use unicode throughout internally.
if six.PY2:
return value.encode('utf-8')
return value
# OrderedDict only available in Python 2.7.
# This will always be the case in Django 1.7 and above, as these versions
# no longer support Python 2.6.

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError as DjangoValidationError
@ -10,7 +11,8 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import ISO_8601
from rest_framework.compat import (
EmailValidator, MinValueValidator, MaxValueValidator,
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict,
unicode_repr, unicode_to_repr
)
from rest_framework.exceptions import ValidationError
from rest_framework.settings import api_settings
@ -113,7 +115,9 @@ class CreateOnlyDefault:
return self.default
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, repr(self.default))
return unicode_to_repr(
'%s(%s)' % (self.__class__.__name__, unicode_repr(self.default))
)
class CurrentUserDefault:
@ -124,7 +128,7 @@ class CurrentUserDefault:
return self.user
def __repr__(self):
return '%s()' % self.__class__.__name__
return unicode_to_repr('%s()' % self.__class__.__name__)
class SkipField(Exception):
@ -463,7 +467,7 @@ class Field(object):
This allows us to create descriptive representations for serializer
instances that show all the declared fields on the serializer.
"""
return representation.field_repr(self)
return unicode_to_repr(representation.field_repr(self))
# Boolean types...

View File

@ -10,12 +10,11 @@ python primitives.
2. The process of marshalling between python primitives and request and
response content is handled by parsers and renderers.
"""
import warnings
from __future__ import unicode_literals
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import unicode_to_repr
from rest_framework.utils import model_meta
from rest_framework.utils.field_mapping import (
get_url_kwargs, get_field_kwargs,
@ -29,6 +28,7 @@ from rest_framework.validators import (
UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator,
UniqueTogetherValidator
)
import warnings
# Note: We do the following so that users of the framework can use this style:
@ -396,7 +396,7 @@ class Serializer(BaseSerializer):
return attrs
def __repr__(self):
return representation.serializer_repr(self, indent=1)
return unicode_to_repr(representation.serializer_repr(self, indent=1))
# The following are used for accessing `BoundField` instances on the
# serializer, for the purposes of presenting a form-like API onto the
@ -564,7 +564,7 @@ class ListSerializer(BaseSerializer):
return self.instance
def __repr__(self):
return representation.list_repr(self, indent=1)
return unicode_to_repr(representation.list_repr(self, indent=1))
# Include a backlink to the serializer class on return objects.
# Allows renderers such as HTMLFormRenderer to get the full field info.

View File

@ -2,9 +2,11 @@
Helper functions for creating user-friendly representations
of serializer classes and serializer fields.
"""
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import force_text
from django.utils.functional import Promise
from rest_framework.compat import unicode_repr
import re
@ -24,7 +26,7 @@ def smart_repr(value):
if isinstance(value, Promise) and value._delegate_text:
value = force_text(value)
value = repr(value)
value = unicode_repr(value)
# Representations like u'help text'
# should simply be presented as 'help text'

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals
import collections
from rest_framework.compat import OrderedDict
from rest_framework.compat import OrderedDict, unicode_to_repr
class ReturnDict(OrderedDict):
@ -47,9 +48,9 @@ class BoundField(object):
return self._field.__class__
def __repr__(self):
return '<%s value=%s errors=%s>' % (
return unicode_to_repr('<%s value=%s errors=%s>' % (
self.__class__.__name__, self.value, self.errors
)
))
class NestedBoundField(BoundField):

View File

@ -6,7 +6,9 @@ This gives us better separation of concerns, allows us to use single-step
object creation, and makes it possible to switch between using the implicit
`ModelSerializer` class and an equivalent explicit `Serializer` class.
"""
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import unicode_to_repr
from rest_framework.exceptions import ValidationError
from rest_framework.utils.representation import smart_repr
@ -59,10 +61,10 @@ class UniqueValidator:
raise ValidationError(self.message)
def __repr__(self):
return '<%s(queryset=%s)>' % (
return unicode_to_repr('<%s(queryset=%s)>' % (
self.__class__.__name__,
smart_repr(self.queryset)
)
))
class UniqueTogetherValidator:
@ -141,11 +143,11 @@ class UniqueTogetherValidator:
raise ValidationError(self.message.format(field_names=field_names))
def __repr__(self):
return '<%s(queryset=%s, fields=%s)>' % (
return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % (
self.__class__.__name__,
smart_repr(self.queryset),
smart_repr(self.fields)
)
))
class BaseUniqueForValidator:
@ -205,12 +207,12 @@ class BaseUniqueForValidator:
raise ValidationError({self.field: message})
def __repr__(self):
return '<%s(queryset=%s, field=%s, date_field=%s)>' % (
return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % (
self.__class__.__name__,
smart_repr(self.queryset),
smart_repr(self.field),
smart_repr(self.date_field)
)
))
class UniqueForDateValidator(BaseUniqueForValidator):

View File

@ -62,7 +62,7 @@ class TestEmpty:
"""
field = serializers.CharField(allow_blank=True)
output = field.run_validation('')
assert output is ''
assert output == ''
def test_default(self):
"""
@ -817,7 +817,7 @@ class TestChoiceField(FieldValues):
]
)
output = field.run_validation('')
assert output is ''
assert output == ''
class TestChoiceFieldWithType(FieldValues):

View File

@ -1,5 +1,7 @@
# coding: utf-8
from __future__ import unicode_literals
from rest_framework import serializers
from rest_framework.compat import unicode_repr
import pytest
@ -197,3 +199,20 @@ class TestIncorrectlyConfigured:
"The serializer field might be named incorrectly and not match any attribute or key on the `ExampleObject` instance.\n"
"Original exception text was:"
)
class TestUnicodeRepr:
def test_unicode_repr(self):
class ExampleSerializer(serializers.Serializer):
example = serializers.CharField()
class ExampleObject:
def __init__(self):
self.example = '한국'
def __repr__(self):
return unicode_repr(self.example)
instance = ExampleObject()
serializer = ExampleSerializer(instance)
repr(serializer) # Should not error.