Permalink
Browse files

Allow SimpleLazyObjects to return None without constantly being reeva…

…luated, also proxy ``__nonzero__``, and do some codecleanup as well.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16308 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 632dfa2 commit 60cf3f2f842b6e56132b8880c70acc87bd753c2e @alex alex committed Jun 1, 2011
Showing with 49 additions and 38 deletions.
  1. +24 −34 django/utils/functional.py
  2. +25 −4 tests/regressiontests/utils/simplelazyobject.py
View
@@ -1,3 +1,4 @@
+import operator
from functools import wraps, update_wrapper
@@ -164,6 +165,14 @@ def wrapper(*args, **kwargs):
return lazy(func, *resultclasses)(*args, **kwargs)
return wrapper
+empty = object()
+def new_method_proxy(func):
+ def inner(self, *args):
+ if self._wrapped is empty:
+ self._setup()
+ return func(self._wrapped, *args)
+ return inner
+
class LazyObject(object):
"""
A wrapper for another class that can be used to delay instantiation of the
@@ -173,26 +182,23 @@ class LazyObject(object):
instantiation. If you don't need to do that, use SimpleLazyObject.
"""
def __init__(self):
- self._wrapped = None
+ self._wrapped = empty
- def __getattr__(self, name):
- if self._wrapped is None:
- self._setup()
- return getattr(self._wrapped, name)
+ __getattr__ = new_method_proxy(getattr)
def __setattr__(self, name, value):
if name == "_wrapped":
# Assign to __dict__ to avoid infinite __setattr__ loops.
self.__dict__["_wrapped"] = value
else:
- if self._wrapped is None:
+ if self._wrapped is empty:
self._setup()
setattr(self._wrapped, name, value)
def __delattr__(self, name):
if name == "_wrapped":
raise TypeError("can't delete _wrapped.")
- if self._wrapped is None:
+ if self._wrapped is empty:
self._setup()
delattr(self._wrapped, name)
@@ -204,11 +210,8 @@ def _setup(self):
# introspection support:
__members__ = property(lambda self: self.__dir__())
+ __dir__ = new_method_proxy(dir)
- def __dir__(self):
- if self._wrapped is None:
- self._setup()
- return dir(self._wrapped)
class SimpleLazyObject(LazyObject):
"""
@@ -229,16 +232,14 @@ def __init__(self, func):
self.__dict__['_setupfunc'] = func
super(SimpleLazyObject, self).__init__()
- def __str__(self):
- if self._wrapped is None: self._setup()
- return str(self._wrapped)
+ def _setup(self):
+ self._wrapped = self._setupfunc()
- def __unicode__(self):
- if self._wrapped is None: self._setup()
- return unicode(self._wrapped)
+ __str__ = new_method_proxy(str)
+ __unicode__ = new_method_proxy(unicode)
def __deepcopy__(self, memo):
- if self._wrapped is None:
+ if self._wrapped is empty:
# We have to use SimpleLazyObject, not self.__class__, because the
# latter is proxied.
result = SimpleLazyObject(self._setupfunc)
@@ -250,21 +251,10 @@ def __deepcopy__(self, memo):
# Need to pretend to be the wrapped class, for the sake of objects that care
# about this (especially in equality tests)
- def __get_class(self):
- if self._wrapped is None: self._setup()
- return self._wrapped.__class__
- __class__ = property(__get_class)
-
- def __eq__(self, other):
- if self._wrapped is None: self._setup()
- return self._wrapped == other
-
- def __hash__(self):
- if self._wrapped is None: self._setup()
- return hash(self._wrapped)
-
- def _setup(self):
- self._wrapped = self._setupfunc()
+ __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
+ __eq__ = new_method_proxy(operator.eq)
+ __hash__ = new_method_proxy(hash)
+ __nonzero__ = new_method_proxy(bool)
class lazy_property(property):
@@ -285,4 +275,4 @@ def fset(instance, value, name=fset.__name__):
@wraps(fdel)
def fdel(instance, name=fdel.__name__):
return getattr(instance, name)()
- return property(fget, fset, fdel, doc)
+ return property(fget, fset, fdel, doc)
@@ -1,7 +1,8 @@
import copy
import unittest
-from django.utils.functional import SimpleLazyObject
+from django.utils.functional import SimpleLazyObject, empty
+
class _ComplexObject(object):
def __init__(self, name):
@@ -65,13 +66,33 @@ def test_deepcopy(self):
# First, for an unevaluated SimpleLazyObject
s = SimpleLazyObject(complex_object)
- assert s._wrapped is None
+ self.assertIs(s._wrapped, empty)
s2 = copy.deepcopy(s)
- assert s._wrapped is None # something has gone wrong is s is evaluated
+ # something has gone wrong is s is evaluated
+ self.assertIs(s._wrapped, empty)
self.assertEqual(s2, complex_object())
# Second, for an evaluated SimpleLazyObject
name = s.name # evaluate
- assert s._wrapped is not None
+ self.assertIsNot(s._wrapped, empty)
s3 = copy.deepcopy(s)
self.assertEqual(s3, complex_object())
+
+
+ def test_none(self):
+ i = [0]
+ def f():
+ i[0] += 1
+ return None
+
+ x = SimpleLazyObject(f)
+ self.assertEqual(str(x), "None")
+ self.assertEqual(i, [1])
+ self.assertEqual(str(x), "None")
+ self.assertEqual(i, [1])
+
+ def test_bool(self):
+ x = SimpleLazyObject(lambda: 3)
+ self.assertTrue(x)
+ x = SimpleLazyObject(lambda: 0)
+ self.assertFalse(x)

0 comments on commit 60cf3f2

Please sign in to comment.