summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Bistuer <loic.bistuer@sixmedia.com>2013-12-04 14:11:34 +0700
committerLoic Bistuer <loic.bistuer@sixmedia.com>2013-12-07 18:58:37 +0700
commita8f4553aaecc7bc6775e0fd54f8c615c792b3d97 (patch)
tree643206574e90ba194516c4708b80f323f4be5963
parent41ebc4838d2b09e7f3ece8889e21492902b55dc8 (diff)
Fixed #21555 -- Made ValidationError pickable.
Thanks trac username zanuxzan for the report and original patch.
-rw-r--r--django/core/exceptions.py10
-rw-r--r--tests/validation/test_picklable.py41
2 files changed, 50 insertions, 1 deletions
diff --git a/django/core/exceptions.py b/django/core/exceptions.py
index 87b173b9b0..78f6d07cf3 100644
--- a/django/core/exceptions.py
+++ b/django/core/exceptions.py
@@ -4,6 +4,7 @@ Global Django exception and warning classes.
from functools import reduce
import operator
+from django.utils import six
from django.utils.encoding import force_text
@@ -84,10 +85,17 @@ class ValidationError(Exception):
list or dictionary can be an actual `list` or `dict` or an instance
of ValidationError with its `error_list` or `error_dict` attribute set.
"""
+
+ # PY2 can't pickle naive exception: http://bugs.python.org/issue1692335.
+ super(ValidationError, self).__init__(message, code, params)
+
if isinstance(message, ValidationError):
if hasattr(message, 'error_dict'):
message = message.error_dict
- elif not hasattr(message, 'message'):
+ # PY2 has a `message` property which is always there so we can't
+ # duck-type on it. It was introduced in Python 2.5 and already
+ # deprecated in Python 2.6.
+ elif not hasattr(message, 'message' if six.PY3 else 'code'):
message = message.error_list
else:
message, code, params = message.message, message.code, message.params
diff --git a/tests/validation/test_picklable.py b/tests/validation/test_picklable.py
new file mode 100644
index 0000000000..a8d84dba20
--- /dev/null
+++ b/tests/validation/test_picklable.py
@@ -0,0 +1,41 @@
+import pickle
+from unittest import TestCase
+
+from django.core.exceptions import ValidationError
+
+
+class PickableValidationErrorTestCase(TestCase):
+
+ def test_validationerror_is_picklable(self):
+ original = ValidationError('a', code='something')
+ unpickled = pickle.loads(pickle.dumps(original))
+ self.assertIs(unpickled, unpickled.error_list[0])
+ self.assertEqual(original.message, unpickled.message)
+ self.assertEqual(original.code, unpickled.code)
+
+ original = ValidationError('a', code='something')
+ unpickled = pickle.loads(pickle.dumps(ValidationError(original)))
+ self.assertIs(unpickled, unpickled.error_list[0])
+ self.assertEqual(original.message, unpickled.message)
+ self.assertEqual(original.code, unpickled.code)
+
+ original = ValidationError(['a', 'b'])
+ unpickled = pickle.loads(pickle.dumps(original))
+ self.assertEqual(original.error_list[0].message, unpickled.error_list[0].message)
+ self.assertEqual(original.error_list[1].message, unpickled.error_list[1].message)
+
+ original = ValidationError(['a', 'b'])
+ unpickled = pickle.loads(pickle.dumps(ValidationError(original)))
+ self.assertEqual(original.error_list[0].message, unpickled.error_list[0].message)
+ self.assertEqual(original.error_list[1].message, unpickled.error_list[1].message)
+
+ original = ValidationError([ValidationError('a'), ValidationError('b')])
+ unpickled = pickle.loads(pickle.dumps(original))
+ self.assertIs(unpickled.args[0][0], unpickled.error_list[0])
+ self.assertEqual(original.error_list[0].message, unpickled.error_list[0].message)
+ self.assertEqual(original.error_list[1].message, unpickled.error_list[1].message)
+
+ message_dict = {'field1': ['a', 'b'], 'field2': ['c', 'd']}
+ original = ValidationError(message_dict)
+ unpickled = pickle.loads(pickle.dumps(original))
+ self.assertEqual(unpickled.message_dict, message_dict)