Refactored RequestNodeMiddleware to use SimpleLazyObject - comparable to django r1629...
[philo.git] / philo / utils / lazycompat.py
1 try:
2         from django.utils.functional import empty, LazyObject, SimpleLazyObject
3 except ImportError:
4         # Supply LazyObject and SimpleLazyObject for django < r16308
5         import operator
6         
7         
8         empty = object()
9         def new_method_proxy(func):
10                 def inner(self, *args):
11                         if self._wrapped is empty:
12                                 self._setup()
13                         return func(self._wrapped, *args)
14                 return inner
15
16         class LazyObject(object):
17                 """
18                 A wrapper for another class that can be used to delay instantiation of the
19                 wrapped class.
20
21                 By subclassing, you have the opportunity to intercept and alter the
22                 instantiation. If you don't need to do that, use SimpleLazyObject.
23                 """
24                 def __init__(self):
25                         self._wrapped = empty
26
27                 __getattr__ = new_method_proxy(getattr)
28
29                 def __setattr__(self, name, value):
30                         if name == "_wrapped":
31                                 # Assign to __dict__ to avoid infinite __setattr__ loops.
32                                 self.__dict__["_wrapped"] = value
33                         else:
34                                 if self._wrapped is empty:
35                                         self._setup()
36                                 setattr(self._wrapped, name, value)
37
38                 def __delattr__(self, name):
39                         if name == "_wrapped":
40                                 raise TypeError("can't delete _wrapped.")
41                         if self._wrapped is empty:
42                                 self._setup()
43                         delattr(self._wrapped, name)
44
45                 def _setup(self):
46                         """
47                         Must be implemented by subclasses to initialise the wrapped object.
48                         """
49                         raise NotImplementedError
50
51                 # introspection support:
52                 __members__ = property(lambda self: self.__dir__())
53                 __dir__ = new_method_proxy(dir)
54
55
56         class SimpleLazyObject(LazyObject):
57                 """
58                 A lazy object initialised from any function.
59
60                 Designed for compound objects of unknown type. For builtins or objects of
61                 known type, use django.utils.functional.lazy.
62                 """
63                 def __init__(self, func):
64                         """
65                         Pass in a callable that returns the object to be wrapped.
66
67                         If copies are made of the resulting SimpleLazyObject, which can happen
68                         in various circumstances within Django, then you must ensure that the
69                         callable can be safely run more than once and will return the same
70                         value.
71                         """
72                         self.__dict__['_setupfunc'] = func
73                         super(SimpleLazyObject, self).__init__()
74
75                 def _setup(self):
76                         self._wrapped = self._setupfunc()
77
78                 __str__ = new_method_proxy(str)
79                 __unicode__ = new_method_proxy(unicode)
80
81                 def __deepcopy__(self, memo):
82                         if self._wrapped is empty:
83                                 # We have to use SimpleLazyObject, not self.__class__, because the
84                                 # latter is proxied.
85                                 result = SimpleLazyObject(self._setupfunc)
86                                 memo[id(self)] = result
87                                 return result
88                         else:
89                                 import copy
90                                 return copy.deepcopy(self._wrapped, memo)
91
92                 # Need to pretend to be the wrapped class, for the sake of objects that care
93                 # about this (especially in equality tests)
94                 __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
95                 __eq__ = new_method_proxy(operator.eq)
96                 __hash__ = new_method_proxy(hash)
97                 __nonzero__ = new_method_proxy(bool)