diff options
author | Michele Simionato <michele.simionato@gmail.com> | 2012-10-18 10:39:28 +0200 |
---|---|---|
committer | Michele Simionato <michele.simionato@gmail.com> | 2012-10-18 10:39:28 +0200 |
commit | ebc8ff5064dc649116047856a44c43853d8cdcd6 (patch) | |
tree | 2fe1041b47755911985f18c251e28b97adc39d25 /decorator/documentation.py | |
parent | c9d029e4d9ba32b09462a635413fe4d80a06c9a1 (diff) | |
download | micheles-devel.tar.gz |
Worked on decorator 3.4devel
Diffstat (limited to 'decorator/documentation.py')
-rw-r--r-- | decorator/documentation.py | 130 |
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() |