summaryrefslogtreecommitdiff
path: root/decorator/documentation.py
diff options
context:
space:
mode:
Diffstat (limited to 'decorator/documentation.py')
-rw-r--r--decorator/documentation.py130
1 files changed, 106 insertions, 24 deletions
diff --git a/decorator/documentation.py b/decorator/documentation.py
index 8bdcf04..3d5a5c0 100644
--- a/decorator/documentation.py
+++ b/decorator/documentation.py
@@ -340,18 +340,16 @@ 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 used as signature-preserving
-decorators. The suggested pattern to do that is to introduce
-a helper method ``call(self, func, *args, **kw)`` and to call
-it in the ``__call__(self, func)`` method.
+callable objects that can be converted into decorators.
-As an example, here I show a decorator
+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.
-The implementation is the following:
+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
@@ -369,7 +367,7 @@ be locked. Here is a minimalistic example:
.. code-block:: python
- >>> async = Async(threading.Thread)
+ >>> async = decorator(Async(threading.Thread))
>>> datalist = [] # for simplicity the written data are stored into a list.
@@ -399,6 +397,71 @@ be no synchronization problems since ``write`` is locked.
>>> 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
---------------------------------------------------------------
@@ -862,29 +925,28 @@ class Async(object):
async_with_processes = Async(multiprocessing.Process)
"""
- def __init__(self, threadfactory):
- self.threadfactory = threadfactory
-
- def __call__(self, func, on_success=on_success,
+ def __init__(self, threadfactory, on_success=on_success,
on_failure=on_failure, on_closing=on_closing):
- # every decorated function has its own independent thread counter
- func.counter = itertools.count(1)
- func.on_success = on_success
- func.on_failure = on_failure
- func.on_closing = on_closing
- return decorator(self.call, func)
-
- def call(self, func, *args, **kw):
+ 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:
- func.on_failure(sys.exc_info())
+ self.on_failure(sys.exc_info())
else:
- return func.on_success(result)
+ return self.on_success(result)
finally:
- func.on_closing()
- name = '%s-%s' % (func.__name__, func.counter.next())
+ self.on_closing()
thread = self.threadfactory(None, func_wrapper, name)
thread.start()
return thread
@@ -1048,5 +1110,25 @@ def a_test_for_pylons():
'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()