web/lib/django/utils/functional.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 # License for code in this file that was taken from Python 2.5.
       
     2 
       
     3 # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
       
     4 # --------------------------------------------
       
     5 #
       
     6 # 1. This LICENSE AGREEMENT is between the Python Software Foundation
       
     7 # ("PSF"), and the Individual or Organization ("Licensee") accessing and
       
     8 # otherwise using this software ("Python") in source or binary form and
       
     9 # its associated documentation.
       
    10 #
       
    11 # 2. Subject to the terms and conditions of this License Agreement, PSF
       
    12 # hereby grants Licensee a nonexclusive, royalty-free, world-wide
       
    13 # license to reproduce, analyze, test, perform and/or display publicly,
       
    14 # prepare derivative works, distribute, and otherwise use Python
       
    15 # alone or in any derivative version, provided, however, that PSF's
       
    16 # License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
       
    17 # 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation;
       
    18 # All Rights Reserved" are retained in Python alone or in any derivative
       
    19 # version prepared by Licensee.
       
    20 #
       
    21 # 3. In the event Licensee prepares a derivative work that is based on
       
    22 # or incorporates Python or any part thereof, and wants to make
       
    23 # the derivative work available to others as provided herein, then
       
    24 # Licensee hereby agrees to include in any such work a brief summary of
       
    25 # the changes made to Python.
       
    26 #
       
    27 # 4. PSF is making Python available to Licensee on an "AS IS"
       
    28 # basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
       
    29 # IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
       
    30 # DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
       
    31 # FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
       
    32 # INFRINGE ANY THIRD PARTY RIGHTS.
       
    33 #
       
    34 # 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
       
    35 # FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
       
    36 # A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
       
    37 # OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
       
    38 #
       
    39 # 6. This License Agreement will automatically terminate upon a material
       
    40 # breach of its terms and conditions.
       
    41 #
       
    42 # 7. Nothing in this License Agreement shall be deemed to create any
       
    43 # relationship of agency, partnership, or joint venture between PSF and
       
    44 # Licensee.  This License Agreement does not grant permission to use PSF
       
    45 # trademarks or trade name in a trademark sense to endorse or promote
       
    46 # products or services of Licensee, or any third party.
       
    47 #
       
    48 # 8. By copying, installing or otherwise using Python, Licensee
       
    49 # agrees to be bound by the terms and conditions of this License
       
    50 # Agreement.
       
    51 
       
    52 
       
    53 def curry(_curried_func, *args, **kwargs):
       
    54     def _curried(*moreargs, **morekwargs):
       
    55         return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
       
    56     return _curried
       
    57 
       
    58 ### Begin from Python 2.5 functools.py ########################################
       
    59 
       
    60 # Summary of changes made to the Python 2.5 code below:
       
    61 #   * swapped ``partial`` for ``curry`` to maintain backwards-compatibility
       
    62 #     in Django.
       
    63 
       
    64 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation.
       
    65 # All Rights Reserved.
       
    66 
       
    67 ###############################################################################
       
    68 
       
    69 # update_wrapper() and wraps() are tools to help write
       
    70 # wrapper functions that can handle naive introspection
       
    71 
       
    72 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
       
    73 WRAPPER_UPDATES = ('__dict__',)
       
    74 def update_wrapper(wrapper,
       
    75                    wrapped,
       
    76                    assigned = WRAPPER_ASSIGNMENTS,
       
    77                    updated = WRAPPER_UPDATES):
       
    78     """Update a wrapper function to look like the wrapped function
       
    79 
       
    80        wrapper is the function to be updated
       
    81        wrapped is the original function
       
    82        assigned is a tuple naming the attributes assigned directly
       
    83        from the wrapped function to the wrapper function (defaults to
       
    84        functools.WRAPPER_ASSIGNMENTS)
       
    85        updated is a tuple naming the attributes off the wrapper that
       
    86        are updated with the corresponding attribute from the wrapped
       
    87        function (defaults to functools.WRAPPER_UPDATES)
       
    88     """
       
    89     for attr in assigned:
       
    90         setattr(wrapper, attr, getattr(wrapped, attr))
       
    91     for attr in updated:
       
    92         getattr(wrapper, attr).update(getattr(wrapped, attr))
       
    93     # Return the wrapper so this can be used as a decorator via curry()
       
    94     return wrapper
       
    95 
       
    96 def wraps(wrapped,
       
    97           assigned = WRAPPER_ASSIGNMENTS,
       
    98           updated = WRAPPER_UPDATES):
       
    99     """Decorator factory to apply update_wrapper() to a wrapper function
       
   100 
       
   101        Returns a decorator that invokes update_wrapper() with the decorated
       
   102        function as the wrapper argument and the arguments to wraps() as the
       
   103        remaining arguments. Default arguments are as for update_wrapper().
       
   104        This is a convenience function to simplify applying curry() to
       
   105        update_wrapper().
       
   106     """
       
   107     return curry(update_wrapper, wrapped=wrapped,
       
   108                  assigned=assigned, updated=updated)
       
   109 
       
   110 ### End from Python 2.5 functools.py ##########################################
       
   111 
       
   112 def memoize(func, cache, num_args):
       
   113     """
       
   114     Wrap a function so that results for any argument tuple are stored in
       
   115     'cache'. Note that the args to the function must be usable as dictionary
       
   116     keys.
       
   117 
       
   118     Only the first num_args are considered when creating the key.
       
   119     """
       
   120     def wrapper(*args):
       
   121         mem_args = args[:num_args]
       
   122         if mem_args in cache:
       
   123             return cache[mem_args]
       
   124         result = func(*args)
       
   125         cache[mem_args] = result
       
   126         return result
       
   127     return wraps(func)(wrapper)
       
   128 
       
   129 class Promise(object):
       
   130     """
       
   131     This is just a base class for the proxy class created in
       
   132     the closure of the lazy function. It can be used to recognize
       
   133     promises in code.
       
   134     """
       
   135     pass
       
   136 
       
   137 def lazy(func, *resultclasses):
       
   138     """
       
   139     Turns any callable into a lazy evaluated callable. You need to give result
       
   140     classes or types -- at least one is needed so that the automatic forcing of
       
   141     the lazy evaluation code is triggered. Results are not memoized; the
       
   142     function is evaluated on every access.
       
   143     """
       
   144 
       
   145     class __proxy__(Promise):
       
   146         """
       
   147         Encapsulate a function call and act as a proxy for methods that are
       
   148         called on the result of that function. The function is not evaluated
       
   149         until one of the methods on the result is called.
       
   150         """
       
   151         __dispatch = None
       
   152 
       
   153         def __init__(self, args, kw):
       
   154             self.__func = func
       
   155             self.__args = args
       
   156             self.__kw = kw
       
   157             if self.__dispatch is None:
       
   158                 self.__prepare_class__()
       
   159 
       
   160         def __reduce__(self):
       
   161             return (
       
   162                 _lazy_proxy_unpickle,
       
   163                 (self.__func, self.__args, self.__kw) + resultclasses
       
   164             )
       
   165 
       
   166         def __prepare_class__(cls):
       
   167             cls.__dispatch = {}
       
   168             for resultclass in resultclasses:
       
   169                 cls.__dispatch[resultclass] = {}
       
   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)
       
   175                     if hasattr(cls, k):
       
   176                         continue
       
   177                     setattr(cls, k, meth)
       
   178             cls._delegate_str = str in resultclasses
       
   179             cls._delegate_unicode = unicode in resultclasses
       
   180             assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
       
   181             if cls._delegate_unicode:
       
   182                 cls.__unicode__ = cls.__unicode_cast
       
   183             elif cls._delegate_str:
       
   184                 cls.__str__ = cls.__str_cast
       
   185         __prepare_class__ = classmethod(__prepare_class__)
       
   186 
       
   187         def __promise__(cls, klass, funcname, func):
       
   188             # Builds a wrapper around some magic method and registers that magic
       
   189             # method for the given type and method name.
       
   190             def __wrapper__(self, *args, **kw):
       
   191                 # Automatically triggers the evaluation of a lazy value and
       
   192                 # applies the given magic method of the result type.
       
   193                 res = self.__func(*self.__args, **self.__kw)
       
   194                 for t in type(res).mro():
       
   195                     if t in self.__dispatch:
       
   196                         return self.__dispatch[t][funcname](res, *args, **kw)
       
   197                 raise TypeError("Lazy object returned unexpected type.")
       
   198 
       
   199             if klass not in cls.__dispatch:
       
   200                 cls.__dispatch[klass] = {}
       
   201             cls.__dispatch[klass][funcname] = func
       
   202             return __wrapper__
       
   203         __promise__ = classmethod(__promise__)
       
   204 
       
   205         def __unicode_cast(self):
       
   206             return self.__func(*self.__args, **self.__kw)
       
   207 
       
   208         def __str_cast(self):
       
   209             return str(self.__func(*self.__args, **self.__kw))
       
   210 
       
   211         def __cmp__(self, rhs):
       
   212             if self._delegate_str:
       
   213                 s = str(self.__func(*self.__args, **self.__kw))
       
   214             elif self._delegate_unicode:
       
   215                 s = unicode(self.__func(*self.__args, **self.__kw))
       
   216             else:
       
   217                 s = self.__func(*self.__args, **self.__kw)
       
   218             if isinstance(rhs, Promise):
       
   219                 return -cmp(rhs, s)
       
   220             else:
       
   221                 return cmp(s, rhs)
       
   222 
       
   223         def __mod__(self, rhs):
       
   224             if self._delegate_str:
       
   225                 return str(self) % rhs
       
   226             elif self._delegate_unicode:
       
   227                 return unicode(self) % rhs
       
   228             else:
       
   229                 raise AssertionError('__mod__ not supported for non-string types')
       
   230 
       
   231         def __deepcopy__(self, memo):
       
   232             # Instances of this class are effectively immutable. It's just a
       
   233             # collection of functions. So we don't need to do anything
       
   234             # complicated for copying.
       
   235             memo[id(self)] = self
       
   236             return self
       
   237 
       
   238     def __wrapper__(*args, **kw):
       
   239         # Creates the proxy object, instead of the actual value.
       
   240         return __proxy__(args, kw)
       
   241 
       
   242     return wraps(func)(__wrapper__)
       
   243 
       
   244 def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):
       
   245     return lazy(func, *resultclasses)(*args, **kwargs)
       
   246 
       
   247 def allow_lazy(func, *resultclasses):
       
   248     """
       
   249     A decorator that allows a function to be called with one or more lazy
       
   250     arguments. If none of the args are lazy, the function is evaluated
       
   251     immediately, otherwise a __proxy__ is returned that will evaluate the
       
   252     function when needed.
       
   253     """
       
   254     def wrapper(*args, **kwargs):
       
   255         for arg in list(args) + kwargs.values():
       
   256             if isinstance(arg, Promise):
       
   257                 break
       
   258         else:
       
   259             return func(*args, **kwargs)
       
   260         return lazy(func, *resultclasses)(*args, **kwargs)
       
   261     return wraps(func)(wrapper)
       
   262 
       
   263 class LazyObject(object):
       
   264     """
       
   265     A wrapper for another class that can be used to delay instantiation of the
       
   266     wrapped class.
       
   267 
       
   268     By subclassing, you have the opportunity to intercept and alter the
       
   269     instantiation. If you don't need to do that, use SimpleLazyObject.
       
   270     """
       
   271     def __init__(self):
       
   272         self._wrapped = None
       
   273 
       
   274     def __getattr__(self, name):
       
   275         if self._wrapped is None:
       
   276             self._setup()
       
   277         return getattr(self._wrapped, name)
       
   278 
       
   279     def __setattr__(self, name, value):
       
   280         if name == "_wrapped":
       
   281             # Assign to __dict__ to avoid infinite __setattr__ loops.
       
   282             self.__dict__["_wrapped"] = value
       
   283         else:
       
   284             if self._wrapped is None:
       
   285                 self._setup()
       
   286             setattr(self._wrapped, name, value)
       
   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 
       
   295     def _setup(self):
       
   296         """
       
   297         Must be implemented by subclasses to initialise the wrapped object.
       
   298         """
       
   299         raise NotImplementedError
       
   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()