diff options
Diffstat (limited to 'decorator/documentation.html')
-rw-r--r-- | decorator/documentation.html | 485 |
1 files changed, 289 insertions, 196 deletions
diff --git a/decorator/documentation.html b/decorator/documentation.html index 4fb7fc8..25a5709 100644 --- a/decorator/documentation.html +++ b/decorator/documentation.html @@ -3,7 +3,7 @@ <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.7: http://docutils.sourceforge.net/" /> +<meta name="generator" content="Docutils 0.8.1: http://docutils.sourceforge.net/" /> <title>The decorator module</title> <meta name="author" content="Michele Simionato" /> <style type="text/css"> @@ -83,10 +83,10 @@ <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.3.2 (2011-11-09)</td></tr> +<td>3.4.0 (2012-10-18)</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.3.2">http://pypi.python.org/pypi/decorator/3.3.2</a></td> +<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.4.0">http://pypi.python.org/pypi/decorator/3.4.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> @@ -105,12 +105,13 @@ <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> +<li><a class="reference internal" href="#contextmanager" id="id11">contextmanager</a></li> +<li><a class="reference internal" href="#the-functionmaker-class" id="id12">The <tt class="docutils literal">FunctionMaker</tt> class</a></li> +<li><a class="reference internal" href="#getting-the-source-code" id="id13">Getting the source code</a></li> +<li><a class="reference internal" href="#dealing-with-third-party-decorators" id="id14">Dealing with third party decorators</a></li> +<li><a class="reference internal" href="#caveats-and-limitations" id="id15">Caveats and limitations</a></li> +<li><a class="reference internal" href="#compatibility-notes" id="id16">Compatibility notes</a></li> +<li><a class="reference internal" href="#licence" id="id17">LICENCE</a></li> </ul> </div> <div class="section" id="introduction"> @@ -179,22 +180,24 @@ but they do not preserve the signature. A simple implementation could be the following (notice that in general it is impossible to memoize correctly something that depends on non-hashable arguments):</p> -<pre class="literal-block"> -def memoize_uw(func): - func.cache = {} - def memoize(*args, **kw): - if kw: # frozenset is used to ensure hashability - key = args, frozenset(kw.iteritems()) - else: - key = args - cache = func.cache - if key in cache: - return cache[key] - else: - cache[key] = result = func(*args, **kw) - return result - return functools.update_wrapper(memoize, func) -</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">memoize_uw</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> + <span class="n">func</span><span class="o">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span> + <span class="k">def</span> <span class="nf">memoize</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">if</span> <span class="n">kw</span><span class="p">:</span> <span class="c"># frozenset is used to ensure hashability</span> + <span class="n">key</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="nb">frozenset</span><span class="p">(</span><span class="n">kw</span><span class="o">.</span><span class="n">iteritems</span><span class="p">())</span> + <span class="k">else</span><span class="p">:</span> + <span class="n">key</span> <span class="o">=</span> <span class="n">args</span> + <span class="n">cache</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">cache</span> + <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cache</span><span class="p">:</span> + <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> + <span class="k">else</span><span class="p">:</span> + <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span> <span class="o">=</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="k">return</span> <span class="n">result</span> + <span class="k">return</span> <span class="n">functools</span><span class="o">.</span><span class="n">update_wrapper</span><span class="p">(</span><span class="n">memoize</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span> +</pre></div> + +</div> <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 @@ -255,25 +258,29 @@ 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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">_memoize</span><span class="p">(</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="k">if</span> <span class="n">kw</span><span class="p">:</span> <span class="c"># frozenset is used to ensure hashability</span> + <span class="n">key</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="nb">frozenset</span><span class="p">(</span><span class="n">kw</span><span class="o">.</span><span class="n">iteritems</span><span class="p">())</span> + <span class="k">else</span><span class="p">:</span> + <span class="n">key</span> <span class="o">=</span> <span class="n">args</span> + <span class="n">cache</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">cache</span> <span class="c"># attributed added by memoize</span> + <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cache</span><span class="p">:</span> + <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> + <span class="k">else</span><span class="p">:</span> + <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span> <span class="o">=</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="k">return</span> <span class="n">result</span> +</pre></div> + +</div> <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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">memoize</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">f</span><span class="o">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span> + <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">_memoize</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> +</pre></div> + +</div> <p>The difference with respect to the <tt class="docutils literal">memoize_uw</tt> 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>). @@ -307,15 +314,19 @@ decorate to the caller function.</p> <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> +<div class="codeblock python"> +<div class="highlight"><pre><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="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">__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="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> +<div class="codeblock python"> +<div class="highlight"><pre><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="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">_trace</span><span class="p">,</span> <span class="n">f</span><span class="p">)</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> @@ -419,21 +430,23 @@ object which can be used as a decorator:</p> 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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">blocking</span><span class="p">(</span><span class="n">not_avail</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">blocking</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="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"thread"</span><span class="p">):</span> <span class="c"># no thread running</span> + <span class="k">def</span> <span class="nf">set_result</span><span class="p">():</span> <span class="n">f</span><span class="o">.</span><span class="n">result</span> <span class="o">=</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">f</span><span class="o">.</span><span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">set_result</span><span class="p">)</span> + <span class="n">f</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> + <span class="k">return</span> <span class="n">not_avail</span> + <span class="k">elif</span> <span class="n">f</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">isAlive</span><span class="p">():</span> + <span class="k">return</span> <span class="n">not_avail</span> + <span class="k">else</span><span class="p">:</span> <span class="c"># the thread is ended, return the stored result</span> + <span class="k">del</span> <span class="n">f</span><span class="o">.</span><span class="n">thread</span> + <span class="k">return</span> <span class="n">f</span><span class="o">.</span><span class="n">result</span> + <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">blocking</span><span class="p">)</span> +</pre></div> + +</div> <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> @@ -467,69 +480,74 @@ available. For instance:</p> 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 +callable objects that can be converted into decorators.</p> +<p>As an example, here will I show a decorator which is able to convert a blocking function into an asynchronous function. The function, when called, is executed in a separate thread. Moreover, it is possible to set three callbacks <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> +to specify how to manage the function call (of course the code here +is just an example, it is not a recommended way of doing multi-threaded +programming). The implementation is the following:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_success</span><span class="p">(</span><span class="n">result</span><span class="p">):</span> <span class="c"># default implementation</span> + <span class="s">"Called on the result of the function"</span> + <span class="k">return</span> <span class="n">result</span> +</pre></div> + +</div> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_failure</span><span class="p">(</span><span class="n">exc_info</span><span class="p">):</span> <span class="c"># default implementation</span> + <span class="s">"Called if the function fails"</span> + <span class="k">pass</span> +</pre></div> + +</div> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_closing</span><span class="p">():</span> <span class="c"># default implementation</span> + <span class="s">"Called at the end, both in case of success and failure"</span> + <span class="k">pass</span> +</pre></div> + +</div> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">class</span> <span class="nc">Async</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> + <span class="sd">"""</span> +<span class="sd"> A decorator converting blocking functions into asynchronous</span> +<span class="sd"> functions, by using threads or processes. Examples:</span> + +<span class="sd"> async_with_threads = Async(threading.Thread)</span> +<span class="sd"> async_with_processes = Async(multiprocessing.Process)</span> +<span class="sd"> """</span> + + <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">threadfactory</span><span class="p">,</span> <span class="n">on_success</span><span class="o">=</span><span class="n">on_success</span><span class="p">,</span> + <span class="n">on_failure</span><span class="o">=</span><span class="n">on_failure</span><span class="p">,</span> <span class="n">on_closing</span><span class="o">=</span><span class="n">on_closing</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">threadfactory</span> <span class="o">=</span> <span class="n">threadfactory</span> + <span class="bp">self</span><span class="o">.</span><span class="n">on_success</span> <span class="o">=</span> <span class="n">on_success</span> + <span class="bp">self</span><span class="o">.</span><span class="n">on_failure</span> <span class="o">=</span> <span class="n">on_failure</span> + <span class="bp">self</span><span class="o">.</span><span class="n">on_closing</span> <span class="o">=</span> <span class="n">on_closing</span> + + <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</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="k">try</span><span class="p">:</span> + <span class="n">counter</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">counter</span> + <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span> <span class="c"># instantiate the counter at the first call</span> + <span class="n">counter</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">itertools</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> + <span class="n">name</span> <span class="o">=</span> <span class="s">'</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">func</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">counter</span><span class="o">.</span><span class="n">next</span><span class="p">())</span> + <span class="k">def</span> <span class="nf">func_wrapper</span><span class="p">():</span> + <span class="k">try</span><span class="p">:</span> + <span class="n">result</span> <span class="o">=</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="k">except</span><span class="p">:</span> + <span class="bp">self</span><span class="o">.</span><span class="n">on_failure</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">())</span> + <span class="k">else</span><span class="p">:</span> + <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_success</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> + <span class="k">finally</span><span class="p">:</span> + <span class="bp">self</span><span class="o">.</span><span class="n">on_closing</span><span class="p">()</span> + <span class="n">thread</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">threadfactory</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">func_wrapper</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> + <span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> + <span class="k">return</span> <span class="n">thread</span> +</pre></div> + +</div> <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> @@ -538,7 +556,7 @@ 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> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">async</span> <span class="o">=</span> <span class="n">decorator</span><span class="p">(</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> @@ -571,8 +589,71 @@ be no synchronization problems since <tt class="docutils literal">write</tt> is </div> </div> +<div class="section" id="contextmanager"> +<h1><a class="toc-backref" href="#id11">contextmanager</a></h1> +<p>For a long time Python had in its standard library a <tt class="docutils literal">contextmanager</tt> +decorator, able to convert generator functions into <tt class="docutils literal">GeneratorContextManager</tt> +factories. For instance if you write</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">contextmanager</span> +<span class="o">>>></span> <span class="nd">@contextmanager</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">before_after</span><span class="p">(</span><span class="n">before</span><span class="p">,</span> <span class="n">after</span><span class="p">):</span> +<span class="o">...</span> <span class="k">print</span><span class="p">(</span><span class="n">before</span><span class="p">)</span> +<span class="o">...</span> <span class="k">yield</span> +<span class="o">...</span> <span class="k">print</span><span class="p">(</span><span class="n">after</span><span class="p">)</span> +</pre></div> + +</div> +<p>then <tt class="docutils literal">before_after</tt> is a factory function returning +<tt class="docutils literal">GeneratorContextManager</tt> objects which can be used with +the <tt class="docutils literal">with</tt> statement:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">ba</span> <span class="o">=</span> <span class="n">before_after</span><span class="p">(</span><span class="s">'BEFORE'</span><span class="p">,</span> <span class="s">'AFTER'</span><span class="p">)</span> +<span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="n">ba</span><span class="p">)</span> +<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">contextlib</span><span class="o">.</span><span class="n">GeneratorContextManager</span><span class="s">'></span> +<span class="o">>>></span> <span class="k">with</span> <span class="n">ba</span><span class="p">:</span> +<span class="o">...</span> <span class="k">print</span> <span class="s">'hello'</span> +<span class="n">BEFORE</span> +<span class="n">hello</span> +<span class="n">AFTER</span> +</pre></div> + +</div> +<p>Basically, it is as if the content of the <tt class="docutils literal">with</tt> block was executed +in the place of the <tt class="docutils literal">yield</tt> expression in the generator function. +In Python 3.2 <tt class="docutils literal">GeneratorContextManager</tt> +objects were enhanced with a <tt class="docutils literal">__call__</tt> +method, so that they can be used as decorators as in this example:</p> +<div class="codeblock python"> +<div class="highlight"><pre><span class="o">>>></span> <span class="nd">@ba</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">hello</span><span class="p">():</span> +<span class="o">...</span> <span class="k">print</span> <span class="s">'hello'</span> +<span class="o">...</span> +<span class="o">>>></span> <span class="n">hello</span><span class="p">()</span> +<span class="n">BEFORE</span> +<span class="n">hello</span> +<span class="n">AFTER</span> +</pre></div> + +</div> +<p>The <tt class="docutils literal">ba</tt> decorator is basically inserting a <tt class="docutils literal">with ba:</tt> +block inside the function. +However there two issues: the first is that <tt class="docutils literal">GeneratorContextManager</tt> +objects are callable only in Python 3.2, so the previous example will break +in older versions of Python; the second is that +<tt class="docutils literal">GeneratorContextManager</tt> objects do not preserve the signature +of the decorated functions: the decorated <tt class="docutils literal">hello</tt> function here will have +a generic signature <tt class="docutils literal"><span class="pre">hello(*args,</span> **kwargs)</tt> but will break when +called with more than zero arguments. For such reasons the decorator +module, starting with release 3.4, offers a <tt class="docutils literal">decorator.contextmanager</tt> +decorator that solves both problems and works even in Python 2.5. +The usage is the same and factories decorated with <tt class="docutils literal">decorator.contextmanager</tt> +will returns instances of <tt class="docutils literal">ContextManager</tt>, a subclass of +<tt class="docutils literal">contextlib.GeneratorContextManager</tt> with a <tt class="docutils literal">__call__</tt> method +acting as a signature-preserving decorator.</p> +</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> +<h1><a class="toc-backref" href="#id12">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 @@ -644,7 +725,7 @@ On the other hand, the functionality provided by 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> +<h1><a class="toc-backref" href="#id13">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 @@ -654,12 +735,14 @@ 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="k">def</span> <span class="nf">identity_dec</span><span class="p">(</span><span class="n">func</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="k">return</span> <span class="n">wrapper</span> +</pre></div> + +</div> <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> @@ -691,23 +774,25 @@ undecorated function:</p> </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> +<h1><a class="toc-backref" href="#id14">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)), __wrapped__=func) -</pre> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">decorator_apply</span><span class="p">(</span><span class="n">dec</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span> + <span class="sd">"""</span> +<span class="sd"> Decorate a function by preserving the signature even if dec</span> +<span class="sd"> is not a signature-preserving decorator.</span> +<span class="sd"> """</span> + <span class="k">return</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> + <span class="n">func</span><span class="p">,</span> <span class="s">'return decorated(</span><span class="si">%(signature)s</span><span class="s">)'</span><span class="p">,</span> + <span class="nb">dict</span><span class="p">(</span><span class="n">decorated</span><span class="o">=</span><span class="n">dec</span><span class="p">(</span><span class="n">func</span><span class="p">)),</span> <span class="n">__wrapped__</span><span class="o">=</span><span class="n">func</span><span class="p">)</span> +</pre></div> + +</div> <p><tt class="docutils literal">decorator_apply</tt> sets the attribute <tt class="docutils literal">.__wrapped__</tt> of the generated function to the original function, so that you can get the right source code.</p> @@ -721,51 +806,57 @@ 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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">class</span> <span class="nc">TailRecursive</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> + <span class="sd">"""</span> +<span class="sd"> tail_recursive decorator based on Kay Schluehr's recipe</span> +<span class="sd"> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691</span> +<span class="sd"> with improvements by me and George Sakkis.</span> +<span class="sd"> """</span> + + <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">func</span> <span class="o">=</span> <span class="n">func</span> + <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">True</span> + <span class="bp">self</span><span class="o">.</span><span class="n">CONTINUE</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span> <span class="c"># sentinel</span> + + <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</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">kwd</span><span class="p">):</span> + <span class="n">CONTINUE</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">CONTINUE</span> + <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span><span class="p">:</span> + <span class="n">func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">func</span> + <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">False</span> + <span class="k">try</span><span class="p">:</span> + <span class="k">while</span> <span class="bp">True</span><span class="p">:</span> + <span class="n">result</span> <span class="o">=</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">kwd</span><span class="p">)</span> + <span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="n">CONTINUE</span><span class="p">:</span> <span class="c"># update arguments</span> + <span class="n">args</span><span class="p">,</span> <span class="n">kwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">argskwd</span> + <span class="k">else</span><span class="p">:</span> <span class="c"># last call</span> + <span class="k">return</span> <span class="n">result</span> + <span class="k">finally</span><span class="p">:</span> + <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">True</span> + <span class="k">else</span><span class="p">:</span> <span class="c"># return the arguments of the tail call</span> + <span class="bp">self</span><span class="o">.</span><span class="n">argskwd</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwd</span> + <span class="k">return</span> <span class="n">CONTINUE</span> +</pre></div> + +</div> <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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">tail_recursive</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> + <span class="k">return</span> <span class="n">decorator_apply</span><span class="p">(</span><span class="n">TailRecursive</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span> +</pre></div> + +</div> <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="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="mi">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="mi">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="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> +</pre></div> + +</div> <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="mi">4</span><span class="p">)</span> <span class="mi">24</span> @@ -777,17 +868,19 @@ 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> +<div class="codeblock python"> +<div class="highlight"><pre><span class="k">def</span> <span class="nf">fact</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># this is not tail-recursive</span> + <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span> + <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">fact</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> +</pre></div> + +</div> <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> +<h1><a class="toc-backref" href="#id15">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> @@ -935,7 +1028,7 @@ a <em>copy</em> of the original function dictionary </div> </div> <div class="section" id="compatibility-notes"> -<h1><a class="toc-backref" href="#id15">Compatibility notes</a></h1> +<h1><a class="toc-backref" href="#id16">Compatibility notes</a></h1> <p>Version 3.3 is the first version of the <tt class="docutils literal">decorator</tt> module to fully support Python 3, including <a class="reference external" href="http://www.python.org/dev/peps/pep-3107/">function annotations</a>. Version 3.2 was the first version to support Python 3 via the <tt class="docutils literal">2to3</tt> conversion tool @@ -975,7 +1068,7 @@ tuple. That means that running the file they are not serious.</p> </div> <div class="section" id="licence"> -<h1><a class="toc-backref" href="#id16">LICENCE</a></h1> +<h1><a class="toc-backref" href="#id17">LICENCE</a></h1> <p>Copyright (c) 2005-2012, Michele Simionato All rights reserved.</p> <p>Redistribution and use in source and binary forms, with or without |