Use unicode internally everywhere for 'repr'

This commit is contained in:
Tom Christie 2014-12-15 11:55:17 +00:00
parent a72f812d80
commit 72e08a3e8b
8 changed files with 67 additions and 21 deletions

View File

@ -16,6 +16,23 @@ from django.utils import six
import django 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. # OrderedDict only available in Python 2.7.
# This will always be the case in Django 1.7 and above, as these versions # This will always be the case in Django 1.7 and above, as these versions
# no longer support Python 2.6. # no longer support Python 2.6.

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError as DjangoValidationError 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 import ISO_8601
from rest_framework.compat import ( from rest_framework.compat import (
EmailValidator, MinValueValidator, MaxValueValidator, 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.exceptions import ValidationError
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -113,7 +115,9 @@ class CreateOnlyDefault:
return self.default return self.default
def __repr__(self): 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: class CurrentUserDefault:
@ -124,7 +128,7 @@ class CurrentUserDefault:
return self.user return self.user
def __repr__(self): def __repr__(self):
return '%s()' % self.__class__.__name__ return unicode_to_repr('%s()' % self.__class__.__name__)
class SkipField(Exception): class SkipField(Exception):
@ -453,7 +457,7 @@ class Field(object):
This allows us to create descriptive representations for serializer This allows us to create descriptive representations for serializer
instances that show all the declared fields on the 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... # Boolean types...

View File

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

View File

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

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals
import collections import collections
from rest_framework.compat import OrderedDict from rest_framework.compat import OrderedDict, unicode_to_repr
class ReturnDict(OrderedDict): class ReturnDict(OrderedDict):
@ -47,9 +48,9 @@ class BoundField(object):
return self._field.__class__ return self._field.__class__
def __repr__(self): 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 self.__class__.__name__, self.value, self.errors
) ))
class NestedBoundField(BoundField): 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 object creation, and makes it possible to switch between using the implicit
`ModelSerializer` class and an equivalent explicit `Serializer` class. `ModelSerializer` class and an equivalent explicit `Serializer` class.
""" """
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ 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.exceptions import ValidationError
from rest_framework.utils.representation import smart_repr from rest_framework.utils.representation import smart_repr
@ -59,10 +61,10 @@ class UniqueValidator:
raise ValidationError(self.message) raise ValidationError(self.message)
def __repr__(self): def __repr__(self):
return '<%s(queryset=%s)>' % ( return unicode_to_repr('<%s(queryset=%s)>' % (
self.__class__.__name__, self.__class__.__name__,
smart_repr(self.queryset) smart_repr(self.queryset)
) ))
class UniqueTogetherValidator: class UniqueTogetherValidator:
@ -141,11 +143,11 @@ class UniqueTogetherValidator:
raise ValidationError(self.message.format(field_names=field_names)) raise ValidationError(self.message.format(field_names=field_names))
def __repr__(self): def __repr__(self):
return '<%s(queryset=%s, fields=%s)>' % ( return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % (
self.__class__.__name__, self.__class__.__name__,
smart_repr(self.queryset), smart_repr(self.queryset),
smart_repr(self.fields) smart_repr(self.fields)
) ))
class BaseUniqueForValidator: class BaseUniqueForValidator:
@ -205,12 +207,12 @@ class BaseUniqueForValidator:
raise ValidationError({self.field: message}) raise ValidationError({self.field: message})
def __repr__(self): 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__, self.__class__.__name__,
smart_repr(self.queryset), smart_repr(self.queryset),
smart_repr(self.field), smart_repr(self.field),
smart_repr(self.date_field) smart_repr(self.date_field)
) ))
class UniqueForDateValidator(BaseUniqueForValidator): class UniqueForDateValidator(BaseUniqueForValidator):

View File

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

View File

@ -1,5 +1,7 @@
# coding: utf-8
from __future__ import unicode_literals from __future__ import unicode_literals
from rest_framework import serializers from rest_framework import serializers
from rest_framework.compat import unicode_repr
import pytest 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" "The serializer field might be named incorrectly and not match any attribute or key on the `ExampleObject` instance.\n"
"Original exception text was:" "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.