diff options
author | michele.simionato <devnull@localhost> | 2007-12-02 11:13:11 +0000 |
---|---|---|
committer | michele.simionato <devnull@localhost> | 2007-12-02 11:13:11 +0000 |
commit | 20ce686b0193d67ea56823a30551140f88b3aee1 (patch) | |
tree | 76015e7e4dc0b000bd857a2bdba6fb7976ac29a7 /pypers/pep318 | |
parent | f08f40335ad7f0ac961f25dabaaed34c4d4bcc44 (diff) | |
download | micheles-20ce686b0193d67ea56823a30551140f88b3aee1.tar.gz |
Commited all py papers into Google code
Diffstat (limited to 'pypers/pep318')
71 files changed, 13889 insertions, 0 deletions
diff --git a/pypers/pep318/Makefile b/pypers/pep318/Makefile new file mode 100755 index 0000000..9249fb8 --- /dev/null +++ b/pypers/pep318/Makefile @@ -0,0 +1,23 @@ +decorators.rst: decorators.py $S/minidoc.py + $S/minidoc.py -r decorators -r +decorators.html: decorators.txt decorators.png decorators.rst + $S/rst.py decorators.html +decorators.dvi: decorators.txt decorators.rst + $S/rst.py -t decorators.tex + perl -pi -e 's/\(non-strict/\n(non-strict/' decorators.tex + perl -pi -e 's/as first/\nas first/' decorators.tex + $S/rst.py -d decorators.dvi +decorators.pdf: decorators.dvi decorators.ps + dvipdf decorators +decorators.ps: makegraph.dot + dot makegraph.dot -Tps -o decorators.ps -Gsize="5.5,6" +decorators.png: makegraph.dot + dot makegraph.dot -Tpng -o decorators.png -Gsize="5.5,6" +decorators.zip: decorators.html decorators.pdf \ + decorators.py README.txt $S/safetype.py $S/doct.py + zip -j decorators decorators.py decorators.png decorators.ps \ + decorators.html decorators.txt decorators.pdf decorators.rst\ + README.txt makegraph.dot $S/safetype.py $S/doct.py +dist: decorators.zip +working: decorators.zip + unzip -o decorators.zip -d working diff --git a/pypers/pep318/README.txt b/pypers/pep318/README.txt new file mode 100755 index 0000000..341bf64 --- /dev/null +++ b/pypers/pep318/README.txt @@ -0,0 +1,48 @@ +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) + +11. decorators.rst (the quick reference to decorators.py) + +12. safetype.rst (the quick reference to safetype.py) + +``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/__main__.html b/pypers/pep318/__main__.html new file mode 100755 index 0000000..06494a9 --- /dev/null +++ b/pypers/pep318/__main__.html @@ -0,0 +1,61 @@ +<?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>Documentation of the __main__ module</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="documentation-of-the-main-module"> +<h1 class="title">Documentation of the __main__ module</h1> +<p>Short utility to extract documentation from a module</p> +<div class="section" id="documented-metaclasses"> +<h1><a name="documented-metaclasses">Documented metaclasses</a></h1> +<div class="system-message"> +<p class="system-message-title">System Message: ERROR/3 (<tt>__main__.rst</tt>, line 6)</p> +Section empty; must have contents.</div> +</div> +<div class="section" id="documented-classes"> +<h1><a name="documented-classes">Documented classes</a></h1> +<div class="system-message"> +<p class="system-message-title">System Message: ERROR/3 (<tt>__main__.rst</tt>, line 10)</p> +Section empty; must have contents.</div> +</div> +<div class="section" id="documented-functions"> +<h1><a name="documented-functions">Documented functions</a></h1> +<p><tt class="literal"><span class="pre">publish_cmdline(reader=None,</span> <span class="pre">reader_name='standalone'</span></tt></p> +<p>Set up & run a <cite>Publisher</cite>. For command-line front ends.</p> +<p>Parameters:</p> +<ul class="simple"> +<li><cite>reader</cite>: A <cite>docutils.readers.Reader</cite> object.</li> +<li><cite>reader_name</cite>: Name or alias of the Reader class to be instantiated if +no <cite>reader</cite> supplied.</li> +<li><cite>parser</cite>: A <cite>docutils.parsers.Parser</cite> object.</li> +<li><cite>parser_name</cite>: Name or alias of the Parser class to be instantiated if +no <cite>parser</cite> supplied.</li> +<li><cite>writer</cite>: A <cite>docutils.writers.Writer</cite> object.</li> +<li><cite>writer_name</cite>: Name or alias of the Writer class to be instantiated if +no <cite>writer</cite> supplied.</li> +<li><cite>settings</cite>: Runtime settings object.</li> +<li><cite>settings_spec</cite>: Extra settings specification; a <cite>docutils.SettingsSpec</cite> +subclass. Used only if no <cite>settings</cite> specified.</li> +<li><cite>settings_overrides</cite>: A dictionary containing program-specific overrides +of component settings.</li> +<li><cite>argv</cite>: Command-line argument list to use instead of <tt class="literal"><span class="pre">sys.argv[1:]</span></tt>.</li> +<li><cite>usage</cite>: Usage string, output if there's a problem parsing the command +line.</li> +<li><cite>description</cite>: Program description, output for the "--help" option +(along with command-line option descriptions).</li> +</ul> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="__main__.rst">View document source</a>. +Generated on: 2003-09-20 09:39 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/addtests.txt b/pypers/pep318/addtests.txt new file mode 100755 index 0000000..67d96d2 --- /dev/null +++ b/pypers/pep318/addtests.txt @@ -0,0 +1,232 @@ +Additional tests +======================================================================= + +You cannot add magic methods magically *after*: + +>>> from customdec import * +>>> enhance_classes() + + +>>> class C: +... "[Decorated]" +... +>>> identity=lambda x:x +>>> C.identity=identity +>>> C.identity(1) +Traceback (most recent call last): + ... +TypeError: unbound method <lambda>() must be called with C instance as first argument (got int instance instead) + +(it could be done by modifying ``__setattr__`` in ``Decorated``). + + +Usage of decorator +------------------------------------------------------------------------ + +>>> decorator(1) + + +Printing representation +------------------------------------------------------------------------ + +A delicate point: inverting the docstring with __metaclass__ + +>>> class M(type): +... def __str__(cls): +... return cls.__name__ +>>> class C(object): +... __metaclass__=M +... "[Decorated]" # not considered docstring! +... def __str__(self): +... return '<C>' +>>> print C.__doc__ +None +>>> C=decorator(C) +>>> print type(C),C,C() +<class 'safetype.MClassDecorator'> C <C> + + +>>> class C(object): +... "[Decorated]" +... __metaclass__=M +... def __str__(self): +... return '<C>' +>>> C=decorator(C) +>>> print type(C),C,C() +<class 'safetype.MClassDecoratorDecorated'> C <C> +>>> #from MROgraph import MROgraph; g=MROgraph(type(C)) + +Tricky ways of passing parameters to decorators +------------------------------------------------------------------------ + +This can be avoided by converting ``logfile`` from a class variable +to an instance variable in the ``__init__`` method: + + :: + + #<chatty1.py> + + import sys,customdec,decorators + + class chattymethod1(customdec.chattymethod): + def __init__(self,func): + super(chattymethod1,self).__init__(func) + self.logfile=self.logfile # class variable -> instance variable + + class D: + chattymethod1.logfile=sys.stdout + def f(self): pass + f=chattymethod1(f) + + chattymethod1.logfile=file('file.log','w') + def g(self): pass + g=chattymethod1(g) + + d=D() + + #</chatty1.py> + +Here is the testing: + +>>> from chatty1 import D,d +>>> D.f(d) +calling <chattymethod1:f> from chatty1.D + +>>> D.g(d) # message written in file.log +>>> D.f(d) # message correctly written in stdout, not in file.log +calling <chattymethod1:f> from chatty1.D + +This works as it is but it is really ugly and fragile: for instance +the magic docstring syntax + + :: + + class D: + "[Decorated]" + chattymethod1.logfile=sys.stdout + def f(self): "[chattymethod1]" + + chattymethod1.logfile=file('file.log','w') + def g(self):"[chattymethod1]" + + +will not work since the logfile attribute will be modified *before* +the conversion in decorators, so both ``f`` and ``g`` will output to +'file.log'. + +Tests on method wrappers +------------------------------------------------------- + +>>> class C: pass +... +>>> c=C() +>>> def f(x): return x +... +>>> sm=staticmethod(f) +>>> C.sm=sm +>>> C.sm.im_func # correct +Traceback (most recent call last): + ... +AttributeError: 'function' object has no attribute 'im_func' + +(idem for ``c.sm.im_func``). On the other hand + +>>> cm=classmethod(f) +>>> C.cm=cm +>>> assert C.cm.im_func is f +>>> assert c.cm.im_func is f +>>> assert C.cm.im_class is C +>>> assert c.cm.im_class is C +>>> assert C.cm.im_self is C +>>> assert c.cm.im_self is C + +Test on tracedmethod from the instance +------------------------------------------------- + +>>> from example6 import C +>>> print C.__dict__['fact'] +<classmethodtracedmethod:fact> +>>> 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 + + +Tests on composition of decorators +-------------------------------------------------: + +To decorate something which is already decorated: + +>>> def g(x): "[tracedmethod]" +... +>>> tm=decorator(g) # creates a tracedmethod from f +>>> print decorator(tm) # trying to decorate a tracedmethod +<tracedmethod:g> + +Staticmethod vs classmethod. + +Staticmethod is non-cooperative, therefore staticmethodclassmethod +acts as a pure staticmethod: + +>>> class C: +... pass +>>> c=C() +>>> smcm=staticmethod(classmethod(f)) +>>> print smcm +<staticmethodclassmethod:f> +>>> C.smcm=smcm +>>> C.smcm(1) +1 +>>> c.smcm(1) +1 + + +Classmethod vs staticmethod + +Idem: staticmethod gains: + +>>> cmsm=classmethod(staticmethod(f)) +>>> print cmsm +<classmethodstaticmethod:f> +>>> C.cmsm=cmsm +>>> C.cmsm(1) +1 +>>> c.cmsm(1) +1 + + +Staticmethod vs tracedmethod works: + +>>> class C(object): +... "[Decorated]" +... def fact(n): +... "[staticmethod, tracedmethod]" +... if n==0: return 1 +... else: return n*C.fact(n-1) +>>> C=decorator(C) + +Called from the class: + +>>> C.fact(2) +Calling 'C.fact' with arguments 2(){} ... + Calling 'C.fact' with arguments 1(){} ... + Calling 'C.fact' with arguments (){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 + +Called from the instance: + +>>> C().fact(2) +Calling 'C.fact' with arguments 2(){} ... + Calling 'C.fact' with arguments 1(){} ... + Calling 'C.fact' with arguments (){} ... + 'C.fact' called with result: 1 + 'C.fact' called with result: 1 +'C.fact' called with result: 2 +2 diff --git a/pypers/pep318/bug.py b/pypers/pep318/bug.py new file mode 100755 index 0000000..f94a80f --- /dev/null +++ b/pypers/pep318/bug.py @@ -0,0 +1,50 @@ +# bug.py + +""" + +Test a bug of doctest in redirecting stdout. + +The module prnt is as follows: + +# prnt.py +import sys +f=sys.stdout + +def hello(): + print >> f, 'hello' + +Notice that + + print >> sys.stdout, 'hello' + +and + + print 'hello' + +would work instead! + +""" + +import doctest,__main__ + +def f1(): + """ + First docstring saying + + >>> import prnt + >>> prnt.hello() + hello + + """ + +def f2(): + """ + Second docstring saying + + >>> import prnt + >>> prnt.hello() + hello + + """ + +doctest.testmod(__main__) diff --git a/pypers/pep318/bug.txt b/pypers/pep318/bug.txt new file mode 100755 index 0000000..8713044 --- /dev/null +++ b/pypers/pep318/bug.txt @@ -0,0 +1,22 @@ +Attention: + +get -> _get not possible! Why ?! + + +Moreover: + + +>>> from tracing import E +>>> E.__dict__['__init__'].output=file('err','w') + + +>>> e=E() + 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 + + +>>> import chatty2 +>>> print file('file1.log').read().rstrip() +calling <chattymethod2:f> from <C instance> diff --git a/pypers/pep318/chatty.py b/pypers/pep318/chatty.py new file mode 100755 index 0000000..432c161 --- /dev/null +++ b/pypers/pep318/chatty.py @@ -0,0 +1,28 @@ +# chatty.py + +from customdec import decorated,chattymethod +chattymethod.logfile=file('file1.log','w') + +class C(object): + "[Decorated]" + def f(): + "[chattymethod, staticmethod]" + +C=decorated(C) +c=C() + +c.f() +C.f() + + + +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 + + diff --git a/pypers/pep318/chatty1.py b/pypers/pep318/chatty1.py new file mode 100755 index 0000000..649f6f2 --- /dev/null +++ b/pypers/pep318/chatty1.py @@ -0,0 +1,21 @@ +# chatty1.py + +import sys,customdec,decorators + +class chattymethod1(customdec.chattymethod): + def __init__(self,func): + super(chattymethod1,self).__init__(func) + self.logfile=self.logfile # class variable -> instance variable + +class D: + chattymethod1.logfile=sys.stdout + def f(self): pass + f=chattymethod1(f) + + chattymethod1.logfile=file('file.log','w') + def g(self): pass + g=chattymethod1(g) + +d=D() + + diff --git a/pypers/pep318/chatty2.py b/pypers/pep318/chatty2.py new file mode 100755 index 0000000..38be546 --- /dev/null +++ b/pypers/pep318/chatty2.py @@ -0,0 +1,28 @@ +# chatty2.py + +import customdec; customdec.enhance_classes() + +# sets the log files +log1=file('file1.log','w') +log2=file('file2.log','w') + +class C: + "[Decorated]" + 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/chatty3.py b/pypers/pep318/chatty3.py new file mode 100755 index 0000000..d1895d9 --- /dev/null +++ b/pypers/pep318/chatty3.py @@ -0,0 +1,13 @@ +# chatty3.py + +"[Decorated]" + +import decorators,customdec; decorators.decorated() + +class C: + def f(): + "[chattymethod2, staticmethod]" + +c=C() + + diff --git a/pypers/pep318/customdec.py b/pypers/pep318/customdec.py new file mode 100755 index 0000000..52a0d0d --- /dev/null +++ b/pypers/pep318/customdec.py @@ -0,0 +1,68 @@ +# 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(Decorated): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType) and name!='__str__': + # cannot trace __str__, since it is invoked by + # tracedmethod and would generate infinite recursion + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) # decorates the methods + + diff --git a/pypers/pep318/decorators.html b/pypers/pep318/decorators.html new file mode 100755 index 0000000..59d584f --- /dev/null +++ b/pypers/pep318/decorators.html @@ -0,0 +1,1534 @@ +<?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> +<meta name="author" content="Michele Simionato" /> +<meta name="date" content="September 2003" /> +<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> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr class="field"><th class="docinfo-name">Module:</th><td class="field-body">decorators</td> +</tr> +<tr><th class="docinfo-name">Version:</th> +<td>0.5</td></tr> +<tr><th class="docinfo-name">Author:</th> +<td>Michele Simionato</td></tr> +<tr class="field"><th class="docinfo-name">e-mail:</th><td class="field-body"><a class="reference" href="mailto:MicheleSimionato@libero.it">MicheleSimionato@libero.it</a></td> +</tr> +<tr class="field"><th class="docinfo-name">Licence:</th><td class="field-body">Python-like</td> +</tr> +<tr><th class="docinfo-name">Date:</th> +<td>September 2003</td></tr> +<tr class="field"><th class="docinfo-name">Disclaimer:</th><td class="field-body">This is experimental code. Use it at your own risk!</td> +</tr> +</tbody> +</table> +<div class="contents topic" id="contents"> +<p class="topic-title"><a name="contents">Contents</a></p> +<ul class="simple"> +<li><a class="reference" href="#using-decorators" id="id4" name="id4">Using decorators</a><ul> +<li><a class="reference" href="#basics" id="id5" name="id5">Basics</a></li> +<li><a class="reference" href="#decorating-methods" id="id6" name="id6">Decorating methods</a></li> +<li><a class="reference" href="#decorating-classes" id="id7" name="id7">Decorating classes</a></li> +<li><a class="reference" href="#adding-magic" id="id8" name="id8">Adding magic</a></li> +<li><a class="reference" href="#defining-method-decorators" id="id9" name="id9">Defining method decorators</a></li> +<li><a class="reference" href="#defining-class-decorators" id="id10" name="id10">Defining class decorators</a></li> +<li><a class="reference" href="#composing-decorators" id="id11" name="id11">Composing decorators</a></li> +<li><a class="reference" href="#diving-into-magic" id="id12" name="id12">Diving into magic</a></li> +<li><a class="reference" href="#advanced-usage" id="id13" name="id13">Advanced usage</a></li> +</ul> +</li> +<li><a class="reference" href="#the-implementation" id="id14" name="id14">The implementation</a><ul> +<li><a class="reference" href="#module-decorators" id="id15" name="id15">Module <tt class="literal"><span class="pre">decorators</span></tt></a><ul> +<li><a class="reference" href="#id3" id="id16" name="id16">Metaclasses</a></li> +<li><a class="reference" href="#classes" id="id17" name="id17">Classes</a></li> +<li><a class="reference" href="#functions" id="id18" name="id18">Functions</a></li> +</ul> +</li> +</ul> +</li> +</ul> +</div> +<div class="section" id="using-decorators"> +<h1><a class="toc-backref" href="#id4" name="using-decorators">Using decorators</a></h1> +<p>Having plenty of free time in these days, I have finished an old +project of mine, the implementation of <a class="reference" href="http://www.python.org/pep">PEP 318</a> in pure Python +(2.3).</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 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 (<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"> +<h2><a class="toc-backref" href="#id5" name="basics">Basics</a></h2> +<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 magic docstrings +and automatically converts methods with magic docstrings in +method 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 +and class decorators:</p> +<ul class="simple"> +<li><em>Method decorators</em> are classes taking a single function as input and +producing a descriptor object as output. <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). +A knowledge of descriptors <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a> 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.</li> +<li><em>Class decorators</em> 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.</li> +</ul> +<p>Notice that properties are not decorators according to my definitions, +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>. +Whereas the decorators concept could be generalized to the case of +multiple inputs, I don't see the need for such complication, so +properties are not implemented as method decorators. Moreover, I +think it is much better to use them trough a class decorator.</p> +<p>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 documentation 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> +<table class="footnote" frame="void" id="id2" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>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 ;).</td></tr> +</tbody> +</table> +</div> +<div class="section" id="decorating-methods"> +<h2><a class="toc-backref" href="#id6" name="decorating-methods">Decorating methods</a></h2> +<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 +sections.</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> +<pre class="doctest-block"> +>>> from example1 import * # for testing purposes +</pre> +<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 in the interpreter, since they have a re-defined +printing representation:</p> +<pre class="doctest-block"> +>>> print dec_identity +<staticmethod:identity> +>>> print dec_name +<classmethod:name> +</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"> +>>> dec_do_nothing is do_nothing # not converted +True +</pre> +<p>and it works as a standard method:</p> +<pre class="doctest-block"> +>>> o.do_nothing() # does nothing, correct +</pre> +<p>On the contrary, <tt class="literal"><span class="pre">dec_</span> <span class="pre">identity</span></tt> works as a staticmethod,</p> +<pre class="doctest-block"> +>>> OldStyle.identity(1) # called from the class +1 +>>> o.identity(1) # called from the instance +1 +</pre> +<p>whereas <tt class="literal"><span class="pre">dec_name</span></tt> works as a classmethod:</p> +<pre class="doctest-block"> +>>> NewStyle.name() # called from the class +'NewStyle' +>>> n.name() # called from the instance +'NewStyle' +</pre> +<p>Notice that, 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>It is possible to recognize method decorators since they provides +a couple of special attributes:</p> +<ul> +<li><p class="first"><tt class="literal"><span class="pre">__func__</span></tt>: returning the function from which they originated:</p> +<pre class="doctest-block"> +>>> assert dec_identity.__func__ is identity +>>> assert dec_name.__func__ is name +</pre> +</li> +<li><p class="first"><tt class="literal"><span class="pre">__klass__</span></tt>: returning the class where they where defined:</p> +<pre class="doctest-block"> +>>> dec_identity.__klass__ +<class 'safetype.?'> +</pre> +</li> +</ul> +<p>The question mark here means that the definition class is unknown. +The <tt class="literal"><span class="pre">__klass__</span></tt> attribute can be set by hand or automatically +with the trick explained in the next section.</p> +</div> +<div class="section" id="decorating-classes"> +<h2><a class="toc-backref" href="#id7" name="decorating-classes">Decorating classes</a></h2> +<p>The problem with the approach just described +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 decorating their +methods 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 by 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 + class Inner: # old style class + "[Decorated]" # required docstring + name=name + +C=decorated(C) # regenerates the class converting methods in decorators +c=C() + +#</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. +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. Moreover, it decorates inner classes, +if a suitable docstring is found, and it works recursively.</p> +<p>Here is the testing:</p> +<pre class="doctest-block"> +>>> from example2 import * +>>> assert C.identity(1) == 1 +>>> assert C.Inner.name() == 'Inner' +>>> assert c.identity(1) == 1 +>>> assert c.Inner.name() == 'Inner' +</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, +since the metaclass automagically converts the old style classes in a +new style one:</p> +<pre class="doctest-block"> +>>> type(C.Inner) # C.Inner is an instance of decorator.Decorated +<class 'decorators.Decorated'> +</pre> +<p>The enhancement provided by the metaclass includes a new default +printing representation for both the class</p> +<pre class="doctest-block"> +>>> print C # returns the name of D and of its metaclass +<class C[Decorated]> +</pre> +<p>and its instances:</p> +<pre class="doctest-block"> +>>> print c +<C instance> +</pre> +<p>On the other hand, if a custom printing representation is already +defined, the metaclass takes it without any change.</p> +<p>One can even forget the docstring in subclasses of decorated +classes, since metaclasses are inherited:</p> +<pre class="doctest-block"> +>>> class D(C): +... def name(cls): +... "[classmethod]" +... return cls.__name__ +>>> print D.name() +D +</pre> +<p>Decorating whole classes 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"> +>>> D.__dict__['name'].__klass__ # the class where 'name' is defined +<class 'D'> +</pre> +<p>This is useful for introspection and debugging purposes.</p> +</div> +<div class="section" id="adding-magic"> +<h2><a class="toc-backref" href="#id8" name="adding-magic">Adding magic</a></h2> +<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 you module. Then all methods in all classes 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"> +#<example3.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() + +#</example3.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 example3 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>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 example4 +>>> type(example4.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 dangers of +adding too much magic are discussed in the <a class="reference" href="#diving-into-magic">Diving into magic</a> section.</p> +</div> +<div class="section" id="defining-method-decorators"> +<h2><a class="toc-backref" href="#id9" name="defining-method-decorators">Defining method decorators</a></h2> +<p>The <tt class="literal"><span class="pre">decorators</span></tt> 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.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 +>>> 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 +becomes rapidly unreadable. Moreover, it complicates the implementation +without any real benefit, so the <tt class="literal"><span class="pre">decorators</span></tt> 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 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>.</p> +<p>Here is the testing:</p> +<blockquote> +<pre class="literal-block"> +#<chatty2.py> + +import customdec; customdec.enhance_classes() + +# sets the log files +log1=file('file1.log','w') +log2=file('file2.log','w') + +class C: + "[Decorated]" + 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 <tt class="literal"><span class="pre">file3.log</span></tt>:</p> +<pre class="doctest-block"> +>>> import example9 # creates file3.log +>>> 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="defining-class-decorators"> +<h2><a class="toc-backref" href="#id10" name="defining-class-decorators">Defining class decorators</a></h2> +<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 couple of +class decorator at work, the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>, which gives +to its instances the ability to interpret class level magic docstrings, and +its subclass <tt class="literal"><span class="pre">Decorated</span></tt>, which converts functions in method decorators +if suitable docstrings are found.</p> +<p>To define a custom class decorator is easy: one defines a custom metaclass +as usual, with the only difference of deriving from <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead +of deriving from <tt class="literal"><span class="pre">type</span></tt>.</p> +<p>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 +(meta-)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 'logged.D'> 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'> 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(Decorated): + def __init__(cls,n,b,d): + for name,func in d.iteritems(): + if isinstance(func,FunctionType) and name!='__str__': + # cannot trace __str__, since it is invoked by + # tracedmethod and would generate infinite recursion + func.__doc__="[tracedmethod] " + (func.__doc__ or '') + super(Traced,cls).__init__(n,b,d) # decorates the methods + +#</customdec.py> +</pre> +</blockquote> +<p>Here is an example of usage:</p> +<pre class="doctest-block"> +>>> class C(object): +... """[Traced] The class decorator adds the magic docstring +... '[tracedmethod]' to f1 and f2, which are then converted +... in method decorator objects.""" +... def f1(self): pass +... def f2(self): pass +... +>>> type(C) +<class 'customdec.Traced'> +>>> 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"> +#<example7.py> + +from example2 import identity,name +import inspect, decorators; decorators.enhance_classes("[Decorated]") + +class C1(object): # automagically converted to a decorated class + identity=identity + +class C2: # automagically converted to a decorated class + name=name + +c1=C1() # C1 instance +c2=C2() # C2 instance + +#</example7.py> +</pre> +</blockquote> +<p>The magic works both for new style classes such as <tt class="literal"><span class="pre">C1</span></tt></p> +<pre class="doctest-block"> +>>> from example7 import C1,c1 +>>> assert C1.identity(1) == 1 +>>> assert c1.identity(1) == 1 +</pre> +<p>and old style classes such as <tt class="literal"><span class="pre">C2</span></tt> by implicitly converting them +to new style classes:</p> +<pre class="doctest-block"> +>>> from example7 import C2,c2 +>>> assert C2.name() == 'C2' +>>> assert c2.name() == 'C2' +</pre> +</div> +<div class="section" id="composing-decorators"> +<h2><a class="toc-backref" href="#id11" name="composing-decorators">Composing decorators</a></h2> +<p>Decorators can be composed by using magic docstrings with comma-separated +decorator names. For instance, you can trace a classmethod:</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">C().fact</span></tt> 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"> +>>> type(C.__dict__['fact']) +<class 'safetype.classmethodtracedmethod'> +</pre> +<p>Notice that the order <em>does</em> 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 order +"[tracedmethod,staticmethod]" will work just fine.</p> +<p>It is possible to compose class decorators just as method decorators, +by using the docstring syntax: for instance we may create a class +which is both <tt class="literal"><span class="pre">Decorated</span></tt> and <tt class="literal"><span class="pre">Logged</span></tt> and by trivially writing</p> +<pre class="doctest-block"> +>>> class C: +... "[Decorated,Logged]" +... def f(): +... "[staticmethod]" +... return 'it works!' +<class C[DecoratedLogged]> created +>>> C().f() +'it works!' +</pre> +</div> +<div class="section" id="diving-into-magic"> +<h2><a class="toc-backref" href="#id12" name="diving-into-magic">Diving into magic</a></h2> +<p>If you never use metaclasses, this part can be safely skipped. On the +other hand, if you are a metaclass user, you <em>must</em> read this section +in order to keep your code working.</p> +<p>The <tt class="literal"><span class="pre">decorators</span></tt> module provide a <tt class="literal"><span class="pre">ClassDecorator</span></tt> metaclass which +converts a regular (both old style or new style) class in a class with +the ability to recognize magic docstrings. Under the hood, 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</p> +<blockquote> +<pre class="literal-block"> +import decorators +object=decorators.ClassDecorator(object) # decorates new style classes +__metaclass__= decorators.ClassDecorator # decorates old style classes +</pre> +</blockquote> +<p>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>The <tt class="literal"><span class="pre">decorators.enhance_classes("[SomeClassDec]")</span></tt> syntax looks at the +magic docstring and executes something like</p> +<blockquote> +<pre class="literal-block"> +import decorators +object=decorators.SomeClassDec(object) # decorates all new style classes +__metaclass__= decorators.SomeClassDec # decorates all old style classes +</pre> +</blockquote> +<p>The problem with the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntaxes is that it is +not 100% safe under metaclass conflicts. In order to explain the issue, +let me give an example.</p> +<p>Suppose we enhance the <tt class="literal"><span class="pre">object</span></tt> class in the interpreter namespace:</p> +<pre class="doctest-block"> +>>> import decorators; decorators.enhance_classes() +</pre> +<p>making it an instance of <tt class="literal"><span class="pre">ClassDecorator</span></tt>:</p> +<pre class="doctest-block"> +>>> object.__class__ +<class 'decorators.ClassDecorator'> +</pre> +<p>Now, if we naively create a custom metaclass <tt class="literal"><span class="pre">M</span></tt>:</p> +<pre class="doctest-block"> +>>> class M(type): +... "Some non-trivial code here..." +</pre> +<p>and we try to use it to enhance a new style class <tt class="literal"><span class="pre">NwithM</span></tt>, we will get a +conflict:</p> +<pre class="doctest-block"> +>>> class NwithM(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">NwithM</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">type</span></tt> metaclass from +my <tt class="literal"><span class="pre">safetype</span></tt> module just to avoid this kind of problems. If we +derive <tt class="literal"><span class="pre">M</span></tt> from <tt class="literal"><span class="pre">decorators.type</span></tt> (which is really <tt class="literal"><span class="pre">safetype.type</span></tt>) +the conflict is automagically avoided:</p> +<pre class="doctest-block"> +>>> class M(decorators.type): +... "This metaclass is conflict-proof" +>>> class NwithM(object): # it works! +... __metaclass__=M +</pre> +<p>The reason why the conflict is avoided is that the <tt class="literal"><span class="pre">safetype</span></tt> module +(which makes use of really <em>deep</em> metaclass magic) automatically +creates the composed class <tt class="literal"><span class="pre">MClassDecorator</span></tt> by multiply inheriting +from <tt class="literal"><span class="pre">M</span></tt> and from <tt class="literal"><span class="pre">ClassDecorator</span></tt>. Then, <tt class="literal"><span class="pre">NwithM</span></tt> can be safely +created as an instance of <tt class="literal"><span class="pre">safetype.MClassDecorator</span></tt>:</p> +<pre class="doctest-block"> +>>> type(NwithM) +<class 'safetype.MClassDecorator'> +</pre> +<p>The situation with old style classes is worse since +apparently</p> +<pre class="doctest-block"> +>>> class OwithM: # old style class with metaclass M +... __metaclass__=M +... def sm(): +... "[staticmethod]" +</pre> +<p>gives no error, but actually <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">OwithM</span></tt> will not recognize magic docstrings:</p> +<pre class="doctest-block"> +>>> type(OwithM) +<class 'M'> +>>> OwithM.sm() +Traceback (most recent call last): + ... +TypeError: unbound method sm() must be called with OwithM instance as first argument (got nothing instead) +</pre> +<p>The simpler solution is to convert <tt class="literal"><span class="pre">OwithM</span></tt> in a new style class:</p> +<pre class="doctest-block"> +>>> class NwithM(OwithM,object): +... __metaclass__=M +... def sm(): +... "[staticmethod]" +>>> type(NwithM) +<class 'safetype.MClassDecorator'> +</pre> +<p>Now <tt class="literal"><span class="pre">NwithM</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">NwithM</span></tt> +subclasses with a "[Decorated]" docstring will be decorated:</p> +<pre class="doctest-block"> +>>> class E(NwithM): +... "[Decorated]" +... def cm(cls): +... "[classmethod]" +... print '.cm() called from',cls +</pre> +<pre class="doctest-block"> +>>> E.cm() # it works +.cm() called from <class E[MClassDecoratorDecorated]> +</pre> +<p>Notice that <tt class="literal"><span class="pre">sm</span></tt> was defined in <tt class="literal"><span class="pre">NwithM</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 that <tt class="literal"><span class="pre">sm</span></tt> is an unbound method and not a +static method.</p> +<p>It is possible to go even deeper in <strong>black</strong> magic, and to decorate all +the new style classes in <em>all</em> modules, by decorating the +<tt class="literal"><span class="pre">__builtin__.object</span></tt>. Still, naively redefining the <tt class="literal"><span class="pre">__builtin__object</span></tt> +class is risky, since it will induce metaclass conflicts in other modules +using metaclasses. In other words, 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 <tt class="literal"><span class="pre">safetype.type</span></tt> as base metaclass. +If you really enjoy black magic, you may solve the problem by +redefining the <tt class="literal"><span class="pre">__builtin__.type</span></tt> as <tt class="literal"><span class="pre">safetype.type</span></tt>. +The <tt class="literal"><span class="pre">decorators</span></tt> module does not go so deep in dark magic, but still +there are cases where you may want to do it. In these cases you must be +explicit and redefine the builtins by hand, without help from the +module. For instance, one of my original motivation for the usage +of metaclasses/class decorators was to use them for debugging purposes. +For instance, I wanted to trace the execution of methods in +complicate inheritance hierarchies, <em>without changing the source code</em>. +For simple situations, i.e. in absence of inheritance and of pre-existing +metaclasses, the function <tt class="literal"><span class="pre">import_with_metaclass</span></tt> discussed in the +<a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta.html">first paper on metaclasses</a> written in collaboration with David Mertz, works +fine but in general the implementation of a working <tt class="literal"><span class="pre">import_with_metaclass</span></tt> +is cumbersome. For debugging purposes, the quick and dirty way can be a +good enough way, so let me show how we can redefine the builtins <tt class="literal"><span class="pre">object</span></tt> and +<tt class="literal"><span class="pre">type</span></tt> <em>before</em> importing the module, in such a way to enhance <em>all</em> +its classes with tracing capabilities.</p> +<p>Let me start from a module using an unsafe metaclass <tt class="literal"><span class="pre">M</span></tt>, such that it +cannot be traced trivially by decorating its classes <em>after</em> the import; +moreover there is an inheritance hierarchy, such that it cannot be +traced correctly by naively decorating all the classes one after the +other (unless one modifies the original source code, but this forbidden +by the rules of the game ;)</p> +<blockquote> +<pre class="literal-block"> +#<tracing.py> + +""" +This is a pre-existing module not using decorators but using multiple +inheritance and unsafe metaclasses. We want to trace it for debugging +purposes. +""" + +class M(type): + "There should be some non-trivial code here" + +class B(object): + def __init__(self): + super(B,self).__init__() + +class D(object): + __metaclass__=M + def __init__(self): + super(D,self).__init__() + +class E(B,D): + def __init__(self): + super(E,self).__init__() + + #</tracing.py> +</pre> +</blockquote> +<p>Everything works is we modify the builtins before importing the module:</p> +<pre class="doctest-block"> +>>> import __builtin__ +>>> __object__=__builtin__.object # the original 'object' class +>>> __builtin__.object=customdec.Traced('tracedobject',(),{}) +>>> __builtin__.type=customdec.type # a safe 'type' class +</pre> +<p>Now, when we import the module, all the classes <tt class="literal"><span class="pre">M</span></tt>, <tt class="literal"><span class="pre">B</span></tt>, <tt class="literal"><span class="pre">D</span></tt> and <tt class="literal"><span class="pre">E</span></tt> +will be created starting for the tricked builtins, so they will be traced +and safe under conflicts. For instance, let me show that <tt class="literal"><span class="pre">E</span></tt> is created +with a conflict safe <tt class="literal"><span class="pre">MTraced</span></tt> metaclass:</p> +<pre class="doctest-block"> +>>> from tracing import E +>>> print E +<class E[MTraced]> +</pre> +<p>This shows that the <tt class="literal"><span class="pre">__init__</span></tt> methods are traced indeed and shows the +Method Resolution Order:</p> +<pre class="doctest-block"> +>>> 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> +<p>This works, indeed. At the end, one should not forget to restore +the standard builtins, otherwise it will trace <em>all</em> the classes +created thereafter.</p> +<pre class="doctest-block"> +>>> __builtin__.object=__object__ # restore the __builtin__ +>>> __builtin__.type=decorators.__type__ # restore the __builtin__ +</pre> +<p>At the end, I will notice that it is possible to solve the problem more +nicely, without redefining the builtins, but I will discuss the solution +elsewhere ;)</p> +</div> +<div class="section" id="advanced-usage"> +<h2><a class="toc-backref" href="#id13" name="advanced-usage">Advanced usage</a></h2> +<p>Whereas the average programmer is expected to use the <tt class="literal"><span class="pre">decorated()</span></tt> +and <tt class="literal"><span class="pre">enhance_classes()</span></tt> functions only, the <tt class="literal"><span class="pre">decorators</span></tt> module provides +facilities which may be useful to the advanced programmer.</p> +<p>The simplest is an utility function which retrieves the list of +recognized decorators, <tt class="literal"><span class="pre">decorators.getdec()</span></tt>. The precise syntax is +<tt class="literal"><span class="pre">decorators.getdec(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.getdec('[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.getdec('[ClassDecorator]')</span></tt> +gives the list of the known class decorators. The utility of the function +is that it also returns decorators that have been automagically created:</p> +<pre class="doctest-block"> +>>> decorators.getdec("[classmethodtracedmethod]") +[<class 'safetype.classmethodtracedmethod'>] +</pre> +<p>It is even possible to use the comma notation:</p> +<pre class="doctest-block"> +>>> decorators.getdec("[classmethod,tracedmethod]") +[<class 'safetype.classmethodtracedmethod'>] +</pre> +<p>If you mispell a decorator name you get an helpful error message:</p> +<pre class="doctest-block"> +>>> class E: +... "[Decorated]" +... def f(): +... "[staticmeth]" +Traceback (most recent call last): + .. a cryptic traceback here .. +UnknownDecoratorError: staticmeth +</pre> +<p>By design, the <tt class="literal"><span class="pre">decorated</span></tt> function is quite limited, and does not +decorate functions without magic docstrings. This is safe and also convenient +for internal usage of the <tt class="literal"><span class="pre">decorated</span></tt> function in the <tt class="literal"><span class="pre">Decorated</span></tt> +metaclass. Nevertheless, advanced users may want to have the ability +of decorating functions by hand, without invoking <cite>decorators.decorated()`</cite> +and magic docstrings. This is possible, by calling the decorator directly. +For instance, here is how to convert a lambda function in a staticmethod:</p> +<pre class="doctest-block"> +>>> do_nothing=decorators.staticmethod(lambda:None) +>>> print do_nothing # ``do_nothing`` is a static method +<staticmethod:<lambda>> +</pre> +<p>The most convenient usage of this feature is in the composition of decorators. +For instance, we may compose the just defined <tt class="literal"><span class="pre">staticmethod</span></tt> with a +<tt class="literal"><span class="pre">chattymethod2</span></tt>:</p> +<pre class="doctest-block"> +>>> chattystatic=customdec.chattymethod2(do_nothing) +>>> print chattystatic +<chattymethod2staticmethod:<lambda>> +</pre> +<p>The syntax</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">safetype</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 that everything works:</p> +<pre class="doctest-block"> +>>> class B: +... chattystatic=chattystatic +>>> B.chattystatic() +calling <chattymethod2staticmethod:<lambda>> from <class 'B'> +</pre> +<p>Notice that <tt class="literal"><span class="pre">chattystatic</span></tt> does not know the class where it +is defined:</p> +<pre class="doctest-block"> +>>> chattystatic.__klass__ +<class 'safetype.?'> +</pre> +<p>unless the <tt class="literal"><span class="pre">__klass__</span></tt> attribute is explicitly set.</p> +<p>This is the hierarchy:</p> +<pre class="doctest-block"> +>>> for C in type(chattystatic).mro(): print C +... +<class 'safetype.chattymethod2staticmethod'> +<class 'customdec.chattymethod2'> +<class 'customdec.chattymethod'> +<class 'decorators.staticmethod'> +<class 'decorators.MethodDecorator'> +<class 'decorators.Decorator'> +<type 'object'> +</pre> +<p>One can also compose classes by hand, by using class decorators:</p> +<pre class="doctest-block"> +>>> class C: +... "[Logged]" +... def f(): "[staticmethod]" +... +<class 'C'> created +>>> C=customdec.Traced(C) +<class C[TracedLogged]> created +</pre> +<p>The <tt class="literal"><span class="pre">decorators.enhance_classes(<classdecorator>)</span></tt> syntax performs +the composition automagically:</p> +<blockquote> +<pre class="literal-block"> +#<example8.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 + +#</example8.py> +</pre> +</blockquote> +<p>In this example the 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 example8 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> +</div> +</div> +<div class="section" id="the-implementation"> +<h1><a class="toc-backref" href="#id14" 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 (less than 150 lines if you skip comments and +docstrings) but far from being trivial, since it is based on extensive +usage of metaclass wizardry. Moreover, +it invokes the <tt class="literal"><span class="pre">safetype</span></tt> module to solve metaclass conflicts, and +<tt class="literal"><span class="pre">safetype</span></tt> contains 50 lines of <em>really</em> deep metaclass wizardry.</p> +<blockquote> +<div class="figure"> +<p><img alt="decorators.png" src="decorators.png" /></p> +</div> +</blockquote> +<p>The main class-metaclass hierarchy is represented in the figure, where +boxes denote classes and ovals denote metaclasses; instantiation is +denoted by dashed green lines whereas inheritance is denoted by continuous +blue lines.</p> +<p>For instance, this is the Method Resolution Order for the <tt class="literal"><span class="pre">Decorated</span></tt> +metaclass:</p> +<pre class="doctest-block"> +>>> for i,c in enumerate(decorators.Decorated.__mro__): +... print i,c,"[%s]" % type(c).__name__ +0 <class 'decorators.Decorated'> [MetaDecorator] +1 <class 'decorators.ClassDecorator'> [MetaDecorator] +2 <class 'safetype.safetype'> [type] +3 <type 'type'> [type] +4 <class 'decorators.Decorator'> [MetaDecorator] +5 <type 'object'> [type] +</pre> +<p><tt class="literal"><span class="pre">Decorator</span></tt> is the mother of all decorators. Its main purpose it to +provide the <tt class="literal"><span class="pre">MetaDecorator</span></tt> +metaclass to all decorators.</p> +<p>The <tt class="literal"><span class="pre">safetype</span></tt> metaclass, imported from the <tt class="literal"><span class="pre">safetype</span></tt> module, +ensures that class decorators can generate classes without incurring +in conflicts.</p> +<p>Since class decorators are metaclasses, +<tt class="literal"><span class="pre">MetaDecorator</span></tt> is actually a meta-metaclass with respect to +instances of decorated classes. For this reason if</p> +<pre class="doctest-block"> +>>> C=decorators.ClassDecorator('C',(),{}) +</pre> +<p>then</p> +<pre class="doctest-block"> +>>> C.__class__ +<class 'decorators.ClassDecorator'> +</pre> +<p>but</p> +<pre class="doctest-block"> +>>> C.__metaclass__ +<class 'decorators.MetaDecorator'> +</pre> +<p>since the <tt class="literal"><span class="pre">C.__metaclass__</span></tt> attribute is inherited from <tt class="literal"><span class="pre">Decorator</span></tt>.</p> +<p>On the other hand, <tt class="literal"><span class="pre">MetaDecorator</span></tt> is a simple metaclass with +respect to instances of decorated methods.</p> +<p>The implementation is relatively smart. Consider for instance the case of +the <tt class="literal"><span class="pre">logged</span></tt> 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 <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 a +<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 this magic is in the <tt class="literal"><span class="pre">safetype.remove_redundant(*classes)</span></tt> function.</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. Probably, they do not 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 involves +~200 tests, most of them 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 fix it or document it.</p> +<!-- References: --> +<div class="section" id="module-decorators"> +<h2><a class="toc-backref" href="#id15" name="module-decorators">Module <tt class="literal"><span class="pre">decorators</span></tt></a></h2> +<p>A module to implement pep318 (decorator syntax) via magic doctrings. +For the full documentation see +<a class="reference" href="http://www.phyast.pitt.edu/~micheles/python/decorators.html">http://www.phyast.pitt.edu/~micheles/python/decorators.html</a> .</p> +<div class="section" id="id3"> +<h3><a class="toc-backref" href="#id16" name="id3">Metaclasses</a></h3> +<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">ClassDecorator(type,Decorator):</span></tt></p> +<blockquote> +Metaclass callable with one or three arguments, having its calling +syntax redefined by <tt class="literal"><span class="pre">MetaDecorator</span></tt>.</blockquote> +<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">Decorated(ClassDecorator):</span></tt></p> +<blockquote> +<p>Metaclass which decorates the methods of its instances according +to the docstrings. It redefines <tt class="literal"><span class="pre">__str__</span></tt> on +its instances and the default <tt class="literal"><span class="pre">__str__</span></tt> on the instances of its +instances.</p> +<p><tt class="literal"><span class="pre">__str__(cls):</span></tt></p> +<blockquote> +Redefine the printing representation of classes</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">safetype(type):</span></tt></p> +<blockquote> +Overrides the <tt class="literal"><span class="pre">__new__</span></tt> method of the <tt class="literal"><span class="pre">type</span></tt> metaclass, making the +generation of classes conflict-proof.</blockquote> +<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">MetaDecorator(type):</span></tt></p> +<blockquote> +<p>Metaclass inducing a certain amount of magic on decorators:</p> +<ol class="arabic simple"> +<li>Each time a decorator is defined in any module, it is stored in +MetaDecorator.dic and MetaDecorator.ls.</li> +<li>If the (method) decorator has a 'get' method, a '__get__' method +is automagically created as an alias to 'get'.</li> +<li>Decorators calls are dispatched to the decorator _call_ +classmethod.</li> +</ol> +<p><tt class="literal"><span class="pre">__call__(dec,*args):</span></tt></p> +<blockquote> +This is the heart of the module. Infers the correct decorator class +from <tt class="literal"><span class="pre">dec</span></tt> and the docstring and creates the correct decorator object. +Returns the original object if <tt class="literal"><span class="pre">dec</span></tt> is the trivial <tt class="literal"><span class="pre">Decorator</span></tt> +class and no docstring is found.</blockquote> +<p><tt class="literal"><span class="pre">__init__(dec,*args):</span></tt></p> +<blockquote> +Update the metaclass attributes <tt class="literal"><span class="pre">dic</span></tt> and <tt class="literal"><span class="pre">ls</span></tt>; +alias <tt class="literal"><span class="pre">get</span></tt> to <tt class="literal"><span class="pre">__get__</span></tt>.</blockquote> +</blockquote> +</div> +<div class="section" id="classes"> +<h3><a class="toc-backref" href="#id17" name="classes">Classes</a></h3> +<p><tt class="literal"><span class="pre">class</span> <span class="pre">UnknownDecoratorError(Exception):</span></tt></p> +<blockquote> +The name says it all.</blockquote> +<p><tt class="literal"><span class="pre">class</span> <span class="pre">Decorator(object):</span></tt></p> +<blockquote> +Instance of MetaDecorator and mothers of all decorators. When called +in the form <tt class="literal"><span class="pre">Decorator(obj)</span></tt> with obj having a magic docstring, it returns +a decorated object; otherwise it returns the original object.</blockquote> +<p><tt class="literal"><span class="pre">class</span> <span class="pre">staticmethod(MethodDecorator):</span></tt></p> +<blockquote> +Decorator, converts a function in a staticmethod</blockquote> +<p><tt class="literal"><span class="pre">class</span> <span class="pre">MethodDecorator(Decorator):</span></tt></p> +<blockquote> +<p>Descriptor class callable with a function or a descriptor object +as argument. It defines a default printing representation on method +decorators objects and a default <tt class="literal"><span class="pre">get</span></tt> method. All the rest is +provided by the metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>.</p> +<p><tt class="literal"><span class="pre">get(self,obj,cls=None):</span></tt></p> +<blockquote> +aliased to __get__ by the metaclass, to be overridden</blockquote> +<p><tt class="literal"><span class="pre">__str__(self):</span></tt></p> +<blockquote> +Printing representation of kind <decoratorclass:functionname></blockquote> +<p><tt class="literal"><span class="pre">__init__(self,objfunc):</span></tt></p> +<blockquote> +objfunc is a decorator object or a function</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">class</span> <span class="pre">classmethod(MethodDecorator):</span></tt></p> +<blockquote> +Decorator, converts a function in a classmethod</blockquote> +</div> +<div class="section" id="functions"> +<h3><a class="toc-backref" href="#id18" name="functions">Functions</a></h3> +<p><tt class="literal"><span class="pre">enhance_classes(magicstring='[ClassDecorator]'):</span></tt></p> +<blockquote> +Enhances all the classes in the caller module with a metaclass +built from the given magicstring.</blockquote> +<p><tt class="literal"><span class="pre">remove_redundant(bases):</span></tt></p> +<blockquote> +<p>Returns a tuple of non-redundant base classes. +Given a sequence of base classes, a class is redundant if it is duplicated +or if it is implied by the others, i.e. it is an ancestor of at least one +of the other classes. For instance, if <tt class="literal"><span class="pre">C</span></tt> is derived from <tt class="literal"><span class="pre">B</span></tt>, in the +sequence <tt class="literal"><span class="pre">C,B</span></tt> the class <tt class="literal"><span class="pre">B</span></tt> is redundant, since all its features are +already provided by <tt class="literal"><span class="pre">C</span></tt>. Therefore <tt class="literal"><span class="pre">B</span></tt> +is removed and <tt class="literal"><span class="pre">remove_redundant</span></tt> returns the tuple <tt class="literal"><span class="pre">(C,)</span></tt>:</p> +<pre class="doctest-block"> +>>> class B(object): pass +... +>>> class C(B): pass +... +>>> import safetype; safetype.remove_redundant([C,B]) +(<class 'C'>,) +</pre> +</blockquote> +<p><tt class="literal"><span class="pre">compose(dclasses,*defaultclasses):</span></tt></p> +<blockquote> +Retrieves or creates a decorator for a tuple of decorators. If +defaults decorators are given, they get the precedence. It removes +redundant classes.</blockquote> +<p><tt class="literal"><span class="pre">instance_str(self):</span></tt></p> +<blockquote> +Redefines the printing representation of simple objects</blockquote> +<p><tt class="literal"><span class="pre">getdec(magicstring="[Decorator]"):</span></tt></p> +<blockquote> +Given a magicstring describing a valid decorator name, returns the list +of its subclasses. By default returns the full list of decorators.</blockquote> +<p><tt class="literal"><span class="pre">decorate(objdict):</span></tt></p> +<blockquote> +Takes an object with a dictionary and decorates all its functions +and classes according to their docstrings.</blockquote> +<p><tt class="literal"><span class="pre">decorators_in(docstring):</span></tt></p> +<blockquote> +Takes a docstring and returns a (possibly empty) tuple of decorator +classes.</blockquote> +</div> +</div> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="decorators.txt">View document source</a>. +Generated on: 2003-09-30 10:07 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/decorators.py b/pypers/pep318/decorators.py new file mode 100755 index 0000000..738b403 --- /dev/null +++ b/pypers/pep318/decorators.py @@ -0,0 +1,184 @@ +# decorators.py + +""" +A module to implement pep318 (decorator syntax) via magic doctrings. +For the full documentation see +http://www.phyast.pitt.edu/~micheles/python/decorators.html . +""" + +import sys, re, inspect +from types import FunctionType,ClassType +from safetype import remove_redundant,__type__,safetype as type + +#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 class is defined in any module, it is stored in + MetaDecorator.dic and MetaDecorator.ls; + 2. if the decorator class 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. + The net effect is that the call decoratorclass(...) returns an + instance of a *subclass* of decoratorclass. + """ + + 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): + """This is the heart of the module. Infers the correct decorator class + from ``dec`` and the docstring and creates the correct decorator + object. Returns the original object if ``dec`` is the trivial + ``Decorator`` class and no docstring is found.""" + nargs=len(args) + if nargs==1: # simple call of kind dec(obj) + obj=args[0] + if not isinstance( # not method decorator, function or class + obj,(MethodDecorator,FunctionType,ClassType,__type__)): + return # do nothing + elif isinstance(obj,decorator): # compose with obj.__class__ too + dec=compose(decorators_in(obj.__doc__), dec, obj.__class__) + else: # compose with dec only + dec=compose(decorators_in(obj.__doc__), dec) + elif nargs==3: # class decorator called with three arguments + dec=compose(decorators_in(args[2].get('__doc__')),dec) + return dec._call_(*args) # dispatch to the correct _call_ classmethod + +def compose(dclasses,*defaultclasses): + """Retrieves or creates a decorator for a tuple of decorators. If + defaults decorators are given, they get the precedence. It removes + redundant classes.""" + dclasses=remove_redundant(defaultclasses+dclasses) + decname=''.join([d.__name__ for d in dclasses]) + dec=MetaDecorator.dic.get(decname) + if not dec: dec=type(decname,dclasses,{}) + return dec + +def decorators_in(docstring): + """Takes a docstring and returns a (possibly empty) tuple of decorator + classes.""" + match=MAGICDOC.match(docstring or '') + if not match: return () + decnames=map(str.strip,match.group(1).split(',')) # get the names + try: dclasses=[MetaDecorator.dic[n] for n in decnames] # get the decorators + except KeyError: raise UnknownDecoratorError(n) + return tuple(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(): # works for classes too + #if getattr(obj,'__doc__',None): # non-empty docstring + dec_obj=decorator(obj) + if dec_obj: + setattr(dec_obj,'__klass__',objdict) + setattr(objdict,name,dec_obj) + +def getdec(magicstring="[decorator]"): + """Given a magicstring describing a valid decorator name, returns the list + of its subclasses. By default returns the full list of decorators.""" + dec=compose(decorators_in(magicstring)) + isdecorator=lambda x: issubclass(x,dec) + return filter(isdecorator,MetaDecorator.ls) + +def instance_str(self): + "Redefines the printing representation of simple objects" + return "<%s instance>" % self.__class__.__name__ + +####################### 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 a decorated object; 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. It defines a default printing representation on method + decorators objects and a default ``get`` method. All the rest is + provided by the metaclass ``MetaDecorator``. + """ + __klass__=type('?',(),{}) # dummy definition 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__ by the metaclass, 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 ``MetaDecorator``.""" + def _call_(dec,*args): + "Returns a decorated class" + a=args[0] # first argument; must be a string or a class + if inspect.isclass(a): args=a.__name__,a.__bases__,a.__dict__.copy() + return type.__call__(dec,*args) # calls __new__ and __init__ + _call_=classmethod(_call_) + +class Decorated(ClassDecorator): + """Metaclass which decorates the methods of its instances according + to the docstrings. It redefines ``__str__`` on + its instances and the default ``__str__`` on the instances of its + instances.""" + def __init__(cls,name,bases,dic): + super(Decorated,cls).__init__(name,bases,dic) + if cls.__str__ is object.__str__: cls.__str__=instance_str + # redefine default __str__ + decorate(cls) + def __str__(cls): + "Redefine the printing representation of classes" + return '<class %s[%s]>' % (cls.__name__,cls.__class__.__name__) + +#################### 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) + +#################### functions of the API ################################# + +def enhance_classes(magicstring='[ClassDecorator]'): + """Enhances all the classes in the caller module with a metaclass + built from the given magicstring.""" + callerglobals=sys._getframe(1).f_globals + dec=compose(decorators_in(magicstring)) + callerglobals['__metaclass__']=dec + callerglobals['object']=dec('object',(),{}) diff --git a/pypers/pep318/decorators.tex b/pypers/pep318/decorators.tex new file mode 100755 index 0000000..7dbc220 --- /dev/null +++ b/pypers/pep318/decorators.tex @@ -0,0 +1,1733 @@ +\documentclass[11pt,english]{article} +\usepackage{babel} +\usepackage{shortvrb} +\usepackage[latin1]{inputenc} +\usepackage{tabularx} +\usepackage{longtable} +\setlength{\extrarowheight}{2pt} +\usepackage{amsmath} +\usepackage{graphicx} +\usepackage{color} +\usepackage{multirow} +\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} +\usepackage[a4paper,margin=2cm]{geometry} +%% generator Docutils: http://docutils.sourceforge.net/ +\newlength{\admonitionwidth} +\setlength{\admonitionwidth}{0.9\textwidth} +\newlength{\docinfowidth} +\setlength{\docinfowidth}{0.9\textwidth} +\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newenvironment{optionlist}[1] +{\begin{list}{} + {\setlength{\labelwidth}{#1} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\optionlistlabel}} +}{\end{list}} +% begin: floats for footnotes tweaking. +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50} +% end floats for footnotes +% some commands, that could be overwritten in the style file. +\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}} +% end of "some commands" +\input{/mnt/exp/MyDocs/pypers/style.tex} +\title{Implementing PEP 318 (decorators)} +\author{} +\date{} +\hypersetup{ +pdftitle={Implementing PEP 318 (decorators)}, +pdfauthor={Michele Simionato} +} +\raggedbottom +\begin{document} +\maketitle + +%___________________________________________________________________________ +\begin{center} +\begin{tabularx}{\docinfowidth}{lX} +\textbf{Module}: & + decorators \\ +\textbf{Version}: & + 0.5 \\ +\textbf{Author}: & + Michele Simionato \\ +\textbf{e-mail}: & + MicheleSimionato@libero.it \\ +\textbf{Licence}: & + Python-like \\ +\textbf{Date}: & + September 2003 \\ +\textbf{Disclaimer}: & + This is experimental code. Use it at your own risk! \\ +\end{tabularx} +\end{center} + + + + +\hypertarget{contents}{}\subsection*{~\hfill Contents\hfill ~} +\pdfbookmark[0]{Contents}{contents} +\begin{list}{}{} +\item \href{#using-decorators}{Using decorators} +\begin{list}{}{} +\item \href{#basics}{Basics} + +\item \href{#decorating-methods}{Decorating methods} + +\item \href{#decorating-classes}{Decorating classes} + +\item \href{#adding-magic}{Adding magic} + +\item \href{#defining-method-decorators}{Defining method decorators} + +\item \href{#defining-class-decorators}{Defining class decorators} + +\item \href{#composing-decorators}{Composing decorators} + +\item \href{#diving-into-magic}{Diving into magic} + +\item \href{#advanced-usage}{Advanced usage} + +\end{list} + +\item \href{#the-implementation}{The implementation} +\begin{list}{}{} +\item \href{#module-decorators}{Module \texttt{decorators}} +\begin{list}{}{} +\item \href{#id3}{Metaclasses} + +\item \href{#classes}{Classes} + +\item \href{#functions}{Functions} + +\end{list} + +\end{list} + +\end{list} + + + +%___________________________________________________________________________ + +\hypertarget{using-decorators}{} +\section*{Using decorators} +\pdfbookmark[0]{Using decorators}{using-decorators} + +Having plenty of free time in these days, I have finished an old +project of mine, the implementation of \href{http://www.python.org/pep}{PEP 318} in pure Python +(2.3). + +Here is the rationale: +\begin{itemize} +\item +some kind of decorator syntax is scheduled to go in Python 2.4, +therefore it is interesting to play with the concept; + +\item +it is nice to play with decorators now, without having to +wait for one year or so; + +\item +it is much easier to experiment with a pure Python implementation +than with a C implementation; + +\item +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 (\href{http://users.rcn.com/python/download/Descriptor.htm}{descriptors}, \href{http://www-106.ibm.com/developerworks/library/l-pymeta2.html}{metaclasses}, +\href{http://www.python.org/2.3/descrintro.html}{cooperative methods}, etc.) + +\end{itemize} + + +%___________________________________________________________________________ + +\hypertarget{basics}{} +\subsection*{Basics} +\pdfbookmark[1]{Basics}{basics} + +PEP 318 has the goal of providing a nice syntactic sugar for expressions like +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{def~identity(x):}\\ +\mbox{~~~~return~x}\\ +\mbox{identity=staticmethod(identity)} +\end{flushleft}\end{ttfamily} +\end{quote} + +or +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{def~name(cls):}\\ +\mbox{~~~return~cls.{\_}{\_}name{\_}{\_}}\\ +\mbox{name=classmethod(name)} +\end{flushleft}\end{ttfamily} +\end{quote} + +which are pretty verbose. It is clear that having new syntax (as +for instance the proposed square bracket notation) +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{def~identity(x)[staticmethod]:}\\ +\mbox{~~~~return~x}\\ +\mbox{}\\ +\mbox{def~name(cls)[classmethod]:}\\ +\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}} +\end{flushleft}\end{ttfamily} +\end{quote} + +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: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{def~identity(x):}\\ +\mbox{~~~~"[staticmethod]"}\\ +\mbox{~~~~return~x}\\ +\mbox{}\\ +\mbox{def~name(cls):}\\ +\mbox{~~~~"[classmethod]"}\\ +\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}} +\end{flushleft}\end{ttfamily} +\end{quote} + +The implementation is able to recognize such magic docstrings +and automatically converts methods with magic docstrings in +method decorators. + +Decorators are nothing else than a sophisticated kind of wrappers. +The \texttt{decorators} module provides support both for method decorators +and class decorators: +\begin{itemize} +\item +\emph{Method decorators} are classes taking a single function as input and +producing a descriptor object as output. \texttt{staticmethod} and +\texttt{classmethod} are two examples of already existing +method decorators (actually my implementation rewrites them). +A knowledge of descriptors [\hyperlink{id2}{1}] is not needed in order to use the \texttt{decorator} +module; however it is welcomed for advanced users wanting to implement +custom method decorators. + +\item +\emph{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. + +\end{itemize} + +Notice that properties are not decorators according to my definitions, +since they take four functions as input, \texttt{get, set, del{\_}} and \texttt{doc}. +Whereas the decorators concept could be generalized to the case of +multiple inputs, I don't see the need for such complication, so +properties are not implemented as method decorators. Moreover, I +think it is much better to use them trough a class decorator. + +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 documentation 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. +\begin{figure}[b]\hypertarget{id2}[1] +Descriptors are objects with a \texttt{{\_}{\_}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 ;). +\end{figure} + + +%___________________________________________________________________________ + +\hypertarget{decorating-methods}{} +\subsection*{Decorating methods} +\pdfbookmark[1]{Decorating methods}{decorating-methods} + +Before talking about the implementation details, I will show +how the \texttt{decorators} module works in practice. The simplest and safest +usage is by means of the \texttt{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 \texttt{decorators.decorated()} +is simple but verbose, so magic shortcuts will be discussed in the next +sections. + +Here, let me give an example, showing that method decorators work both for +\href{http://www.python.org/2.3/descrintro.html}{new style classes and old style classes}: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example1.py{$>$}}\\ +\mbox{}\\ +\mbox{import~decorators}\\ +\mbox{}\\ +\mbox{def~do{\_}nothing(self):}\\ +\mbox{~~~"No~magic~docstring~here"}\\ +\mbox{dec{\_}do{\_}nothing=decorators.decorated(do{\_}nothing)}\\ +\mbox{}\\ +\mbox{def~identity(x):}\\ +\mbox{~~~~"[staticmethod]"}\\ +\mbox{~~~~return~x}\\ +\mbox{dec{\_}identity=decorators.decorated(identity)~}\\ +\mbox{}\\ +\mbox{def~name(cls):}\\ +\mbox{~~~~"[classmethod]"}\\ +\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}}\\ +\mbox{dec{\_}name=decorators.decorated(name)}\\ +\mbox{}\\ +\mbox{class~OldStyle:}\\ +\mbox{~~~~do{\_}nothing=dec{\_}do{\_}nothing}\\ +\mbox{~~~~identity=dec{\_}identity}\\ +\mbox{}\\ +\mbox{class~NewStyle(object):}\\ +\mbox{~~~~name=dec{\_}name}\\ +\mbox{}\\ +\mbox{o=OldStyle()~{\#}~creates~an~old~style~instance}\\ +\mbox{n=NewStyle()~{\#}~creates~a~new~style~instance}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example1.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} +\begin{verbatim}>>> from example1 import * # for testing purposes\end{verbatim} + +In this example, both \texttt{dec{\_}identity} and \texttt{dec{\_}name} are decorator objects, +i.e. descriptors modifiying the attribute access. It is easy to recognize +decorators objects in the interpreter, since they have a re-defined +printing representation: +\begin{verbatim}>>> print dec_identity +<staticmethod:identity> +>>> print dec_name +<classmethod:name>\end{verbatim} + +On the other hand, \texttt{do{\_}nothing} does not have a magic +docstring, therefore it is not converted to a decorator object; +actually it is \emph{exactly} the original function +\begin{verbatim}>>> dec_do_nothing is do_nothing # not converted +True\end{verbatim} + +and it works as a standard method: +\begin{verbatim}>>> o.do_nothing() # does nothing, correct\end{verbatim} + +On the contrary, \texttt{dec{\_} identity} works as a staticmethod, +\begin{verbatim}>>> OldStyle.identity(1) # called from the class +1 +>>> o.identity(1) # called from the instance +1\end{verbatim} + +whereas \texttt{dec{\_}name} works as a classmethod: +\begin{verbatim}>>> NewStyle.name() # called from the class +'NewStyle' +>>> n.name() # called from the instance +'NewStyle'\end{verbatim} + +Notice that, I have re-implemented the built-in +\texttt{staticmethod} and \texttt{classmethod}, so +\begin{verbatim}>>> isinstance(dec_identity,staticmethod) +False\end{verbatim} + +and +\begin{verbatim}>>> isinstance(dec_name,classmethod) +False\end{verbatim} + +It is possible to recognize method decorators since they provides +a couple of special attributes: +\begin{itemize} +\item +\texttt{{\_}{\_}func{\_}{\_}}: returning the function from which they originated: +\begin{verbatim}>>> assert dec_identity.__func__ is identity +>>> assert dec_name.__func__ is name\end{verbatim} + +\item +\texttt{{\_}{\_}klass{\_}{\_}}: returning the class where they where defined: +\begin{verbatim}>>> dec_identity.__klass__ +<class 'safetype.?'>\end{verbatim} + +\end{itemize} + +The question mark here means that the definition class is unknown. +The \texttt{{\_}{\_}klass{\_}{\_}} attribute can be set by hand or automatically +with the trick explained in the next section. + + +%___________________________________________________________________________ + +\hypertarget{decorating-classes}{} +\subsection*{Decorating classes} +\pdfbookmark[1]{Decorating classes}{decorating-classes} + +The problem with the approach just described +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 decorating their +methods 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 by decorating the class. +Here is an example: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example2.py{$>$}}\\ +\mbox{}\\ +\mbox{from~decorators~import~decorated}\\ +\mbox{from~example1~import~do{\_}nothing,identity,name}\\ +\mbox{}\\ +\mbox{class~B(object):}\\ +\mbox{~~~~"This~is~a~regular~class"}\\ +\mbox{}\\ +\mbox{B=decorated(B)~{\#}~does~nothing}\\ +\mbox{}\\ +\mbox{class~C(B):}\\ +\mbox{~~~"[Decorated]"}\\ +\mbox{~~~do{\_}nothing=do{\_}nothing}\\ +\mbox{~~~identity=identity}\\ +\mbox{~~~class~Inner:~{\#}~old~style~class}\\ +\mbox{~~~~~~~"[Decorated]"~{\#}~required~docstring~~~}\\ +\mbox{~~~~~~~name=name}\\ +\mbox{}\\ +\mbox{C=decorated(C)~{\#}~regenerates~the~class~converting~methods~in~decorators}\\ +\mbox{c=C()}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example2.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Under the hood \texttt{decorators.decorated()} recognizes the class level +magic docstring ``[Decorated]'' and creates an instance of the +\texttt{decorators.Decorated} metaclass. +Internally the metaclass invokes \texttt{decorators.decorated()} +on the methods of its instances: this is why they becomes decorated +if a suitable docstring is found. Moreover, it decorates inner classes, +if a suitable docstring is found, and it works recursively. + +Here is the testing: +\begin{verbatim}>>> from example2 import * +>>> assert C.identity(1) == 1 +>>> assert C.Inner.name() == 'Inner' +>>> assert c.identity(1) == 1 +>>> assert c.Inner.name() == 'Inner' \end{verbatim} + +Notice that adding \texttt{identity} after the class creation with the syntax +\texttt{C.identity=identity} would not work; +\texttt{C.identity=decorators.decorated(identity)} is required: +\begin{verbatim}>>> C.identity=decorators.decorated(identity) +>>> C.identity(1) # it works +1\end{verbatim} + +If a class misses the magic docstring, nothing happens: +\begin{verbatim}>>> B # returns the original B +<class 'example2.B'>\end{verbatim} + +The mechanism works for old style classes too, +since the metaclass automagically converts the old style classes in a +new style one: +\begin{verbatim}>>> type(C.Inner) # C.Inner is an instance of decorator.Decorated +<class 'decorators.Decorated'>\end{verbatim} + +The enhancement provided by the metaclass includes a new default +printing representation for both the class +\begin{verbatim}>>> print C # returns the name of D and of its metaclass +<class C[Decorated]>\end{verbatim} + +and its instances: +\begin{verbatim}>>> print c +<C instance>\end{verbatim} + +On the other hand, if a custom printing representation is already +defined, the metaclass takes it without any change. + +One can even forget the docstring in subclasses of decorated +classes, since metaclasses are inherited: +\begin{verbatim}>>> class D(C): +... def name(cls): +... "[classmethod]" +... return cls.__name__ +>>> print D.name() +D\end{verbatim} + +Decorating whole classes presents another advantage: the decorated methods know +the class where they were defined via the special attribute \texttt{{\_}{\_}klass{\_}{\_}}: +\begin{verbatim}>>> D.__dict__['name'].__klass__ # the class where 'name' is defined +<class 'D'>\end{verbatim} + +This is useful for introspection and debugging purposes. + + +%___________________________________________________________________________ + +\hypertarget{adding-magic}{} +\subsection*{Adding magic} +\pdfbookmark[1]{Adding magic}{adding-magic} + +The problem of the previous approach is that one must explicitely +decorate the classes by hand, by invoking \texttt{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 \texttt{decorators.enhance{\_}classes()} +on top of you module. Then all methods in all classes with a magic docstring +will be checked for magic docstrings and automagically decorated if needed. +For instance, the previous example would be written +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example3.py{$>$}}\\ +\mbox{}\\ +\mbox{import~decorators;~decorators.enhance{\_}classes()}\\ +\mbox{}\\ +\mbox{class~C:}\\ +\mbox{~~~~"[Decorated]"~{\#}~magic~docstring~here~}\\ +\mbox{~~~~def~do{\_}nothing(self):}\\ +\mbox{~~~~~~~"No~magic~docstring~here"}\\ +\mbox{}\\ +\mbox{~~~~def~identity(x):}\\ +\mbox{~~~~~~~~"[staticmethod]"}\\ +\mbox{~~~~~~~~return~x}\\ +\mbox{}\\ +\mbox{class~D(object):}\\ +\mbox{~~~~"Undecorated"~{\#}~no~magic~docstring~here}\\ +\mbox{~~~~def~name(cls):}\\ +\mbox{~~~~~~~~"[classmethod]"}\\ +\mbox{~~~~~~~~return~cls.{\_}{\_}name{\_}{\_}}\\ +\mbox{}\\ +\mbox{c=C();~d=D()}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example3.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +\texttt{C} has a \texttt{[Decorated]} docstring, so its methods +are automatically decorated: +\begin{verbatim}>>> from example3 import * +>>> assert c.do_nothing() is None +>>> assert C.identity(1) == 1 +>>> assert c.identity(1) == 1 \end{verbatim} + +On the other hand, since \texttt{D} misses a magic docstring, +its \texttt{name} method is not decorated: +\begin{verbatim}>>> hasattr(D.__dict__['name'],'__func__') # not a decorator +False\end{verbatim} + +Since \texttt{D.name} is a regular method and not a classmethod, \texttt{D.name()} +gives an error: +\begin{verbatim}>>> D.name() +Traceback (most recent call last): + ... +TypeError: unbound method name() must be called with D instance +as first argument (got nothing instead)\end{verbatim} + +Under the hood, the magic works by enhancing the \texttt{object} class +of the module with a \texttt{decorators.ClassDecorator} metaclass: +\begin{verbatim}>>> import example4 +>>> type(example4.object) +<class 'decorators.ClassDecorator'>\end{verbatim} + +Notice that for safety reasons the enhancement is only on the module +\texttt{object} class, not on the \texttt{{\_}{\_}builtin{\_}{\_}.object} class. The dangers of +adding too much magic are discussed in the \href{#diving-into-magic}{Diving into magic} section. + + +%___________________________________________________________________________ + +\hypertarget{defining-method-decorators}{} +\subsection*{Defining method decorators} +\pdfbookmark[1]{Defining method decorators}{defining-method-decorators} + +The \texttt{decorators} module contains two predefinite method decorators, +\texttt{staticmethod} and \texttt{classmethod}, which emulate the built-ins +with the same names. However, it is possible to write your own +custom decorators. The \texttt{decorators.MethodDecorator} class which is here +exactly for that purpose. + +Custom decorators are expected to be implemented by subclassing +\texttt{MethodDecorator} and by overriding its \texttt{get} method. The +\texttt{get} method automagically induces a \texttt{{\_}{\_}get{\_}{\_}} method, turning the +class in a descriptor. The machinery is needed since \texttt{{\_}{\_}get{\_}{\_}} cannot +be made cooperative using the standard \texttt{super} mechanism because +there would be a confusion between \texttt{super.{\_}{\_}get{\_}{\_}} and the decorator +\texttt{{\_}{\_}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 \emph{too} easy, since they are potentially dangerous. + +In order to give a simple example, let me show the implementation +of a \texttt{chattymethod} that prints a message when it is called: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}customdec.py{$>$}}\\ +\mbox{}\\ +\mbox{from~decorators~import~*~~}\\ +\mbox{}\\ +\mbox{class~chattymethod(MethodDecorator):}\\ +\mbox{~~~~logfile=sys.stdout~{\#}~default}\\ +\mbox{~~~~def~get(self,obj,cls=None):~{\#}~same~signature~as~{\_}{\_}get{\_}{\_}}\\ +\mbox{~~~~~~~~self.logfile.write('calling~{\%}s~from~{\%}s{\textbackslash}n'~{\%}~(self,obj~or~cls))}\\ +\mbox{~~~~~~~~return~super(chattymethod,self).get(obj,cls)}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/customdec.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Notice the usage of the \texttt{super().get} trick. This guarantees that +\texttt{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: +\begin{verbatim}>>> import customdec # adds chattymethod to the list of known decorators +>>> customdec.enhance_classes() # automagically enhances classes +>>> class C: +... " [Decorated] " +... def f(self): +... """ +... [ chattymethod ] +... """ +>>> c=C() +>>> c.f() +calling <chattymethod:f> from <C instance>\end{verbatim} + +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 \texttt{C.f(c)} works too: +\begin{verbatim}>>> C.f(c) +calling <chattymethod:f> from <class C[Decorated]>\end{verbatim} + +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 +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{def~f(self)[chattymethod(logfile=file('file1.log','w'))]} +\end{flushleft}\end{ttfamily} +\end{quote} + +In my view, there are too many parenthesis in this syntax, and it +becomes rapidly unreadable. Moreover, it complicates the implementation +without any real benefit, so the \texttt{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: +\begin{verbatim}>>> 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\end{verbatim} + +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 +\begin{verbatim}>>> c.f()\end{verbatim} + +will print a message to \texttt{file.log} too, and not to standard output. +Here is the confirmation: +\begin{verbatim}>>> chattymethod.logfile.close() +>>> print file('file.log').read().rstrip() +calling <chattymethod:g> from <C instance> +calling <chattymethod:f> from <C instance>\end{verbatim} + +A 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 \texttt{{\_}{\_}init{\_}{\_}} method. Here is an example: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}customdec.py{$>$}}\\ +\mbox{}\\ +\mbox{class~chattymethod2(chattymethod):~}\\ +\mbox{~~~~logfile=sys.stdout~{\#}~default}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,objfunc):}\\ +\mbox{~~~~~~~~super(chattymethod2,self).{\_}{\_}init{\_}{\_}(objfunc)}\\ +\mbox{~~~~~~~~logfile=getattr(self.{\_}{\_}func{\_}{\_},'logfile',None)}\\ +\mbox{~~~~~~~~if~logfile:~self.logfile=logfile~}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/customdec.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Notice that the \texttt{{\_}{\_}init{\_}{\_}} method has the signature +\texttt{{\_}{\_}init{\_}{\_}(self,objfunc)}, where the \texttt{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 \texttt{{\_}{\_}init{\_}{\_}}. + +Here is the testing: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}chatty2.py{$>$}}\\ +\mbox{}\\ +\mbox{import~customdec;~customdec.enhance{\_}classes()}\\ +\mbox{}\\ +\mbox{{\#}~sets~the~log~files}\\ +\mbox{log1=file('file1.log','w')}\\ +\mbox{log2=file('file2.log','w')}\\ +\mbox{}\\ +\mbox{class~C:}\\ +\mbox{~~~~"[Decorated]"}\\ +\mbox{~~~~def~f(self):~}\\ +\mbox{~~~~~~~~"[chattymethod2]"}\\ +\mbox{~~~~f.logfile=log1~{\#}~function~attribute}\\ +\mbox{~~~~def~g(self):~}\\ +\mbox{~~~~~~~~"[chattymethod2]"}\\ +\mbox{~~~~g.logfile=log2~{\#}~function~attribute}\\ +\mbox{}\\ +\mbox{assert~C.{\_}{\_}dict{\_}{\_}['f'].logfile~is~log1~{\#}~check~the~conversion~}\\ +\mbox{assert~C.{\_}{\_}dict{\_}{\_}['g'].logfile~is~log2~{\#}~function~attr.~-{$>$}~decorator~attr.}\\ +\mbox{}\\ +\mbox{c=C()~{\#}~C~instantiation}\\ +\mbox{}\\ +\mbox{c.f()~{\#}~print~a~message~in~file1.log}\\ +\mbox{c.g()~{\#}~print~a~message~in~file2.log}\\ +\mbox{}\\ +\mbox{log1.close();~log2.close()~{\#}~finally}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/chatty2.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Let me check the contents of the log files: +\begin{verbatim}>>> 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>\end{verbatim} + +\texttt{chattymethod} is the poor man version of \texttt{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. +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}customdec.py{$>$}}\\ +\mbox{}\\ +\mbox{class~tracedmethod(MethodDecorator):}\\ +\mbox{~~~~"Descriptor~class,~converts~a~method~in~a~traced~method"}\\ +\mbox{~~~~indent=0;~output=sys.stdout~{\#}~defaults}\\ +\mbox{}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,objfunc):}\\ +\mbox{~~~~~~~~super(tracedmethod,self).{\_}{\_}init{\_}{\_}(objfunc)}\\ +\mbox{~~~~~~~~self.funcname=self.{\_}{\_}func{\_}{\_}.{\_}{\_}name{\_}{\_}}\\ +\mbox{~~~~~~~~output=getattr(self.{\_}{\_}func{\_}{\_},'output',None)~}\\ +\mbox{~~~~~~~~if~output:~self.output=output~{\#}~func.attr.~-{$>$}~dec.attr.}\\ +\mbox{}\\ +\mbox{~~~~def~get(self,obj,cls):}\\ +\mbox{~~~~~~~~clsname=self.{\_}{\_}klass{\_}{\_}.{\_}{\_}name{\_}{\_}~{\#}~definition~clas}\\ +\mbox{~~~~~~~~def~tracedmeth(obj,*args,**kw):}\\ +\mbox{~~~~~~~~~~~~i='~'*self.indent~{\#}~default~indentation}\\ +\mbox{~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.indent+=4~{\#}~increases~indentation}\\ +\mbox{~~~~~~~~~~~~self.output.write("{\%}sCalling~'{\%}s.{\%}s'~with~arguments~"~{\%}~}\\ +\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(i,clsname,self.funcname))}\\ +\mbox{~~~~~~~~~~~~self.output.write("{\%}s{\%}s~...{\textbackslash}n"~{\%}~(obj~or~'',str(args)+str(kw)))}\\ +\mbox{~~~~~~~~~~~~res=super(tracedmethod,self).get(obj,cls)(*args,**kw)}\\ +\mbox{~~~~~~~~~~~~self.output.write("{\%}s'{\%}s.{\%}s'~called~with~result:~{\%}s{\textbackslash}n"}\\ +\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\%}~(i,clsname,self.funcname,res))}\\ +\mbox{~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.indent-=4~{\#}~restores~default~indentation}\\ +\mbox{~~~~~~~~~~~~return~res}\\ +\mbox{~~~~~~~~return~tracedmeth.{\_}{\_}get{\_}{\_}(obj,cls)~{\#}~method~wrapper}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/customdec.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +\texttt{tracedmethod.get} returns a method wrapper object, so it is +possible to use \texttt{im{\_}func} to retrieve the internal function +\texttt{tracedmeth}: +\begin{verbatim}>>> 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'\end{verbatim} + +As soon as the \texttt{tracedmethod} module is loaded, the \texttt{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: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example9.py{$>$}}\\ +\mbox{}\\ +\mbox{import~customdec;~customdec.enhance{\_}classes()}\\ +\mbox{}\\ +\mbox{logfile=file('file3.log','w')}\\ +\mbox{}\\ +\mbox{class~C(object):}\\ +\mbox{~~~~"[Decorated]"}\\ +\mbox{~~~~def~fact(self,n):}\\ +\mbox{~~~~~~~~"[tracedmethod]~The~good~old~factorial."}\\ +\mbox{~~~~~~~~if~n==0:~return~1}\\ +\mbox{~~~~~~~~else:~return~n*self.fact(n-1)}\\ +\mbox{~~~~fact.output=logfile}\\ +\mbox{}\\ +\mbox{C().fact(2)~{\#}~write~a~message~to~logfile}\\ +\mbox{}\\ +\mbox{logfile.close()}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example9.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Here is the content of \texttt{file3.log}: +\begin{verbatim}>>> import example9 # creates file3.log +>>> 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\end{verbatim} + + +%___________________________________________________________________________ + +\hypertarget{defining-class-decorators}{} +\subsection*{Defining class decorators} +\pdfbookmark[1]{Defining class decorators}{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 couple of +class decorator at work, the metaclass \texttt{ClassDecorator}, which gives +to its instances the ability to interpret class level magic docstrings, and +its subclass \texttt{Decorated}, which converts functions in method decorators +if suitable docstrings are found. + +To define a custom class decorator is easy: one defines a custom metaclass +as usual, with the only difference of deriving from \texttt{ClassDecorator} instead +of deriving from \texttt{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: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}customdec.py{$>$}}\\ +\mbox{}\\ +\mbox{class~Logged(ClassDecorator):}\\ +\mbox{~~~~output=sys.stdout}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,dic):}\\ +\mbox{~~~~~~~~super(Logged,cls).{\_}{\_}init{\_}{\_}(name,bases,dic)}\\ +\mbox{~~~~~~~~print~{$>$}{$>$}~cls.output,"{\%}s~created"~{\%}~cls}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/customdec.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +\texttt{Logged} is derived by the metaclass \texttt{ClassDecorator}, +which provides a certain amount of magic under the hood (in particular +its printing representation and its calling syntax are redefined by its +(meta-)metaclass \texttt{MetaDecorator}). +Logging capabilities can be added to a class +by simply using the magic docstring syntax: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}logged.py{$>$}}\\ +\mbox{}\\ +\mbox{import~customdec;~customdec.enhance{\_}classes()}\\ +\mbox{}\\ +\mbox{class~D(object):~{\#}~will~print~a~message~at~D~creation}\\ +\mbox{~~~~"[Logged]"}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/logged.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} +\begin{verbatim}>>> import logged +<class 'logged.D'> created\end{verbatim} + +Notice that the printing representation of \texttt{D} involves the name +of \texttt{D} preceded by the name of its metaclass, which in this case +is \texttt{Logged} + +Each time an instance of \texttt{Logged} is created, a similar message is printed: +\begin{verbatim}>>> class E(logged.D): +... pass +<class 'E'> created\end{verbatim} + +Notice that \texttt{E} does not have any magic docstring +\begin{verbatim}>>> E.__doc__ # no docstring\end{verbatim} + +but still it inherits its magic from \texttt{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: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}customdec.py{$>$}}\\ +\mbox{}\\ +\mbox{from~types~import~FunctionType~~}\\ +\mbox{}\\ +\mbox{class~Traced(Decorated):}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,n,b,d):}\\ +\mbox{~~~~~~~~for~name,func~in~d.iteritems():}\\ +\mbox{~~~~~~~~~~~~if~isinstance(func,FunctionType)~and~name!='{\_}{\_}str{\_}{\_}':~}\\ +\mbox{~~~~~~~~~~~~~~~~{\#}~cannot~trace~{\_}{\_}str{\_}{\_},~since~it~is~invoked~by}\\ +\mbox{~~~~~~~~~~~~~~~~{\#}~tracedmethod~and~would~generate~infinite~recursion}\\ +\mbox{~~~~~~~~~~~~~~~~func.{\_}{\_}doc{\_}{\_}="[tracedmethod]~"~+~(func.{\_}{\_}doc{\_}{\_}~or~'')}\\ +\mbox{~~~~~~~~super(Traced,cls).{\_}{\_}init{\_}{\_}(n,b,d)~{\#}~decorates~the~methods}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/customdec.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Here is an example of usage: +\begin{verbatim}>>> class C(object): +... """[Traced] The class decorator adds the magic docstring +... '[tracedmethod]' to f1 and f2, which are then converted +... in method decorator objects.""" +... def f1(self): pass +... def f2(self): pass +... +>>> type(C) +<class 'customdec.Traced'> +>>> 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\end{verbatim} + +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 \texttt{decorators.enhance{\_}classes()} +with a magic docstring corresponding to a class decorator as argument, +as in this example: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example7.py{$>$}}\\ +\mbox{}\\ +\mbox{from~example2~import~identity,name}\\ +\mbox{import~inspect,~decorators;~decorators.enhance{\_}classes("[Decorated]")}\\ +\mbox{}\\ +\mbox{class~C1(object):~{\#}~automagically~converted~to~a~decorated~class}\\ +\mbox{~~~~identity=identity~}\\ +\mbox{}\\ +\mbox{class~C2:~{\#}~automagically~converted~to~a~decorated~class}\\ +\mbox{~~~~name=name}\\ +\mbox{}\\ +\mbox{c1=C1()~{\#}~C1~instance}\\ +\mbox{c2=C2()~{\#}~C2~instance}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example7.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +The magic works both for new style classes such as \texttt{C1} +\begin{verbatim}>>> from example7 import C1,c1 +>>> assert C1.identity(1) == 1 +>>> assert c1.identity(1) == 1 \end{verbatim} + +and old style classes such as \texttt{C2} by implicitly converting them +to new style classes: +\begin{verbatim}>>> from example7 import C2,c2 +>>> assert C2.name() == 'C2' +>>> assert c2.name() == 'C2' \end{verbatim} + + +%___________________________________________________________________________ + +\hypertarget{composing-decorators}{} +\subsection*{Composing decorators} +\pdfbookmark[1]{Composing decorators}{composing-decorators} + +Decorators can be composed by using magic docstrings with comma-separated +decorator names. For instance, you can trace a classmethod: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example6.py{$>$}}\\ +\mbox{}\\ +\mbox{"How~to~trace~a~class~method"}\\ +\mbox{}\\ +\mbox{import~customdec;~customdec.enhance{\_}classes()}\\ +\mbox{}\\ +\mbox{class~C(object):}\\ +\mbox{~~~~"[Decorated]"}\\ +\mbox{~~~~def~fact(cls,n):~{\#}~a~traced~classmethod}\\ +\mbox{~~~~~~~~"[classmethod,tracedmethod]"}\\ +\mbox{~~~~~~~~if~n==0:~return~1}\\ +\mbox{~~~~~~~~else:~return~n*cls.fact(n-1)}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example6.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Here is the testing: +\begin{verbatim}>>> 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\end{verbatim} + +You may easily check that calling \texttt{C().fact} will work too. + +Under the hood the syntax +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{[classmethod,tracedmethod]} +\end{flushleft}\end{ttfamily} +\end{quote} + +generates a \texttt{classmethodtracedmethod} class obtained via +multiple inheritance: +\begin{verbatim}>>> type(C.__dict__['fact']) +<class 'safetype.classmethodtracedmethod'>\end{verbatim} + +Notice that the order \emph{does} matter and using the docstring +``[tracedmethod,classmethod]'' will not work: +\begin{verbatim}>>> 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)\end{verbatim} + +The problem here is that \texttt{tracedmethod.get} returns a method-wrapper object +which expects a D instance +as first argument whereas it gets \texttt{None} since +it is called from the class. On the other hand, +\begin{verbatim}>>> D().f() +Calling 'D.f' with arguments <D instance>(){} ... +'D.f' called with result: None\end{verbatim} + +will work. When \texttt{classmethod} precedes \texttt{tracedmethod}, then +\texttt{classmethod} passes to \texttt{tracedmeth} a non-empty first argument, +i.e. the calling class, even when called from the instance: +\begin{verbatim}>>> 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\end{verbatim} + +If we try to trace a staticmethod, we will get a different error with +the order ``tracedmethod, staticmethod'': +\begin{verbatim}>>> 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)\end{verbatim} + +The message is self-explanatory. + +On the other hand, composing the decorators in the order +``[tracedmethod,staticmethod]'' will work just fine. + +It is possible to compose class decorators just as method decorators, +by using the docstring syntax: for instance we may create a class +which is both \texttt{Decorated} and \texttt{Logged} and by trivially writing +\begin{verbatim}>>> class C: +... "[Decorated,Logged]" +... def f(): +... "[staticmethod]" +... return 'it works!' +<class C[DecoratedLogged]> created +>>> C().f() +'it works!'\end{verbatim} + + +%___________________________________________________________________________ + +\hypertarget{diving-into-magic}{} +\subsection*{Diving into magic} +\pdfbookmark[1]{Diving into magic}{diving-into-magic} + +If you never use metaclasses, this part can be safely skipped. On the +other hand, if you are a metaclass user, you \emph{must} read this section +in order to keep your code working. + +The \texttt{decorators} module provide a \texttt{ClassDecorator} metaclass which +converts a regular (both old style or new style) class in a class with +the ability to recognize magic docstrings. Under the hood, the +\texttt{decorators.enhance{\_}classes()} trick works by decorating the +\texttt{object} class with \texttt{decorators.ClassDecorator} and by setting +the custom metaclass to \texttt{decorators.ClassDecorator} and it is +equivalent to +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{import~decorators}\\ +\mbox{object=decorators.ClassDecorator(object)~{\#}~decorates~new~style~classes}\\ +\mbox{{\_}{\_}metaclass{\_}{\_}=~decorators.ClassDecorator~{\#}~decorates~old~style~classes} +\end{flushleft}\end{ttfamily} +\end{quote} + +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. + +The \texttt{decorators.enhance{\_}classes("[SomeClassDec]")} syntax looks at the +magic docstring and executes something like +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{import~decorators}\\ +\mbox{object=decorators.SomeClassDec(object)~{\#}~decorates~all~new~style~classes}\\ +\mbox{{\_}{\_}metaclass{\_}{\_}=~decorators.SomeClassDec~{\#}~decorates~all~old~style~classes} +\end{flushleft}\end{ttfamily} +\end{quote} + +The problem with the \texttt{enhance{\_}classes()} syntaxes is that it is +not 100{\%} safe under metaclass conflicts. In order to explain the issue, +let me give an example. + +Suppose we enhance the \texttt{object} class in the interpreter namespace: +\begin{verbatim}>>> import decorators; decorators.enhance_classes()\end{verbatim} + +making it an instance of \texttt{ClassDecorator}: +\begin{verbatim}>>> object.__class__ +<class 'decorators.ClassDecorator'>\end{verbatim} + +Now, if we naively create a custom metaclass \texttt{M}: +\begin{verbatim}>>> class M(type): +... "Some non-trivial code here..." \end{verbatim} + +and we try to use it to enhance a new style class \texttt{NwithM}, we will get a +conflict: +\begin{verbatim}>>> class NwithM(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\end{verbatim} + +The problem is that the previous line tries to create a class \texttt{NwithM} +which should have both metaclasses \texttt{ClassDecorator} and \texttt{M}: +a conflict follows. + +Fortunately, the decorators module imports the \texttt{type} metaclass from +my \texttt{safetype} module just to avoid this kind of problems. If we +derive \texttt{M} from \texttt{decorators.type} (which is really \texttt{safetype.type}) +the conflict is automagically avoided: +\begin{verbatim}>>> class M(decorators.type): +... "This metaclass is conflict-proof" +>>> class NwithM(object): # it works! +... __metaclass__=M\end{verbatim} + +The reason why the conflict is avoided is that the \texttt{safetype} module +(which makes use of really \emph{deep} metaclass magic) automatically +creates the composed class \texttt{MClassDecorator} by multiply inheriting +from \texttt{M} and from \texttt{ClassDecorator}. Then, \texttt{NwithM} can be safely +created as an instance of \texttt{safetype.MClassDecorator}: +\begin{verbatim}>>> type(NwithM) +<class 'safetype.MClassDecorator'>\end{verbatim} + +The situation with old style classes is worse since +apparently +\begin{verbatim}>>> class OwithM: # old style class with metaclass M +... __metaclass__=M +... def sm(): +... "[staticmethod]"\end{verbatim} + +gives no error, but actually \texttt{M} overrides +\texttt{ClassDecorator}, so \texttt{OwithM} will not recognize magic docstrings: +\begin{verbatim}>>> type(OwithM) +<class 'M'> +>>> OwithM.sm() +Traceback (most recent call last): + ... +TypeError: unbound method sm() must be called with OwithM instance +as first argument (got nothing instead)\end{verbatim} + +The simpler solution is to convert \texttt{OwithM} in a new style class: +\begin{verbatim}>>> class NwithM(OwithM,object): +... __metaclass__=M +... def sm(): +... "[staticmethod]" +>>> type(NwithM) +<class 'safetype.MClassDecorator'>\end{verbatim} + +Now \texttt{NwithM} is not decorated since it does miss a magic docstring, but +it provides the ability to recognizing magic docstrings, so \texttt{NwithM} +subclasses with a ``[Decorated]'' docstring will be decorated: +\begin{verbatim}>>> class E(NwithM): +... "[Decorated]" +... def cm(cls): +... "[classmethod]" +... print '.cm() called from',cls\end{verbatim} +\begin{verbatim}>>> E.cm() # it works +.cm() called from <class E[MClassDecoratorDecorated]>\end{verbatim} + +Notice that \texttt{sm} was defined in \texttt{NwithM}, the undecorated class: therefore +it is not decorated: +\begin{verbatim}>>> 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)\end{verbatim} + +The error message says that \texttt{sm} is an unbound method and not a +static method. + +It is possible to go even deeper in \textbf{black} magic, and to decorate all +the new style classes in \emph{all} modules, by decorating the +\texttt{{\_}{\_}builtin{\_}{\_}.object}. Still, naively redefining the \texttt{{\_}{\_}builtin{\_}{\_}object} +class is risky, since it will induce metaclass conflicts in other modules +using metaclasses. In other words, 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 \texttt{safetype.type} as base metaclass. +If you really enjoy black magic, you may solve the problem by +redefining the \texttt{{\_}{\_}builtin{\_}{\_}.type} as \texttt{safetype.type}. +The \texttt{decorators} module does not go so deep in dark magic, but still +there are cases where you may want to do it. In these cases you must be +explicit and redefine the builtins by hand, without help from the +module. For instance, one of my original motivation for the usage +of metaclasses/class decorators was to use them for debugging purposes. +For instance, I wanted to trace the execution of methods in +complicate inheritance hierarchies, \emph{without changing the source code}. +For simple situations, i.e. in absence of inheritance and of pre-existing +metaclasses, the function \texttt{import{\_}with{\_}metaclass} discussed in the +\href{http://www-106.ibm.com/developerworks/library/l-pymeta.html}{first paper on metaclasses} written in collaboration with David Mertz, works +fine but in general the implementation of a working \texttt{import{\_}with{\_}metaclass} +is cumbersome. For debugging purposes, the quick and dirty way can be a +good enough way, so let me show how we can redefine the builtins \texttt{object} and +\texttt{type} \emph{before} importing the module, in such a way to enhance \emph{all} +its classes with tracing capabilities. + +Let me start from a module using an unsafe metaclass \texttt{M}, such that it +cannot be traced trivially by decorating its classes \emph{after} the import; +moreover there is an inheritance hierarchy, such that it cannot be +traced correctly by naively decorating all the classes one after the +other (unless one modifies the original source code, but this forbidden +by the rules of the game ;) +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}tracing.py{$>$}}\\ +\mbox{}\\ +\mbox{"""}\\ +\mbox{This~is~a~pre-existing~module~not~using~decorators~but~using~multiple}\\ +\mbox{inheritance~and~unsafe~metaclasses.~We~want~to~trace~it~for~debugging~}\\ +\mbox{purposes.}\\ +\mbox{"""}\\ +\mbox{~}\\ +\mbox{class~M(type):~}\\ +\mbox{~~~~"There~should~be~some~non-trivial~code~here"}\\ +\mbox{}\\ +\mbox{class~B(object):~}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\ +\mbox{~~~~~~~~super(B,self).{\_}{\_}init{\_}{\_}()}\\ +\mbox{}\\ +\mbox{class~D(object):~}\\ +\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=M}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\ +\mbox{~~~~~~~~super(D,self).{\_}{\_}init{\_}{\_}()}\\ +\mbox{}\\ +\mbox{class~E(B,D):}\\ +\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\ +\mbox{~~~~~~~~super(E,self).{\_}{\_}init{\_}{\_}()}\\ +\mbox{}\\ +\mbox{~{\#}{$<$}/tracing.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +Everything works is we modify the builtins before importing the module: +\begin{verbatim}>>> import __builtin__ +>>> __object__=__builtin__.object # the original 'object' class +>>> __builtin__.object=customdec.Traced('tracedobject',(),{}) +>>> __builtin__.type=customdec.type # a safe 'type' class\end{verbatim} + +Now, when we import the module, all the classes \texttt{M}, \texttt{B}, \texttt{D} and \texttt{E} +will be created starting for the tricked builtins, so they will be traced +and safe under conflicts. For instance, let me show that \texttt{E} is created +with a conflict safe \texttt{MTraced} metaclass: +\begin{verbatim}>>> from tracing import E +>>> print E +<class E[MTraced]>\end{verbatim} + +This shows that the \texttt{{\_}{\_}init{\_}{\_}} methods are traced indeed and shows the +Method Resolution Order: +\begin{verbatim}>>> 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\end{verbatim} + +This works, indeed. At the end, one should not forget to restore +the standard builtins, otherwise it will trace \emph{all} the classes +created thereafter. +\begin{verbatim}>>> __builtin__.object=__object__ # restore the __builtin__ +>>> __builtin__.type=decorators.__type__ # restore the __builtin__\end{verbatim} + +At the end, I will notice that it is possible to solve the problem more +nicely, without redefining the builtins, but I will discuss the solution +elsewhere ;) + + +%___________________________________________________________________________ + +\hypertarget{advanced-usage}{} +\subsection*{Advanced usage} +\pdfbookmark[1]{Advanced usage}{advanced-usage} + +Whereas the average programmer is expected to use the \texttt{decorated()} +and \texttt{enhance{\_}classes()} functions only, the \texttt{decorators} module provides +facilities which may be useful to the advanced programmer. + +The simplest is an utility function which retrieves the list of +recognized decorators, \texttt{decorators.getdec()}. The precise syntax is +\texttt{decorators.getdec(docstring)}, where \texttt{docstring} +is a magic docstring, i.e. a bracketed comma-separated list +of decorator names. For instance \texttt{decorators.getdec('[MethodDecorator]')} +gives the list of all subclasses of \texttt{MethodDecorator}, i.e. all method +decorators, whereas \texttt{decorators.getdec('[ClassDecorator]')} +gives the list of the known class decorators. The utility of the function +is that it also returns decorators that have been automagically created: +\begin{verbatim}>>> decorators.getdec("[classmethodtracedmethod]") +[<class 'safetype.classmethodtracedmethod'>]\end{verbatim} + +It is even possible to use the comma notation: +\begin{verbatim}>>> decorators.getdec("[classmethod,tracedmethod]") +[<class 'safetype.classmethodtracedmethod'>]\end{verbatim} + +If you mispell a decorator name you get an helpful error message: +\begin{verbatim}>>> class E: +... "[Decorated]" +... def f(): +... "[staticmeth]" +Traceback (most recent call last): + .. a cryptic traceback here .. +UnknownDecoratorError: staticmeth\end{verbatim} + +By design, the \texttt{decorated} function is quite limited, and does not +decorate functions without magic docstrings. This is safe and also convenient +for internal usage of the \texttt{decorated} function in the \texttt{Decorated} +metaclass. Nevertheless, advanced users may want to have the ability +of decorating functions by hand, without invoking decorators.decorated()` +and magic docstrings. This is possible, by calling the decorator directly. +For instance, here is how to convert a lambda function in a staticmethod: +\begin{verbatim}>>> do_nothing=decorators.staticmethod(lambda:None) +>>> print do_nothing # ``do_nothing`` is a static method +<staticmethod:<lambda>>\end{verbatim} + +The most convenient usage of this feature is in the composition of decorators. +For instance, we may compose the just defined \texttt{staticmethod} with a +\texttt{chattymethod2}: +\begin{verbatim}>>> chattystatic=customdec.chattymethod2(do_nothing) +>>> print chattystatic +<chattymethod2staticmethod:<lambda>>\end{verbatim} + +The syntax +\begin{quote} + +\texttt{decorator1(decorator2(obj))} +\end{quote} + +automagically creates a composed class \texttt{decorator1decorator2} in the +\texttt{safetype} module (or recycle it, if \texttt{decorator1decorator2} has +been already created) and it is equivalent to +\begin{quote} + +\texttt{decorator1decorator2(obj)} +\end{quote} + +Here is the check that everything works: +\begin{verbatim}>>> class B: +... chattystatic=chattystatic +>>> B.chattystatic() +calling <chattymethod2staticmethod:<lambda>> from <class 'B'>\end{verbatim} + +Notice that \texttt{chattystatic} does not know the class where it +is defined: +\begin{verbatim}>>> chattystatic.__klass__ +<class 'safetype.?'>\end{verbatim} + +unless the \texttt{{\_}{\_}klass{\_}{\_}} attribute is explicitly set. + +This is the hierarchy: +\begin{verbatim}>>> for C in type(chattystatic).mro(): print C +... +<class 'safetype.chattymethod2staticmethod'> +<class 'customdec.chattymethod2'> +<class 'customdec.chattymethod'> +<class 'decorators.staticmethod'> +<class 'decorators.MethodDecorator'> +<class 'decorators.Decorator'> +<type 'object'>\end{verbatim} + +One can also compose classes by hand, by using class decorators: +\begin{verbatim}>>> class C: +... "[Logged]" +... def f(): "[staticmethod]" +... +<class 'C'> created +>>> C=customdec.Traced(C) +<class C[TracedLogged]> created\end{verbatim} + +The \texttt{decorators.enhance{\_}classes({$<$}classdecorator{$>$})} syntax performs +the composition automagically: +\begin{quote} +\begin{ttfamily}\begin{flushleft} +\mbox{{\#}{$<$}example8.py{$>$}}\\ +\mbox{}\\ +\mbox{from~example2~import~identity,name}\\ +\mbox{import~inspect,~decorators;~decorators.enhance{\_}classes("[Decorated]")}\\ +\mbox{}\\ +\mbox{class~C1:~{\#}~automagically~converted~to~a~Decorated~class}\\ +\mbox{~~~~identity=identity~}\\ +\mbox{}\\ +\mbox{class~C2:~{\#}~automagically~converted~to~a~DecoratedLogged~class}\\ +\mbox{~~~~"[Logged]"}\\ +\mbox{~~~~name=name}\\ +\mbox{}\\ +\mbox{c1=C1()~{\#}~C1~instance}\\ +\mbox{c2=C2()~{\#}~C2~instance}\\ +\mbox{}\\ +\mbox{{\#}{$<$}/example8.py{$>$}} +\end{flushleft}\end{ttfamily} +\end{quote} + +In this example the class \texttt{C2} has already a magic docstring. This means that +\texttt{C2} has to be enhanced both from \texttt{Logged} and from \texttt{Decorated}. +This is done by automagically creating a \texttt{DecoratedLogged} class +decorator: +\begin{verbatim}>>> from example8 import C1,C2,c1,c2 +<class C2[DecoratedLogged]> created\end{verbatim} + +The second line is printed because of the logging capabilities of \texttt{C2}. +Moreover, since \texttt{C2} is decorated too, the following will work: +\begin{verbatim}>>> assert C2.name() == 'C2' +>>> assert c2.name() == 'C2' \end{verbatim} + +Idem for \texttt{C1}: +\begin{verbatim}>>> assert C1.identity(1) == 1 +>>> assert c1.identity(1) == 1 \end{verbatim} + + +%___________________________________________________________________________ + +\hypertarget{the-implementation}{} +\section*{The implementation} +\pdfbookmark[0]{The implementation}{the-implementation} + +This part can be safely skipped, unless you are a \emph{really} curious and +you want to know how the implementation works. + +The module is rather short (less than 150 lines if you skip comments and +docstrings) but far from being trivial, since it is based on extensive +usage of metaclass wizardry. Moreover, +it invokes the \texttt{safetype} module to solve metaclass conflicts, and +\texttt{safetype} contains 50 lines of \emph{really} deep metaclass wizardry. +\begin{quote} +\begin{figure} + +\includegraphics{decorators.ps} +\end{figure} +\end{quote} + +The main class-metaclass hierarchy is represented in the figure, where +boxes denote classes and ovals denote metaclasses; instantiation is +denoted by dashed green lines whereas inheritance is denoted by continuous +blue lines. + +For instance, this is the Method Resolution Order for the \texttt{Decorated} +metaclass: +\begin{verbatim}>>> for i,c in enumerate(decorators.Decorated.__mro__): +... print i,c,"[%s]" % type(c).__name__ +0 <class 'decorators.Decorated'> [MetaDecorator] +1 <class 'decorators.ClassDecorator'> [MetaDecorator] +2 <class 'safetype.safetype'> [type] +3 <type 'type'> [type] +4 <class 'decorators.Decorator'> [MetaDecorator] +5 <type 'object'> [type]\end{verbatim} + +\texttt{Decorator} is the mother of all decorators. Its main purpose it to +provide the \texttt{MetaDecorator} +metaclass to all decorators. + +The \texttt{safetype} metaclass, imported from the \texttt{safetype} module, +ensures that class decorators can generate classes without incurring +in conflicts. + +Since class decorators are metaclasses, +\texttt{MetaDecorator} is actually a meta-metaclass with respect to +instances of decorated classes. For this reason if +\begin{verbatim}>>> C=decorators.ClassDecorator('C',(),{})\end{verbatim} + +then +\begin{verbatim}>>> C.__class__ +<class 'decorators.ClassDecorator'>\end{verbatim} + +but +\begin{verbatim}>>> C.__metaclass__ +<class 'decorators.MetaDecorator'>\end{verbatim} + +since the \texttt{C.{\_}{\_}metaclass{\_}{\_}} attribute is inherited from \texttt{Decorator}. + +On the other hand, \texttt{MetaDecorator} is a simple metaclass with +respect to instances of decorated methods. + +The implementation is relatively smart. Consider for instance the case of +the \texttt{logged} example. In that example class \texttt{D} was a subclass +of a tricked \texttt{object} class, enhanced by \texttt{ClassDecorator}. Moreover \texttt{D} +had a \texttt{Logged} docstring. Therefore it should have been an instance of a +\texttt{ClassDecoratorLogged} metaclass. But logged was +a subclass of \texttt{ClassDecorator}, therefore it already had all the features +of \texttt{ClassDecorator} and it would have been redundant to create +\texttt{ClassDecoratorLogged}, when``Logged`` would have been enough. +So \texttt{Logged} was used and \texttt{ClassDecoratorLogged} was never created. +All this magic is in the \texttt{safetype.remove{\_}redundant(*classes)} function. + +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. Probably, they do not matter unless one has to decorate +thousands or millions of functions and classes. + +Finally, a word about bugs. The \texttt{decorators} module is fairly sophisticated, +therefore whereas I can guarantee that it passes my test suite (which involves +{\~{ }}200 tests, most of them 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 fix it or document it. +% References: + + +%___________________________________________________________________________ + +\hypertarget{module-decorators}{} +\subsection*{Module \texttt{decorators}} +\pdfbookmark[1]{Module decorators}{module-decorators} + +A module to implement pep318 (decorator syntax) via magic doctrings. +For the full documentation see +\href{http://www.phyast.pitt.edu/~micheles/python/decorators.html}{http://www.phyast.pitt.edu/{\~{ }}micheles/python/decorators.html} . + + +%___________________________________________________________________________ + +\hypertarget{id3}{} +\subsubsection*{Metaclasses} +\pdfbookmark[2]{Metaclasses}{id3} + +\texttt{metaclass ClassDecorator(type,Decorator):} +\begin{quote} + +Metaclass callable with one or three arguments, having its calling +syntax redefined by \texttt{MetaDecorator}. +\end{quote} + +\texttt{metaclass Decorated(ClassDecorator):} +\begin{quote} + +Metaclass which decorates the methods of its instances according +to the docstrings. It redefines \texttt{{\_}{\_}str{\_}{\_}} on +its instances and the default \texttt{{\_}{\_}str{\_}{\_}} on the instances of its +instances. + +\texttt{{\_}{\_}str{\_}{\_}(cls):} +\begin{quote} + +Redefine the printing representation of classes +\end{quote} +\end{quote} + +\texttt{metaclass safetype(type):} +\begin{quote} + +Overrides the \texttt{{\_}{\_}new{\_}{\_}} method of the \texttt{type} metaclass, making the +generation of classes conflict-proof. +\end{quote} + +\texttt{metaclass MetaDecorator(type):} +\begin{quote} + +Metaclass inducing a certain amount of magic on decorators: +\newcounter{listcnt1} +\begin{list}{\arabic{listcnt1}.} +{ +\usecounter{listcnt1} +\setlength{\rightmargin}{\leftmargin} +} +\item +Each time a decorator is defined in any module, it is stored in +MetaDecorator.dic and MetaDecorator.ls. + +\item +If the (method) decorator has a 'get' method, a '{\_}{\_}get{\_}{\_}' method +is automagically created as an alias to 'get'. + +\item +Decorators calls are dispatched to the decorator {\_}call{\_} +classmethod. + +\end{list} + +\texttt{{\_}{\_}call{\_}{\_}(dec,*args):} +\begin{quote} + +This is the heart of the module. Infers the correct decorator class +from \texttt{dec} and the docstring and creates the correct decorator object. +Returns the original object if \texttt{dec} is the trivial \texttt{Decorator} +class and no docstring is found. +\end{quote} + +\texttt{{\_}{\_}init{\_}{\_}(dec,*args):} +\begin{quote} + +Update the metaclass attributes \texttt{dic} and \texttt{ls}; +alias \texttt{get} to \texttt{{\_}{\_}get{\_}{\_}}. +\end{quote} +\end{quote} + + +%___________________________________________________________________________ + +\hypertarget{classes}{} +\subsubsection*{Classes} +\pdfbookmark[2]{Classes}{classes} + +\texttt{class UnknownDecoratorError(Exception):} +\begin{quote} + +The name says it all. +\end{quote} + +\texttt{class Decorator(object):} +\begin{quote} + +Instance of MetaDecorator and mothers of all decorators. When called +in the form \texttt{Decorator(obj)} with obj having a magic docstring, it returns +a decorated object; otherwise it returns the original object. +\end{quote} + +\texttt{class staticmethod(MethodDecorator):} +\begin{quote} + +Decorator, converts a function in a staticmethod +\end{quote} + +\texttt{class MethodDecorator(Decorator):} +\begin{quote} + +Descriptor class callable with a function or a descriptor object +as argument. It defines a default printing representation on method +decorators objects and a default \texttt{get} method. All the rest is +provided by the metaclass \texttt{MetaDecorator}. + +\texttt{get(self,obj,cls=None):} +\begin{quote} + +aliased to {\_}{\_}get{\_}{\_} by the metaclass, to be overridden +\end{quote} + +\texttt{{\_}{\_}str{\_}{\_}(self):} +\begin{quote} + +Printing representation of kind {$<$}decoratorclass:functionname{$>$} +\end{quote} + +\texttt{{\_}{\_}init{\_}{\_}(self,objfunc):} +\begin{quote} + +objfunc is a decorator object or a function +\end{quote} +\end{quote} + +\texttt{class classmethod(MethodDecorator):} +\begin{quote} + +Decorator, converts a function in a classmethod +\end{quote} + + +%___________________________________________________________________________ + +\hypertarget{functions}{} +\subsubsection*{Functions} +\pdfbookmark[2]{Functions}{functions} + +\texttt{enhance{\_}classes(magicstring='[ClassDecorator]'):} +\begin{quote} + +Enhances all the classes in the caller module with a metaclass +built from the given magicstring. +\end{quote} + +\texttt{remove{\_}redundant(bases):} +\begin{quote} + +Returns a tuple of non-redundant base classes. +Given a sequence of base classes, a class is redundant if it is duplicated +or if it is implied by the others, i.e. it is an ancestor of at least one +of the other classes. For instance, if \texttt{C} is derived from \texttt{B}, in the +sequence \texttt{C,B} the class \texttt{B} is redundant, since all its features are +already provided by \texttt{C}. Therefore \texttt{B} +is removed and \texttt{remove{\_}redundant} returns the tuple \texttt{(C,)}: +\begin{verbatim}>>> class B(object): pass +... +>>> class C(B): pass +... +>>> import safetype; safetype.remove_redundant([C,B]) +(<class 'C'>,)\end{verbatim} +\end{quote} + +\texttt{compose(dclasses,*defaultclasses):} +\begin{quote} + +Retrieves or creates a decorator for a tuple of decorators. If +defaults decorators are given, they get the precedence. It removes +redundant classes. +\end{quote} + +\texttt{instance{\_}str(self):} +\begin{quote} + +Redefines the printing representation of simple objects +\end{quote} + +\texttt{getdec(magicstring="[Decorator]"):} +\begin{quote} + +Given a magicstring describing a valid decorator name, returns the list +of its subclasses. By default returns the full list of decorators. +\end{quote} + +\texttt{decorate(objdict):} +\begin{quote} + +Takes an object with a dictionary and decorates all its functions +and classes according to their docstrings. +\end{quote} + +\texttt{decorators{\_}in(docstring):} +\begin{quote} + +Takes a docstring and returns a (possibly empty) tuple of decorator +classes. +\end{quote} + +\end{document} + diff --git a/pypers/pep318/decorators.txt b/pypers/pep318/decorators.txt new file mode 100755 index 0000000..fb7f321 --- /dev/null +++ b/pypers/pep318/decorators.txt @@ -0,0 +1,1413 @@ +Implementing PEP 318 (decorators)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+:Module: decorators
+:Version: 0.5
+:Author: Michele Simionato
+:e-mail: MicheleSimionato@libero.it
+:Licence: Python-like
+:Date: September 2003
+:Disclaimer: This is experimental code. Use it at your own risk!
+
+.. contents::
+
+Using decorators
+=========================================================================
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of `PEP 318`_ in pure Python
+(2.3).
+
+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 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 (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 magic docstrings
+and automatically converts methods with magic docstrings in
+method decorators.
+
+Decorators are nothing else than a sophisticated kind of wrappers.
+The ``decorators`` module provides support both for method decorators
+and class decorators:
+
++ *Method decorators* are classes taking a single function as input and
+ producing a descriptor object as output. ``staticmethod`` and
+ ``classmethod`` are two examples of already existing
+ method decorators (actually my implementation rewrites them).
+ A knowledge of descriptors [1]_ 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.
+
+Notice that properties are not decorators according to my definitions,
+since they take four functions as input, ``get, set, del_`` and ``doc``.
+Whereas the decorators concept could be generalized to the case of
+multiple inputs, I don't see the need for such complication, so
+properties are not implemented as method decorators. Moreover, I
+think it is much better to use them trough a class decorator.
+
+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 documentation 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.
+
+.. [1] 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 ;).
+
+Decorating methods
+------------------------------------------------------------------------
+
+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.decorator()`` 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.decorator()``
+is simple but verbose, so magic shortcuts will be discussed in the next
+sections.
+
+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.decorator(do_nothing)
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+ dec_identity=decorators.decorator(identity)
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+ dec_name=decorators.decorator(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>
+
+>>> from example1 import * # for testing purposes
+
+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 in the interpreter, since they have a re-defined
+printing representation:
+
+>>> print dec_identity
+<staticmethod:identity>
+>>> print dec_name
+<classmethod:name>
+
+On the other hand, ``do_nothing`` does not have a magic
+docstring, therefore it is *not* converted to a decorator object:
+actually, in this case ``decorator`` returns ``None``:
+
+
+>>> print dec_do_nothing
+None
+
+This is useful since one can tests if an object is a decorator
+object with idioms like
+
+ ``if decorator(obj): do_something()``
+
+and one can convert generic objects with the idiom
+
+ ``dec_obj=decorator(obj) or object``
+
+which returns the original object if no magic docstring is found.
+
+
+Let me check that ``dec_ identity`` works correctly as a staticmethod,
+
+>>> OldStyle.identity(1) # called from the class
+1
+>>> o.identity(1) # called from the instance
+1
+
+whereas ``dec_name`` works as a classmethod:
+
+>>> NewStyle.name() # called from the class
+'NewStyle'
+>>> n.name() # called from the instance
+'NewStyle'
+
+Notice that, I have re-implemented the built-in
+``staticmethod`` and ``classmethod``, so
+
+>>> isinstance(dec_identity,staticmethod)
+False
+
+and
+
+>>> isinstance(dec_name,classmethod)
+False
+
+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
+
+- ``__klass__``: returning the class where they where defined:
+
+ >>> dec_identity.__klass__
+ <class 'safetype.?'>
+
+The question mark here means that the definition class is unknown.
+The ``__klass__`` attribute can be set by hand or automatically
+with the trick explained in the next section.
+
+Decorating classes
+----------------------------------------------------------------------
+
+The problem with the approach just described
+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 decorating their
+methods 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 by decorating the class.
+Here is an example:
+
+ ::
+
+ #<example2.py>
+
+ from decorators import decorator
+ from example1 import do_nothing,identity,name
+
+ class B(object):
+ "This is a regular class"
+
+ B=decorator(B) or B # return the original B
+
+ class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ class Inner: # old style class
+ "[Decorated]" # required docstring
+ name=name
+
+ C=decorator(C) # regenerate the class converting methods in decorators
+ c=C()
+
+ #</example2.py>
+
+Under the hood ``decorators.decorator()`` recognizes the class level
+magic docstring "[Decorated]" and creates an instance of the
+``decorators.Decorated`` metaclass.
+Internally the metaclass invokes ``decorators.decorator()``
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. Moreover, it decorates inner classes,
+if a suitable docstring is found, and it works recursively.
+
+Here is the testing:
+
+>>> from example2 import *
+>>> assert C.identity(1) == 1
+>>> assert C.Inner.name() == 'Inner'
+>>> assert c.identity(1) == 1
+>>> assert c.Inner.name() == 'Inner'
+
+Notice that adding ``identity`` after the class creation with the syntax
+``C.identity=identity`` would not work;
+``C.identity=decorators.decorator(identity)`` is required:
+
+>>> C.identity=decorators.decorator(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,
+since the metaclass automagically converts the old style classes in a
+new style one:
+
+>>> type(C.Inner) # C.Inner is an instance of decorator.Decorated
+<class 'decorators.Decorated'>
+
+The enhancement provided by the metaclass includes a new default
+printing representation for both the class
+
+>>> print C # returns the name of D and of its metaclass
+<class C[Decorated]>
+
+and its instances:
+
+>>> print c
+<C instance>
+
+On the other hand, if a custom printing representation is already
+defined, the metaclass takes it without any change.
+
+One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:
+
+>>> class D(C):
+... def name(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print D.name()
+D
+
+Decorating whole classes presents another advantage: the decorated methods know
+the class where they were defined via the special attribute ``__klass__``:
+
+>>> D.__dict__['name'].__klass__ # the class where 'name' is defined
+<class 'D'>
+
+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.decorator()``
+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 you module. Then all methods in all classes with a magic docstring
+will be checked for magic docstrings and automagically decorated if needed.
+For instance, the previous example would be written
+
+ ::
+
+ #<example3.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()
+
+ #</example3.py>
+
+``C`` has a ``[Decorated]`` docstring, so its methods
+are automatically decorated:
+
+>>> from example3 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)
+
+Under the hood, the magic works by enhancing the ``object`` class
+of the module with a ``decorators.ClassDecorator`` metaclass:
+
+>>> import example4
+>>> type(example4.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 dangers of
+adding too much magic are discussed in the `Diving into magic`_ section.
+
+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
+>>> 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
+becomes 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,decorator
+>>> chattymethod.logfile=file('file.log','w')
+>>> def g(self):
+... "[chattymethod]"
+>>> C.g=decorator(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 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__``.
+
+Here is the testing:
+
+ ::
+
+ #<chatty2.py>
+
+ import customdec; customdec.enhance_classes()
+
+ # sets the log files
+ log1=file('file1.log','w')
+ log2=file('file2.log','w')
+
+ class C:
+ "[Decorated]"
+ 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 ``file3.log``:
+
+>>> import example9 # creates file3.log
+>>> 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
+
+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 couple of
+class decorator at work, the metaclass ``ClassDecorator``, which gives
+to its instances the ability to interpret class level magic docstrings, and
+its subclass ``Decorated``, which converts functions in method decorators
+if suitable docstrings are found.
+
+To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference of deriving from ``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
+(meta-)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 'logged.D'> 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'> 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(Decorated):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType) and name!='__str__':
+ # cannot trace __str__, since it is invoked by
+ # tracedmethod and would generate infinite recursion
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d) # decorates the methods
+
+ #</customdec.py>
+
+Here is an example of usage:
+
+>>> class C(object):
+... """[Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... in method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> type(C)
+<class 'customdec.Traced'>
+>>> 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:
+
+ ::
+
+ #<example7.py>
+
+ from example2 import identity,name
+ import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+ class C1(object): # automagically converted to a decorated class
+ identity=identity
+
+ class C2: # automagically converted to a decorated class
+ name=name
+
+ c1=C1() # C1 instance
+ c2=C2() # C2 instance
+
+ #</example7.py>
+
+The magic works both for new style classes such as ``C1``
+
+>>> from example7 import C1,c1
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1
+
+and old style classes such as ``C2`` by implicitly converting them
+to new style classes:
+
+>>> from example7 import C2,c2
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2'
+
+Composing decorators
+---------------------------------------------------------------------
+
+Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod:
+
+ ::
+
+ #<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 ``C().fact`` will work too.
+
+Under the hood the syntax
+
+ ::
+
+ [classmethod,tracedmethod]
+
+generates a ``classmethodtracedmethod`` class obtained via
+multiple inheritance:
+
+>>> type(C.__dict__['fact'])
+<class 'safetype.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 order
+"[tracedmethod,staticmethod]" will work just fine.
+
+It is possible to compose class decorators just as method decorators,
+by using the docstring syntax: for instance we may create a class
+which is both ``Decorated`` and ``Logged`` and by trivially writing
+
+>>> class C:
+... "[Decorated,Logged]"
+... def f():
+... "[staticmethod]"
+... return 'it works!'
+<class C[DecoratedLogged]> created
+>>> C().f()
+'it works!'
+
+Diving into magic
+-----------------------------------------------------------------------
+
+If you never use metaclasses, this part can be safely skipped. On the
+other hand, if you are a metaclass user, you *must* read this section
+in order to keep your code working.
+
+The ``decorators`` module provide a ``ClassDecorator`` metaclass which
+converts a regular (both old style or new style) class in a class with
+the ability to recognize magic docstrings. Under the hood, 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
+
+ ::
+
+ import decorators
+ object=decorators.ClassDecorator(object) # decorates new style classes
+ __metaclass__= decorators.ClassDecorator # decorates old style classes
+
+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.
+
+The ``decorators.enhance_classes("[SomeClassDec]")`` syntax looks at the
+magic docstring and executes something like
+
+ ::
+
+ import decorators
+ object=decorators.SomeClassDec(object) # decorates all new style classes
+ __metaclass__= decorators.SomeClassDec # decorates all old style classes
+
+The problem with the ``enhance_classes()`` syntaxes is that it is
+not 100% safe under metaclass conflicts. In order to explain the issue,
+let me give an example.
+
+Suppose we enhance the ``object`` class in the interpreter namespace:
+
+>>> import decorators; decorators.enhance_classes()
+
+making it an instance of ``ClassDecorator``:
+
+>>> object.__class__
+<class 'decorators.ClassDecorator'>
+
+Now, if we naively create a custom metaclass ``M``:
+
+>>> class M(type):
+... "Some non-trivial code here..."
+
+and we try to use it to enhance a new style class ``NwithM``, we will get a
+conflict:
+
+>>> class NwithM(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 ``NwithM``
+which should have both metaclasses ``ClassDecorator`` and ``M``:
+a conflict follows.
+
+Fortunately, the decorators module imports the ``type`` metaclass from
+my ``safetype`` module just to avoid this kind of problems. If we
+derive ``M`` from ``decorators.type`` (which is really ``safetype.type``)
+the conflict is automagically avoided:
+
+>>> class M(decorators.type):
+... "This metaclass is conflict-proof"
+>>> class NwithM(object): # it works!
+... __metaclass__=M
+
+The reason why the conflict is avoided is that the ``safetype`` module
+(which makes use of really *deep* metaclass magic) automatically
+creates the composed class ``MClassDecorator`` by multiply inheriting
+from ``M`` and from ``ClassDecorator``. Then, ``NwithM`` can be safely
+created as an instance of ``safetype.MClassDecorator``:
+
+>>> type(NwithM)
+<class 'safetype.MClassDecorator'>
+
+The situation with old style classes is worse since
+apparently
+
+>>> class OwithM: # old style class with metaclass M
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+
+gives no error, but actually ``M`` overrides
+``ClassDecorator``, so ``OwithM`` will not recognize magic docstrings:
+
+>>> type(OwithM)
+<class 'M'>
+>>> OwithM.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with OwithM instance as first argument (got nothing instead)
+
+The simpler solution is to convert ``OwithM`` in a new style class:
+
+>>> class NwithM(OwithM,object):
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+>>> type(NwithM)
+<class 'safetype.MClassDecorator'>
+
+Now ``NwithM`` is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so ``NwithM``
+subclasses with a "[Decorated]" docstring will be decorated:
+
+>>> class E(NwithM):
+... "[Decorated]"
+... def cm(cls):
+... "[classmethod]"
+... print '.cm() called from',cls
+
+>>> E.cm() # it works
+.cm() called from <class E[MClassDecoratorDecorated]>
+
+Notice that ``sm`` was defined in ``NwithM``, 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 that ``sm`` is an unbound method and not a
+static method.
+
+It is possible to go even deeper in **black** magic, and to decorate all
+the new style classes in *all* modules, by decorating the
+``__builtin__.object``. Still, naively redefining the ``__builtin__object``
+class is risky, since it will induce metaclass conflicts in other modules
+using metaclasses. In other words, 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 ``safetype.type`` as base metaclass.
+If you really enjoy black magic, you may solve the problem by
+redefining the ``__builtin__.type`` as ``safetype.type``.
+The ``decorators`` module does not go so deep in dark magic, but still
+there are cases where you may want to do it. In these cases you must be
+explicit and redefine the builtins by hand, without help from the
+module. For instance, one of my original motivation for the usage
+of metaclasses/class decorators was to use them for debugging purposes.
+For instance, I wanted to trace the execution of methods in
+complicate inheritance hierarchies, *without changing the source code*.
+For simple situations, i.e. in absence of inheritance and of pre-existing
+metaclasses, the function ``import_with_metaclass`` discussed in the
+`first paper on metaclasses`_ written in collaboration with David Mertz, works
+fine but in general the implementation of a working ``import_with_metaclass``
+is cumbersome. For debugging purposes, the quick and dirty way can be a
+good enough way, so let me show how we can redefine the builtins ``object`` and
+``type`` *before* importing the module, in such a way to enhance *all*
+its classes with tracing capabilities.
+
+Let me start from a module using an unsafe metaclass ``M``, such that it
+cannot be traced trivially by decorating its classes *after* the import;
+moreover there is an inheritance hierarchy, such that it cannot be
+traced correctly by naively decorating all the classes one after the
+other (unless one modifies the original source code, but this forbidden
+by the rules of the game ;)
+
+ ::
+
+ #<tracing.py>
+
+ """
+ This is a pre-existing module not using decorators but using multiple
+ inheritance and unsafe metaclasses. We want to trace it for debugging
+ purposes.
+ """
+
+ class M(type):
+ "There should be some non-trivial code here"
+
+ class B(object):
+ def __init__(self):
+ super(B,self).__init__()
+
+ class D(object):
+ __metaclass__=M
+ def __init__(self):
+ super(D,self).__init__()
+
+ class E(B,D):
+ def __init__(self):
+ super(E,self).__init__()
+
+ #</tracing.py>
+
+Everything works is we modify the builtins before importing the module:
+
+>>> import __builtin__
+>>> __object__=__builtin__.object # the original 'object' class
+>>> __builtin__.object=customdec.Traced('tracedobject',(),{})
+>>> __builtin__.type=customdec.type # a safe 'type' class
+
+Now, when we import the module, all the classes ``M``, ``B``, ``D`` and ``E``
+will be created starting for the tricked builtins, so they will be traced
+and safe under conflicts. For instance, let me show that ``E`` is created
+with a conflict safe ``MTraced`` metaclass:
+
+>>> from tracing import E
+>>> print E
+<class E[MTraced]>
+
+This shows that the ``__init__`` methods are traced indeed and shows the
+Method Resolution Order:
+
+>>> 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
+
+This works, indeed. At the end, one should not forget to restore
+the standard builtins, otherwise it will trace *all* the classes
+created thereafter.
+
+>>> __builtin__.object=__object__ # restore the __builtin__
+>>> __builtin__.type=decorators.__type__ # restore the __builtin__
+
+At the end, I will notice that it is possible to solve the problem more
+nicely, without redefining the builtins, but I will discuss the solution
+elsewhere ;)
+
+Advanced usage
+---------------------------------------------------------------------------
+
+Whereas the average programmer is expected to use the ``decorator()``
+and ``enhance_classes()`` functions only, the ``decorators`` module provides
+facilities which may be useful to the advanced programmer.
+
+The simplest is an utility function which retrieves the list of
+recognized decorators, ``decorators.getdec()``. The precise syntax is
+``decorators.getdec(docstring)``, where ``docstring``
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance ``decorators.getdec('[MethodDecorator]')``
+gives the list of all subclasses of ``MethodDecorator``, i.e. all method
+decorators, whereas ``decorators.getdec('[ClassDecorator]')``
+gives the list of the known class decorators. The utility of the function
+is that it also returns decorators that have been automagically created:
+
+>>> decorators.getdec("[classmethodtracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]
+
+It is even possible to use the comma notation:
+
+>>> decorators.getdec("[classmethod,tracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]
+
+If you mispell a decorator name you get an helpful error message:
+
+>>> class E:
+... "[Decorated]"
+... def f():
+... "[staticmeth]"
+Traceback (most recent call last):
+ .. a cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+
+
+By design, the ``decorated`` function is quite limited, and does not
+decorate functions without magic docstrings. This is safe and also convenient
+for internal usage of the ``decorated`` function in the ``Decorated``
+metaclass. Nevertheless, advanced users may want to have the ability
+of decorating functions by hand, without invoking `decorators.decorator()``
+and magic docstrings. This is possible, by calling the decorator directly.
+For instance, here is how to convert a lambda function in a staticmethod:
+
+>>> do_nothing=decorators.staticmethod(lambda:None)
+>>> print do_nothing # ``do_nothing`` is a static method
+<staticmethod:<lambda>>
+
+The most convenient usage of this feature is in the composition of decorators.
+For instance, we may compose the just defined ``staticmethod`` with a
+``chattymethod2``:
+
+>>> chattystatic=customdec.chattymethod2(do_nothing)
+>>> print chattystatic
+<chattymethod2staticmethod:<lambda>>
+
+The syntax
+
+ ``decorator1(decorator2(obj))``
+
+automagically creates a composed class ``decorator1decorator2`` in the
+``safetype`` module (or recycle it, if ``decorator1decorator2`` has
+been already created) and it is equivalent to
+
+ ``decorator1decorator2(obj)``
+
+Here is the check that everything works:
+
+>>> class B:
+... chattystatic=chattystatic
+>>> B.chattystatic()
+calling <chattymethod2staticmethod:<lambda>> from <class 'B'>
+
+Notice that ``chattystatic`` does not know the class where it
+is defined:
+
+>>> chattystatic.__klass__
+<class 'safetype.?'>
+
+unless the ``__klass__`` attribute is explicitly set.
+
+This is the hierarchy:
+
+>>> for C in type(chattystatic).mro(): print C
+...
+<class 'safetype.chattymethod2staticmethod'>
+<class 'customdec.chattymethod2'>
+<class 'customdec.chattymethod'>
+<class 'decorators.staticmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.decorator'>
+<type 'object'>
+
+One can also compose classes by hand, by using class decorators:
+
+>>> class C:
+... "[Logged]"
+... def f(): "[staticmethod]"
+...
+<class 'C'> created
+>>> C=customdec.Traced(C)
+<class C[TracedLogged]> created
+
+
+The ``decorators.enhance_classes(<classdecorator>)`` syntax performs
+the composition automagically:
+
+ ::
+
+ #<example8.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
+
+ #</example8.py>
+
+In this example the 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 example8 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
+
+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 (less than 150 lines if you skip comments and
+docstrings) but quite non-trivial, since it is based on extensive
+usage of metaclass wizardry. Moreover,
+it invokes the ``safetype`` module to solve metaclass conflicts, and
+``safetype`` contains 50 lines of *really* deep metaclass magic.
+
+ .. figure:: decorators.png
+
+The main class-metaclass hierarchy is represented in figure, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.
+
+For instance, this is the Method Resolution Order for the ``Decorated``
+metaclass:
+
+>>> for i,c in enumerate(decorators.Decorated.__mro__):
+... print i,c,"[%s]" % type(c).__name__
+0 <class 'decorators.Decorated'> [MetaDecorator]
+1 <class 'decorators.ClassDecorator'> [MetaDecorator]
+2 <class 'safetype.safetype'> [type]
+3 <type 'type'> [type]
+4 <class 'decorators.decorator'> [MetaDecorator]
+5 <type 'object'> [type]
+
+``Decorator`` is the mother of all decorators. Its main purpose it to
+provide the ``MetaDecorator``
+metaclass to all decorators.
+
+The ``safetype`` metaclass, imported from the ``safetype`` module,
+ensures that class decorators can generate classes without incurring
+in conflicts.
+
+Since class decorators are metaclasses,
+``MetaDecorator`` is actually a meta-metaclass with respect to
+instances of decorated classes. For this reason if
+
+>>> C=decorators.ClassDecorator('C',(),{})
+
+then
+
+>>> C.__class__
+<class 'decorators.ClassDecorator'>
+
+but
+
+>>> C.__metaclass__
+<class 'decorators.MetaDecorator'>
+
+since the ``C.__metaclass__`` attribute is inherited from ``Decorator``.
+
+On the other hand, ``MetaDecorator`` is a simple metaclass with
+respect to instances of decorated methods.
+
+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 ``ClassDecorator``. Moreover ``D``
+had a ``Logged`` docstring. Therefore it should have been an instance of a
+``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 this magic is in the ``safetype.remove_redundant(*classes)`` function.
+
+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. Probably, they do not 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
+~200 tests, most of them 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 fix it or document it.
+
+.. References:
+
+.. _PEP 318:
+ http://www.python.org/pep
+.. _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
+.. _first paper on metaclasses:
+ http://www-106.ibm.com/developerworks/library/l-pymeta.html
+.. _metaclasses: http://www-106.ibm.com/developerworks/library/l-pymeta2.html
+
+.. include:: decorators.rst
diff --git a/pypers/pep318/example.py b/pypers/pep318/example.py new file mode 100755 index 0000000..aedb6f3 --- /dev/null +++ b/pypers/pep318/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/example1.py b/pypers/pep318/example1.py new file mode 100755 index 0000000..5e8f7c8 --- /dev/null +++ b/pypers/pep318/example1.py @@ -0,0 +1,29 @@ +# example1.py + +import decorators + +def do_nothing(self): + "No magic docstring here" +dec_do_nothing=decorators.decorator(do_nothing) + +def identity(x): + "[staticmethod]" + return x +dec_identity=decorators.decorator(identity) + +def name(cls): + "[classmethod]" + return cls.__name__ +dec_name=decorators.decorator(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/example2.py b/pypers/pep318/example2.py new file mode 100755 index 0000000..0377c0c --- /dev/null +++ b/pypers/pep318/example2.py @@ -0,0 +1,22 @@ +# example2.py + +from decorators import decorator +from example1 import do_nothing,identity,name + +class B(object): + "This is a regular class" + +B=decorator(B) or B # return the original B + +class C(B): + "[Decorated]" + do_nothing=do_nothing + identity=identity + class Inner: # old style class + "[Decorated]" # required docstring + name=name + +C=decorator(C) # regenerate the class converting methods in decorators +c=C() + + diff --git a/pypers/pep318/example3.py b/pypers/pep318/example3.py new file mode 100755 index 0000000..5f3ddac --- /dev/null +++ b/pypers/pep318/example3.py @@ -0,0 +1,22 @@ +# example3.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/example4.py b/pypers/pep318/example4.py new file mode 100755 index 0000000..723c7c5 --- /dev/null +++ b/pypers/pep318/example4.py @@ -0,0 +1,20 @@ +# example4.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/example5.py b/pypers/pep318/example5.py new file mode 100755 index 0000000..81ebce9 --- /dev/null +++ b/pypers/pep318/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/example6.py b/pypers/pep318/example6.py new file mode 100755 index 0000000..145c06a --- /dev/null +++ b/pypers/pep318/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/example7.py b/pypers/pep318/example7.py new file mode 100755 index 0000000..ce12589 --- /dev/null +++ b/pypers/pep318/example7.py @@ -0,0 +1,15 @@ +# example7.py + +from example2 import identity,name +import inspect, decorators; decorators.enhance_classes("[Decorated]") + +class C1(object): # automagically converted to a decorated class + identity=identity + +class C2: # automagically converted to a decorated class + name=name + +c1=C1() # C1 instance +c2=C2() # C2 instance + + diff --git a/pypers/pep318/example8.py b/pypers/pep318/example8.py new file mode 100755 index 0000000..5b4f9df --- /dev/null +++ b/pypers/pep318/example8.py @@ -0,0 +1,16 @@ +# example8.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/example9.py b/pypers/pep318/example9.py new file mode 100755 index 0000000..8a6fd33 --- /dev/null +++ b/pypers/pep318/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/lessmeta/decorators.py b/pypers/pep318/lessmeta/decorators.py new file mode 100755 index 0000000..b452c24 --- /dev/null +++ b/pypers/pep318/lessmeta/decorators.py @@ -0,0 +1,210 @@ +# Simpler implementation with smaller usage of metaclasses + +""" +A module to implement pep318 (decorator syntax) via magic doctrings. +It defines two new classes, MethodDecorator and ClassDecorator, +which are meant to be subclassed. + +def f(x): + "[chattymethod]" + +as a shortcut for + +def f(x): + pass +f=chattymethod(f) + +Decorators can be composed and for instance + +def f(x): + "[chattymethod,classmethod]" + +is a shortcut for + +def f(x): + pass +f=chattymethodclassmethod(f) + +where 'chattymethodclassmethod' is a decorator class obtained by +multiple inheriting from chattymethod and classmethod. +Notice that the built-in classmethod descriptor (idem for +staticmethod) is non=cooperatice, whereas the custom +descriptor chattymethod is cooperative: this means that +the custom descriptor has to be put *first* in the list. + +The implementation stores the generated descriptors in a +dictionary, and avoids creating unneeded subclasses. + +The names in the module are quite specific, since I am trying to avoid +troubles to people using the form "from decorator import *". +""" + +import sys,re,inspect,__builtin__,time,noconflict +anyTrue=sum + +############################ UTILITIES ############################## + +class UnknownDecoratorError(Exception): + "In case of mistakes" + +class StoredDecorators(type): + "Metaclass storing its instances in the dictionary dic" + dic={} + + def __init__(cls,*args): + super(StoredDecorators,cls).__init__(*args) + StoredDecorators.dic[cls.__name__]=cls + get=cls.__dict__.get('get') # if get + if get: cls.__get__=get # set __get__ + + def methodlike(mcl): + "List of recognized MethodDecorators" + return [name for name in mcl.dic + if issubclass(mcl.dic[name],MethodDecorator)] + methodlike=classmethod(methodlike) + + def classlike(mcl): + "List of recognized ClassDecorators" + return [name for name in mcl.dic + if issubclass(mcl.dic[name],ClassDecorator)] + classlike=classmethod(classlike) + +def decorate(obj='THISMODULE'): + """ + obj can be: + - the string 'THISMODULE' + in this case magic docstrings are interpreted in the new + style classes of the calling module; + - the string 'ALLMODULES' + in this case magic docstrings are interpreted in the new + style classes of ALL modules; + - a dictionary or an object with a dictionary + in this case magic docstrings are interpreted in all the + functions and classes in the dictionary + """ + dic={} + if obj=='THISMODULE': + callerglobals=sys._getframe(1).f_globals + callerglobals['object']=_EnableMagicDocstrings + elif obj=='ALLMODULES': + __builtin__.object=_EnableMagicDocstrings + elif isinstance(obj,dict): + dic=obj + elif hasattr(obj,'__dict__'): + dic=obj.__dict__ + else: + raise TypeError("Dictionary or object with a __dict__ required") + for name,value in dic.items(): + _set(obj,name,value) + +def _decorate(obj): + """Given an object with a magic docstrings, returns its decorated + version; otherwise, returns None""" + docstring=inspect.getdoc(obj) or '' + MO=re.match(r'\[([\w_ ,]*)\]',docstring) + # for instance [name_1, name_2, name_3] is matched + if MO: + decorators=MO.group(1).split(',') + try: dclasses=[StoredDecorators.dic[n] for n in decorators] + except KeyError: raise UnknownDecoratorError(n) + dclasses=noconflict.remove_redundant(dclasses) + decname=''.join([d.__name__ for d in dclasses]) + decorator=StoredDecorators.dic.get(decname) + if not decorator: decorator=makecls()(decname,dclasses,{}) + if issubclass(decorator,ClassDecorator): return decorator(obj)() + return decorator(obj) + +def _set(objdict,name,obj): + dec=_decorate(obj) + if dec: + dec.inside=objdict + if isinstance(objdict,dict): + objdict[name]=dec + else: + setattr(objdict,name,dec) + +class _MagicDocstrings: + def __init__(cls,name,bases,dic): + decorate(cls) # both cls and its dictionary + +class _EnableMagicDocstrings: + __metaclass__=_MagicDocstrings + +class Decorator(object): + """Instance of StoredDecorators, i.e. each time Decorator is + subclassed, StoredDecorators.dic is updated. Provides a setattributes + method to recognize magic attributes with ``set_`` prefix. + Provides an 'inside' attribute (default='?')""" + __metaclass__=StoredDecorators + inside=type('?',(),{}) # default placeholder class (to be + # replaced by the class that contains the decorated method) + def setattributes(self): + for a in dir(self): + if a.startswith('set_'): + setattr(self,a[4:],getattr(self,a)) + +class MethodDecorator(Decorator): + """MethodDecorator objects provide a 'get' method and a 'str' method""" + def __init__(self,func): + super(MethodDecorator,self).__init__(func) + self.func=func; self.setattributes() + def get(self,obj,cls=None): # default, to be overridden + return self.func.__get__(obj,cls) + def __str__(self): + return '<%s:%s>' % (self.__class__.__name__,self.func.__name__) + +class ClassDecorator(Decorator): + """ClassDecorator takes a class as argument and returns a callable + object acting as a factory of decorated objects""" + def __init__(self,klass): + super(ClassDecorator,self).__init__(klass) + self.klass=klass; self.setattributes() + def __call__(self): # to be cooperatively overridden + return self.klass + def __str__(self): + return '<%s:%s>' % (self.__class__.__name__,self.klass.__name__) + +#################### Useful Method Decorators ###################### + +class staticmethod(MethodDecorator): + "Decorator, converts a function in a staticmethod" + def get(self,obj,cls=None): + super(staticmethod,self).get(obj,cls) + return self.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) + +class tracedmethod(MethodDecorator): + "Descriptor class, converts a function 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): + clsname=self.inside.__name__ + boundmethod=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,clsname,self.funcname)) + self.output.write("%s ...\n" % (str(args)+str(kw))) + res=boundmethod(*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 _ + +####################### Class Decorators ############################### + +class Register(ClassDecorator): + output=sys.stdout + def __call__(self): + cls=super(Register,self).__call__() + print >> self.output, "%s: %s created" % (time.asctime(),cls) + return cls diff --git a/pypers/pep318/logged.py b/pypers/pep318/logged.py new file mode 100755 index 0000000..04be3fd --- /dev/null +++ b/pypers/pep318/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/mod.py b/pypers/pep318/mod.py new file mode 100755 index 0000000..4e7073d --- /dev/null +++ b/pypers/pep318/mod.py @@ -0,0 +1,11 @@ +# mod.py + +#"[TraceFunctions]" + +def f1(): pass + +def f2(): pass + +def f3(): "-untraced-" + + diff --git a/pypers/pep318/module.py b/pypers/pep318/module.py new file mode 100755 index 0000000..d22c798 --- /dev/null +++ b/pypers/pep318/module.py @@ -0,0 +1,13 @@ +# module.py + +"Magically decorated module" + +import decorators,sys + +thismodule=sys.modules[__name__] + +class MyClass: "[Decorated]" + +newmod=decorators.decorated(thismodule) + + diff --git a/pypers/pep318/moduledec.py b/pypers/pep318/moduledec.py new file mode 100755 index 0000000..ccaa73c --- /dev/null +++ b/pypers/pep318/moduledec.py @@ -0,0 +1,51 @@ + + +err=file('err','w') + +def printerr(*args): + "For debugging purposes" + for a in args: print >> err, a, + print >> err + +def importmodule(name,dic): + """Given a module name and a dictionary, executes the module in a copy + of the dictionary and returns a new module.""" + already_imported=sys.modules.get(name) + if already_imported: return already_imported # do nothing + fname=name+'.py' + dic=dic.copy() + execfile(fname,dic) + mod=types.ModuleType(name) + for k,v in dic.iteritems(): + setattr(mod,k,v) + sys.modules[name]=mod + mod.__name__=name + + mod.__file__=fname + return mod + +class ModuleDecorator(Decorator,types.ModuleType): + def __init__(self,mod): # non-cooperative + self.undecorated=mod + for k,v in mod.__dict__.iteritems(): + setattr(self,k,v) + decorate(self) + def __str__(self): + return '<module %s[%s]>' % (self.mod.__name__,self.__class__.__name__) + +class DecoratedModule(ModuleDecorator): # defined for readability + pass + +def callModuleDecorator(dec,*args): + if issubclass(dec,ModuleDecorator): + nargs=len(args) + if nargs==1: + mod=args[0] + elif nargs==2: + name,glob=args # a different object for each module + glob['object']=ClassDecorator(object) + mod=importmodule(name,glob) + else: + raise TypeError('%s() takes 1 or 2 arguments' % dec.__name__) + return type.__call__(dec,mod) + diff --git a/pypers/pep318/moduledec.txt b/pypers/pep318/moduledec.txt new file mode 100755 index 0000000..602aec4 --- /dev/null +++ b/pypers/pep318/moduledec.txt @@ -0,0 +1,97 @@ + + + +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> + + 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 traced(func): + def tracedfunc(*args,**kw): + print 'called',func.__name__ + return func(*args,**kw) + return tracedfunc + setattr(self,name,traced(func)) + + #</customdec.py> + +There is no way of tinkering with the attribute access in modules (as +opposed to attribute access in classes) so we cannot used descriptors +here and we are forced to use closures. + +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 + +One has always access to the original, undecorated module, via the +``undecorated`` attribute: + +>>> orig=mod.undecorated +>>> orig.f1() # 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) + diff --git a/pypers/pep318/mydoc.html b/pypers/pep318/mydoc.html new file mode 100755 index 0000000..fb70714 --- /dev/null +++ b/pypers/pep318/mydoc.html @@ -0,0 +1,51 @@ +<?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>Documentation of the mydoc module</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="documentation-of-the-mydoc-module"> +<h1 class="title">Documentation of the mydoc module</h1> +<p>Short utility to extract documentation from a module</p> +<div class="section" id="documented-functions"> +<h1><a name="documented-functions">Documented functions</a></h1> +<p><tt class="literal"><span class="pre">publish_cmdline(reader=None,</span> <span class="pre">reader_name='standalone'</span></tt></p> +<blockquote> +<p>Set up & run a <cite>Publisher</cite>. For command-line front ends.</p> +<p>Parameters:</p> +<ul class="simple"> +<li><cite>reader</cite>: A <cite>docutils.readers.Reader</cite> object.</li> +<li><cite>reader_name</cite>: Name or alias of the Reader class to be instantiated if +no <cite>reader</cite> supplied.</li> +<li><cite>parser</cite>: A <cite>docutils.parsers.Parser</cite> object.</li> +<li><cite>parser_name</cite>: Name or alias of the Parser class to be instantiated if +no <cite>parser</cite> supplied.</li> +<li><cite>writer</cite>: A <cite>docutils.writers.Writer</cite> object.</li> +<li><cite>writer_name</cite>: Name or alias of the Writer class to be instantiated if +no <cite>writer</cite> supplied.</li> +<li><cite>settings</cite>: Runtime settings object.</li> +<li><cite>settings_spec</cite>: Extra settings specification; a <cite>docutils.SettingsSpec</cite> +subclass. Used only if no <cite>settings</cite> specified.</li> +<li><cite>settings_overrides</cite>: A dictionary containing program-specific overrides +of component settings.</li> +<li><cite>argv</cite>: Command-line argument list to use instead of <tt class="literal"><span class="pre">sys.argv[1:]</span></tt>.</li> +<li><cite>usage</cite>: Usage string, output if there's a problem parsing the command +line.</li> +<li><cite>description</cite>: Program description, output for the "--help" option +(along with command-line option descriptions).</li> +</ul> +</blockquote> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="mydoc.rst">View document source</a>. +Generated on: 2003-09-20 11:43 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/nonrecognized.py b/pypers/pep318/nonrecognized.py new file mode 100755 index 0000000..d8e3be5 --- /dev/null +++ b/pypers/pep318/nonrecognized.py @@ -0,0 +1,23 @@ +# nonrecognized.py + +"Classes and functions inside a function are not decorated" + +import decorators; decorators.enable() + +def outer(): + + class C(object): + def f(): + "[staticmethod]" + + def g(): + "[staticmethod]" + + # testing + + assert isinstance(C.__dict__['f'],decorators.staticmethod) + assert not isinstance(g,decorators.staticmethod) + +outer() + + diff --git a/pypers/pep318/oopp.html b/pypers/pep318/oopp.html new file mode 100755 index 0000000..948dad1 --- /dev/null +++ b/pypers/pep318/oopp.html @@ -0,0 +1,324 @@ +<?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>Documentation of the oopp module</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="documentation-of-the-oopp-module"> +<h1 class="title">Documentation of the oopp module</h1> +<div class="section" id="documented-metaclasses"> +<h1><a name="documented-metaclasses">Documented metaclasses</a></h1> +<p><tt class="literal"><span class="pre">Final(type):</span> <span class="pre">#</span> <span class="pre">better</span> <span class="pre">derived</span> <span class="pre">from</span> <span class="pre">WithCounter,typ</span></tt></p> +<blockquote> +Instances of Final cannot be derived</blockquote> +<p><tt class="literal"><span class="pre">AutoWrapped(type)</span></tt></p> +<blockquote> +Metaclass that looks at the methods declared in the attributes +builtinlist and wraplist of its instances and wraps them with +autowrappedmethod.</blockquote> +<p><tt class="literal"><span class="pre">Wrapped(Customizable,type)</span></tt></p> +<blockquote> +<p>A customizable metaclass to wrap methods with a given wrapper and +a given condition</p> +<p><tt class="literal"><span class="pre">Reflective(type)</span></tt></p> +<blockquote> +Cooperative metaclass that defines the private variable __this in +its instances. __this contains a reference to the class, therefore +it allows anonymous cooperative super calls in the class.</blockquote> +<p><tt class="literal"><span class="pre">wrappedmethod(Customizable)</span></tt></p> +<blockquote> +Customizable method factory intended for derivation. +The wrapper method is overridden in the children.</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">Cooperative(BracketCallable,type)</span></tt></p> +<blockquote> +<p>Bracket-callable metaclass implementing cooperative methods. Works +well for plain methods returning None, such as __init__</p> +<p><tt class="literal"><span class="pre">coop_method(cls,name,method):</span> <span class="pre">#</span> <span class="pre">method</span> <span class="pre">can</span> <span class="pre">be</span> <span class="pre">Non</span></tt></p> +<blockquote> +Calls both the superclass method and the class method (if the +class has an explicit method). Implemented via a closure</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">Logged(WithLogger,Reflective)</span></tt></p> +<blockquote> +Metaclass that reuses the features provided by WithLogger. +In particular the classes created by Logged are Reflective, +PrettyPrinted and Customizable.</blockquote> +<p><tt class="literal"><span class="pre">MagicallyTransformed(type)</span></tt></p> +<blockquote> +Metaclass changing the formatstring of its instances</blockquote> +<p><tt class="literal"><span class="pre">Printable(PrettyPrinted,type)</span></tt></p> +<blockquote> +Apparently does nothing, but actually makes PrettyPrinted acting as +a metaclass.</blockquote> +</div> +<div class="section" id="documented-classes"> +<h1><a name="documented-classes">Documented classes</a></h1> +<p><tt class="literal"><span class="pre">convert2descriptor(object)</span></tt></p> +<blockquote> +<p>To all practical means, this class acts as a function that, given an +object, adds to it a __get__ method if it is not already there. The +added __get__ method is trivial and simply returns the original object, +independently from obj and cls.</p> +<p><tt class="literal"><span class="pre">__get__(self,obj,cls=None)</span></tt></p> +<blockquote> +Returns self.a independently from obj and cls</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">Object(object)</span></tt></p> +<blockquote> +A convenient Object class</blockquote> +<p><tt class="literal"><span class="pre">GeometricFigure(object):</span> <span class="pre">#an</span> <span class="pre">example</span> <span class="pre">of</span> <span class="pre">object</span> <span class="pre">factor</span></tt></p> +<blockquote> +<p>This class allows to define geometric figures according to their +equation in the cartesian plane. It will be extended later.</p> +<p><tt class="literal"><span class="pre">__init__(self,equation,**parameters)</span></tt></p> +<blockquote> +Specify the cartesian equation of the object and its parameters</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">UglyDuckling(PrettyPrinted)</span></tt></p> +<blockquote> +A plain, regular class</blockquote> +<p><tt class="literal"><span class="pre">autowrappedmethod(wrappedmethod)</span></tt></p> +<blockquote> +Makes the method returning cls instances, by wrapping its +output with cls</blockquote> +<p><tt class="literal"><span class="pre">Clock(object)</span></tt></p> +<blockquote> +Clock with a staticmethod</blockquote> +<p><tt class="literal"><span class="pre">AccessError(object)</span></tt></p> +<blockquote> +Descriptor raising an AttributeError when the attribute is +accessed</blockquote> +<p><tt class="literal"><span class="pre">Frozen(object)</span></tt></p> +<blockquote> +Subclasses of Frozen are frozen, i.e. it is impossibile to add +new attributes to them and their instances</blockquote> +<p><tt class="literal"><span class="pre">Timer(Clock)</span></tt></p> +<blockquote> +Inherits the get_time staticmethod from Clock</blockquote> +<p><tt class="literal"><span class="pre">Vector(list)</span></tt></p> +<blockquote> +Implements finite dimensional vectors as lists. Can be instantiated +as Vector([a,b,c,..]) or as Vector(a,b,c ..)</blockquote> +<p><tt class="literal"><span class="pre">Singleton(WithCounter)</span></tt></p> +<blockquote> +If you inherit from me, you can only have one instance</blockquote> +<p><tt class="literal"><span class="pre">Homo(PrettyPrinted)</span></tt></p> +<blockquote> +Defines the method 'can', which is intended to be overriden +in the children classes, and inherits '__str__' from PrettyPrinted, +ensuring a nice printing representation for all children.</blockquote> +<p><tt class="literal"><span class="pre">kwdict(dict)</span></tt></p> +<blockquote> +<p>Keyword dictionary base class</p> +<p><tt class="literal"><span class="pre">pretty(dic)</span></tt></p> +<blockquote> +Returns a nice string representation for the dictionary</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">WithLogger(WithCounter,PrettyPrinted)</span></tt></p> +<blockquote> +WithLogger inherits from WithCounter the 'count' class attribute; +moreover it inherits '__str__' from PrettyPrinted</blockquote> +<p><tt class="literal"><span class="pre">Get(object)</span></tt></p> +<blockquote> +Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod, +property, plainmethod, plaindata, returns the corresponding +attributes as a keyword dictionary. It works by internally calling +the routine inspect.classify_class_attrs. Notice that special data +attributes are not retrieved (this is by design).</blockquote> +<p><tt class="literal"><span class="pre">Makeobj(object)</span></tt></p> +<blockquote> +A factory of object factories. Makeobj(cls) returns instances +of cls</blockquote> +<p><tt class="literal"><span class="pre">ExampleBaseClass(PrettyPrinted)</span></tt></p> +<blockquote> +Contains a regular method 'm', a staticmethod 's', a classmethod +'c', a property 'p' and a data attribute 'd'.</blockquote> +<p><tt class="literal"><span class="pre">TransformedUglyDuckling(PrettyPrinted)</span></tt></p> +<blockquote> +A class metamagically modified</blockquote> +<p><tt class="literal"><span class="pre">Customizable(object)</span></tt></p> +<blockquote> +Classes inhering from 'Customizable' have a 'with' method acting as +an object modifier and 'With' classmethod acting as a class factory</blockquote> +<p><tt class="literal"><span class="pre">WithMultiCounter(WithCounter)</span></tt></p> +<blockquote> +Each time a new subclass is derived, the counter is reset</blockquote> +<p><tt class="literal"><span class="pre">BracketCallable(object)</span></tt></p> +<blockquote> +Any subclass C(BracketCallable) can be called with the syntax C[t], +where t is a tuple of arguments stored in bracket_args; returns the +class or an instance of it, depending on the flag 'returnclass'.</blockquote> +<p><tt class="literal"><span class="pre">ClsFactory(BracketCallable)</span></tt></p> +<blockquote> +<dl> +<dt>Bracket callable non-cooperative class acting as </dt> +<dd>a factory of class factories. +ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.</dd> +<dt>. They automatically converts functions to static methods </dt> +<dd>if the input object is not a class. If an explicit name is not passed +the name of the created class is obtained by adding an underscore to +the name of the original object.</dd> +</dl> +<p><tt class="literal"><span class="pre">__call__(self,</span> <span class="pre">*args)</span></tt></p> +<blockquote> +Generates a new class using self.meta and avoiding conflicts. +The first metaobject can be a dictionary, an object with a +dictionary (except a class), or a simple name.</blockquote> +</blockquote> +<p><tt class="literal"><span class="pre">WithCounter(object)</span></tt></p> +<blockquote> +Mixin class counting the total number of its instances and storing +it in the class attribute counter.</blockquote> +</div> +<div class="section" id="documented-functions"> +<h1><a name="documented-functions">Documented functions</a></h1> +<p><tt class="literal"><span class="pre">mandelbrot(row,col)</span></tt></p> +<blockquote> +Computes the Mandelbrot set in one line</blockquote> +<p><tt class="literal"><span class="pre">with_tracer(function,namespace='__main__',output=sys.stdout,</span> <span class="pre">indent=[0])</span></tt></p> +<blockquote> +Closure returning traced functions. It is typically invoked +trough an auxiliary function fixing the parameters of with_tracer.</blockquote> +<p><tt class="literal"><span class="pre">ancestor(C,S=None)</span></tt></p> +<blockquote> +Returns the ancestors of the first argument with respect to the +MRO of the second argument. If the second argument is None, then +returns the MRO of the first argument.</blockquote> +<p><tt class="literal"><span class="pre">customize(obj,errfile=None,**kw)</span></tt></p> +<blockquote> +Adds attributes to an object, if possible. If not, writes an error +message on 'errfile'. If errfile is None, skips the exception.</blockquote> +<p><tt class="literal"><span class="pre">loop_overhead(N)</span></tt></p> +<blockquote> +Computes the time spent in empty loop of N iterations</blockquote> +<p><tt class="literal"><span class="pre">indent(block,n)</span></tt></p> +<blockquote> +Indent a block of code by n spaces</blockquote> +<p><tt class="literal"><span class="pre">totuple(arg)</span></tt></p> +<blockquote> +Converts the argument to a tuple, if need there is</blockquote> +<p><tt class="literal"><span class="pre">attributes(obj,condition=lambda</span> <span class="pre">n,v:</span> <span class="pre">not</span> <span class="pre">special(n))</span></tt></p> +<blockquote> +Returns a dictionary containing the accessible attributes of +an object. By default, returns the non-special attributes only.</blockquote> +<p><tt class="literal"><span class="pre">generateblocks(regexp,text)</span></tt></p> +<blockquote> +Generator splitting text in blocks according to regexp</blockquote> +<p><tt class="literal"><span class="pre">wrapfunctions(obj,wrapper,err=None,**options)</span></tt></p> +<blockquote> +Traces the callable objects in an object with a dictionary</blockquote> +<p><tt class="literal"><span class="pre">wrap(obj,wrapped,condition=lambda</span> <span class="pre">k,v:</span> <span class="pre">True,</span> <span class="pre">err=None)</span></tt></p> +<blockquote> +Retrieves obj's dictionary and wraps it</blockquote> +<p><tt class="literal"><span class="pre">reflective(*classes)</span></tt></p> +<blockquote> +Reflective classes know themselves, i.e. they own a private +attribute __this containing a reference to themselves. If the class +name starts with '_', the underscores are stripped. This is needed +to make the mangling mechanism work.</blockquote> +<p><tt class="literal"><span class="pre">withmemory(f)</span></tt></p> +<blockquote> +This closure invokes the callable object f only if need there is</blockquote> +<p><tt class="literal"><span class="pre">get_time()</span></tt></p> +<blockquote> +Return the time of the system in the format HH:MM:SS</blockquote> +<p><tt class="literal"><span class="pre">prn(s)</span></tt></p> +<blockquote> +Given an evaluable string, print its value and its object reference. +Notice that the evaluation is done in the __main__ dictionary.</blockquote> +<p><tt class="literal"><span class="pre">isplaindata(a)</span></tt></p> +<blockquote> +A data attribute has no __get__ or __set__ attributes, is not +a built-in function, nor a built-in method.</blockquote> +<p><tt class="literal"><span class="pre">Pizza(toppings,**dic)</span></tt></p> +<blockquote> +This function produces classes inheriting from GenericPizza and +WithLogger, using a metaclass inferred from Logged</blockquote> +<p><tt class="literal"><span class="pre">modulesub(s,r,module)</span></tt></p> +<blockquote> +Requires 2.3</blockquote> +<p><tt class="literal"><span class="pre">memoize(f)</span></tt></p> +<blockquote> +This closure remembers all f invocations</blockquote> +<p><tt class="literal"><span class="pre">with_timer(func,</span> <span class="pre">modulename='__main__',</span> <span class="pre">n=1,</span> <span class="pre">logfile=sys.stdout)</span></tt></p> +<blockquote> +Wraps the function func and executes it n times (default n=1). +The average time spent in one iteration, express in milliseconds, +is stored in the attributes func.time and func.CPUtime, and saved +in a log file which defaults to the standard output.</blockquote> +<p><tt class="literal"><span class="pre">dedent(text)</span></tt></p> +<blockquote> +<p>dedent(text : string) -> string</p> +<blockquote> +<blockquote> +<p>Remove any whitespace than can be uniformly removed from the left +of every line in <cite>text</cite>.</p> +<p>This can be used e.g. to make triple-quoted strings line up with +the left edge of screen/whatever, while still presenting it in the +source code in indented form.</p> +<p>For example:</p> +<blockquote> +<blockquote> +<dl> +<dt>def test():</dt> +<dd><p class="first"># end first line with to avoid the empty line! +s = ''' hello</p> +<div class="system-message"> +<p class="system-message-title">System Message: ERROR/3 (<tt>oopp.rst</tt>, line 337)</p> +Unexpected indentation.</div> +<blockquote> +world</blockquote> +<div class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 338)</p> +Block quote ends without a blank line; unexpected unindent.</div> +<p class="last">''' +print repr(s) # prints ' hello</p> +</dd> +</dl> +</blockquote> +<div class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 340)</p> +Block quote ends without a blank line; unexpected unindent.</div> +<p>world</p> +</blockquote> +<div class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 341)</p> +Block quote ends without a blank line; unexpected unindent.</div> +<dl> +<dt>'</dt> +<dd>print repr(dedent(s)) # prints 'hello</dd> +</dl> +</blockquote> +<div class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 343)</p> +Block quote ends without a blank line; unexpected unindent.</div> +<p>world</p> +</blockquote> +<div class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 344)</p> +Block quote ends without a blank line; unexpected unindent.</div> +<p>'</p> +</blockquote> +<p><tt class="literal"><span class="pre">makecls(*metas,**options)</span></tt></p> +<blockquote> +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</blockquote> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="oopp.rst">View document source</a>. +Generated on: 2003-09-20 12:54 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/oopp.tex b/pypers/pep318/oopp.tex new file mode 100755 index 0000000..e5ffd3c --- /dev/null +++ b/pypers/pep318/oopp.tex @@ -0,0 +1,573 @@ +\documentclass[10pt,english]{article} +\usepackage{babel} +\usepackage{shortvrb} +\usepackage[latin1]{inputenc} +\usepackage{tabularx} +\usepackage{longtable} +\setlength{\extrarowheight}{2pt} +\usepackage{amsmath} +\usepackage{graphicx} +\usepackage{color} +\usepackage{multirow} +\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} +\usepackage[a4paper,margin=2cm,nohead]{geometry} +%% generator Docutils: http://docutils.sourceforge.net/ +\newlength{\admonitionwidth} +\setlength{\admonitionwidth}{0.9\textwidth} +\newlength{\docinfowidth} +\setlength{\docinfowidth}{0.9\textwidth} +\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newenvironment{optionlist}[1] +{\begin{list}{} + {\setlength{\labelwidth}{#1} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\optionlistlabel}} +}{\end{list}} +% begin: floats for footnotes tweaking. +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50} +% end floats for footnotes +% some commands, that could be overwritten in the style file. +\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}} +% end of "some commands" +\input{/mnt/exp/MyDocs/pypers/style.tex} +\title{Documentation of the oopp module} +\author{} +\date{} +\hypersetup{ +pdftitle={Documentation of the oopp module} +} +\raggedbottom +\begin{document} +\maketitle + + + +%___________________________________________________________________________ + +\hypertarget{documented-metaclasses}{} +\section*{Documented metaclasses} +\pdfbookmark[0]{Documented metaclasses}{documented-metaclasses} + +\texttt{Final(type): {\#} better derived from WithCounter,typ} +\begin{quote} + +Instances of Final cannot be derived +\end{quote} + +\texttt{AutoWrapped(type)} +\begin{quote} + +Metaclass that looks at the methods declared in the attributes +builtinlist and wraplist of its instances and wraps them with +autowrappedmethod. +\end{quote} + +\texttt{Wrapped(Customizable,type)} +\begin{quote} + +A customizable metaclass to wrap methods with a given wrapper and +a given condition +\begin{quote} + +\texttt{Reflective(type)} +\begin{quote} + +Cooperative metaclass that defines the private variable {\_}{\_}this in +its instances. {\_}{\_}this contains a reference to the class, therefore +it allows anonymous cooperative super calls in the class. +\end{quote} + +\texttt{wrappedmethod(Customizable)} +\begin{quote} + +Customizable method factory intended for derivation. +The wrapper method is overridden in the children. +\end{quote} +\end{quote} +\end{quote} + +\texttt{Cooperative(BracketCallable,type)} +\begin{quote} + +Bracket-callable metaclass implementing cooperative methods. Works +well for plain methods returning None, such as {\_}{\_}init{\_}{\_} + +\texttt{coop{\_}method(cls,name,method): {\#} method can be Non} +\begin{quote} + +Calls both the superclass method and the class method (if the +class has an explicit method). Implemented via a closure +\end{quote} +\end{quote} + +\texttt{Logged(WithLogger,Reflective)} +\begin{quote} + +Metaclass that reuses the features provided by WithLogger. +In particular the classes created by Logged are Reflective, +PrettyPrinted and Customizable. +\end{quote} + +\texttt{MagicallyTransformed(type)} +\begin{quote} + +Metaclass changing the formatstring of its instances +\end{quote} + +\texttt{Printable(PrettyPrinted,type)} +\begin{quote} + +Apparently does nothing, but actually makes PrettyPrinted acting as +a metaclass. +\end{quote} + + +%___________________________________________________________________________ + +\hypertarget{documented-classes}{} +\section*{Documented classes} +\pdfbookmark[0]{Documented classes}{documented-classes} + +\texttt{convert2descriptor(object)} +\begin{quote} + +To all practical means, this class acts as a function that, given an +object, adds to it a {\_}{\_}get{\_}{\_} method if it is not already there. The +added {\_}{\_}get{\_}{\_} method is trivial and simply returns the original object, +independently from obj and cls. + +\texttt{{\_}{\_}get{\_}{\_}(self,obj,cls=None)} +\begin{quote} + +Returns self.a independently from obj and cls +\end{quote} +\end{quote} + +\texttt{Object(object)} +\begin{quote} + +A convenient Object class +\end{quote} + +\texttt{GeometricFigure(object): {\#}an example of object factor} +\begin{quote} + +This class allows to define geometric figures according to their +equation in the cartesian plane. It will be extended later. + +\texttt{{\_}{\_}init{\_}{\_}(self,equation,**parameters)} +\begin{quote} + +Specify the cartesian equation of the object and its parameters +\end{quote} +\end{quote} + +\texttt{UglyDuckling(PrettyPrinted)} +\begin{quote} + +A plain, regular class +\end{quote} + +\texttt{autowrappedmethod(wrappedmethod)} +\begin{quote} + +Makes the method returning cls instances, by wrapping its +output with cls +\end{quote} + +\texttt{Clock(object)} +\begin{quote} + +Clock with a staticmethod +\end{quote} + +\texttt{AccessError(object)} +\begin{quote} + +Descriptor raising an AttributeError when the attribute is +accessed +\end{quote} + +\texttt{Frozen(object)} +\begin{quote} + +Subclasses of Frozen are frozen, i.e. it is impossibile to add +new attributes to them and their instances +\end{quote} + +\texttt{Timer(Clock)} +\begin{quote} + +Inherits the get{\_}time staticmethod from Clock +\end{quote} + +\texttt{Vector(list)} +\begin{quote} + +Implements finite dimensional vectors as lists. Can be instantiated +as Vector([a,b,c,..]) or as Vector(a,b,c ..) +\end{quote} + +\texttt{Singleton(WithCounter)} +\begin{quote} + +If you inherit from me, you can only have one instance +\end{quote} + +\texttt{Homo(PrettyPrinted)} +\begin{quote} + +Defines the method 'can', which is intended to be overriden +in the children classes, and inherits '{\_}{\_}str{\_}{\_}' from PrettyPrinted, +ensuring a nice printing representation for all children. +\end{quote} + +\texttt{kwdict(dict)} +\begin{quote} + +Keyword dictionary base class + +\texttt{pretty(dic)} +\begin{quote} + +Returns a nice string representation for the dictionary +\end{quote} +\end{quote} + +\texttt{WithLogger(WithCounter,PrettyPrinted)} +\begin{quote} + +WithLogger inherits from WithCounter the 'count' class attribute; +moreover it inherits '{\_}{\_}str{\_}{\_}' from PrettyPrinted +\end{quote} + +\texttt{Get(object)} +\begin{quote} + +Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod, +property, plainmethod, plaindata, returns the corresponding +attributes as a keyword dictionary. It works by internally calling +the routine inspect.classify{\_}class{\_}attrs. Notice that special data +attributes are not retrieved (this is by design). +\end{quote} + +\texttt{Makeobj(object)} +\begin{quote} + +A factory of object factories. Makeobj(cls) returns instances +of cls +\end{quote} + +\texttt{ExampleBaseClass(PrettyPrinted)} +\begin{quote} + +Contains a regular method 'm', a staticmethod 's', a classmethod +'c', a property 'p' and a data attribute 'd'. +\end{quote} + +\texttt{TransformedUglyDuckling(PrettyPrinted)} +\begin{quote} + +A class metamagically modified +\end{quote} + +\texttt{Customizable(object)} +\begin{quote} + +Classes inhering from 'Customizable' have a 'with' method acting as +an object modifier and 'With' classmethod acting as a class factory +\end{quote} + +\texttt{WithMultiCounter(WithCounter)} +\begin{quote} + +Each time a new subclass is derived, the counter is reset +\end{quote} + +\texttt{BracketCallable(object)} +\begin{quote} + +Any subclass C(BracketCallable) can be called with the syntax C[t], +where t is a tuple of arguments stored in bracket{\_}args; returns the +class or an instance of it, depending on the flag 'returnclass'. +\end{quote} + +\texttt{ClsFactory(BracketCallable)} +\begin{quote} +\begin{description} +%[visit_definition_list_item] +\item[Bracket callable non-cooperative class acting as :] +%[visit_definition] + +a factory of class factories. +ClsFactory instances are class factories accepting 0,1,2 or 3 arguments. + +%[depart_definition] +%[depart_definition_list_item] +%[visit_definition_list_item] +\item[. They automatically converts functions to static methods :] +%[visit_definition] + +if the input object is not a class. If an explicit name is not passed +the name of the created class is obtained by adding an underscore to +the name of the original object. + +%[depart_definition] +%[depart_definition_list_item] +\end{description} + +\texttt{{\_}{\_}call{\_}{\_}(self, *args)} +\begin{quote} + +Generates a new class using self.meta and avoiding conflicts. +The first metaobject can be a dictionary, an object with a +dictionary (except a class), or a simple name. +\end{quote} +\end{quote} + +\texttt{WithCounter(object)} +\begin{quote} + +Mixin class counting the total number of its instances and storing +it in the class attribute counter. +\end{quote} + + +%___________________________________________________________________________ + +\hypertarget{documented-functions}{} +\section*{Documented functions} +\pdfbookmark[0]{Documented functions}{documented-functions} + +\texttt{mandelbrot(row,col)} +\begin{quote} + +Computes the Mandelbrot set in one line +\end{quote} + +\texttt{with{\_}tracer(function,namespace='{\_}{\_}main{\_}{\_}',output=sys.stdout, indent=[0])} +\begin{quote} + +Closure returning traced functions. It is typically invoked +trough an auxiliary function fixing the parameters of with{\_}tracer. +\end{quote} + +\texttt{ancestor(C,S=None)} +\begin{quote} + +Returns the ancestors of the first argument with respect to the +MRO of the second argument. If the second argument is None, then +returns the MRO of the first argument. +\end{quote} + +\texttt{customize(obj,errfile=None,**kw)} +\begin{quote} + +Adds attributes to an object, if possible. If not, writes an error +message on 'errfile'. If errfile is None, skips the exception. +\end{quote} + +\texttt{loop{\_}overhead(N)} +\begin{quote} + +Computes the time spent in empty loop of N iterations +\end{quote} + +\texttt{indent(block,n)} +\begin{quote} + +Indent a block of code by n spaces +\end{quote} + +\texttt{totuple(arg)} +\begin{quote} + +Converts the argument to a tuple, if need there is +\end{quote} + +\texttt{attributes(obj,condition=lambda n,v: not special(n))} +\begin{quote} + +Returns a dictionary containing the accessible attributes of +an object. By default, returns the non-special attributes only. +\end{quote} + +\texttt{generateblocks(regexp,text)} +\begin{quote} + +Generator splitting text in blocks according to regexp +\end{quote} + +\texttt{wrapfunctions(obj,wrapper,err=None,**options)} +\begin{quote} + +Traces the callable objects in an object with a dictionary +\end{quote} + +\texttt{wrap(obj,wrapped,condition=lambda k,v: True, err=None)} +\begin{quote} + +Retrieves obj's dictionary and wraps it +\end{quote} + +\texttt{reflective(*classes)} +\begin{quote} + +Reflective classes know themselves, i.e. they own a private +attribute {\_}{\_}this containing a reference to themselves. If the class +name starts with '{\_}', the underscores are stripped. This is needed +to make the mangling mechanism work. +\end{quote} + +\texttt{withmemory(f)} +\begin{quote} + +This closure invokes the callable object f only if need there is +\end{quote} + +\texttt{get{\_}time()} +\begin{quote} + +Return the time of the system in the format HH:MM:SS +\end{quote} + +\texttt{prn(s)} +\begin{quote} + +Given an evaluable string, print its value and its object reference. +Notice that the evaluation is done in the {\_}{\_}main{\_}{\_} dictionary. +\end{quote} + +\texttt{isplaindata(a)} +\begin{quote} + +A data attribute has no {\_}{\_}get{\_}{\_} or {\_}{\_}set{\_}{\_} attributes, is not +a built-in function, nor a built-in method. +\end{quote} + +\texttt{Pizza(toppings,**dic)} +\begin{quote} + +This function produces classes inheriting from GenericPizza and +WithLogger, using a metaclass inferred from Logged +\end{quote} + +\texttt{modulesub(s,r,module)} +\begin{quote} + +Requires 2.3 +\end{quote} + +\texttt{memoize(f)} +\begin{quote} + +This closure remembers all f invocations +\end{quote} + +\texttt{with{\_}timer(func, modulename='{\_}{\_}main{\_}{\_}', n=1, logfile=sys.stdout)} +\begin{quote} + +Wraps the function func and executes it n times (default n=1). +The average time spent in one iteration, express in milliseconds, +is stored in the attributes func.time and func.CPUtime, and saved +in a log file which defaults to the standard output. +\end{quote} + +\texttt{dedent(text)} +\begin{quote} + +dedent(text : string) -{$>$} string +\begin{quote} +\begin{quote} + +Remove any whitespace than can be uniformly removed from the left +of every line in text. + +This can be used e.g. to make triple-quoted strings line up with +the left edge of screen/whatever, while still presenting it in the +source code in indented form. + +For example: +\begin{quote} +\begin{quote} +\begin{description} +%[visit_definition_list_item] +\item[def test()::] +%[visit_definition] + +{\#} end first line with to avoid the empty line! +s = ''' hello + +Unexpected indentation. + +\begin{quote} + +world +\end{quote} + +Block quote ends without a blank line; unexpected unindent. + + +''' +print repr(s) {\#} prints ' hello + +%[depart_definition] +%[depart_definition_list_item] +\end{description} +\end{quote} + +Block quote ends without a blank line; unexpected unindent. + + +world +\end{quote} + +Block quote ends without a blank line; unexpected unindent. + +\begin{description} +%[visit_definition_list_item] +\item[':] +%[visit_definition] + +print repr(dedent(s)) {\#} prints 'hello + +%[depart_definition] +%[depart_definition_list_item] +\end{description} +\end{quote} + +Block quote ends without a blank line; unexpected unindent. + + +world +\end{quote} + +Block quote ends without a blank line; unexpected unindent. + + +' +\end{quote} + +\texttt{makecls(*metas,**options)} +\begin{quote} + +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 +\end{quote} + +\end{document} diff --git a/pypers/pep318/post.txt b/pypers/pep318/post.txt new file mode 100755 index 0000000..cfbb221 --- /dev/null +++ b/pypers/pep318/post.txt @@ -0,0 +1,21 @@ +Instructions: + +1. Download the file ``decorators.zip`` from my home-page: + + http://www.phyast.pitt.edu/~micheles/python/decorators.zip + +2. Unzip it in a directory, for instance: + + ``unzip decorators -d decorators`` + +3. Test that everything works: + + ``cd decorators; python doct.py decorators.txt`` + +4. Add the decorators directory to your Python path or copy the files + decorators.py, noconflict.py, debugger.py into your Python distribution. + +5. It is always a good idea to give a look to the README.txt file. + +Notice: Python 2.3 is required. I have tested that it works (at least for +me) both under Red Hat 7.3 and under Windows 98SE. diff --git a/pypers/pep318/printerr.py b/pypers/pep318/printerr.py new file mode 100755 index 0000000..7de2333 --- /dev/null +++ b/pypers/pep318/printerr.py @@ -0,0 +1,7 @@ +err=file('err','w') + +def printerr(*args): + "For debugging purposes" + for a in args: print >> err, a, + print >> err + diff --git a/pypers/pep318/prnt.py b/pypers/pep318/prnt.py new file mode 100755 index 0000000..cb56674 --- /dev/null +++ b/pypers/pep318/prnt.py @@ -0,0 +1,7 @@ +# prnt.py + +import sys +f=sys.stdout + +def hello(): + print >> f,'hello' diff --git a/pypers/pep318/pro.py b/pypers/pep318/pro.py new file mode 100755 index 0000000..d875d55 --- /dev/null +++ b/pypers/pep318/pro.py @@ -0,0 +1,28 @@ +"""Funziona solo se e' composto una sola volta, senno' ricorsione +infinita""" # ?? + +import customdec,__builtin__ +__builtin__.type=customdec.type +__builtin__.object=customdec.TraceFunctions(object) +print __builtin__.object.__mro__ + +__builtin__.object=customdec.Decorated(customdec.TraceFunctions(object)) +print __builtin__.object.__mro__ + +#raise SystemExit + +#__builtin__.object=customdec.TraceFunctions(object) +from tracing import E +e=E() +print E,type(type(E)) + + + + + + + + + + + diff --git a/pypers/pep318/pro1.py b/pypers/pep318/pro1.py new file mode 100755 index 0000000..a9c2e0f --- /dev/null +++ b/pypers/pep318/pro1.py @@ -0,0 +1,7 @@ +# pro1.py +import sys +f=sys.stdout +a='ciao' +def prn(): + print >> f, a + diff --git a/pypers/pep318/pro1.txt b/pypers/pep318/pro1.txt new file mode 100755 index 0000000..c75b12a --- /dev/null +++ b/pypers/pep318/pro1.txt @@ -0,0 +1,10 @@ +#<pro1.py> +import sys +f=sys.stdout +a='ciao' +def prn(): + print >> f, a +#</pro1.py> +>>> import pro1 +>>> pro1.prn() +ciao diff --git a/pypers/pep318/pro2.py b/pypers/pep318/pro2.py new file mode 100755 index 0000000..58cffb4 --- /dev/null +++ b/pypers/pep318/pro2.py @@ -0,0 +1,24 @@ +from customdec import * + +def f(self): pass + +tracedf=tracedmethod(f) +tracedtracedf=decorated(tracedmethod(f)) + +class C: pass + +c=C() + +C.f=tracedtracedf + +c.f() + +class Chatty(ClassDecorator): + def __init__(cls,*args): + print 'Chatty.__init__' + +class C:pass + + + +C=Chatty(Chatty(C)) diff --git a/pypers/pep318/pro2.txt b/pypers/pep318/pro2.txt new file mode 100755 index 0000000..70dbcd4 --- /dev/null +++ b/pypers/pep318/pro2.txt @@ -0,0 +1,3 @@ +>>> import pro1 +>>> pro1.prn() +ciao diff --git a/pypers/pep318/psyco.tex b/pypers/pep318/psyco.tex new file mode 100755 index 0000000..93dcc9f --- /dev/null +++ b/pypers/pep318/psyco.tex @@ -0,0 +1,201 @@ +\documentclass[10pt,english]{article} +\usepackage{babel} +\usepackage{shortvrb} +\usepackage[latin1]{inputenc} +\usepackage{tabularx} +\usepackage{longtable} +\setlength{\extrarowheight}{2pt} +\usepackage{amsmath} +\usepackage{graphicx} +\usepackage{color} +\usepackage{multirow} +\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} +\usepackage[a4paper,margin=2cm,nohead]{geometry} +%% generator Docutils: http://docutils.sourceforge.net/ +\newlength{\admonitionwidth} +\setlength{\admonitionwidth}{0.9\textwidth} +\newlength{\docinfowidth} +\setlength{\docinfowidth}{0.9\textwidth} +\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newenvironment{optionlist}[1] +{\begin{list}{} + {\setlength{\labelwidth}{#1} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\optionlistlabel}} +}{\end{list}} +% begin: floats for footnotes tweaking. +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50} +% end floats for footnotes +% some commands, that could be overwritten in the style file. +\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}} +% end of "some commands" +\input{/mnt/exp/MyDocs/pypers/style.tex} +\title{Module psyco} +\author{} +\date{} +\hypersetup{ +pdftitle={Module psyco} +} +\raggedbottom +\begin{document} +\maketitle + + +Psyco -- the Python Specializing Compiler. + +Typical usage: add the following lines to your application's main module: +\begin{description} +%[visit_definition_list_item] +\item[try::] +%[visit_definition] + +import psyco +psyco.profile() + +%[depart_definition] +%[depart_definition_list_item] +%[visit_definition_list_item] +\item[except::] +%[visit_definition] + +print 'Psyco not found, ignoring it' + +%[depart_definition] +%[depart_definition_list_item] +\end{description} + + +%___________________________________________________________________________ + +\hypertarget{functions}{} +\section*{Functions} +\pdfbookmark[0]{Functions}{functions} + +\texttt{cannotcompile(x):} +\begin{quote} + +Instruct Psyco never to compile the given function, method +or code object. +\end{quote} + +\texttt{log(logfile='', mode='w', top=10):} +\begin{quote} + +Enable logging to the given file. + +If the file name is unspecified, a default name is built by appending +a 'log-psyco' extension to the main script name. + +Mode is 'a' to append to a possibly existing file or 'w' to overwrite +an existing file. Note that the log file may grow quickly in 'a' mode. +\end{quote} + +\texttt{runonly(memory=None, time=None, memorymax=None, timemax=None):} +\begin{quote} + +Nonprofiler. + +XXX check if this is useful and document. +\end{quote} + +\texttt{profile(watermark = default{\_}watermark,} +\begin{quote} + +Turn on profiling. + +The 'watermark' parameter controls how easily running functions will +be compiled. The smaller the value, the more functions are compiled. +\end{quote} + +\texttt{full(memory=None, time=None, memorymax=None, timemax=None):} +\begin{quote} + +Compile as much as possible. + +Typical use is for small scripts performing intensive computations +or string handling. +\end{quote} + +\texttt{dumpcodebuf():} +\begin{quote} + +Write in file psyco.dump a copy of the emitted machine code, +provided Psyco was compiled with a non-zero CODE{\_}DUMP. +See py-utils/httpxam.py to examine psyco.dump. +\end{quote} + +\texttt{stop():} +\begin{quote} + +Turn off all automatic compilation. bind() calls remain in effect. +\end{quote} + +\texttt{proxy(x, rec=None):} +\begin{quote} + +Return a Psyco-enabled copy of the function. + +The original function is still available for non-compiled calls. +The optional second argument specifies the number of recursive +compilation levels: all functions called by func are compiled +up to the given depth of indirection. +\end{quote} + +\texttt{background(watermark = default{\_}watermark,} +\begin{quote} + +Turn on passive profiling. + +This is a very lightweight mode in which only intensively computing +functions can be detected. The smaller the 'watermark', the more functions +are compiled. +\end{quote} + +\texttt{unbind(x):} +\begin{quote} + +Reverse of bind(). +\end{quote} + +\texttt{bind(x, rec=None):} +\begin{quote} + +Enable compilation of the given function, method, or class object. + +If C is a class (or anything with a '{\_}{\_}dict{\_}{\_}' attribute), bind(C) will +rebind all functions and methods found in C.{\_}{\_}dict{\_}{\_} (which means, for +classes, all methods defined in the class but not in its parents). + +The optional second argument specifies the number of recursive +compilation levels: all functions called by func are compiled +up to the given depth of indirection. +\end{quote} + +\texttt{{\_}getemulframe(depth=0):} +\begin{quote} + +As {\_}getframe(), but the returned objects are real Python frame objects +emulating Psyco frames. Some of their attributes can be wrong or missing, +however. +\end{quote} + +\texttt{unproxy(proxy):} +\begin{quote} + +Return a new copy of the original function of method behind a proxy. +The result behaves like the original function in that calling it +does not trigger compilation nor execution of any compiled code. +\end{quote} + +\end{document} diff --git a/pypers/pep318/pydoc.html b/pypers/pep318/pydoc.html new file mode 100755 index 0000000..8de6660 --- /dev/null +++ b/pypers/pep318/pydoc.html @@ -0,0 +1,508 @@ + +<!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> +and the on-line help.</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> +</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 calling<br> +syntax 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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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> and mothers of all decorators. When called<br> +in the form <a href="#Decorator">Decorator</a>(obj), with obj having a magic docstring, it returns<br> +an instance of the correct decorator, otherwise it returns None.<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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <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#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></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>Descriptor class callable with a function as argument. The calling<br> +syntax is redefined by the meta-metaclass <a href="#MetaDecorator">MetaDecorator</a>. It redefines<br> +__str__ and get (i.e. __get__) on its instances.<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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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. Each time a decorator is defined in any module, it is stored in<br> + <a href="#MetaDecorator">MetaDecorator</a>.dic and <a href="#MetaDecorator">MetaDecorator</a>.ls.<br> +2. If the (method) decorator has a 'get' method, a '__get__' method<br> + is automagically created as an alias to 'get'.<br> +3. Decorators calls are dispatched to the decorator _call_<br> + <a href="#classmethod">classmethod</a>.</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="-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)</dt><dd><tt>Returns a new decorated <a href="__builtin__.html#object">object</a> created from obj, if obj is a function<br> +or a class; otherwise it returns None</tt></dd></dl> + <dl><dt><a name="-decorator_from"><strong>decorator_from</strong></a>(docstring, defaultdec<font color="#909090">=None</font>)</dt><dd><tt>Takes a magic docstring and a default decorator<br> +and returns a decorator class or None. It tries to be smart.</tt></dd></dl> + <dl><dt><a name="-enhance_classes"><strong>enhance_classes</strong></a>(docstring<font color="#909090">=''</font>)</dt><dd><tt>Enhance all the classes in the caller module with a metaclass<br> +built from the given docstring; the default is <a href="#ClassDecorator">ClassDecorator</a>.</tt></dd></dl> + <dl><dt><a name="-get"><strong>get</strong></a>(docstring<font color="#909090">=None</font>)</dt><dd><tt>List of recognized decorators</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></td></tr></table> +</body></html>
\ No newline at end of file diff --git a/pypers/pep318/safetype.html b/pypers/pep318/safetype.html new file mode 100755 index 0000000..a3c3cd4 --- /dev/null +++ b/pypers/pep318/safetype.html @@ -0,0 +1,33 @@ +<?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>Module safetype</title> +<link rel="stylesheet" href="default.css" type="text/css" /> +</head> +<body> +<div class="document" id="module-safetype"> +<h1 class="title">Module safetype</h1> +<p>Deep, DEEP magic to remove metaclass conflicts. +Metaclasses derived from safetype.type by cooperatively overriding __new__ +are safe under conflicts. +The suggested import syntax for usage in other modules is</p> +<blockquote> +from safetype import type</blockquote> +<div class="section" id="documented-metaclasses"> +<h1><a name="documented-metaclasses">Documented metaclasses</a></h1> +<p><tt class="literal"><span class="pre">safetype(type)</span></tt></p> +<blockquote> +Redefines the 'type' metaclass, making it safe under conflicts.</blockquote> +</div> +</div> +<hr class="footer"/> +<div class="footer"> +<a class="reference" href="safetype.rst">View document source</a>. +Generated on: 2003-09-20 16:41 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/safetype.tex b/pypers/pep318/safetype.tex new file mode 100755 index 0000000..fc80a11 --- /dev/null +++ b/pypers/pep318/safetype.tex @@ -0,0 +1,61 @@ +\documentclass[10pt,english]{article} +\usepackage{babel} +\usepackage{shortvrb} +\usepackage[latin1]{inputenc} +\usepackage{tabularx} +\usepackage{longtable} +\setlength{\extrarowheight}{2pt} +\usepackage{amsmath} +\usepackage{graphicx} +\usepackage{color} +\usepackage{multirow} +\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} +\usepackage[a4paper,margin=2cm,nohead]{geometry} +%% generator Docutils: http://docutils.sourceforge.net/ +\newlength{\admonitionwidth} +\setlength{\admonitionwidth}{0.9\textwidth} +\newlength{\docinfowidth} +\setlength{\docinfowidth}{0.9\textwidth} +\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newenvironment{optionlist}[1] +{\begin{list}{} + {\setlength{\labelwidth}{#1} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\optionlistlabel}} +}{\end{list}} +% begin: floats for footnotes tweaking. +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50} +% end floats for footnotes +% some commands, that could be overwritten in the style file. +\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}} +% end of "some commands" +\input{/mnt/exp/MyDocs/pypers/style.tex} +\title{Documentation of the safetype module\\ +\large{Metaclasses} +} +\author{} +\date{} +\hypersetup{ +pdftitle={Documentation of the safetype module} +} +\raggedbottom +\begin{document} +\maketitle + + +\texttt{safetype(type)} + +Redefines the 'type' metaclass, making it safe under conflicts. + +\end{document} diff --git a/pypers/pep318/tracing.py b/pypers/pep318/tracing.py new file mode 100755 index 0000000..5ea3552 --- /dev/null +++ b/pypers/pep318/tracing.py @@ -0,0 +1,25 @@ +# tracing.py + +""" +This is a pre-existing module not using decorators but using multiple +inheritance and unsafe metaclasses. We want to trace it for debugging +purposes. +""" + +class M(type): + "There should be some non-trivial code here" + +class B(object): + def __init__(self): + super(B,self).__init__() + +class D(object): + __metaclass__=M + def __init__(self): + super(D,self).__init__() + +class E(B,D): + def __init__(self): + super(E,self).__init__() + + 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__() + + diff --git a/pypers/pep318/x.py b/pypers/pep318/x.py new file mode 100755 index 0000000..fb6f93d --- /dev/null +++ b/pypers/pep318/x.py @@ -0,0 +1,17 @@ +"[Decorated]" + +import decorators,customdec; decorators.decorated() + +class desc(object): + def __get__(self,obj,cls): + print obj,cls + +class C(object): + def f(cls): + "[classmethod]" + print cls + g=desc() + + +C.g +C().g diff --git a/pypers/pep318/x.txt b/pypers/pep318/x.txt new file mode 100755 index 0000000..870e527 --- /dev/null +++ b/pypers/pep318/x.txt @@ -0,0 +1,7 @@ +>>> import customdec; customdec.enhance_classes() +>>> class C: +... "[Decorated,Logged]" +... def f(): +... "[staticmethod]" +... return 'it works!' +<class C[DecoratedLogged]> created |