diff options
| author | Ben Kraft <benjaminjkraft@gmail.com> | 2015-09-11 23:06:25 -0700 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-10-03 13:00:37 -0400 |
| commit | 35355a4ffedb2aeed52d5fe3034380ffc6a438db (patch) | |
| tree | fe5e0987ef7e5cedca3a8aa36496b544f8bf868b /django/utils/functional.py | |
| parent | c055224763e11b29cce0a7c10751354c40dac63e (diff) | |
Fixed #25389 -- Fixed pickling a SimpleLazyObject wrapping a model.
Pickling a `SimpleLazyObject` wrapping a model did not work correctly; in
particular it did not add the `_django_version` attribute added in 42736ac8.
Now it will handle this and other custom `__reduce__` methods correctly.
Diffstat (limited to 'django/utils/functional.py')
| -rw-r--r-- | django/utils/functional.py | 54 |
1 files changed, 30 insertions, 24 deletions
diff --git a/django/utils/functional.py b/django/utils/functional.py index ee0a1953dd..c487cd8517 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -3,7 +3,6 @@ import operator from functools import total_ordering, wraps from django.utils import six -from django.utils.six.moves import copyreg # You can't trivially replace this with `functools.partial` because this binds @@ -247,32 +246,30 @@ class LazyObject(object): raise NotImplementedError('subclasses of LazyObject must provide a _setup() method') # Because we have messed with __class__ below, we confuse pickle as to what - # class we are pickling. It also appears to stop __reduce__ from being - # called. So, we define __getstate__ in a way that cooperates with the way - # that pickle interprets this class. This fails when the wrapped class is - # a builtin, but it is better than nothing. - def __getstate__(self): + # class we are pickling. We're going to have to initialize the wrapped + # object to successfully pickle it, so we might as well just pickle the + # wrapped object since they're supposed to act the same way. + # + # Unfortunately, if we try to simply act like the wrapped object, the ruse + # will break down when pickle gets our id(). Thus we end up with pickle + # thinking, in effect, that we are a distinct object from the wrapped + # object, but with the same __dict__. This can cause problems (see #25389). + # + # So instead, we define our own __reduce__ method and custom unpickler. We + # pickle the wrapped object as the unpickler's argument, so that pickle + # will pickle it normally, and then the unpickler simply returns its + # argument. + def __reduce__(self): if self._wrapped is empty: self._setup() - return self._wrapped.__dict__ - - # Python 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) + return (unpickle_lazyobject, (self._wrapped,)) - 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__()) + # We have to explicitly override __getstate__ so that older versions of + # pickle don't try to pickle the __dict__ (which in the case of a + # SimpleLazyObject may contain a lambda). The value will end up being + # ignored by our __reduce__ and custom unpickler. + def __getstate__(self): + return {} def __deepcopy__(self, memo): if self._wrapped is empty: @@ -311,6 +308,15 @@ class LazyObject(object): __contains__ = new_method_proxy(operator.contains) +def unpickle_lazyobject(wrapped): + """ + Used to unpickle lazy objects. Just return its argument, which will be the + wrapped object. + """ + return wrapped +unpickle_lazyobject.__safe_for_unpickling__ = True + + # Workaround for http://bugs.python.org/issue12370 _super = super |
