diff options
Diffstat (limited to 'pypers/pep318/working')
-rwxr-xr-x | pypers/pep318/working/README.txt | 45 | ||||
-rwxr-xr-x | pypers/pep318/working/chatty2.py | 27 | ||||
-rwxr-xr-x | pypers/pep318/working/customdec.py | 67 | ||||
-rwxr-xr-x | pypers/pep318/working/debugger.py | 17 | ||||
-rwxr-xr-x | pypers/pep318/working/decorators.html | 1337 | ||||
-rwxr-xr-x | pypers/pep318/working/decorators.py | 173 | ||||
-rwxr-xr-x | pypers/pep318/working/decorators.txt | 1346 | ||||
-rwxr-xr-x | pypers/pep318/working/doct.py | 60 | ||||
-rwxr-xr-x | pypers/pep318/working/example.py | 16 | ||||
-rwxr-xr-x | pypers/pep318/working/example1.py | 29 | ||||
-rwxr-xr-x | pypers/pep318/working/example2.py | 36 | ||||
-rwxr-xr-x | pypers/pep318/working/example4.py | 22 | ||||
-rwxr-xr-x | pypers/pep318/working/example5.py | 20 | ||||
-rwxr-xr-x | pypers/pep318/working/example6.py | 14 | ||||
-rwxr-xr-x | pypers/pep318/working/example9.py | 19 | ||||
-rwxr-xr-x | pypers/pep318/working/logged.py | 8 | ||||
-rwxr-xr-x | pypers/pep318/working/noconflict.py | 64 | ||||
-rwxr-xr-x | pypers/pep318/working/pep318.html | 1046 | ||||
-rwxr-xr-x | pypers/pep318/working/pep318.txt | 1048 | ||||
-rwxr-xr-x | pypers/pep318/working/pydoc.html | 504 | ||||
-rwxr-xr-x | pypers/pep318/working/tracing.py | 20 |
21 files changed, 5918 insertions, 0 deletions
diff --git a/pypers/pep318/working/README.txt b/pypers/pep318/working/README.txt new file mode 100755 index 0000000..31f878e --- /dev/null +++ b/pypers/pep318/working/README.txt @@ -0,0 +1,45 @@ +DECORATORS README +======================================================================== + +The ``decorators`` distribution contains the following files: + +1. README.txt (your are reading it) + +2. decorators.txt (the documentation in ReStructuredText format) + +3. decorators.html (the documentation in HTML format) + +4. decorators.pdf (the documentation in pdf format) + +5. decorators.py (the heart of the distribution) + +6. noconflict.py (imported by decorators, resolves metaclass conflicts) + +7. doct.py (utility to extract tests from the documentation) + +8. decorators.ps (a figure included in decorators.pdf) + +9. decorators.png (a figure included in decorators.html) + +10. makegraph.dot (DOT script generating the figure) + + +``noconflict`` and ``doct`` can be used as standalone +modules too. They are documented in the on-line Python cookbook. + +After running ``python doct.py decorators.txt`` a number of files will be +generated, including a module ``customdec.py`` containing the examples +of custom decorators discussed in the documentation. + +If the tests fail, then there is something wrong with your Python +installation, and I cannot help you since I don't have your machine +at my disposal :-( It works for me both under Red Hat 7.3 and +Windows 98SE. Notice that Python 2.3 is required. + +If you use the decorators module in your code (of course, you should +not use it in production software!) and you find some bug of unexpected +behaviour, please send a bug report to me: + + MicheleSimionato@libero.it + +That's all, folks. Enjoy! diff --git a/pypers/pep318/working/chatty2.py b/pypers/pep318/working/chatty2.py new file mode 100755 index 0000000..8907abb --- /dev/null +++ b/pypers/pep318/working/chatty2.py @@ -0,0 +1,27 @@ +# chatty2.py + +import customdec; customdec.enhance_classes("[Decorated]") + +# sets the log files +log1=file('file1.log','w') +log2=file('file2.log','w') + +class C: + def f(self): + "[chattymethod2]" + f.logfile=log1 # function attribute + def g(self): + "[chattymethod2]" + g.logfile=log2 # function attribute + +assert C.__dict__['f'].logfile is log1 # check the conversion +assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr. + +c=C() # C instantiation + +c.f() # print a message in file1.log +c.g() # print a message in file2.log + +log1.close(); log2.close() # finally + + diff --git a/pypers/pep318/working/customdec.py b/pypers/pep318/working/customdec.py new file mode 100755 index 0000000..bebec11 --- /dev/null +++ b/pypers/pep318/working/customdec.py @@ -0,0 +1,67 @@ +# customdec.py + +from decorators import * + +class chattymethod(MethodDecorator): + logfile=sys.stdout # default + def get(self,obj,cls=None): # same signature as __get__ + self.logfile.write('calling %s from %s\n' % (self,obj or cls)) + return super(chattymethod,self).get(obj,cls) + + + +class chattymethod2(chattymethod): + logfile=sys.stdout # default + def __init__(self,objfunc): + super(chattymethod2,self).__init__(objfunc) + logfile=getattr(self.__func__,'logfile',None) + if logfile: self.logfile=logfile + + + +class tracedmethod(MethodDecorator): + "Descriptor class, converts a method in a traced method" + indent=0; output=sys.stdout # defaults + + def __init__(self,objfunc): + super(tracedmethod,self).__init__(objfunc) + self.funcname=self.__func__.__name__ + output=getattr(self.__func__,'output',None) + if output: self.output=output # func.attr. -> dec.attr. + + def get(self,obj,cls): + clsname=self.__klass__.__name__ # definition clas + def tracedmeth(obj,*args,**kw): + i=' '*self.indent # default indentation + self.__class__.indent+=4 # increases indentation + self.output.write("%sCalling '%s.%s' with arguments " % + (i,clsname,self.funcname)) + self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw))) + res=super(tracedmethod,self).get(obj,cls)(*args,**kw) + self.output.write("%s'%s.%s' called with result: %s\n" + % (i,clsname,self.funcname,res)) + self.__class__.indent-=4 # restores default indentation + return res + return tracedmeth.__get__(obj,cls) # method wrapper + + + +class Logged(ClassDecorator): + output=sys.stdout + def __init__(cls,name,bases,dic): + super(Logged,cls).__init__(name,bases,dic) + print >> cls.output,"%s created" % cls + + + +from types import FunctionType + +class Traced(ClassDecorator): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType): # modifies the docstring + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) + + + diff --git a/pypers/pep318/working/debugger.py b/pypers/pep318/working/debugger.py new file mode 100755 index 0000000..30510df --- /dev/null +++ b/pypers/pep318/working/debugger.py @@ -0,0 +1,17 @@ +import sys + +def info(type, value, tb): + if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \ + type == SyntaxError: + # we are in interactive mode or we don't have a tty-like + # device, so we call the default hook + sys.__excepthook__(type, value, tb) + else: + import traceback, pdb + # we are NOT in interactive mode, print the exception... + traceback.print_exception(type, value, tb) + print + # ...then start the debugger in post-mortem mode. + pdb.pm() + +sys.excepthook = info diff --git a/pypers/pep318/working/decorators.html b/pypers/pep318/working/decorators.html new file mode 100755 index 0000000..ceac9b6 --- /dev/null +++ b/pypers/pep318/working/decorators.html @@ -0,0 +1,1337 @@ +<?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.2.9: http://docutils.sourceforge.net/" /> +<title>Implementing PEP 318 (decorators)</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="implementing-pep-318-decorators"> +<h1 class="title">Implementing PEP 318 (decorators)</h1> +<blockquote> +<table class="field-list" frame="void" rules="none"> +<col class="field-name" /> +<col class="field-body" /> +<tbody valign="top"> +<tr class="field"><th class="field-name">Module:</th><td class="field-body">decorators</td> +</tr> +<tr class="field"><th class="field-name">Version:</th><td class="field-body">0.5</td> +</tr> +<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td> +</tr> +<tr class="field"><th class="field-name">e-mail:</th><td class="field-body"><a class="reference" href="mailto:MicheleSimionato@libero.it">MicheleSimionato@libero.it</a></td> +</tr> +<tr class="field"><th class="field-name">Licence:</th><td class="field-body">Python-like</td> +</tr> +<tr class="field"><th class="field-name">Disclaimer:</th><td class="field-body">This is experimental code. Use it at your own risk!</td> +</tr> +</tbody> +</table> +</blockquote> +<div class="contents topic" id="contents"> +<p class="topic-title"><a name="contents">Contents</a></p> +<ul class="simple"> +<li><a class="reference" href="#basics" id="id1" name="id1">Basics</a></li> +<li><a class="reference" href="#simple-usage-of-decorators" id="id2" name="id2">Simple usage of decorators</a></li> +<li><a class="reference" href="#decorating-classes" id="id3" name="id3">Decorating classes</a></li> +<li><a class="reference" href="#adding-magic" id="id4" name="id4">Adding magic</a></li> +<li><a class="reference" href="#the-dangers-of-magic" id="id5" name="id5">The dangers of magic</a></li> +<li><a class="reference" href="#defining-method-decorators" id="id6" name="id6">Defining method decorators</a></li> +<li><a class="reference" href="#composing-decorators" id="id7" name="id7">Composing decorators</a></li> +<li><a class="reference" href="#defining-class-decorators" id="id8" name="id8">Defining class decorators</a></li> +<li><a class="reference" href="#advanced-usage" id="id9" name="id9">Advanced usage</a></li> +<li><a class="reference" href="#the-implementation" id="id10" name="id10">The implementation</a></li> +</ul> +</div> +<p>Having plenty of free time in these days, I have finished an old +project of mine, the implementation of PEP 318 in pure Python.</p> +<p>Here is the rationale:</p> +<ul class="simple"> +<li>some kind of decorator syntax is scheduled to go in Python 2.4, +therefore it is interesting to play with the concept;</li> +<li>it is nice to play with decorators now, without having to +wait for one year or so;</li> +<li>it is much easier levelto experiment with a pure Python implementation +than with a C implementation;</li> +<li>the implementation can be seen as an exercise on modern Python +programming and may be valuable to people wanting to study the most +advanced new constructs in Python 2.2 (<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">descriptors</a>, <a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta2.html">metaclasses</a>, +<a class="reference" href="http://www.python.org/2.3/descrintro.html">cooperative methods</a>, etc.)</li> +</ul> +<div class="section" id="basics"> +<h1><a class="toc-backref" href="#id1" name="basics">Basics</a></h1> +<p>PEP 318 has the goal of providing a nice syntactic sugar for expressions like</p> +<blockquote> +<pre class="literal-block"> +def identity(x): + return x +identity=staticmethod(identity) +</pre> +</blockquote> +<p>or</p> +<blockquote> +<pre class="literal-block"> +def name(cls): + return cls.__name__ +name=classmethod(name) +</pre> +</blockquote> +<p>which are pretty verbose. It is clear that having new syntax (as +for instance the proposed square bracket notation)</p> +<blockquote> +<pre class="literal-block"> +def identity(x)[staticmethod]: + return x + +def name(cls)[classmethod]: + return cls.__name__ +</pre> +</blockquote> +<p>involves changing the grammar and modifying the interpreter at the +C level. This means a lot of work. Fortunately, it is possible to +have the same effect without changing the Python grammar. +The idea is to use magic docstrings like this:</p> +<blockquote> +<pre class="literal-block"> +def identity(x): + "[staticmethod]" + return x + +def name(cls): + "[classmethod]" + return cls.__name__ +</pre> +</blockquote> +<p>The implementation is able to recognize such docstrings +and to automagically convert those methods in decorators.</p> +<p>Decorators are nothing else than a sophisticated kind of wrappers. +The <tt class="literal"><span class="pre">decorators</span></tt> module provides support both for method decorators, +which wrap functions and class decorator, which wrap classes. +<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt> are two examples of already existing +method decorators (actually my implementation rewrites them, but let me +pass on this detail). Technically speaking, method decorators are classes +taking a single function as input and producing a descriptor object +as output (properties are not decorators according to this definition, +since they take four functions as input, <tt class="literal"><span class="pre">get,</span> <span class="pre">set,</span> <span class="pre">del_</span></tt> and <tt class="literal"><span class="pre">doc</span></tt>). +Descriptors are objects with a <tt class="literal"><span class="pre">__get__</span></tt> method; they are quite +sophisticated, but fortunately they have been wonderfully explained by +Raymond Hettinger already, so I am allowed to skip on this point ;). +A knowledge of descriptors is not needed in order to use the <tt class="literal"><span class="pre">decorator</span></tt> +module; however it is welcomed for advanced users wanting to implement +custom method decorators. +Class decorators are metaclasses taking a class as imput and returning +a decorated class as output. A good understanding of metaclasses is needed +in order to be able to write custom class decorators, but no knowledge +at all is required in order to use the pre-defined class decorators +provided by the module. +Finally, the module is meant to be extensible; so one could +define new kind of decorators. For instance, the original version of +the module also had the concept of module decorators; however I have cut +down that part in order to keep the module short.</p> +<p>Admittedly, the implementation +is not for the faint of heart, nevertheless I have tried to make the +basic usage easy and simple to understand.</p> +</div> +<div class="section" id="simple-usage-of-decorators"> +<h1><a class="toc-backref" href="#id2" name="simple-usage-of-decorators">Simple usage of decorators</a></h1> +<p>Before talking about the implementation details, I will show +how the <tt class="literal"><span class="pre">decorators</span></tt> module works in practice. The simplest and safest +usage is by means of the <tt class="literal"><span class="pre">decorators.decorated()</span></tt> function, which +takes an object (a function or a class) and checks its docstring: if +a magic docstring is found, it returns a decorated version of the object, +otherwise it returns the original object. Using <tt class="literal"><span class="pre">decorators.decorated()</span></tt> +is simple but verbose, so magic shortcuts will be discussed in the next +section.</p> +<p>Here, let me give an example, showing that method decorators work both for +<a class="reference" href="http://www.python.org/2.3/descrintro.html">new style classes and old style classes</a>:</p> +<blockquote> +<pre class="literal-block"> +#<example1.py> + +import decorators + +def do_nothing(self): + "No magic docstring here" +dec_do_nothing=decorators.decorated(do_nothing) + +def identity(x): + "[staticmethod]" + return x +dec_identity=decorators.decorated(identity) + +def name(cls): + "[classmethod]" + return cls.__name__ +dec_name=decorators.decorated(name) + +class OldStyle: + do_nothing=dec_do_nothing + identity=dec_identity + +class NewStyle(object): + name=dec_name + +o=OldStyle() # creates an old style instance +n=NewStyle() # creates a new style instance + +#</example1.py> +</pre> +</blockquote> +<p>In this example, both <tt class="literal"><span class="pre">dec_identity</span></tt> and <tt class="literal"><span class="pre">dec_name</span></tt> are decorator objects, +i.e. descriptors modifiying the attribute access. It is easy to recognize +decorators objects, since they have a re-defined printing representation:</p> +<pre class="doctest-block"> +>>> from example1 import * +</pre> +<pre class="doctest-block"> +>>> print dec_identity +<staticmethod:identity> +</pre> +<pre class="doctest-block"> +>>> print dec_name +<classmethod:name> +</pre> +<p>Now, let me check that <tt class="literal"><span class="pre">dec_</span> <span class="pre">identity</span></tt> works as a staticmethod,</p> +<pre class="doctest-block"> +>>> assert OldStyle.identity(1) == 1 # called from the class +>>> assert o.identity(1) == 1 # called from the instance +</pre> +<p>whereas <tt class="literal"><span class="pre">dec_name</span></tt> works as a classmethod:</p> +<pre class="doctest-block"> +>>> assert NewStyle.name() == 'NewStyle' # called from the class +>>> assert n.name() == 'NewStyle' # called from the instance +</pre> +<p>On the other hand, <tt class="literal"><span class="pre">do_nothing</span></tt> does not have a magic +docstring, therefore it is not converted to a decorator object; +actually it is <em>exactly</em> the original function:</p> +<pre class="doctest-block"> +>>> assert dec_do_nothing is do_nothing # not converted +</pre> +<p>Therefore it works without surprises:</p> +<pre class="doctest-block"> +>>> o.do_nothing() # does nothing, correct +</pre> +<p>For sake of convenience, I have re-implemented the built-in +<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, so</p> +<pre class="doctest-block"> +>>> isinstance(dec_identity,staticmethod) +False +</pre> +<p>and</p> +<pre class="doctest-block"> +>>> isinstance(dec_name,classmethod) +False +</pre> +<p>but</p> +<pre class="doctest-block"> +>>> isinstance(dec_identity,decorators.staticmethod) +True +</pre> +<p>and</p> +<pre class="doctest-block"> +>>> isinstance(dec_name,decorators.classmethod) +True +</pre> +<p>It is possible to recognize method decorators since they provides +a couple of special attributes:</p> +<p><tt class="literal"><span class="pre">__func__</span></tt>, returning the function from which they originated</p> +<blockquote> +<pre class="doctest-block"> +>>> assert dec_identity.__func__ is identity +>>> assert dec_name.__func__ is name +</pre> +</blockquote> +<p>and <tt class="literal"><span class="pre">__klass__</span></tt>, returning the class where they where defined</p> +<blockquote> +<pre class="doctest-block"> +>>> dec_identity.__klass__ +<class 'decorators.?'> +</pre> +</blockquote> +<p>The question mark here means that the definition class is unknown.</p> +</div> +<div class="section" id="decorating-classes"> +<h1><a class="toc-backref" href="#id3" name="decorating-classes">Decorating classes</a></h1> +<p>The problem with the approach described in the previous section +is that it does not present any significant advantage over +the already existing mechanism. A real step forward would be to +have classes with the ability of automatically converting their +methods to method decorators according to the docstrings. +This sounds a bit of magic, but actually can be done very simply +by adding to the class a docstring starting with "[Decorated]" +and decorating the class. +Here is an example:</p> +<blockquote> +<pre class="literal-block"> +#<example2.py> + +from decorators import decorated +from example1 import do_nothing,identity,name + +class B(object): + "This is a regular class" + +B=decorated(B) # does nothing + +class C(B): + "[Decorated]" + do_nothing=do_nothing + identity=identity + name=name + +C=decorated(C) # regenerates the class converting methods in decorators +c=C() + +#</example2.py> +</pre> +</blockquote> +<p>Here is the testing:</p> +<pre class="doctest-block"> +>>> from example2 import * +>>> assert C.identity(1) == 1 +>>> assert C.name() == 'C' +>>> assert c.identity(1) == 1 +>>> assert c.name() == 'C' +</pre> +<p>Notice that adding <tt class="literal"><span class="pre">identity</span></tt> after the class creation with the syntax +<tt class="literal"><span class="pre">C.identity=identity</span></tt> would not work; +<tt class="literal"><span class="pre">C.identity=decorators.decorated(identity)</span></tt> is required:</p> +<pre class="doctest-block"> +>>> C.identity=decorators.decorated(identity) +>>> C.identity(1) # it works +1 +</pre> +<p>If a class misses the magic docstring, nothing happens:</p> +<pre class="doctest-block"> +>>> B # returns the original B +<class 'example2.B'> +</pre> +<p>The mechanism works for old style classes too:</p> +<blockquote> +<pre class="literal-block"> +#<example2.py> + +class D: # old style + "[Decorated]" + def identity(x): + "[staticmethod]" + return x + +D=decorated(D) + +d=D() + +# test +assert d.identity(1) == 1 +assert D.identity(1) == 1 + +#</example2.py> +</pre> +</blockquote> +<p>Under the hood <tt class="literal"><span class="pre">decorators.decorated()</span></tt> recognizes the class level +magic docstring "[Decorated]" and creates an instance of the +<tt class="literal"><span class="pre">decorators.Decorated</span></tt> metaclass; incidentally, +this converts old style classes in new style classes:</p> +<pre class="doctest-block"> +>>> from example2 import D,d +>>> type(D) # D is an instance of decorator.Decorated +<class 'decorators.Decorated'> +</pre> +<p>Internally the metaclass invokes <tt class="literal"><span class="pre">decorators.decorated()</span></tt> +on the methods of its instances: this is why they becomes decorated +if a suitable docstring is found. By the way, if you mispell a +decorator name you get an helpful error message:</p> +<pre class="doctest-block"> +>>> class E: +... "[Decorated]" +... def f(): +... "[staticmeth]" +>>> E=decorators.decorated(E) +Traceback (most recent call last): + .. a long and cryptic traceback here .. +UnknownDecoratorError: staticmeth +</pre> +<p>The enhancement provided by the metaclass includes a new default +printing representation for both the class</p> +<pre class="doctest-block"> +>>> print D # returns the name of D and of its metaclass +<class D[Decorated]> +</pre> +<p>and its instances:</p> +<pre class="doctest-block"> +>>> print d +<D instance> +</pre> +<p>One can even forget the docstring in subclasses of decorated +classes, since metaclasses are inherited:</p> +<pre class="doctest-block"> +>>> class E(D): +... def name(cls): +... "[classmethod]" +... return cls.__name__ +>>> print E.name() +E +</pre> +<p>This approach presents another advantage: the decorated methods know +the class where they were defined via the special attribute <tt class="literal"><span class="pre">__klass__</span></tt>:</p> +<pre class="doctest-block"> +>>> E.__dict__['name'].__klass__ # the class where 'name' is defined +<class 'E'> +</pre> +<p>This is useful for introspection and debugging purposes.</p> +</div> +<div class="section" id="adding-magic"> +<h1><a class="toc-backref" href="#id4" name="adding-magic">Adding magic</a></h1> +<p>The problem of the previous approach is that one must explicitely +decorate the classes by hand, by invoking <tt class="literal"><span class="pre">decorators.decorated()</span></tt> +each time. However, it is possible to add more magic +and to decorate all the classes automatically. +It is as easy as writing <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt> +on top of the module. Then all methods in all classes of the module +with a magic docstring will be checked for magic docstrings and +automagically decorated if needed. +For instance, the previous example would be written</p> +<blockquote> +<pre class="literal-block"> +#<example4.py> + +import decorators; decorators.enhance_classes() + +class C: + "[Decorated]" # magic docstring here + def do_nothing(self): + "No magic docstring here" + + def identity(x): + "[staticmethod]" + return x + +class D(object): + "Undecorated" # no magic docstring here + def name(cls): + "[classmethod]" + return cls.__name__ + +c=C(); d=D() + +#</example4.py> +</pre> +</blockquote> +<p><tt class="literal"><span class="pre">C</span></tt> has a <tt class="literal"><span class="pre">[Decorated]</span></tt> docstring, so its methods +are automatically decorated:</p> +<pre class="doctest-block"> +>>> from example4 import * +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert c.identity(1) == 1 +</pre> +<p>On the other hand, since <tt class="literal"><span class="pre">D</span></tt> misses a magic docstring, +its <tt class="literal"><span class="pre">name</span></tt> method is not decorated:</p> +<pre class="doctest-block"> +>>> hasattr(D.__dict__['name'],'__func__') # not a decorator +False +</pre> +<p>Since <tt class="literal"><span class="pre">D.name</span></tt> is a regular method and not a classmethod, <tt class="literal"><span class="pre">D.name()</span></tt> +gives an error:</p> +<pre class="doctest-block"> +>>> D.name() +Traceback (most recent call last): + ... +TypeError: unbound method name() must be called with D instance as first argument (got nothing instead) +</pre> +<p>The trick works for classes containing inner classes, too:</p> +<blockquote> +<pre class="literal-block"> +#<example5.py> + +import decorators; decorators.enhance_classes() + +class C: + "[Decorated]" # required docstring + def identity(x): + "[staticmethod]" + return x + class Inner: + "[Decorated]" # required docstring + def name(cls): + "[classmethod]" + return cls.__name__ + + +assert C.identity(1) == 1 +assert C.Inner.name() == 'Inner' + +#</example5.py> +</pre> +</blockquote> +<p>Under the hood, the magic works by enhancing the <tt class="literal"><span class="pre">object</span></tt> class +of the module with a <tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> metaclass:</p> +<pre class="doctest-block"> +>>> import example5 +>>> type(example5.object) +<class 'decorators.ClassDecorator'> +</pre> +<p>Notice that for safety reasons the enhancement is only on the module +<tt class="literal"><span class="pre">object</span></tt> class, not on the <tt class="literal"><span class="pre">__builtin__.object</span></tt> class. The problem +is that adding too much magic can be risky.</p> +</div> +<div class="section" id="the-dangers-of-magic"> +<h1><a class="toc-backref" href="#id5" name="the-dangers-of-magic">The dangers of magic</a></h1> +<p>For the sake of metaclass users, in this section I will point out the +dangers of the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntax. On the other hand, if you +never use metaclasses, you may safely skip to the following section.</p> +<p>The problem is that the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntax is not 100% safe, +because of possible metaclass conflicts. +Here is an example:</p> +<pre class="doctest-block"> +>>> import decorators; decorators.enhance_classes() +</pre> +<p>This line enhances the <tt class="literal"><span class="pre">object</span></tt> class in the interpreter namespace:</p> +<pre class="doctest-block"> +>>> print object +<class object[ClassDecorator]> +</pre> +<p>This shows that <tt class="literal"><span class="pre">object</span></tt> is an instance of <tt class="literal"><span class="pre">ClassDecorator</span></tt>.</p> +<pre class="doctest-block"> +>>> class M(type): +... "Some non-trivial code here..." +</pre> +<p>This line creates a custom metaclass we want to use to enhance our classes.</p> +<pre class="doctest-block"> +>>> class D(object): __metaclass__=M # does not work! +... +Traceback (most recent call last): + ... +TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +</pre> +<p>The problem is that the previous line tries to create a class <tt class="literal"><span class="pre">D</span></tt> +which should have both metaclasses <tt class="literal"><span class="pre">ClassDecorator</span></tt> and <tt class="literal"><span class="pre">M</span></tt>: +a conflict follows.</p> +<p>Fortunately, the decorators module imports the <tt class="literal"><span class="pre">makecls</span></tt> function from my +<tt class="literal"><span class="pre">noconflict</span></tt> module (described in the <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a>) +just to avoid this kind of problems:</p> +<pre class="doctest-block"> +>>> class D(object): +... __metaclass__=decorators.makecls(M) +</pre> +<p>Now the class has been safely created as an instance of the composed +class <tt class="literal"><span class="pre">ClassDecoratorM</span></tt>.</p> +<pre class="doctest-block"> +>>> type(D) +<class 'noconflict.ClassDecoratorM'> +</pre> +<p>If we want <tt class="literal"><span class="pre">M</span></tt> to have the priority over <tt class="literal"><span class="pre">ClassDecorator</span></tt>, the +option <tt class="literal"><span class="pre">priority=True</span></tt> makes the job:</p> +<pre class="doctest-block"> +>>> class D(object): +... __metaclass__=decorators.makecls(M,priority=True) +>>> type(D) +<class 'noconflict.MClassDecorator'> +</pre> +<p>The situation for old style classes is worse, since</p> +<pre class="doctest-block"> +>>> class D: +... __metaclass__=M +... def sm(): +... "[staticmethod]" +</pre> +<p>apparently gives no error, but actually the metaclass <tt class="literal"><span class="pre">M</span></tt> overrides +<tt class="literal"><span class="pre">ClassDecorator</span></tt>, so <tt class="literal"><span class="pre">D</span></tt> will not recognize magic docstrings:</p> +<pre class="doctest-block"> +>>> type(D) +<class 'M'> +>>> D.sm() +Traceback (most recent call last): + ... +TypeError: unbound method sm() must be called with D instance as first argument (got nothing instead) +</pre> +<p>Using <tt class="literal"><span class="pre">decorators.makecls(M)</span></tt> will not help here (because of the way Python +assigns metaclasses) and the only solution is to be completely explicit:</p> +<pre class="doctest-block"> +>>> class D: +... __metaclass__=decorators.makecls(decorators.ClassDecorator,M) +... def sm(): +... "[staticmethod]" +>>> type(D) +<class 'noconflict.ClassDecoratorM'> +</pre> +<p>Now <tt class="literal"><span class="pre">D</span></tt> is not decorated since it does miss a magic docstring, but +it provides the ability to recognizing magic docstrings, so <tt class="literal"><span class="pre">D</span></tt> +subclasses with a "[Decorated]" docstring will be decorated:</p> +<pre class="doctest-block"> +>>> class E(D): +... "[Decorated]" +... def cm(cls): +... "[classmethod]" +... print '.cm() called from',cls +</pre> +<pre class="doctest-block"> +>>> E.cm() # it works +.cm() called from <class E[ClassDecoratorMDecorated]> +</pre> +<p>Notice that <tt class="literal"><span class="pre">sm</span></tt> was defined in <tt class="literal"><span class="pre">D</span></tt>, the undecorated class: therefore +it is not decorated:</p> +<pre class="doctest-block"> +>>> E.sm() # correctly, does not work +Traceback (most recent call last): + ... +TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead) +</pre> +<p>The error message says clearly that <tt class="literal"><span class="pre">sm</span></tt> is an unbound method and not +a static method.</p> +</div> +<div class="section" id="defining-method-decorators"> +<h1><a class="toc-backref" href="#id6" name="defining-method-decorators">Defining method decorators</a></h1> +<p>The <tt class="literal"><span class="pre">decorators</span> <span class="pre">module</span></tt> contains two predefinite method decorators, +<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, which emulate the built-ins +with the same names. However, it is possible to write your own +custom decorators. The <tt class="literal"><span class="pre">decorators.MethodDecorator</span></tt> class which is here +exactly for that purpose.</p> +<p>Custom decorators are expected to be implemented by subclassing +<tt class="literal"><span class="pre">MethodDecorator</span></tt> and by overriding its <tt class="literal"><span class="pre">get</span></tt> method. The +<tt class="literal"><span class="pre">get</span></tt> method automagically induces a <tt class="literal"><span class="pre">__get__</span></tt> method, turning the +class in a descriptor. The machinery is needed since <tt class="literal"><span class="pre">__get__</span></tt> cannot +be made cooperative using the standard <tt class="literal"><span class="pre">super</span></tt> mechanism because +there would be a confusion between <tt class="literal"><span class="pre">super.__get__</span></tt> and the decorator +<tt class="literal"><span class="pre">__get__</span></tt>. This is a bit tricky, but the causal programmer is not +expected to write custom decorators, and actually I don't want to make +the access to decorators <em>too</em> easy, since they are potentially dangerous.</p> +<p>In order to give a simple example, let me show the implementation +of a <tt class="literal"><span class="pre">chattymethod</span></tt> that prints a message when it is called:</p> +<blockquote> +<pre class="literal-block"> +#<customdec.py> + +from decorators import * + +class chattymethod(MethodDecorator): + logfile=sys.stdout # default + def get(self,obj,cls=None): # same signature as __get__ + self.logfile.write('calling %s from %s\n' % (self,obj or cls)) + return super(chattymethod,self).get(obj,cls) + +#</customdec.py> +</pre> +</blockquote> +<p>Notice the usage of the <tt class="literal"><span class="pre">super().get</span></tt> trick. This guarantees that +<tt class="literal"><span class="pre">chattymethod</span></tt> will play well with other decorators (i.e. it +can be nicely composed via multiple inheritance). The point will +be fully discussed in the section on composing decorators.</p> +<p>Here is an example of usage:</p> +<pre class="doctest-block"> +>>> import customdec # adds chattymethod to the list of known decorators +>>> customdec.enhance_classes() # automagically enhances classes when needed +>>> class C: +... " [Decorated] " +... def f(self): +... """ +... [ chattymethod ] +... """ +>>> c=C() +>>> c.f() +calling <chattymethod:f> from <C instance> +</pre> +<p>By the way, this shows that one can safely add whitespaces (including +newlines) to the magic docstring: they are simply ignored.</p> +<p>One can check that the syntax <tt class="literal"><span class="pre">C.f(c)</span></tt> works too:</p> +<pre class="doctest-block"> +>>> C.f(c) +calling <chattymethod:f> from <class C[Decorated]> +</pre> +<p>A tricky point of the decorators mechanism is the issue of parameter passing. +In comp.lang.python there was the proposal of allowing explicit parameter +passing to decorators, with a syntax of kind</p> +<blockquote> +<pre class="literal-block"> +def f(self)[chattymethod(logfile=file('file1.log','w'))] +</pre> +</blockquote> +<p>In my view, there are too many parenthesis in this syntax, and it may +become rapidly unreadable. Moreover, it complicates the implementation +without any real benefit, so the decorators module does not allow +this kind of parameter passings. There are however viable +workarounds, so you should not miss the syntax.</p> +<p>A simple minded solution is to change the defaults by hand:</p> +<pre class="doctest-block"> +>>> from customdec import chattymethod,decorated +>>> chattymethod.logfile=file('file.log','w') +>>> def g(self): +... "[chattymethod]" +>>> C.g=decorated(g) +>>> c.g() # will print a message on file.log +</pre> +<p>This approach has the drawback that chattymethods created before changing +the logfile will also print to the new logfile, if invoked after the +change. Therefore</p> +<pre class="doctest-block"> +>>> c.f() +</pre> +<p>will print a message to <tt class="literal"><span class="pre">file.log</span></tt> too, and not to standard output. +Here is the confirmation:</p> +<pre class="doctest-block"> +>>> chattymethod.logfile.close() +>>> print file('file.log').read().rstrip() +calling <chattymethod:g> from <C instance> +calling <chattymethod:f> from <C instance> +</pre> +<p>A much better solution is to pass +parameters to method decorators as function attributes: then the function +attributes can be converted to attributes of the decorator +in the <tt class="literal"><span class="pre">__init__</span></tt> method. Here is an example:</p> +<blockquote> +<pre class="literal-block"> +#<customdec.py> + +class chattymethod2(chattymethod): + logfile=sys.stdout # default + def __init__(self,objfunc): + super(chattymethod2,self).__init__(objfunc) + logfile=getattr(self.__func__,'logfile',None) + if logfile: self.logfile=logfile + +#</customdec.py> +</pre> +</blockquote> +<p>Notice that the <tt class="literal"><span class="pre">__init__</span></tt> method has the signature +<tt class="literal"><span class="pre">__init__(self,objfunc)</span></tt>, where the <tt class="literal"><span class="pre">objfunc</span></tt> object is a +decorator object or the function to be converted in the decorator +object, and that it is cooperative. +This is the suggested way of overriding <tt class="literal"><span class="pre">__init__</span></tt> (see also +<tt class="literal"><span class="pre">help(decorators.MethodDecorator.__init__)</span></tt>).</p> +<p>Here is the testing:</p> +<blockquote> +<pre class="literal-block"> +#<chatty2.py> + +import customdec; customdec.enhance_classes("[Decorated]") + +# sets the log files +log1=file('file1.log','w') +log2=file('file2.log','w') + +class C: + def f(self): + "[chattymethod2]" + f.logfile=log1 # function attribute + def g(self): + "[chattymethod2]" + g.logfile=log2 # function attribute + +assert C.__dict__['f'].logfile is log1 # check the conversion +assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr. + +c=C() # C instantiation + +c.f() # print a message in file1.log +c.g() # print a message in file2.log + +log1.close(); log2.close() # finally + +#</chatty2.py> +</pre> +</blockquote> +<p>Let me check the contents of the log files:</p> +<pre class="doctest-block"> +>>> import chatty2 +>>> print file('file1.log').read().rstrip() +calling <chattymethod2:f> from <C instance> +>>> print file('file2.log').read().rstrip() +calling <chattymethod2:g> from <C instance> +</pre> +<p><tt class="literal"><span class="pre">chattymethod</span></tt> is the poor man version of <tt class="literal"><span class="pre">tracedmethod</span></tt>, a +sophisticated decorator for tracing methods. +Here is the code, given for pedagogical purposes; the lazy reader can +skip it and go directly to the usage section.</p> +<blockquote> +<pre class="literal-block"> +#<customdec.py> + +class tracedmethod(MethodDecorator): + "Descriptor class, converts a method in a traced method" + indent=0; output=sys.stdout # defaults + + def __init__(self,objfunc): + super(tracedmethod,self).__init__(objfunc) + self.funcname=self.__func__.__name__ + output=getattr(self.__func__,'output',None) + if output: self.output=output # func.attr. -> dec.attr. + + def get(self,obj,cls): + clsname=self.__klass__.__name__ # definition clas + def tracedmeth(obj,*args,**kw): + i=' '*self.indent # default indentation + self.__class__.indent+=4 # increases indentation + self.output.write("%sCalling '%s.%s' with arguments " % + (i,clsname,self.funcname)) + self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw))) + res=super(tracedmethod,self).get(obj,cls)(*args,**kw) + self.output.write("%s'%s.%s' called with result: %s\n" + % (i,clsname,self.funcname,res)) + self.__class__.indent-=4 # restores default indentation + return res + return tracedmeth.__get__(obj,cls) # method wrapper + +#</customdec.py> +</pre> +</blockquote> +<p><tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method wrapper object, so it is +possible to use <tt class="literal"><span class="pre">im_func</span></tt> to retrieve the internal function +<tt class="literal"><span class="pre">tracedmeth</span></tt>:</p> +<pre class="doctest-block"> +>>> class C: +... "[Decorated]" +... def f(self): +... "[tracedmethod]" +>>> c=C(); c.f() +Calling 'C.f' with arguments <C instance>(){} ... +'C.f' called with result: None +>>> c.f.im_func.__name__ +'tracedmeth' +</pre> +<p>As soon as the <tt class="literal"><span class="pre">tracedmethod</span></tt> module is loaded, the <tt class="literal"><span class="pre">tracedmethod</span></tt> class +is added to the list of know decorators, so one should use the +"[tracedmethod]" docstring and not something like +"[customdec.tracedmethod]".</p> +<p>Here is a less trivial example of usage, writing in a log file the +internal working of a recursive function:</p> +<blockquote> +<pre class="literal-block"> +#<example9.py> + +import customdec; customdec.enhance_classes() + +logfile=file('file3.log','w') + +class C(object): + "[Decorated]" + def fact(self,n): + "[tracedmethod] The good old factorial." + if n==0: return 1 + else: return n*self.fact(n-1) + fact.output=logfile + +C().fact(2) # write a message to logfile + +logfile.close() + +#</example9.py> +</pre> +</blockquote> +<p>Here is the content of the <tt class="literal"><span class="pre">file3.log</span></tt>:</p> +<pre class="doctest-block"> +>>> import example9 +>>> print file('file3.log').read().rstrip() +Calling 'C.fact' with arguments <C instance>(2,){} ... + Calling 'C.fact' with arguments <C instance>(1,){} ... + Calling 'C.fact' with arguments <C instance>(0,){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +</pre> +</div> +<div class="section" id="composing-decorators"> +<h1><a class="toc-backref" href="#id7" name="composing-decorators">Composing decorators</a></h1> +<p>Decorators can be composed by using magic docstrings with comma-separated +decorator names. For instance, you can trace a classmethod as in this example:</p> +<blockquote> +<pre class="literal-block"> +#<example6.py> + +"How to trace a class method" + +import customdec; customdec.enhance_classes() + +class C(object): + "[Decorated]" + def fact(cls,n): # a traced classmethod + "[classmethod,tracedmethod]" + if n==0: return 1 + else: return n*cls.fact(n-1) + +#</example6.py> +</pre> +</blockquote> +<p>Here is the testing:</p> +<pre class="doctest-block"> +>>> from example6 import C +>>> C.fact(2) +Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ... + Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ... + Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 +</pre> +<p>You may easily check that calling <tt class="literal"><span class="pre">.fact</span></tt> from the instance will work too.</p> +<p>Under the hood the syntax</p> +<blockquote> +<pre class="literal-block"> +[classmethod,tracedmethod] +</pre> +</blockquote> +<p>generates a <tt class="literal"><span class="pre">classmethodtracedmethod</span></tt> class obtained via +multiple inheritance:</p> +<pre class="doctest-block"> +>>> C.__dict__['fact'].__class__ +<class 'noconflict.classmethodtracedmethod'> +</pre> +<p>Notice that the order does matter and using the docstring +"[tracedmethod,classmethod]" will not work:</p> +<pre class="doctest-block"> +>>> class D: +... "[Decorated]" +... def f(cls): +... "[tracedmethod,classmethod]" +>>> D.f() +Traceback (most recent call last): + ... +TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead) +</pre> +<p>The problem here is that <tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method-wrapper object +which expects a D instance as first argument whereas it gets <tt class="literal"><span class="pre">None</span></tt> since +it is called from the class. On the other hand,</p> +<pre class="doctest-block"> +>>> D().f() +Calling 'D.f' with arguments <D instance>(){} ... +'D.f' called with result: None +</pre> +<p>will work. When <tt class="literal"><span class="pre">classmethod</span></tt> precedes <tt class="literal"><span class="pre">tracedmethod</span></tt>, then +<tt class="literal"><span class="pre">classmethod</span></tt> passes to <tt class="literal"><span class="pre">tracedmeth</span></tt> a non-empty first argument, +i.e. the calling class, even when called from the instance:</p> +<pre class="doctest-block"> +>>> C().fact(2) +Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ... + Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ... + Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 +</pre> +<p>If we try to trace a staticmethod, we will get a different error with +the order "tracedmethod, staticmethod":</p> +<pre class="doctest-block"> +>>> class F(object): +... "[Decorated]" +... def fact(n): +... "[tracedmethod,staticmethod]" +... if n==0: return 1 +... else: return n*F.fact(n-1) +>>> F.fact(2) +Traceback (most recent call last): + ... +TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead) +</pre> +<p>The message is self-explanatory.</p> +<p>On the other hand, composing the decorators in the other order +"[tracedmethod,staticmethod]" will work just fine.</p> +</div> +<div class="section" id="defining-class-decorators"> +<h1><a class="toc-backref" href="#id8" name="defining-class-decorators">Defining class decorators</a></h1> +<p>PEP 318 proposes to decorate methods by using descriptors; it is +quite natural to extend this idea and to decorate classes by using +class decorators implemented as metaclasses. We already saw a +class decorator at work, the metaclass <tt class="literal"><span class="pre">Decorated</span></tt>, giving +to its instances the ability to interpret magic docstrings, +and converting functions in method decorators.</p> +<p>To define a custom class decorator is easy: one defines a custom metaclass +as usual, with the only difference from deriving by <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead +of deriving from <tt class="literal"><span class="pre">type</span></tt>. To understand how this works in practice, let me +show how to add logging capabilities to a given class. The first +step is to define a suitable class decorator, such as the following:</p> +<blockquote> +<pre class="literal-block"> +#<customdec.py> + +class Logged(ClassDecorator): + output=sys.stdout + def __init__(cls,name,bases,dic): + super(Logged,cls).__init__(name,bases,dic) + print >> cls.output,"%s created" % cls + +#</customdec.py> +</pre> +</blockquote> +<p><tt class="literal"><span class="pre">Logged</span></tt> is derived by the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>, +which provides a certain amount of magic under the hood (in particular +its printing representation and its calling syntax are redefined by its +metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>). Logging capabilities can be added to a class +by simply using the magic docstring syntax:</p> +<blockquote> +<pre class="literal-block"> +#<logged.py> + +import customdec; customdec.enhance_classes() + +class D(object): # will print a message at D creation + "[Logged]" + +#</logged.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> import logged +<class D[Logged]> created +</pre> +<p>Notice that the printing representation of <tt class="literal"><span class="pre">D</span></tt> involves the name +of <tt class="literal"><span class="pre">D</span></tt> preceded by the name of its metaclass, which in this case +is <tt class="literal"><span class="pre">Logged</span></tt></p> +<p>Each time an instance of <tt class="literal"><span class="pre">Logged</span></tt> is created, a similar message is printed:</p> +<pre class="doctest-block"> +>>> class E(logged.D): +... pass +<class E[Logged]> created +</pre> +<p>Notice that <tt class="literal"><span class="pre">E</span></tt> does not have any magic docstring</p> +<pre class="doctest-block"> +>>> E.__doc__ # no docstring +</pre> +<p>but still it inherits its magic from <tt class="literal"><span class="pre">D</span></tt>.</p> +<p>Another simple example of class decorator is the following metaclass +which modifies the docstrings of the methods of its instances, +by magically inducing tracing capabilities on them:</p> +<blockquote> +<pre class="literal-block"> +#<customdec.py> + +from types import FunctionType + +class Traced(ClassDecorator): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType): # modifies the docstring + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) + + +#</customdec.py> +</pre> +</blockquote> +<p>Here is an example of usage:</p> +<pre class="doctest-block"> +>>> class C(object): +... """[Decorated,Traced] The class decorator adds the magic docstring +... '[tracedmethod]' to f1 and f2, which are then converted +... to method decorator objects.""" +... def f1(self): pass +... def f2(self): pass +... +>>> type(C) +<class 'noconflict.DecoratedTraced'> +>>> c=C() +>>> c.f1() +Calling 'C.f1' with arguments <C instance>(){} ... +'C.f1' called with result: None +>>> c.f2() +Calling 'C.f2' with arguments <C instance>(){} ... +'C.f2' called with result: None +</pre> +<p>By default, the decorators module only decorates classes with a magic +docstring (and they subclasses, even without magic docstrings). +If all the classes of your module have the same magic docstring, +it makes sense to decorate them all +with a single command. It is enough to use <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt> +with a magic docstring corresponding to a class decorator as argument, +as in this example:</p> +<blockquote> +<pre class="literal-block"> +#<example.py> + +from example2 import identity,name +import inspect, decorators; decorators.enhance_classes("[Decorated]") + +class C1: # automagically converted to a decorated class + identity=identity + +class C2: # automagically converted to a DecoratedLogged class + "[Logged]" + name=name + +c1=C1() # C1 instance +c2=C2() # C2 instance + +#</example.py> +</pre> +</blockquote> +<p>Notice that class <tt class="literal"><span class="pre">C2</span></tt> has already a magic docstring. This means that +<tt class="literal"><span class="pre">C2</span></tt> has to be enhanced both from <tt class="literal"><span class="pre">Logged</span></tt> and from <tt class="literal"><span class="pre">Decorated</span></tt>. +This is done by automagically creating a <tt class="literal"><span class="pre">DecoratedLogged</span></tt> class +decorator:</p> +<pre class="doctest-block"> +>>> from example import C1,C2,c1,c2 +<class C2[DecoratedLogged]> created +</pre> +<p>The second line is printed because of the logging capabilities of <tt class="literal"><span class="pre">C2</span></tt>. +Moreover, since <tt class="literal"><span class="pre">C2</span></tt> is decorated too, the following will work:</p> +<pre class="doctest-block"> +>>> assert C2.name() == 'C2' +>>> assert c2.name() == 'C2' +</pre> +<p>Idem for <tt class="literal"><span class="pre">C1</span></tt>:</p> +<pre class="doctest-block"> +>>> assert C1.identity(1) == 1 +>>> assert c1.identity(1) == 1 +</pre> +<p>You may check that the magic works both for new style classes (decorating +module <tt class="literal"><span class="pre">object</span></tt> class) and old style classes (by setting the module level +<tt class="literal"><span class="pre">__metaclass</span></tt> attribute and by implicitly converting the classes +to new style classes).</p> +<p>This magical approach is the easiest way to go if you want to trace +inheritance hierarchies. For instance, here is how to trace cooperative +methods in complicate (which is useful for debugging):</p> +<blockquote> +<pre class="literal-block"> +#<tracing.py> + +import customdec; customdec.enhance_classes("[Decorated]") + +class B(object): + def __init__(self): + "[tracedmethod]" + super(B,self).__init__() + +class D(object): + def __init__(self): + "[tracedmethod]" + super(D,self).__init__() + +class E(B,D): + def __init__(self): + "[tracedmethod]" + super(E,self).__init__() + + #</tracing.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from tracing import E +>>> e=E() +Calling 'E.__init__' with arguments <E instance>(){} ... + Calling 'B.__init__' with arguments <E instance>(){} ... + Calling 'D.__init__' with arguments <E instance>(){} ... + 'D.__init__' called with result: None + 'B.__init__' called with result: None +'E.__init__' called with result: None +</pre> +</div> +<div class="section" id="advanced-usage"> +<h1><a class="toc-backref" href="#id9" name="advanced-usage">Advanced usage</a></h1> +<p>Whereas the average programmer is expected to use the +<tt class="literal"><span class="pre">decorators.decorated</span></tt> function only, the module provides access to +its underlining implementation, which may be useful to the advanced +programmer.</p> +<p>The module provides an utility functions to retrieve the list of +recognized decorators: <tt class="literal"><span class="pre">decorators.get(docstring)</span></tt>, where <tt class="literal"><span class="pre">docstring</span></tt> +is a magic docstring, i.e. a bracketed comma-separated list +of decorator names. For instance <tt class="literal"><span class="pre">decorators.get('[MethodDecorator]')</span></tt> +gives the list of all subclasses of <tt class="literal"><span class="pre">MethodDecorator</span></tt>, i.e. all method +decorators, whereas <tt class="literal"><span class="pre">decorators.get('[ClassDecorator]')</span></tt> +gives the list of the known class decorators. It is even possible +to use the comma notation:</p> +<pre class="doctest-block"> +>>> decorators.get("[classmethod,tracedmethod]") +[<class 'noconflict.classmethodtracedmethod'>] +</pre> +<p>For instance, it is possible to decorate functions by hand, +without using magic docstring. Here is an example:</p> +<pre class="doctest-block"> +>>> do_nothing=decorators.staticmethod(lambda:None) +>>> print do_nothing # ``do_nothing`` is a static method +<staticmethod:<lambda>> +</pre> +<p>One can even compose decorators by hand:</p> +<pre class="doctest-block"> +>>> class B: pass +... +>>> B.chattystatic=customdec.chattymethod2(do_nothing) +>>> B.chattystatic() +calling <chattymethod2staticmethod:<lambda>> from <class B[ClassDecorator]> +</pre> +<p>In other words</p> +<blockquote> +<tt class="literal"><span class="pre">decorator1(decorator2(obj))</span></tt></blockquote> +<p>automagically creates a composed class <tt class="literal"><span class="pre">decorator1decorator2</span></tt> in the +<tt class="literal"><span class="pre">noconflict</span></tt> module (or recycle it, if <tt class="literal"><span class="pre">decorator1decorator2</span></tt> has +been already created) and it is equivalent to</p> +<blockquote> +<tt class="literal"><span class="pre">decorator1decorator2(obj)</span></tt></blockquote> +<p>Here is the check:</p> +<pre class="doctest-block"> +>>> decorators.get("[chattymethod2staticmethod]") +[<class 'noconflict.chattymethod2staticmethod'>] +</pre> +<p>Here is another example:</p> +<pre class="doctest-block"> +>>> from customdec import tracedmethod +>>> class C(object): +... def fact(self,n): +... if n==0: return 1 +... else: return n*self.fact(n-1) +... fact=tracedmethod(fact) +>>> c=C() +>>> c.fact(2) +Calling '?.fact' with arguments <C instance>(2,){} ... + Calling '?.fact' with arguments <C instance>(1,){} ... + Calling '?.fact' with arguments <C instance>(0,){} ... + '?.fact' called with result: 1 + '?.fact' called with result: 1 +'?.fact' called with result: 2 +2 +</pre> +<p>In this second syntax <tt class="literal"><span class="pre">fact</span></tt> does not know where it +is defined, unless the containing class is explicitly set:</p> +<pre class="doctest-block"> +>>> C.__dict__['fact'].__klass__=C +</pre> +<p>Now the code will work as with the docstring syntax.</p> +<p>What happens if you try to decorate something which is already +decorated? You get <tt class="literal"><span class="pre">None</span></tt>:</p> +<pre class="doctest-block"> +>>> def f(x): "[tracedmethod]" +... +>>> tm=decorators.decorated(f) # creates a tracedmethod from f +>>> print decorators.decorated(tm) # trying to decorate a tracedmethod +None +</pre> +<p>You can compose decorators in a trivial way:</p> +<pre class="doctest-block"> +>>> cmtm=decorators.classmethod(tm) +>>> print cmtm +<classmethodtracedmethod:f> +</pre> +<p>whereas you can compose classes:</p> +<pre class="doctest-block"> +>>> class C: "[Decorated]" +... +>>> print customdec.Traced(C) +<class C[TracedDecorated]> +</pre> +<pre class="doctest-block"> +>>> class D: pass +... +>>> print customdec.Decorated(customdec.Traced(D)) +<class D[DecoratedTraced]> +</pre> +<p>This is the hierarchy:</p> +<pre class="doctest-block"> +>>> for C in type(cmtm).mro(): print C +... +<class 'noconflict.classmethodtracedmethod'> +<class 'decorators.classmethod'> +<class 'customdec.tracedmethod'> +<class 'decorators.MethodDecorator'> +<class 'decorators.Decorator'> +<type 'object'> +</pre> +<p>One can also decorate classes by hand, by using class decorators. +<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> which converts a regular (both old +style or new style) class in a class with the ability to recognize +magic docstrings. Actually, the <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt> +trick works by decorating the <tt class="literal"><span class="pre">object</span></tt> class with +<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and by setting the custom metaclass to +<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and it is equivalent to write</p> +<blockquote> +<pre class="literal-block"> +import decorators +object=decorators.ClassDecorator(object) # decorates all new style classes +__metaclass__= decorators.ClassDecorator # decorates all old style classes +</pre> +</blockquote> +<p>on top of your module. +If you want the magic to work only for new style classes only, you may +forget the +second line; if you want the magic to work for old style classes only, you +may forget the first line.</p> +<p>If the module contains a magic docstring which is an explicit class decorator, +such as <tt class="literal"><span class="pre">decorators.Decorated</span></tt>, then <tt class="literal"><span class="pre">decorators.decorated</span></tt> look at the +magic docstring and executes something like</p> +<blockquote> +<pre class="literal-block"> +import decorators +object=decorators.Decorated(object) # decorates all new style classes +__metaclass__= decorators.Decorated # decorates all old style classes +</pre> +</blockquote> +<p>It is possible to go even deeper in black magic, and to decorate all +the new style classes in <em>all</em> modules, by decorating <tt class="literal"><span class="pre">__builtin__.object</span></tt>:</p> +<blockquote> +<pre class="literal-block"> +import decorators,__builtin__ +__builtin.object=decorators.decorated(object) +</pre> +</blockquote> +<p>Still, redefining <tt class="literal"><span class="pre">__builtin__object</span></tt> is not recommended since it +may induce metaclass conflicts in other modules using metaclasses. +It will work only if you import modules not using metaclasses, or +modules using metaclasses safely, i.e. modules taking care of +possible conflicts by using the <tt class="literal"><span class="pre">makecls</span></tt> function or an equivalent one.</p> +</div> +<div class="section" id="the-implementation"> +<h1><a class="toc-backref" href="#id10" name="the-implementation">The implementation</a></h1> +<p>This part can be safely skipped, unless you are a <em>really</em> curious and +you want to know how the implementation works.</p> +<p>The module is rather short (~150 lines) but far from being trivial, +since it is based on extensive usage of metaclass wizardry.</p> +<p>The main class-metaclass hierarchy is represented in figure 1, where +boxes denote classes and ovals denote metaclasses; instantiation is +denoted by dashed green lines whereas inheritance is denoted by continuous +blue lines.</p> +<div class="figure"> +<p><img alt="decorators.png" src="decorators.png" /></p> +</div> +<p>Notice that <tt class="literal"><span class="pre">MetaDecorator</span></tt> (inherited via <tt class="literal"><span class="pre">Decorator</span></tt>) is the +metaclass of all decorators. Class decorators are already metaclasses, +so <tt class="literal"><span class="pre">MetaDecorator</span></tt> is actually a meta-metaclass with respect to +instances of decorated classes, whereas it is a simple metaclass with +respect to instances of decorated methods. For this reason in the +presenced of class decorators the unusual relation</p> +<p>assert object.__metaclass__ != object.__class__</p> +<p>holds. More specifically</p> +<pre class="doctest-block"> +>>> object.__class__ +<class 'decorators.ClassDecorator'> +</pre> +<p>but</p> +<pre class="doctest-block"> +>>> object.__metaclass__ +<class 'decorators.MetaDecorator'> +</pre> +<p>since <tt class="literal"><span class="pre">object</span></tt> the <tt class="literal"><span class="pre">__metaclass__</span></tt> +attribute is inherited from <tt class="literal"><span class="pre">Decorator</span></tt>.</p> +<p>The implementation is relatively smart. Consider for instance the case of +the logged example. In that example class <tt class="literal"><span class="pre">D</span></tt> was a subclass of a tricked +<tt class="literal"><span class="pre">object</span></tt> class, enhanced by a metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>. Moreover <tt class="literal"><span class="pre">D</span></tt> +had a <tt class="literal"><span class="pre">Logged</span></tt> docstring. Therefore it should have been an instance of +<tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt> metaclass. But logged was +a subclass of <tt class="literal"><span class="pre">ClassDecorator</span></tt>, therefore it already had all the features +of <tt class="literal"><span class="pre">ClassDecorator</span></tt> and it would have been redundant to create +<tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt>, when``Logged`` would have been enough. +So <tt class="literal"><span class="pre">Logged</span></tt> +was used and <tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt> was never created. All the magic is in +the <tt class="literal"><span class="pre">noconflict</span></tt> module discussed in my <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a> recipe.</p> +<p>The current implementation does not make any attempt of optimization and +there may be alternative implementations faster or more memory efficient. +At this experimental level I didn't care to explore about performances +issues. They does not probably matter unless one has to decorate +thousands or millions of functions and classes.</p> +<p>Things to do: adding more error checking.</p> +<p>Finally, a word about bugs. The <tt class="literal"><span class="pre">decorators</span></tt> module is fairly sophisticated, +therefore whereas I can guarantee that it passes my test suite (which involves +~100 tests automatically extracted from the documentation you are reading), +I cannot guarantee that it is correct. +If somebody finds a bug or an unexpected +behavior, please let me know and I will try to fix it.</p> +<!-- References: --> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="decorators.txt">View document source</a>. +Generated on: 2003-09-17 14:45 UTC. +Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. +</div> +</body> +</html> diff --git a/pypers/pep318/working/decorators.py b/pypers/pep318/working/decorators.py new file mode 100755 index 0000000..ddf3ec9 --- /dev/null +++ b/pypers/pep318/working/decorators.py @@ -0,0 +1,173 @@ +# implementation with a strong usage of metaclasses + +""" +A module to implement pep318 (decorator syntax) via magic doctrings. +For the documentation see + +http://www.phyast.pitt.edu/~micheles/python/decorators,html + +and the on-line help. +""" + +import sys, re, inspect, __builtin__, noconflict +from types import FunctionType,ClassType +from noconflict import makecls +#from printerr import printerr + +############################ UTILITIES ############################## + +MAGICDOC=re.compile(r'\s*\[([\w_\s,]*)\]') +# regexp to recognize magic docstrings + +class UnknownDecoratorError(Exception): + "The name says it all" + +class MetaDecorator(type): + """Metaclass inducing a certain amount of magic on decorators: + 1. Each time a decorator is defined in any module, it is stored in + MetaDecorator.dic and MetaDecorator.ls. + 2. If the (method) decorator has a 'get' method, a '__get__' method + is automagically created as an alias to 'get'. + 3. Decorators calls are dispatched to the decorator _call_ + classmethod.""" + + dic,ls={},[] + + def __init__(dec,*args): + super(MetaDecorator,dec).__init__(*args) + MetaDecorator.dic[dec.__name__]=dec + MetaDecorator.ls.append(dec) + get=dec.__dict__.get('get') # look at get + if get: dec.__get__=get # alias __get__ to get + + def __call__(dec,*args): + nargs=len(args) + if nargs==1: # simple call of kind dec(obj) + obj=args[0] + if isinstance(obj,Decorator): + dec=composed(dec,obj.__class__) # composed default decorator + dec=decorator_from(obj.__doc__,dec) + elif nargs==3: # class decorator called with three arguments + dec=decorator_from(args[2].get('__doc__'),dec) + return dec._call_(*args) # dispatch to the correct _call_ classmethod + +def composed(*dclasses): + dclasses=noconflict.remove_redundant(dclasses) + decname=''.join([d.__name__ for d in dclasses]) + decorator=MetaDecorator.dic.get(decname) + if not decorator: decorator=makecls()(decname,dclasses,{}) + return decorator + +def decorator_from(docstring,defaultdec=None): + """Takes a magic docstring and a default decorator + and returns a decorator class or None. It tries to be smart.""" + match=MAGICDOC.match(docstring or '') + if not match: return defaultdec + decnames=map(str.strip,match.group(1).split(',')) + try: dclasses=[MetaDecorator.dic[n] for n in decnames] # get the decorator + except KeyError: raise UnknownDecoratorError(n) # classes from the names + if defaultdec: dclasses.insert(0,defaultdec) + return composed(*dclasses) + +def decorate(objdict): + """Takes an object with a dictionary and decorates all its functions + and classes according to their docstrings.""" + for name,obj in objdict.__dict__.items(): + if getattr(obj,'__doc__',None): # non-empty docstring + dec_obj=decorated(obj) or obj + if dec_obj is not obj: + setattr(dec_obj,'__klass__',objdict) + setattr(objdict,name,dec_obj) + +def get(docstring=None): + "List of recognized decorators" + dec=decorator_from(docstring,Decorator) + isdecorator=lambda x: issubclass(x,dec) + return filter(isdecorator,MetaDecorator.ls) + +#################### functions of the API ################################# + +def decorated(obj): + """If obj is a function or a class, returns a decorated object created + according to obj's docstring or the original obj if no magic docstring + is found; otherwise it returns None""" + if isinstance(obj,(FunctionType,ClassType,type)): # is function or class + return Decorator(obj) or obj + +def enhance_classes(docstring='[ClassDecorator]'): + """Enhances all the classes in the caller module with a metaclass + built from the given docstring.""" + callerglobals=sys._getframe(1).f_globals + dec=decorator_from(docstring) + callerglobals['__metaclass__']=dec + callerglobals['object']=dec('object',(),{}) + +####################### BASIC DECORATORS ########################### + +class Decorator(object): + """Instance of MetaDecorator and mothers of all decorators. When called + in the form Decorator(obj), with obj having a magic docstring, it returns + an instance of the correct decorator, otherwise it returns None.""" + __metaclass__=MetaDecorator + def _call_(dec,obj): + "Returns None, all the interesting stuff is in MetaDecorator.__call__" + _call_=classmethod(_call_) + +class MethodDecorator(Decorator): + """Descriptor class callable with a function or a descriptor object + as argument. The calling syntax is redefined by the meta-metaclass + MetaDecorator. It redefines__str__ and get (i.e. __get__) on its instances. + """ + __klass__=type('?',(),{}) # dummy owner class, to be overridden + def __init__(self,objfunc): + "objfunc is a decorator object or a function" + assert isinstance(objfunc,(FunctionType,Decorator)) + super(MethodDecorator,self).__init__(objfunc) + self.__func__=getattr(objfunc,'__func__',objfunc) + def get(self,obj,cls=None): + "aliased to __get__, to be overridden" + return self.__func__.__get__(obj,cls) + def __str__(self): + "Printing representation of kind <decoratorclass:functionname>" + return '<%s:%s>' % (self.__class__.__name__,self.__func__.__name__) + def _call_(dec,obj): + "Returns a method decorator object." + return type.__call__(dec,obj) # calls __new__ and __init__ + _call_=classmethod(_call_) + + +class ClassDecorator(type,Decorator): + """Metaclass callable with one or three arguments, having its calling + syntax redefined by the meta-metaclass MetaDecorator. It redefines + __str__ both on classes and instances.""" + def __init__(cls,name,bases,dic): + super(ClassDecorator,cls).__init__(name,bases,dic) + if cls.__str__ is object.__str__: # redefine default __str__ + cls.__str__=lambda self: "<%s instance>" % self.__class__.__name__ + def __str__(cls): + return '<class %s[%s]>' % (cls.__name__,cls.__class__.__name__) + def _call_(dec,*args): + "Returns a decorated class" + a=args[0] # first argument; can be a string or a class + if inspect.isclass(a): args=a.__name__,a.__bases__,a.__dict__.copy() + return makecls(dec)(*args) + _call_=classmethod(_call_) + +class Decorated(ClassDecorator): + """Metaclass which decorates its instances""" + def __init__(cls,name,bases,dic): + super(Decorated,cls).__init__(name,bases,dic) + decorate(cls) + +#################### Built-in Method Decorators ###################### + +class staticmethod(MethodDecorator): + "Decorator, converts a function in a staticmethod" + def get(self,obj,cls=None): + return super(staticmethod,self).get(obj,cls).im_func + +class classmethod(MethodDecorator): + "Decorator, converts a function in a classmethod" + def get(self,obj,cls=None): + if cls is None: cls=type(obj) + return super(classmethod,self).get(cls,cls) diff --git a/pypers/pep318/working/decorators.txt b/pypers/pep318/working/decorators.txt new file mode 100755 index 0000000..18fb320 --- /dev/null +++ b/pypers/pep318/working/decorators.txt @@ -0,0 +1,1346 @@ +Implementing PEP 318 (decorators)
+======================================================================
+
+ :Module: decorators
+ :Version: 0.5
+ :Author: Michele Simionato
+ :e-mail: MicheleSimionato@libero.it
+ :Licence: Python-like
+ :Disclaimer: This is experimental code. Use it at your own risk!
+
+.. contents::
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of PEP 318 in pure Python.
+
+Here is the rationale:
+
+* some kind of decorator syntax is scheduled to go in Python 2.4,
+ therefore it is interesting to play with the concept;
+
+* it is nice to play with decorators now, without having to
+ wait for one year or so;
+
+* it is much easier levelto experiment with a pure Python implementation
+ than with a C implementation;
+
+* the implementation can be seen as an exercise on modern Python
+ programming and may be valuable to people wanting to study the most
+ advanced new constructs in Python 2.2 (descriptors_, metaclasses_,
+ `cooperative methods`_, etc.)
+
+Basics
+--------------------------------------------------------------------
+
+PEP 318 has the goal of providing a nice syntactic sugar for expressions like
+
+ ::
+
+ def identity(x):
+ return x
+ identity=staticmethod(identity)
+
+or
+
+ ::
+
+ def name(cls):
+ return cls.__name__
+ name=classmethod(name)
+
+which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)
+
+ ::
+
+ def identity(x)[staticmethod]:
+ return x
+
+ def name(cls)[classmethod]:
+ return cls.__name__
+
+involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:
+
+ ::
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+The implementation is able to recognize such docstrings
+and to automagically convert those methods in decorators.
+
+Decorators are nothing else than a sophisticated kind of wrappers.
+The ``decorators`` module provides support both for method decorators,
+which wrap functions and class decorator, which wrap classes.
+``staticmethod`` and ``classmethod`` are two examples of already existing
+method decorators (actually my implementation rewrites them, but let me
+pass on this detail). Technically speaking, method decorators are classes
+taking a single function as input and producing a descriptor object
+as output (properties are not decorators according to this definition,
+since they take four functions as input, ``get, set, del_`` and ``doc``).
+Descriptors are objects with a ``__get__`` method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip on this point ;).
+A knowledge of descriptors is not needed in order to use the ``decorator``
+module; however it is welcomed for advanced users wanting to implement
+custom method decorators.
+Class decorators are metaclasses taking a class as imput and returning
+a decorated class as output. A good understanding of metaclasses is needed
+in order to be able to write custom class decorators, but no knowledge
+at all is required in order to use the pre-defined class decorators
+provided by the module.
+Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the module short.
+
+Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.
+
+Simple usage of decorators
+------------------------------------------------------------------------
+
+Before talking about the implementation details, I will show
+how the ``decorators`` module works in practice. The simplest and safest
+usage is by means of the ``decorators.decorated()`` function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using ``decorators.decorated()``
+is simple but verbose, so magic shortcuts will be discussed in the next
+section.
+
+Here, let me give an example, showing that method decorators work both for
+`new style classes and old style classes`_:
+
+ ::
+
+ #<example1.py>
+
+ import decorators
+
+ def do_nothing(self):
+ "No magic docstring here"
+ dec_do_nothing=decorators.decorated(do_nothing)
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+ dec_identity=decorators.decorated(identity)
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+ dec_name=decorators.decorated(name)
+
+ class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+ class NewStyle(object):
+ name=dec_name
+
+ o=OldStyle() # creates an old style instance
+ n=NewStyle() # creates a new style instance
+
+ #</example1.py>
+
+In this example, both ``dec_identity`` and ``dec_name`` are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects, since they have a re-defined printing representation:
+
+>>> from example1 import *
+
+>>> print dec_identity
+<staticmethod:identity>
+
+>>> print dec_name
+<classmethod:name>
+
+Now, let me check that ``dec_ identity`` works as a staticmethod,
+
+>>> assert OldStyle.identity(1) == 1 # called from the class
+>>> assert o.identity(1) == 1 # called from the instance
+
+whereas ``dec_name`` works as a classmethod:
+
+>>> assert NewStyle.name() == 'NewStyle' # called from the class
+>>> assert n.name() == 'NewStyle' # called from the instance
+
+On the other hand, ``do_nothing`` does not have a magic
+docstring, therefore it is not converted to a decorator object;
+actually it is *exactly* the original function:
+
+>>> assert dec_do_nothing is do_nothing # not converted
+
+Therefore it works without surprises:
+
+>>> o.do_nothing() # does nothing, correct
+
+For sake of convenience, I have re-implemented the built-in
+``staticmethod`` and ``classmethod``, so
+
+>>> isinstance(dec_identity,staticmethod)
+False
+
+and
+
+>>> isinstance(dec_name,classmethod)
+False
+
+but
+
+>>> isinstance(dec_identity,decorators.staticmethod)
+True
+
+and
+
+>>> isinstance(dec_name,decorators.classmethod)
+True
+
+It is possible to recognize method decorators since they provides
+a couple of special attributes:
+
+``__func__``, returning the function from which they originated
+
+ >>> assert dec_identity.__func__ is identity
+ >>> assert dec_name.__func__ is name
+
+and ``__klass__``, returning the class where they where defined
+
+ >>> dec_identity.__klass__
+ <class 'decorators.?'>
+
+The question mark here means that the definition class is unknown.
+
+Decorating classes
+----------------------------------------------------------------------
+
+The problem with the approach described in the previous section
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically converting their
+methods to method decorators according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with "[Decorated]"
+and decorating the class.
+Here is an example:
+
+ ::
+
+ #<example2.py>
+
+ from decorators import decorated
+ from example1 import do_nothing,identity,name
+
+ class B(object):
+ "This is a regular class"
+
+ B=decorated(B) # does nothing
+
+ class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ name=name
+
+ C=decorated(C) # regenerates the class converting methods in decorators
+ c=C()
+
+ #</example2.py>
+
+Here is the testing:
+
+>>> from example2 import *
+>>> assert C.identity(1) == 1
+>>> assert C.name() == 'C'
+>>> assert c.identity(1) == 1
+>>> assert c.name() == 'C'
+
+Notice that adding ``identity`` after the class creation with the syntax
+``C.identity=identity`` would not work;
+``C.identity=decorators.decorated(identity)`` is required:
+
+>>> C.identity=decorators.decorated(identity)
+>>> C.identity(1) # it works
+1
+
+If a class misses the magic docstring, nothing happens:
+
+>>> B # returns the original B
+<class 'example2.B'>
+
+The mechanism works for old style classes too:
+
+ ::
+
+ #<example2.py>
+
+ class D: # old style
+ "[Decorated]"
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ D=decorated(D)
+
+ d=D()
+
+ # test
+ assert d.identity(1) == 1
+ assert D.identity(1) == 1
+
+ #</example2.py>
+
+Under the hood ``decorators.decorated()`` recognizes the class level
+magic docstring "[Decorated]" and creates an instance of the
+``decorators.Decorated`` metaclass; incidentally,
+this converts old style classes in new style classes:
+
+>>> from example2 import D,d
+>>> type(D) # D is an instance of decorator.Decorated
+<class 'decorators.Decorated'>
+
+Internally the metaclass invokes ``decorators.decorated()``
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. By the way, if you mispell a
+decorator name you get an helpful error message:
+
+>>> class E:
+... "[Decorated]"
+... def f():
+... "[staticmeth]"
+>>> E=decorators.decorated(E)
+Traceback (most recent call last):
+ .. a long and cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+
+The enhancement provided by the metaclass includes a new default
+printing representation for both the class
+
+>>> print D # returns the name of D and of its metaclass
+<class D[Decorated]>
+
+and its instances:
+
+>>> print d
+<D instance>
+
+One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:
+
+>>> class E(D):
+... def name(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print E.name()
+E
+
+This approach presents another advantage: the decorated methods know
+the class where they were defined via the special attribute ``__klass__``:
+
+>>> E.__dict__['name'].__klass__ # the class where 'name' is defined
+<class 'E'>
+
+This is useful for introspection and debugging purposes.
+
+Adding magic
+----------------------------------------------------------------------------
+
+The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking ``decorators.decorated()``
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing ``decorators.enhance_classes()``
+on top of the module. Then all methods in all classes of the module
+with a magic docstring will be checked for magic docstrings and
+automagically decorated if needed.
+For instance, the previous example would be written
+
+ ::
+
+ #<example4.py>
+
+ import decorators; decorators.enhance_classes()
+
+ class C:
+ "[Decorated]" # magic docstring here
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ class D(object):
+ "Undecorated" # no magic docstring here
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ c=C(); d=D()
+
+ #</example4.py>
+
+``C`` has a ``[Decorated]`` docstring, so its methods
+are automatically decorated:
+
+>>> from example4 import *
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert c.identity(1) == 1
+
+On the other hand, since ``D`` misses a magic docstring,
+its ``name`` method is not decorated:
+
+>>> hasattr(D.__dict__['name'],'__func__') # not a decorator
+False
+
+Since ``D.name`` is a regular method and not a classmethod, ``D.name()``
+gives an error:
+
+>>> D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance as first argument (got nothing instead)
+
+The trick works for classes containing inner classes, too:
+
+ ::
+
+ #<example5.py>
+
+ import decorators; decorators.enhance_classes()
+
+ class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+
+ assert C.identity(1) == 1
+ assert C.Inner.name() == 'Inner'
+
+ #</example5.py>
+
+Under the hood, the magic works by enhancing the ``object`` class
+of the module with a ``decorators.ClassDecorator`` metaclass:
+
+>>> import example5
+>>> type(example5.object)
+<class 'decorators.ClassDecorator'>
+
+Notice that for safety reasons the enhancement is only on the module
+``object`` class, not on the ``__builtin__.object`` class. The problem
+is that adding too much magic can be risky.
+
+The dangers of magic
+-----------------------------------------------------------------------
+
+For the sake of metaclass users, in this section I will point out the
+dangers of the ``enhance_classes()`` syntax. On the other hand, if you
+never use metaclasses, you may safely skip to the following section.
+
+The problem is that the ``enhance_classes()`` syntax is not 100% safe,
+because of possible metaclass conflicts.
+Here is an example:
+
+>>> import decorators; decorators.enhance_classes()
+
+This line enhances the ``object`` class in the interpreter namespace:
+
+>>> print object
+<class object[ClassDecorator]>
+
+This shows that ``object`` is an instance of ``ClassDecorator``.
+
+>>> class M(type):
+... "Some non-trivial code here..."
+
+This line creates a custom metaclass we want to use to enhance our classes.
+
+>>> class D(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The problem is that the previous line tries to create a class ``D``
+which should have both metaclasses ``ClassDecorator`` and ``M``:
+a conflict follows.
+
+Fortunately, the decorators module imports the ``makecls`` function from my
+``noconflict`` module (described in the cookbook_)
+just to avoid this kind of problems:
+
+>>> class D(object):
+... __metaclass__=decorators.makecls(M)
+
+Now the class has been safely created as an instance of the composed
+class ``ClassDecoratorM``.
+
+>>> type(D)
+<class 'noconflict.ClassDecoratorM'>
+
+If we want ``M`` to have the priority over ``ClassDecorator``, the
+option ``priority=True`` makes the job:
+
+>>> class D(object):
+... __metaclass__=decorators.makecls(M,priority=True)
+>>> type(D)
+<class 'noconflict.MClassDecorator'>
+
+The situation for old style classes is worse, since
+
+>>> class D:
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+
+apparently gives no error, but actually the metaclass ``M`` overrides
+``ClassDecorator``, so ``D`` will not recognize magic docstrings:
+
+>>> type(D)
+<class 'M'>
+>>> D.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with D instance as first argument (got nothing instead)
+
+Using ``decorators.makecls(M)`` will not help here (because of the way Python
+assigns metaclasses) and the only solution is to be completely explicit:
+
+>>> class D:
+... __metaclass__=decorators.makecls(decorators.ClassDecorator,M)
+... def sm():
+... "[staticmethod]"
+>>> type(D)
+<class 'noconflict.ClassDecoratorM'>
+
+Now ``D`` is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so ``D``
+subclasses with a "[Decorated]" docstring will be decorated:
+
+>>> class E(D):
+... "[Decorated]"
+... def cm(cls):
+... "[classmethod]"
+... print '.cm() called from',cls
+
+>>> E.cm() # it works
+.cm() called from <class E[ClassDecoratorMDecorated]>
+
+Notice that ``sm`` was defined in ``D``, the undecorated class: therefore
+it is not decorated:
+
+>>> E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead)
+
+The error message says clearly that ``sm`` is an unbound method and not
+a static method.
+
+Defining method decorators
+-----------------------------------------------------------------------
+
+The ``decorators module`` contains two predefinite method decorators,
+``staticmethod`` and ``classmethod``, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The ``decorators.MethodDecorator`` class which is here
+exactly for that purpose.
+
+Custom decorators are expected to be implemented by subclassing
+``MethodDecorator`` and by overriding its ``get`` method. The
+``get`` method automagically induces a ``__get__`` method, turning the
+class in a descriptor. The machinery is needed since ``__get__`` cannot
+be made cooperative using the standard ``super`` mechanism because
+there would be a confusion between ``super.__get__`` and the decorator
+``__get__``. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators *too* easy, since they are potentially dangerous.
+
+In order to give a simple example, let me show the implementation
+of a ``chattymethod`` that prints a message when it is called:
+
+ ::
+
+ #<customdec.py>
+
+ from decorators import *
+
+ class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+ #</customdec.py>
+
+Notice the usage of the ``super().get`` trick. This guarantees that
+``chattymethod`` will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.
+
+Here is an example of usage:
+
+>>> import customdec # adds chattymethod to the list of known decorators
+>>> customdec.enhance_classes() # automagically enhances classes when needed
+>>> class C:
+... " [Decorated] "
+... def f(self):
+... """
+... [ chattymethod ]
+... """
+>>> c=C()
+>>> c.f()
+calling <chattymethod:f> from <C instance>
+
+By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.
+
+One can check that the syntax ``C.f(c)`` works too:
+
+>>> C.f(c)
+calling <chattymethod:f> from <class C[Decorated]>
+
+A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind
+
+ ::
+
+ def f(self)[chattymethod(logfile=file('file1.log','w'))]
+
+In my view, there are too many parenthesis in this syntax, and it may
+become rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the decorators module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.
+
+A simple minded solution is to change the defaults by hand:
+
+>>> from customdec import chattymethod,decorated
+>>> chattymethod.logfile=file('file.log','w')
+>>> def g(self):
+... "[chattymethod]"
+>>> C.g=decorated(g)
+>>> c.g() # will print a message on file.log
+
+This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore
+
+>>> c.f()
+
+will print a message to ``file.log`` too, and not to standard output.
+Here is the confirmation:
+
+>>> chattymethod.logfile.close()
+>>> print file('file.log').read().rstrip()
+calling <chattymethod:g> from <C instance>
+calling <chattymethod:f> from <C instance>
+
+A much better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the ``__init__`` method. Here is an example:
+
+ ::
+
+ #<customdec.py>
+
+ class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+ #</customdec.py>
+
+Notice that the ``__init__`` method has the signature
+``__init__(self,objfunc)``, where the ``objfunc`` object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding ``__init__`` (see also
+``help(decorators.MethodDecorator.__init__)``).
+
+Here is the testing:
+
+ ::
+
+ #<chatty2.py>
+
+ import customdec; customdec.enhance_classes("[Decorated]")
+
+ # sets the log files
+ log1=file('file1.log','w')
+ log2=file('file2.log','w')
+
+ class C:
+ def f(self):
+ "[chattymethod2]"
+ f.logfile=log1 # function attribute
+ def g(self):
+ "[chattymethod2]"
+ g.logfile=log2 # function attribute
+
+ assert C.__dict__['f'].logfile is log1 # check the conversion
+ assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr.
+
+ c=C() # C instantiation
+
+ c.f() # print a message in file1.log
+ c.g() # print a message in file2.log
+
+ log1.close(); log2.close() # finally
+
+ #</chatty2.py>
+
+Let me check the contents of the log files:
+
+>>> import chatty2
+>>> print file('file1.log').read().rstrip()
+calling <chattymethod2:f> from <C instance>
+>>> print file('file2.log').read().rstrip()
+calling <chattymethod2:g> from <C instance>
+
+``chattymethod`` is the poor man version of ``tracedmethod``, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.
+
+ ::
+
+ #<customdec.py>
+
+ class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -> dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+ #</customdec.py>
+
+``tracedmethod.get`` returns a method wrapper object, so it is
+possible to use ``im_func`` to retrieve the internal function
+``tracedmeth``:
+
+>>> class C:
+... "[Decorated]"
+... def f(self):
+... "[tracedmethod]"
+>>> c=C(); c.f()
+Calling 'C.f' with arguments <C instance>(){} ...
+'C.f' called with result: None
+>>> c.f.im_func.__name__
+'tracedmeth'
+
+As soon as the ``tracedmethod`` module is loaded, the ``tracedmethod`` class
+is added to the list of know decorators, so one should use the
+"[tracedmethod]" docstring and not something like
+"[customdec.tracedmethod]".
+
+Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:
+
+ ::
+
+ #<example9.py>
+
+ import customdec; customdec.enhance_classes()
+
+ logfile=file('file3.log','w')
+
+ class C(object):
+ "[Decorated]"
+ def fact(self,n):
+ "[tracedmethod] The good old factorial."
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+ C().fact(2) # write a message to logfile
+
+ logfile.close()
+
+ #</example9.py>
+
+Here is the content of the ``file3.log``:
+
+>>> import example9
+>>> print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments <C instance>(2,){} ...
+ Calling 'C.fact' with arguments <C instance>(1,){} ...
+ Calling 'C.fact' with arguments <C instance>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+
+Composing decorators
+---------------------------------------------------------------------
+
+Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod as in this example:
+
+ ::
+
+ #<example6.py>
+
+ "How to trace a class method"
+
+ import customdec; customdec.enhance_classes()
+
+ class C(object):
+ "[Decorated]"
+ def fact(cls,n): # a traced classmethod
+ "[classmethod,tracedmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+ #</example6.py>
+
+Here is the testing:
+
+>>> from example6 import C
+>>> C.fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+You may easily check that calling ``.fact`` from the instance will work too.
+
+Under the hood the syntax
+
+ ::
+
+ [classmethod,tracedmethod]
+
+generates a ``classmethodtracedmethod`` class obtained via
+multiple inheritance:
+
+>>> C.__dict__['fact'].__class__
+<class 'noconflict.classmethodtracedmethod'>
+
+Notice that the order does matter and using the docstring
+"[tracedmethod,classmethod]" will not work:
+
+>>> class D:
+... "[Decorated]"
+... def f(cls):
+... "[tracedmethod,classmethod]"
+>>> D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead)
+
+The problem here is that ``tracedmethod.get`` returns a method-wrapper object
+which expects a D instance as first argument whereas it gets ``None`` since
+it is called from the class. On the other hand,
+
+>>> D().f()
+Calling 'D.f' with arguments <D instance>(){} ...
+'D.f' called with result: None
+
+will work. When ``classmethod`` precedes ``tracedmethod``, then
+``classmethod`` passes to ``tracedmeth`` a non-empty first argument,
+i.e. the calling class, even when called from the instance:
+
+>>> C().fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+If we try to trace a staticmethod, we will get a different error with
+the order "tracedmethod, staticmethod":
+
+>>> class F(object):
+... "[Decorated]"
+... def fact(n):
+... "[tracedmethod,staticmethod]"
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+>>> F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead)
+
+The message is self-explanatory.
+
+On the other hand, composing the decorators in the other order
+"[tracedmethod,staticmethod]" will work just fine.
+
+Defining class decorators
+-----------------------------------------------------------------------
+
+PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a
+class decorator at work, the metaclass ``Decorated``, giving
+to its instances the ability to interpret magic docstrings,
+and converting functions in method decorators.
+
+To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference from deriving by ``ClassDecorator`` instead
+of deriving from ``type``. To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:
+
+ ::
+
+ #<customdec.py>
+
+ class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print >> cls.output,"%s created" % cls
+
+ #</customdec.py>
+
+``Logged`` is derived by the metaclass ``ClassDecorator``,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+metaclass ``MetaDecorator``). Logging capabilities can be added to a class
+by simply using the magic docstring syntax:
+
+ ::
+
+ #<logged.py>
+
+ import customdec; customdec.enhance_classes()
+
+ class D(object): # will print a message at D creation
+ "[Logged]"
+
+ #</logged.py>
+
+>>> import logged
+<class D[Logged]> created
+
+Notice that the printing representation of ``D`` involves the name
+of ``D`` preceded by the name of its metaclass, which in this case
+is ``Logged``
+
+Each time an instance of ``Logged`` is created, a similar message is printed:
+
+>>> class E(logged.D):
+... pass
+<class E[Logged]> created
+
+Notice that ``E`` does not have any magic docstring
+
+>>> E.__doc__ # no docstring
+
+but still it inherits its magic from ``D``.
+
+Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:
+
+ ::
+
+ #<customdec.py>
+
+ from types import FunctionType
+
+ class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType): # modifies the docstring
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+
+ #</customdec.py>
+
+Here is an example of usage:
+
+>>> class C(object):
+... """[Decorated,Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... to method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> type(C)
+<class 'noconflict.DecoratedTraced'>
+>>> c=C()
+>>> c.f1()
+Calling 'C.f1' with arguments <C instance>(){} ...
+'C.f1' called with result: None
+>>> c.f2()
+Calling 'C.f2' with arguments <C instance>(){} ...
+'C.f2' called with result: None
+
+By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use ``decorators.enhance_classes()``
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:
+
+ ::
+
+ #<example.py>
+
+ from example2 import identity,name
+ import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+ class C1: # automagically converted to a decorated class
+ identity=identity
+
+ class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+ c1=C1() # C1 instance
+ c2=C2() # C2 instance
+
+ #</example.py>
+
+Notice that class ``C2`` has already a magic docstring. This means that
+``C2`` has to be enhanced both from ``Logged`` and from ``Decorated``.
+This is done by automagically creating a ``DecoratedLogged`` class
+decorator:
+
+>>> from example import C1,C2,c1,c2
+<class C2[DecoratedLogged]> created
+
+The second line is printed because of the logging capabilities of ``C2``.
+Moreover, since ``C2`` is decorated too, the following will work:
+
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2'
+
+Idem for ``C1``:
+
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1
+
+You may check that the magic works both for new style classes (decorating
+module ``object`` class) and old style classes (by setting the module level
+``__metaclass`` attribute and by implicitly converting the classes
+to new style classes).
+
+This magical approach is the easiest way to go if you want to trace
+inheritance hierarchies. For instance, here is how to trace cooperative
+methods in complicate (which is useful for debugging):
+
+ ::
+
+ #<tracing.py>
+
+ import customdec; customdec.enhance_classes("[Decorated]")
+
+ class B(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(B,self).__init__()
+
+ class D(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(D,self).__init__()
+
+ class E(B,D):
+ def __init__(self):
+ "[tracedmethod]"
+ super(E,self).__init__()
+
+ #</tracing.py>
+
+>>> from tracing import E
+>>> e=E()
+Calling 'E.__init__' with arguments <E instance>(){} ...
+ Calling 'B.__init__' with arguments <E instance>(){} ...
+ Calling 'D.__init__' with arguments <E instance>(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None
+
+Advanced usage
+---------------------------------------------------------------------------
+
+Whereas the average programmer is expected to use the
+``decorators.decorated`` function only, the module provides access to
+its underlining implementation, which may be useful to the advanced
+programmer.
+
+
+The module provides an utility functions to retrieve the list of
+recognized decorators: ``decorators.get(docstring)``, where ``docstring``
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance ``decorators.get('[MethodDecorator]')``
+gives the list of all subclasses of ``MethodDecorator``, i.e. all method
+decorators, whereas ``decorators.get('[ClassDecorator]')``
+gives the list of the known class decorators. It is even possible
+to use the comma notation:
+
+>>> decorators.get("[classmethod,tracedmethod]")
+[<class 'noconflict.classmethodtracedmethod'>]
+
+For instance, it is possible to decorate functions by hand,
+without using magic docstring. Here is an example:
+
+>>> do_nothing=decorators.staticmethod(lambda:None)
+>>> print do_nothing # ``do_nothing`` is a static method
+<staticmethod:<lambda>>
+
+One can even compose decorators by hand:
+
+>>> class B: pass
+...
+>>> B.chattystatic=customdec.chattymethod2(do_nothing)
+>>> B.chattystatic()
+calling <chattymethod2staticmethod:<lambda>> from <class B[ClassDecorator]>
+
+In other words
+
+ ``decorator1(decorator2(obj))``
+
+automagically creates a composed class ``decorator1decorator2`` in the
+``noconflict`` module (or recycle it, if ``decorator1decorator2`` has
+been already created) and it is equivalent to
+
+ ``decorator1decorator2(obj)``
+
+Here is the check:
+
+>>> decorators.get("[chattymethod2staticmethod]")
+[<class 'noconflict.chattymethod2staticmethod'>]
+
+Here is another example:
+
+>>> from customdec import tracedmethod
+>>> class C(object):
+... def fact(self,n):
+... if n==0: return 1
+... else: return n*self.fact(n-1)
+... fact=tracedmethod(fact)
+>>> c=C()
+>>> c.fact(2)
+Calling '?.fact' with arguments <C instance>(2,){} ...
+ Calling '?.fact' with arguments <C instance>(1,){} ...
+ Calling '?.fact' with arguments <C instance>(0,){} ...
+ '?.fact' called with result: 1
+ '?.fact' called with result: 1
+'?.fact' called with result: 2
+2
+
+In this second syntax ``fact`` does not know where it
+is defined, unless the containing class is explicitly set:
+
+>>> C.__dict__['fact'].__klass__=C
+
+Now the code will work as with the docstring syntax.
+
+
+
+
+What happens if you try to decorate something which is already
+decorated? You get ``None``:
+
+>>> def f(x): "[tracedmethod]"
+...
+>>> tm=decorators.decorated(f) # creates a tracedmethod from f
+>>> print decorators.decorated(tm) # trying to decorate a tracedmethod
+None
+
+You can compose decorators in a trivial way:
+
+>>> cmtm=decorators.classmethod(tm)
+>>> print cmtm
+<classmethodtracedmethod:f>
+
+
+whereas you can compose classes:
+
+>>> class C: "[Decorated]"
+...
+>>> print customdec.Traced(C)
+<class C[TracedDecorated]>
+
+
+
+
+>>> class D: pass
+...
+>>> print customdec.Decorated(customdec.Traced(D))
+<class D[DecoratedTraced]>
+
+
+This is the hierarchy:
+
+>>> for C in type(cmtm).mro(): print C
+...
+<class 'noconflict.classmethodtracedmethod'>
+<class 'decorators.classmethod'>
+<class 'customdec.tracedmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.Decorator'>
+<type 'object'>
+
+
+
+One can also decorate classes by hand, by using class decorators.
+``decorators.ClassDecorator`` which converts a regular (both old
+style or new style) class in a class with the ability to recognize
+magic docstrings. Actually, the ``decorators.enhance_classes()``
+trick works by decorating the ``object`` class with
+``decorators.ClassDecorator`` and by setting the custom metaclass to
+``decorators.ClassDecorator`` and it is equivalent to write
+
+ ::
+
+ import decorators
+ object=decorators.ClassDecorator(object) # decorates all new style classes
+ __metaclass__= decorators.ClassDecorator # decorates all old style classes
+
+on top of your module.
+If you want the magic to work only for new style classes only, you may
+forget the
+second line; if you want the magic to work for old style classes only, you
+may forget the first line.
+
+If the module contains a magic docstring which is an explicit class decorator,
+such as ``decorators.Decorated``, then ``decorators.decorated`` look at the
+magic docstring and executes something like
+
+ ::
+
+ import decorators
+ object=decorators.Decorated(object) # decorates all new style classes
+ __metaclass__= decorators.Decorated # decorates all old style classes
+
+It is possible to go even deeper in black magic, and to decorate all
+the new style classes in *all* modules, by decorating ``__builtin__.object``:
+
+ ::
+
+ import decorators,__builtin__
+ __builtin.object=decorators.decorated(object)
+
+Still, redefining ``__builtin__object`` is not recommended since it
+may induce metaclass conflicts in other modules using metaclasses.
+It will work only if you import modules not using metaclasses, or
+modules using metaclasses safely, i.e. modules taking care of
+possible conflicts by using the ``makecls`` function or an equivalent one.
+
+The implementation
+-----------------------------------------------------------------------
+
+This part can be safely skipped, unless you are a *really* curious and
+you want to know how the implementation works.
+
+The module is rather short (~150 lines) but far from being trivial,
+since it is based on extensive usage of metaclass wizardry.
+
+The main class-metaclass hierarchy is represented in figure 1, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.
+
+.. figure:: decorators.png
+
+Notice that ``MetaDecorator`` (inherited via ``Decorator``) is the
+metaclass of all decorators. Class decorators are already metaclasses,
+so ``MetaDecorator`` is actually a meta-metaclass with respect to
+instances of decorated classes, whereas it is a simple metaclass with
+respect to instances of decorated methods. For this reason in the
+presenced of class decorators the unusual relation
+
+assert object.__metaclass__ != object.__class__
+
+holds. More specifically
+
+>>> object.__class__
+<class 'decorators.ClassDecorator'>
+
+but
+
+>>> object.__metaclass__
+<class 'decorators.MetaDecorator'>
+
+since ``object`` the ``__metaclass__``
+attribute is inherited from ``Decorator``.
+
+The implementation is relatively smart. Consider for instance the case of
+the logged example. In that example class ``D`` was a subclass of a tricked
+``object`` class, enhanced by a metaclass ``ClassDecorator``. Moreover ``D``
+had a ``Logged`` docstring. Therefore it should have been an instance of
+``_ClassDecoratorLogged`` metaclass. But logged was
+a subclass of ``ClassDecorator``, therefore it already had all the features
+of ``ClassDecorator`` and it would have been redundant to create
+``_ClassDecoratorLogged``, when``Logged`` would have been enough.
+So ``Logged``
+was used and ``_ClassDecoratorLogged`` was never created. All the magic is in
+the ``noconflict`` module discussed in my cookbook_ recipe.
+
+The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. They does not probably matter unless one has to decorate
+thousands or millions of functions and classes.
+
+Things to do: adding more error checking.
+
+Finally, a word about bugs. The ``decorators`` module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~100 tests automatically extracted from the documentation you are reading),
+I cannot guarantee that it is correct.
+If somebody finds a bug or an unexpected
+behavior, please let me know and I will try to fix it.
+
+.. References:
+
+.. _cooperative methods:
+ http://www.python.org/2.3/descrintro.html
+.. _new style classes and old style classes:
+ http://www.python.org/2.3/descrintro.html
+.. _descriptors: http://users.rcn.com/python/download/Descriptor.htm
+.. _cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+.. _metaclasses: http://www-106.ibm.com/developerworks/library/l-pymeta2.html
diff --git a/pypers/pep318/working/doct.py b/pypers/pep318/working/doct.py new file mode 100755 index 0000000..7e32291 --- /dev/null +++ b/pypers/pep318/working/doct.py @@ -0,0 +1,60 @@ +# doct.py + +"""Search text files for examples to run via doctest. Example of usage: + +$ doct doc1.txt -v doc2.txt doc3.txt + +This runs the tests in doc1.txt and doc3.txt in silent mode and the +tests in doc2.txt in verbose mode.""" + +import os,sys,doctest,time,textwrap,re + +sys.path[0]=os.getcwd() # explicitly puts the current directory in the path + +DOTNAME=r'\b[a-zA-Z_][\w\.]*', # identifier with or without dots +SCRIPT=re.compile(r'(?s)#<(%s)>(.*?)#</\1>' % DOTNAME) +# regular expressions to identify code blocks of the form +#<scriptname.py> +#... +#</scriptname.py> + +TESTER=doctest.Tester(globs={},verbose=0),doctest.Tester(globs={},verbose=1) +# regular and verbose tester instances + +def extractscript(txt): + for MO in SCRIPT.finditer(txt): + yield MO.group(1),textwrap.dedent(MO.group(2)) + +def rundoct(fname,tester): + txt=file(fname,'U').read() + scriptnames=[]; scriptdict={} + for scriptname,script in extractscript(txt): # read scripts + if scriptname not in scriptnames: + scriptdict[scriptname]=script + scriptnames.append(scriptname) + else: + scriptdict[scriptname]+=script + for scriptname in scriptnames: # save scripts + code='# '+scriptname+scriptdict[scriptname] + print >> file(scriptname,'w'),code + t0=time.clock() + failed,total = tester.runstring(txt,fname) + if failed: + return '' + else: + return "%s: %s tests passed in %s seconds\n" % ( + fname,total,time.clock()-t0) + +def main(args=sys.argv[1:]): + args=iter(args) + for a in args: + if a=='-v': # verbose option + v=1; a=args.next() + else: # default, non-verbose + v=0 + print rundoct(fname=a,tester=TESTER[v]), + +if __name__=='__main__': + if sys.argv[1:]: main() # parse arguments + else: print __doc__ # no arguments given + diff --git a/pypers/pep318/working/example.py b/pypers/pep318/working/example.py new file mode 100755 index 0000000..aedb6f3 --- /dev/null +++ b/pypers/pep318/working/example.py @@ -0,0 +1,16 @@ +# example.py + +from example2 import identity,name +import inspect, decorators; decorators.enhance_classes("[Decorated]") + +class C1: # automagically converted to a decorated class + identity=identity + +class C2: # automagically converted to a DecoratedLogged class + "[Logged]" + name=name + +c1=C1() # C1 instance +c2=C2() # C2 instance + + diff --git a/pypers/pep318/working/example1.py b/pypers/pep318/working/example1.py new file mode 100755 index 0000000..d9226d7 --- /dev/null +++ b/pypers/pep318/working/example1.py @@ -0,0 +1,29 @@ +# example1.py + +import decorators + +def do_nothing(self): + "No magic docstring here" +dec_do_nothing=decorators.decorated(do_nothing) + +def identity(x): + "[staticmethod]" + return x +dec_identity=decorators.decorated(identity) + +def name(cls): + "[classmethod]" + return cls.__name__ +dec_name=decorators.decorated(name) + +class OldStyle: + do_nothing=dec_do_nothing + identity=dec_identity + +class NewStyle(object): + name=dec_name + +o=OldStyle() # creates an old style instance +n=NewStyle() # creates a new style instance + + diff --git a/pypers/pep318/working/example2.py b/pypers/pep318/working/example2.py new file mode 100755 index 0000000..6657edd --- /dev/null +++ b/pypers/pep318/working/example2.py @@ -0,0 +1,36 @@ +# example2.py + +from decorators import decorated +from example1 import do_nothing,identity,name + +class B(object): + "This is a regular class" + +B=decorated(B) # does nothing + +class C(B): + "[Decorated]" + do_nothing=do_nothing + identity=identity + name=name + +C=decorated(C) # regenerates the class converting methods in decorators +c=C() + + + +class D: # old style + "[Decorated]" + def identity(x): + "[staticmethod]" + return x + +D=decorated(D) + +d=D() + +# test +assert d.identity(1) == 1 +assert D.identity(1) == 1 + + diff --git a/pypers/pep318/working/example4.py b/pypers/pep318/working/example4.py new file mode 100755 index 0000000..25338d1 --- /dev/null +++ b/pypers/pep318/working/example4.py @@ -0,0 +1,22 @@ +# example4.py + +import decorators; decorators.enhance_classes() + +class C: + "[Decorated]" # magic docstring here + def do_nothing(self): + "No magic docstring here" + + def identity(x): + "[staticmethod]" + return x + +class D(object): + "Undecorated" # no magic docstring here + def name(cls): + "[classmethod]" + return cls.__name__ + +c=C(); d=D() + + diff --git a/pypers/pep318/working/example5.py b/pypers/pep318/working/example5.py new file mode 100755 index 0000000..81ebce9 --- /dev/null +++ b/pypers/pep318/working/example5.py @@ -0,0 +1,20 @@ +# example5.py + +import decorators; decorators.enhance_classes() + +class C: + "[Decorated]" # required docstring + def identity(x): + "[staticmethod]" + return x + class Inner: + "[Decorated]" # required docstring + def name(cls): + "[classmethod]" + return cls.__name__ + + +assert C.identity(1) == 1 +assert C.Inner.name() == 'Inner' + + diff --git a/pypers/pep318/working/example6.py b/pypers/pep318/working/example6.py new file mode 100755 index 0000000..145c06a --- /dev/null +++ b/pypers/pep318/working/example6.py @@ -0,0 +1,14 @@ +# example6.py + +"How to trace a class method" + +import customdec; customdec.enhance_classes() + +class C(object): + "[Decorated]" + def fact(cls,n): # a traced classmethod + "[classmethod,tracedmethod]" + if n==0: return 1 + else: return n*cls.fact(n-1) + + diff --git a/pypers/pep318/working/example9.py b/pypers/pep318/working/example9.py new file mode 100755 index 0000000..8a6fd33 --- /dev/null +++ b/pypers/pep318/working/example9.py @@ -0,0 +1,19 @@ +# example9.py + +import customdec; customdec.enhance_classes() + +logfile=file('file3.log','w') + +class C(object): + "[Decorated]" + def fact(self,n): + "[tracedmethod] The good old factorial." + if n==0: return 1 + else: return n*self.fact(n-1) + fact.output=logfile + +C().fact(2) # write a message to logfile + +logfile.close() + + diff --git a/pypers/pep318/working/logged.py b/pypers/pep318/working/logged.py new file mode 100755 index 0000000..04be3fd --- /dev/null +++ b/pypers/pep318/working/logged.py @@ -0,0 +1,8 @@ +# logged.py + +import customdec; customdec.enhance_classes() + +class D(object): # will print a message at D creation + "[Logged]" + + diff --git a/pypers/pep318/working/noconflict.py b/pypers/pep318/working/noconflict.py new file mode 100755 index 0000000..e14a1a7 --- /dev/null +++ b/pypers/pep318/working/noconflict.py @@ -0,0 +1,64 @@ +""" +Avoids metaclass conflicts by providing an additional built-in, makecls, +to be used as __metaclass__=makecls(*metaclasses). Returns a class factory. +""" + +metadic={} + +anyTrue=sum + +def remove_redundant(bases): + ls=list(bases); nonredundant={} + for c in bases: + if anyTrue([issubclass(C,c) and c is not C for C in ls], + c in nonredundant): + ls.remove(c) # if c is less specific or duplicated + else: + nonredundant[c]=True + return tuple(ls) + +def _generatemetaclass(bases,metas,priority): + metabs=tuple(map(type,bases)) + metabases=remove_redundant((metabs+metas, metas+metabs)[priority]) + if metabases in metadic: # already generated metaclass + return metadic[metabases] + elif not metabases: # trivial metabase + meta=type + elif len(metabases)==1: # single metabase + meta=metabases[0] + else: # multiple metabases + metaname=''.join([m.__name__ for m in metabases]) + meta=makecls()(metaname,metabases,{}) + return metadic.setdefault(metabases,meta) + +def makecls(*metas,**options): + """Class factory avoiding metatype conflicts. The invocation syntax is + makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have + metaclasses conflicting within themselves or with the given metaclasses, + it automatically generates a compatible metaclass and instantiate it. + If priority is True, the given metaclasses have priority over the + bases metaclasses.""" + + priority=options.get('priority',False) # default, no priority + def clsfactory(n,b,d): + # workaround to allow redefinition of meta-meta.__call__ + meta=_generatemetaclass(b,metas,priority) + return type.__call__(meta,n,b,d) # calls __new__ and __init__ + return clsfactory + +#class Noconflict(type): +# """Meta-metaclass tweaking the metaclass calling in such a way to +# avoid conflicts""" +# def __call__(mcl,n,b,d): +# return makecls(mcl)(n,b,d) + +if __name__=='__main__': # test + import __builtin__ + __builtin__.type=Noconflict('Type',(type,),{}) + class M1(type): pass + class M2(type): pass + class C1: __metaclass__=M1 + class C2: __metaclass__=M2 + class C(C1,C2): pass + #__metaclass__=makecls() +# assert type(C),__name__=='_M1M2' diff --git a/pypers/pep318/working/pep318.html b/pypers/pep318/working/pep318.html new file mode 100755 index 0000000..a3ce15b --- /dev/null +++ b/pypers/pep318/working/pep318.html @@ -0,0 +1,1046 @@ +<?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.2.9: http://docutils.sourceforge.net/" /> +<title>Implementing PEP 318 (decorators)</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="implementing-pep-318-decorators"> +<h1 class="title">Implementing PEP 318 (decorators)</h1> +<div class="contents topic" id="contents"> +<p class="topic-title"><a name="contents">Contents</a></p> +<ul class="simple"> +<li><a class="reference" href="#basics" id="id1" name="id1">Basics</a></li> +<li><a class="reference" href="#simple-usage-of-decorators" id="id2" name="id2">Simple usage of decorators</a></li> +<li><a class="reference" href="#adding-a-bit-of-magic" id="id3" name="id3">Adding a bit of magic</a></li> +<li><a class="reference" href="#adding-more-magic" id="id4" name="id4">Adding more magic</a></li> +<li><a class="reference" href="#defining-method-decorators" id="id5" name="id5">Defining method decorators</a></li> +<li><a class="reference" href="#tracing-methods" id="id6" name="id6">Tracing methods</a></li> +<li><a class="reference" href="#composition-of-decorators" id="id7" name="id7">Composition of decorators</a></li> +<li><a class="reference" href="#class-decorators" id="id8" name="id8">Class decorators</a></li> +<li><a class="reference" href="#module-decorators" id="id9" name="id9">Module decorators</a></li> +<li><a class="reference" href="#introspection-features" id="id10" name="id10">Introspection features</a></li> +<li><a class="reference" href="#the-implementation" id="id11" name="id11">The implementation</a></li> +</ul> +</div> +<p>Having plenty of free time in these days, I have finished an old +project of mine, the implementation of PEP 318 in pure Python.</p> +<p>Here is the rationale:</p> +<ul class="simple"> +<li>some kind of decorator syntax is scheduled to go in Python 2.4, +therefore it is interesting to play with the concept;</li> +<li>it is nice to play with decorators now, without having to +wait for one year or so;</li> +<li>it is much easier to experiment with the pure Python implementation;</li> +<li>the implementation can be seen as an exercise on modern Python +programming and may be valuable to people wanting to study the most +advanced new constructs in Python 2.2 (descriptors, metaclasses, +cooperative methods, etc.)</li> +</ul> +<div class="section" id="basics"> +<h1><a class="toc-backref" href="#id1" name="basics">Basics</a></h1> +<p>As people in this list most probably know, PEP 318 has the goal +of providing a nice syntactic sugar for expressions like</p> +<blockquote> +<pre class="literal-block"> +def identity(x): + return x +identity=staticmethod(identity) +</pre> +</blockquote> +<p>or</p> +<blockquote> +<pre class="literal-block"> +def nameof(cls): + return cls.__name__ +nameof=classmethod(nameof) +</pre> +</blockquote> +<p>which are pretty verbose. It is clear that having new syntax (as +for instance the proposed square bracket notation)</p> +<blockquote> +<pre class="literal-block"> +def identity(x)[staticmethod]: + return x + +def nameof(cls)[classmethod]: + return cls.__name__ +</pre> +</blockquote> +<p>involves changing the grammar and modifying the interpreter at the +C level. However, it is possible to have the same effect without +changing the Python grammar. The idea is to use magic docstrings +like this:</p> +<blockquote> +<pre class="literal-block"> +def identity(x): + "[staticmethod]" + return x + +def nameof(cls): + "[classmethod]" + return cls.__name__ +</pre> +</blockquote> +<p>The implementation is able to recognize such docstrings +and to automagically convert the functions in (method) decorators.</p> +<p>Method decorators are nothing else than a sophisticated kind of wrappers. +<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt> are two examples of already existing +decorators (actually my implementation rewrites them, but let me pass on +this detail). Technically speaking, method decorators are classes +taking a single function as input and producing a descriptor object +as output (properties are not decorators according to this definition, +since they take four functions as input, <tt class="literal"><span class="pre">get,</span> <span class="pre">set,</span> <span class="pre">del_</span></tt> and <tt class="literal"><span class="pre">doc</span></tt>). +<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">Descriptors</a> are objects with a <tt class="literal"><span class="pre">__get__</span></tt> method; they are quite +sophisticated, but fortunately they have been wonderfully explained by +Raymond Hettinger already, so I am allowed to skip this point ;). A knowledge +of descriptors is not needed in order to use the <tt class="literal"><span class="pre">decorator</span></tt> module; +however it is welcomed for advanced users wanting to implement +custom decorators.</p> +</div> +<div class="section" id="simple-usage-of-decorators"> +<h1><a class="toc-backref" href="#id2" name="simple-usage-of-decorators">Simple usage of decorators</a></h1> +<p>Before talking about the implementation details, I will show +how the <tt class="literal"><span class="pre">decorators</span></tt> module works in practice. The simplest and +safest usage is by means of the functions <tt class="literal"><span class="pre">decorators.decorate</span></tt> +and <tt class="literal"><span class="pre">decorators.decorated</span></tt>:</p> +<ol class="arabic simple"> +<li><tt class="literal"><span class="pre">decorators.decorated(obj)</span></tt> takes an object and checks its docstring; +if a magic docstring is found, it returns a decorated version of the +object, otherwise it returns <tt class="literal"><span class="pre">None</span></tt>;</li> +<li><tt class="literal"><span class="pre">decorators.decorate(obj)</span></tt> takes a dictionary or an object with a +<tt class="literal"><span class="pre">.__dict__</span></tt> attribute and returns <tt class="literal"><span class="pre">None</span></tt>. It works by +invoking <tt class="literal"><span class="pre">decorators.decorated</span></tt> on the elements of the dictionary +and by modifying them if needed.</li> +</ol> +<p>Here is an example:</p> +<blockquote> +<pre class="literal-block"> +#<example1.py> + +"How to use ``decorators.decorate`` and ``decorators.decorated``" + +import decorators + +def do_nothing(self): + "No magic docstring here" + +def identity(x): + "[staticmethod]" # magic docstring here + return x + +def nameof(cls): + "[classmethod]" # magic docstring here too + return cls.__name__ + +dic={'nameof': nameof, 'do_nothing': do_nothing} +decorators.decorate(dic) # converts nameof -> classmethod + +C=type('C',(),dic) # creates the class with the modified dictionary +C.identity=decorators.decorated(identity) # converts identity -> staticmethod +c=C() # creates the instance + +#</example1.py> +</pre> +</blockquote> +<p>and here is the testing:</p> +<pre class="doctest-block"> +>>> from example1 import C,c +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert C.nameof() == 'C' +>>> assert c.identity(1) == 1 +>>> assert c.nameof() == 'C' +</pre> +<p>One can even pass the <tt class="literal"><span class="pre">globals()</span></tt> dictionary since objects without +a magic docstring are simply ignored. Therefore the previous example +can be rewritten as</p> +<blockquote> +<pre class="literal-block"> +#<example2.py> + +import decorators + +def do_nothing(self): + "No magic docstring here" + +def identity(x): + "[staticmethod]" + return x + +def nameof(cls): + "[classmethod]" + return cls.__name__ + +decorators.decorate(globals()) # decorates the functions + +class C(object): + identity=identity + nameof=nameof + do_nothing=do_nothing + +c=C() + +#</example2.py> +</pre> +</blockquote> +<p>Here is the testing:</p> +<pre class="doctest-block"> +>>> from example2 import c,C +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert C.nameof() == 'C' +>>> assert c.identity(1) == 1 +>>> assert c.nameof() == 'C' +</pre> +<p>Notice that the call to <tt class="literal"><span class="pre">decorators.decorate(globals())</span></tt> must be done +<em>after</em> the function definitions, otherwise the functions would +not converted, since they were not in the global dictionary at the +time of the call. Moreover, one should not try to pass the <tt class="literal"><span class="pre">locals()</span></tt> +dictionary, since it will not work when <tt class="literal"><span class="pre">locals()</span> <span class="pre">!=</span> <span class="pre">globals()</span></tt>.</p> +<p>Alternatively, one can just decorate the class:</p> +<blockquote> +<pre class="literal-block"> +#<example3.py> + +import decorators + +def identity(x): + "[staticmethod]" + return x + +def nameof(cls): + "[classmethod]" + return cls.__name__ + +class C: + identity=identity + nameof=nameof + +decorators.decorate(C) + +c=C() + +# testing +assert C.identity(1) == 1 +assert C.nameof() == 'C' +assert c.identity(1) == 1 +assert c.nameof() == 'C' + +#</example3.py> +</pre> +</blockquote> +<p>This example also shows that decorators work both for <a class="reference" href="http://www.python.org/2.3/descrintro.html">new style classes +and old style classes</a>:</p> +<pre class="doctest-block"> +>>> from example3 import * +>>> type(C) # C is an old style class +<type 'classobj'> +</pre> +<p>At this point the difference between <tt class="literal"><span class="pre">decorators.decorate</span></tt> and +<tt class="literal"><span class="pre">decorators.decorated</span></tt> should be pointed out. The first syntax +modifies the class dictionary, whereas the second creates a new +class with the same name of the first one:</p> +<pre class="doctest-block"> +>>> class D: +... identity=identity +>>> decorators.decorated(D) +<class 'D'> +>>> D.identity(1) # this is the old D +Traceback (most recent call last): + ... +TypeError: unbound method identity() must be called with D instance as first argument (got int instance instead) +</pre> +<p>Therefore one has to redefine to old class in order the statement to +have effect:</p> +<pre class="doctest-block"> +>>> D=decorators.decorated(D) +>>> D.identity(1) # this is the new D +1 +</pre> +</div> +<div class="section" id="adding-a-bit-of-magic"> +<h1><a class="toc-backref" href="#id3" name="adding-a-bit-of-magic">Adding a bit of magic</a></h1> +<p>It would be nice to have classes with the ability of automatically +converting their methods to method decorators according to the docstrings. +This sounds a bit of magic, but actually can be done very simply by adding +to the class a docstring starting with "[Decorated]". +Here is an example:</p> +<blockquote> +<pre class="literal-block"> +#<example.py> + +import decorators + +class C: # works for old style classes too + "[Decorated]" + def identity(x): + "[staticmethod]" + return x + +decorators.decorate(globals()) + +c=C() + +# test +assert C.identity(1) == 1 +assert c.identity(1) == 1 + +#</example.py> +</pre> +</blockquote> +<p>Under the hood, the magic docstring "[Decorated]" creates an instance of the +<tt class="literal"><span class="pre">decorators.Decorated</span></tt> metaclass and replace the original class <tt class="literal"><span class="pre">C</span></tt> +in the global namespace with the new class <tt class="literal"><span class="pre">C</span></tt>; incidentally, +this converts <tt class="literal"><span class="pre">C</span></tt> in a new style class:</p> +<pre class="doctest-block"> +>>> from example import C +>>> type(C) +<class 'decorators.Decorated'> +</pre> +<p>On the other hand using <tt class="literal"><span class="pre">decorators.decorate(C)</span></tt> would decorate <tt class="literal"><span class="pre">C</span></tt>, but +without re-creating it as an instance of "[Decorated]". One can also +forget the docstring in subclasses of decorated classes:</p> +<pre class="doctest-block"> +>>> class D(C): +... def nameof(cls): +... "[classmethod]" +... return cls.__name__ +>>> print D.nameof() +D +</pre> +<p>The trick works for classes containing inner classes, too:</p> +<blockquote> +<pre class="literal-block"> +#<example4.py> + +import decorators + +class C: + "[Decorated]" # required docstring + def identity(x): + "[staticmethod]" + return x + class Inner: + "[Decorated]" # required docstring + def nameof(cls): + "[classmethod]" + return cls.__name__ + +decorators.decorate(globals()) + +assert C.identity(1) == 1 +assert C.Inner.nameof() == 'Inner' + +#</example4.py> +</pre> +</blockquote> +</div> +<div class="section" id="adding-more-magic"> +<h1><a class="toc-backref" href="#id4" name="adding-more-magic">Adding more magic</a></h1> +<p>There is a neat trick to simplify the usage of decorators: decorating the +<tt class="literal"><span class="pre">object</span></tt> class. Then all methods in all new style classes of your module +will be checked for magic docstrings and automagically decorated if needed. +This can be done by simply writing</p> +<blockquote> +<pre class="literal-block"> +import decorators +object=decorators.decorated(object) +</pre> +</blockquote> +<p>on top of your module. Here is an example:</p> +<blockquote> +<pre class="literal-block"> +#<example.py> + +import inspect, decorators +object=decorators.decorated(object) + +def identity(x): + "[staticmethod] defined outside a class" + return x + +assert inspect.isfunction(identity) # not yet a decorator + +class C1(object): + def nameof(cls): + "[classmethod] defined inside a class" + return cls.__name__ + identity=identity # automagically converted to a decorator + +c1=C1() # C1 instance + +# testing + +assert C1.identity(1) == 1 +assert C1.nameof() == 'C1' +assert c1.identity(1) == 1 +assert c1.nameof() == 'C1' + +#</example.py> +</pre> +</blockquote> +<p>Notice that adding <tt class="literal"><span class="pre">identity</span></tt> after the class creation with the syntax +<tt class="literal"><span class="pre">C.identity=identity</span></tt> would not work. Moreover, the magic only works +for new style classes, since the implementation operates +by enhancing the <tt class="literal"><span class="pre">object</span></tt> class in the calling module. +The enhancement includes providing a new default printing representation +for instances:</p> +<pre class="doctest-block"> +>>> from example import c1 +>>> print c1 +<instance of C1> +</pre> +<p>The <tt class="literal"><span class="pre">decorated(object)</span></tt> trick (and the "[Decorated]" syntax too) +is not 100% safe, because of possible metaclass conflicts:</p> +<pre class="doctest-block"> +>>> import decorators; object=decorators.decorated(object) +>>> class M(type): pass +... +>>> class D(object): +... __metaclass__=M +Traceback (most recent call last): + ... +TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +</pre> +<p>The decorators module imports the <tt class="literal"><span class="pre">makecls</span></tt> function from my +<tt class="literal"><span class="pre">noconflict</span></tt> module just to avoid this kind of problems:</p> +<pre class="doctest-block"> +>>> class D(object): +... __metaclass__=decorators.makecls(M) +>>> type(D) +<class 'noconflict._DecoratedM'> +</pre> +<p>It is possible to go even deeper in black magic, and to decorate all +the new style classes in <em>all</em> modules, by decorating <tt class="literal"><span class="pre">__builtin__.object</span></tt>:</p> +<blockquote> +<pre class="literal-block"> +import decorators,__builtin__ +__builtin.object=decorators.decorated(object) +</pre> +</blockquote> +<p>Still, redefining <tt class="literal"><span class="pre">__builtin__object</span></tt> is not recommended since it +may induce metaclass conflicts in other modules using metaclasses. +It will work only if you import modules not using metaclasses, or +modules using metaclasses safely, i.e. modules taking care of +possible conflicts by using the <tt class="literal"><span class="pre">makecls</span></tt> function or an equivalent one.</p> +</div> +<div class="section" id="defining-method-decorators"> +<h1><a class="toc-backref" href="#id5" name="defining-method-decorators">Defining method decorators</a></h1> +<p>The decorators module contains two predefinite method decorators, +<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, which emulate the built-ins +with the same names. However, it is possible to write your own +custom decorators. The <tt class="literal"><span class="pre">decorators</span></tt> module provides a +<tt class="literal"><span class="pre">MethodDecorator</span></tt> class which is here exactly for that purpose.</p> +<p>Custom decorators are expected to be implemented by subclassing +<tt class="literal"><span class="pre">MethodDecorator</span></tt> and by overriding its <tt class="literal"><span class="pre">get</span></tt> method, which +automagically induces a <tt class="literal"><span class="pre">__get__</span></tt> method, turning the class +in a descriptor. This +machinery is needed since <tt class="literal"><span class="pre">__get__</span></tt> cannot be made cooperative +using the standard <tt class="literal"><span class="pre">super</span></tt> mechanism (there would be a confusion +between <tt class="literal"><span class="pre">super.__get__</span></tt> and the decorator <tt class="literal"><span class="pre">__get__</span></tt>). This is a bit +tricky, but the causal programmer is not expected to write custom +decorators, and actually I don't want to make the access to decorators +<em>too</em> easy</p> +<p>For instance, let me show the implementation of a <tt class="literal"><span class="pre">chattymethod</span></tt> +that prints a message when it is called:</p> +<blockquote> +<pre class="literal-block"> +#<chatty.py> + +from decorators import * +object=decorated(object) + +class chattymethod(MethodDecorator): + logfile=file('file1.log','w') + def get(self,obj,cls=None): # same signature as __get__ + print >> self.logfile,'calling %s from %s' % (self,obj or cls) + return super(chattymethod,self).get(obj,cls) + +#</chatty.py> +</pre> +</blockquote> +<p>Notice the usage of the <tt class="literal"><span class="pre">super().get</span></tt> trick. This guarantees that +<tt class="literal"><span class="pre">chattymethod</span></tt> will play well with other decorators (i.e. it +can be nicely composed via multiple inheritance)</p> +<p>Here is an example of usage</p> +<blockquote> +<pre class="literal-block"> +#<chatty.py> + +class C(object): + def f(): + "[chattymethod,staticmethod]" + +c=C() + +c.f() +C.f() + +#</chatty.py> +</pre> +</blockquote> +<p>The content of the logfile is then</p> +<blockquote> +<pre class="literal-block"> +calling <chattymethodstaticmethod:f> from <instance of C> +calling <chattymethodstaticmethod:f> from <class C[Decorated]> +</pre> +</blockquote> +<p>From this output we see how the "[chattymethod,staticmethod]" +magic docstring is responsible for the creation of a new decorator class +<tt class="literal"><span class="pre">chattymethodstaticmethod</span></tt>, obtained via multiple inheritance from +<tt class="literal"><span class="pre">chattymethod</span></tt> and <tt class="literal"><span class="pre">staticmethod</span></tt> respectively.</p> +<p>One can easily change the logfile, if need there is</p> +<blockquote> +<pre class="literal-block"> +#<chatty.py> + +chattymethod.logfile=file('file2.log','w') + +def g(): + "[chattymethod,staticmethod]" + +C.g=decorated(g) +C.g # message written in file2.log +C.f # message written in file2.log + +#</chatty.py> +</pre> +</blockquote> +<p>Now <tt class="literal"><span class="pre">file2.log</span></tt> will contains the messages</p> +<blockquote> +<pre class="literal-block"> +calling <chattymethodstaticmethod:g> from <class C[Decorated]> +calling <chattymethodstaticmethod:f> from <class C[Decorated]> +</pre> +</blockquote> +<p>This approach has the drawback that chattymethods created before changing +the logfile will also print to the new logfile, if invoked after the +change. This can be avoided by converting <tt class="literal"><span class="pre">logfile</span></tt> from a class variable +to an instance variable in the <tt class="literal"><span class="pre">__init__</span></tt> method:</p> +<blockquote> +<pre class="literal-block"> +#<chatty2.py> + +import sys +from chatty import * + +class chattymethod2(chattymethod): + def __init__(self,func): + super(chattymethod2,self).__init__(func) + self.logfile=self.logfile # class variable becomes instance variable + +class C(object): + chattymethod2.logfile=sys.stdout + f=chattymethod2(lambda self:None) + chattymethod2.logfile=file('file3.log','w') + g=chattymethod2(lambda self:None) + +c=C() + +#</chatty2.py> +</pre> +</blockquote> +<p>Notice that the <tt class="literal"><span class="pre">__init__</span></tt> method should have the signature +<tt class="literal"><span class="pre">__init__(self,func)</span></tt>, where <tt class="literal"><span class="pre">func</span></tt> is the function to be +converted in the decorator object. Here is the testing:</p> +<pre class="doctest-block"> +>>> from chatty2 import c +>>> c.f() +calling <chattymethod2:<lambda>> from <instance of C> +>>> c.g() # message written in file3.log +>>> c.f() # message written in stdout, not in file3.log! +calling <chattymethod2:<lambda>> from <instance of C> +</pre> +</div> +<div class="section" id="tracing-methods"> +<h1><a class="toc-backref" href="#id6" name="tracing-methods">Tracing methods</a></h1> +<p>In order to show a non-trivial example, I will show how +decorators can be used to implement traced methods. +Here is the code (notice: the lazy reader can safely skip the +implementation details and go directly to the usage section ;)</p> +<blockquote> +<pre class="literal-block"> +#<tracedmethod.py> + +import sys,decorators + +class tracedmethod(decorators.MethodDecorator): + "Descriptor class, converts a method in a traced method" + indent=0; output=sys.stdout # defaults + def __init__(self,func): + super(tracedmethod,self).__init__(func) + self.funcname=self.func.__name__ + def get(self,obj,cls): + if obj is None: name=self.inside.__name__ # called from class + else: name='<%s>' % self.inside.__name__ # from instance + methodwrapper=super(tracedmethod,self).get(obj,cls) + def _(*args,**kw): + i=' '*self.indent # default indentation + self.__class__.indent+=4 # increases indentation + self.output.write("%sCalling '%s.%s' with arguments " % + (i,name,self.funcname)) + self.output.write("%s ...\n" % (str(args)+str(kw))) + res=methodwrapper(*args,**kw) + self.output.write("%s'%s.%s' called with result: %s\n" + % (i,name,self.funcname,res)) + self.__class__.indent-=4 # restores default indentation + return res + return _ + +#</tracedmethod.py> +</pre> +</blockquote> +<p>As soon as the <tt class="literal"><span class="pre">tracedmethod</span></tt> module is loaded, the <tt class="literal"><span class="pre">tracedmethod</span></tt> class +is added to the list of know decorators, so one should use the "[tracedmethod]" +docstring instead and not "[tracedmethod.tracedmethod]".</p> +<p><tt class="literal"><span class="pre">tracedmethod</span></tt> which is quite useful during +debugging. Here is an example of usage, in tracing the internal working +of a recursive function:</p> +<blockquote> +<pre class="literal-block"> +#<example4.py> + +import decorators,tracedmethod + +class C(object): + def fact(self,n): + "[tracedmethod]" + if n==0: return 1 + else: return n*self.fact(n-1) + +decorators.decorate(C) + +c=C() + +#</example4.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from example4 import c +>>> c.fact(2) +Calling '<C>.fact' with arguments (2,){} ... + Calling '<C>.fact' with arguments (1,){} ... + Calling '<C>.fact' with arguments (0,){} ... + '<C>.fact' called with result: 1 + '<C>.fact' called with result: 1 +'<C>.fact' called with result: 2 +2 +</pre> +<p>An alternative spelling, not involving magic docstrings, nor the +decorators module, is the following:</p> +<blockquote> +<pre class="literal-block"> +#<example5.py> + +from tracedmethod import tracedmethod + +class C(object): + def fact(self,n): + if n==0: return 1 + else: return n*self.fact(n-1) + fact=tracedmethod(fact) + +c=C() + +#</example5.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from example5 import c +>>> c.fact(2) +Calling '<?>.fact' with arguments (2,){} ... + Calling '<?>.fact' with arguments (1,){} ... + Calling '<?>.fact' with arguments (0,){} ... + '<?>.fact' called with result: 1 + '<?>.fact' called with result: 1 +'<?>.fact' called with result: 2 +2 +</pre> +<p>Notice that in this second syntax <tt class="literal"><span class="pre">fact</span></tt> does not know where it +is defined; however the containing class can be explicitly set:</p> +<blockquote> +<tt class="literal"><span class="pre">C.__dict__['fact'].inside=C</span></tt></blockquote> +<p>The <tt class="literal"><span class="pre">inside</span></tt> attribute is automagically set if the docstring syntax +is used.</p> +<p>Here is how to trace cooperative methods in complicate hierarchies +(which is useful for debugging):</p> +<blockquote> +<pre class="literal-block"> +#<tracing.py> + +import decorators,tracedmethod +object=decorators.decorated(object) + +class B(object): + def __init__(self): + "[tracedmethod]" + super(B,self).__init__() + +class D(object): + def __init__(self): + "[tracedmethod]" + super(D,self).__init__() + +class E(B,D): + def __init__(self): + "[tracedmethod]" + super(E,self).__init__() + + #</tracing.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from tracing import E +>>> e=E() +Calling '<E>.__init__' with arguments (){} ... + Calling '<B>.__init__' with arguments (){} ... + Calling '<D>.__init__' with arguments (){} ... + '<D>.__init__' called with result: None + '<B>.__init__' called with result: None +'<E>.__init__' called with result: None +</pre> +<p>In this example decorating <tt class="literal"><span class="pre">object</span></tt> is the easiest way to go.</p> +</div> +<div class="section" id="composition-of-decorators"> +<h1><a class="toc-backref" href="#id7" name="composition-of-decorators">Composition of decorators</a></h1> +<p>Decorators can be composed: for instance, you can trace a +classmethod as in this example:</p> +<blockquote> +<pre class="literal-block"> +#<example6.py> + +import tracedmethod +from decorators import decorated +object=decorated(object) + +class C(object): + def fact(cls,n): + "[tracedmethod,classmethod]" + if n==0: return 1 + else: return n*cls.fact(n-1) + +#</example6.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from example6 import C +>>> C.fact(2) +Calling 'C.fact' with arguments (2,){} ... + Calling 'C.fact' with arguments (1,){} ... + Calling 'C.fact' with arguments (0,){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 +</pre> +<p>Under the hood the syntax</p> +<blockquote> +<pre class="literal-block"> +[tracedmethod,classmethod] +</pre> +</blockquote> +<p>generates a <tt class="literal"><span class="pre">tracedmethodclassmethod</span></tt> class obtained via +multiple inheritance:</p> +<pre class="doctest-block"> +>>> for c in type(C.__dict__['fact']).__mro__: print c +... +<class 'noconflict.tracedmethodclassmethod'> +<class 'tracedmethod.tracedmethod'> +<class 'decorators.classmethod'> +<class 'decorators.MethodDecorator'> +<class 'decorators.Decorator'> +<type 'object'> +</pre> +<p>In this case the order does not matter and using the docstring +"[classmethod,tracedmethod]" would work too, but +in general one must pay attention to precedence issues. +For instance the following will not work:</p> +<pre class="doctest-block"> +>>> class C(object): +... def fact(n): +... "[staticmethod,tracedmethod]" +... if n==0: return 1 +... else: return n*C.fact(n-1) +>>> C.fact(2) +Traceback (most recent call last): + ... +AttributeError: 'function' object has no attribute 'im_func' +</pre> +<p>The problem here is that <tt class="literal"><span class="pre">staticmethod.get</span></tt> invokes <tt class="literal"><span class="pre">tracedmethod.get</span></tt> +which returns a function and not a method-wrapper with an <tt class="literal"><span class="pre">im_func</span></tt> method. +On the other hand, composing the decorators in the other order +"[tracedmethod,staticmethod]" will work just fine.</p> +</div> +<div class="section" id="class-decorators"> +<h1><a class="toc-backref" href="#id8" name="class-decorators">Class decorators</a></h1> +<p>PEP 318 proposes to decorate methods by using descriptors; it is +quite natural to extend this idea and to decorate classes by using +class decorators implemented as metaclasses. We already saw a +class decorator at work, the metaclass <tt class="literal"><span class="pre">Decorated</span></tt>, giving +to its instances the ability to interpret magic docstrings, +and converting functions in method decorators.</p> +<p>To define a custom class decorator is easy: one defines a custom metaclasses +as usual, with the only difference from deriving by <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead +of deriving from <tt class="literal"><span class="pre">type</span></tt>. To understand how this works in practice, let me +show how to add logging capabilities to a given class. The first +step is to define a suitable class decorator, such as the following:</p> +<blockquote> +<pre class="literal-block"> +#<logged.py> + +from decorators import * + +class Logged(ClassDecorator): + def __init__(cls,name,bases,dic): + super(Logged,cls).__init__(name,bases,dic) + print "%s created" % cls + +#</logged.py> +</pre> +</blockquote> +<p><tt class="literal"><span class="pre">Logged</span></tt> is derived by the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>, +which provides a certain amount of magic under the hood (in particular +its printing representation and its calling syntax are redefined by its +metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>). Logging capabilities can be added to a class +by simply using the magic docstring syntax:</p> +<pre class="doctest-block"> +>>> from logged import * +>>> object=decorators.decorated(object) +>>> class D(object): +... "[Logged]" +<class D[_DecoratedLogged]> created +</pre> +<p>Notice that <tt class="literal"><span class="pre">D</span></tt> inherits the <tt class="literal"><span class="pre">Decorated</span></tt> metaclass from <tt class="literal"><span class="pre">object</span></tt> +and the <tt class="literal"><span class="pre">Logged</span></tt> metaclass from the docstring; the conflict is +automagically avoid by the miracolous creation of a <tt class="literal"><span class="pre">_DecoratedLogged</span></tt> +metaclass, obtained via multiple inheritance from <tt class="literal"><span class="pre">Decorated</span></tt> and +<tt class="literal"><span class="pre">Logged</span></tt>. All the magic is performed in the <tt class="literal"><span class="pre">noconflict</span></tt> module, +discussed in a <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a> recipe of mine.</p> +<p>Notice that the printing representation of <tt class="literal"><span class="pre">D</span></tt> involves the name +of <tt class="literal"><span class="pre">D</span></tt> preceded by the name of its metaclass, which in this case +is <tt class="literal"><span class="pre">_DecoratedLogged</span></tt></p> +<p>Each time an instance of <tt class="literal"><span class="pre">Logged</span></tt> is created, a similar message is printed:</p> +<pre class="doctest-block"> +>>> class E(D): +... pass +<class E[_DecoratedLogged]> created +</pre> +<p>Notice that <tt class="literal"><span class="pre">E</span></tt> does not have any magic docstring</p> +<pre class="doctest-block"> +>>> E.__doc__ # no docstring +</pre> +<p>but still it inherits its magic from <tt class="literal"><span class="pre">D</span></tt>.</p> +<p>The <tt class="literal"><span class="pre">decorators</span></tt> module provides the already saw class decorator +<tt class="literal"><span class="pre">Decorated</span></tt>, which converts methods in method decorators.</p> +<p>Another example is</p> +<blockquote> +<pre class="literal-block"> +#<Traced.py> + +import tracedmethod +from decorators import * +from types import FunctionType +object=decorated(object) + +class Traced(ClassDecorator): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType): + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) + +class C(object): + """[Traced] The class decorator adds the magic docstring + '[tracedmethod]' to f1 and f2, which are then converted + to method decorator objects.""" + def f1(self): pass + def f2(self): pass + +c=C() + +#</Traced.py> +</pre> +</blockquote> +<p>Here is an example of usage:</p> +<pre class="doctest-block"> +>>> from Traced import C; c=C() +>>> c.f1() +Calling '<C>.f1' with arguments (){} ... +'<C>.f1' called with result: None +>>> c.f2() +Calling '<C>.f2' with arguments (){} ... +'<C>.f2' called with result: None +</pre> +</div> +<div class="section" id="module-decorators"> +<h1><a class="toc-backref" href="#id9" name="module-decorators">Module decorators</a></h1> +<p>Finally, one can decorate entire modules through the concept of +<em>module decorators</em>. Module decorators have the ability of decorating +modules by changing their dictionary. Custom module decorators +should be derived from the class <tt class="literal"><span class="pre">decorators.ModuleDecorator</span></tt>, by +cooperatively overriding its <tt class="literal"><span class="pre">__init__(self,mod)</span></tt> method. Writing +a module decorator is a bit tricky, but I do expect only +expert programmers to play this kind of game. +For instance, suppose one wants to trace all the functions in a module, +unless they have the docstring "-untraced-" . This can be done with a +suitable module decorator which modifies the module dictionary. +Here is an example</p> +<blockquote> +<pre class="literal-block"> +#<tracefunctions.py> + +from decorators import * + +class TraceFunctions(ModuleDecorator): + def __init__(self,mod): + super(TraceFunctions,self).__init__(mod) + for name,func in self.__dict__.items(): + if inspect.isfunction(func): + doc=func.__doc__ or '' + if doc.startswith('-untraced-'): + pass # do nothing + else: + def tracedfunc(func=func): # default argument trick + def _(*args,**kw): + print 'called',func.__name__ + return func(*args,**kw) + return _ + setattr(self,name,tracedfunc()) + +#</tracefunctions.py> +</pre> +</blockquote> +<p>Let me test that the module decorator does its job. Consider the module</p> +<blockquote> +<pre class="literal-block"> +#<mod.py> + +#"[TraceFunctions]" + +def f1(): pass + +def f2(): pass + +def f3(): "-untraced-" + +#</mod.py> +</pre> +</blockquote> +<p>By importing this module, only the functions <tt class="literal"><span class="pre">f1</span></tt> and <tt class="literal"><span class="pre">f2</span></tt> should +be traced. This is indeed the case:</p> +<pre class="doctest-block"> +>>> from tracefunctions import TraceFunctions +>>> mod=TraceFunctions('mod',{}) +>>> mod.f1() +called f1 +>>> mod.f2() +called f2 +>>> mod.f3() # does nothing, correct +</pre> +<blockquote> +<pre class="literal-block"> +#<module.py> + +"Magically decorated module" + +import decorators,sys + +thismodule=sys.modules[__name__] + +class MyClass: "[Decorated]" + +newmod=decorators.decorated(thismodule) + +#</module.py> +</pre> +</blockquote> +<pre class="doctest-block"> +>>> from module import * +>>> assert isinstance(newmod.MyClass, decorators.Decorated) +>>> assert isinstance(newmod,decorators.DecoratedModule) +</pre> +</div> +<div class="section" id="introspection-features"> +<h1><a class="toc-backref" href="#id10" name="introspection-features">Introspection features</a></h1> +<p>The module provides three utilities functions to retrieve the list of +recognized decorators: <tt class="literal"><span class="pre">decorators.methodlike()</span></tt>, <tt class="literal"><span class="pre">decorators.classlike()</span></tt> +and <tt class="literal"><span class="pre">decorators.modulelike()</span></tt>:</p> +<pre class="doctest-block"> +>>> for d in decorators.methodlike(): print d +... +MethodDecorator +tracedmethodclassmethod +tracedmethod +classmethod +chattymethodstaticmethod +staticmethodtracedmethod +staticmethod +chattymethod2 +chattymethod +>>> decorators.classlike() +['Traced', 'Logged', '_DecoratedLogged', 'ClassDecorator', '_DecoratedM', 'Decorated', '_DecoratedTraced'] +>>> decorators.modulelike() +['ModuleDecorator', 'DecoratedModule', 'TraceFunctions'] +</pre> +</div> +<div class="section" id="the-implementation"> +<h1><a class="toc-backref" href="#id11" name="the-implementation">The implementation</a></h1> +<p>This part can be safely skipped, unless you are a <em>really</em> curious and +you want to know how the implementation works.</p> +<p>The module is rather short (~250 lines) but far from being trivial, +since it is based on extensive usage of metaclass wizardry.</p> +<p>The main class-metaclass hierarchy is represented in figure 1, where +boxes denote classes and ovals denote metaclasses; instantiation is +denoted by dashed lines whereas inheritance is denoted by continuous +lines.</p> +<div class="figure"> +<p><img alt="decorators.png" src="decorators.png" /></p> +</div> +<p>The implementation is relatively smart. Suppose for instance +that a programmer wrote something like</p> +<pre class="doctest-block"> +>>> from decorators import * +>>> object=decorated(object) +>>> class C(object): +... def f(): +... "[staticmethod,MethodDecorator]" +</pre> +<p>to document the fact that <tt class="literal"><span class="pre">staticmethod</span></tt> is a method decorator +and not the built-in <tt class="literal"><span class="pre">staticmethod</span></tt>. Since <tt class="literal"><span class="pre">staticmethod</span></tt> already +derives from <tt class="literal"><span class="pre">MethodDecorator</span></tt>, it is redundant to repeat +<tt class="literal"><span class="pre">MethodDecorator</span></tt>. Apparently, there is the risk of generating +an useless <tt class="literal"><span class="pre">staticmethodMethodDecorator</span></tt> class, doing the same +as <tt class="literal"><span class="pre">staticmethod</span></tt>. Fortunately, the implementation is able +to recognize redundant class. In this case, a class called +<tt class="literal"><span class="pre">MethodDecoratorDecorator</span></tt> is <em>not</em> created; <tt class="literal"><span class="pre">staticmethod</span></tt> +is used instead:</p> +<pre class="doctest-block"> +>>> print type(C.__dict__['f']) +<class 'decorators.staticmethod'> +</pre> +<p>The current implementation does not make any attempt of optimization and +there may be alternative implementations faster or more memory efficient. +At this experimental level I didn't care to explore about performances +issues. They does not probably matter unless one has to decorate +thousands or millions of functions and classes.</p> +<p>Finally, a word about bugs. The <tt class="literal"><span class="pre">decorators</span></tt> module is fairly sophisticated, +therefore whereas I can guarantee that it passes my test suite (which is extracted +from the documentation that you are reading), I cannot guarantee that it +is correct. If somebody finds a bug or an unexpected behavior, please let me +know and I will try to fix it.</p> +<!-- References: --> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="pep318.txt">View document source</a>. +Generated on: 2003-09-09 16:26 UTC. +Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. +</div> +</body> +</html> diff --git a/pypers/pep318/working/pep318.txt b/pypers/pep318/working/pep318.txt new file mode 100755 index 0000000..03667bf --- /dev/null +++ b/pypers/pep318/working/pep318.txt @@ -0,0 +1,1048 @@ +Implementing PEP 318 (decorators) +====================================================================== + +.. contents:: + +Having plenty of free time in these days, I have finished an old +project of mine, the implementation of PEP 318 in pure Python. + +Here is the rationale: + +* some kind of decorator syntax is scheduled to go in Python 2.4, + therefore it is interesting to play with the concept; + +* it is nice to play with decorators now, without having to + wait for one year or so; + +* it is much easier to experiment with the pure Python implementation; + +* the implementation can be seen as an exercise on modern Python + programming and may be valuable to people wanting to study the most + advanced new constructs in Python 2.2 (descriptors, metaclasses, + cooperative methods, etc.) + +Basics +-------------------------------------------------------------------- + +As people in this list most probably know, PEP 318 has the goal +of providing a nice syntactic sugar for expressions like + + :: + + def identity(x): + return x + identity=staticmethod(identity) + +or + + :: + + def nameof(cls): + return cls.__name__ + nameof=classmethod(nameof) + +which are pretty verbose. It is clear that having new syntax (as +for instance the proposed square bracket notation) + + :: + + def identity(x)[staticmethod]: + return x + + def nameof(cls)[classmethod]: + return cls.__name__ + +involves changing the grammar and modifying the interpreter at the +C level. However, it is possible to have the same effect without +changing the Python grammar. The idea is to use magic docstrings +like this: + + :: + + def identity(x): + "[staticmethod]" + return x + + def nameof(cls): + "[classmethod]" + return cls.__name__ + +The implementation is able to recognize such docstrings +and to automagically convert the functions in (method) decorators. + +Method decorators are nothing else than a sophisticated kind of wrappers. +``staticmethod`` and ``classmethod`` are two examples of already existing +decorators (actually my implementation rewrites them, but let me pass on +this detail). Technically speaking, method decorators are classes +taking a single function as input and producing a descriptor object +as output (properties are not decorators according to this definition, +since they take four functions as input, ``get, set, del_`` and ``doc``). +Descriptors_ are objects with a ``__get__`` method; they are quite +sophisticated, but fortunately they have been wonderfully explained by +Raymond Hettinger already, so I am allowed to skip this point ;). A knowledge +of descriptors is not needed in order to use the ``decorator`` module; +however it is welcomed for advanced users wanting to implement +custom decorators. + +Simple usage of decorators +------------------------------------------------------------------------ + +Before talking about the implementation details, I will show +how the ``decorators`` module works in practice. The simplest and +safest usage is by means of the functions ``decorators.decorate`` +and ``decorators.decorated``: + +1. ``decorators.decorated(obj)`` takes an object and checks its docstring; + if a magic docstring is found, it returns a decorated version of the + object, otherwise it returns ``None``; + +2. ``decorators.decorate(obj)`` takes a dictionary or an object with a + ``.__dict__`` attribute and returns ``None``. It works by + invoking ``decorators.decorated`` on the elements of the dictionary + and by modifying them if needed. + +Here is an example: + + :: + + #<example1.py> + + "How to use ``decorators.decorate`` and ``decorators.decorated``" + + import decorators + + def do_nothing(self): + "No magic docstring here" + + def identity(x): + "[staticmethod]" # magic docstring here + return x + + def nameof(cls): + "[classmethod]" # magic docstring here too + return cls.__name__ + + dic={'nameof': nameof, 'do_nothing': do_nothing} + decorators.decorate(dic) # converts nameof -> classmethod + + C=type('C',(),dic) # creates the class with the modified dictionary + C.identity=decorators.decorated(identity) # converts identity -> staticmethod + c=C() # creates the instance + + #</example1.py> + +and here is the testing: + +>>> from example1 import C,c +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert C.nameof() == 'C' +>>> assert c.identity(1) == 1 +>>> assert c.nameof() == 'C' + +One can even pass the ``globals()`` dictionary since objects without +a magic docstring are simply ignored. Therefore the previous example +can be rewritten as + + :: + + #<example2.py> + + import decorators + + def do_nothing(self): + "No magic docstring here" + + def identity(x): + "[staticmethod]" + return x + + def nameof(cls): + "[classmethod]" + return cls.__name__ + + decorators.decorate(globals()) # decorates the functions + + class C(object): + identity=identity + nameof=nameof + do_nothing=do_nothing + + c=C() + + #</example2.py> + +Here is the testing: + +>>> from example2 import c,C +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert C.nameof() == 'C' +>>> assert c.identity(1) == 1 +>>> assert c.nameof() == 'C' + +Notice that the call to ``decorators.decorate(globals())`` must be done +*after* the function definitions, otherwise the functions would +not converted, since they were not in the global dictionary at the +time of the call. Moreover, one should not try to pass the ``locals()`` +dictionary, since it will not work when ``locals() != globals()``. + +Alternatively, one can just decorate the class: + + :: + + #<example3.py> + + import decorators + + def identity(x): + "[staticmethod]" + return x + + def nameof(cls): + "[classmethod]" + return cls.__name__ + + class C: + identity=identity + nameof=nameof + + decorators.decorate(C) + + c=C() + + # testing + assert C.identity(1) == 1 + assert C.nameof() == 'C' + assert c.identity(1) == 1 + assert c.nameof() == 'C' + + #</example3.py> + +This example also shows that decorators work both for `new style classes +and old style classes`_: + +>>> from example3 import * +>>> type(C) # C is an old style class +<type 'classobj'> + +At this point the difference between ``decorators.decorate`` and +``decorators.decorated`` should be pointed out. The first syntax +modifies the class dictionary, whereas the second creates a new +class with the same name of the first one: + +>>> class D: +... identity=identity +>>> decorators.decorated(D) +<class 'D'> +>>> D.identity(1) # this is the old D +Traceback (most recent call last): + ... +TypeError: unbound method identity() must be called with D instance as first argument (got int instance instead) + +Therefore one has to redefine to old class in order the statement to +have effect: + + +>>> D=decorators.decorated(D) +>>> D.identity(1) # this is the new D +1 + +Adding a bit of magic +---------------------------------------------------------------------- + +It would be nice to have classes with the ability of automatically +converting their methods to method decorators according to the docstrings. +This sounds a bit of magic, but actually can be done very simply by adding +to the class a docstring starting with "[Decorated]". +Here is an example: + + :: + + #<example.py> + + import decorators + + class C: # works for old style classes too + "[Decorated]" + def identity(x): + "[staticmethod]" + return x + + decorators.decorate(globals()) + + c=C() + + # test + assert C.identity(1) == 1 + assert c.identity(1) == 1 + + #</example.py> + +Under the hood, the magic docstring "[Decorated]" creates an instance of the +``decorators.Decorated`` metaclass and replace the original class ``C`` +in the global namespace with the new class ``C``; incidentally, +this converts ``C`` in a new style class: + +>>> from example import C +>>> type(C) +<class 'decorators.Decorated'> + +On the other hand using ``decorators.decorate(C)`` would decorate ``C``, but +without re-creating it as an instance of "[Decorated]". One can also +forget the docstring in subclasses of decorated classes: + +>>> class D(C): +... def nameof(cls): +... "[classmethod]" +... return cls.__name__ +>>> print D.nameof() +D + +The trick works for classes containing inner classes, too: + + :: + + #<example4.py> + + import decorators + + class C: + "[Decorated]" # required docstring + def identity(x): + "[staticmethod]" + return x + class Inner: + "[Decorated]" # required docstring + def nameof(cls): + "[classmethod]" + return cls.__name__ + + decorators.decorate(globals()) + + assert C.identity(1) == 1 + assert C.Inner.nameof() == 'Inner' + + #</example4.py> + + +Adding more magic +---------------------------------------------------------------------------- + +There is a neat trick to simplify the usage of decorators: decorating the +``object`` class. Then all methods in all new style classes of your module +will be checked for magic docstrings and automagically decorated if needed. +This can be done by simply writing + + :: + + import decorators + object=decorators.decorated(object) + +on top of your module. Here is an example: + + :: + + #<example.py> + + import inspect, decorators + object=decorators.decorated(object) + + def identity(x): + "[staticmethod] defined outside a class" + return x + + assert inspect.isfunction(identity) # not yet a decorator + + class C1(object): + def nameof(cls): + "[classmethod] defined inside a class" + return cls.__name__ + identity=identity # automagically converted to a decorator + + c1=C1() # C1 instance + + # testing + + assert C1.identity(1) == 1 + assert C1.nameof() == 'C1' + assert c1.identity(1) == 1 + assert c1.nameof() == 'C1' + + #</example.py> + +Notice that adding ``identity`` after the class creation with the syntax +``C.identity=identity`` would not work. Moreover, the magic only works +for new style classes, since the implementation operates +by enhancing the ``object`` class in the calling module. +The enhancement includes providing a new default printing representation +for instances: + +>>> from example import c1 +>>> print c1 +<instance of C1> + + +The ``decorated(object)`` trick (and the "[Decorated]" syntax too) +is not 100% safe, because of possible metaclass conflicts: + +>>> import decorators; object=decorators.decorated(object) +>>> class M(type): pass +... +>>> class D(object): +... __metaclass__=M +Traceback (most recent call last): + ... +TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases + +The decorators module imports the ``makecls`` function from my +``noconflict`` module just to avoid this kind of problems: + +>>> class D(object): +... __metaclass__=decorators.makecls(M) +>>> type(D) +<class 'noconflict._DecoratedM'> + +It is possible to go even deeper in black magic, and to decorate all +the new style classes in *all* modules, by decorating ``__builtin__.object``: + + :: + + import decorators,__builtin__ + __builtin.object=decorators.decorated(object) + +Still, redefining ``__builtin__object`` is not recommended since it +may induce metaclass conflicts in other modules using metaclasses. +It will work only if you import modules not using metaclasses, or +modules using metaclasses safely, i.e. modules taking care of +possible conflicts by using the ``makecls`` function or an equivalent one. + +Defining method decorators +----------------------------------------------------------------------- + +The decorators module contains two predefinite method decorators, +``staticmethod`` and ``classmethod``, which emulate the built-ins +with the same names. However, it is possible to write your own +custom decorators. The ``decorators`` module provides a +``MethodDecorator`` class which is here exactly for that purpose. + +Custom decorators are expected to be implemented by subclassing +``MethodDecorator`` and by overriding its ``get`` method, which +automagically induces a ``__get__`` method, turning the class +in a descriptor. This +machinery is needed since ``__get__`` cannot be made cooperative +using the standard ``super`` mechanism (there would be a confusion +between ``super.__get__`` and the decorator ``__get__``). This is a bit +tricky, but the causal programmer is not expected to write custom +decorators, and actually I don't want to make the access to decorators +*too* easy + +For instance, let me show the implementation of a ``chattymethod`` +that prints a message when it is called: + + :: + + #<customdec.py> + + from decorators import * + + class chattymethod(MethodDecorator): + logfile=file('file1.log','w') + def get(self,obj,cls=None): # same signature as __get__ + print >> self.logfile,'calling %s from %s' % (self,obj or cls) + return super(chattymethod,self).get(obj,cls) + + #</customdec.py> + +Notice the usage of the ``super().get`` trick. This guarantees that +``chattymethod`` will play well with other decorators (i.e. it +can be nicely composed via multiple inheritance) + +Here is an example of usage + + :: + + #<chatty.py> + + from customdec import decorated,chattymethod + object=decorated(object) + + class C(object): + def f(): + "[chattymethod,staticmethod]" + + c=C() + + c.f() + C.f() + + #</chatty.py> + +The content of the logfile is then + + :: + + calling <chattymethodstaticmethod:f> from <instance of C> + calling <chattymethodstaticmethod:f> from <class C[Decorated]> + +From this output we see how the "[chattymethod,staticmethod]" +magic docstring is responsible for the creation of a new decorator class +``chattymethodstaticmethod``, obtained via multiple inheritance from +``chattymethod`` and ``staticmethod`` respectively. + +One can easily change the logfile, if need there is + + :: + + #<chatty.py> + + chattymethod.logfile=file('file2.log','w') + + def g(): + "[chattymethod,staticmethod]" + + C.g=decorated(g) + C.g # message written in file2.log + C.f # message written in file2.log + + #</chatty.py> + +Now ``file2.log`` will contains the messages + + :: + + calling <chattymethodstaticmethod:g> from <class C[Decorated]> + calling <chattymethodstaticmethod:f> from <class C[Decorated]> + +This approach has the drawback that chattymethods created before changing +the logfile will also print to the new logfile, if invoked after the +change. This can be avoided by converting ``logfile`` from a class variable +to an instance variable in the ``__init__`` method: + + :: + + #<chatty2.py> + + import sys + from chatty import * + + class chattymethod2(chattymethod): + def __init__(self,func): + super(chattymethod2,self).__init__(func) + self.logfile=self.logfile # class variable becomes instance variable + + class C(object): + chattymethod2.logfile=sys.stdout + f=chattymethod2(lambda self:None) + chattymethod2.logfile=file('file3.log','w') + g=chattymethod2(lambda self:None) + + c=C() + + #</chatty2.py> + +Notice that the ``__init__`` method should have the signature +``__init__(self,func)``, where ``func`` is the function to be +converted in the decorator object. Here is the testing: + +>>> from chatty2 import c +>>> c.f() +calling <chattymethod2:<lambda>> from <instance of C> +>>> c.g() # message written in file3.log +>>> c.f() # message written in stdout, not in file3.log! +calling <chattymethod2:<lambda>> from <instance of C> + +Tracing methods +-------------------------------------------------------------------------- + +In order to show a non-trivial example, I will show how +decorators can be used to implement traced methods. +Here is the code (notice: the lazy reader can safely skip the +implementation details and go directly to the usage section ;) + + :: + + #<customdec.py> + + class tracedmethod(MethodDecorator): + "Descriptor class, converts a method in a traced method" + indent=0; output=sys.stdout # defaults + def __init__(self,func): + super(tracedmethod,self).__init__(func) + self.funcname=self.func.__name__ + def get(self,obj,cls): + if obj is None: name=self.inside.__name__ # called from class + else: name='<%s>' % self.inside.__name__ # from instance + methodwrapper=super(tracedmethod,self).get(obj,cls) + def _(*args,**kw): + i=' '*self.indent # default indentation + self.__class__.indent+=4 # increases indentation + self.output.write("%sCalling '%s.%s' with arguments " % + (i,name,self.funcname)) + self.output.write("%s ...\n" % (str(args)+str(kw))) + res=methodwrapper(*args,**kw) + self.output.write("%s'%s.%s' called with result: %s\n" + % (i,name,self.funcname,res)) + self.__class__.indent-=4 # restores default indentation + return res + return _ + + #</customdec.py> + +As soon as the ``tracedmethod`` module is loaded, the ``tracedmethod`` class +is added to the list of know decorators, so one should use the "[tracedmethod]" +docstring instead and not "[tracedmethod.tracedmethod]". + +``tracedmethod`` which is quite useful during +debugging. Here is an example of usage, in tracing the internal working +of a recursive function: + + :: + + #<example4.py> + + import decorators,customdec + + class C(object): + def fact(self,n): + "[tracedmethod]" + if n==0: return 1 + else: return n*self.fact(n-1) + + decorators.decorate(C) + + c=C() + + #</example4.py> + +>>> from example4 import c +>>> c.fact(2) +Calling '<C>.fact' with arguments (2,){} ... + Calling '<C>.fact' with arguments (1,){} ... + Calling '<C>.fact' with arguments (0,){} ... + '<C>.fact' called with result: 1 + '<C>.fact' called with result: 1 +'<C>.fact' called with result: 2 +2 + +An alternative spelling, not involving magic docstrings, nor the +decorators module, is the following: + + :: + + #<example5.py> + + from customdec import tracedmethod + + class C(object): + def fact(self,n): + if n==0: return 1 + else: return n*self.fact(n-1) + fact=tracedmethod(fact) + + c=C() + + #</example5.py> + +>>> from example5 import c +>>> c.fact(2) +Calling '<?>.fact' with arguments (2,){} ... + Calling '<?>.fact' with arguments (1,){} ... + Calling '<?>.fact' with arguments (0,){} ... + '<?>.fact' called with result: 1 + '<?>.fact' called with result: 1 +'<?>.fact' called with result: 2 +2 + +Notice that in this second syntax ``fact`` does not know where it +is defined; however the containing class can be explicitly set: + + ``C.__dict__['fact'].inside=C`` + +The ``inside`` attribute is automagically set if the docstring syntax +is used. + +Here is how to trace cooperative methods in complicate hierarchies +(which is useful for debugging): + + :: + + #<tracing.py> + + import decorators,customdec + object=decorators.decorated(object) + + class B(object): + def __init__(self): + "[tracedmethod]" + super(B,self).__init__() + + class D(object): + def __init__(self): + "[tracedmethod]" + super(D,self).__init__() + + class E(B,D): + def __init__(self): + "[tracedmethod]" + super(E,self).__init__() + + #</tracing.py> + +>>> from tracing import E +>>> e=E() +Calling '<E>.__init__' with arguments (){} ... + Calling '<B>.__init__' with arguments (){} ... + Calling '<D>.__init__' with arguments (){} ... + '<D>.__init__' called with result: None + '<B>.__init__' called with result: None +'<E>.__init__' called with result: None + +In this example decorating ``object`` is the easiest way to go. + +Composition of decorators +-------------------------------------------------------------------- + +Decorators can be composed: for instance, you can trace a +classmethod as in this example: + + :: + + #<example6.py> + + import customdec + from decorators import decorated + object=decorated(object) + + class C(object): + def fact(cls,n): + "[tracedmethod,classmethod]" + if n==0: return 1 + else: return n*cls.fact(n-1) + + #</example6.py> + +>>> from example6 import C +>>> C.fact(2) +Calling 'C.fact' with arguments (2,){} ... + Calling 'C.fact' with arguments (1,){} ... + Calling 'C.fact' with arguments (0,){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 + +Under the hood the syntax + + :: + + [tracedmethod,classmethod] + +generates a ``tracedmethodclassmethod`` class obtained via +multiple inheritance: + +>>> for c in type(C.__dict__['fact']).__mro__: print c +... +<class 'noconflict.tracedmethodclassmethod'> +<class 'customdec.tracedmethod'> +<class 'decorators.classmethod'> +<class 'decorators.MethodDecorator'> +<class 'decorators.Decorator'> +<type 'object'> + +In this case the order does not matter and using the docstring +"[classmethod,tracedmethod]" would work too, but +in general one must pay attention to precedence issues. +For instance the following will not work: + +>>> class C(object): +... def fact(n): +... "[staticmethod,tracedmethod]" +... if n==0: return 1 +... else: return n*C.fact(n-1) +>>> C.fact(2) +Traceback (most recent call last): + ... +AttributeError: 'function' object has no attribute 'im_func' + +The problem here is that ``staticmethod.get`` invokes ``tracedmethod.get`` +which returns a function and not a method-wrapper with an ``im_func`` method. +On the other hand, composing the decorators in the other order +"[tracedmethod,staticmethod]" will work just fine. + +Class decorators +----------------------------------------------------------------------- + +PEP 318 proposes to decorate methods by using descriptors; it is +quite natural to extend this idea and to decorate classes by using +class decorators implemented as metaclasses. We already saw a +class decorator at work, the metaclass ``Decorated``, giving +to its instances the ability to interpret magic docstrings, +and converting functions in method decorators. + +To define a custom class decorator is easy: one defines a custom metaclasses +as usual, with the only difference from deriving by ``ClassDecorator`` instead +of deriving from ``type``. To understand how this works in practice, let me +show how to add logging capabilities to a given class. The first +step is to define a suitable class decorator, such as the following: + + :: + + #<customdec.py> + + class Logged(ClassDecorator): + def __init__(cls,name,bases,dic): + super(Logged,cls).__init__(name,bases,dic) + print "%s created" % cls + + #</customdec.py> + +``Logged`` is derived by the metaclass ``ClassDecorator``, +which provides a certain amount of magic under the hood (in particular +its printing representation and its calling syntax are redefined by its +metaclass ``MetaDecorator``). Logging capabilities can be added to a class +by simply using the magic docstring syntax: + +>>> from customdec import Logged +>>> object=decorators.decorated(object) +>>> class D(object): +... "[Logged]" +<class D[_DecoratedLogged]> created + +Notice that ``D`` inherits the ``Decorated`` metaclass from ``object`` +and the ``Logged`` metaclass from the docstring; the conflict is +automagically avoid by the miracolous creation of a ``_DecoratedLogged`` +metaclass, obtained via multiple inheritance from ``Decorated`` and +``Logged``. All the magic is performed in the ``noconflict`` module, +discussed in a cookbook_ recipe of mine. + +Notice that the printing representation of ``D`` involves the name +of ``D`` preceded by the name of its metaclass, which in this case +is ``_DecoratedLogged`` + +Each time an instance of ``Logged`` is created, a similar message is printed: + +>>> class E(D): +... pass +<class E[_DecoratedLogged]> created + +Notice that ``E`` does not have any magic docstring + +>>> E.__doc__ # no docstring + +but still it inherits its magic from ``D``. + +The ``decorators`` module provides the already saw class decorator +``Decorated``, which converts methods in method decorators. + +Another example is + + :: + + #<customdec.py> + + from types import FunctionType + + class Traced(ClassDecorator): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType): + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) + + + #</customdec.py> + +Here is an example of usage: + +>>> from customdec import * +>>> object=decorated(object) +>>> class C(object): +... """[Traced] The class decorator adds the magic docstring +... '[tracedmethod]' to f1 and f2, which are then converted +... to method decorator objects.""" +... def f1(self): pass +... def f2(self): pass +... +>>> c=C() +>>> c.f1() +Calling '<C>.f1' with arguments (){} ... +'<C>.f1' called with result: None +>>> c.f2() +Calling '<C>.f2' with arguments (){} ... +'<C>.f2' called with result: None + +Module decorators +----------------------------------------------------------------------- + +Finally, one can decorate entire modules through the concept of +*module decorators*. Module decorators have the ability of decorating +modules by changing their dictionary. Custom module decorators +should be derived from the class ``decorators.ModuleDecorator``, by +cooperatively overriding its ``__init__(self,mod)`` method. Writing +a module decorator is a bit tricky, but I do expect only +expert programmers to play this kind of game. +For instance, suppose one wants to trace all the functions in a module, +unless they have the docstring "-untraced-" . This can be done with a +suitable module decorator which modifies the module dictionary. +Here is an example + + :: + + #<customdec.py> + + from decorators import * + + class TraceFunctions(ModuleDecorator): + def __init__(self,mod): + super(TraceFunctions,self).__init__(mod) + for name,func in self.__dict__.items(): + if inspect.isfunction(func): + doc=func.__doc__ or '' + if doc.startswith('-untraced-'): + pass # do nothing + else: + def tracedfunc(func=func): # default argument trick + def _(*args,**kw): + print 'called',func.__name__ + return func(*args,**kw) + return _ + setattr(self,name,tracedfunc()) + + #</customdec.py> + +Let me test that the module decorator does its job. Consider the module + + :: + + #<mod.py> + + #"[TraceFunctions]" + + def f1(): pass + + def f2(): pass + + def f3(): "-untraced-" + + #</mod.py> + +By importing this module, only the functions ``f1`` and ``f2`` should +be traced. This is indeed the case: + +>>> from customdec import TraceFunctions +>>> mod=TraceFunctions('mod',{}) +>>> mod.f1() +called f1 +>>> mod.f2() +called f2 +>>> mod.f3() # does nothing, correct + + :: + + #<module.py> + + "Magically decorated module" + + import decorators,sys + + thismodule=sys.modules[__name__] + + class MyClass: "[Decorated]" + + newmod=decorators.decorated(thismodule) + + #</module.py> + +>>> from module import * +>>> assert isinstance(newmod.MyClass, decorators.Decorated) +>>> assert isinstance(newmod,decorators.DecoratedModule) + +The implementation +----------------------------------------------------------------------- + +This part can be safely skipped, unless you are a *really* curious and +you want to know how the implementation works. + +The module is rather short (~250 lines) but far from being trivial, +since it is based on extensive usage of metaclass wizardry. + +The main class-metaclass hierarchy is represented in figure 1, where +boxes denote classes and ovals denote metaclasses; instantiation is +denoted by dashed lines whereas inheritance is denoted by continuous +lines. + +.. figure:: decorators.png + +The implementation is relatively smart. Suppose for instance +that a programmer wrote something like + +>>> from decorators import * +>>> object=decorated(object) +>>> class C(object): +... def f(): +... "[staticmethod,MethodDecorator]" + +to document the fact that ``staticmethod`` is a method decorator +and not the built-in ``staticmethod``. Since ``staticmethod`` already +derives from ``MethodDecorator``, it is redundant to repeat +``MethodDecorator``. Apparently, there is the risk of generating +an useless ``staticmethodMethodDecorator`` class, doing the same +as ``staticmethod``. Fortunately, the implementation is able +to recognize redundant class. In this case, a class called +``MethodDecoratorDecorator`` is *not* created; ``staticmethod`` +is used instead: + +>>> print type(C.__dict__['f']) +<class 'decorators.staticmethod'> + +The module provides three utilities functions to retrieve the list of +recognized decorators: ``decorators.methodlike()``, ``decorators.classlike()`` +and ``decorators.modulelike()``: + +>>> for d in decorators.methodlike(): print d +... +<class 'decorators.MethodDecorator'> +<class 'decorators.staticmethod'> +<class 'decorators.classmethod'> +<class 'customdec.chattymethod'> +<class 'customdec.tracedmethod'> +<class 'noconflict.chattymethodstaticmethod'> +<class 'chatty2.chattymethod2'> +<class 'noconflict.tracedmethodclassmethod'> +<class 'noconflict.staticmethodtracedmethod'> + +>>> for d in decorators.classlike(): print d +... +<class 'decorators.ClassDecorator'> +<class 'decorators.Decorated'> +<class 'noconflict._DecoratedM'> +<class 'customdec.Logged'> +<class 'customdec.Traced'> +<class 'noconflict._DecoratedLogged'> +<class 'noconflict._DecoratedTraced'> + +>>> for d in decorators.modulelike(): print d +... +<class 'decorators.ModuleDecorator'> +<class 'decorators.DecoratedModule'> +<class 'customdec.TraceFunctions'> + +The current implementation does not make any attempt of optimization and +there may be alternative implementations faster or more memory efficient. +At this experimental level I didn't care to explore about performances +issues. They does not probably matter unless one has to decorate +thousands or millions of functions and classes. + +Finally, a word about bugs. The ``decorators`` module is fairly sophisticated, +therefore whereas I can guarantee that it passes my test suite (which involves +~100 tests automatically extracted from the documentation you are reading), +I cannot guarantee that it is correct. If somebody finds a bug or an unexpected +behavior, please let me know and I will try to fix it. + +.. References: + +.. _new style classes and old style classes: + http://www.python.org/2.3/descrintro.html +.. _Descriptors: http://users.rcn.com/python/download/Descriptor.htm +.. _cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197 diff --git a/pypers/pep318/working/pydoc.html b/pypers/pep318/working/pydoc.html new file mode 100755 index 0000000..8422868 --- /dev/null +++ b/pypers/pep318/working/pydoc.html @@ -0,0 +1,504 @@ + +<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html><head><title>Python: module decorators</title> +</head><body bgcolor="#f0f0f8"> + +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading"> +<tr bgcolor="#7799ee"> +<td valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>decorators</strong></big></big></font></td +><td align=right valign=bottom +><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/mnt/exp/MyDocs/pypers/pep318/decorators.py">/mnt/exp/MyDocs/pypers/pep318/decorators.py</a></font></td></tr></table> + <p><tt>A module to implement pep318 (decorator syntax) via magic doctrings.<br> +For the documentation see<br> + <br> +<a href="http://www.phyast.pitt.edu/~micheles/python/decorators,html">http://www.phyast.pitt.edu/~micheles/python/decorators,html</a><br> + <br> +help and pydoc are useful too.</tt></p> +<p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#aa55cc"> +<td colspan=3 valign=bottom> <br> +<font color="#fffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr> + +<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td> +<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="__builtin__.html">__builtin__</a><br> +<a href="inspect.html">inspect</a><br> +</td><td width="25%" valign=top><a href="noconflict.html">noconflict</a><br> +<a href="re.html">re</a><br> +</td><td width="25%" valign=top><a href="sys.html">sys</a><br> +<a href="types.html">types</a><br> +</td><td width="25%" valign=top></td></tr></table></td></tr></table><p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ee77aa"> +<td colspan=3 valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr> + +<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td> +<td width="100%"><dl> +<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a> +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#UnknownDecoratorError">UnknownDecoratorError</a> +</font></dt></dl> +</dd> +<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a> +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#Decorator">Decorator</a> +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>) +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a> +</font></dt></dl> +</dd> +<dt><font face="helvetica, arial"><a href="decorators.html#MethodDecorator">MethodDecorator</a> +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#classmethod">classmethod</a> +</font></dt><dt><font face="helvetica, arial"><a href="decorators.html#staticmethod">staticmethod</a> +</font></dt></dl> +</dd> +</dl> +</dd> +</dl> +</dd> +<dt><font face="helvetica, arial"><a href="__builtin__.html#type">__builtin__.type</a>(<a href="__builtin__.html#object">__builtin__.object</a>) +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>) +</font></dt><dd> +<dl> +<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a> +</font></dt></dl> +</dd> +<dt><font face="helvetica, arial"><a href="decorators.html#MetaDecorator">MetaDecorator</a> +</font></dt></dl> +</dd> +</dl> + <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="ClassDecorator">class <strong>ClassDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt>Metaclass callable with one or three arguments, having its __call__<br> +method redefined by the meta-metaclass <a href="#MetaDecorator">MetaDecorator</a>. It redefines<br> +__str__ both on classes and instances.<br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd> +<dd><a href="__builtin__.html#type">__builtin__.type</a></dd> +<dd><a href="decorators.html#Decorator">Decorator</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="ClassDecorator-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl> + +<dl><dt><a name="ClassDecorator-__str__"><strong>__str__</strong></a>(cls)</dt></dl> + +<hr> +Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><a name="ClassDecorator-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__call__">__call__</a>(...) <==> x(...)</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__cmp__">__cmp__</a>(y) <==> cmp(x,y)</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__delattr__">__delattr__</a>('name') <==> del x.name</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__getattribute__">__getattribute__</a>('name') <==> x.name</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__hash__">__hash__</a>() <==> hash(x)</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__repr__">__repr__</a>() <==> repr(x)</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__setattr__">__setattr__</a>('name', value) <==> x.name = value</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-__subclasses__">__subclasses__</a>() -> list of immediate subclasses</tt></dd></dl> + +<dl><dt><a name="ClassDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-mro">mro</a>() -> list<br> +return a <a href="__builtin__.html#type">type</a>'s method resolution order</tt></dd></dl> + +<hr> +Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><strong>__base__</strong> = <type 'type'></dl> + +<dl><dt><strong>__bases__</strong> = (<type 'type'>, <class 'decorators.Decorator'>)</dl> + +<dl><dt><strong>__basicsize__</strong> = 420</dl> + +<dl><dt><strong>__dict__</strong> = <dictproxy object></dl> + +<dl><dt><strong>__dictoffset__</strong> = 132</dl> + +<dl><dt><strong>__flags__</strong> = 22523</dl> + +<dl><dt><strong>__itemsize__</strong> = 20</dl> + +<dl><dt><strong>__mro__</strong> = (<class 'decorators.ClassDecorator'>, <type 'type'>, <class 'decorators.Decorator'>, <type 'object'>)</dl> + +<dl><dt><strong>__new__</strong> = <built-in method __new__ of type object><dd><tt>T.<a href="#ClassDecorator-__new__">__new__</a>(S, ...) -> a new <a href="__builtin__.html#object">object</a> with <a href="__builtin__.html#type">type</a> S, a subtype of T</tt></dl> + +<dl><dt><strong>__weakrefoffset__</strong> = 184</dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br> +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="Decorated">class <strong>Decorated</strong></a>(<a href="decorators.html#ClassDecorator">ClassDecorator</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt>Metaclass which decorates its instances<br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#Decorated">Decorated</a></dd> +<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd> +<dd><a href="__builtin__.html#type">__builtin__.type</a></dd> +<dd><a href="decorators.html#Decorator">Decorator</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="Decorated-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl> + +<hr> +Methods inherited from <a href="decorators.html#ClassDecorator">ClassDecorator</a>:<br> +<dl><dt><a name="Decorated-__str__"><strong>__str__</strong></a>(cls)</dt></dl> + +<hr> +Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><a name="Decorated-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__call__">__call__</a>(...) <==> x(...)</tt></dd></dl> + +<dl><dt><a name="Decorated-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__cmp__">__cmp__</a>(y) <==> cmp(x,y)</tt></dd></dl> + +<dl><dt><a name="Decorated-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__delattr__">__delattr__</a>('name') <==> del x.name</tt></dd></dl> + +<dl><dt><a name="Decorated-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__getattribute__">__getattribute__</a>('name') <==> x.name</tt></dd></dl> + +<dl><dt><a name="Decorated-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__hash__">__hash__</a>() <==> hash(x)</tt></dd></dl> + +<dl><dt><a name="Decorated-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__repr__">__repr__</a>() <==> repr(x)</tt></dd></dl> + +<dl><dt><a name="Decorated-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__setattr__">__setattr__</a>('name', value) <==> x.name = value</tt></dd></dl> + +<dl><dt><a name="Decorated-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#Decorated-__subclasses__">__subclasses__</a>() -> list of immediate subclasses</tt></dd></dl> + +<dl><dt><a name="Decorated-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#Decorated-mro">mro</a>() -> list<br> +return a <a href="__builtin__.html#type">type</a>'s method resolution order</tt></dd></dl> + +<hr> +Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><strong>__base__</strong> = <class 'decorators.ClassDecorator'></dl> + +<dl><dt><strong>__bases__</strong> = (<class 'decorators.ClassDecorator'>,)</dl> + +<dl><dt><strong>__basicsize__</strong> = 420</dl> + +<dl><dt><strong>__dict__</strong> = <dictproxy object></dl> + +<dl><dt><strong>__dictoffset__</strong> = 132</dl> + +<dl><dt><strong>__flags__</strong> = 22523</dl> + +<dl><dt><strong>__itemsize__</strong> = 20</dl> + +<dl><dt><strong>__mro__</strong> = (<class 'decorators.Decorated'>, <class 'decorators.ClassDecorator'>, <type 'type'>, <class 'decorators.Decorator'>, <type 'object'>)</dl> + +<dl><dt><strong>__new__</strong> = <built-in method __new__ of type object><dd><tt>T.<a href="#Decorated-__new__">__new__</a>(S, ...) -> a new <a href="__builtin__.html#object">object</a> with <a href="__builtin__.html#type">type</a> S, a subtype of T</tt></dl> + +<dl><dt><strong>__weakrefoffset__</strong> = 184</dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br> +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="Decorator">class <strong>Decorator</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt>Instance of <a href="#MetaDecorator">MetaDecorator</a>, i.e. each time <a href="#Decorator">Decorator</a> is<br> +subclassed, <a href="#MetaDecorator">MetaDecorator</a>.dic is updated.<br> </tt></td></tr> +<tr><td> </td> +<td width="100%">Data and other attributes defined here:<br> +<dl><dt><strong>__dict__</strong> = <dictproxy object><dd><tt>dictionary for instance variables (if defined)</tt></dl> + +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="MetaDecorator">class <strong>MetaDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.<br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#MetaDecorator">MetaDecorator</a></dd> +<dd><a href="__builtin__.html#type">__builtin__.type</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="MetaDecorator-__call__"><strong>__call__</strong></a>(dec, *args)</dt><dd><tt>Dispatch to the decorator _call_ <a href="#classmethod">classmethod</a></tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__init__"><strong>__init__</strong></a>(dec, *args)</dt></dl> + +<hr> +Data and other attributes defined here:<br> +<dl><dt><strong>dic</strong> = {'ClassDecorator': <class 'decorators.ClassDecorator'>, 'Decorated': <class 'decorators.Decorated'>, 'Decorator': <class 'decorators.Decorator'>, 'MethodDecorator': <class 'decorators.MethodDecorator'>, 'classmethod': <class 'decorators.classmethod'>, 'staticmethod': <class 'decorators.staticmethod'>}</dl> + +<dl><dt><strong>ls</strong> = [<class 'decorators.Decorator'>, <class 'decorators.MethodDecorator'>, <class 'decorators.ClassDecorator'>, <class 'decorators.Decorated'>, <class 'decorators.staticmethod'>, <class 'decorators.classmethod'>]</dl> + +<hr> +Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><a name="MetaDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__cmp__">__cmp__</a>(y) <==> cmp(x,y)</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__delattr__">__delattr__</a>('name') <==> del x.name</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__getattribute__">__getattribute__</a>('name') <==> x.name</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__hash__">__hash__</a>() <==> hash(x)</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__repr__">__repr__</a>() <==> repr(x)</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__setattr__">__setattr__</a>('name', value) <==> x.name = value</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-__subclasses__">__subclasses__</a>() -> list of immediate subclasses</tt></dd></dl> + +<dl><dt><a name="MetaDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-mro">mro</a>() -> list<br> +return a <a href="__builtin__.html#type">type</a>'s method resolution order</tt></dd></dl> + +<hr> +Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br> +<dl><dt><strong>__base__</strong> = <type 'type'></dl> + +<dl><dt><strong>__bases__</strong> = (<type 'type'>,)</dl> + +<dl><dt><strong>__basicsize__</strong> = 420</dl> + +<dl><dt><strong>__dict__</strong> = <dictproxy object></dl> + +<dl><dt><strong>__dictoffset__</strong> = 132</dl> + +<dl><dt><strong>__flags__</strong> = 22523</dl> + +<dl><dt><strong>__itemsize__</strong> = 20</dl> + +<dl><dt><strong>__mro__</strong> = (<class 'decorators.MetaDecorator'>, <type 'type'>, <type 'object'>)</dl> + +<dl><dt><strong>__new__</strong> = <built-in method __new__ of type object><dd><tt>T.<a href="#MetaDecorator-__new__">__new__</a>(S, ...) -> a new <a href="__builtin__.html#object">object</a> with <a href="__builtin__.html#type">type</a> S, a subtype of T</tt></dl> + +<dl><dt><strong>__weakrefoffset__</strong> = 184</dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="MethodDecorator">class <strong>MethodDecorator</strong></a>(<a href="decorators.html#Decorator">Decorator</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt><a href="#MethodDecorator">MethodDecorator</a> objects provide a 'get' method, a 'str' method<br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd> +<dd><a href="decorators.html#Decorator">Decorator</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="MethodDecorator-__get__"><strong>__get__</strong></a> = <a href="#MethodDecorator-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<dl><dt><a name="MethodDecorator-__init__"><strong>__init__</strong></a>(self, func)</dt></dl> + +<dl><dt><a name="MethodDecorator-__str__"><strong>__str__</strong></a>(self)</dt></dl> + +<dl><dt><a name="MethodDecorator-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<hr> +Data and other attributes defined here:<br> +<dl><dt><strong>__klass__</strong> = <class 'decorators.?'></dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br> +<dl><dt><strong>__dict__</strong> = <dictproxy object><dd><tt>dictionary for instance variables (if defined)</tt></dl> + +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="UnknownDecoratorError">class <strong>UnknownDecoratorError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt>The name says it all<br> </tt></td></tr> +<tr><td> </td> +<td width="100%">Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br> +<dl><dt><a name="UnknownDecoratorError-__getitem__"><strong>__getitem__</strong></a>(...)</dt></dl> + +<dl><dt><a name="UnknownDecoratorError-__init__"><strong>__init__</strong></a>(...)</dt></dl> + +<dl><dt><a name="UnknownDecoratorError-__str__"><strong>__str__</strong></a>(...)</dt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="classmethod">class <strong>classmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt><a href="#Decorator">Decorator</a>, converts a function in a <a href="#classmethod">classmethod</a><br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#classmethod">classmethod</a></dd> +<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd> +<dd><a href="decorators.html#Decorator">Decorator</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="classmethod-__get__"><strong>__get__</strong></a> = <a href="#classmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<dl><dt><a name="classmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<hr> +Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br> +<dl><dt><a name="classmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl> + +<dl><dt><a name="classmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br> +<dl><dt><strong>__klass__</strong> = <class 'decorators.?'></dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br> +<dl><dt><strong>__dict__</strong> = <dictproxy object><dd><tt>dictionary for instance variables (if defined)</tt></dl> + +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table> <p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#ffc8d8"> +<td colspan=3 valign=bottom> <br> +<font color="#000000" face="helvetica, arial"><a name="staticmethod">class <strong>staticmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr> + +<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> +<td colspan=2><tt><a href="#Decorator">Decorator</a>, converts a function in a <a href="#staticmethod">staticmethod</a><br> </tt></td></tr> +<tr><td> </td> +<td width="100%"><dl><dt>Method resolution order:</dt> +<dd><a href="decorators.html#staticmethod">staticmethod</a></dd> +<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd> +<dd><a href="decorators.html#Decorator">Decorator</a></dd> +<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> +</dl> +<hr> +Methods defined here:<br> +<dl><dt><a name="staticmethod-__get__"><strong>__get__</strong></a> = <a href="#staticmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<dl><dt><a name="staticmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl> + +<hr> +Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br> +<dl><dt><a name="staticmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl> + +<dl><dt><a name="staticmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br> +<dl><dt><strong>__klass__</strong> = <class 'decorators.?'></dl> + +<hr> +Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br> +<dl><dt><strong>__dict__</strong> = <dictproxy object><dd><tt>dictionary for instance variables (if defined)</tt></dl> + +<dl><dt><strong>__metaclass__</strong> = <class 'decorators.MetaDecorator'><dd><tt>Metaclass inducing a certain amount of magic on decorators:<br> +1. Guarantees the calling syntax decorator(obj)<br> +2. Each time a decorator is defined, it is stored in <a href="#MetaDecorator">MetaDecorator</a>.dic<br> + and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +3. If the (method) decorator has a 'get' method, a '__get__' method<br> +is automagically created as an alias to 'get'.</tt></dl> + +<dl><dt><strong>__weakref__</strong> = <attribute '__weakref__' of 'Decorator' objects><dd><tt>list of weak references to the <a href="__builtin__.html#object">object</a> (if defined)</tt></dl> + +</td></tr></table></td></tr></table><p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#eeaa77"> +<td colspan=3 valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr> + +<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td> +<td width="100%"><dl><dt><a name="-anyTrue"><strong>anyTrue</strong></a> = sum(...)</dt><dd><tt>sum(sequence, start=0) -> value<br> + <br> +Returns the sum of a sequence of numbers (NOT strings) plus the value<br> +of parameter 'start'. When the sequence is empty, returns start.</tt></dd></dl> + <dl><dt><a name="-decorate"><strong>decorate</strong></a>(objdict)</dt><dd><tt>takes an <a href="__builtin__.html#object">object</a> with a dictionary and decorates all its functions<br> +and classes according to their docstrings.</tt></dd></dl> + <dl><dt><a name="-decorated"><strong>decorated</strong></a>(obj<font color="#909090">=None</font>)</dt><dd><tt>Returns a new decorated <a href="__builtin__.html#object">object</a> created from obj, or None, if obj<br> +cannot be decorated.</tt></dd></dl> + <dl><dt><a name="-decorator_from"><strong>decorator_from</strong></a>(magicstring)</dt><dd><tt>Takes a magic string, i.e. a list of comma-separated decorator names,<br> +and returns a decorator class or None (if the string is not matched).</tt></dd></dl> + <dl><dt><a name="-get"><strong>get</strong></a>(magicstring<font color="#909090">='Decorator'</font>)</dt><dd><tt>List of recognized decorators</tt></dd></dl> + <dl><dt><a name="-magicstring"><strong>magicstring</strong></a>(docstring)</dt></dl> + <dl><dt><a name="-printerr"><strong>printerr</strong></a>(*args)</dt><dd><tt>For debugging purposes</tt></dd></dl> +</td></tr></table><p> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> +<tr bgcolor="#55aa55"> +<td colspan=3 valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> + +<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> +<td width="100%"><strong>MAGICDOC</strong> = <_sre.SRE_Pattern object><br> +<strong>err</strong> = <open file 'err', mode 'w'></td></tr></table> +</body></html>
\ No newline at end of file diff --git a/pypers/pep318/working/tracing.py b/pypers/pep318/working/tracing.py new file mode 100755 index 0000000..05f94b4 --- /dev/null +++ b/pypers/pep318/working/tracing.py @@ -0,0 +1,20 @@ +# tracing.py + +import customdec; customdec.enhance_classes("[Decorated]") + +class B(object): + def __init__(self): + "[tracedmethod]" + super(B,self).__init__() + +class D(object): + def __init__(self): + "[tracedmethod]" + super(D,self).__init__() + +class E(B,D): + def __init__(self): + "[tracedmethod]" + super(E,self).__init__() + + |