summaryrefslogtreecommitdiff
path: root/documentation.py
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-08-16 12:53:00 +0000
committermichele.simionato <devnull@localhost>2009-08-16 12:53:00 +0000
commite9cda2473b617ef454e5ed2755d9c678c0603ca6 (patch)
tree8db3f954f4ed916f6146f7867026f8ffeae96602 /documentation.py
parent4fab653aa6f1a2843daa9fa119673583c317ba7b (diff)
downloadpython-decorator-git-e9cda2473b617ef454e5ed2755d9c678c0603ca6.tar.gz
Decorator module, version 3.1
Diffstat (limited to 'documentation.py')
-rw-r--r--documentation.py186
1 files changed, 106 insertions, 80 deletions
diff --git a/documentation.py b/documentation.py
index e5cc32c..2452333 100644
--- a/documentation.py
+++ b/documentation.py
@@ -275,16 +275,15 @@ For instance, you can write directly
... print "calling %s with args %s, %s" % (f.func_name, args, kw)
... return f(*args, **kw)
-and now ``trace`` will be a decorator. You
-can easily check that the signature has changed:
+and now ``trace`` will be a decorator. Actually ``trace`` is a ``partial``
+object which can be used as a decorator:
.. code-block:: python
- >>> print getargspec(trace)
- (['f'], None, None, None)
+ >>> trace # doctest: +ELLIPSIS
+ <functools.partial object at 0x...>
-Therefore now ``trace`` can be used as a decorator and
-the following will work:
+Here is an example of usage:
.. code-block:: python
@@ -294,21 +293,59 @@ the following will work:
>>> func()
calling func with args (), {}
-For the rest of this document, I will discuss examples of useful
-decorators built on top of ``decorator``.
+If you are using an old Python version (Python 2.4) the
+``decorator`` module provides a poor man replacement for
+``functools.partial`` as a higher order function.
+
+There is also an easy way to create one-parameter factories of
+decorators. Suppose for instance you want to generated different
+tracing generator, with different tracing messages.
+Here is how to do it:
+
+.. code-block:: python
+
+ >>> @decorator.factory
+ ... def trace_factory(message_template, f, *args, **kw):
+ ... name = f.func_name
+ ... print message_template % locals()
+ ... return f(*args, **kw)
+
+``decorator.factory`` converts a function with signature
+``(param, func, *args, **kw)`` into a one-parameter family
+of decorators.
+
+.. code-block:: python
+
+ >>> trace_factory # doctest: +ELLIPSIS
+ <functools.partial object at 0x...>
+
+ >>> trace = trace_factory('Calling %(name)s with args %(args)s '
+ ... 'and keywords %(kw)s')
+
+In this example the parameter (``message_template``) is
+just a string, but in general it can be a tuple, a dictionary, or
+a generic object, so there is no real restriction (for instance,
+if you want to define a two-parameter family of decorators just
+use a tuple with two arguments as parameter).
+Here is an example of usage:
+
+.. code-block:: python
+
+ >>> @trace
+ ... def func(): pass
+
+ >>> func()
+ Calling func with args () and keywords {}
``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 decorator:
+This behavior can be implemented with a suitable family of decorators,
+where the parameter is the busy message:
$$blocking
-
-(notice that without the help of ``decorator``, an additional level of
-nesting would have been needed). This is actually an example
-of a one-parameter family of decorators.
Functions decorated with ``blocking`` will return a busy message if
the resource is unavailable, and the intended result if the resource is
@@ -387,13 +424,13 @@ be locked. Here is a minimalistic example:
Each call to ``write`` will create a new writer thread, but there will
be no synchronization problems since ``write`` is locked.
->>> write("data1")
-<Thread(write-1, started)>
+>>> 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")
-<Thread(write-2, started)>
+>>> write("data2") # doctest: +ELLIPSIS
+<Thread(write-2, started ...)>
>>> time.sleep(2) # wait for the writers to complete
@@ -409,48 +446,24 @@ 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. We will see an example in two
-paragraphs, when implementing a custom decorator factory
-(``decorator_apply``).
-
-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.
+it is handy in some circumstances. You will see an example shortly, in
+the implementation of a cool decorator utility (``decorator_apply``).
-``FunctionMaker`` takes the name and the signature (as a string) of a
-function in input, or a whole function. Then, it creates a new
-function (actually a closure) from a function template (the function
-template must begin with ``def`` with no comments before and you cannot
-use a ``lambda``) via its
-``.make`` method: the name and the signature of the resulting function
-are determinated by the specified name and signature. For instance,
-here is an example of how to restrict the signature of a function:
+``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
- >>> fun = FunctionMaker(name="f1", signature="a,b")
- >>> f1 = fun.make('''\
- ... def %(name)s(%(signature)s):
- ... f(%(signature)s)''', dict(f=f))
- ...
+ >>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f))
>>> f1(1,2)
(1, 2) {}
-The dictionary passed in this example (``dict(f=f)``) is the
-execution environment: ``FunctionMaker.make`` actually returns a
-closure, and the original function ``f`` is a variable in the
-closure environment.
-``FunctionMaker.make`` also accepts keyword arguments and such
+``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__``.
@@ -462,19 +475,28 @@ be added to the decorated function:
.. code-block:: python
- >>> f1 = fun.make('''\
- ... def %(name)s(%(signature)s):
- ... f(%(signature)s)''', dict(f=f), addsource=True)
- ...
+ >>> f1 = FunctionMaker.create(
+ ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True)
>>> print f1.__source__
- def f1(a,b):
- f(a,b)
+ def f1(a, b):
+ f(a, b)
<BLANKLINE>
+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.make`` uses ``exec`` to generate the
+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
@@ -647,9 +669,10 @@ 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.0 it is impossible to change the function signature
+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 am releasing version 3.0.
+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
@@ -747,6 +770,9 @@ 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).
+Starting from version 3.1 there is
+an easy way to define decorator factories by using ``decorator.factory``,
+so that there is less need to use classes to implement decorator factories.
All these changes should not cause any trouble, since they were
all rarely used features. Should you have any trouble, you can always
@@ -756,10 +782,9 @@ The examples shown here have been tested with Python 2.5. Python 2.4
is also supported - of course the examples requiring the ``with``
statement will not work there. Python 2.6 works fine, but if you
run the examples here in the interactive interpreter
-you will notice a couple of minor differences since
+you will notice a few differences since
``getargspec`` returns an ``ArgSpec`` namedtuple instead of a regular
-tuple, and the string representation of a thread object returns a
-thread identifier number. That means that running the file
+tuple. That means that running the file
``documentation.py`` under Python 2.5 will a few errors, but
they are not serious. Python 3.0 is kind of supported too.
Simply run the script ``2to3`` on the module
@@ -818,11 +843,13 @@ today = time.strftime('%Y-%m-%d')
__doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today)
def decorator_apply(dec, func):
- "Decorate a function using a signature-non-preserving decorator"
- fun = FunctionMaker(func)
- src = '''def %(name)s(%(signature)s):
- return decorated(%(signature)s)'''
- return fun.make(src, dict(decorated=dec(func)), undecorated=func)
+ """
+ Decorate a function by preserving the signature even if dec
+ is not a signature-preserving decorator.
+ """
+ return FunctionMaker.create(
+ func, 'return decorated(%(signature)s)',
+ dict(decorated=dec(func)), undecorated=func)
def _trace(f, *args, **kw):
print "calling %s with args %s, %s" % (f.__name__, args, kw)
@@ -917,20 +944,19 @@ def _memoize(func, *args, **kw):
def memoize(f):
f.cache = {}
return decorator(_memoize, f)
-
-def blocking(not_avail="Not Available"):
- 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)
+
+@decorator.factory
+def blocking(not_avail, 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
class User(object):
"Will just be able to see a page"