summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST.in2
-rw-r--r--PKG-INFO100
-rw-r--r--README.txt80
-rw-r--r--documentation.py1134
-rw-r--r--documentation3.py1170
-rw-r--r--setup.cfg5
-rw-r--r--setup.py41
-rw-r--r--src/decorator.egg-info/PKG-INFO100
-rw-r--r--src/decorator.egg-info/SOURCES.txt11
-rw-r--r--src/decorator.egg-info/dependency_links.txt1
-rw-r--r--src/decorator.egg-info/not-zip-safe1
-rw-r--r--src/decorator.egg-info/top_level.txt1
-rw-r--r--src/decorator.py251
13 files changed, 2897 insertions, 0 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..52e7659
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include documentation.py documentation3.py
+exclude Makefile
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..8491af5
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,100 @@
+Metadata-Version: 1.1
+Name: decorator
+Version: 3.4.0
+Summary: Better living through Python with decorators
+Home-page: http://pypi.python.org/pypi/decorator
+Author: Michele Simionato
+Author-email: michele.simionato@gmail.com
+License: BSD License
+Description: Decorator module
+ =================
+
+
+ :Author: Michele Simionato
+ :E-mail: michele.simionato@gmail.com
+ :Requires: Python 2.4+
+ :Download page: http://pypi.python.org/pypi/decorator
+ :Installation: ``easy_install decorator``
+ :License: BSD license
+
+ Installation
+ -------------
+
+ If you are lazy, just perform
+
+ $ easy_install decorator
+
+ which will install just the module on your system. Notice that
+ Python 3 requires the easy_install version of the distribute_ project.
+
+ If you prefer to install the full distribution from source, including
+ the documentation, download the tarball_, unpack it and run
+
+ $ python setup.py install
+
+ in the main directory, possibly as superuser.
+
+ .. _tarball: http://pypi.python.org/pypi/decorator
+ .. _distribute: http://packages.python.org/distribute/
+
+ Testing
+ --------
+
+ For Python 2.5, 2.6, 2.7 run
+
+ $ python documentation.py
+
+ for Python 3.X run
+
+ $ python documentation3.py
+
+ You will see a few innocuous errors with Python 2.5, because some
+ inner details such as the introduction of the ArgSpec namedtuple and
+ Thread.__repr__ changed. You may safely ignore them.
+
+ You cannot run the tests in Python 2.4, since there is a test using
+ the with statement, but the decorator module is expected to work
+ anyway (it has been used in production with Python 2.4 for years). My
+ plan is to keep supporting all Python versions >= 2.4 in the core
+ module, but I will keep the documentation and the tests updated only
+ for the latest Python versions in both the 2.X and 3.X branches.
+
+ Finally, notice that you may run into trouble if in your system there
+ is an older version of the decorator module; in such a case remove the
+ old version.
+
+ Documentation
+ --------------
+
+ There are various versions of the documentation:
+
+ - `HTML version (Python 2)`_
+ - `PDF version (Python 2)`_
+
+ - `HTML version (Python 3)`_
+ - `PDF version (Python 3)`_
+
+ .. _HTML version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.html
+ .. _PDF version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.pdf
+ .. _HTML version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.html
+ .. _PDF version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.pdf
+
+ Repository
+ ---------------
+
+ The project is hosted on GoogleCode as a Mercurial repository. You
+ can look at the source here:
+
+ http://code.google.com/p/micheles/source/browse/#hg%2Fdecorator
+
+Keywords: decorators generic utility
+Platform: All
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..50b39c1
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,80 @@
+Decorator module
+=================
+
+
+:Author: Michele Simionato
+:E-mail: michele.simionato@gmail.com
+:Requires: Python 2.4+
+:Download page: http://pypi.python.org/pypi/decorator
+:Installation: ``easy_install decorator``
+:License: BSD license
+
+Installation
+-------------
+
+If you are lazy, just perform
+
+$ easy_install decorator
+
+which will install just the module on your system. Notice that
+Python 3 requires the easy_install version of the distribute_ project.
+
+If you prefer to install the full distribution from source, including
+the documentation, download the tarball_, unpack it and run
+
+$ python setup.py install
+
+in the main directory, possibly as superuser.
+
+.. _tarball: http://pypi.python.org/pypi/decorator
+.. _distribute: http://packages.python.org/distribute/
+
+Testing
+--------
+
+For Python 2.5, 2.6, 2.7 run
+
+$ python documentation.py
+
+for Python 3.X run
+
+$ python documentation3.py
+
+You will see a few innocuous errors with Python 2.5, because some
+inner details such as the introduction of the ArgSpec namedtuple and
+Thread.__repr__ changed. You may safely ignore them.
+
+You cannot run the tests in Python 2.4, since there is a test using
+the with statement, but the decorator module is expected to work
+anyway (it has been used in production with Python 2.4 for years). My
+plan is to keep supporting all Python versions >= 2.4 in the core
+module, but I will keep the documentation and the tests updated only
+for the latest Python versions in both the 2.X and 3.X branches.
+
+Finally, notice that you may run into trouble if in your system there
+is an older version of the decorator module; in such a case remove the
+old version.
+
+Documentation
+--------------
+
+There are various versions of the documentation:
+
+- `HTML version (Python 2)`_
+- `PDF version (Python 2)`_
+
+- `HTML version (Python 3)`_
+- `PDF version (Python 3)`_
+
+.. _HTML version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.html
+.. _PDF version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.pdf
+.. _HTML version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.html
+.. _PDF version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.pdf
+
+Repository
+---------------
+
+The project is hosted on GoogleCode as a Mercurial repository. You
+can look at the source here:
+
+ http://code.google.com/p/micheles/source/browse/#hg%2Fdecorator
diff --git a/documentation.py b/documentation.py
new file mode 100644
index 0000000..3d5a5c0
--- /dev/null
+++ b/documentation.py
@@ -0,0 +1,1134 @@
+r"""
+The ``decorator`` module
+=============================================================
+
+:Author: Michele Simionato
+:E-mail: michele.simionato@gmail.com
+:Version: $VERSION ($DATE)
+:Requires: Python 2.4+
+:Download page: http://pypi.python.org/pypi/decorator/$VERSION
+:Installation: ``easy_install decorator``
+:License: BSD license
+
+.. contents::
+
+Introduction
+------------------------------------------------
+
+Python decorators are an interesting example of why syntactic sugar
+matters. In principle, their introduction in Python 2.4 changed
+nothing, since they do not provide any new functionality which was not
+already present in the language. In practice, their introduction has
+significantly changed the way we structure our programs in Python. I
+believe the change is for the best, and that decorators are a great
+idea since:
+
+* decorators help reducing boilerplate code;
+* decorators help separation of concerns;
+* decorators enhance readability and maintenability;
+* decorators are explicit.
+
+Still, 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.
+
+The aim of the ``decorator`` module it to simplify the usage of
+decorators for the average programmer, and to popularize decorators by
+showing various non-trivial examples. Of course, as all techniques,
+decorators can be abused (I have seen that) and you should not try to
+solve every problem with a decorator, just because you can.
+
+You may find the source code for all the examples
+discussed here in the ``documentation.py`` file, which contains
+this documentation in the form of doctests.
+
+Definitions
+------------------------------------
+
+Technically speaking, any Python object which can be called with one argument
+can be used as a decorator. However, this definition is somewhat too large
+to be really useful. It is more convenient to split the generic class of
+decorators in two subclasses:
+
++ *signature-preserving* decorators, i.e. callable objects taking a
+ function as input and returning a function *with the same
+ signature* as output;
+
++ *signature-changing* decorators, i.e. decorators that change
+ the signature of their input function, or decorators returning
+ non-callable objects.
+
+Signature-changing decorators have their use: for instance the
+builtin classes ``staticmethod`` and ``classmethod`` are in this
+group, since they take functions and return descriptor objects which
+are not functions, nor callables.
+
+However, signature-preserving decorators are more common and easier to
+reason about; in particular signature-preserving decorators can be
+composed together whereas other decorators in general cannot.
+
+Writing signature-preserving decorators from scratch is not that
+obvious, especially if one wants to define proper decorators that
+can accept functions with any signature. A simple example will clarify
+the issue.
+
+Statement of the problem
+------------------------------
+
+A very common use case for decorators is the memoization of functions.
+A ``memoize`` decorator works by caching
+the result of the function call in a dictionary, so that the next time
+the function is called with the same input parameters the result is retrieved
+from the cache and not recomputed. There are many implementations of
+``memoize`` in http://www.python.org/moin/PythonDecoratorLibrary,
+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):
+
+$$memoize_uw
+
+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
+``__name__``, ``__doc__``, ``__module__`` and ``__dict__``
+from the original function to the decorated function by hand).
+
+.. _functools.update_wrapper: http://www.python.org/doc/2.5.2/lib/module-functools.html
+
+The implementation above works in the sense that the decorator
+can accept functions with generic signatures; unfortunately this
+implementation does *not* define a signature-preserving decorator, since in
+general ``memoize_uw`` returns a function with a
+*different signature* from the original function.
+
+Consider for instance the following case:
+
+.. code-block:: python
+
+ >>> @memoize_uw
+ ... def f1(x):
+ ... time.sleep(1) # simulate some long computation
+ ... return x
+
+Here the original function takes a single argument named ``x``,
+but the decorated function takes any number of arguments and
+keyword arguments:
+
+.. code-block:: python
+
+ >>> from inspect import getargspec
+ >>> print getargspec(f1) # I am using Python 2.6+ here
+ ArgSpec(args=[], varargs='args', keywords='kw', defaults=None)
+
+This means that introspection tools such as pydoc will give
+wrong informations about the signature of ``f1``. This is pretty bad:
+pydoc will tell you that the function accepts a generic signature
+``*args``, ``**kw``, but when you try to call the function with more than an
+argument, you will get an error:
+
+.. code-block:: python
+
+ >>> f1(0, 1)
+ Traceback (most recent call last):
+ ...
+ TypeError: f1() takes exactly 1 argument (2 given)
+
+The solution
+-----------------------------------------
+
+The solution is to provide a generic factory of generators, which
+hides the complexity of making signature-preserving decorators
+from the application programmer. The ``decorator`` function in
+the ``decorator`` module is such a factory:
+
+.. code-block:: python
+
+ >>> from decorator import decorator
+
+``decorator`` takes two arguments, a caller function describing the
+functionality of the decorator and a function to be decorated; it
+returns the decorated function. The caller function must have
+signature ``(f, *args, **kw)`` and it must call the original function ``f``
+with arguments ``args`` and ``kw``, implementing the wanted capability,
+i.e. memoization in this case:
+
+$$_memoize
+
+At this point you can define your decorator as follows:
+
+$$memoize
+
+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*).
+Moreover, you are forced to pass explicitly the function you want to
+decorate to the caller function.
+
+Here is a test of usage:
+
+.. code-block:: python
+
+ >>> @memoize
+ ... def heavy_computation():
+ ... time.sleep(2)
+ ... return "done"
+
+ >>> print heavy_computation() # the first time it will take 2 seconds
+ done
+
+ >>> print heavy_computation() # the second time it will be instantaneous
+ done
+
+The signature of ``heavy_computation`` is the one you would expect:
+
+.. code-block:: python
+
+ >>> print getargspec(heavy_computation)
+ ArgSpec(args=[], varargs=None, keywords=None, defaults=None)
+
+A ``trace`` decorator
+------------------------------------------------------
+
+As an additional example, here is how you can define a trivial
+``trace`` decorator, which prints a message everytime the traced
+function is called:
+
+$$_trace
+
+$$trace
+
+Here is an example of usage:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f1(x):
+ ... pass
+
+It is immediate to verify that ``f1`` works
+
+.. code-block:: python
+
+ >>> f1(0)
+ calling f1 with args (0,), {}
+
+and it that it has the correct signature:
+
+.. code-block:: python
+
+ >>> print getargspec(f1)
+ ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
+
+The same decorator works with functions of any signature:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f(x, y=1, z=2, *args, **kw):
+ ... pass
+
+ >>> 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))
+
+That includes even functions with exotic signatures like the following:
+
+.. code-block:: python
+
+ >>> @trace
+ ... 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),))
+ >>> exotic_signature()
+ calling exotic_signature with args ((1, 2),), {}
+ 3
+
+Notice that the support for exotic signatures has been deprecated
+in Python 2.6 and removed in Python 3.0.
+
+``decorator`` is a decorator
+---------------------------------------------
+
+It may be annoying 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 just call ``decorator`` with a single argument.
+In our example you can just write ``trace = decorator(_trace)``.
+The ``decorator`` function can also 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.
+For instance, you can write directly
+
+.. code-block:: python
+
+ >>> @decorator
+ ... def trace(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. Actually ``trace`` is a ``partial``
+object which can be used as a decorator:
+
+.. code-block:: python
+
+ >>> trace # doctest: +ELLIPSIS
+ <function trace at 0x...>
+
+Here is an example of usage:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def func(): pass
+
+ >>> func()
+ calling func with args (), {}
+
+If you are using an old Python version (Python 2.4) the
+``decorator`` module provides a poor man replacement for
+``functools.partial``.
+
+``blocking``
+-------------------------------------------
+
+Sometimes one has to deal with blocking resources, such as ``stdin``, and
+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:
+
+$$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:
+
+.. code-block:: python
+
+ >>> @blocking("Please wait ...")
+ ... def read_data():
+ ... time.sleep(3) # simulate a blocking resource
+ ... return "some data"
+
+ >>> print read_data() # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1)
+ >>> print read_data() # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1)
+ >>> print read_data() # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1.1) # after 3.1 seconds, data is available
+ >>> print read_data()
+ some data
+
+``async``
+--------------------------------------------
+
+We have just seen an examples of a simple decorator factory,
+implemented as a function returning a decorator.
+For more complex situations, it is more
+convenient to implement decorator factories as classes returning
+callable objects that can be converted into decorators.
+
+As an example, here will I show a decorator
+which is able to convert a blocking function into an asynchronous
+function. The function, when called,
+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 (of course the code here
+is just an example, it is not a recommended way of doing multi-threaded
+programming). The implementation is the following:
+
+$$on_success
+$$on_failure
+$$on_closing
+$$Async
+
+The decorated function returns
+the current execution thread, which can be stored and checked later, for
+instance to verify that the thread ``.isAlive()``.
+
+Here is an example of usage. Suppose one wants to write some data to
+an external resource which can be accessed by a single user at once
+(for instance a printer). Then the access to the writing function must
+be locked. Here is a minimalistic example:
+
+.. code-block:: python
+
+ >>> async = decorator(Async(threading.Thread))
+
+ >>> datalist = [] # for simplicity the written data are stored into a list.
+
+ >>> @async
+ ... def write(data):
+ ... # append data to the datalist by locking
+ ... with threading.Lock():
+ ... time.sleep(1) # emulate some long running operation
+ ... datalist.append(data)
+ ... # other operations not requiring a lock here
+
+Each call to ``write`` will create a new writer thread, but there will
+be no synchronization problems since ``write`` is locked.
+
+.. code-block:: python
+
+ >>> write("data1") # doctest: +ELLIPSIS
+ <Thread(write-1, started...)>
+
+ >>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
+
+ >>> write("data2") # doctest: +ELLIPSIS
+ <Thread(write-2, started...)>
+
+ >>> time.sleep(2) # wait for the writers to complete
+
+ >>> print datalist
+ ['data1', 'data2']
+
+contextmanager
+-------------------------------------
+
+For a long time Python had in its standard library a ``contextmanager``
+decorator, able to convert generator functions into ``GeneratorContextManager``
+factories. For instance if you write
+
+.. code-block:: python
+
+ >>> from contextlib import contextmanager
+ >>> @contextmanager
+ ... def before_after(before, after):
+ ... print(before)
+ ... yield
+ ... print(after)
+
+
+then ``before_after`` is a factory function returning
+``GeneratorContextManager`` objects which can be used with
+the ``with`` statement:
+
+.. code-block:: python
+
+ >>> ba = before_after('BEFORE', 'AFTER')
+ >>> type(ba)
+ <class 'contextlib.GeneratorContextManager'>
+ >>> with ba:
+ ... print 'hello'
+ BEFORE
+ hello
+ AFTER
+
+Basically, it is as if the content of the ``with`` block was executed
+in the place of the ``yield`` expression in the generator function.
+In Python 3.2 ``GeneratorContextManager``
+objects were enhanced with a ``__call__``
+method, so that they can be used as decorators as in this example:
+
+.. code-block:: python
+
+ >>> @ba # doctest: +SKIP
+ ... def hello():
+ ... print 'hello'
+ ...
+ >>> hello() # doctest: +SKIP
+ BEFORE
+ hello
+ AFTER
+
+The ``ba`` decorator is basically inserting a ``with ba:``
+block inside the function.
+However there two issues: the first is that ``GeneratorContextManager``
+objects are callable only in Python 3.2, so the previous example will break
+in older versions of Python; the second is that
+``GeneratorContextManager`` objects do not preserve the signature
+of the decorated functions: the decorated ``hello`` function here will have
+a generic signature ``hello(*args, **kwargs)`` but will break when
+called with more than zero arguments. For such reasons the decorator
+module, starting with release 3.4, offers a ``decorator.contextmanager``
+decorator that solves both problems and works even in Python 2.5.
+The usage is the same and factories decorated with ``decorator.contextmanager``
+will returns instances of ``ContextManager``, a subclass of
+``contextlib.GeneratorContextManager`` with a ``__call__`` method
+acting as a signature-preserving decorator.
+
+The ``FunctionMaker`` class
+---------------------------------------------------------------
+
+You may wonder about how the functionality of the ``decorator`` module
+is implemented. The basic building block is
+a ``FunctionMaker`` class which is able to generate on the fly
+functions with a given name and signature from a function template
+passed as a string. Generally speaking, you should not need to
+resort to ``FunctionMaker`` when writing ordinary decorators, but
+it is handy in some circumstances. You will see an example shortly, in
+the implementation of a cool decorator utility (``decorator_apply``).
+
+``FunctionMaker`` provides a ``.create`` classmethod which
+takes as input the name, signature, and body of the function
+we want to generate as well as the execution environment
+were the function is generated by ``exec``. Here is an example:
+
+.. code-block:: python
+
+ >>> def f(*args, **kw): # a function with a generic signature
+ ... print args, kw
+
+ >>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f))
+ >>> f1(1,2)
+ (1, 2) {}
+
+It is important to notice that the function body is interpolated
+before being executed, so be careful with the ``%`` sign!
+
+``FunctionMaker.create`` also accepts keyword arguments and such
+arguments are attached to the resulting function. This is useful
+if you want to set some function attributes, for instance the
+docstring ``__doc__``.
+
+For debugging/introspection purposes it may be useful to see
+the source code of the generated function; to do that, just
+pass the flag ``addsource=True`` and a ``__source__`` attribute will
+be added to the generated function:
+
+.. code-block:: python
+
+ >>> f1 = FunctionMaker.create(
+ ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True)
+ >>> print f1.__source__
+ def f1(a, b):
+ f(a, b)
+ <BLANKLINE>
+
+``FunctionMaker.create`` can take as first argument a string,
+as in the examples before, or a function. This is the most common
+usage, since typically you want to decorate a pre-existing
+function. A framework author may want to use directly ``FunctionMaker.create``
+instead of ``decorator``, since it gives you direct access to the body
+of the generated function. For instance, suppose you want to instrument
+the ``__init__`` methods of a set of classes, by preserving their
+signature (such use case is not made up; this is done in SQAlchemy
+and in other frameworks). When the first argument of ``FunctionMaker.create``
+is a function, a ``FunctionMaker`` object is instantiated internally,
+with attributes ``args``, ``varargs``,
+``keywords`` and ``defaults`` which are the
+the return values of the standard library function ``inspect.getargspec``.
+For each argument in the ``args`` (which is a list of strings containing
+the names of the mandatory arguments) an attribute ``arg0``, ``arg1``,
+..., ``argN`` is also generated. Finally, there is a ``signature``
+attribute, a string with the signature of the original function.
+
+Notice that while I do not have plans
+to change or remove the functionality provided in the
+``FunctionMaker`` class, I do not guarantee that it will stay
+unchanged forever. For instance, right now I am using the traditional
+string interpolation syntax for function templates, but Python 2.6
+and Python 3.0 provide a newer interpolation syntax and I may use
+the new syntax in the future.
+On the other hand, the functionality provided by
+``decorator`` has been there from version 0.1 and it is guaranteed to
+stay there forever.
+
+Getting the source code
+---------------------------------------------------
+
+Internally ``FunctionMaker.create`` uses ``exec`` to generate the
+decorated function. Therefore
+``inspect.getsource`` will not work for decorated functions. That
+means that the usual '??' trick in IPython will give you the (right on
+the spot) message ``Dynamically generated function. No source code
+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:
+
+$$identity_dec
+
+.. code-block:: python
+
+ @identity_dec
+ def example(): pass
+
+ >>> print inspect.getsource(example)
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+ <BLANKLINE>
+
+(see bug report 1764286_ for an explanation of what is happening).
+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 ``.__wrapped__`` to the decorated function, containing
+a reference to the original function. The easy way to get
+the source code is to call ``inspect.getsource`` on the
+undecorated function:
+
+.. code-block:: python
+
+ >>> print inspect.getsource(factorial.__wrapped__)
+ @tail_recursive
+ def factorial(n, acc=1):
+ "The good old factorial"
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
+ <BLANKLINE>
+
+.. _1764286: http://bugs.python.org/issue1764286
+
+Dealing with third party decorators
+-----------------------------------------------------------------
+
+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``. You can use a
+``FunctionMaker`` to implement that functionality as follows:
+
+$$decorator_apply
+
+``decorator_apply`` sets the attribute ``.__wrapped__`` of the generated
+function to the original function, so that you can get the right
+source code.
+
+Notice that I am not providing this functionality in the ``decorator``
+module directly since I think it is best to rewrite the 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.
+
+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,
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691.
+
+$$TailRecursive
+
+Here the decorator is implemented as a class returning callable
+objects.
+
+$$tail_recursive
+
+Here is how you apply the upgraded decorator to the good old factorial:
+
+$$factorial
+
+.. code-block:: python
+
+ >>> print factorial(4)
+ 24
+
+This decorator is pretty impressive, and should give you some food for
+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
+
+$$fact
+
+(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).
+
+Caveats and limitations
+-------------------------------------------
+
+The first thing you should be aware of, it the fact that decorators
+have a performance penalty.
+The worse case is shown by the following example::
+
+ $ cat performance.sh
+ python -m timeit -s "
+ from decorator import decorator
+
+ @decorator
+ def do_nothing(func, *args, **kw):
+ return func(*args, **kw)
+
+ @do_nothing
+ def f():
+ pass
+ " "f()"
+
+ python -m timeit -s "
+ def f():
+ pass
+ " "f()"
+
+On my MacBook, using the ``do_nothing`` decorator instead of the
+plain function is more than three times slower::
+
+ $ bash performance.sh
+ 1000000 loops, best of 3: 0.995 usec per loop
+ 1000000 loops, best of 3: 0.273 usec per loop
+
+It should be noted that a real life function would probably do
+something more useful than ``f`` here, and therefore in real life the
+performance penalty could be completely negligible. As always, the
+only way to know if there is
+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:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f():
+ ... 1/0
+
+Calling ``f()`` will give you a ``ZeroDivisionError``, but since the
+function is decorated the traceback will be longer:
+
+.. code-block:: python
+
+ >>> f()
+ Traceback (most recent call last):
+ ...
+ 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
+ ZeroDivisionError: integer division or modulo by zero
+
+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
+``exec`` is *not* responsibile for the performance penalty, since is the
+called *only once* at function decoration time, and not every time
+the decorated function is called.
+
+At present, there is no clean way to avoid ``exec``. A clean solution
+would require to change the CPython implementation of functions and
+add an hook to make it possible to change their signature directly.
+That could happen in future versions of Python (see PEP 362_) and
+then the decorator module would become obsolete. However, at present,
+even in Python 3.1 it is impossible to change the function signature
+directly, therefore the ``decorator`` module is still useful.
+Actually, this is one of the main reasons why I keep maintaining
+the module and releasing new versions.
+
+.. _362: http://www.python.org/dev/peps/pep-0362
+
+In the present implementation, decorators generated by ``decorator``
+can only be used on user-defined Python functions or methods, not on generic
+callable objects, nor on built-in functions, due to limitations of the
+``inspect`` module in the standard library. Moreover, notice
+that you can decorate a method, but only before if becomes a bound or unbound
+method, i.e. inside the class.
+Here is an example of valid decoration:
+
+.. code-block:: python
+
+ >>> class C(object):
+ ... @trace
+ ... def meth(self):
+ ... pass
+
+Here is an example of invalid decoration, when the decorator in
+called too late:
+
+.. code-block:: python
+
+ >>> class C(object):
+ ... def meth(self):
+ ... pass
+ ...
+ >>> trace(C.meth)
+ Traceback (most recent call last):
+ ...
+ TypeError: You are decorating a non function: <unbound method C.meth>
+
+The solution is to extract the inner function from the unbound method:
+
+.. code-block:: python
+
+ >>> trace(C.meth.im_func) # doctest: +ELLIPSIS
+ <function meth at 0x...>
+
+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``:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f(_func_): print f
+ ...
+ Traceback (most recent call last):
+ ...
+ NameError: _func_ is overridden in
+ def f(_func_):
+ return _call_(_func_, _func_)
+
+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)``):
+
+.. code-block:: python
+
+ >>> def f(): pass # the original function
+ >>> f.attr1 = "something" # setting an attribute
+ >>> f.attr2 = "something else" # setting another attribute
+
+ >>> traced_f = trace(f) # the decorated function
+
+ >>> traced_f.attr1
+ 'something'
+ >>> traced_f.attr2 = "something different" # setting attr
+ >>> f.attr2 # the original attribute did not change
+ 'something else'
+
+Compatibility notes
+---------------------------------------------------------------
+
+Version 3.3 is the first version of the ``decorator`` module to fully
+support Python 3, including `function annotations`_. Version 3.2 was the
+first version to support Python 3 via the ``2to3`` conversion tool
+invoked in the build process by the distribute_ project, the Python
+3-compatible replacement of easy_install. The hard work (for me) has
+been converting the documentation and the doctests. This has been
+possible only after that docutils_ and pygments_ have been ported to
+Python 3.
+
+Version 3 of the ``decorator`` module do not contain any backward
+incompatible change, apart from the removal of the functions
+``get_info`` and ``new_wrapper``, which have been deprecated for
+years. ``get_info`` has been removed since it was little used and
+since it had 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``, whereas the other use
+case can be managed with the ``FunctionMaker``.
+
+There are a few changes in the documentation: I removed the
+``decorator_factory`` example, which was confusing some of my users,
+and I removed the part about exotic signatures in the Python 3
+documentation, since Python 3 does not support them.
+
+Finally ``decorator`` cannot be used as a class decorator and the
+`functionality introduced in version 2.3`_ has been removed. That
+means that in order to define decorator factories with classes you
+need to define the ``__call__`` method explicitly (no magic anymore).
+All these changes should not cause any trouble, since they were
+all rarely used features. Should you have any trouble, you can always
+downgrade to the 2.3 version.
+
+The examples shown here have been tested with Python 2.6. Python 2.4
+is also supported - of course the examples requiring the ``with``
+statement will not work there. Python 2.5 works fine, but if you
+run the examples in the interactive interpreter
+you will notice a few differences since
+``getargspec`` returns an ``ArgSpec`` namedtuple instead of a regular
+tuple. That means that running the file
+``documentation.py`` under Python 2.5 will print a few errors, but
+they are not serious.
+
+.. _functionality introduced in version 2.3: http://www.phyast.pitt.edu/~micheles/python/documentation.html#class-decorators-and-decorator-factories
+.. _function annotations: http://www.python.org/dev/peps/pep-3107/
+.. _distribute: http://packages.python.org/distribute/
+.. _docutils: http://docutils.sourceforge.net/
+.. _pygments: http://pygments.org/
+
+LICENCE
+---------------------------------------------
+
+Copyright (c) 2005-2012, Michele Simionato
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ Redistributions in bytecode form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+If you use this software and you are happy with it, consider sending me a
+note, just to gratify my ego. On the other hand, if you use this software and
+you are unhappy with it, send me a patch!
+"""
+from __future__ import with_statement
+import sys, threading, time, functools, inspect, itertools
+from decorator import *
+from functools import partial
+from setup import VERSION
+
+today = time.strftime('%Y-%m-%d')
+
+__doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today)
+
+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)), __wrapped__=func)
+
+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 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, on_success=on_success,
+ on_failure=on_failure, on_closing=on_closing):
+ self.threadfactory = threadfactory
+ self.on_success = on_success
+ self.on_failure = on_failure
+ self.on_closing = on_closing
+
+ def __call__(self, func, *args, **kw):
+ try:
+ counter = func.counter
+ except AttributeError: # instantiate the counter at the first call
+ counter = func.counter = itertools.count(1)
+ name = '%s-%s' % (func.__name__, counter.next())
+ def func_wrapper():
+ try:
+ result = func(*args, **kw)
+ except:
+ self.on_failure(sys.exc_info())
+ else:
+ return self.on_success(result)
+ finally:
+ self.on_closing()
+ thread = self.threadfactory(None, func_wrapper, name)
+ thread.start()
+ return thread
+
+def identity_dec(func):
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+ return wrapper
+
+@identity_dec
+def example(): pass
+
+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(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(f):
+ f.cache = {}
+ return decorator(_memoize, f)
+
+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)
+
+class User(object):
+ "Will just be able to see a page"
+
+class PowerUser(User):
+ "Will be able to add new pages too"
+
+class Admin(PowerUser):
+ "Will be able to delete pages too"
+
+def get_userclass():
+ return User
+
+class PermissionError(Exception):
+ pass
+
+def restricted(user_class):
+ def restricted(func, *args, **kw):
+ "Restrict access to a given class of users"
+ userclass = get_userclass()
+ if issubclass(userclass, user_class):
+ return func(*args, **kw)
+ else:
+ raise PermissionError(
+ '%s does not have the permission to run %s!'
+ % (userclass.__name__, func.__name__))
+ return decorator(restricted)
+
+class Action(object):
+ """
+ >>> a = Action()
+ >>> a.view() # ok
+ >>> a.insert() # err
+ Traceback (most recent call last):
+ ...
+ PermissionError: User does not have the permission to run insert!
+
+ """
+ @restricted(User)
+ def view(self):
+ pass
+
+ @restricted(PowerUser)
+ def insert(self):
+ pass
+
+ @restricted(Admin)
+ def delete(self):
+ pass
+
+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
+
+def tail_recursive(func):
+ return decorator_apply(TailRecursive, func)
+
+@tail_recursive
+def factorial(n, acc=1):
+ "The good old factorial"
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
+
+def fact(n): # this is not tail-recursive
+ if n == 0: return 1
+ return n * fact(n-1)
+
+def a_test_for_pylons():
+ """
+ In version 3.1.0 decorator(caller) returned a nameless partial
+ object, thus breaking Pylons. That must not happen again.
+
+ >>> decorator(_memoize).__name__
+ '_memoize'
+
+ Here is another bug of version 3.1.1 missing the docstring to avoid:
+
+ >>> factorial.__doc__
+ 'The good old factorial'
+ """
+
+@contextmanager
+def before_after(before, after):
+ print(before)
+ yield
+ print(after)
+
+ba = before_after('BEFORE', 'AFTER') # ContextManager instance
+
+@ba
+def hello(user):
+ """
+ >>> ba.__class__.__name__
+ 'ContextManager'
+ >>> hello('michele')
+ BEFORE
+ hello michele
+ AFTER
+ """
+ print('hello %s' % user)
+
+if __name__ == '__main__':
+ import doctest; doctest.testmod()
diff --git a/documentation3.py b/documentation3.py
new file mode 100644
index 0000000..bd86cc6
--- /dev/null
+++ b/documentation3.py
@@ -0,0 +1,1170 @@
+r"""
+The ``decorator`` module
+=============================================================
+
+:Author: Michele Simionato
+:E-mail: michele.simionato@gmail.com
+:Version: $VERSION ($DATE)
+:Requires: Python 2.4+
+:Download page: http://pypi.python.org/pypi/decorator/$VERSION
+:Installation: ``easy_install decorator``
+:License: BSD license
+
+.. contents::
+
+Introduction
+------------------------------------------------
+
+Python decorators are an interesting example of why syntactic sugar
+matters. In principle, their introduction in Python 2.4 changed
+nothing, since they do not provide any new functionality which was not
+already present in the language. In practice, their introduction has
+significantly changed the way we structure our programs in Python. I
+believe the change is for the best, and that decorators are a great
+idea since:
+
+* decorators help reducing boilerplate code;
+* decorators help separation of concerns;
+* decorators enhance readability and maintenability;
+* decorators are explicit.
+
+Still, 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.
+
+The aim of the ``decorator`` module it to simplify the usage of
+decorators for the average programmer, and to popularize decorators by
+showing various non-trivial examples. Of course, as all techniques,
+decorators can be abused (I have seen that) and you should not try to
+solve every problem with a decorator, just because you can.
+
+You may find the source code for all the examples
+discussed here in the ``documentation.py`` file, which contains
+this documentation in the form of doctests.
+
+Definitions
+------------------------------------
+
+Technically speaking, any Python object which can be called with one argument
+can be used as a decorator. However, this definition is somewhat too large
+to be really useful. It is more convenient to split the generic class of
+decorators in two subclasses:
+
++ *signature-preserving* decorators, i.e. callable objects taking a
+ function as input and returning a function *with the same
+ signature* as output;
+
++ *signature-changing* decorators, i.e. decorators that change
+ the signature of their input function, or decorators returning
+ non-callable objects.
+
+Signature-changing decorators have their use: for instance the
+builtin classes ``staticmethod`` and ``classmethod`` are in this
+group, since they take functions and return descriptor objects which
+are not functions, nor callables.
+
+However, signature-preserving decorators are more common and easier to
+reason about; in particular signature-preserving decorators can be
+composed together whereas other decorators in general cannot.
+
+Writing signature-preserving decorators from scratch is not that
+obvious, especially if one wants to define proper decorators that
+can accept functions with any signature. A simple example will clarify
+the issue.
+
+Statement of the problem
+------------------------------
+
+A very common use case for decorators is the memoization of functions.
+A ``memoize`` decorator works by caching
+the result of the function call in a dictionary, so that the next time
+the function is called with the same input parameters the result is retrieved
+from the cache and not recomputed. There are many implementations of
+``memoize`` in http://www.python.org/moin/PythonDecoratorLibrary,
+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):
+
+$$memoize_uw
+
+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
+``__name__``, ``__doc__``, ``__module__`` and ``__dict__``
+from the original function to the decorated function by hand).
+
+.. _functools.update_wrapper: http://www.python.org/doc/2.5.2/lib/module-functools.html
+
+The implementation above works in the sense that the decorator
+can accept functions with generic signatures; unfortunately this
+implementation does *not* define a signature-preserving decorator, since in
+general ``memoize_uw`` returns a function with a
+*different signature* from the original function.
+
+Consider for instance the following case:
+
+.. code-block:: python
+
+ >>> @memoize_uw
+ ... def f1(x):
+ ... time.sleep(1) # simulate some long computation
+ ... return x
+
+Here the original function takes a single argument named ``x``,
+but the decorated function takes any number of arguments and
+keyword arguments:
+
+.. code-block:: python
+
+ >>> from inspect import getargspec
+ >>> print(getargspec(f1))
+ ArgSpec(args=[], varargs='args', keywords='kw', defaults=None)
+
+This means that introspection tools such as pydoc will give
+wrong informations about the signature of ``f1``. This is pretty bad:
+pydoc will tell you that the function accepts a generic signature
+``*args``, ``**kw``, but when you try to call the function with more than an
+argument, you will get an error:
+
+.. code-block:: python
+
+ >>> f1(0, 1) # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ TypeError: f1() takes exactly 1 positional argument (2 given)
+
+The solution
+-----------------------------------------
+
+The solution is to provide a generic factory of generators, which
+hides the complexity of making signature-preserving decorators
+from the application programmer. The ``decorator`` function in
+the ``decorator`` module is such a factory:
+
+.. code-block:: python
+
+ >>> from decorator import decorator
+
+``decorator`` takes two arguments, a caller function describing the
+functionality of the decorator and a function to be decorated; it
+returns the decorated function. The caller function must have
+signature ``(f, *args, **kw)`` and it must call the original function ``f``
+with arguments ``args`` and ``kw``, implementing the wanted capability,
+i.e. memoization in this case:
+
+$$_memoize
+
+At this point you can define your decorator as follows:
+
+$$memoize
+
+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*).
+Moreover, you are forced to pass explicitly the function you want to
+decorate to the caller function.
+
+Here is a test of usage:
+
+.. code-block:: python
+
+ >>> @memoize
+ ... def heavy_computation():
+ ... time.sleep(2)
+ ... return "done"
+
+ >>> print(heavy_computation()) # the first time it will take 2 seconds
+ done
+
+ >>> print(heavy_computation()) # the second time it will be instantaneous
+ done
+
+The signature of ``heavy_computation`` is the one you would expect:
+
+.. code-block:: python
+
+ >>> print(getargspec(heavy_computation))
+ ArgSpec(args=[], varargs=None, keywords=None, defaults=None)
+
+A ``trace`` decorator
+------------------------------------------------------
+
+As an additional example, here is how you can define a trivial
+``trace`` decorator, which prints a message everytime the traced
+function is called:
+
+$$_trace
+
+$$trace
+
+Here is an example of usage:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f1(x):
+ ... pass
+
+It is immediate to verify that ``f1`` works
+
+.. code-block:: python
+
+ >>> f1(0)
+ calling f1 with args (0,), {}
+
+and it that it has the correct signature:
+
+.. code-block:: python
+
+ >>> print(getargspec(f1))
+ ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
+
+The same decorator works with functions of any signature:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f(x, y=1, z=2, *args, **kw):
+ ... pass
+
+ >>> 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))
+
+Function annotations
+---------------------------------------------
+
+Python 3 introduced the concept of `function annotations`_,i.e. the ability
+to annotate the signature of a function with additional information,
+stored in a dictionary named ``__annotations__``. The decorator module,
+starting from release 3.3, is able to understand and to preserve the
+annotations. Here is an example:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f(x: 'the first argument', y: 'default argument'=1, z=2,
+ ... *args: 'varargs', **kw: 'kwargs'):
+ ... pass
+
+In order to introspect functions with annotations, one needs the
+utility ``inspect.getfullargspec``, new in Python 3:
+
+.. code-block:: python
+
+ >>> from inspect import getfullargspec
+ >>> argspec = getfullargspec(f)
+ >>> argspec.args
+ ['x', 'y', 'z']
+ >>> argspec.varargs
+ 'args'
+ >>> argspec.varkw
+ 'kw'
+ >>> argspec.defaults
+ (1, 2)
+ >>> argspec.kwonlyargs
+ []
+ >>> argspec.kwonlydefaults
+
+You can also check that the ``__annotations__`` dictionary is preserved:
+
+.. code-block:: python
+
+ >>> f.__annotations__ == f.__wrapped__.__annotations__
+ True
+
+Depending on the version of the decorator module, the two dictionaries can
+be the same object or not: you cannot rely on object identity, but you can
+rely on the content being the same.
+
+``decorator`` is a decorator
+---------------------------------------------
+
+It may be annoying 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 just call ``decorator`` with a single argument.
+In our example you can just write ``trace = decorator(_trace)``.
+The ``decorator`` function can also 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.
+For instance, you can write directly
+
+.. code-block:: python
+
+ >>> @decorator
+ ... def trace(f, *args, **kw):
+ ... kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw))
+ ... print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr))
+ ... return f(*args, **kw)
+
+and now ``trace`` will be a decorator. Actually ``trace`` is a ``partial``
+object which can be used as a decorator:
+
+.. code-block:: python
+
+ >>> trace # doctest: +ELLIPSIS
+ <function trace at 0x...>
+
+Here is an example of usage:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def func(): pass
+
+ >>> func()
+ calling func with args (), {}
+
+If you are using an old Python version (Python 2.4) the
+``decorator`` module provides a poor man replacement for
+``functools.partial``.
+
+``blocking``
+-------------------------------------------
+
+Sometimes one has to deal with blocking resources, such as ``stdin``, and
+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:
+
+$$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:
+
+.. code-block:: python
+
+ >>> @blocking("Please wait ...")
+ ... def read_data():
+ ... time.sleep(3) # simulate a blocking resource
+ ... return "some data"
+
+ >>> print(read_data()) # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1)
+ >>> print(read_data()) # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1)
+ >>> print(read_data()) # data is not available yet
+ Please wait ...
+
+ >>> time.sleep(1.1) # after 3.1 seconds, data is available
+ >>> print(read_data())
+ some data
+
+``async``
+--------------------------------------------
+
+We have just seen an examples of a simple decorator factory,
+implemented as a function returning a decorator.
+For more complex situations, it is more
+convenient to implement decorator factories as classes returning
+callable objects that can be converted into decorators.
+
+As an example, here will I show a decorator
+which is able to convert a blocking function into an asynchronous
+function. The function, when called,
+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 (of course the code here
+is just an example, it is not a recommended way of doing multi-threaded
+programming). The implementation is the following:
+
+$$on_success
+$$on_failure
+$$on_closing
+$$Async
+
+The decorated function returns
+the current execution thread, which can be stored and checked later, for
+instance to verify that the thread ``.isAlive()``.
+
+Here is an example of usage. Suppose one wants to write some data to
+an external resource which can be accessed by a single user at once
+(for instance a printer). Then the access to the writing function must
+be locked. Here is a minimalistic example:
+
+.. code-block:: python
+
+ >>> async = decorator(Async(threading.Thread))
+
+ >>> datalist = [] # for simplicity the written data are stored into a list.
+
+ >>> @async
+ ... def write(data):
+ ... # append data to the datalist by locking
+ ... with threading.Lock():
+ ... time.sleep(1) # emulate some long running operation
+ ... datalist.append(data)
+ ... # other operations not requiring a lock here
+
+Each call to ``write`` will create a new writer thread, but there will
+be no synchronization problems since ``write`` is locked.
+
+.. code-block:: python
+
+ >>> write("data1") # doctest: +ELLIPSIS
+ <Thread(write-1, started...)>
+
+ >>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
+
+ >>> write("data2") # doctest: +ELLIPSIS
+ <Thread(write-2, started...)>
+
+ >>> time.sleep(2) # wait for the writers to complete
+
+ >>> print(datalist)
+ ['data1', 'data2']
+
+contextmanager
+-------------------------------------
+
+For a long time Python had in its standard library a ``contextmanager``
+decorator, able to convert generator functions into ``_GeneratorContextManager``
+factories. For instance if you write
+
+.. code-block:: python
+
+ >>> from contextlib import contextmanager
+ >>> @contextmanager
+ ... def before_after(before, after):
+ ... print(before)
+ ... yield
+ ... print(after)
+
+
+then ``before_after`` is a factory function returning
+``_GeneratorContextManager`` objects which can be used with
+the ``with`` statement:
+
+.. code-block:: python
+
+ >>> ba = before_after('BEFORE', 'AFTER')
+ >>> type(ba)
+ <class 'contextlib._GeneratorContextManager'>
+ >>> with ba:
+ ... print('hello')
+ BEFORE
+ hello
+ AFTER
+
+Basically, it is as if the content of the ``with`` block was executed
+in the place of the ``yield`` expression in the generator function.
+In Python 3.2 ``_GeneratorContextManager``
+objects were enhanced with a ``__call__``
+method, so that they can be used as decorators as in this example:
+
+.. code-block:: python
+
+ >>> @ba # doctest: +SKIP
+ ... def hello():
+ ... print('hello')
+ ...
+ >>> hello() # doctest: +SKIP
+ BEFORE
+ hello
+ AFTER
+
+The ``ba`` decorator is basically inserting a ``with ba:``
+block inside the function.
+However there two issues: the first is that ``_GeneratorContextManager``
+objects are callable only in Python 3.2, so the previous example will break
+in older versions of Python; the second is that
+``_GeneratorContextManager`` objects do not preserve the signature
+of the decorated functions: the decorated ``hello`` function here will have
+a generic signature ``hello(*args, **kwargs)`` but will break when
+called with more than zero arguments. For such reasons the decorator
+module, starting with release 3.4, offers a ``decorator.contextmanager``
+decorator that solves both problems and works even in Python 2.5.
+The usage is the same and factories decorated with ``decorator.contextmanager``
+will returns instances of ``ContextManager``, a subclass of
+``contextlib._GeneratorContextManager`` with a ``__call__`` method
+acting as a signature-preserving decorator.
+
+The ``FunctionMaker`` class
+---------------------------------------------------------------
+
+You may wonder about how the functionality of the ``decorator`` module
+is implemented. The basic building block is
+a ``FunctionMaker`` class which is able to generate on the fly
+functions with a given name and signature from a function template
+passed as a string. Generally speaking, you should not need to
+resort to ``FunctionMaker`` when writing ordinary decorators, but
+it is handy in some circumstances. You will see an example shortly, in
+the implementation of a cool decorator utility (``decorator_apply``).
+
+``FunctionMaker`` provides a ``.create`` classmethod which
+takes as input the name, signature, and body of the function
+we want to generate as well as the execution environment
+were the function is generated by ``exec``. Here is an example:
+
+.. code-block:: python
+
+ >>> def f(*args, **kw): # a function with a generic signature
+ ... print(args, kw)
+
+ >>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f))
+ >>> f1(1,2)
+ (1, 2) {}
+
+It is important to notice that the function body is interpolated
+before being executed, so be careful with the ``%`` sign!
+
+``FunctionMaker.create`` also accepts keyword arguments and such
+arguments are attached to the resulting function. This is useful
+if you want to set some function attributes, for instance the
+docstring ``__doc__``.
+
+For debugging/introspection purposes it may be useful to see
+the source code of the generated function; to do that, just
+pass the flag ``addsource=True`` and a ``__source__`` attribute will
+be added to the generated function:
+
+.. code-block:: python
+
+ >>> f1 = FunctionMaker.create(
+ ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True)
+ >>> print(f1.__source__)
+ def f1(a, b):
+ f(a, b)
+ <BLANKLINE>
+
+``FunctionMaker.create`` can take as first argument a string,
+as in the examples before, or a function. This is the most common
+usage, since typically you want to decorate a pre-existing
+function. A framework author may want to use directly ``FunctionMaker.create``
+instead of ``decorator``, since it gives you direct access to the body
+of the generated function. For instance, suppose you want to instrument
+the ``__init__`` methods of a set of classes, by preserving their
+signature (such use case is not made up; this is done in SQAlchemy
+and in other frameworks). When the first argument of ``FunctionMaker.create``
+is a function, a ``FunctionMaker`` object is instantiated internally,
+with attributes ``args``, ``varargs``,
+``keywords`` and ``defaults`` which are the
+the return values of the standard library function ``inspect.getargspec``.
+For each argument in the ``args`` (which is a list of strings containing
+the names of the mandatory arguments) an attribute ``arg0``, ``arg1``,
+..., ``argN`` is also generated. Finally, there is a ``signature``
+attribute, a string with the signature of the original function.
+
+Notice that while I do not have plans
+to change or remove the functionality provided in the
+``FunctionMaker`` class, I do not guarantee that it will stay
+unchanged forever. For instance, right now I am using the traditional
+string interpolation syntax for function templates, but Python 2.6
+and Python 3.0 provide a newer interpolation syntax and I may use
+the new syntax in the future.
+On the other hand, the functionality provided by
+``decorator`` has been there from version 0.1 and it is guaranteed to
+stay there forever.
+
+Getting the source code
+---------------------------------------------------
+
+Internally ``FunctionMaker.create`` uses ``exec`` to generate the
+decorated function. Therefore
+``inspect.getsource`` will not work for decorated functions. That
+means that the usual '??' trick in IPython will give you the (right on
+the spot) message ``Dynamically generated function. No source code
+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:
+
+$$identity_dec
+
+.. code-block:: python
+
+ @identity_dec
+ def example(): pass
+
+ >>> print(inspect.getsource(example))
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+ <BLANKLINE>
+
+(see bug report 1764286_ for an explanation of what is happening).
+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 ``.__wrapped__`` to the decorated function, containing
+a reference to the original function. The easy way to get
+the source code is to call ``inspect.getsource`` on the
+undecorated function:
+
+.. code-block:: python
+
+ >>> print(inspect.getsource(factorial.__wrapped__))
+ @tail_recursive
+ def factorial(n, acc=1):
+ "The good old factorial"
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
+ <BLANKLINE>
+
+.. _1764286: http://bugs.python.org/issue1764286
+
+Dealing with third party decorators
+-----------------------------------------------------------------
+
+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``. You can use a
+``FunctionMaker`` to implement that functionality as follows:
+
+$$decorator_apply
+
+``decorator_apply`` sets the attribute ``.__wrapped__`` of the generated
+function to the original function, so that you can get the right
+source code.
+
+Notice that I am not providing this functionality in the ``decorator``
+module directly since I think it is best to rewrite the 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.
+
+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,
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691.
+
+$$TailRecursive
+
+Here the decorator is implemented as a class returning callable
+objects.
+
+$$tail_recursive
+
+Here is how you apply the upgraded decorator to the good old factorial:
+
+$$factorial
+
+.. code-block:: python
+
+ >>> print(factorial(4))
+ 24
+
+This decorator is pretty impressive, and should give you some food for
+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
+
+$$fact
+
+(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).
+
+Caveats and limitations
+-------------------------------------------
+
+The first thing you should be aware of, it the fact that decorators
+have a performance penalty.
+The worse case is shown by the following example::
+
+ $ cat performance.sh
+ python3 -m timeit -s "
+ from decorator import decorator
+
+ @decorator
+ def do_nothing(func, *args, **kw):
+ return func(*args, **kw)
+
+ @do_nothing
+ def f():
+ pass
+ " "f()"
+
+ python3 -m timeit -s "
+ def f():
+ pass
+ " "f()"
+
+On my MacBook, using the ``do_nothing`` decorator instead of the
+plain function is more than three times slower::
+
+ $ bash performance.sh
+ 1000000 loops, best of 3: 0.669 usec per loop
+ 1000000 loops, best of 3: 0.181 usec per loop
+
+It should be noted that a real life function would probably do
+something more useful than ``f`` here, and therefore in real life the
+performance penalty could be completely negligible. As always, the
+only way to know if there is
+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:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f():
+ ... 1/0
+
+Calling ``f()`` will give you a ``ZeroDivisionError``, but since the
+function is decorated the traceback will be longer:
+
+.. code-block:: python
+
+ >>> f() # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ File "<string>", line 2, in f
+ File "<doctest __main__[22]>", line 4, in trace
+ return f(*args, **kw)
+ File "<doctest __main__[51]>", line 3, in f
+ 1/0
+ ZeroDivisionError: ...
+
+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
+``exec`` is *not* responsibile for the performance penalty, since is the
+called *only once* at function decoration time, and not every time
+the decorated function is called.
+
+At present, there is no clean way to avoid ``exec``. A clean solution
+would require to change the CPython implementation of functions and
+add an hook to make it possible to change their signature directly.
+That could happen in future versions of Python (see PEP 362_) and
+then the decorator module would become obsolete. However, at present,
+even in Python 3.2 it is impossible to change the function signature
+directly, therefore the ``decorator`` module is still useful.
+Actually, this is one of the main reasons why I keep maintaining
+the module and releasing new versions.
+
+.. _362: http://www.python.org/dev/peps/pep-0362
+
+In the present implementation, decorators generated by ``decorator``
+can only be used on user-defined Python functions or methods, not on generic
+callable objects, nor on built-in functions, due to limitations of the
+``inspect`` module in the standard library.
+
+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``:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def f(_func_): print(f)
+ ...
+ Traceback (most recent call last):
+ ...
+ NameError: _func_ is overridden in
+ def f(_func_):
+ return _call_(_func_, _func_)
+
+Finally, the implementation is such that the decorated function contains
+a *copy* of the original function dictionary
+(``vars(decorated_f) is not vars(f)``):
+
+.. code-block:: python
+
+ >>> def f(): pass # the original function
+ >>> f.attr1 = "something" # setting an attribute
+ >>> f.attr2 = "something else" # setting another attribute
+
+ >>> traced_f = trace(f) # the decorated function
+
+ >>> traced_f.attr1
+ 'something'
+ >>> traced_f.attr2 = "something different" # setting attr
+ >>> f.attr2 # the original attribute did not change
+ 'something else'
+
+Compatibility notes
+---------------------------------------------------------------
+
+Version 3.3 is the first version of the ``decorator`` module to fully
+support Python 3, including `function annotations`_. Version 3.2 was the
+first version to support Python 3 via the ``2to3`` conversion tool
+invoked in the build process by the distribute_ project, the Python
+3-compatible replacement of easy_install. The hard work (for me) has
+been converting the documentation and the doctests. This has been
+possible only after that docutils_ and pygments_ have been ported to
+Python 3.
+
+Version 3 of the ``decorator`` module do not contain any backward
+incompatible change, apart from the removal of the functions
+``get_info`` and ``new_wrapper``, which have been deprecated for
+years. ``get_info`` has been removed since it was little used and
+since it had 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``, whereas the other use
+case can be managed with the ``FunctionMaker``.
+
+There are a few changes in the documentation: I removed the
+``decorator_factory`` example, which was confusing some of my users,
+and I removed the part about exotic signatures in the Python 3
+documentation, since Python 3 does not support them.
+
+Finally ``decorator`` cannot be used as a class decorator and the
+`functionality introduced in version 2.3`_ has been removed. That
+means that in order to define decorator factories with classes you
+need to define the ``__call__`` method explicitly (no magic anymore).
+All these changes should not cause any trouble, since they were
+all rarely used features. Should you have any trouble, you can always
+downgrade to the 2.3 version.
+
+The examples shown here have been tested with Python 2.6. Python 2.4
+is also supported - of course the examples requiring the ``with``
+statement will not work there. Python 2.5 works fine, but if you
+run the examples in the interactive interpreter
+you will notice a few differences since
+``getargspec`` returns an ``ArgSpec`` namedtuple instead of a regular
+tuple. That means that running the file
+``documentation.py`` under Python 2.5 will print a few errors, but
+they are not serious.
+
+.. _functionality introduced in version 2.3: http://www.phyast.pitt.edu/~micheles/python/documentation.html#class-decorators-and-decorator-factories
+.. _function annotations: http://www.python.org/dev/peps/pep-3107/
+.. _distribute: http://packages.python.org/distribute/
+.. _docutils: http://docutils.sourceforge.net/
+.. _pygments: http://pygments.org/
+
+LICENCE
+---------------------------------------------
+
+Copyright (c) 2005-2012, Michele Simionato
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ Redistributions in bytecode form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+If you use this software and you are happy with it, consider sending me a
+note, just to gratify my ego. On the other hand, if you use this software and
+you are unhappy with it, send me a patch!
+"""
+from __future__ import with_statement
+import sys, threading, time, functools, inspect, itertools
+from decorator import *
+from functools import partial
+from setup import VERSION
+
+today = time.strftime('%Y-%m-%d')
+
+__doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today)
+
+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)), __wrapped__=func)
+
+def _trace(f, *args, **kw):
+ kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw))
+ print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr))
+ return f(*args, **kw)
+
+def trace(f):
+ return decorator(_trace, f)
+
+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, on_success=on_success,
+ on_failure=on_failure, on_closing=on_closing):
+ self.threadfactory = threadfactory
+ self.on_success = on_success
+ self.on_failure = on_failure
+ self.on_closing = on_closing
+
+ def __call__(self, func, *args, **kw):
+ try:
+ counter = func.counter
+ except AttributeError: # instantiate the counter at the first call
+ counter = func.counter = itertools.count(1)
+ name = '%s-%s' % (func.__name__, next(counter))
+ def func_wrapper():
+ try:
+ result = func(*args, **kw)
+ except:
+ self.on_failure(sys.exc_info())
+ else:
+ return self.on_success(result)
+ finally:
+ self.on_closing()
+ thread = self.threadfactory(None, func_wrapper, name)
+ thread.start()
+ return thread
+
+def identity_dec(func):
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+ return wrapper
+
+@identity_dec
+def example(): pass
+
+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(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(f):
+ f.cache = {}
+ return decorator(_memoize, f)
+
+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)
+
+class User(object):
+ "Will just be able to see a page"
+
+class PowerUser(User):
+ "Will be able to add new pages too"
+
+class Admin(PowerUser):
+ "Will be able to delete pages too"
+
+def get_userclass():
+ return User
+
+class PermissionError(Exception):
+ pass
+
+def restricted(user_class):
+ def restricted(func, *args, **kw):
+ "Restrict access to a given class of users"
+ userclass = get_userclass()
+ if issubclass(userclass, user_class):
+ return func(*args, **kw)
+ else:
+ raise PermissionError(
+ '%s does not have the permission to run %s!'
+ % (userclass.__name__, func.__name__))
+ return decorator(restricted)
+
+class Action(object):
+ """
+ >>> a = Action()
+ >>> a.view() # ok
+ >>> a.insert() # err
+ Traceback (most recent call last):
+ ...
+ PermissionError: User does not have the permission to run insert!
+
+ """
+ @restricted(User)
+ def view(self):
+ pass
+
+ @restricted(PowerUser)
+ def insert(self):
+ pass
+
+ @restricted(Admin)
+ def delete(self):
+ pass
+
+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
+
+def tail_recursive(func):
+ return decorator_apply(TailRecursive, func)
+
+@tail_recursive
+def factorial(n, acc=1):
+ "The good old factorial"
+ if n == 0: return acc
+ return factorial(n-1, n*acc)
+
+def fact(n): # this is not tail-recursive
+ if n == 0: return 1
+ return n * fact(n-1)
+
+def a_test_for_pylons():
+ """
+ In version 3.1.0 decorator(caller) returned a nameless partial
+ object, thus breaking Pylons. That must not happen again.
+
+ >>> decorator(_memoize).__name__
+ '_memoize'
+
+ Here is another bug of version 3.1.1 missing the docstring:
+
+ >>> factorial.__doc__
+ 'The good old factorial'
+ """
+
+def test_kwonlydefaults():
+ """
+ >>> @trace
+ ... def f(arg, defarg=1, *args, kwonly=2): pass
+ ...
+ >>> f.__kwdefaults__
+ {'kwonly': 2}
+ """
+
+def test_kwonlyargs():
+ """
+ >>> @trace
+ ... def func(a, b, *args, y=2, z=3, **kwargs):
+ ... return y, z
+ ...
+ >>> func('a', 'b', 'c', 'd', 'e', y='y', z='z', cat='dog')
+ calling func with args ('a', 'b', 'c', 'd', 'e'), {'cat': 'dog', 'y': 'y', 'z': 'z'}
+ ('y', 'z')
+ """
+
+def test_kwonly_no_args():
+ """# this was broken with decorator 3.3.3
+ >>> @trace
+ ... def f(**kw): pass
+ ...
+ >>> f()
+ calling f with args (), {}
+ """
+def test_kwonly_star_notation():
+ """
+ >>> @trace
+ ... def f(*, a=1, **kw): pass
+ ...
+ >>> inspect.getfullargspec(f)
+ FullArgSpec(args=[], varargs=None, varkw='kw', defaults=None, kwonlyargs=['a'], kwonlydefaults={'a': 1}, annotations={})
+ """
+
+@contextmanager
+def before_after(before, after):
+ print(before)
+ yield
+ print(after)
+
+ba = before_after('BEFORE', 'AFTER') # ContextManager instance
+
+@ba
+def hello(user):
+ """
+ >>> ba.__class__.__name__
+ 'ContextManager'
+ >>> hello('michele')
+ BEFORE
+ hello michele
+ AFTER
+ """
+ print('hello %s' % user)
+
+if __name__ == '__main__':
+ import doctest; doctest.testmod()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..ebbec92
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_svn_revision = 0
+tag_date = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..8190acd
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,41 @@
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+import os.path
+
+def getversion(fname):
+ """Get the __version__ reading the file: works both in Python 2.X and 3.X,
+ whereas direct importing would break in Python 3.X with a syntax error"""
+ for line in open(fname):
+ if line.startswith('__version__'):
+ return eval(line[13:])
+ raise NameError('Missing __version__ in decorator.py')
+
+VERSION = getversion(
+ os.path.join(os.path.dirname(__file__), 'src/decorator.py'))
+
+if __name__ == '__main__':
+ setup(name='decorator',
+ version=VERSION,
+ description='Better living through Python with decorators',
+ long_description=open('README.txt').read(),
+ author='Michele Simionato',
+ author_email='michele.simionato@gmail.com',
+ url='http://pypi.python.org/pypi/decorator',
+ license="BSD License",
+ package_dir = {'': 'src'},
+ py_modules = ['decorator'],
+ keywords="decorators generic utility",
+ platforms=["All"],
+ classifiers=['Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities'],
+ use_2to3=True,
+ zip_safe=False)
diff --git a/src/decorator.egg-info/PKG-INFO b/src/decorator.egg-info/PKG-INFO
new file mode 100644
index 0000000..8491af5
--- /dev/null
+++ b/src/decorator.egg-info/PKG-INFO
@@ -0,0 +1,100 @@
+Metadata-Version: 1.1
+Name: decorator
+Version: 3.4.0
+Summary: Better living through Python with decorators
+Home-page: http://pypi.python.org/pypi/decorator
+Author: Michele Simionato
+Author-email: michele.simionato@gmail.com
+License: BSD License
+Description: Decorator module
+ =================
+
+
+ :Author: Michele Simionato
+ :E-mail: michele.simionato@gmail.com
+ :Requires: Python 2.4+
+ :Download page: http://pypi.python.org/pypi/decorator
+ :Installation: ``easy_install decorator``
+ :License: BSD license
+
+ Installation
+ -------------
+
+ If you are lazy, just perform
+
+ $ easy_install decorator
+
+ which will install just the module on your system. Notice that
+ Python 3 requires the easy_install version of the distribute_ project.
+
+ If you prefer to install the full distribution from source, including
+ the documentation, download the tarball_, unpack it and run
+
+ $ python setup.py install
+
+ in the main directory, possibly as superuser.
+
+ .. _tarball: http://pypi.python.org/pypi/decorator
+ .. _distribute: http://packages.python.org/distribute/
+
+ Testing
+ --------
+
+ For Python 2.5, 2.6, 2.7 run
+
+ $ python documentation.py
+
+ for Python 3.X run
+
+ $ python documentation3.py
+
+ You will see a few innocuous errors with Python 2.5, because some
+ inner details such as the introduction of the ArgSpec namedtuple and
+ Thread.__repr__ changed. You may safely ignore them.
+
+ You cannot run the tests in Python 2.4, since there is a test using
+ the with statement, but the decorator module is expected to work
+ anyway (it has been used in production with Python 2.4 for years). My
+ plan is to keep supporting all Python versions >= 2.4 in the core
+ module, but I will keep the documentation and the tests updated only
+ for the latest Python versions in both the 2.X and 3.X branches.
+
+ Finally, notice that you may run into trouble if in your system there
+ is an older version of the decorator module; in such a case remove the
+ old version.
+
+ Documentation
+ --------------
+
+ There are various versions of the documentation:
+
+ - `HTML version (Python 2)`_
+ - `PDF version (Python 2)`_
+
+ - `HTML version (Python 3)`_
+ - `PDF version (Python 3)`_
+
+ .. _HTML version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.html
+ .. _PDF version (Python 2): http://micheles.googlecode.com/hg/decorator/documentation.pdf
+ .. _HTML version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.html
+ .. _PDF version (Python 3): http://micheles.googlecode.com/hg/decorator/documentation3.pdf
+
+ Repository
+ ---------------
+
+ The project is hosted on GoogleCode as a Mercurial repository. You
+ can look at the source here:
+
+ http://code.google.com/p/micheles/source/browse/#hg%2Fdecorator
+
+Keywords: decorators generic utility
+Platform: All
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
diff --git a/src/decorator.egg-info/SOURCES.txt b/src/decorator.egg-info/SOURCES.txt
new file mode 100644
index 0000000..4b7a116
--- /dev/null
+++ b/src/decorator.egg-info/SOURCES.txt
@@ -0,0 +1,11 @@
+MANIFEST.in
+README.txt
+documentation.py
+documentation3.py
+setup.py
+src/decorator.py
+src/decorator.egg-info/PKG-INFO
+src/decorator.egg-info/SOURCES.txt
+src/decorator.egg-info/dependency_links.txt
+src/decorator.egg-info/not-zip-safe
+src/decorator.egg-info/top_level.txt \ No newline at end of file
diff --git a/src/decorator.egg-info/dependency_links.txt b/src/decorator.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/decorator.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/src/decorator.egg-info/not-zip-safe b/src/decorator.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/decorator.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/src/decorator.egg-info/top_level.txt b/src/decorator.egg-info/top_level.txt
new file mode 100644
index 0000000..3fe18a4
--- /dev/null
+++ b/src/decorator.egg-info/top_level.txt
@@ -0,0 +1 @@
+decorator
diff --git a/src/decorator.py b/src/decorator.py
new file mode 100644
index 0000000..e003914
--- /dev/null
+++ b/src/decorator.py
@@ -0,0 +1,251 @@
+########################## LICENCE ###############################
+
+# Copyright (c) 2005-2012, Michele Simionato
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+
+# Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# Redistributions in bytecode form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+
+"""
+Decorator module, see http://pypi.python.org/pypi/decorator
+for the documentation.
+"""
+
+__version__ = '3.4.0'
+
+__all__ = ["decorator", "FunctionMaker", "contextmanager"]
+
+import sys, re, inspect
+if sys.version >= '3':
+ from inspect import getfullargspec
+ def get_init(cls):
+ return cls.__init__
+else:
+ class getfullargspec(object):
+ "A quick and dirty replacement for getfullargspec for Python 2.X"
+ def __init__(self, f):
+ self.args, self.varargs, self.varkw, self.defaults = \
+ inspect.getargspec(f)
+ self.kwonlyargs = []
+ self.kwonlydefaults = None
+ def __iter__(self):
+ yield self.args
+ yield self.varargs
+ yield self.varkw
+ yield self.defaults
+ def get_init(cls):
+ return cls.__init__.im_func
+
+DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
+
+# basic functionality
+class FunctionMaker(object):
+ """
+ An object with the ability to create functions with a given signature.
+ It has attributes name, doc, module, signature, defaults, dict and
+ methods update and make.
+ """
+ def __init__(self, func=None, name=None, signature=None,
+ defaults=None, doc=None, module=None, funcdict=None):
+ self.shortsignature = signature
+ if func:
+ # func can be a class or a callable, but not an instance method
+ self.name = func.__name__
+ if self.name == '<lambda>': # small hack for lambda functions
+ self.name = '_lambda_'
+ self.doc = func.__doc__
+ self.module = func.__module__
+ if inspect.isfunction(func):
+ argspec = getfullargspec(func)
+ self.annotations = getattr(func, '__annotations__', {})
+ for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
+ 'kwonlydefaults'):
+ setattr(self, a, getattr(argspec, a))
+ for i, arg in enumerate(self.args):
+ setattr(self, 'arg%d' % i, arg)
+ if sys.version < '3': # easy way
+ self.shortsignature = self.signature = \
+ inspect.formatargspec(
+ formatvalue=lambda val: "", *argspec)[1:-1]
+ else: # Python 3 way
+ allargs = list(self.args)
+ allshortargs = list(self.args)
+ if self.varargs:
+ allargs.append('*' + self.varargs)
+ allshortargs.append('*' + self.varargs)
+ elif self.kwonlyargs:
+ allargs.append('*') # single star syntax
+ for a in self.kwonlyargs:
+ allargs.append('%s=None' % a)
+ allshortargs.append('%s=%s' % (a, a))
+ if self.varkw:
+ allargs.append('**' + self.varkw)
+ allshortargs.append('**' + self.varkw)
+ self.signature = ', '.join(allargs)
+ self.shortsignature = ', '.join(allshortargs)
+ self.dict = func.__dict__.copy()
+ # func=None happens when decorating a caller
+ if name:
+ self.name = name
+ if signature is not None:
+ self.signature = signature
+ if defaults:
+ self.defaults = defaults
+ if doc:
+ self.doc = doc
+ if module:
+ self.module = module
+ if funcdict:
+ self.dict = funcdict
+ # check existence required attributes
+ assert hasattr(self, 'name')
+ if not hasattr(self, 'signature'):
+ raise TypeError('You are decorating a non function: %s' % func)
+
+ def update(self, func, **kw):
+ "Update the signature of func with the data in self"
+ func.__name__ = self.name
+ func.__doc__ = getattr(self, 'doc', None)
+ func.__dict__ = getattr(self, 'dict', {})
+ func.func_defaults = getattr(self, 'defaults', ())
+ func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
+ func.__annotations__ = getattr(self, 'annotations', None)
+ callermodule = sys._getframe(3).f_globals.get('__name__', '?')
+ func.__module__ = getattr(self, 'module', callermodule)
+ func.__dict__.update(kw)
+
+ def make(self, src_templ, evaldict=None, addsource=False, **attrs):
+ "Make a new function from a given template and update the signature"
+ src = src_templ % vars(self) # expand name and signature
+ evaldict = evaldict or {}
+ mo = DEF.match(src)
+ if mo is None:
+ raise SyntaxError('not a valid function template\n%s' % src)
+ name = mo.group(1) # extract the function name
+ names = set([name] + [arg.strip(' *') for arg in
+ self.shortsignature.split(',')])
+ for n in names:
+ if n in ('_func_', '_call_'):
+ raise NameError('%s is overridden in\n%s' % (n, src))
+ if not src.endswith('\n'): # add a newline just for safety
+ src += '\n' # this is needed in old versions of Python
+ try:
+ code = compile(src, '<string>', 'single')
+ # print >> sys.stderr, 'Compiling %s' % src
+ exec code in evaldict
+ except:
+ print >> sys.stderr, 'Error in generated code:'
+ print >> sys.stderr, src
+ raise
+ func = evaldict[name]
+ if addsource:
+ attrs['__source__'] = src
+ self.update(func, **attrs)
+ return func
+
+ @classmethod
+ def create(cls, obj, body, evaldict, defaults=None,
+ doc=None, module=None, addsource=True, **attrs):
+ """
+ Create a function from the strings name, signature and body.
+ evaldict is the evaluation dictionary. If addsource is true an attribute
+ __source__ is added to the result. The attributes attrs are added,
+ if any.
+ """
+ if isinstance(obj, str): # "name(signature)"
+ name, rest = obj.strip().split('(', 1)
+ signature = rest[:-1] #strip a right parens
+ func = None
+ else: # a function
+ name = None
+ signature = None
+ func = obj
+ self = cls(func, name, signature, defaults, doc, module)
+ ibody = '\n'.join(' ' + line for line in body.splitlines())
+ return self.make('def %(name)s(%(signature)s):\n' + ibody,
+ evaldict, addsource, **attrs)
+
+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 not None: # returns a decorated function
+ evaldict = func.func_globals.copy()
+ evaldict['_call_'] = caller
+ evaldict['_func_'] = func
+ return FunctionMaker.create(
+ func, "return _call_(_func_, %(shortsignature)s)",
+ evaldict, undecorated=func, __wrapped__=func)
+ else: # returns a decorator
+ if inspect.isclass(caller):
+ name = caller.__name__.lower()
+ callerfunc = get_init(caller)
+ doc = 'decorator(%s) converts functions/generators into ' \
+ 'factories of %s objects' % (caller.__name__, caller.__name__)
+ fun = getfullargspec(callerfunc).args[1] # second arg
+ elif inspect.isfunction(caller):
+ name = '_lambda_' if caller.__name__ == '<lambda>' \
+ else caller.__name__
+ callerfunc = caller
+ doc = caller.__doc__
+ fun = getfullargspec(callerfunc).args[0] # first arg
+ else: # assume caller is an object with a __call__ method
+ name = caller.__class__.__name__.lower()
+ callerfunc = caller.__call__.im_func
+ doc = caller.__call__.__doc__
+ fun = getfullargspec(callerfunc).args[1] # second arg
+ evaldict = callerfunc.func_globals.copy()
+ evaldict['_call_'] = caller
+ evaldict['decorator'] = decorator
+ return FunctionMaker.create(
+ '%s(%s)' % (name, fun),
+ 'return decorator(_call_, %s)' % fun,
+ evaldict, undecorated=caller, __wrapped__=caller,
+ doc=doc, module=caller.__module__)
+
+######################### contextmanager ########################
+
+def __call__(self, func):
+ 'Context manager decorator'
+ return FunctionMaker.create(
+ func, "with _self_: return _func_(%(shortsignature)s)",
+ dict(_self_=self, _func_=func), __wrapped__=func)
+
+try: # Python >= 3.2
+
+ from contextlib import _GeneratorContextManager
+ ContextManager = type(
+ 'ContextManager', (_GeneratorContextManager,), dict(__call__=__call__))
+
+except ImportError: # Python >= 2.5
+
+ from contextlib import GeneratorContextManager
+ def __init__(self, f, *a, **k):
+ return GeneratorContextManager.__init__(self, f(*a, **k))
+ ContextManager = type(
+ 'ContextManager', (GeneratorContextManager,),
+ dict(__call__=__call__, __init__=__init__))
+
+contextmanager = decorator(ContextManager)