summaryrefslogtreecommitdiff
path: root/decorator
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2008-12-10 07:05:52 +0000
committermichele.simionato <devnull@localhost>2008-12-10 07:05:52 +0000
commitb4bcdb1fda1f8c033b81daa5c8ca1b33ad601e91 (patch)
treef49b40aaabe06c97f04f857d0da5b9d7a8f00a6c /decorator
parent8de0e0c7f4a26e5f9fe23b677153f971a4637c49 (diff)
downloadmicheles-b4bcdb1fda1f8c033b81daa5c8ca1b33ad601e91.tar.gz
Removed decorator.apply and decorator.wrap
Diffstat (limited to 'decorator')
-rwxr-xr-xdecorator/decorator.py38
-rw-r--r--decorator/documentation.py211
-rw-r--r--decorator/setup.py61
3 files changed, 159 insertions, 151 deletions
diff --git a/decorator/decorator.py b/decorator/decorator.py
index 1f1607b..a04f9e6 100755
--- a/decorator/decorator.py
+++ b/decorator/decorator.py
@@ -108,30 +108,22 @@ class FunctionMaker(object):
self.update(func, __source__=source)
return func
-def decorator_wrap(caller, func):
- "Decorate a function with a caller"
- fun = FunctionMaker(func)
- src = """def %(name)s(%(signature)s):
+def decorator(caller, func=None):
+ """
+ decorator(caller) converts a caller function into a decorator;
+ decorator(caller, func) decorates a function using a caller.
+ """
+ if func is None: # returns a decorator
+ fun = FunctionMaker(caller)
+ first_arg = inspect.getargspec(caller)[0][0]
+ src = 'def %s(%s): return _call_(caller, %s)' % (
+ caller.__name__, first_arg, first_arg)
+ return fun.make(src, caller=caller, _call_=decorator)
+ else: # returns a decorated function
+ fun = FunctionMaker(func)
+ src = """def %(name)s(%(signature)s):
return _call_(_func_, %(signature)s)"""
- return fun.make(src, _func_=func, _call_=caller)
-
-def decorator_apply(dec, func):
- "Decorate a function using a signature-non-preserving decorator"
- fun = FunctionMaker(func)
- src = '''def %(name)s(%(signature)s):
- return decorated(%(signature)s)'''
- return fun.make(src, decorated=dec(func))
-
-def decorator(caller):
- "decorator(caller) converts a caller function into a decorator"
- fun = FunctionMaker(caller)
- first_arg = fun.signature.split(',')[0]
- src = 'def %s(%s): return _call_(caller, %s)' % (
- caller.__name__, first_arg, first_arg)
- return fun.make(src, caller=caller, _call_=decorator_wrap)
-
-decorator.wrap = decorator_wrap
-decorator.apply = decorator_apply
+ return fun.make(src, _func_=func, _call_=caller)
###################### deprecated functionality #########################
diff --git a/decorator/documentation.py b/decorator/documentation.py
index a73693a..be2a3a2 100644
--- a/decorator/documentation.py
+++ b/decorator/documentation.py
@@ -4,8 +4,8 @@ The ``decorator`` module
:author: Michele Simionato
:E-mail: michele.simionato@gmail.com
-:version: 3.0 (11 December 2008)
-:Download page: http://pypi.python.org/decorator
+:version: $VERSION ($DATE)
+:Download page: http://pypi.python.org/pypi/decorator
:Installation: ``easy_install decorator``
:License: BSD license
@@ -88,6 +88,9 @@ A simple implementation for Python 2.5 could be the following:
$$memoize25
+Notice that in general it is impossible to memoize correctly something
+that depends on non-hashable arguments.
+
Here we used the ``functools.update_wrapper`` utility, which has
been added in Python 2.5 to simplify the definition of decorators.
@@ -141,10 +144,18 @@ and implements the tracing capability:
$$_memoize
-At this point you can define your decorator by means of ``decorator.wrap``:
+At this point you can define your decorator by means of ``decorator``:
$$memoize
+The difference with respect to the Python 2.5 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*).
+Moreover, you are forced to pass explicitly the function you want to
+decorate as first argument of the helper function, also know as
+the *caller* function, since it calls the original function with the
+given arguments.
+
Here is a test of usage:
>>> @memoize
@@ -163,9 +174,6 @@ The signature of ``heavy_computation`` is the one you would expect:
>>> print getargspec(heavy_computation)
([], None, None, None)
-Notice that in general it is impossible to memoize correctly something
-that depends on mutable arguments.
-
A ``trace`` decorator
------------------------------------------------------
@@ -220,40 +228,35 @@ in Python 2.6 and removed in Python 3.0.
``decorator`` is a decorator
---------------------------------------------
-The ``decorator`` module provides an easy shortcut to convert
-the helper function into a signature-preserving decorator: the
-``decorator`` function itself, which can be considered as a signature-changing
+It may be annoying to be forced to write a caller function (like the ``_trace``
+function above) and then a trivial wrapper
+(``def trace(f): return decorator(_trace, f)``) every time. For this reason,
+the ``decorator`` module provides an easy shortcut to convert
+the caller function into a signature-preserving decorator:
+you can call ``decorator`` with a single argument and you will get out
+your decorator: ``trace = decorator(_trace)``.
+That means that the ``decorator`` function can be used as a signature-changing
decorator, just as ``classmethod`` and ``staticmethod``.
However, ``classmethod`` and ``staticmethod`` return generic
objects which are not callable, while ``decorator`` returns
signature-preserving decorators, i.e. functions of a single argument.
-Therefore, you can write
+For instance, you can write directly
>>> @decorator
-... def tracing(f, *args, **kw):
+... def trace(f, *args, **kw):
... print "calling %s with args %s, %s" % (f.func_name, args, kw)
... return f(*args, **kw)
-instead of
-
-.. code-block:: python
-
- def _tracing(f, *args, **kw):
- print "calling %s with args %s, %s" % (f.func_name, args, kw)
- return f(*args, **kw)
+and now ``trace`` will be a decorator. You
+can easily check that the signature has changed:
- def tracing(f):
- return decorator.wrap(_tracing, f)
-
-We can easily check that the signature has changed:
-
->>> print getargspec(tracing)
+>>> print getargspec(trace)
(['f'], None, None, None)
-Therefore now ``tracing`` can be used as a decorator and
+Therefore now ``trace`` can be used as a decorator and
the following will work:
->>> @tracing
+>>> @trace
... def func(): pass
>>> func()
@@ -440,18 +443,55 @@ interface requirements for (more stringent) inheritance requirements.
.. _I generally dislike inheritance: http://stacktrace.it/articoli/2008/06/i-pericoli-della-programmazione-con-i-mixin1
-Dealing with third party decorators: ``decorator.apply``
-------------------------------------------------------------
+The ``FunctionMaker`` class
+---------------------------------------------------------------
+
+The public API of the ``decorator`` module consists in the
+``decorator`` function and its two attributes ``decorator`` and
+``decorator_apply``. Internally, the functionality is implemented via
+a ``FunctionMaker`` class which is able to generate on the fly
+functions with a given name and signature. You should not need to
+resort to ``FunctionMaker`` when writing ordinary decorators, but it
+is interesting to know how the module works internally, so I have
+decided to add this paragraph. Notice that while I do not have plan
+to change or remove the functionality provided in the
+``FunctionMaker`` class, I do not guarantee that it will stay
+unchanged forever. On the other hand, the functionality provided by
+``decorator`` has been there from version 0.1 and it is guaranteed to
+stay there forever.
+``FunctionMaker`` takes the name and the signature of a function in
+input, or a whole function. Here is an example of how to
+restrict the signature of a function:
+
+>>> def f(*args, **kw):
+... print args, kw
+
+>>> f1 = FunctionMaker(name="f1", signature="a,b").make('''
+... def %(name)s(%(signature)s):
+... f(%(signature)s)''', f=f)
+
+>>> f1(1,2)
+(1, 2) {}
Sometimes you find on the net some cool decorator that you would
like to include in your code. However, more often than not the cool
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``. To this aim the
-``decorator`` module provides an utility function
-``decorator.apply(third_party_decorator, func)``.
+having to rewrite them in terms of ``decorator``. You can use a
+``FunctionMaker`` to implement that functionality as follows:
+
+$$decorator_apply
-In order to give an example of usage, I will show a
+Notice that I am not providing this functionality in the ``decorator``
+module directly since I think it is best to rewrite a decorator rather
+than adding an additional level of indirection. However, practicality
+beats purity, so you can add ``decorator_apply`` to your toolbox and
+use it if you need to.
+
+``tail-recursive``
+------------------------------------------------------------
+
+In order to give an example of usage of ``decorator_apply``, I will show a
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,
@@ -530,7 +570,7 @@ a penalty in your specific use case is to measure it.
You should be aware that decorators will make your tracebacks
longer and more difficult to understand. Consider this example:
->>> @tracing
+>>> @trace
... def f():
... 1/0
@@ -542,13 +582,13 @@ Traceback (most recent call last):
File "<stdin>", line 1, in ?
f()
File "<string>", line 2, in f
- File "<stdin>", line 4, in tracing
+ File "<stdin>", line 4, in trace
return f(*args, **kw)
File "<stdin>", line 3, in f
1/0
ZeroDivisionError: integer division or modulo by zero
-You see here the inner call to the decorator ``tracing``, which calls
+You see here the inner call to the decorator ``trace``, which calls
``f(*args, **kw)``, and a reference to ``File "<string>", line 2, in f``.
This latter reference is due to the fact that internally the decorator
module uses ``exec`` to generate the decorated function. Notice that
@@ -579,7 +619,7 @@ a ``__source__`` attribute to the decorated function, therefore
you can get the code which is executed:
>>> print f.__source__
-# _call_=<function __main__.tracing>
+# _call_=<function __main__.trace>
# _func_=<function __main__.f>
def f():
return _call_(_func_, )
@@ -610,14 +650,14 @@ callable objects, nor on built-in functions, due to limitations of the
Moreover, you can decorate anonymous functions:
->>> tracing(lambda : None)()
+>>> trace(lambda : None)()
calling <lambda> with args (), {}
There is a restriction on the names of the arguments: for instance,
if try to call an argument ``_call_`` or ``_func_``
you will get a ``NameError``:
->>> @tracing
+>>> @trace
... def f(_func_): print f
...
Traceback (most recent call last):
@@ -633,7 +673,7 @@ a copy of the original function attributes:
>>> f.attr1 = "something" # setting an attribute
>>> f.attr2 = "something else" # setting another attribute
->>> traced_f = tracing(f) # the decorated function
+>>> traced_f = trace(f) # the decorated function
>>> traced_f.attr1
'something'
@@ -641,41 +681,6 @@ a copy of the original function attributes:
>>> f.attr2 # the original attribute did not change
'something else'
-The ``FunctionMaker`` class
----------------------------------------------------------------
-
-The public API of the ``decorator`` module consists in the
-``decorator`` function and its two attributes ``decorator.wrap`` and
-``decorator.apply``. Internally, the functionality is implemented via
-a ``FunctionMaker`` class which is able to generate on the fly
-functions with a given name and signature. You should not need to
-resort to ``FunctionMaker`` when writing ordinary decorators, but it
-is interesting to know how the module works internally, so I have
-decided to add this paragraph. Notice that while I do not have plan
-to change or remove the functionality provided in the
-``FunctionMaker`` class, I do not guarantee that it will stay
-unchanged forever. On the other hand, the functionality provided by
-``decorator`` has been there from version 0.1 and it is guaranteed to
-stay there forever.
-``FunctionMaker`` takes the name and the signature of a function in
-input, or a whole function. Here is an example of how to
-restrict the signature of a function:
-
->>> def f(*args, **kw):
-... print args, kw
-
->>> f1 = FunctionMaker(name="f1", signature="a,b").make('''
-... def %(name)s(%(signature)s):
-... f(%(signature)s)''', f=f)
-
->>> f1(1,2)
-(1, 2) {}
-
-The utility ``decorator.wrap`` instead takes a function in input and
-returns a new function; it is defined as follows:
-
-$$decorator_wrap
-
Backward compatibility notes
---------------------------------------------------------------
@@ -689,7 +694,7 @@ in the future. For the moment, using them raises a ``DeprecationWarning``.
to be changed anyway to work with Python 3.0; ``new_wrapper`` has been
removed since it was useless: its major use case (converting
signature changing decorators to signature preserving decorators)
-has been subsumed by ``decorator.apply``
+has been subsumed by ``decorator_apply``
and the other use case can be managed with the ``FunctionMaker``.
Finally ``decorator`` cannot be used as a class decorator and the
@@ -737,15 +742,25 @@ you are unhappy with it, send me a patch!
from __future__ import with_statement
import sys, threading, time, functools
from decorator import *
+from setup import VERSION
+
+today = time.strftime('%Y-%m-%d')
+
+__doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today)
-decorator_wrap = decorator.wrap
+def decorator_apply(dec, func):
+ "Decorate a function using a signature-non-preserving decorator"
+ fun = FunctionMaker(func)
+ src = '''def %(name)s(%(signature)s):
+ return decorated(%(signature)s)'''
+ return fun.make(src, decorated=dec(func))
def _trace(f, *args, **kw):
print "calling %s with args %s, %s" % (f.func_name, args, kw)
return f(*args, **kw)
def trace(f):
- return decorator.wrap(_trace, f)
+ return decorator(_trace, f)
def delayed(nsec):
def _delayed(proc, *args, **kw):
@@ -762,13 +777,28 @@ def identity_dec(func):
@identity_dec
def example(): pass
+def memoize25(func):
+ func.cache = {}
+ def memoize(*args, **kw):
+ if kw:
+ key = args, frozenset(kw.items())
+ 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(func, *args, **kw):
# args and kw must be hashable
if kw:
- key = args, frozenset(kw.items())
+ key = args, frozenset(kw.items())
else:
- key = args
- cache = func.cache # created at decoration time
+ key = args
+ cache = func.cache
if key in cache:
return cache[key]
else:
@@ -777,22 +807,7 @@ def _memoize(func, *args, **kw):
def memoize(f):
f.cache = {}
- return decorator.wrap(_memoize, f)
-
-def memoize25(func):
- func.cache = {}
- def memoize(*args, **kw):
- if kw:
- key = args, frozenset(kw.items())
- else:
- key = args
- cache = func.cache # created at decoration time
- if key in cache:
- return cache[key]
- else:
- cache[key] = result = func(*args, **kw)
- return result
- return functools.update_wrapper(memoize, func)
+ return decorator(_memoize, f)
threaded = delayed(0) # no-delay decorator
@@ -847,7 +862,7 @@ class Restricted(object):
'%s does not have the permission to run %s!'
% (userclass.__name__, func.__name__))
def __call__(self, func):
- return decorator.wrap(self.call, func)
+ return decorator(self.call, func)
class Action(object):
@Restricted(User)
@@ -894,7 +909,7 @@ class TailRecursive(object):
return result
def tail_recursive(func):
- return decorator.apply(TailRecursive, func)
+ return decorator_apply(TailRecursive, func)
@tail_recursive
def factorial(n, acc=1):
diff --git a/decorator/setup.py b/decorator/setup.py
index e6240f6..3b18292 100644
--- a/decorator/setup.py
+++ b/decorator/setup.py
@@ -3,35 +3,36 @@ try:
except ImportError:
from distutils.core import setup
-setup(name='decorator',
- version='2.3.2',
- description=\
- 'Better living through Python with decorators.',
- long_description="""\
-As of now, writing custom decorators correctly requires some experience
-and it is not as easy as it could be. For instance, typical implementations
-of decorators involve nested functions, and we all know
-that flat is better than nested. Moreover, typical implementations
-of decorators do not preserve the signature of decorated functions,
-thus confusing both documentation tools and developers.
+VERSION = '3.0.0'
-The aim of the decorator module it to simplify the usage of decorators
-for the average programmer, and to popularize decorators usage giving
-examples of useful decorators, such as memoize, tracing,
-redirecting_stdout, locked, etc.""",
- author='Michele Simionato',
- author_email='michele.simionato@gmail.com',
- url='http://www.phyast.pitt.edu/~micheles/python/documentation.html',
- license="BSD License",
- py_modules = ['decorator'],
- keywords="decorators generic utility",
- classifiers=['Development Status :: 5 - Production/Stable',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Utilities'],
- zip_safe=False)
+if __name__ == '__main__':
+ setup(name='decorator',
+ version=VERSION,
+ description='Better living through Python with decorators',
+ long_description="""\
+ As of now, writing custom decorators correctly requires some experience
+ and it is not as easy as it could be. For instance, typical implementations
+ of decorators involve nested functions, and we all know
+ that flat is better than nested. Moreover, typical implementations
+ of decorators do not preserve the signature of decorated functions,
+ thus confusing both documentation tools and developers.
+
+ The aim of the decorator module it to simplify the usage of decorators
+ for the average programmer, and to popularize decorators usage giving
+ examples of useful decorators, such as memoize, tracing, threaded, etc.""",
+ author='Michele Simionato',
+ author_email='michele.simionato@gmail.com',
+ url='http://pypi.python.org/pypi/decorator',
+ license="BSD License",
+ py_modules = ['decorator'],
+ keywords="decorators generic utility",
+ classifiers=['Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities'],
+ zip_safe=False)