58 ### Begin from Python 2.5 functools.py ######################################## |
58 ### Begin from Python 2.5 functools.py ######################################## |
59 |
59 |
60 # Summary of changes made to the Python 2.5 code below: |
60 # Summary of changes made to the Python 2.5 code below: |
61 # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility |
61 # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility |
62 # in Django. |
62 # in Django. |
63 # * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except |
|
64 # block to make it compatible with Python 2.3, which doesn't allow |
|
65 # assigning to ``__name__``. |
|
66 |
63 |
67 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation. |
64 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation. |
68 # All Rights Reserved. |
65 # All Rights Reserved. |
69 |
66 |
70 ############################################################################### |
67 ############################################################################### |
88 updated is a tuple naming the attributes off the wrapper that |
85 updated is a tuple naming the attributes off the wrapper that |
89 are updated with the corresponding attribute from the wrapped |
86 are updated with the corresponding attribute from the wrapped |
90 function (defaults to functools.WRAPPER_UPDATES) |
87 function (defaults to functools.WRAPPER_UPDATES) |
91 """ |
88 """ |
92 for attr in assigned: |
89 for attr in assigned: |
93 try: |
90 setattr(wrapper, attr, getattr(wrapped, attr)) |
94 setattr(wrapper, attr, getattr(wrapped, attr)) |
|
95 except TypeError: # Python 2.3 doesn't allow assigning to __name__. |
|
96 pass |
|
97 for attr in updated: |
91 for attr in updated: |
98 getattr(wrapper, attr).update(getattr(wrapped, attr)) |
92 getattr(wrapper, attr).update(getattr(wrapped, attr)) |
99 # Return the wrapper so this can be used as a decorator via curry() |
93 # Return the wrapper so this can be used as a decorator via curry() |
100 return wrapper |
94 return wrapper |
101 |
95 |
145 Turns any callable into a lazy evaluated callable. You need to give result |
139 Turns any callable into a lazy evaluated callable. You need to give result |
146 classes or types -- at least one is needed so that the automatic forcing of |
140 classes or types -- at least one is needed so that the automatic forcing of |
147 the lazy evaluation code is triggered. Results are not memoized; the |
141 the lazy evaluation code is triggered. Results are not memoized; the |
148 function is evaluated on every access. |
142 function is evaluated on every access. |
149 """ |
143 """ |
|
144 |
150 class __proxy__(Promise): |
145 class __proxy__(Promise): |
151 """ |
146 """ |
152 Encapsulate a function call and act as a proxy for methods that are |
147 Encapsulate a function call and act as a proxy for methods that are |
153 called on the result of that function. The function is not evaluated |
148 called on the result of that function. The function is not evaluated |
154 until one of the methods on the result is called. |
149 until one of the methods on the result is called. |
160 self.__args = args |
155 self.__args = args |
161 self.__kw = kw |
156 self.__kw = kw |
162 if self.__dispatch is None: |
157 if self.__dispatch is None: |
163 self.__prepare_class__() |
158 self.__prepare_class__() |
164 |
159 |
|
160 def __reduce__(self): |
|
161 return ( |
|
162 _lazy_proxy_unpickle, |
|
163 (self.__func, self.__args, self.__kw) + resultclasses |
|
164 ) |
|
165 |
165 def __prepare_class__(cls): |
166 def __prepare_class__(cls): |
166 cls.__dispatch = {} |
167 cls.__dispatch = {} |
167 for resultclass in resultclasses: |
168 for resultclass in resultclasses: |
168 cls.__dispatch[resultclass] = {} |
169 cls.__dispatch[resultclass] = {} |
169 for (k, v) in resultclass.__dict__.items(): |
170 for (k, v) in resultclass.__dict__.items(): |
|
171 # All __promise__ return the same wrapper method, but they |
|
172 # also do setup, inserting the method into the dispatch |
|
173 # dict. |
|
174 meth = cls.__promise__(resultclass, k, v) |
170 if hasattr(cls, k): |
175 if hasattr(cls, k): |
171 continue |
176 continue |
172 setattr(cls, k, cls.__promise__(resultclass, k, v)) |
177 setattr(cls, k, meth) |
173 cls._delegate_str = str in resultclasses |
178 cls._delegate_str = str in resultclasses |
174 cls._delegate_unicode = unicode in resultclasses |
179 cls._delegate_unicode = unicode in resultclasses |
175 assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types." |
180 assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types." |
176 if cls._delegate_unicode: |
181 if cls._delegate_unicode: |
177 cls.__unicode__ = cls.__unicode_cast |
182 cls.__unicode__ = cls.__unicode_cast |
234 # Creates the proxy object, instead of the actual value. |
239 # Creates the proxy object, instead of the actual value. |
235 return __proxy__(args, kw) |
240 return __proxy__(args, kw) |
236 |
241 |
237 return wraps(func)(__wrapper__) |
242 return wraps(func)(__wrapper__) |
238 |
243 |
|
244 def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses): |
|
245 return lazy(func, *resultclasses)(*args, **kwargs) |
|
246 |
239 def allow_lazy(func, *resultclasses): |
247 def allow_lazy(func, *resultclasses): |
240 """ |
248 """ |
241 A decorator that allows a function to be called with one or more lazy |
249 A decorator that allows a function to be called with one or more lazy |
242 arguments. If none of the args are lazy, the function is evaluated |
250 arguments. If none of the args are lazy, the function is evaluated |
243 immediately, otherwise a __proxy__ is returned that will evaluate the |
251 immediately, otherwise a __proxy__ is returned that will evaluate the |
255 class LazyObject(object): |
263 class LazyObject(object): |
256 """ |
264 """ |
257 A wrapper for another class that can be used to delay instantiation of the |
265 A wrapper for another class that can be used to delay instantiation of the |
258 wrapped class. |
266 wrapped class. |
259 |
267 |
260 This is useful, for example, if the wrapped class needs to use Django |
268 By subclassing, you have the opportunity to intercept and alter the |
261 settings at creation time: we want to permit it to be imported without |
269 instantiation. If you don't need to do that, use SimpleLazyObject. |
262 accessing settings. |
|
263 """ |
270 """ |
264 def __init__(self): |
271 def __init__(self): |
265 self._wrapped = None |
272 self._wrapped = None |
266 |
273 |
267 def __getattr__(self, name): |
274 def __getattr__(self, name): |
268 if self._wrapped is None: |
275 if self._wrapped is None: |
269 self._setup() |
276 self._setup() |
270 if name == "__members__": |
|
271 # Used to implement dir(obj) |
|
272 return self._wrapped.get_all_members() |
|
273 return getattr(self._wrapped, name) |
277 return getattr(self._wrapped, name) |
274 |
278 |
275 def __setattr__(self, name, value): |
279 def __setattr__(self, name, value): |
276 if name == "_wrapped": |
280 if name == "_wrapped": |
277 # Assign to __dict__ to avoid infinite __setattr__ loops. |
281 # Assign to __dict__ to avoid infinite __setattr__ loops. |
279 else: |
283 else: |
280 if self._wrapped is None: |
284 if self._wrapped is None: |
281 self._setup() |
285 self._setup() |
282 setattr(self._wrapped, name, value) |
286 setattr(self._wrapped, name, value) |
283 |
287 |
|
288 def __delattr__(self, name): |
|
289 if name == "_wrapped": |
|
290 raise TypeError("can't delete _wrapped.") |
|
291 if self._wrapped is None: |
|
292 self._setup() |
|
293 delattr(self._wrapped, name) |
|
294 |
284 def _setup(self): |
295 def _setup(self): |
285 """ |
296 """ |
286 Must be implemented by subclasses to initialise the wrapped object. |
297 Must be implemented by subclasses to initialise the wrapped object. |
287 """ |
298 """ |
288 raise NotImplementedError |
299 raise NotImplementedError |
289 |
300 |
|
301 # introspection support: |
|
302 __members__ = property(lambda self: self.__dir__()) |
|
303 |
|
304 def __dir__(self): |
|
305 if self._wrapped is None: |
|
306 self._setup() |
|
307 return dir(self._wrapped) |
|
308 |
|
309 class SimpleLazyObject(LazyObject): |
|
310 """ |
|
311 A lazy object initialised from any function. |
|
312 |
|
313 Designed for compound objects of unknown type. For builtins or objects of |
|
314 known type, use django.utils.functional.lazy. |
|
315 """ |
|
316 def __init__(self, func): |
|
317 """ |
|
318 Pass in a callable that returns the object to be wrapped. |
|
319 |
|
320 If copies are made of the resulting SimpleLazyObject, which can happen |
|
321 in various circumstances within Django, then you must ensure that the |
|
322 callable can be safely run more than once and will return the same |
|
323 value. |
|
324 """ |
|
325 self.__dict__['_setupfunc'] = func |
|
326 # For some reason, we have to inline LazyObject.__init__ here to avoid |
|
327 # recursion |
|
328 self._wrapped = None |
|
329 |
|
330 def __str__(self): |
|
331 if self._wrapped is None: self._setup() |
|
332 return str(self._wrapped) |
|
333 |
|
334 def __unicode__(self): |
|
335 if self._wrapped is None: self._setup() |
|
336 return unicode(self._wrapped) |
|
337 |
|
338 def __deepcopy__(self, memo): |
|
339 if self._wrapped is None: |
|
340 # We have to use SimpleLazyObject, not self.__class__, because the |
|
341 # latter is proxied. |
|
342 result = SimpleLazyObject(self._setupfunc) |
|
343 memo[id(self)] = result |
|
344 return result |
|
345 else: |
|
346 # Changed to use deepcopy from copycompat, instead of copy |
|
347 # For Python 2.4. |
|
348 from django.utils.copycompat import deepcopy |
|
349 return deepcopy(self._wrapped, memo) |
|
350 |
|
351 # Need to pretend to be the wrapped class, for the sake of objects that care |
|
352 # about this (especially in equality tests) |
|
353 def __get_class(self): |
|
354 if self._wrapped is None: self._setup() |
|
355 return self._wrapped.__class__ |
|
356 __class__ = property(__get_class) |
|
357 |
|
358 def __eq__(self, other): |
|
359 if self._wrapped is None: self._setup() |
|
360 return self._wrapped == other |
|
361 |
|
362 def __hash__(self): |
|
363 if self._wrapped is None: self._setup() |
|
364 return hash(self._wrapped) |
|
365 |
|
366 def _setup(self): |
|
367 self._wrapped = self._setupfunc() |