Loading django/utils/functional.py +18 −5 Original line number Diff line number Diff line Loading @@ -5,6 +5,11 @@ import sys from django.utils import six try: import copyreg except ImportError: import copy_reg as copyreg # You can't trivially replace this with `functools.partial` because this binds # to classes and returns bound instances, whereas functools.partial (on Loading Loading @@ -323,15 +328,23 @@ class SimpleLazyObject(LazyObject): self._setup() return self._wrapped.__dict__ # Python 3.3 will call __reduce__ when pickling; these methods are needed # to serialize and deserialize correctly. They are not called in earlier # versions of Python. # Python 3.3 will call __reduce__ when pickling; this method is needed # to serialize and deserialize correctly. @classmethod def __newobj__(cls, *args): return cls.__new__(cls, *args) def __reduce__(self): def __reduce_ex__(self, proto): if proto >= 2: # On Py3, since the default protocol is 3, pickle uses the # ``__newobj__`` method (& more efficient opcodes) for writing. return (self.__newobj__, (self.__class__,), self.__getstate__()) else: # On Py2, the default protocol is 0 (for back-compat) & the above # code fails miserably (see regression test). Instead, we return # exactly what's returned if there's no ``__reduce__`` method at # all. return (copyreg._reconstructor, (self.__class__, object, None), self.__getstate__()) # Return a meaningful representation of the lazy object for debugging # without evaluating the wrapped object. Loading tests/utils_tests/test_simplelazyobject.py +22 −0 Original line number Diff line number Diff line Loading @@ -161,3 +161,25 @@ class TestUtilsSimpleLazyObject(TestCase): self.assertNotEqual(lazy1, lazy3) self.assertTrue(lazy1 != lazy3) self.assertFalse(lazy1 != lazy2) def test_pickle_py2_regression(self): from django.contrib.auth.models import User # See ticket #20212 user = User.objects.create_user('johndoe', 'john@example.com', 'pass') x = SimpleLazyObject(lambda: user) # This would fail with "TypeError: can't pickle instancemethod objects", # only on Python 2.X. pickled = pickle.dumps(x) # Try the variant protocol levels. pickled = pickle.dumps(x, 0) pickled = pickle.dumps(x, 1) pickled = pickle.dumps(x, 2) if not six.PY3: import cPickle # This would fail with "TypeError: expected string or Unicode object, NoneType found". pickled = cPickle.dumps(x) Loading
django/utils/functional.py +18 −5 Original line number Diff line number Diff line Loading @@ -5,6 +5,11 @@ import sys from django.utils import six try: import copyreg except ImportError: import copy_reg as copyreg # You can't trivially replace this with `functools.partial` because this binds # to classes and returns bound instances, whereas functools.partial (on Loading Loading @@ -323,15 +328,23 @@ class SimpleLazyObject(LazyObject): self._setup() return self._wrapped.__dict__ # Python 3.3 will call __reduce__ when pickling; these methods are needed # to serialize and deserialize correctly. They are not called in earlier # versions of Python. # Python 3.3 will call __reduce__ when pickling; this method is needed # to serialize and deserialize correctly. @classmethod def __newobj__(cls, *args): return cls.__new__(cls, *args) def __reduce__(self): def __reduce_ex__(self, proto): if proto >= 2: # On Py3, since the default protocol is 3, pickle uses the # ``__newobj__`` method (& more efficient opcodes) for writing. return (self.__newobj__, (self.__class__,), self.__getstate__()) else: # On Py2, the default protocol is 0 (for back-compat) & the above # code fails miserably (see regression test). Instead, we return # exactly what's returned if there's no ``__reduce__`` method at # all. return (copyreg._reconstructor, (self.__class__, object, None), self.__getstate__()) # Return a meaningful representation of the lazy object for debugging # without evaluating the wrapped object. Loading
tests/utils_tests/test_simplelazyobject.py +22 −0 Original line number Diff line number Diff line Loading @@ -161,3 +161,25 @@ class TestUtilsSimpleLazyObject(TestCase): self.assertNotEqual(lazy1, lazy3) self.assertTrue(lazy1 != lazy3) self.assertFalse(lazy1 != lazy2) def test_pickle_py2_regression(self): from django.contrib.auth.models import User # See ticket #20212 user = User.objects.create_user('johndoe', 'john@example.com', 'pass') x = SimpleLazyObject(lambda: user) # This would fail with "TypeError: can't pickle instancemethod objects", # only on Python 2.X. pickled = pickle.dumps(x) # Try the variant protocol levels. pickled = pickle.dumps(x, 0) pickled = pickle.dumps(x, 1) pickled = pickle.dumps(x, 2) if not six.PY3: import cPickle # This would fail with "TypeError: expected string or Unicode object, NoneType found". pickled = cPickle.dumps(x)