Skip to content

Commit af53e34

Browse files
committed
Merge pull request #2279 from tomchristie/fix-serializer-repr-unicode-bug
Use unicode internally everywhere for 'repr'.
2 parents 1f6fd92 + dc66cce commit af53e34

File tree

8 files changed

+66
-21
lines changed

8 files changed

+66
-21
lines changed

rest_framework/compat.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@
1616
import django
1717

1818

19+
def unicode_repr(instance):
20+
# Get the repr of an instance, but ensure it is a unicode string
21+
# on both python 3 (already the case) and 2 (not the case).
22+
if six.PY2:
23+
repr(instance).decode('utf-8')
24+
return repr(instance)
25+
26+
27+
def unicode_to_repr(value):
28+
# Coerce a unicode string to the correct repr return type, depending on
29+
# the Python version. We wrap all our `__repr__` implementations with
30+
# this and then use unicode throughout internally.
31+
if six.PY2:
32+
return value.encode('utf-8')
33+
return value
34+
35+
1936
# OrderedDict only available in Python 2.7.
2037
# This will always be the case in Django 1.7 and above, as these versions
2138
# no longer support Python 2.6.

rest_framework/fields.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import unicode_literals
12
from django.conf import settings
23
from django.core.exceptions import ObjectDoesNotExist
34
from django.core.exceptions import ValidationError as DjangoValidationError
@@ -10,7 +11,8 @@
1011
from rest_framework import ISO_8601
1112
from rest_framework.compat import (
1213
EmailValidator, MinValueValidator, MaxValueValidator,
13-
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict
14+
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict,
15+
unicode_repr, unicode_to_repr
1416
)
1517
from rest_framework.exceptions import ValidationError
1618
from rest_framework.settings import api_settings
@@ -113,7 +115,9 @@ def __call__(self):
113115
return self.default
114116

115117
def __repr__(self):
116-
return '%s(%s)' % (self.__class__.__name__, repr(self.default))
118+
return unicode_to_repr(
119+
'%s(%s)' % (self.__class__.__name__, unicode_repr(self.default))
120+
)
117121

118122

119123
class CurrentUserDefault:
@@ -124,7 +128,7 @@ def __call__(self):
124128
return self.user
125129

126130
def __repr__(self):
127-
return '%s()' % self.__class__.__name__
131+
return unicode_to_repr('%s()' % self.__class__.__name__)
128132

129133

130134
class SkipField(Exception):
@@ -463,7 +467,7 @@ def __repr__(self):
463467
This allows us to create descriptive representations for serializer
464468
instances that show all the declared fields on the serializer.
465469
"""
466-
return representation.field_repr(self)
470+
return unicode_to_repr(representation.field_repr(self))
467471

468472

469473
# Boolean types...

rest_framework/serializers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
2. The process of marshalling between python primitives and request and
1111
response content is handled by parsers and renderers.
1212
"""
13-
import warnings
14-
13+
from __future__ import unicode_literals
1514
from django.db import models
1615
from django.db.models.fields import FieldDoesNotExist
1716
from django.utils.translation import ugettext_lazy as _
18-
17+
from rest_framework.compat import unicode_to_repr
1918
from rest_framework.utils import model_meta
2019
from rest_framework.utils.field_mapping import (
2120
get_url_kwargs, get_field_kwargs,
@@ -29,6 +28,7 @@
2928
UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator,
3029
UniqueTogetherValidator
3130
)
31+
import warnings
3232

3333

3434
# Note: We do the following so that users of the framework can use this style:
@@ -396,7 +396,7 @@ def validate(self, attrs):
396396
return attrs
397397

398398
def __repr__(self):
399-
return representation.serializer_repr(self, indent=1)
399+
return unicode_to_repr(representation.serializer_repr(self, indent=1))
400400

401401
# The following are used for accessing `BoundField` instances on the
402402
# serializer, for the purposes of presenting a form-like API onto the
@@ -564,7 +564,7 @@ def save(self, **kwargs):
564564
return self.instance
565565

566566
def __repr__(self):
567-
return representation.list_repr(self, indent=1)
567+
return unicode_to_repr(representation.list_repr(self, indent=1))
568568

569569
# Include a backlink to the serializer class on return objects.
570570
# Allows renderers such as HTMLFormRenderer to get the full field info.

rest_framework/utils/representation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
Helper functions for creating user-friendly representations
33
of serializer classes and serializer fields.
44
"""
5+
from __future__ import unicode_literals
56
from django.db import models
67
from django.utils.encoding import force_text
78
from django.utils.functional import Promise
9+
from rest_framework.compat import unicode_repr
810
import re
911

1012

@@ -24,7 +26,7 @@ def smart_repr(value):
2426
if isinstance(value, Promise) and value._delegate_text:
2527
value = force_text(value)
2628

27-
value = repr(value)
29+
value = unicode_repr(value)
2830

2931
# Representations like u'help text'
3032
# should simply be presented as 'help text'

rest_framework/utils/serializer_helpers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
from __future__ import unicode_literals
12
import collections
2-
from rest_framework.compat import OrderedDict
3+
from rest_framework.compat import OrderedDict, unicode_to_repr
34

45

56
class ReturnDict(OrderedDict):
@@ -47,9 +48,9 @@ def _proxy_class(self):
4748
return self._field.__class__
4849

4950
def __repr__(self):
50-
return '<%s value=%s errors=%s>' % (
51+
return unicode_to_repr('<%s value=%s errors=%s>' % (
5152
self.__class__.__name__, self.value, self.errors
52-
)
53+
))
5354

5455

5556
class NestedBoundField(BoundField):

rest_framework/validators.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
object creation, and makes it possible to switch between using the implicit
77
`ModelSerializer` class and an equivalent explicit `Serializer` class.
88
"""
9+
from __future__ import unicode_literals
910
from django.utils.translation import ugettext_lazy as _
11+
from rest_framework.compat import unicode_to_repr
1012
from rest_framework.exceptions import ValidationError
1113
from rest_framework.utils.representation import smart_repr
1214

@@ -59,10 +61,10 @@ def __call__(self, value):
5961
raise ValidationError(self.message)
6062

6163
def __repr__(self):
62-
return '<%s(queryset=%s)>' % (
64+
return unicode_to_repr('<%s(queryset=%s)>' % (
6365
self.__class__.__name__,
6466
smart_repr(self.queryset)
65-
)
67+
))
6668

6769

6870
class UniqueTogetherValidator:
@@ -141,11 +143,11 @@ def __call__(self, attrs):
141143
raise ValidationError(self.message.format(field_names=field_names))
142144

143145
def __repr__(self):
144-
return '<%s(queryset=%s, fields=%s)>' % (
146+
return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % (
145147
self.__class__.__name__,
146148
smart_repr(self.queryset),
147149
smart_repr(self.fields)
148-
)
150+
))
149151

150152

151153
class BaseUniqueForValidator:
@@ -205,12 +207,12 @@ def __call__(self, attrs):
205207
raise ValidationError({self.field: message})
206208

207209
def __repr__(self):
208-
return '<%s(queryset=%s, field=%s, date_field=%s)>' % (
210+
return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % (
209211
self.__class__.__name__,
210212
smart_repr(self.queryset),
211213
smart_repr(self.field),
212214
smart_repr(self.date_field)
213-
)
215+
))
214216

215217

216218
class UniqueForDateValidator(BaseUniqueForValidator):

tests/test_fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_allow_blank(self):
6262
"""
6363
field = serializers.CharField(allow_blank=True)
6464
output = field.run_validation('')
65-
assert output is ''
65+
assert output == ''
6666

6767
def test_default(self):
6868
"""
@@ -817,7 +817,7 @@ def test_allow_blank(self):
817817
]
818818
)
819819
output = field.run_validation('')
820-
assert output is ''
820+
assert output == ''
821821

822822

823823
class TestChoiceFieldWithType(FieldValues):

tests/test_serializer.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
# coding: utf-8
12
from __future__ import unicode_literals
23
from rest_framework import serializers
4+
from rest_framework.compat import unicode_repr
35
import pytest
46

57

@@ -197,3 +199,20 @@ def __init__(self):
197199
"The serializer field might be named incorrectly and not match any attribute or key on the `ExampleObject` instance.\n"
198200
"Original exception text was:"
199201
)
202+
203+
204+
class TestUnicodeRepr:
205+
def test_unicode_repr(self):
206+
class ExampleSerializer(serializers.Serializer):
207+
example = serializers.CharField()
208+
209+
class ExampleObject:
210+
def __init__(self):
211+
self.example = '한국'
212+
213+
def __repr__(self):
214+
return unicode_repr(self.example)
215+
216+
instance = ExampleObject()
217+
serializer = ExampleSerializer(instance)
218+
repr(serializer) # Should not error.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy