1 # License for code in this file that was taken from Python 2.5.
3 # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
4 # --------------------------------------------
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.
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.
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.
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.
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.
39 # 6. This License Agreement will automatically terminate upon a material
40 # breach of its terms and conditions.
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.
48 # 8. By copying, installing or otherwise using Python, Licensee
49 # agrees to be bound by the terms and conditions of this License
53 def curry(_curried_func
, *args
, **kwargs
):
54 def _curried(*moreargs
, **morekwargs
):
55 return _curried_func(*(args
+moreargs
), **dict(kwargs
, **morekwargs
))
58 ### Begin from Python 2.5 functools.py ########################################
60 # Summary of changes made to the Python 2.5 code below:
61 # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility
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__``.
67 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation.
68 # All Rights Reserved.
70 ###############################################################################
72 # update_wrapper() and wraps() are tools to help write
73 # wrapper functions that can handle naive introspection
75 WRAPPER_ASSIGNMENTS
= ('__module__', '__name__', '__doc__')
76 WRAPPER_UPDATES
= ('__dict__',)
77 def update_wrapper(wrapper
,
79 assigned
= WRAPPER_ASSIGNMENTS
,
80 updated
= WRAPPER_UPDATES
):
81 """Update a wrapper function to look like the wrapped function
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)
94 setattr(wrapper
, attr
, getattr(wrapped
, attr
))
95 except TypeError: # Python 2.3 doesn't allow assigning to __name__.
98 getattr(wrapper
, attr
).update(getattr(wrapped
, attr
))
99 # Return the wrapper so this can be used as a decorator via curry()
103 assigned
= WRAPPER_ASSIGNMENTS
,
104 updated
= WRAPPER_UPDATES
):
105 """Decorator factory to apply update_wrapper() to a wrapper function
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
113 return curry(update_wrapper
, wrapped
=wrapped
,
114 assigned
=assigned
, updated
=updated
)
116 ### End from Python 2.5 functools.py ##########################################
118 def memoize(func
, cache
, num_args
):
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
124 Only the first num_args are considered when creating the key.
127 mem_args
= args
[:num_args
]
128 if mem_args
in cache
:
129 return cache
[mem_args
]
131 cache
[mem_args
] = result
133 return wraps(func
)(wrapper
)
135 class Promise(object):
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
143 def lazy(func
, *resultclasses
):
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.
150 class __proxy__(Promise
):
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.
158 def __init__(self
, args
, kw
):
162 if self
.__dispatch
is None:
163 self
.__prepare
_class
__()
165 def __prepare_class__(cls
):
167 for resultclass
in resultclasses
:
168 cls
.__dispatch
[resultclass
] = {}
169 for (k
, v
) in resultclass
.__dict
__.items():
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__
)
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.")
194 if klass
not in cls
.__dispatch
:
195 cls
.__dispatch
[klass
] = {}
196 cls
.__dispatch
[klass
][funcname
] = func
198 __promise__
= classmethod(__promise__
)
200 def __unicode_cast(self
):
201 return self
.__func
(*self
.__args
, **self
.__kw
)
203 def __str_cast(self
):
204 return str(self
.__func
(*self
.__args
, **self
.__kw
))
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
))
212 s
= self
.__func
(*self
.__args
, **self
.__kw
)
213 if isinstance(rhs
, Promise
):
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
224 raise AssertionError('__mod__ not supported for non-string types')
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
233 def __wrapper__(*args
, **kw
):
234 # Creates the proxy object, instead of the actual value.
235 return __proxy__(args
, kw
)
237 return wraps(func
)(__wrapper__
)
239 def allow_lazy(func
, *resultclasses
):
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.
246 def wrapper(*args
, **kwargs
):
247 for arg
in list(args
) + kwargs
.values():
248 if isinstance(arg
, Promise
):
251 return func(*args
, **kwargs
)
252 return lazy(func
, *resultclasses
)(*args
, **kwargs
)
253 return wraps(func
)(wrapper
)