From b951c74df2d10a7497152144baf5d105e804162f Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 15 Dec 2014 01:24:12 -0800 Subject: [PATCH 1/5] Made docs consistent --- docs/tutorial/2-requests-and-responses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 49e96d030..c04269695 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -181,7 +181,7 @@ Similarly, we can control the format of the request that we send, using the `Con "id": 4, "title": "", "code": "print 456", - "linenos": true, + "linenos": false, "language": "python", "style": "friendly" } From 488b13e7b8591f667a01c01f3f0aa09749627831 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 15 Dec 2014 09:29:54 +0000 Subject: [PATCH 2/5] Better messaging for 'Field.to_representation'. Closes #2271. --- rest_framework/fields.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 205efd2f8..21a1effde 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -382,13 +382,23 @@ class Field(object): """ Transform the *incoming* primitive data into a native value. """ - raise NotImplementedError('to_internal_value() must be implemented.') + raise NotImplementedError( + '{cls}.to_internal_value() must be implemented.'.format( + cls=self.__class__.__name__ + ) + ) def to_representation(self, value): """ Transform the *outgoing* native value into primitive data. """ - raise NotImplementedError('to_representation() must be implemented.') + raise NotImplementedError( + '{cls}.to_representation() must be implemented.\n' + 'If you are upgrading from REST framework version 2 ' + 'you might want `ReadOnlyField`.'.format( + cls=self.__class__.__name__ + ) + ) def fail(self, key, **kwargs): """ From 56c09cc1f2bdd22d1a5465ad618a86b04a8b1d6a Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 15 Dec 2014 02:41:39 -0800 Subject: [PATCH 3/5] Doc typo --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index f068f0f72..ddcefadbc 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -104,7 +104,7 @@ This permission is suitable if you want your API to only be accessible to regist The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff` is `True` in which case permission will be allowed. -This permission is suitable is you want your API to only be accessible to a subset of trusted administrators. +This permission is suitable if you want your API to only be accessible to a subset of trusted administrators. ## IsAuthenticatedOrReadOnly From 72e08a3e8b6427cb93f0f98b42724e31e5b3d8f9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 15 Dec 2014 11:55:17 +0000 Subject: [PATCH 4/5] Use unicode internally everywhere for 'repr' --- rest_framework/compat.py | 17 +++++++++++++++++ rest_framework/fields.py | 12 ++++++++---- rest_framework/serializers.py | 10 +++++----- rest_framework/utils/representation.py | 5 ++++- rest_framework/utils/serializer_helpers.py | 7 ++++--- rest_framework/validators.py | 14 ++++++++------ tests/test_fields.py | 4 ++-- tests/test_serializer.py | 19 +++++++++++++++++++ 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 71520b928..69fdd7936 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -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. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 205efd2f8..1a4712d84 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -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): @@ -453,7 +457,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... diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5adbca3bd..e9860a2fc 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -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. diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py index 3f17a8b9b..0d829a83b 100644 --- a/rest_framework/utils/representation.py +++ b/rest_framework/utils/representation.py @@ -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,10 +26,11 @@ 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' + print type(value), value if value.startswith("u'") and value.endswith("'"): return value[1:] diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 277cf6492..65a04d06b 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -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): diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 63eb7b225..e3719b8d5 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -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): diff --git a/tests/test_fields.py b/tests/test_fields.py index 3f4e65f24..c20bdd8c2 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -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): diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 56b390956..c17b6d8c5 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -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. From dc66cce16da6793efe4a4a4dcdd18db62c859abb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 15 Dec 2014 12:01:29 +0000 Subject: [PATCH 5/5] Remove erronous print --- rest_framework/utils/representation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py index 0d829a83b..1bfc64c1f 100644 --- a/rest_framework/utils/representation.py +++ b/rest_framework/utils/representation.py @@ -30,7 +30,6 @@ def smart_repr(value): # Representations like u'help text' # should simply be presented as 'help text' - print type(value), value if value.startswith("u'") and value.endswith("'"): return value[1:]