web/lib/django/utils/functional.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     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 #   * 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 
       
    67 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation.
       
    68 # All Rights Reserved.
       
    69 
       
    70 ###############################################################################
       
    71 
       
    72 # update_wrapper() and wraps() are tools to help write
       
    73 # wrapper functions that can handle naive introspection
       
    74 
       
    75 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
       
    76 WRAPPER_UPDATES = ('__dict__',)
       
    77 def update_wrapper(wrapper,
       
    78                    wrapped,
       
    79                    assigned = WRAPPER_ASSIGNMENTS,
       
    80                    updated = WRAPPER_UPDATES):
       
    81     """Update a wrapper function to look like the wrapped function
       
    82 
       
    83        wrapper is the function to be updated
       
    84        wrapped is the original function
       
    85        assigned is a tuple naming the attributes assigned directly
       
    86        from the wrapped function to the wrapper function (defaults to
       
    87        functools.WRAPPER_ASSIGNMENTS)
       
    88        updated is a tuple naming the attributes off the wrapper that
       
    89        are updated with the corresponding attribute from the wrapped
       
    90        function (defaults to functools.WRAPPER_UPDATES)
       
    91     """
       
    92     for attr in assigned:
       
    93         try:
       
    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:
       
    98         getattr(wrapper, attr).update(getattr(wrapped, attr))
       
    99     # Return the wrapper so this can be used as a decorator via curry()
       
   100     return wrapper
       
   101 
       
   102 def wraps(wrapped,
       
   103           assigned = WRAPPER_ASSIGNMENTS,
       
   104           updated = WRAPPER_UPDATES):
       
   105     """Decorator factory to apply update_wrapper() to a wrapper function
       
   106 
       
   107        Returns a decorator that invokes update_wrapper() with the decorated
       
   108        function as the wrapper argument and the arguments to wraps() as the
       
   109        remaining arguments. Default arguments are as for update_wrapper().
       
   110        This is a convenience function to simplify applying curry() to
       
   111        update_wrapper().
       
   112     """
       
   113     return curry(update_wrapper, wrapped=wrapped,
       
   114                  assigned=assigned, updated=updated)
       
   115 
       
   116 ### End from Python 2.5 functools.py ##########################################
       
   117 
       
   118 def memoize(func, cache, num_args):
       
   119     """
       
   120     Wrap a function so that results for any argument tuple are stored in
       
   121     'cache'. Note that the args to the function must be usable as dictionary
       
   122     keys.
       
   123 
       
   124     Only the first num_args are considered when creating the key.
       
   125     """
       
   126     def wrapper(*args):
       
   127         mem_args = args[:num_args]
       
   128         if mem_args in cache:
       
   129             return cache[mem_args]
       
   130         result = func(*args)
       
   131         cache[mem_args] = result
       
   132         return result
       
   133     return wraps(func)(wrapper)
       
   134 
       
   135 class Promise(object):
       
   136     """
       
   137     This is just a base class for the proxy class created in
       
   138     the closure of the lazy function. It can be used to recognize
       
   139     promises in code.
       
   140     """
       
   141     pass
       
   142 
       
   143 def lazy(func, *resultclasses):
       
   144     """
       
   145     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
       
   147     the lazy evaluation code is triggered. Results are not memoized; the
       
   148     function is evaluated on every access.
       
   149     """
       
   150     class __proxy__(Promise):
       
   151         """
       
   152         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
       
   154         until one of the methods on the result is called.
       
   155         """
       
   156         __dispatch = None
       
   157 
       
   158         def __init__(self, args, kw):
       
   159             self.__func = func
       
   160             self.__args = args
       
   161             self.__kw = kw
       
   162             if self.__dispatch is None:
       
   163                 self.__prepare_class__()
       
   164 
       
   165         def __prepare_class__(cls):
       
   166             cls.__dispatch = {}
       
   167             for resultclass in resultclasses:
       
   168                 cls.__dispatch[resultclass] = {}
       
   169                 for (k, v) in resultclass.__dict__.items():
       
   170                     if hasattr(cls, k):
       
   171                         continue
       
   172                     setattr(cls, k, cls.__promise__(resultclass, k, v))
       
   173             cls._delegate_str = str in resultclasses
       
   174             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."
       
   176             if cls._delegate_unicode:
       
   177                 cls.__unicode__ = cls.__unicode_cast
       
   178             elif cls._delegate_str:
       
   179                 cls.__str__ = cls.__str_cast
       
   180         __prepare_class__ = classmethod(__prepare_class__)
       
   181 
       
   182         def __promise__(cls, klass, funcname, func):
       
   183             # Builds a wrapper around some magic method and registers that magic
       
   184             # method for the given type and method name.
       
   185             def __wrapper__(self, *args, **kw):
       
   186                 # Automatically triggers the evaluation of a lazy value and
       
   187                 # applies the given magic method of the result type.
       
   188                 res = self.__func(*self.__args, **self.__kw)
       
   189                 for t in type(res).mro():
       
   190                     if t in self.__dispatch:
       
   191                         return self.__dispatch[t][funcname](res, *args, **kw)
       
   192                 raise TypeError("Lazy object returned unexpected type.")
       
   193 
       
   194             if klass not in cls.__dispatch:
       
   195                 cls.__dispatch[klass] = {}
       
   196             cls.__dispatch[klass][funcname] = func
       
   197             return __wrapper__
       
   198         __promise__ = classmethod(__promise__)
       
   199 
       
   200         def __unicode_cast(self):
       
   201             return self.__func(*self.__args, **self.__kw)
       
   202 
       
   203         def __str_cast(self):
       
   204             return str(self.__func(*self.__args, **self.__kw))
       
   205 
       
   206         def __cmp__(self, rhs):
       
   207             if self._delegate_str:
       
   208                 s = str(self.__func(*self.__args, **self.__kw))
       
   209             elif self._delegate_unicode:
       
   210                 s = unicode(self.__func(*self.__args, **self.__kw))
       
   211             else:
       
   212                 s = self.__func(*self.__args, **self.__kw)
       
   213             if isinstance(rhs, Promise):
       
   214                 return -cmp(rhs, s)
       
   215             else:
       
   216                 return cmp(s, rhs)
       
   217 
       
   218         def __mod__(self, rhs):
       
   219             if self._delegate_str:
       
   220                 return str(self) % rhs
       
   221             elif self._delegate_unicode:
       
   222                 return unicode(self) % rhs
       
   223             else:
       
   224                 raise AssertionError('__mod__ not supported for non-string types')
       
   225 
       
   226         def __deepcopy__(self, memo):
       
   227             # Instances of this class are effectively immutable. It's just a
       
   228             # collection of functions. So we don't need to do anything
       
   229             # complicated for copying.
       
   230             memo[id(self)] = self
       
   231             return self
       
   232 
       
   233     def __wrapper__(*args, **kw):
       
   234         # Creates the proxy object, instead of the actual value.
       
   235         return __proxy__(args, kw)
       
   236 
       
   237     return wraps(func)(__wrapper__)
       
   238 
       
   239 def allow_lazy(func, *resultclasses):
       
   240     """
       
   241     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
       
   243     immediately, otherwise a __proxy__ is returned that will evaluate the
       
   244     function when needed.
       
   245     """
       
   246     def wrapper(*args, **kwargs):
       
   247         for arg in list(args) + kwargs.values():
       
   248             if isinstance(arg, Promise):
       
   249                 break
       
   250         else:
       
   251             return func(*args, **kwargs)
       
   252         return lazy(func, *resultclasses)(*args, **kwargs)
       
   253     return wraps(func)(wrapper)
       
   254 
       
   255 class LazyObject(object):
       
   256     """
       
   257     A wrapper for another class that can be used to delay instantiation of the
       
   258     wrapped class.
       
   259 
       
   260     This is useful, for example, if the wrapped class needs to use Django
       
   261     settings at creation time: we want to permit it to be imported without
       
   262     accessing settings.
       
   263     """
       
   264     def __init__(self):
       
   265         self._wrapped = None
       
   266 
       
   267     def __getattr__(self, name):
       
   268         if self._wrapped is None:
       
   269             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)
       
   274 
       
   275     def __setattr__(self, name, value):
       
   276         if name == "_wrapped":
       
   277             # Assign to __dict__ to avoid infinite __setattr__ loops.
       
   278             self.__dict__["_wrapped"] = value
       
   279         else:
       
   280             if self._wrapped is None:
       
   281                 self._setup()
       
   282             setattr(self._wrapped, name, value)
       
   283 
       
   284     def _setup(self):
       
   285         """
       
   286         Must be implemented by subclasses to initialise the wrapped object.
       
   287         """
       
   288         raise NotImplementedError
       
   289