diff options
-rw-r--r-- | CHANGES.txt | 7 | ||||
-rw-r--r-- | MANIFEST.in | 1 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | README.txt | 27 | ||||
-rw-r--r-- | documentation.html | 1013 | ||||
-rw-r--r-- | documentation.py | 83 | ||||
-rw-r--r-- | documentation3.html | 966 | ||||
-rw-r--r-- | documentation3.py | 1006 | ||||
-rw-r--r-- | index.html | 330 | ||||
-rw-r--r-- | performance.sh | 16 | ||||
-rw-r--r-- | setup.py | 23 | ||||
-rw-r--r-- | src/decorator.py (renamed from decorator.py) | 1 |
12 files changed, 3416 insertions, 78 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index a18d568..4e54d9b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,9 +1,10 @@ HISTORY ---------- -3.2. Added __version__, removed functionality which has been deprecated for - more than one year, removed the confusing decorator_factory example - from the documentation (21/01/2010) +3.2. Added __version__ (thanks to Gregg Lind), removed functionality which + has been deprecated for years, removed the confusing decorator_factory + example and added full support for Python 3 (thanks to Claus Klein) + (22/05/2010) 3.1.2. Added attributes args, varargs, keywords and arg0, ..., argN to FunctionMaker objects generated from a function; fixed another Pylons-breaking bug signaled by Lawrence Oluyede (25/08/2009) diff --git a/MANIFEST.in b/MANIFEST.in index 78a2129..5a305de 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1 @@ -include documentation.html documentation.pdf exclude Makefile @@ -1,9 +1,18 @@ -RST=$(HOME)/trunk/ROnline/RCommon/Python/ms/tools/rst.py +RST=python /home/micheles/trunk/ROnline/RCommon/Python/ms/tools/rst.py -rst: documentation.py - python $(HOME)/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -dH documentation.py +rst: documentation.py documentation3.py + python $(HOME)/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -d documentation.py + python3 $(S)/minidoc3.py -d documentation3.py -pdf: /tmp/documentation.rst - $(RST) -ptd /tmp/documentation.rst; cp /tmp/documentation.pdf /tmp/documentation.html . -upload: documentation.pdf +html: /tmp/documentation.rst /tmp/documentation3.rst + $(RST) /tmp/documentation.rst + $(RST) /tmp/documentation3.rst + rst2html README.txt index.html + +pdf: /tmp/documentation.rst /tmp/documentation3.rst + rst2pdf /tmp/documentation.rst -o documentation.pdf + rst2pdf /tmp/documentation3.rst -o documentation3.pdf + cp /tmp/documentation.html /tmp/documentation3.html . + +upload: documentation.pdf documentation3.pdf python setup.py register sdist upload @@ -7,14 +7,31 @@ The decorator module requires Python 2.4. Installation:
-Copy the file decorator.py somewhere in your Python path.
+$ python setup.py install
Testing:
-Run
+For Python 2.4, 2.5, 2.6, 2.7 run
$ python documentation.py
-This should work perfectly with Python 2.7 and Python 2.6 and give a few
-expected errors with Python 2.5 and 2.4 (some inner details such as the
-introduction of the ArgSpec namedtuple and Thread.__repr__ changed).
+for Python 3.X run
+
+$ python documentation3.py
+
+You will see a few innocuous errors with Python 2.4 and 2.5, because
+some inner details such as the introduction of the ArgSpec namedtuple
+and Thread.__repr__ changed. You may safely ignore them.
+
+Notice:
+
+You may get 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 two versions of the documentation, one for `Python 2`_ and one
+for `Python 3`_ .
+
+.. _Python 2: documentation.html
+.. _Python 3: documentation3.html
diff --git a/documentation.html b/documentation.html new file mode 100644 index 0000000..2bca550 --- /dev/null +++ b/documentation.html @@ -0,0 +1,1013 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" /> +<title>The decorator module</title> +<meta name="author" content="Michele Simionato" /> +<style type="text/css"> + +.highlight { background: #f8f8f8; } +.highlight .c { color: #408080; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ +.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #008000; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #7D9029 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ + +</style> +</head> +<body> +<div class="document" id="the-decorator-module"> +<h1 class="title">The <tt class="docutils literal">decorator</tt> module</h1> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr><th class="docinfo-name">Author:</th> +<td>Michele Simionato</td></tr> +<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td> +</tr> +<tr><th class="docinfo-name">Version:</th> +<td>3.2.0 (2010-05-22)</td></tr> +<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.4+</td> +</tr> +<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/decorator/3.2.0">http://pypi.python.org/pypi/decorator/3.2.0</a></td> +</tr> +<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install decorator</tt></td> +</tr> +<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td> +</tr> +</tbody> +</table> +<div class="contents topic" id="contents"> +<p class="topic-title first">Contents</p> +<ul class="simple"> +<li><a class="reference internal" href="#introduction" id="id3">Introduction</a></li> +<li><a class="reference internal" href="#definitions" id="id4">Definitions</a></li> +<li><a class="reference internal" href="#statement-of-the-problem" id="id5">Statement of the problem</a></li> +<li><a class="reference internal" href="#the-solution" id="id6">The solution</a></li> +<li><a class="reference internal" href="#a-trace-decorator" id="id7">A <tt class="docutils literal">trace</tt> decorator</a></li> +<li><a class="reference internal" href="#decorator-is-a-decorator" id="id8"><tt class="docutils literal">decorator</tt> is a decorator</a></li> +<li><a class="reference internal" href="#blocking" id="id9"><tt class="docutils literal">blocking</tt></a></li> +<li><a class="reference internal" href="#async" id="id10"><tt class="docutils literal">async</tt></a></li> +<li><a class="reference internal" href="#the-functionmaker-class" id="id11">The <tt class="docutils literal">FunctionMaker</tt> class</a></li> +<li><a class="reference internal" href="#getting-the-source-code" id="id12">Getting the source code</a></li> +<li><a class="reference internal" href="#dealing-with-third-party-decorators" id="id13">Dealing with third party decorators</a></li> +<li><a class="reference internal" href="#caveats-and-limitations" id="id14">Caveats and limitations</a></li> +<li><a class="reference internal" href="#compatibility-notes" id="id15">Compatibility notes</a></li> +<li><a class="reference internal" href="#licence" id="id16">LICENCE</a></li> +</ul> +</div> +<div class="section" id="introduction"> +<h1><a class="toc-backref" href="#id3">Introduction</a></h1> +<p>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:</p> +<ul class="simple"> +<li>decorators help reducing boilerplate code;</li> +<li>decorators help separation of concerns;</li> +<li>decorators enhance readability and maintenability;</li> +<li>decorators are explicit.</li> +</ul> +<p>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.</p> +<p>The aim of the <tt class="docutils literal">decorator</tt> 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.</p> +<p>You may find the source code for all the examples +discussed here in the <tt class="docutils literal">documentation.py</tt> file, which contains +this documentation in the form of doctests.</p> +</div> +<div class="section" id="definitions"> +<h1><a class="toc-backref" href="#id4">Definitions</a></h1> +<p>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:</p> +<ul class="simple"> +<li><em>signature-preserving</em> decorators, i.e. callable objects taking a +function as input and returning a function <em>with the same +signature</em> as output;</li> +<li><em>signature-changing</em> decorators, i.e. decorators that change +the signature of their input function, or decorators returning +non-callable objects.</li> +</ul> +<p>Signature-changing decorators have their use: for instance the +builtin classes <tt class="docutils literal">staticmethod</tt> and <tt class="docutils literal">classmethod</tt> are in this +group, since they take functions and return descriptor objects which +are not functions, nor callables.</p> +<p>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.</p> +<p>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.</p> +</div> +<div class="section" id="statement-of-the-problem"> +<h1><a class="toc-backref" href="#id5">Statement of the problem</a></h1> +<p>A very common use case for decorators is the memoization of functions. +A <tt class="docutils literal">memoize</tt> 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 +<tt class="docutils literal">memoize</tt> in <a class="reference external" href="http://www.python.org/moin/PythonDecoratorLibrary">http://www.python.org/moin/PythonDecoratorLibrary</a>, +but they do not preserve the signature. +A simple implementation for Python 2.5 could be the following (notice +that in general it is impossible to memoize correctly something +that depends on non-hashable arguments):</p> +<pre class="literal-block"> +def memoize25(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) +</pre> +<p>Here we used the <a class="reference external" href="http://www.python.org/doc/2.5.2/lib/module-functools.html">functools.update_wrapper</a> 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 +<tt class="docutils literal">__name__</tt>, <tt class="docutils literal">__doc__</tt>, <tt class="docutils literal">__module__</tt> and <tt class="docutils literal">__dict__</tt> +from the original function to the decorated function by hand).</p> +<p>The implementation above works in the sense that the decorator +can accept functions with generic signatures; unfortunately this +implementation does <em>not</em> define a signature-preserving decorator, since in +general <tt class="docutils literal">memoize25</tt> returns a function with a +<em>different signature</em> from the original function.</p> +<p>Consider for instance the following case:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize25</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># simulate some long computation</span> +<span class="o">...</span> <span class="k">return</span> <span class="n">x</span> +</pre></div> + +</div> +<p>Here the original function takes a single argument named <tt class="docutils literal">x</tt>, +but the decorated function takes any number of arguments and +keyword arguments:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="kn">from</span> <span class="nn">inspect</span> <span class="kn">import</span> <span class="n">getargspec</span> +<span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +<p>This means that introspection tools such as pydoc will give +wrong informations about the signature of <tt class="docutils literal">f1</tt>. This is pretty bad: +pydoc will tell you that the function accepts a generic signature +<tt class="docutils literal">*args</tt>, <tt class="docutils literal">**kw</tt>, but when you try to call the function with more than an +argument, you will get an error:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">f1</span><span class="p">()</span> <span class="n">takes</span> <span class="n">exactly</span> <span class="mf">1</span> <span class="n">argument</span> <span class="p">(</span><span class="mf">2</span> <span class="n">given</span><span class="p">)</span> +</pre></div> + +</div> +</div> +<div class="section" id="the-solution"> +<h1><a class="toc-backref" href="#id6">The solution</a></h1> +<p>The solution is to provide a generic factory of generators, which +hides the complexity of making signature-preserving decorators +from the application programmer. The <tt class="docutils literal">decorator</tt> function in +the <tt class="docutils literal">decorator</tt> module is such a factory:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span> +</pre></div> + +</div> +<p><tt class="docutils literal">decorator</tt> 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 <tt class="docutils literal">(f, *args, **kw)</tt> and it must call the original function <tt class="docutils literal">f</tt> +with arguments <tt class="docutils literal">args</tt> and <tt class="docutils literal">kw</tt>, implementing the wanted capability, +i.e. memoization in this case:</p> +<pre class="literal-block"> +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 +</pre> +<p>At this point you can define your decorator as follows:</p> +<pre class="literal-block"> +def memoize(f): + f.cache = {} + return decorator(_memoize, f) +</pre> +<p>The difference with respect to the Python 2.5 approach, which is based +on nested functions, is that the decorator module forces you to lift +the inner function at the outer level (<em>flat is better than nested</em>). +Moreover, you are forced to pass explicitly the function you want to +decorate to the caller function.</p> +<p>Here is a test of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">heavy_computation</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> +<span class="o">...</span> <span class="k">return</span> <span class="s">"done"</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">heavy_computation</span><span class="p">()</span> <span class="c"># the first time it will take 2 seconds</span> +<span class="n">done</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">heavy_computation</span><span class="p">()</span> <span class="c"># the second time it will be instantaneous</span> +<span class="n">done</span> +</pre></div> + +</div> +<p>The signature of <tt class="docutils literal">heavy_computation</tt> is the one you would expect:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">heavy_computation</span><span class="p">)</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +</div> +<div class="section" id="a-trace-decorator"> +<h1><a class="toc-backref" href="#id7">A <tt class="docutils literal">trace</tt> decorator</a></h1> +<p>As an additional example, here is how you can define a trivial +<tt class="docutils literal">trace</tt> decorator, which prints a message everytime the traced +function is called:</p> +<pre class="literal-block"> +def _trace(f, *args, **kw): + print "calling %s with args %s, %s" % (f.__name__, args, kw) + return f(*args, **kw) +</pre> +<pre class="literal-block"> +def trace(f): + return decorator(_trace, f) +</pre> +<p>Here is an example of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> +</pre></div> + +</div> +<p>It is immediate to verify that <tt class="docutils literal">f1</tt> works</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f1</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,),</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>and it that it has the correct signature:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +<p>The same decorator works with functions of any signature:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mf">1</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mf">2</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">,</span> <span class="mf">2</span><span class="p">),</span> <span class="p">{}</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">,</span> <span class="s">'z'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">))</span> +</pre></div> + +</div> +<p>That includes even functions with exotic signatures like the following:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">exotic_signature</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span><span class="o">=</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="mf">2</span><span class="p">)):</span> <span class="k">return</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">exotic_signature</span><span class="p">)</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">]],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">((</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">),))</span> +<span class="o">>>></span> <span class="n">exotic_signature</span><span class="p">()</span> +<span class="n">calling</span> <span class="n">exotic_signature</span> <span class="k">with</span> <span class="n">args</span> <span class="p">((</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">),),</span> <span class="p">{}</span> +<span class="mf">3</span> +</pre></div> + +</div> +<p>Notice that the support for exotic signatures has been deprecated +in Python 2.6 and removed in Python 3.0.</p> +</div> +<div class="section" id="decorator-is-a-decorator"> +<h1><a class="toc-backref" href="#id8"><tt class="docutils literal">decorator</tt> is a decorator</a></h1> +<p>It may be annoying to write a caller function (like the <tt class="docutils literal">_trace</tt> +function above) and then a trivial wrapper +(<tt class="docutils literal">def trace(f): return decorator(_trace, f)</tt>) every time. For this reason, +the <tt class="docutils literal">decorator</tt> module provides an easy shortcut to convert +the caller function into a signature-preserving decorator: +you can just call <tt class="docutils literal">decorator</tt> with a single argument. +In our example you can just write <tt class="docutils literal">trace = decorator(_trace)</tt>. +The <tt class="docutils literal">decorator</tt> function can also be used as a signature-changing +decorator, just as <tt class="docutils literal">classmethod</tt> and <tt class="docutils literal">staticmethod</tt>. +However, <tt class="docutils literal">classmethod</tt> and <tt class="docutils literal">staticmethod</tt> return generic +objects which are not callable, while <tt class="docutils literal">decorator</tt> returns +signature-preserving decorators, i.e. functions of a single argument. +For instance, you can write directly</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@decorator</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">trace</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> +<span class="o">...</span> <span class="k">print</span> <span class="s">"calling </span><span class="si">%s</span><span class="s"> with args </span><span class="si">%s</span><span class="s">, </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">func_name</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kw</span><span class="p">)</span> +<span class="o">...</span> <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> +</pre></div> + +</div> +<p>and now <tt class="docutils literal">trace</tt> will be a decorator. Actually <tt class="docutils literal">trace</tt> is a <tt class="docutils literal">partial</tt> +object which can be used as a decorator:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">trace</span> +<span class="o"><</span><span class="n">function</span> <span class="n">trace</span> <span class="n">at</span> <span class="mf">0</span><span class="n">x</span><span class="o">...></span> +</pre></div> + +</div> +<p>Here is an example of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">func</span><span class="p">():</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="n">func</span><span class="p">()</span> +<span class="n">calling</span> <span class="n">func</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(),</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>If you are using an old Python version (Python 2.4) the +<tt class="docutils literal">decorator</tt> module provides a poor man replacement for +<tt class="docutils literal">functools.partial</tt>.</p> +</div> +<div class="section" id="blocking"> +<h1><a class="toc-backref" href="#id9"><tt class="docutils literal">blocking</tt></a></h1> +<p>Sometimes one has to deal with blocking resources, such as <tt class="docutils literal">stdin</tt>, 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:</p> +<pre class="literal-block"> +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) +</pre> +<p>Functions decorated with <tt class="docutils literal">blocking</tt> will return a busy message if +the resource is unavailable, and the intended result if the resource is +available. For instance:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@blocking</span><span class="p">(</span><span class="s">"Please wait ..."</span><span class="p">)</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">read_data</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span> <span class="c"># simulate a blocking resource</span> +<span class="o">...</span> <span class="k">return</span> <span class="s">"some data"</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.1</span><span class="p">)</span> <span class="c"># after 3.1 seconds, data is available</span> +<span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> +<span class="n">some</span> <span class="n">data</span> +</pre></div> + +</div> +</div> +<div class="section" id="async"> +<h1><a class="toc-backref" href="#id10"><tt class="docutils literal">async</tt></a></h1> +<p>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 <tt class="docutils literal">call(self, func, *args, **kw)</tt> and to call +it in the <tt class="docutils literal">__call__(self, func)</tt> method.</p> +<p>As an example, here 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 <tt class="docutils literal">on_success</tt>, <tt class="docutils literal">on_failure</tt> and <tt class="docutils literal">on_closing</tt>, +to specify how to manage the function call. +The implementation is the following:</p> +<pre class="literal-block"> +def on_success(result): # default implementation + "Called on the result of the function" + return result +</pre> +<pre class="literal-block"> +def on_failure(exc_info): # default implementation + "Called if the function fails" + pass +</pre> +<pre class="literal-block"> +def on_closing(): # default implementation + "Called at the end, both in case of success and failure" + pass +</pre> +<pre class="literal-block"> +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): + self.threadfactory = threadfactory + + def __call__(self, func, 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): + def func_wrapper(): + try: + result = func(*args, **kw) + except: + func.on_failure(sys.exc_info()) + else: + return func.on_success(result) + finally: + func.on_closing() + name = '%s-%s' % (func.__name__, func.counter.next()) + thread = self.threadfactory(None, func_wrapper, name) + thread.start() + return thread +</pre> +<p>The decorated function returns +the current execution thread, which can be stored and checked later, for +instance to verify that the thread <tt class="docutils literal">.isAlive()</tt>.</p> +<p>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:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">async</span> <span class="o">=</span> <span class="n">Async</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">)</span> + +<span class="o">>>></span> <span class="n">datalist</span> <span class="o">=</span> <span class="p">[]</span> <span class="c"># for simplicity the written data are stored into a list.</span> + +<span class="o">>>></span> <span class="nd">@async</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> +<span class="o">...</span> <span class="c"># append data to the datalist by locking</span> +<span class="o">...</span> <span class="k">with</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># emulate some long running operation</span> +<span class="o">...</span> <span class="n">datalist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> +<span class="o">...</span> <span class="c"># other operations not requiring a lock here</span> +</pre></div> + +</div> +<p>Each call to <tt class="docutils literal">write</tt> will create a new writer thread, but there will +be no synchronization problems since <tt class="docutils literal">write</tt> is locked.</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data1"</span><span class="p">)</span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="o">.</span><span class="mf">1</span><span class="p">)</span> <span class="c"># wait a bit, so we are sure data2 is written after data1</span> + +<span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data2"</span><span class="p">)</span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">2</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> <span class="c"># wait for the writers to complete</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">datalist</span> +<span class="p">[</span><span class="s">'data1'</span><span class="p">,</span> <span class="s">'data2'</span><span class="p">]</span> +</pre></div> + +</div> +</div> +<div class="section" id="the-functionmaker-class"> +<h1><a class="toc-backref" href="#id11">The <tt class="docutils literal">FunctionMaker</tt> class</a></h1> +<p>You may wonder about how the functionality of the <tt class="docutils literal">decorator</tt> module +is implemented. The basic building block is +a <tt class="docutils literal">FunctionMaker</tt> 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 <tt class="docutils literal">FunctionMaker</tt> 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 (<tt class="docutils literal">decorator_apply</tt>).</p> +<p><tt class="docutils literal">FunctionMaker</tt> provides a <tt class="docutils literal">.create</tt> 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 <tt class="docutils literal">exec</tt>. Here is an example:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> <span class="c"># a function with a generic signature</span> +<span class="o">...</span> <span class="k">print</span> <span class="n">args</span><span class="p">,</span> <span class="n">kw</span> + +<span class="o">>>></span> <span class="n">f1</span> <span class="o">=</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">'f1(a, b)'</span><span class="p">,</span> <span class="s">'f(a, b)'</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="n">f</span><span class="p">))</span> +<span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="mf">2</span><span class="p">)</span> +<span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>It is important to notice that the function body is interpolated +before being executed, so be careful with the <tt class="docutils literal">%</tt> sign!</p> +<p><tt class="docutils literal">FunctionMaker.create</tt> 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 <tt class="docutils literal">__doc__</tt>.</p> +<p>For debugging/introspection purposes it may be useful to see +the source code of the generated function; to do that, just +pass the flag <tt class="docutils literal">addsource=True</tt> and a <tt class="docutils literal">__source__</tt> attribute will +be added to the generated function:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span> <span class="o">=</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> +<span class="o">...</span> <span class="s">'f1(a, b)'</span><span class="p">,</span> <span class="s">'f(a, b)'</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="n">f</span><span class="p">),</span> <span class="n">addsource</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span> <span class="n">f1</span><span class="o">.</span><span class="n">__source__</span> +<span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> + <span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +<p><tt class="docutils literal">FunctionMaker.create</tt> 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 <tt class="docutils literal">FunctionMaker.create</tt> +instead of <tt class="docutils literal">decorator</tt>, since it gives you direct access to the body +of the generated function. For instance, suppose you want to instrument +the <tt class="docutils literal">__init__</tt> 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 <tt class="docutils literal">FunctionMaker.create</tt> +is a function, a <tt class="docutils literal">FunctionMaker</tt> object is instantiated internally, +with attributes <tt class="docutils literal">args</tt>, <tt class="docutils literal">varargs</tt>, +<tt class="docutils literal">keywords</tt> and <tt class="docutils literal">defaults</tt> which are the +the return values of the standard library function <tt class="docutils literal">inspect.getargspec</tt>. +For each argument in the <tt class="docutils literal">args</tt> (which is a list of strings containing +the names of the mandatory arguments) an attribute <tt class="docutils literal">arg0</tt>, <tt class="docutils literal">arg1</tt>, +..., <tt class="docutils literal">argN</tt> is also generated. Finally, there is a <tt class="docutils literal">signature</tt> +attribute, a string with the signature of the original function.</p> +<p>Notice that while I do not have plans +to change or remove the functionality provided in the +<tt class="docutils literal">FunctionMaker</tt> 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 +<tt class="docutils literal">decorator</tt> has been there from version 0.1 and it is guaranteed to +stay there forever.</p> +</div> +<div class="section" id="getting-the-source-code"> +<h1><a class="toc-backref" href="#id12">Getting the source code</a></h1> +<p>Internally <tt class="docutils literal">FunctionMaker.create</tt> uses <tt class="docutils literal">exec</tt> to generate the +decorated function. Therefore +<tt class="docutils literal">inspect.getsource</tt> will not work for decorated functions. That +means that the usual '??' trick in IPython will give you the (right on +the spot) message <tt class="docutils literal">Dynamically generated function. No source code +available</tt>. In the past I have considered this acceptable, since +<tt class="docutils literal">inspect.getsource</tt> does not really work even with regular +decorators. In that case <tt class="docutils literal">inspect.getsource</tt> gives you the wrapper +source code which is probably not what you want:</p> +<pre class="literal-block"> +def identity_dec(func): + def wrapper(*args, **kw): + return func(*args, **kw) + return wrapper +</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="nd">@identity_dec</span> +<span class="k">def</span> <span class="nf">example</span><span class="p">():</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="k">print</span> <span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">example</span><span class="p">)</span> + <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> + <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +<p>(see bug report <a class="reference external" href="http://bugs.python.org/issue1764286">1764286</a> for an explanation of what is happening). +Unfortunately the bug is still there, even in Python 2.6 and 3.0. +There is however a workaround. The decorator module adds an +attribute <tt class="docutils literal">.undecorated</tt> to the decorated function, containing +a reference to the original function. The easy way to get +the source code is to call <tt class="docutils literal">inspect.getsource</tt> on the +undecorated function:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">factorial</span><span class="o">.</span><span class="n">undecorated</span><span class="p">)</span> +<span class="nd">@tail_recursive</span> +<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">acc</span><span class="o">=</span><span class="mf">1</span><span class="p">):</span> + <span class="s">"The good old factorial"</span> + <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mf">0</span><span class="p">:</span> <span class="k">return</span> <span class="n">acc</span> + <span class="k">return</span> <span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +</div> +<div class="section" id="dealing-with-third-party-decorators"> +<h1><a class="toc-backref" href="#id13">Dealing with third party decorators</a></h1> +<p>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 <tt class="docutils literal">decorator</tt>. You can use a +<tt class="docutils literal">FunctionMaker</tt> to implement that functionality as follows:</p> +<pre class="literal-block"> +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)), undecorated=func) +</pre> +<p><tt class="docutils literal">decorator_apply</tt> sets the attribute <tt class="docutils literal">.undecorated</tt> of the generated +function to the original function, so that you can get the right +source code.</p> +<p>Notice that I am not providing this functionality in the <tt class="docutils literal">decorator</tt> +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 <tt class="docutils literal">decorator_apply</tt> to your toolbox and +use it if you need to.</p> +<p>In order to give an example of usage of <tt class="docutils literal">decorator_apply</tt>, 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, +<a class="reference external" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691</a>.</p> +<pre class="literal-block"> +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 +</pre> +<p>Here the decorator is implemented as a class returning callable +objects.</p> +<pre class="literal-block"> +def tail_recursive(func): + return decorator_apply(TailRecursive, func) +</pre> +<p>Here is how you apply the upgraded decorator to the good old factorial:</p> +<pre class="literal-block"> +@tail_recursive +def factorial(n, acc=1): + "The good old factorial" + if n == 0: return acc + return factorial(n-1, n*acc) +</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">factorial</span><span class="p">(</span><span class="mf">4</span><span class="p">)</span> +<span class="mf">24</span> +</pre></div> + +</div> +<p>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 <tt class="docutils literal">factorial(1001)</tt> 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</p> +<pre class="literal-block"> +def fact(n): # this is not tail-recursive + if n == 0: return 1 + return n * fact(n-1) +</pre> +<p>(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).</p> +</div> +<div class="section" id="caveats-and-limitations"> +<h1><a class="toc-backref" href="#id14">Caveats and limitations</a></h1> +<p>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:</p> +<pre class="literal-block"> +$ 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()" +</pre> +<p>On my MacBook, using the <tt class="docutils literal">do_nothing</tt> decorator instead of the +plain function is more than three times slower:</p> +<pre class="literal-block"> +$ bash performance.sh +1000000 loops, best of 3: 0.995 usec per loop +1000000 loops, best of 3: 0.273 usec per loop +</pre> +<p>It should be noted that a real life function would probably do +something more useful than <tt class="docutils literal">f</tt> 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.</p> +<p>You should be aware that decorators will make your tracebacks +longer and more difficult to understand. Consider this example:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> +<span class="o">...</span> <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> +</pre></div> + +</div> +<p>Calling <tt class="docutils literal">f()</tt> will give you a <tt class="docutils literal">ZeroDivisionError</tt>, but since the +function is decorated the traceback will be longer:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f</span><span class="p">()</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> + <span class="n">File</span> <span class="s">"<string>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="n">File</span> <span class="s">"<doctest __main__[18]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">4</span><span class="p">,</span> <span class="ow">in</span> <span class="n">trace</span> + <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> + <span class="n">File</span> <span class="s">"<doctest __main__[47]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">3</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> +<span class="ne">ZeroDivisionError</span><span class="p">:</span> <span class="n">integer</span> <span class="n">division</span> <span class="ow">or</span> <span class="n">modulo</span> <span class="n">by</span> <span class="n">zero</span> +</pre></div> + +</div> +<p>You see here the inner call to the decorator <tt class="docutils literal">trace</tt>, which calls +<tt class="docutils literal"><span class="pre">f(*args,</span> **kw)</tt>, and a reference to <tt class="docutils literal">File <span class="pre">"<string>",</span> line 2, in f</tt>. +This latter reference is due to the fact that internally the decorator +module uses <tt class="docutils literal">exec</tt> to generate the decorated function. Notice that +<tt class="docutils literal">exec</tt> is <em>not</em> responsibile for the performance penalty, since is the +called <em>only once</em> at function decoration time, and not every time +the decorated function is called.</p> +<p>At present, there is no clean way to avoid <tt class="docutils literal">exec</tt>. 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 <a class="reference external" href="http://www.python.org/dev/peps/pep-0362">362</a>) 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 <tt class="docutils literal">decorator</tt> module is still useful. +Actually, this is one of the main reasons why I keep maintaining +the module and releasing new versions.</p> +<p>In the present implementation, decorators generated by <tt class="docutils literal">decorator</tt> +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 +<tt class="docutils literal">inspect</tt> 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:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> +<span class="o">...</span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> +</pre></div> + +</div> +<p>Here is an example of invalid decoration, when the decorator in +called too late:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> +<span class="o">...</span> +<span class="o">>>></span> <span class="n">trace</span><span class="p">(</span><span class="n">C</span><span class="o">.</span><span class="n">meth</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">You</span> <span class="n">are</span> <span class="n">decorating</span> <span class="n">a</span> <span class="n">non</span> <span class="n">function</span><span class="p">:</span> <span class="o"><</span><span class="n">unbound</span> <span class="n">method</span> <span class="n">C</span><span class="o">.</span><span class="n">meth</span><span class="o">></span> +</pre></div> + +</div> +<p>The solution is to extract the inner function from the unbound method:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">trace</span><span class="p">(</span><span class="n">C</span><span class="o">.</span><span class="n">meth</span><span class="o">.</span><span class="n">im_func</span><span class="p">)</span> +<span class="o"><</span><span class="n">function</span> <span class="n">meth</span> <span class="n">at</span> <span class="mf">0</span><span class="n">x</span><span class="o">...></span> +</pre></div> + +</div> +<p>There is a restriction on the names of the arguments: for instance, +if try to call an argument <tt class="docutils literal">_call_</tt> or <tt class="docutils literal">_func_</tt> +you will get a <tt class="docutils literal">NameError</tt>:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">_func_</span><span class="p">):</span> <span class="k">print</span> <span class="n">f</span> +<span class="o">...</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> +<span class="ne">NameError</span><span class="p">:</span> <span class="n">_func_</span> <span class="ow">is</span> <span class="n">overridden</span> <span class="ow">in</span> +<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">_func_</span><span class="p">):</span> + <span class="k">return</span> <span class="n">_call_</span><span class="p">(</span><span class="n">_func_</span><span class="p">,</span> <span class="n">_func_</span><span class="p">)</span> +</pre></div> + +</div> +<p>Finally, the implementation is such that the decorated function contains +a <em>copy</em> of the original function dictionary +(<tt class="docutils literal">vars(decorated_f) is not vars(f)</tt>):</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> <span class="k">pass</span> <span class="c"># the original function</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr1</span> <span class="o">=</span> <span class="s">"something"</span> <span class="c"># setting an attribute</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr2</span> <span class="o">=</span> <span class="s">"something else"</span> <span class="c"># setting another attribute</span> + +<span class="o">>>></span> <span class="n">traced_f</span> <span class="o">=</span> <span class="n">trace</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="c"># the decorated function</span> + +<span class="o">>>></span> <span class="n">traced_f</span><span class="o">.</span><span class="n">attr1</span> +<span class="s">'something'</span> +<span class="o">>>></span> <span class="n">traced_f</span><span class="o">.</span><span class="n">attr2</span> <span class="o">=</span> <span class="s">"something different"</span> <span class="c"># setting attr</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr2</span> <span class="c"># the original attribute did not change</span> +<span class="s">'something else'</span> +</pre></div> + +</div> +</div> +<div class="section" id="compatibility-notes"> +<h1><a class="toc-backref" href="#id15">Compatibility notes</a></h1> +<p>Version 3.2 is the first version of the <tt class="docutils literal">decorator</tt> module to officially +support Python 3.0. Actually, the module has supported Python 3.0 from +the beginning, via the <tt class="docutils literal">2to3</tt> conversion tool, but this step has +been now integrated in the build process, thanks to the <a class="reference external" href="http://packages.python.org/distribute/">distribute</a> +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 possibly only now that <a class="reference external" href="http://docutils.sourceforge.net/">docutils</a> and <a class="reference external" href="http://pygments.org/">pygments</a> +have been ported to Python 3.</p> +<p>The <tt class="docutils literal">decorator</tt> module <em>per se</em> does not contain any change, apart +from the removal of the functions <tt class="docutils literal">get_info</tt> and <tt class="docutils literal">new_wrapper</tt>, +which have been deprecated for years. <tt class="docutils literal">get_info</tt> has been removed +since it was little used and since it had to be changed anyway to work +with Python 3.0; <tt class="docutils literal">new_wrapper</tt> has been removed since it was +useless: its major use case (converting signature changing decorators +to signature preserving decorators) has been subsumed by +<tt class="docutils literal">decorator_apply</tt> and the other use case can be managed with the +<tt class="docutils literal">FunctionMaker</tt>.</p> +<p>There are a few changes in the documentation: I removed the +<tt class="docutils literal">decorator_factory</tt> 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. +Notice that there is no support for Python 3 <a class="reference external" href="http://www.python.org/dev/peps/pep-3107/">function annotations</a> +since it seems premature at the moment, when most people are +still using Python 2.X.</p> +<p>Finally <tt class="docutils literal">decorator</tt> cannot be used as a class decorator and the +<a class="reference external" href="http://www.phyast.pitt.edu/~micheles/python/documentation.html#class-decorators-and-decorator-factories">functionality introduced in version 2.3</a> has been removed. That +means that in order to define decorator factories with classes you +need to define the <tt class="docutils literal">__call__</tt> 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.</p> +<p>The examples shown here have been tested with Python 2.6. Python 2.4 +is also supported - of course the examples requiring the <tt class="docutils literal">with</tt> +statement will not work there. Python 2.5 works fine, but if you +run the examples here in the interactive interpreter +you will notice a few differences since +<tt class="docutils literal">getargspec</tt> returns an <tt class="docutils literal">ArgSpec</tt> namedtuple instead of a regular +tuple. That means that running the file +<tt class="docutils literal">documentation.py</tt> under Python 2.5 will a few errors, but +they are not serious.</p> +</div> +<div class="section" id="licence"> +<h1><a class="toc-backref" href="#id16">LICENCE</a></h1> +<p>Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met:</p> +<pre class="literal-block"> +Copyright (c) 2005, Michele Simionato +All rights reserved. + +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. +</pre> +<p>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!</p> +</div> +</div> +</body> +</html> diff --git a/documentation.py b/documentation.py index cdd4fae..aff5d17 100644 --- a/documentation.py +++ b/documentation.py @@ -384,18 +384,20 @@ 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") # 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 +.. code-block:: python ->>> print datalist -['data1', 'data2'] + >>> 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'] The ``FunctionMaker`` class --------------------------------------------------------------- @@ -729,23 +731,32 @@ a *copy* of the original function dictionary Compatibility notes --------------------------------------------------------------- -Version 3.0 is a complete rewrite of the original implementation. -It is mostly compatible with the past, a part for a few differences. - -First of all, the utilites ``get_info`` and ``new_wrapper``, available -in the 2.X versions, have been deprecated and they will be removed -in the future. For the moment, using them raises a ``DeprecationWarning``. -Incidentally, the functionality has been implemented through a -decorator which makes a good example for this documentation: - -$$deprecated - -``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`` -and the other use case can be managed with the ``FunctionMaker``. +Version 3.2 is the first version of the ``decorator`` module to officially +support Python 3.0. Actually, the module has supported Python 3.0 from +the beginning, via the ``2to3`` conversion tool, but this step has +been now integrated in the build process, thanks to 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 possibly only now that docutils_ and pygments_ +have been ported to Python 3. + +The ``decorator`` module *per se* does not contain any 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`` and 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. +Notice that there is no support for Python 3 `function annotations`_ +since it seems premature at the moment, when most people are +still using Python 2.X. Finally ``decorator`` cannot be used as a class decorator and the `functionality introduced in version 2.3`_ has been removed. That @@ -755,25 +766,21 @@ 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.5. Python 2.4 +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.6 works fine, but if you +statement will not work there. Python 2.5 works fine, but if you run the examples here 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 a few errors, but -they are not serious. Python 3.0 is kind of supported too. -Simply run the script ``2to3`` on the module -``decorator.py`` and you will get a version of the code running -with Python 3.0 (at least, I did some simple checks and it seemed -to work). However there is no support for `function annotations`_ yet -since it seems premature at this moment (most people are -still using Python 2.5). +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 --------------------------------------------- diff --git a/documentation3.html b/documentation3.html new file mode 100644 index 0000000..209dd61 --- /dev/null +++ b/documentation3.html @@ -0,0 +1,966 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" /> +<title>The decorator module</title> +<meta name="author" content="Michele Simionato" /> +<style type="text/css"> + +.highlight { background: #f8f8f8; } +.highlight .c { color: #408080; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ +.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #008000; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #7D9029 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ + +</style> +</head> +<body> +<div class="document" id="the-decorator-module"> +<h1 class="title">The <tt class="docutils literal">decorator</tt> module</h1> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr><th class="docinfo-name">Author:</th> +<td>Michele Simionato</td></tr> +<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td> +</tr> +<tr><th class="docinfo-name">Version:</th> +<td>3.2.0 (2010-05-22)</td></tr> +<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.4+</td> +</tr> +<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/decorator/3.2.0">http://pypi.python.org/pypi/decorator/3.2.0</a></td> +</tr> +<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install decorator</tt></td> +</tr> +<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td> +</tr> +</tbody> +</table> +<div class="contents topic" id="contents"> +<p class="topic-title first">Contents</p> +<ul class="simple"> +<li><a class="reference internal" href="#introduction" id="id3">Introduction</a></li> +<li><a class="reference internal" href="#definitions" id="id4">Definitions</a></li> +<li><a class="reference internal" href="#statement-of-the-problem" id="id5">Statement of the problem</a></li> +<li><a class="reference internal" href="#the-solution" id="id6">The solution</a></li> +<li><a class="reference internal" href="#a-trace-decorator" id="id7">A <tt class="docutils literal">trace</tt> decorator</a></li> +<li><a class="reference internal" href="#decorator-is-a-decorator" id="id8"><tt class="docutils literal">decorator</tt> is a decorator</a></li> +<li><a class="reference internal" href="#blocking" id="id9"><tt class="docutils literal">blocking</tt></a></li> +<li><a class="reference internal" href="#async" id="id10"><tt class="docutils literal">async</tt></a></li> +<li><a class="reference internal" href="#the-functionmaker-class" id="id11">The <tt class="docutils literal">FunctionMaker</tt> class</a></li> +<li><a class="reference internal" href="#getting-the-source-code" id="id12">Getting the source code</a></li> +<li><a class="reference internal" href="#dealing-with-third-party-decorators" id="id13">Dealing with third party decorators</a></li> +<li><a class="reference internal" href="#caveats-and-limitations" id="id14">Caveats and limitations</a></li> +<li><a class="reference internal" href="#compatibility-notes" id="id15">Compatibility notes</a></li> +<li><a class="reference internal" href="#licence" id="id16">LICENCE</a></li> +</ul> +</div> +<div class="section" id="introduction"> +<h1><a class="toc-backref" href="#id3">Introduction</a></h1> +<p>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:</p> +<ul class="simple"> +<li>decorators help reducing boilerplate code;</li> +<li>decorators help separation of concerns;</li> +<li>decorators enhance readability and maintenability;</li> +<li>decorators are explicit.</li> +</ul> +<p>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.</p> +<p>The aim of the <tt class="docutils literal">decorator</tt> 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.</p> +<p>You may find the source code for all the examples +discussed here in the <tt class="docutils literal">documentation.py</tt> file, which contains +this documentation in the form of doctests.</p> +</div> +<div class="section" id="definitions"> +<h1><a class="toc-backref" href="#id4">Definitions</a></h1> +<p>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:</p> +<ul class="simple"> +<li><em>signature-preserving</em> decorators, i.e. callable objects taking a +function as input and returning a function <em>with the same +signature</em> as output;</li> +<li><em>signature-changing</em> decorators, i.e. decorators that change +the signature of their input function, or decorators returning +non-callable objects.</li> +</ul> +<p>Signature-changing decorators have their use: for instance the +builtin classes <tt class="docutils literal">staticmethod</tt> and <tt class="docutils literal">classmethod</tt> are in this +group, since they take functions and return descriptor objects which +are not functions, nor callables.</p> +<p>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.</p> +<p>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.</p> +</div> +<div class="section" id="statement-of-the-problem"> +<h1><a class="toc-backref" href="#id5">Statement of the problem</a></h1> +<p>A very common use case for decorators is the memoization of functions. +A <tt class="docutils literal">memoize</tt> 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 +<tt class="docutils literal">memoize</tt> in <a class="reference external" href="http://www.python.org/moin/PythonDecoratorLibrary">http://www.python.org/moin/PythonDecoratorLibrary</a>, +but they do not preserve the signature. +A simple implementation for Python 2.5 could be the following (notice +that in general it is impossible to memoize correctly something +that depends on non-hashable arguments):</p> +<pre class="literal-block"> +def memoize25(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) +</pre> +<p>Here we used the <a class="reference external" href="http://www.python.org/doc/2.5.2/lib/module-functools.html">functools.update_wrapper</a> 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 +<tt class="docutils literal">__name__</tt>, <tt class="docutils literal">__doc__</tt>, <tt class="docutils literal">__module__</tt> and <tt class="docutils literal">__dict__</tt> +from the original function to the decorated function by hand).</p> +<p>The implementation above works in the sense that the decorator +can accept functions with generic signatures; unfortunately this +implementation does <em>not</em> define a signature-preserving decorator, since in +general <tt class="docutils literal">memoize25</tt> returns a function with a +<em>different signature</em> from the original function.</p> +<p>Consider for instance the following case:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize25</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># simulate some long computation</span> +<span class="o">...</span> <span class="k">return</span> <span class="n">x</span> +</pre></div> + +</div> +<p>Here the original function takes a single argument named <tt class="docutils literal">x</tt>, +but the decorated function takes any number of arguments and +keyword arguments:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="kn">from</span> <span class="nn">inspect</span> <span class="kn">import</span> <span class="n">getargspec</span> +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">getargspec</span><span class="p">(</span><span class="n">f1</span><span class="p">))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +<p>This means that introspection tools such as pydoc will give +wrong informations about the signature of <tt class="docutils literal">f1</tt>. This is pretty bad: +pydoc will tell you that the function accepts a generic signature +<tt class="docutils literal">*args</tt>, <tt class="docutils literal">**kw</tt>, but when you try to call the function with more than an +argument, you will get an error:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">f1</span><span class="p">()</span> <span class="n">takes</span> <span class="n">exactly</span> <span class="mf">1</span> <span class="n">positional</span> <span class="n">argument</span> <span class="p">(</span><span class="mf">2</span> <span class="n">given</span><span class="p">)</span> +</pre></div> + +</div> +</div> +<div class="section" id="the-solution"> +<h1><a class="toc-backref" href="#id6">The solution</a></h1> +<p>The solution is to provide a generic factory of generators, which +hides the complexity of making signature-preserving decorators +from the application programmer. The <tt class="docutils literal">decorator</tt> function in +the <tt class="docutils literal">decorator</tt> module is such a factory:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span> +</pre></div> + +</div> +<p><tt class="docutils literal">decorator</tt> 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 <tt class="docutils literal">(f, *args, **kw)</tt> and it must call the original function <tt class="docutils literal">f</tt> +with arguments <tt class="docutils literal">args</tt> and <tt class="docutils literal">kw</tt>, implementing the wanted capability, +i.e. memoization in this case:</p> +<pre class="literal-block"> +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 +</pre> +<p>At this point you can define your decorator as follows:</p> +<pre class="literal-block"> +def memoize(f): + f.cache = {} + return decorator(_memoize, f) +</pre> +<p>The difference with respect to the Python 2.5 approach, which is based +on nested functions, is that the decorator module forces you to lift +the inner function at the outer level (<em>flat is better than nested</em>). +Moreover, you are forced to pass explicitly the function you want to +decorate to the caller function.</p> +<p>Here is a test of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">heavy_computation</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> +<span class="o">...</span> <span class="k">return</span> <span class="s">"done"</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">heavy_computation</span><span class="p">())</span> <span class="c"># the first time it will take 2 seconds</span> +<span class="n">done</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">heavy_computation</span><span class="p">())</span> <span class="c"># the second time it will be instantaneous</span> +<span class="n">done</span> +</pre></div> + +</div> +<p>The signature of <tt class="docutils literal">heavy_computation</tt> is the one you would expect:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">getargspec</span><span class="p">(</span><span class="n">heavy_computation</span><span class="p">))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +</div> +<div class="section" id="a-trace-decorator"> +<h1><a class="toc-backref" href="#id7">A <tt class="docutils literal">trace</tt> decorator</a></h1> +<p>As an additional example, here is how you can define a trivial +<tt class="docutils literal">trace</tt> decorator, which prints a message everytime the traced +function is called:</p> +<pre class="literal-block"> +def _trace(f, *args, **kw): + print("calling %s with args %s, %s" % (f.__name__, args, kw)) + return f(*args, **kw) +</pre> +<pre class="literal-block"> +def trace(f): + return decorator(_trace, f) +</pre> +<p>Here is an example of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> +</pre></div> + +</div> +<p>It is immediate to verify that <tt class="docutils literal">f1</tt> works</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f1</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,),</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>and it that it has the correct signature:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">getargspec</span><span class="p">(</span><span class="n">f1</span><span class="p">))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span> +</pre></div> + +</div> +<p>The same decorator works with functions of any signature:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mf">1</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mf">2</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> +<span class="o">...</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">,</span> <span class="mf">2</span><span class="p">),</span> <span class="p">{}</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">getargspec</span><span class="p">(</span><span class="n">f</span><span class="p">))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">,</span> <span class="s">'z'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">))</span> +</pre></div> + +</div> +</div> +<div class="section" id="decorator-is-a-decorator"> +<h1><a class="toc-backref" href="#id8"><tt class="docutils literal">decorator</tt> is a decorator</a></h1> +<p>It may be annoying to write a caller function (like the <tt class="docutils literal">_trace</tt> +function above) and then a trivial wrapper +(<tt class="docutils literal">def trace(f): return decorator(_trace, f)</tt>) every time. For this reason, +the <tt class="docutils literal">decorator</tt> module provides an easy shortcut to convert +the caller function into a signature-preserving decorator: +you can just call <tt class="docutils literal">decorator</tt> with a single argument. +In our example you can just write <tt class="docutils literal">trace = decorator(_trace)</tt>. +The <tt class="docutils literal">decorator</tt> function can also be used as a signature-changing +decorator, just as <tt class="docutils literal">classmethod</tt> and <tt class="docutils literal">staticmethod</tt>. +However, <tt class="docutils literal">classmethod</tt> and <tt class="docutils literal">staticmethod</tt> return generic +objects which are not callable, while <tt class="docutils literal">decorator</tt> returns +signature-preserving decorators, i.e. functions of a single argument. +For instance, you can write directly</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@decorator</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">trace</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> +<span class="o">...</span> <span class="k">print</span><span class="p">(</span><span class="s">"calling </span><span class="si">%s</span><span class="s"> with args </span><span class="si">%s</span><span class="s">, </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kw</span><span class="p">))</span> +<span class="o">...</span> <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> +</pre></div> + +</div> +<p>and now <tt class="docutils literal">trace</tt> will be a decorator. Actually <tt class="docutils literal">trace</tt> is a <tt class="docutils literal">partial</tt> +object which can be used as a decorator:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">trace</span> +<span class="o"><</span><span class="n">function</span> <span class="n">trace</span> <span class="n">at</span> <span class="mf">0</span><span class="n">x</span><span class="o">...></span> +</pre></div> + +</div> +<p>Here is an example of usage:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">func</span><span class="p">():</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="n">func</span><span class="p">()</span> +<span class="n">calling</span> <span class="n">func</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(),</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>If you are using an old Python version (Python 2.4) the +<tt class="docutils literal">decorator</tt> module provides a poor man replacement for +<tt class="docutils literal">functools.partial</tt>.</p> +</div> +<div class="section" id="blocking"> +<h1><a class="toc-backref" href="#id9"><tt class="docutils literal">blocking</tt></a></h1> +<p>Sometimes one has to deal with blocking resources, such as <tt class="docutils literal">stdin</tt>, 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:</p> +<pre class="literal-block"> +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) +</pre> +<p>Functions decorated with <tt class="docutils literal">blocking</tt> will return a busy message if +the resource is unavailable, and the intended result if the resource is +available. For instance:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@blocking</span><span class="p">(</span><span class="s">"Please wait ..."</span><span class="p">)</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">read_data</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span> <span class="c"># simulate a blocking resource</span> +<span class="o">...</span> <span class="k">return</span> <span class="s">"some data"</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">read_data</span><span class="p">())</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">read_data</span><span class="p">())</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">read_data</span><span class="p">())</span> <span class="c"># data is not available yet</span> +<span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.1</span><span class="p">)</span> <span class="c"># after 3.1 seconds, data is available</span> +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">read_data</span><span class="p">())</span> +<span class="n">some</span> <span class="n">data</span> +</pre></div> + +</div> +</div> +<div class="section" id="async"> +<h1><a class="toc-backref" href="#id10"><tt class="docutils literal">async</tt></a></h1> +<p>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 <tt class="docutils literal">call(self, func, *args, **kw)</tt> and to call +it in the <tt class="docutils literal">__call__(self, func)</tt> method.</p> +<p>As an example, here 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 <tt class="docutils literal">on_success</tt>, <tt class="docutils literal">on_failure</tt> and <tt class="docutils literal">on_closing</tt>, +to specify how to manage the function call. +The implementation is the following:</p> +<pre class="literal-block"> +def on_success(result): # default implementation + "Called on the result of the function" + return result +</pre> +<pre class="literal-block"> +def on_failure(exc_info): # default implementation + "Called if the function fails" + pass +</pre> +<pre class="literal-block"> +def on_closing(): # default implementation + "Called at the end, both in case of success and failure" + pass +</pre> +<pre class="literal-block"> +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): + self.threadfactory = threadfactory + + def __call__(self, func, 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): + def func_wrapper(): + try: + result = func(*args, **kw) + except: + func.on_failure(sys.exc_info()) + else: + return func.on_success(result) + finally: + func.on_closing() + name = '%s-%s' % (func.__name__, next(func.counter)) + thread = self.threadfactory(None, func_wrapper, name) + thread.start() + return thread +</pre> +<p>The decorated function returns +the current execution thread, which can be stored and checked later, for +instance to verify that the thread <tt class="docutils literal">.isAlive()</tt>.</p> +<p>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:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">async</span> <span class="o">=</span> <span class="n">Async</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">)</span> + +<span class="o">>>></span> <span class="n">datalist</span> <span class="o">=</span> <span class="p">[]</span> <span class="c"># for simplicity the written data are stored into a list.</span> + +<span class="o">>>></span> <span class="nd">@async</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> +<span class="o">...</span> <span class="c"># append data to the datalist by locking</span> +<span class="o">...</span> <span class="k">with</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">():</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># emulate some long running operation</span> +<span class="o">...</span> <span class="n">datalist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> +<span class="o">...</span> <span class="c"># other operations not requiring a lock here</span> +</pre></div> + +</div> +<p>Each call to <tt class="docutils literal">write</tt> will create a new writer thread, but there will +be no synchronization problems since <tt class="docutils literal">write</tt> is locked.</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data1"</span><span class="p">)</span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="o">.</span><span class="mf">1</span><span class="p">)</span> <span class="c"># wait a bit, so we are sure data2 is written after data1</span> + +<span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data2"</span><span class="p">)</span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">2</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> + +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> <span class="c"># wait for the writers to complete</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">datalist</span><span class="p">)</span> +<span class="p">[</span><span class="s">'data1'</span><span class="p">,</span> <span class="s">'data2'</span><span class="p">]</span> +</pre></div> + +</div> +</div> +<div class="section" id="the-functionmaker-class"> +<h1><a class="toc-backref" href="#id11">The <tt class="docutils literal">FunctionMaker</tt> class</a></h1> +<p>You may wonder about how the functionality of the <tt class="docutils literal">decorator</tt> module +is implemented. The basic building block is +a <tt class="docutils literal">FunctionMaker</tt> 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 <tt class="docutils literal">FunctionMaker</tt> 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 (<tt class="docutils literal">decorator_apply</tt>).</p> +<p><tt class="docutils literal">FunctionMaker</tt> provides a <tt class="docutils literal">.create</tt> 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 <tt class="docutils literal">exec</tt>. Here is an example:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> <span class="c"># a function with a generic signature</span> +<span class="o">...</span> <span class="k">print</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">kw</span><span class="p">)</span> + +<span class="o">>>></span> <span class="n">f1</span> <span class="o">=</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">'f1(a, b)'</span><span class="p">,</span> <span class="s">'f(a, b)'</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="n">f</span><span class="p">))</span> +<span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="mf">2</span><span class="p">)</span> +<span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{}</span> +</pre></div> + +</div> +<p>It is important to notice that the function body is interpolated +before being executed, so be careful with the <tt class="docutils literal">%</tt> sign!</p> +<p><tt class="docutils literal">FunctionMaker.create</tt> 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 <tt class="docutils literal">__doc__</tt>.</p> +<p>For debugging/introspection purposes it may be useful to see +the source code of the generated function; to do that, just +pass the flag <tt class="docutils literal">addsource=True</tt> and a <tt class="docutils literal">__source__</tt> attribute will +be added to the generated function:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span> <span class="o">=</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> +<span class="o">...</span> <span class="s">'f1(a, b)'</span><span class="p">,</span> <span class="s">'f(a, b)'</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="n">f</span><span class="p">),</span> <span class="n">addsource</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">f1</span><span class="o">.</span><span class="n">__source__</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> + <span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +<p><tt class="docutils literal">FunctionMaker.create</tt> 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 <tt class="docutils literal">FunctionMaker.create</tt> +instead of <tt class="docutils literal">decorator</tt>, since it gives you direct access to the body +of the generated function. For instance, suppose you want to instrument +the <tt class="docutils literal">__init__</tt> 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 <tt class="docutils literal">FunctionMaker.create</tt> +is a function, a <tt class="docutils literal">FunctionMaker</tt> object is instantiated internally, +with attributes <tt class="docutils literal">args</tt>, <tt class="docutils literal">varargs</tt>, +<tt class="docutils literal">keywords</tt> and <tt class="docutils literal">defaults</tt> which are the +the return values of the standard library function <tt class="docutils literal">inspect.getargspec</tt>. +For each argument in the <tt class="docutils literal">args</tt> (which is a list of strings containing +the names of the mandatory arguments) an attribute <tt class="docutils literal">arg0</tt>, <tt class="docutils literal">arg1</tt>, +..., <tt class="docutils literal">argN</tt> is also generated. Finally, there is a <tt class="docutils literal">signature</tt> +attribute, a string with the signature of the original function.</p> +<p>Notice that while I do not have plans +to change or remove the functionality provided in the +<tt class="docutils literal">FunctionMaker</tt> 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 +<tt class="docutils literal">decorator</tt> has been there from version 0.1 and it is guaranteed to +stay there forever.</p> +</div> +<div class="section" id="getting-the-source-code"> +<h1><a class="toc-backref" href="#id12">Getting the source code</a></h1> +<p>Internally <tt class="docutils literal">FunctionMaker.create</tt> uses <tt class="docutils literal">exec</tt> to generate the +decorated function. Therefore +<tt class="docutils literal">inspect.getsource</tt> will not work for decorated functions. That +means that the usual '??' trick in IPython will give you the (right on +the spot) message <tt class="docutils literal">Dynamically generated function. No source code +available</tt>. In the past I have considered this acceptable, since +<tt class="docutils literal">inspect.getsource</tt> does not really work even with regular +decorators. In that case <tt class="docutils literal">inspect.getsource</tt> gives you the wrapper +source code which is probably not what you want:</p> +<pre class="literal-block"> +def identity_dec(func): + def wrapper(*args, **kw): + return func(*args, **kw) + return wrapper +</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="nd">@identity_dec</span> +<span class="k">def</span> <span class="nf">example</span><span class="p">():</span> <span class="k">pass</span> + +<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">example</span><span class="p">))</span> + <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> + <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +<p>(see bug report <a class="reference external" href="http://bugs.python.org/issue1764286">1764286</a> for an explanation of what is happening). +Unfortunately the bug is still there, even in Python 2.6 and 3.0. +There is however a workaround. The decorator module adds an +attribute <tt class="docutils literal">.undecorated</tt> to the decorated function, containing +a reference to the original function. The easy way to get +the source code is to call <tt class="docutils literal">inspect.getsource</tt> on the +undecorated function:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">factorial</span><span class="o">.</span><span class="n">undecorated</span><span class="p">))</span> +<span class="nd">@tail_recursive</span> +<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">acc</span><span class="o">=</span><span class="mf">1</span><span class="p">):</span> + <span class="s">"The good old factorial"</span> + <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mf">0</span><span class="p">:</span> <span class="k">return</span> <span class="n">acc</span> + <span class="k">return</span> <span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> +<span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> +</pre></div> + +</div> +</div> +<div class="section" id="dealing-with-third-party-decorators"> +<h1><a class="toc-backref" href="#id13">Dealing with third party decorators</a></h1> +<p>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 <tt class="docutils literal">decorator</tt>. You can use a +<tt class="docutils literal">FunctionMaker</tt> to implement that functionality as follows:</p> +<pre class="literal-block"> +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)), undecorated=func) +</pre> +<p><tt class="docutils literal">decorator_apply</tt> sets the attribute <tt class="docutils literal">.undecorated</tt> of the generated +function to the original function, so that you can get the right +source code.</p> +<p>Notice that I am not providing this functionality in the <tt class="docutils literal">decorator</tt> +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 <tt class="docutils literal">decorator_apply</tt> to your toolbox and +use it if you need to.</p> +<p>In order to give an example of usage of <tt class="docutils literal">decorator_apply</tt>, 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, +<a class="reference external" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691</a>.</p> +<pre class="literal-block"> +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 +</pre> +<p>Here the decorator is implemented as a class returning callable +objects.</p> +<pre class="literal-block"> +def tail_recursive(func): + return decorator_apply(TailRecursive, func) +</pre> +<p>Here is how you apply the upgraded decorator to the good old factorial:</p> +<pre class="literal-block"> +@tail_recursive +def factorial(n, acc=1): + "The good old factorial" + if n == 0: return acc + return factorial(n-1, n*acc) +</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">factorial</span><span class="p">(</span><span class="mf">4</span><span class="p">))</span> +<span class="mf">24</span> +</pre></div> + +</div> +<p>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 <tt class="docutils literal">factorial(1001)</tt> 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</p> +<pre class="literal-block"> +def fact(n): # this is not tail-recursive + if n == 0: return 1 + return n * fact(n-1) +</pre> +<p>(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).</p> +</div> +<div class="section" id="caveats-and-limitations"> +<h1><a class="toc-backref" href="#id14">Caveats and limitations</a></h1> +<p>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:</p> +<pre class="literal-block"> +$ 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()" +</pre> +<p>On my MacBook, using the <tt class="docutils literal">do_nothing</tt> decorator instead of the +plain function is more than three times slower:</p> +<pre class="literal-block"> +$ bash performance.sh +1000000 loops, best of 3: 0.669 usec per loop +1000000 loops, best of 3: 0.181 usec per loop +</pre> +<p>It should be noted that a real life function would probably do +something more useful than <tt class="docutils literal">f</tt> 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.</p> +<p>You should be aware that decorators will make your tracebacks +longer and more difficult to understand. Consider this example:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> +<span class="o">...</span> <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> +</pre></div> + +</div> +<p>Calling <tt class="docutils literal">f()</tt> will give you a <tt class="docutils literal">ZeroDivisionError</tt>, but since the +function is decorated the traceback will be longer:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f</span><span class="p">()</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> + <span class="n">File</span> <span class="s">"<string>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="n">File</span> <span class="s">"<doctest __main__[22]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">4</span><span class="p">,</span> <span class="ow">in</span> <span class="n">trace</span> + <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> + <span class="n">File</span> <span class="s">"<doctest __main__[51]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">3</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> +<span class="ne">ZeroDivisionError</span><span class="p">:</span> <span class="nb">int</span> <span class="n">division</span> <span class="ow">or</span> <span class="n">modulo</span> <span class="n">by</span> <span class="n">zero</span> +</pre></div> + +</div> +<p>You see here the inner call to the decorator <tt class="docutils literal">trace</tt>, which calls +<tt class="docutils literal"><span class="pre">f(*args,</span> **kw)</tt>, and a reference to <tt class="docutils literal">File <span class="pre">"<string>",</span> line 2, in f</tt>. +This latter reference is due to the fact that internally the decorator +module uses <tt class="docutils literal">exec</tt> to generate the decorated function. Notice that +<tt class="docutils literal">exec</tt> is <em>not</em> responsibile for the performance penalty, since is the +called <em>only once</em> at function decoration time, and not every time +the decorated function is called.</p> +<p>At present, there is no clean way to avoid <tt class="docutils literal">exec</tt>. 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 <a class="reference external" href="http://www.python.org/dev/peps/pep-0362">362</a>) 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 <tt class="docutils literal">decorator</tt> module is still useful. +Actually, this is one of the main reasons why I keep maintaining +the module and releasing new versions.</p> +<p>In the present implementation, decorators generated by <tt class="docutils literal">decorator</tt> +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 +<tt class="docutils literal">inspect</tt> module in the standard library.</p> +<p>There is a restriction on the names of the arguments: for instance, +if try to call an argument <tt class="docutils literal">_call_</tt> or <tt class="docutils literal">_func_</tt> +you will get a <tt class="docutils literal">NameError</tt>:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">_func_</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> +<span class="o">...</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="o">...</span> +<span class="ne">NameError</span><span class="p">:</span> <span class="n">_func_</span> <span class="ow">is</span> <span class="n">overridden</span> <span class="ow">in</span> +<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">_func_</span><span class="p">):</span> + <span class="k">return</span> <span class="n">_call_</span><span class="p">(</span><span class="n">_func_</span><span class="p">,</span> <span class="n">_func_</span><span class="p">)</span> +</pre></div> + +</div> +<p>Finally, the implementation is such that the decorated function contains +a <em>copy</em> of the original function dictionary +(<tt class="docutils literal">vars(decorated_f) is not vars(f)</tt>):</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> <span class="k">pass</span> <span class="c"># the original function</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr1</span> <span class="o">=</span> <span class="s">"something"</span> <span class="c"># setting an attribute</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr2</span> <span class="o">=</span> <span class="s">"something else"</span> <span class="c"># setting another attribute</span> + +<span class="o">>>></span> <span class="n">traced_f</span> <span class="o">=</span> <span class="n">trace</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="c"># the decorated function</span> + +<span class="o">>>></span> <span class="n">traced_f</span><span class="o">.</span><span class="n">attr1</span> +<span class="s">'something'</span> +<span class="o">>>></span> <span class="n">traced_f</span><span class="o">.</span><span class="n">attr2</span> <span class="o">=</span> <span class="s">"something different"</span> <span class="c"># setting attr</span> +<span class="o">>>></span> <span class="n">f</span><span class="o">.</span><span class="n">attr2</span> <span class="c"># the original attribute did not change</span> +<span class="s">'something else'</span> +</pre></div> + +</div> +</div> +<div class="section" id="compatibility-notes"> +<h1><a class="toc-backref" href="#id15">Compatibility notes</a></h1> +<p>Version 3.2 is the first version of the <tt class="docutils literal">decorator</tt> module to officially +support Python 3.0. Actually, the module has supported Python 3.0 from +the beginning, via the <tt class="docutils literal">2to3</tt> conversion tool, but this step has +been now integrated in the build process, thanks to the <a class="reference external" href="http://packages.python.org/distribute/">distribute</a> +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 possibly only now that <a class="reference external" href="http://docutils.sourceforge.net/">docutils</a> and <a class="reference external" href="http://pygments.org/">pygments</a> +have been ported to Python 3.</p> +<p>The <tt class="docutils literal">decorator</tt> module <em>per se</em> does not contain any change, apart +from the removal of the functions <tt class="docutils literal">get_info</tt> and <tt class="docutils literal">new_wrapper</tt>, +which have been deprecated for years. <tt class="docutils literal">get_info</tt> has been removed +since it was little used and since it had to be changed anyway to work +with Python 3.0; <tt class="docutils literal">new_wrapper</tt> has been removed since it was +useless: its major use case (converting signature changing decorators +to signature preserving decorators) has been subsumed by +<tt class="docutils literal">decorator_apply</tt> and the other use case can be managed with the +<tt class="docutils literal">FunctionMaker</tt>.</p> +<p>There are a few changes in the documentation: I removed the +<tt class="docutils literal">decorator_factory</tt> 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. +Notice that there is no support for Python 3 <a class="reference external" href="http://www.python.org/dev/peps/pep-3107/">function annotations</a> +since it seems premature at the moment, when most people are +still using Python 2.X.</p> +<p>Finally <tt class="docutils literal">decorator</tt> cannot be used as a class decorator and the +<a class="reference external" href="http://www.phyast.pitt.edu/~micheles/python/documentation.html#class-decorators-and-decorator-factories">functionality introduced in version 2.3</a> has been removed. That +means that in order to define decorator factories with classes you +need to define the <tt class="docutils literal">__call__</tt> 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.</p> +<p>The examples shown here have been tested with Python 2.6. Python 2.4 +is also supported - of course the examples requiring the <tt class="docutils literal">with</tt> +statement will not work there. Python 2.5 works fine, but if you +run the examples here in the interactive interpreter +you will notice a few differences since +<tt class="docutils literal">getargspec</tt> returns an <tt class="docutils literal">ArgSpec</tt> namedtuple instead of a regular +tuple. That means that running the file +<tt class="docutils literal">documentation.py</tt> under Python 2.5 will a few errors, but +they are not serious.</p> +</div> +<div class="section" id="licence"> +<h1><a class="toc-backref" href="#id16">LICENCE</a></h1> +<p>Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met:</p> +<pre class="literal-block"> +Copyright (c) 2005, Michele Simionato +All rights reserved. + +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. +</pre> +<p>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!</p> +</div> +</div> +</body> +</html> diff --git a/documentation3.py b/documentation3.py new file mode 100644 index 0000000..259d0ba --- /dev/null +++ b/documentation3.py @@ -0,0 +1,1006 @@ +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 for Python 2.5 could be the following (notice +that in general it is impossible to memoize correctly something +that depends on non-hashable arguments): + +$$memoize25 + +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 ``memoize25`` returns a function with a +*different signature* from the original function. + +Consider for instance the following case: + +.. code-block:: python + + >>> @memoize25 + ... 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) + 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 Python 2.5 approach, which is based +on nested functions, is that the decorator module forces you to lift +the inner function at the outer level (*flat is better than nested*). +Moreover, you are forced to pass explicitly the function you want to +decorate 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)) + +``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.__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 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. + +As an example, here 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: + +$$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 = 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'] + +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.6 and 3.0. +There is however a workaround. The decorator module adds an +attribute ``.undecorated`` 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.undecorated)) + @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 ``.undecorated`` 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() + 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: int 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. + +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.2 is the first version of the ``decorator`` module to officially +support Python 3.0. Actually, the module has supported Python 3.0 from +the beginning, via the ``2to3`` conversion tool, but this step has +been now integrated in the build process, thanks to 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 possibly only now that docutils_ and pygments_ +have been ported to Python 3. + +The ``decorator`` module *per se* does not contain any 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`` and 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. +Notice that there is no support for Python 3 `function annotations`_ +since it seems premature at the moment, when most people are +still using Python 2.X. + +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 here 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 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 +--------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met:: + + Copyright (c) 2005, Michele Simionato + All rights reserved. + + 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)), undecorated=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): + self.threadfactory = threadfactory + + def __call__(self, func, 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): + def func_wrapper(): + try: + result = func(*args, **kw) + except: + func.on_failure(sys.exc_info()) + else: + return func.on_success(result) + finally: + func.on_closing() + name = '%s-%s' % (func.__name__, next(func.counter)) + 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 memoize25(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' + """ + +if __name__ == '__main__': + import doctest; doctest.testmod() diff --git a/index.html b/index.html new file mode 100644 index 0000000..4f9912a --- /dev/null +++ b/index.html @@ -0,0 +1,330 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" /> +<title>Decorator module</title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 5951 2009-05-18 18:03:10Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left{ + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: left } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document" id="decorator-module"> +<h1 class="title">Decorator module</h1> + +<p>Dependencies:</p> +<p>The decorator module requires Python 2.4.</p> +<p>Installation:</p> +<p>$ python setup.py install</p> +<p>Testing:</p> +<p>For Python 2.4, 2.5, 2.6, 2.7 run</p> +<p>$ python documentation.py</p> +<p>for Python 3.X run</p> +<p>$ python documentation3.py</p> +<p>You will see a few innocuous errors with Python 2.4 and 2.5, because +some inner details such as the introduction of the ArgSpec namedtuple +and Thread.__repr__ changed. You may safely ignore them.</p> +<p>Notice:</p> +<p>You may get into trouble if in your system there is an older version +of the decorator module; in such a case remove the old version.</p> +<p>Documentation:</p> +<p>There are two versions of the documentation, one for <a class="reference external" href="documentation.html">Python 2</a> and one +for <a class="reference external" href="documentation3.html">Python 3</a> .</p> +</div> +</body> +</html> diff --git a/performance.sh b/performance.sh deleted file mode 100644 index 75e8928..0000000 --- a/performance.sh +++ /dev/null @@ -1,16 +0,0 @@ -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()" @@ -2,24 +2,29 @@ try: from setuptools import setup except ImportError: from distutils.core import setup +import os.path -from decorator import __version__ as VERSION +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__': - try: - docfile = file('/tmp/documentation.html') - except IOError: # file not found, ignore - doc = '' - else: - doc = docfile.read() setup(name='decorator', version=VERSION, description='Better living through Python with decorators', - long_description='</pre>%s<pre>' % doc, + 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"], @@ -31,5 +36,5 @@ if __name__ == '__main__': 'Programming Language :: Python', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities'], + use_2to3=True, zip_safe=False) - diff --git a/decorator.py b/src/decorator.py index 7d511fa..9f0b21a 100644 --- a/decorator.py +++ b/src/decorator.py @@ -33,6 +33,7 @@ __version__ = '3.2.0' __all__ = ["decorator", "FunctionMaker", "partial"] import os, sys, re, inspect, string, warnings + try: from functools import partial except ImportError: # for Python version < 2.5 |