From a9d773f5580bd2975c68e09d2731014ba4b8f281 Mon Sep 17 00:00:00 2001
From: Michele Simionato E-mail: michele.simionato@gmail.com
+Version:
-3.2.0 (2010-05-25) 3.2.1 (2010-11-28)
-Requires: Python 2.4+
Download page: http://pypi.python.org/pypi/decorator/3.2.0
+Download page: http://pypi.python.org/pypi/decorator/3.2.1
@@ -179,24 +179,22 @@ but they do not preserve the signature.
A simple implementation could be the following (notice
that in general it is impossible to memoize correctly something
that depends on non-hashable arguments):Installation: easy_install decorator
def memoize_uw(func):
- func.cache = {}
- def memoize(*args, **kw):
- if kw: # frozenset is used to ensure hashability
- key = args, frozenset(kw.iteritems())
- else:
- key = args
- cache = func.cache
- if key in cache:
- return cache[key]
- else:
- cache[key] = result = func(*args, **kw)
- return result
- return functools.update_wrapper(memoize, func)
-
+def memoize_uw(func):
+ func.cache = {}
+ def memoize(*args, **kw):
+ if kw: # frozenset is used to ensure hashability
+ key = args, frozenset(kw.iteritems())
+ else:
+ key = args
+ cache = func.cache
+ if key in cache:
+ return cache[key]
+ else:
+ cache[key] = result = func(*args, **kw)
+ return result
+ return functools.update_wrapper(memoize, func)
+
Here we used the functools.update_wrapper utility, which has been added in Python 2.5 expressly to simplify the definition of decorators (in older versions of Python you need to copy the function attributes @@ -211,7 +209,7 @@ general memoize_uw returns a function with a
>>> @memoize_uw
... def f1(x):
-... time.sleep(1) # simulate some long computation
+... time.sleep(1) # simulate some long computation
... return x
>>> f1(0, 1)
+>>> f1(0, 1)
Traceback (most recent call last):
...
-TypeError: f1() takes exactly 1 argument (2 given)
+TypeError: f1() takes exactly 1 argument (2 given)
def _memoize(func, *args, **kw):
- if kw: # frozenset is used to ensure hashability
- key = args, frozenset(kw.iteritems())
- else:
- key = args
- cache = func.cache # attributed added by memoize
- if key in cache:
- return cache[key]
- else:
- cache[key] = result = func(*args, **kw)
- return result
-+def _memoize(func, *args, **kw): + if kw: # frozenset is used to ensure hashability + key = args, frozenset(kw.iteritems()) + else: + key = args + cache = func.cache # attributed added by memoize + if key in cache: + return cache[key] + else: + cache[key] = result = func(*args, **kw) + return result +
At this point you can define your decorator as follows:
-def memoize(f):
- f.cache = {}
- return decorator(_memoize, f)
-
+def memoize(f):
+ f.cache = {}
+ return decorator(_memoize, f)
+
The difference with respect to the memoize_uw approach, which is based on nested functions, is that the decorator module forces you to lift the inner function at the outer level (flat is better than nested). @@ -289,7 +283,7 @@ decorate to the caller function.
>>> @memoize
... def heavy_computation():
-... time.sleep(2)
+... time.sleep(2)
... return "done"
>>> print heavy_computation() # the first time it will take 2 seconds
@@ -313,19 +307,15 @@ decorate to the caller function.
As an additional example, here is how you can define a trivial
trace decorator, which prints a message everytime the traced
function is called:
-
-def _trace(f, *args, **kw):
- print "calling %s with args %s, %s" % (f.__name__, args, kw)
- return f(*args, **kw)
-
-
-
-
-def trace(f):
- return decorator(_trace, f)
-
-
-
+
+def _trace(f, *args, **kw):
+ print "calling %s with args %s, %s" % (f.__name__, args, kw)
+ return f(*args, **kw)
+
+
+def trace(f):
+ return decorator(_trace, f)
+
Here is an example of usage:
>>> @trace
@@ -336,8 +326,8 @@ function is called:
It is immediate to verify that f1 works
->>> f1(0)
-calling f1 with args (0,), {}
+>>> f1(0)
+calling f1 with args (0,), {}
@@ -351,27 +341,27 @@ function is called:
The same decorator works with functions of any signature:
>>> @trace
-... def f(x, y=1, z=2, *args, **kw):
+... def f(x, y=1, z=2, *args, **kw):
... pass
->>> f(0, 3)
-calling f with args (0, 3, 2), {}
+>>> f(0, 3)
+calling f with args (0, 3, 2), {}
>>> print getargspec(f)
-ArgSpec(args=['x', 'y', 'z'], varargs='args', keywords='kw', defaults=(1, 2))
+ArgSpec(args=['x', 'y', 'z'], varargs='args', keywords='kw', defaults=(1, 2))
That includes even functions with exotic signatures like the following:
>>> @trace
-... def exotic_signature((x, y)=(1,2)): return x+y
+... def exotic_signature((x, y)=(1,2)): return x+y
>>> print getargspec(exotic_signature)
-ArgSpec(args=[['x', 'y']], varargs=None, keywords=None, defaults=((1, 2),))
+ArgSpec(args=[['x', 'y']], varargs=None, keywords=None, defaults=((1, 2),))
>>> exotic_signature()
-calling exotic_signature with args ((1, 2),), {}
-3
+calling exotic_signature with args ((1, 2),), {}
+3
@@ -405,7 +395,7 @@ For instance, you can write directly
object which can be used as a decorator:
>>> trace
-<function trace at 0x...>
+<function trace at 0x...>
@@ -429,40 +419,38 @@ object which can be used as a decorator:
sometimes it is best to have back a "busy" message than to block everything.
This behavior can be implemented with a suitable family of decorators,
where the parameter is the busy message:
-
-def blocking(not_avail):
- def blocking(f, *args, **kw):
- if not hasattr(f, "thread"): # no thread running
- def set_result(): f.result = f(*args, **kw)
- f.thread = threading.Thread(None, set_result)
- f.thread.start()
- return not_avail
- elif f.thread.isAlive():
- return not_avail
- else: # the thread is ended, return the stored result
- del f.thread
- return f.result
- return decorator(blocking)
-
-
-
+
+def blocking(not_avail):
+ def blocking(f, *args, **kw):
+ if not hasattr(f, "thread"): # no thread running
+ def set_result(): f.result = f(*args, **kw)
+ f.thread = threading.Thread(None, set_result)
+ f.thread.start()
+ return not_avail
+ elif f.thread.isAlive():
+ return not_avail
+ else: # the thread is ended, return the stored result
+ del f.thread
+ return f.result
+ return decorator(blocking)
+
Functions decorated with blocking will return a busy message if
the resource is unavailable, and the intended result if the resource is
available. For instance:
>>> @blocking("Please wait ...")
... def read_data():
-... time.sleep(3) # simulate a blocking resource
+... time.sleep(3) # simulate a blocking resource
... return "some data"
>>> print read_data() # data is not available yet
Please wait ...
->>> time.sleep(1)
+>>> time.sleep(1)
>>> print read_data() # data is not available yet
Please wait ...
->>> time.sleep(1)
+>>> time.sleep(1)
>>> print read_data() # data is not available yet
Please wait ...
@@ -490,66 +478,58 @@ is executed in a separate thread. Moreover, it is possible to set
three callbacks on_success, on_failure and on_closing,
to specify how to manage the function call.
The implementation is the following:
-
-def on_success(result): # default implementation
- "Called on the result of the function"
- return result
-
-
-
-
-def on_failure(exc_info): # default implementation
- "Called if the function fails"
- pass
-
-
-
-
-def on_closing(): # default implementation
- "Called at the end, both in case of success and failure"
- pass
-
-
-
-
-class Async(object):
- """
- A decorator converting blocking functions into asynchronous
- functions, by using threads or processes. Examples:
-
- async_with_threads = Async(threading.Thread)
- async_with_processes = Async(multiprocessing.Process)
- """
-
- def __init__(self, threadfactory):
- self.threadfactory = threadfactory
-
- def __call__(self, func, on_success=on_success,
- on_failure=on_failure, on_closing=on_closing):
- # every decorated function has its own independent thread counter
- func.counter = itertools.count(1)
- func.on_success = on_success
- func.on_failure = on_failure
- func.on_closing = on_closing
- return decorator(self.call, func)
-
- def call(self, func, *args, **kw):
- def func_wrapper():
- try:
- result = func(*args, **kw)
- except:
- func.on_failure(sys.exc_info())
- else:
- return func.on_success(result)
- finally:
- func.on_closing()
- name = '%s-%s' % (func.__name__, func.counter.next())
- thread = self.threadfactory(None, func_wrapper, name)
- thread.start()
- return thread
-
-
-
+
+def on_success(result): # default implementation
+ "Called on the result of the function"
+ return result
+
+
+def on_failure(exc_info): # default implementation
+ "Called if the function fails"
+ pass
+
+
+def on_closing(): # default implementation
+ "Called at the end, both in case of success and failure"
+ pass
+
+
+class Async(object):
+ """
+ A decorator converting blocking functions into asynchronous
+ functions, by using threads or processes. Examples:
+
+ async_with_threads = Async(threading.Thread)
+ async_with_processes = Async(multiprocessing.Process)
+ """
+
+ def __init__(self, threadfactory):
+ self.threadfactory = threadfactory
+
+ def __call__(self, func, on_success=on_success,
+ on_failure=on_failure, on_closing=on_closing):
+ # every decorated function has its own independent thread counter
+ func.counter = itertools.count(1)
+ func.on_success = on_success
+ func.on_failure = on_failure
+ func.on_closing = on_closing
+ return decorator(self.call, func)
+
+ def call(self, func, *args, **kw):
+ def func_wrapper():
+ try:
+ result = func(*args, **kw)
+ except:
+ func.on_failure(sys.exc_info())
+ else:
+ return func.on_success(result)
+ finally:
+ func.on_closing()
+ name = '%s-%s' % (func.__name__, func.counter.next())
+ thread = self.threadfactory(None, func_wrapper, name)
+ thread.start()
+ return thread
+
The decorated function returns
the current execution thread, which can be stored and checked later, for
instance to verify that the thread .isAlive().
@@ -566,7 +546,7 @@ be locked. Here is a minimalistic example:
... def write(data):
... # append data to the datalist by locking
... with threading.Lock():
-... time.sleep(1) # emulate some long running operation
+... time.sleep(1) # emulate some long running operation
... datalist.append(data)
... # other operations not requiring a lock here
@@ -576,14 +556,14 @@ be locked. Here is a minimalistic example:
be no synchronization problems since write is locked.
>>> write("data1")
-<Thread(write-1, started...)>
+<Thread(write-1, started...)>
->>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
+>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
>>> write("data2")
-<Thread(write-2, started...)>
+<Thread(write-2, started...)>
->>> time.sleep(2) # wait for the writers to complete
+>>> time.sleep(2) # wait for the writers to complete
>>> print datalist
['data1', 'data2']
@@ -610,8 +590,8 @@ were the function is generated by exec. Here i
... print args, kw
>>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f))
->>> f1(1,2)
-(1, 2) {}
+>>> f1(1,2)
+(1, 2) {}
@@ -674,14 +654,12 @@ available. In the past I have considered this acceptable, since
inspect.getsource does not really work even with regular
decorators. In that case inspect.getsource gives you the wrapper
source code which is probably not what you want:
-
-def identity_dec(func):
- def wrapper(*args, **kw):
- return func(*args, **kw)
- return wrapper
-
-
-
+
+def identity_dec(func):
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+ return wrapper
+
@identity_dec
def example(): pass
@@ -694,7 +672,7 @@ source code which is probably not what you want:
(see bug report 1764286 for an explanation of what is happening).
-Unfortunately the bug is still there, even in Python 2.6 and 3.0.
+Unfortunately the bug is still there, even in Python 2.7 and 3.1.
There is however a workaround. The decorator module adds an
attribute .undecorated to the decorated function, containing
a reference to the original function. The easy way to get
@@ -703,10 +681,10 @@ undecorated function:
>>> print inspect.getsource(factorial.undecorated)
@tail_recursive
-def factorial(n, acc=1):
+def factorial(n, acc=1):
"The good old factorial"
- if n == 0: return acc
- return factorial(n-1, n*acc)
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
<BLANKLINE>
@@ -720,18 +698,16 @@ decorator is not signature-preserving. Therefore you may want an easy way to
upgrade third party decorators to signature-preserving decorators without
having to rewrite them in terms of decorator. You can use a
FunctionMaker to implement that functionality as follows:
-
-def decorator_apply(dec, func):
- """
- Decorate a function by preserving the signature even if dec
- is not a signature-preserving decorator.
- """
- return FunctionMaker.create(
- func, 'return decorated(%(signature)s)',
- dict(decorated=dec(func)), undecorated=func)
-
-
-
+
+def decorator_apply(dec, func):
+ """
+ Decorate a function by preserving the signature even if dec
+ is not a signature-preserving decorator.
+ """
+ return FunctionMaker.create(
+ func, 'return decorated(%(signature)s)',
+ dict(decorated=dec(func)), undecorated=func)
+
decorator_apply sets the attribute .undecorated of the generated
function to the original function, so that you can get the right
source code.
@@ -745,60 +721,54 @@ pretty slick decorator that converts a tail-recursive function in an iterative
function. I have shamelessly stolen the basic idea from Kay Schluehr's recipe
in the Python Cookbook,
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691.
-
-class TailRecursive(object):
- """
- tail_recursive decorator based on Kay Schluehr's recipe
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691
- with improvements by me and George Sakkis.
- """
-
- def __init__(self, func):
- self.func = func
- self.firstcall = True
- self.CONTINUE = object() # sentinel
-
- def __call__(self, *args, **kwd):
- CONTINUE = self.CONTINUE
- if self.firstcall:
- func = self.func
- self.firstcall = False
- try:
- while True:
- result = func(*args, **kwd)
- if result is CONTINUE: # update arguments
- args, kwd = self.argskwd
- else: # last call
- return result
- finally:
- self.firstcall = True
- else: # return the arguments of the tail call
- self.argskwd = args, kwd
- return CONTINUE
-
-
-
+
+class TailRecursive(object):
+ """
+ tail_recursive decorator based on Kay Schluehr's recipe
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691
+ with improvements by me and George Sakkis.
+ """
+
+ def __init__(self, func):
+ self.func = func
+ self.firstcall = True
+ self.CONTINUE = object() # sentinel
+
+ def __call__(self, *args, **kwd):
+ CONTINUE = self.CONTINUE
+ if self.firstcall:
+ func = self.func
+ self.firstcall = False
+ try:
+ while True:
+ result = func(*args, **kwd)
+ if result is CONTINUE: # update arguments
+ args, kwd = self.argskwd
+ else: # last call
+ return result
+ finally:
+ self.firstcall = True
+ else: # return the arguments of the tail call
+ self.argskwd = args, kwd
+ return CONTINUE
+
Here the decorator is implemented as a class returning callable
objects.
-
-def tail_recursive(func):
- return decorator_apply(TailRecursive, func)
-
-
-
+
+def tail_recursive(func):
+ return decorator_apply(TailRecursive, func)
+
Here is how you apply the upgraded decorator to the good old factorial:
+
+@tail_recursive
+def factorial(n, acc=1):
+ "The good old factorial"
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
+
-@tail_recursive
-def factorial(n, acc=1):
- "The good old factorial"
- if n == 0: return acc
- return factorial(n-1, n*acc)
-
-
-
-
->>> print factorial(4)
-24
+>>> print factorial(4)
+24
@@ -807,13 +777,11 @@ your mind ;) Notice that there is no recursion limit now, and you can
easily compute factorial(1001) or larger without filling the stack
frame. Notice also that the decorator will not work on functions which
are not tail recursive, such as the following
-
-def fact(n): # this is not tail-recursive
- if n == 0: return 1
- return n * fact(n-1)
-
-
-
+
+def fact(n): # this is not tail-recursive
+ if n == 0: return 1
+ return n * fact(n-1)
+
(reminder: a function is tail recursive if it either returns a value without
making a recursive call, or returns directly the result of a recursive
call).
@@ -859,7 +827,7 @@ longer and more difficult to understand. Consider this example:
>>> @trace
... def f():
-... 1/0
+... 1/0
@@ -869,11 +837,11 @@ function is decorated the traceback will be longer:
>>> f()
Traceback (most recent call last):
...
- File "<string>", line 2, in f
- File "<doctest __main__[18]>", line 4, in trace
+ File "<string>", line 2, in f
+ File "<doctest __main__[18]>", line 4, in trace
return f(*args, **kw)
- File "<doctest __main__[47]>", line 3, in f
- 1/0
+ File "<doctest __main__[47]>", line 3, in f
+ 1/0
ZeroDivisionError: integer division or modulo by zero
@@ -926,7 +894,7 @@ called too late:
The solution is to extract the inner function from the unbound method:
>>> trace(C.meth.im_func)
-<function meth at 0x...>
+<function meth at 0x...>
@@ -945,7 +913,9 @@ you will get a NameError:
-Finally, the implementation is such that the decorated function contains
+
Finally, the implementation is such that the decorated function
+attribute .func_globals is a copy of the original function
+attribute. Moreover the decorated function contains
a copy of the original function dictionary
(vars(decorated_f) is not vars(f)):
--
cgit v1.2.1