diff options
Diffstat (limited to 'decorator/documentation.html')
-rw-r--r-- | decorator/documentation.html | 500 |
1 files changed, 295 insertions, 205 deletions
diff --git a/decorator/documentation.html b/decorator/documentation.html index 9239568..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-09-01)</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,22 +1068,20 @@ 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 modification, are permitted provided that the following conditions are met:</p> -<pre class="literal-block"> -Copyright (c) 2005, Michele Simionato -All rights reserved. - +<blockquote> 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 +distribution.</blockquote> +<p>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 @@ -1001,8 +1092,7 @@ 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> +DAMAGE.</p> <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> |