web/lib/django/utils/functional.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
    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()