Permalink
Browse files

Fixed #21840 -- Moved dunder methods from SimpleLazyObject to LazyObj…

…ect.

This commit also added tests for LazyObject and refactored
the testsuite of SimpleLazyObject so that it can share
test cases with LazyObject.
  • Loading branch information...
bmispelon committed Jan 29, 2014
1 parent 4b4c704 commit 61917aa08b4ab2bc35f3ffe87b7693bd8b58e205
Showing with 340 additions and 227 deletions.
  1. +62 −54 django/utils/functional.py
  2. +275 −0 tests/utils_tests/test_lazyobject.py
  3. +3 −173 tests/utils_tests/test_simplelazyobject.py
View
@@ -265,9 +265,62 @@ def _setup(self):
"""
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):
+ if self._wrapped is empty:
+ self._setup()
+ return self._wrapped.__dict__
+
+ # 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_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__())
+
+ def __deepcopy__(self, memo):
+ if self._wrapped is empty:
+ # We have to use type(self), not self.__class__, because the
+ # latter is proxied.
+ result = type(self)()
+ memo[id(self)] = result
+ return result
+ return copy.deepcopy(self._wrapped, memo)
+
+ if six.PY3:
+ __bytes__ = new_method_proxy(bytes)
+ __str__ = new_method_proxy(str)
+ __bool__ = new_method_proxy(bool)
+ else:
+ __str__ = new_method_proxy(str)
+ __unicode__ = new_method_proxy(unicode)
+ __nonzero__ = new_method_proxy(bool)
+
# Introspection support
__dir__ = new_method_proxy(dir)
+ # Need to pretend to be the wrapped class, for the sake of objects that
+ # care about this (especially in equality tests)
+ __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
+ __eq__ = new_method_proxy(operator.eq)
+ __ne__ = new_method_proxy(operator.ne)
+ __hash__ = new_method_proxy(hash)
+
# Dictionary methods support
__getitem__ = new_method_proxy(operator.getitem)
__setitem__ = new_method_proxy(operator.setitem)
@@ -303,68 +356,23 @@ def __init__(self, func):
def _setup(self):
self._wrapped = self._setupfunc()
- if six.PY3:
- __bytes__ = new_method_proxy(bytes)
- __str__ = new_method_proxy(str)
- else:
- __str__ = new_method_proxy(str)
- __unicode__ = new_method_proxy(unicode)
-
- def __deepcopy__(self, memo):
- if self._wrapped is empty:
- # We have to use SimpleLazyObject, not self.__class__, because the
- # latter is proxied.
- result = SimpleLazyObject(self._setupfunc)
- memo[id(self)] = result
- return result
- else:
- return copy.deepcopy(self._wrapped, memo)
-
- # 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):
- if self._wrapped is empty:
- self._setup()
- return self._wrapped.__dict__
-
- # 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_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.
def __repr__(self):
if self._wrapped is empty:
repr_attr = self._setupfunc
else:
repr_attr = self._wrapped
- return '<SimpleLazyObject: %r>' % repr_attr
+ return '<%s: %r>' % (type(self).__name__, repr_attr)
- # Need to pretend to be the wrapped class, for the sake of objects that
- # care about this (especially in equality tests)
- __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
- __eq__ = new_method_proxy(operator.eq)
- __ne__ = new_method_proxy(operator.ne)
- __hash__ = new_method_proxy(hash)
- __bool__ = new_method_proxy(bool) # Python 3
- __nonzero__ = __bool__ # Python 2
+ def __deepcopy__(self, memo):
+ if self._wrapped is empty:
+ # We have to use type(self), not self.__class__, because the
+ # latter is proxied.
+ result = SimpleLazyObject(self._setupfunc)
+ memo[id(self)] = result
+ return result
+ return copy.deepcopy(self._wrapped, memo)
class lazy_property(property):
Oops, something went wrong.

0 comments on commit 61917aa

Please sign in to comment.