[1.6.x] Merge pull request #1582 from rca/12756-missing-yaml-module-serializer-error-message

Fixed #12756: Improved error message when yaml module is missing.

Backport of 4f5faa1916 from master.
This commit is contained in:
Russell Keith-Magee 2013-09-06 16:05:02 -07:00
parent 99952bab30
commit 3df9647ad9
6 changed files with 159 additions and 63 deletions

View File

@ -57,6 +57,7 @@ answer newbie questions, and generally made Django that much better:
Gisle Aas <gisle@aas.no> Gisle Aas <gisle@aas.no>
Chris Adams Chris Adams
Mathieu Agopian <mathieu.agopian@gmail.com> Mathieu Agopian <mathieu.agopian@gmail.com>
Roberto Aguilar <roberto@baremetal.io>
ajs <adi@sieker.info> ajs <adi@sieker.info>
alang@bright-green.com alang@bright-green.com
A S Alam <aalam@users.sf.net> A S Alam <aalam@users.sf.net>

View File

@ -105,11 +105,11 @@ class Command(BaseCommand):
# Check that the serialization format exists; this is a shortcut to # Check that the serialization format exists; this is a shortcut to
# avoid collating all the objects and _then_ failing. # avoid collating all the objects and _then_ failing.
if format not in serializers.get_public_serializer_formats(): if format not in serializers.get_public_serializer_formats():
raise CommandError("Unknown serialization format: %s" % format)
try: try:
serializers.get_serializer(format) serializers.get_serializer(format)
except KeyError: except serializers.SerializerDoesNotExist:
pass
raise CommandError("Unknown serialization format: %s" % format) raise CommandError("Unknown serialization format: %s" % format)
def get_objects(): def get_objects():

View File

@ -26,17 +26,29 @@ BUILTIN_SERIALIZERS = {
"xml" : "django.core.serializers.xml_serializer", "xml" : "django.core.serializers.xml_serializer",
"python" : "django.core.serializers.python", "python" : "django.core.serializers.python",
"json" : "django.core.serializers.json", "json" : "django.core.serializers.json",
"yaml" : "django.core.serializers.pyyaml",
} }
# Check for PyYaml and register the serializer if it's available.
try:
import yaml
BUILTIN_SERIALIZERS["yaml"] = "django.core.serializers.pyyaml"
except ImportError:
pass
_serializers = {} _serializers = {}
class BadSerializer(object):
"""
Stub serializer to hold exception raised during registration
This allows the serializer registration to cache serializers and if there
is an error raised in the process of creating a serializer it will be
raised and passed along to the caller when the serializer is used.
"""
internal_use_only = False
def __init__(self, exception):
self.exception = exception
def __call__(self, *args, **kwargs):
raise self.exception
def register_serializer(format, serializer_module, serializers=None): def register_serializer(format, serializer_module, serializers=None):
"""Register a new serializer. """Register a new serializer.
@ -52,12 +64,23 @@ def register_serializer(format, serializer_module, serializers=None):
""" """
if serializers is None and not _serializers: if serializers is None and not _serializers:
_load_serializers() _load_serializers()
try:
module = importlib.import_module(serializer_module) module = importlib.import_module(serializer_module)
except ImportError, exc:
bad_serializer = BadSerializer(exc)
module = type('BadSerializerModule', (object,), {
'Deserializer': bad_serializer,
'Serializer': bad_serializer,
})
if serializers is None: if serializers is None:
_serializers[format] = module _serializers[format] = module
else: else:
serializers[format] = module serializers[format] = module
def unregister_serializer(format): def unregister_serializer(format):
"Unregister a given serializer. This is not a thread-safe operation." "Unregister a given serializer. This is not a thread-safe operation."
if not _serializers: if not _serializers:

View File

@ -1,17 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
# -*- coding: utf-8 -*-
import json import json
from datetime import datetime from datetime import datetime
from xml.dom import minidom from xml.dom import minidom
try:
import yaml
HAS_YAML = True
except ImportError:
HAS_YAML = False
from django.conf import settings from django.conf import settings
from django.core import serializers from django.core import management, serializers
from django.db import transaction, connection from django.db import transaction, connection
from django.test import TestCase, TransactionTestCase, Approximate from django.test import TestCase, TransactionTestCase, Approximate
from django.utils import six from django.utils import six
from django.utils.six import StringIO from django.utils.six import StringIO
from django.utils import unittest from django.utils import unittest
from django.utils import importlib
from .models import (Category, Author, Article, AuthorProfile, Actor, Movie, from .models import (Category, Author, Article, AuthorProfile, Actor, Movie,
Score, Player, Team) Score, Player, Team)
@ -420,12 +427,72 @@ class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, Transact
} }
}]""" }]"""
try:
import yaml YAML_IMPORT_ERROR_MESSAGE = r'No module named yaml'
except ImportError: class YamlImportModuleMock(object):
pass """Provides a wrapped import_module function to simulate yaml ImportError
else:
class YamlSerializerTestCase(SerializersTestBase, TestCase): In order to run tests that verify the behavior of the YAML serializer
when run on a system that has yaml installed (like the django CI server),
mock import_module, so that it raises an ImportError when the yaml
serializer is being imported. The importlib.import_module() call is
being made in the serializers.register_serializer().
Refs: #12756
"""
def __init__(self):
self._import_module = importlib.import_module
def import_module(self, module_path):
if module_path == serializers.BUILTIN_SERIALIZERS['yaml']:
raise ImportError(YAML_IMPORT_ERROR_MESSAGE)
return self._import_module(module_path)
class NoYamlSerializerTestCase(TestCase):
"""Not having pyyaml installed provides a misleading error
Refs: #12756
"""
@classmethod
def setUpClass(cls):
"""Removes imported yaml and stubs importlib.import_module"""
super(NoYamlSerializerTestCase, cls).setUpClass()
cls._import_module_mock = YamlImportModuleMock()
importlib.import_module = cls._import_module_mock.import_module
# clear out cached serializers to emulate yaml missing
serializers._serializers = {}
@classmethod
def tearDownClass(cls):
"""Puts yaml back if necessary"""
super(NoYamlSerializerTestCase, cls).tearDownClass()
importlib.import_module = cls._import_module_mock._import_module
# clear out cached serializers to clean out BadSerializer instances
serializers._serializers = {}
def test_serializer_pyyaml_error_message(self):
"""Using yaml serializer without pyyaml raises ImportError"""
jane = Author(name="Jane")
self.assertRaises(ImportError, serializers.serialize, "yaml", [jane])
def test_deserializer_pyyaml_error_message(self):
"""Using yaml deserializer without pyyaml raises ImportError"""
self.assertRaises(ImportError, serializers.deserialize, "yaml", "")
def test_dumpdata_pyyaml_error_message(self):
"""Calling dumpdata produces an error when yaml package missing"""
self.assertRaisesRegexp(management.CommandError, YAML_IMPORT_ERROR_MESSAGE,
management.call_command, 'dumpdata', format='yaml')
@unittest.skipUnless(HAS_YAML, "No yaml library detected")
class YamlSerializerTestCase(SerializersTestBase, TestCase):
serializer_name = "yaml" serializer_name = "yaml"
fwd_ref_str = """- fields: fwd_ref_str = """- fields:
headline: Forward references pose no problem headline: Forward references pose no problem
@ -484,7 +551,9 @@ else:
ret_list.append(str(field_value)) ret_list.append(str(field_value))
return ret_list return ret_list
class YamlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase):
@unittest.skipUnless(HAS_YAML, "No yaml library detected")
class YamlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase):
serializer_name = "yaml" serializer_name = "yaml"
fwd_ref_str = """- fields: fwd_ref_str = """- fields:
headline: Forward references pose no problem headline: Forward references pose no problem

View File

@ -523,7 +523,10 @@ def streamTest(format, self):
else: else:
self.assertEqual(string_data, stream.content.decode('utf-8')) self.assertEqual(string_data, stream.content.decode('utf-8'))
for format in serializers.get_serializer_formats(): for format in [
f for f in serializers.get_serializer_formats()
if not isinstance(serializers.get_serializer(f), serializers.BadSerializer)
]:
setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format)) setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format))
setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))

View File

@ -599,7 +599,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30") self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object
@ -623,7 +623,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30.405060") self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30.405060")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object
@ -647,7 +647,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30.405060+07:00") self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30.405060+07:00")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object
@ -671,7 +671,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 10:20:30+00:00") self.assert_yaml_contains_datetime(data, "2011-09-01 10:20:30+00:00")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object
@ -695,7 +695,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30+03:00") self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30+03:00")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object
@ -719,7 +719,7 @@ class SerializationTests(TestCase):
obj = next(serializers.deserialize('xml', data)).object obj = next(serializers.deserialize('xml', data)).object
self.assertEqual(obj.dt, dt) self.assertEqual(obj.dt, dt)
if 'yaml' in serializers.get_serializer_formats(): if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
data = serializers.serialize('yaml', [Event(dt=dt)]) data = serializers.serialize('yaml', [Event(dt=dt)])
self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30+07:00") self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30+07:00")
obj = next(serializers.deserialize('yaml', data)).object obj = next(serializers.deserialize('yaml', data)).object