Skip to content

Commit

Permalink
Fixed the loading of the YAML format to handle Unicode once again.
Browse files Browse the repository at this point in the history
  • Loading branch information
toastdriven committed Nov 23, 2011
1 parent 5c00bec commit 66832f1
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 48 deletions.
30 changes: 29 additions & 1 deletion tastypie/serializers.py
Expand Up @@ -25,6 +25,34 @@
biplist = None


# Ugh & blah.
# So doing a regular dump is generally fine, since Tastypie doesn't usually
# serialize advanced types. *HOWEVER*, it will dump out Python Unicode strings
# as a custom YAML tag, which of course ``yaml.safe_load`` can't handle.
if yaml is not None:
from yaml.constructor import SafeConstructor
from yaml.loader import Reader, Scanner, Parser, Composer, Resolver

class TastypieConstructor(SafeConstructor):
def construct_yaml_unicode_dammit(self, node):
value = self.construct_scalar(node)
try:
return value.encode('ascii')
except UnicodeEncodeError:
return value

TastypieConstructor.add_constructor(u'tag:yaml.org,2002:python/unicode', TastypieConstructor.construct_yaml_unicode_dammit)

class TastypieLoader(Reader, Scanner, Parser, Composer, TastypieConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
TastypieConstructor.__init__(self)
Resolver.__init__(self)


class Serializer(object):
"""
A swappable class for serialization.
Expand Down Expand Up @@ -354,7 +382,7 @@ def from_yaml(self, content):
if yaml is None:
raise ImproperlyConfigured("Usage of the YAML aspects requires yaml.")

return yaml.safe_load(content)
return yaml.load(content, Loader=TastypieLoader)

def to_plist(self, data, options=None):
"""
Expand Down
94 changes: 47 additions & 47 deletions tests/core/tests/serializers.py
Expand Up @@ -19,11 +19,11 @@ class AnotherNoteResource(ModelResource):
aliases = fields.ListField(attribute='aliases', null=True)
meta = fields.DictField(attribute='metadata', null=True)
owed = fields.DecimalField(attribute='money_owed', null=True)

class Meta:
resource_name = 'anothernotes'
queryset = Note.objects.filter(is_active=True)

def dehydrate(self, bundle):
bundle.data['aliases'] = ['Mr. Smith', 'John Doe']
bundle.data['meta'] = {'threat': 'high'}
Expand All @@ -37,22 +37,22 @@ def test_init(self):
self.assertEqual(serializer_1.formats, ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist'])
self.assertEqual(serializer_1.content_types, {'xml': 'application/xml', 'yaml': 'text/yaml', 'json': 'application/json', 'jsonp': 'text/javascript', 'html': 'text/html', 'plist': 'application/x-plist'})
self.assertEqual(serializer_1.supported_formats, ['application/json', 'text/javascript', 'application/xml', 'text/yaml', 'text/html', 'application/x-plist'])

serializer_2 = Serializer(formats=['json', 'xml'])
self.assertEqual(serializer_2.formats, ['json', 'xml'])
self.assertEqual(serializer_2.content_types, {'xml': 'application/xml', 'yaml': 'text/yaml', 'json': 'application/json', 'jsonp': 'text/javascript', 'html': 'text/html', 'plist': 'application/x-plist'})
self.assertEqual(serializer_2.supported_formats, ['application/json', 'application/xml'])

serializer_3 = Serializer(formats=['json', 'xml'], content_types={'json': 'text/json', 'xml': 'application/xml'})
self.assertEqual(serializer_3.formats, ['json', 'xml'])
self.assertEqual(serializer_3.content_types, {'xml': 'application/xml', 'json': 'text/json'})
self.assertEqual(serializer_3.supported_formats, ['text/json', 'application/xml'])

serializer_4 = Serializer(formats=['plist', 'json'], content_types={'plist': 'application/x-plist', 'json': 'application/json'})
self.assertEqual(serializer_4.formats, ['plist', 'json'])
self.assertEqual(serializer_4.content_types, {'plist': 'application/x-plist', 'json': 'application/json'})
self.assertEqual(serializer_4.supported_formats, ['application/x-plist', 'application/json'])

self.assertRaises(ImproperlyConfigured, Serializer, formats=['json', 'xml'], content_types={'json': 'text/json'})

def get_sample1(self):
Expand All @@ -70,97 +70,97 @@ def get_sample2(self):
'true': True,
'false': False,
}

def test_format_datetime(self):
serializer = Serializer()
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), '2010-12-16T02:31:33')

serializer = Serializer(datetime_formatting='iso-8601')
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), '2010-12-16T02:31:33')

serializer = Serializer(datetime_formatting='rfc-2822')
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), u'Thu, 16 Dec 2010 02:31:33 -0600')

serializer = Serializer(datetime_formatting='random-garbage')
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), '2010-12-16T02:31:33')

# Stow.
old_format = getattr(settings, 'TASTYPIE_DATETIME_FORMATTING', 'iso-8601')

settings.TASTYPIE_DATETIME_FORMATTING = 'iso-8601'
serializer = Serializer()
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), '2010-12-16T02:31:33')

settings.TASTYPIE_DATETIME_FORMATTING = 'rfc-2822'
serializer = Serializer()
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), u'Thu, 16 Dec 2010 02:31:33 -0600')

settings.TASTYPIE_DATETIME_FORMATTING = 'random-garbage'
serializer = Serializer()
self.assertEqual(serializer.format_datetime(datetime.datetime(2010, 12, 16, 2, 31, 33)), '2010-12-16T02:31:33')

# Restore.
settings.TASTYPIE_DATETIME_FORMATTING = old_format

def test_format_date(self):
serializer = Serializer()
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), '2010-12-16')

serializer = Serializer(datetime_formatting='iso-8601')
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), '2010-12-16')

serializer = Serializer(datetime_formatting='rfc-2822')
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), u'16 Dec 2010')

serializer = Serializer(datetime_formatting='random-garbage')
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), '2010-12-16')

# Stow.
old_format = getattr(settings, 'TASTYPIE_DATETIME_FORMATTING', 'iso-8601')

settings.TASTYPIE_DATETIME_FORMATTING = 'iso-8601'
serializer = Serializer()
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), '2010-12-16')

settings.TASTYPIE_DATETIME_FORMATTING = 'rfc-2822'
serializer = Serializer()
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), u'16 Dec 2010')

settings.TASTYPIE_DATETIME_FORMATTING = 'random-garbage'
serializer = Serializer()
self.assertEqual(serializer.format_date(datetime.date(2010, 12, 16)), '2010-12-16')

# Restore.
settings.TASTYPIE_DATETIME_FORMATTING = old_format

def test_format_time(self):
serializer = Serializer()
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), '02:31:33')

serializer = Serializer(datetime_formatting='iso-8601')
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), '02:31:33')

serializer = Serializer(datetime_formatting='rfc-2822')
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), u'02:31:33 -0600')

serializer = Serializer(datetime_formatting='random-garbage')
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), '02:31:33')

# Stow.
old_format = getattr(settings, 'TASTYPIE_DATETIME_FORMATTING', 'iso-8601')

settings.TASTYPIE_DATETIME_FORMATTING = 'iso-8601'
serializer = Serializer()
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), '02:31:33')

settings.TASTYPIE_DATETIME_FORMATTING = 'rfc-2822'
serializer = Serializer()
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), u'02:31:33 -0600')

settings.TASTYPIE_DATETIME_FORMATTING = 'random-garbage'
serializer = Serializer()
self.assertEqual(serializer.format_time(datetime.time(2, 31, 33)), '02:31:33')

# Restore.
settings.TASTYPIE_DATETIME_FORMATTING = old_format

Expand All @@ -183,16 +183,16 @@ def test_from_xml2(self):
serializer = Serializer()
data = '<?xml version=\'1.0\' encoding=\'utf-8\'?>\n<request><somelist type="list"><value>hello</value><value type="integer">1</value><value type="null"/></somelist><somehash type="hash"><pi type="float">3.14</pi><foo>bar</foo></somehash><false type="boolean">False</false><true type="boolean">True</true><somestring>hello</somestring></request>'
self.assertEqual(serializer.from_xml(data), self.get_sample2())

def test_to_json(self):
serializer = Serializer()

sample_1 = self.get_sample1()
self.assertEqual(serializer.to_json(sample_1), '{"age": 27, "date_joined": "2010-03-27", "name": "Daniel"}')

def test_from_json(self):
serializer = Serializer()

sample_1 = serializer.from_json('{"age": 27, "date_joined": "2010-03-27", "name": "Daniel"}')
self.assertEqual(len(sample_1), 3)
self.assertEqual(sample_1['name'], 'Daniel')
Expand Down Expand Up @@ -231,13 +231,13 @@ def test_to_jsonp(self):

def test_to_plist(self):
serializer = Serializer()

sample_1 = self.get_sample1()
self.assertEqual(serializer.to_plist(sample_1), 'bplist00bybiplist1.0\x00\xd3\x01\x02\x03\x04\x05\x06SageTname[date_joined\x10\x1bf\x00D\x00a\x00n\x00i\x00e\x00lZ2010-03-27\x15\x1c %13@\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00K')

def test_from_plist(self):
serializer = Serializer()

sample_1 = serializer.from_plist('bplist00bybiplist1.0\x00\xd3\x01\x02\x03\x04\x05\x06SageTname[date_joined\x10\x1bf\x00D\x00a\x00n\x00i\x00e\x00lZ2010-03-27\x15\x1c %13@\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00K')
self.assertEqual(len(sample_1), 3)
self.assertEqual(sample_1['name'], 'Daniel')
Expand Down Expand Up @@ -282,7 +282,7 @@ def test_to_json_single(self):
serializer = Serializer()
resource = self.obj_list[0]
self.assertEqual(serializer.to_json(resource), '{"content": "This is my very first post using my shiny new API. Pretty sweet, huh?", "created": "2010-03-30T20:05:00", "id": "1", "is_active": true, "resource_uri": "", "slug": "first-post", "title": "First Post!", "updated": "2010-03-30T20:05:00"}')

def test_to_json_decimal_list_dict(self):
serializer = Serializer()
resource = self.another_obj_list[0]
Expand All @@ -308,11 +308,11 @@ def __init__(self, *args, **kwargs):
self.from_yaml_called = False
self.from_html_called = False
self.from_jsonp_called = False

def from_json(self, data):
self.from_json_called = True
return True

def from_xml(self, data):
self.from_xml_called = True
return True
Expand All @@ -339,7 +339,7 @@ def test_deserialize_json_with_charset(self):
serializer = StubbedSerializer()
serializer.deserialize('{}', 'application/json; charset=UTF-8')
self.assertTrue(serializer.from_json_called)

def test_deserialize_xml(self):
serializer = StubbedSerializer()
serializer.deserialize('', 'application/xml')
Expand All @@ -359,7 +359,7 @@ def test_deserialize_yaml_with_charset(self):
serializer = StubbedSerializer()
serializer.deserialize('', 'text/yaml; charset=UTF-8')
self.assertTrue(serializer.from_yaml_called)

def test_deserialize_jsonp(self):
serializer = StubbedSerializer()
serializer.deserialize('{}', 'text/javascript')
Expand All @@ -369,7 +369,7 @@ def test_deserialize_jsonp_with_charset(self):
serializer = StubbedSerializer()
serializer.deserialize('{}', 'text/javascript; charset=UTF-8')
self.assertTrue(serializer.from_jsonp_called)

def test_deserialize_html(self):
serializer = StubbedSerializer()
serializer.deserialize('', 'text/html')
Expand All @@ -379,4 +379,4 @@ def test_deserialize_html_with_charset(self):
serializer = StubbedSerializer()
serializer.deserialize('', 'text/html; charset=UTF-8')
self.assertTrue(serializer.from_html_called)

0 comments on commit 66832f1

Please sign in to comment.