summaryrefslogtreecommitdiff
path: root/pypers/oxford
diff options
context:
space:
mode:
Diffstat (limited to 'pypers/oxford')
-rwxr-xr-xpypers/oxford/BaseClass.py6
-rwxr-xr-xpypers/oxford/Makefile14
-rwxr-xr-xpypers/oxford/README.txt38
-rwxr-xr-xpypers/oxford/XMLtag.py25
-rwxr-xr-xpypers/oxford/after-the-event-1.txt104
-rwxr-xr-xpypers/oxford/after-the-event-2.txt92
-rwxr-xr-xpypers/oxford/after-the-event-3.txt77
-rwxr-xr-xpypers/oxford/all.html2366
-rwxr-xr-xpypers/oxford/all.rst2006
-rwxr-xr-xpypers/oxford/all.tex2395
-rwxr-xr-xpypers/oxford/callsupermethod.py123
-rwxr-xr-xpypers/oxford/check_overriding.py24
-rwxr-xr-xpypers/oxford/chop.py14
-rwxr-xr-xpypers/oxford/cooperative_init.py41
-rwxr-xr-xpypers/oxford/cript_user.py17
-rwxr-xr-xpypers/oxford/crypt_user.py20
-rwxr-xr-xpypers/oxford/custom_iterable.py9
-rwxr-xr-xpypers/oxford/data.txt3
-rwxr-xr-xpypers/oxford/dec.py25
-rwxr-xr-xpypers/oxford/decorate.py3
-rwxr-xr-xpypers/oxford/decorators.py104
-rwxr-xr-xpypers/oxford/defaultdict.py21
-rwxr-xr-xpypers/oxford/defaultdict2.py20
-rwxr-xr-xpypers/oxford/deferred.py21
-rwxr-xr-xpypers/oxford/descriptor.py43
-rwxr-xr-xpypers/oxford/dictmixin.py13
-rwxr-xr-xpypers/oxford/dictwrapper.py7
-rwxr-xr-xpypers/oxford/doctest_talk/Makefile5
-rwxr-xr-xpypers/oxford/doctest_talk/P01.html110
-rwxr-xr-xpypers/oxford/doctest_talk/P02.html106
-rwxr-xr-xpypers/oxford/doctest_talk/P03.html102
-rwxr-xr-xpypers/oxford/doctest_talk/P04.html108
-rwxr-xr-xpypers/oxford/doctest_talk/P05.html107
-rwxr-xr-xpypers/oxford/doctest_talk/P06.html108
-rwxr-xr-xpypers/oxford/doctest_talk/P07.html107
-rwxr-xr-xpypers/oxford/doctest_talk/P08.html102
-rwxr-xr-xpypers/oxford/doctest_talk/P09.html114
-rwxr-xr-xpypers/oxford/doctest_talk/P10.html101
-rwxr-xr-xpypers/oxford/doctest_talk/P11.html114
-rwxr-xr-xpypers/oxford/doctest_talk/P12.html119
-rwxr-xr-xpypers/oxford/doctest_talk/P13.html111
-rwxr-xr-xpypers/oxford/doctest_talk/P14.html118
-rwxr-xr-xpypers/oxford/doctest_talk/P15.html115
-rwxr-xr-xpypers/oxford/doctest_talk/P16.html118
-rwxr-xr-xpypers/oxford/doctest_talk/P17.html116
-rwxr-xr-xpypers/oxford/doctest_talk/P18.html110
-rwxr-xr-xpypers/oxford/doctest_talk/P19.html113
-rwxr-xr-xpypers/oxford/doctest_talk/P20.html108
-rwxr-xr-xpypers/oxford/doctest_talk/P21.html117
-rwxr-xr-xpypers/oxford/doctest_talk/P22.html108
-rwxr-xr-xpypers/oxford/doctest_talk/P23.html115
-rwxr-xr-xpypers/oxford/doctest_talk/P24.html112
-rwxr-xr-xpypers/oxford/doctest_talk/P25.html110
-rwxr-xr-xpypers/oxford/doctest_talk/P26.html113
-rwxr-xr-xpypers/oxford/doctest_talk/abstract.txt11
-rwxr-xr-xpypers/oxford/doctest_talk/doct_pkg.py22
-rwxr-xr-xpypers/oxford/doctest_talk/doctester_frontend.py44
-rwxr-xr-xpypers/oxford/doctest_talk/doctester_frontend.txt23
-rwxr-xr-xpypers/oxford/doctest_talk/ex24.py17
-rwxr-xr-xpypers/oxford/doctest_talk/ex_inner.py16
-rwxr-xr-xpypers/oxford/doctest_talk/index.html16
-rwxr-xr-xpypers/oxford/doctest_talk/index.txt6
-rwxr-xr-xpypers/oxford/doctest_talk/maketalk.py98
-rwxr-xr-xpypers/oxford/doctest_talk/more.txt33
-rwxr-xr-xpypers/oxford/doctest_talk/refresh.txt0
-rwxr-xr-xpypers/oxford/doctest_talk/split-failure.txt5
-rwxr-xr-xpypers/oxford/doctest_talk/split-failure_txt.py8
-rwxr-xr-xpypers/oxford/doctest_talk/split.html32
-rwxr-xr-xpypers/oxford/doctest_talk/split.py15
-rwxr-xr-xpypers/oxford/doctest_talk/split.txt18
-rwxr-xr-xpypers/oxford/doctest_talk/talk.txt403
-rwxr-xr-xpypers/oxford/doctest_talk/test_pkg/__init__.py6
-rwxr-xr-xpypers/oxford/doctest_talk/test_pkg/a.py7
-rwxr-xr-xpypers/oxford/doctest_talk/test_pkg/b.py7
-rwxr-xr-xpypers/oxford/doctest_talk/testfile_ex.py11
-rwxr-xr-xpypers/oxford/doctest_talk/the_story.txt23
-rwxr-xr-xpypers/oxford/doctest_talk/x.html203
-rwxr-xr-xpypers/oxford/doctest_talk/x.py19
-rwxr-xr-xpypers/oxford/doctest_talk/x.txt55
-rwxr-xr-xpypers/oxford/doctester.py59
-rwxr-xr-xpypers/oxford/dynamic_pages.py9
-rwxr-xr-xpypers/oxford/evilprop.py18
-rwxr-xr-xpypers/oxford/ex.py26
-rwxr-xr-xpypers/oxford/flatten.py27
-rwxr-xr-xpypers/oxford/for_loop.py15
-rwxr-xr-xpypers/oxford/frontpage.txt10
-rwxr-xr-xpypers/oxford/gen_with_attr.py21
-rwxr-xr-xpypers/oxford/getlevel.py9
-rwxr-xr-xpypers/oxford/htmltable.py19
-rwxr-xr-xpypers/oxford/import_with_meta.py18
-rwxr-xr-xpypers/oxford/import_with_metaclass.py23
-rwxr-xr-xpypers/oxford/index.txt83
-rwxr-xr-xpypers/oxford/infix.py53
-rwxr-xr-xpypers/oxford/interp.py45
-rwxr-xr-xpypers/oxford/kwdict.py10
-rwxr-xr-xpypers/oxford/latebinding.py11
-rwxr-xr-xpypers/oxford/latebinding.scm8
-rwxr-xr-xpypers/oxford/lazy.txt14
-rwxr-xr-xpypers/oxford/logfile.py45
-rwxr-xr-xpypers/oxford/loops.html753
-rwxr-xr-xpypers/oxford/loops.txt457
-rwxr-xr-xpypers/oxford/magic.html717
-rwxr-xr-xpypers/oxford/magic.txt783
-rwxr-xr-xpypers/oxford/magicprop.py28
-rwxr-xr-xpypers/oxford/magicsuper.py104
-rwxr-xr-xpypers/oxford/martelli.txt37
-rwxr-xr-xpypers/oxford/metatracer.py21
-rwxr-xr-xpypers/oxford/metatracer2.py28
-rwxr-xr-xpypers/oxford/mro.html788
-rwxr-xr-xpypers/oxford/mro.tex988
-rwxr-xr-xpypers/oxford/mro.txt787
-rwxr-xr-xpypers/oxford/multilingual.py52
-rwxr-xr-xpypers/oxford/multilingualprop.py30
-rwxr-xr-xpypers/oxford/namedtuple.py31
-rwxr-xr-xpypers/oxford/noconflict.py75
-rwxr-xr-xpypers/oxford/non_cooperative.py13
-rwxr-xr-xpypers/oxford/not_cooperative.txt120
-rwxr-xr-xpypers/oxford/objects.html743
-rwxr-xr-xpypers/oxford/objects.txt747
-rwxr-xr-xpypers/oxford/other.txt193
-rwxr-xr-xpypers/oxford/paleo.py20
-rwxr-xr-xpypers/oxford/parens2indent.py30
-rwxr-xr-xpypers/oxford/passwd.py31
-rwxr-xr-xpypers/oxford/point.py17
-rwxr-xr-xpypers/oxford/pro.py5
-rwxr-xr-xpypers/oxford/program.txt45
-rwxr-xr-xpypers/oxford/quixote_ex.py15
-rwxr-xr-xpypers/oxford/reiterable.py10
-rwxr-xr-xpypers/oxford/s_parser.py42
-rwxr-xr-xpypers/oxford/sect.py12
-rwxr-xr-xpypers/oxford/setter.py17
-rwxr-xr-xpypers/oxford/sexpr2indent.py26
-rwxr-xr-xpypers/oxford/skip_redundant.py11
-rwxr-xr-xpypers/oxford/skip_rendundant.py11
-rwxr-xr-xpypers/oxford/super.py26
-rwxr-xr-xpypers/oxford/super2.py18
-rwxr-xr-xpypers/oxford/super_ex.py17
-rwxr-xr-xpypers/oxford/super_old_new.py11
-rwxr-xr-xpypers/oxford/supermeth.py40
-rwxr-xr-xpypers/oxford/threaded.py15
-rwxr-xr-xpypers/oxford/timed.py43
-rwxr-xr-xpypers/oxford/trace_builtin.py18
-rwxr-xr-xpypers/oxford/traced.py13
-rwxr-xr-xpypers/oxford/traced_function2.py19
-rwxr-xr-xpypers/oxford/tracedaccess.py15
-rwxr-xr-xpypers/oxford/transl.py23
-rwxr-xr-xpypers/oxford/walk.py18
-rwxr-xr-xpypers/oxford/webapp.py13
-rwxr-xr-xpypers/oxford/why_super.py27
-rwxr-xr-xpypers/oxford/wraplist.py20
-rwxr-xr-xpypers/oxford/x.py24
-rwxr-xr-xpypers/oxford/y.py11
-rwxr-xr-xpypers/oxford/z.py8
153 files changed, 20305 insertions, 0 deletions
diff --git a/pypers/oxford/BaseClass.py b/pypers/oxford/BaseClass.py
new file mode 100755
index 0000000..8dc01c2
--- /dev/null
+++ b/pypers/oxford/BaseClass.py
@@ -0,0 +1,6 @@
+# BaseClass.py
+
+class BaseClass(object):
+ "Do something"
+
+
diff --git a/pypers/oxford/Makefile b/pypers/oxford/Makefile
new file mode 100755
index 0000000..ea6efa7
--- /dev/null
+++ b/pypers/oxford/Makefile
@@ -0,0 +1,14 @@
+rst = "/home/micheles/mp/ms/tools/rst.py"
+all:
+ make all.rst
+all.rst: frontpage.txt loops.txt objects.txt magic.txt
+ $(rst) frontpage.txt loops.txt objects.txt magic.txt
+pdf: all.rst
+ $(rst) -Ptd all; xpdf all.pdf
+test: all.rst
+ python2.4 -mdoctester < all.rst
+zip: all.rst
+ zip oxford-lectures README.txt all.rst all.html all.pdf accu2005.png \
+ doctester.py
+upload:
+ scp oxford-lectures.zip alpha.phyast.pitt.edu:public_html
diff --git a/pypers/oxford/README.txt b/pypers/oxford/README.txt
new file mode 100755
index 0000000..9bf0ebc
--- /dev/null
+++ b/pypers/oxford/README.txt
@@ -0,0 +1,38 @@
+** PYTHON 2.4 IS REQUIRED TO RUN THE EXAMPLES IN THE LECTURE NOTES **
+
+Instructions:
+
+$ unzip oxford-lectures.zip
+$ cd oxford-lectures
+$ python -m doctester < all.rst
+
+This will extracts all code snippets from the source file and run all tests.
+You should get the message
+
+doctest: run 209 tests, failed 0
+
+If you are on Windows, you will likely miss the "crypt" module, so a few
+examples may not run. Don't worry about them. I have not tried
+to make the scripts portable (for instance I have hard coded the
+location of optparse in the import_with_metaclass example; if the
+pathname differs in your system you will have to change it).
+
+Generally speaking these lectures are unpolished, too concise, with
+more code than words, and with cutting edge/very experimental code.
+Read them at your risk and peril. Take in account the fact that they
+were prepared as a last minute replacement for Alex Martelli's tutorial,
+with a limited amount of time and a very advanced program to follow.
+
+My main concern in preparing these notes was to give the readers a few
+*ideas*, not polished solutions. If you are reading these notes, you
+will be more than capable to customize these ideas to your own situation
+and to fix the unavoidable little bugs, imperfections, annoyances.
+
+Whereas I recommend the first lecture about iterators and generators
+to everybody, take in account than the second and especially the
+third lecture may cause your head to explode. I do not take any
+responsability in that case.
+
+Enjoy!
+
+ Michele Simionato, April 2005
diff --git a/pypers/oxford/XMLtag.py b/pypers/oxford/XMLtag.py
new file mode 100755
index 0000000..aaeb1b3
--- /dev/null
+++ b/pypers/oxford/XMLtag.py
@@ -0,0 +1,25 @@
+# XMLtag.py
+
+def makeattr(dict_or_list_of_pairs):
+ dic = dict(dict_or_list_of_pairs)
+ return " ".join('%s="%s"' % (k, dic[k]) for k in dic) # simplistic
+
+class XMLTag(object):
+ def __getattr__(self, name):
+ def tag(value, **attr):
+ """value can be a string or a sequence of strings."""
+ if hasattr(value, "__iter__"): # is iterable
+ value = " ".join(value)
+ return "<%s %s>%s</%s>" % (name, makeattr(attr), value, name)
+ return tag
+
+class XMLShortTag(object):
+ def __getattr__(self, name):
+ def tag(**attr):
+ return "<%s %s />" % (name, makeattr(attr))
+ return tag
+
+tag = XMLTag()
+tg = XMLShortTag()
+
+
diff --git a/pypers/oxford/after-the-event-1.txt b/pypers/oxford/after-the-event-1.txt
new file mode 100755
index 0000000..dc1d52a
--- /dev/null
+++ b/pypers/oxford/after-the-event-1.txt
@@ -0,0 +1,104 @@
+This should go in a blog, but I do not have one, nor any intention to
+start one, so I thought I will post here instead.
+
+Warning: this is a long post!
+
+ACCU Conference (PyUK) 2005: a personal view
+=======================================================
+
+Maybe not everybody knows that last week (19-23 of April) we had a
+pretty important event in Oxford: the fifth PyUK conference - hosted
+by the ACCU association - which is probably the second most important
+Python-related event in Europe after EuroPython.
+
+ACCU means Association of C and C++ Users, so most of the people there
+were not Python programmers; still it amazed me how much steam Python
+has gathered in the last years between C++ programmers.
+
+Ideally, I was there to give just a short presentation on doctest,
+but since Alex Martelli got hired from Google, I had to act as
+replacement of the Martelli & Ravenscroft couple, since it not
+that easy to find somebody crazy enough to take over or a 6-hours
+guru-level Python course. And, of course, there was some Italian
+mafia involved ;)
+
+It is not easy to act as a replacement for the martellibot,
+especially on short notice, but I tried to do my best. BTW,
+interested people can find my slides somehere on the ACCU website
+https://www.accu.org/conference/ or on my site
+http://www.phyast.pitt.edu/~micheles/python/oxford-lectures.zip
+
+I was actually worried a bit about people deserting the lectures once
+they discovered that Alex & Anna where not coming; it turns out my
+worries were injustified. We actually had something like 20 persons
+there, so we filled the room pretty well.
+
+The interesting thing was that at least half the people were
+experienced C++ programmers willing to learn Python, and not actual
+Python programmers. So, I had to correct the scope of the lectures
+in real time and I could not cover metaclasses, whereas I covered
+decorators but not as well as they deserve. Next year the tutorial will
+probably have a title such as "Python for C++ programmers" and the program
+will be changed accordingly. Anyway, people were extremely interested
+and the session (originally scheduled to end at 4 PM) actually
+went go until after 6 PM!
+
+It turned out that one of my "students" was Stephen Turner from Microsoft:
+Steve's title is "Developer evangelist" and it is part of his job to
+present to the developers the new cool projects Microsoft is working on:
+in this particular case, he went to the course since he in charge
+of evangelizing Jim Hugunin's brainchild, IronPython, i.e. Python
+running (fast!) on Dot Net, and he wanted to have a good picture
+of CPython capabilities.
+
+Obviously when I discovered that, I immediately asked him if he was
+willing to give a presentation on IronPython. We were lucky, since he
+accepted, he got some slides from Jim Hugunin's PyCON presentation
+and he gaves us a truly wonderful demonstration of IronPython
+capabilities. *Really* impressive.
+
+One cannot overrate the importance of this development for the future
+of Python. I asked Steve if Microsoft plans to support IronPython as
+part of the DotNet choice of languages: the answer was that there is no
+intention to sell IronPython. IronPython is an OpenSource project based
+on DotNet but it is not part of the DotNet offer and there are no
+plans in this sense.
+
+Some of you may be surprised (I certainly was) but Microsoft has been
+financing various Open Source projects in the last few years, released
+under BSD-style licences. IronPython is just one of these projects.
+There will probably be more. So stay tuned and keep an eye on what Redmont
+is doing. What it clear is that now Microsoft knows about the existence of
+Python and it is actually investing money on it.
+
+This is quite a change, especially with respect to what our keynote
+speaker, Greg Stein, told us about his experience with Microsoft
+7-8 years ago, when he was employed by them: at that time Microsoft's
+reaction to Python was something along the line of "Python what? is
+that a programming language?".
+
+Greg also told has about the programming language policy at Google
+(his current) employer: Googles uses and acknowledges officially only
+three mainstream languages: C++, Java, and Python. Python *is* mainstream
+for them. And judging from the space accorded to Python at the ACCU conference,
+Python is mainstream for the ACCU members too.
+
+An extremely impressive accomplishement for Python, if you think about
+it. And if Microsoft and Google are not enough, know that Nokia
+is offering Python on their mobile phones. Tapio Tallgren gaves us an
+extremely interesting technical talk on how you can program the
+Series 90 mobile using Python. They are targetting Python 2.2
+and most of the standard library just works, the speed is pretty
+good and actually they were surprised of how easy was to make the
+port.
+
+All in all, pretty good news, people! It seems a pretty good moment
+to be a Python programmer!
+
+I have something else to say, but I will make another post for that.
+
+ Michele Simionato
+
+
+#IronPython
+http://www.gotdotnet.com/workspaces/workspace.aspx?id=ad7acff7-ab1e-4bcb-99c0-57ac5a3a9742
diff --git a/pypers/oxford/after-the-event-2.txt b/pypers/oxford/after-the-event-2.txt
new file mode 100755
index 0000000..b68139b
--- /dev/null
+++ b/pypers/oxford/after-the-event-2.txt
@@ -0,0 +1,92 @@
+The previous post was getting too long, so I decided to put my
+impressions on the technical talks here. You may also want to
+read what our chairman Andy Robinson has to say:
+http://www.reportlab.org/~andy/accu2005/accu2005.html
+There you can find the materials of the conference (slides etc.)
+
+ACCU Conference (PyUK) 2005: a personal view (part 2)
+=======================================================
+
+I am definitely a technical person (as opposed to a marketing person)
+so what impressed me the most were the presentations by Armin Rigo.
+
+He gave two presentations: the official one, about the PyPy project, and
+a lightening talk about greenlets. Of course, I had heard something
+about these topics previously, but it is pretty different to see a live
+demonstration. Armin shown us really *impressive* stuff.
+
+Basically, PyPy is already there and it is working! He explained us
+the idea behind just-in-time compilation (which is actually quite simple),
+type inference at work, as well as other really impressive stuff, such
+as sharing an object through an Internet connection (!)
+
+The only problem with PyPy as it is now, is that it runs on top of
+traditional Python and it is terribly slow. Nevertheless, Armin says
+that it can be made fast and actually faster than current Python.
+If any other guy would have made the same claim I would have been
+skeptical, but Armin is the person who gave us Psyco, so I am forced to
+take him seriously. And the other persons working full time on the
+project have names such as Christian Tismer, Samuele Pedroni,
+Holger Krekel, etc. As reported by Jacob Hallen, Guido himself has
+great hopes for the project and Tim Peters would have liked to work
+full time on PyPy.
+
+The European Community founded the project for 1,300,000+ Euros in two
+years. So there is money to join the project and to cover travel expenses,
+if you are an Europen citizen. If you are no EU member,
+things are more complicated from the burocratical point
+of view. If somebody knows how to turn around the
+restrictions and to get some money for non-EU nationalities,
+I am sure Jacob Hallen would be happy to know.
+
+Jacob Hallen is in charge of the organization of the
+sprints and of some of the burocratical part. He looks
+like he is doing a pretty good job. Of course his firm,
+the Strakt, has a very strong and direct interest in the
+project. It is worth repeating that Alex Martelli worked for Strakt,
+that Laura Chreighton is there as well, and that recently
+they hired Samuele Pedroni of Jython fame. So, it looks
+like they are really doing a LOT for Python in Europe.
+
+On my part, I have resolved to keep a close eye on the
+development in http://codespeak.net, since there is really
+cool stuff in there, and really innovative ideas. It is
+unfortunate that most of the stuff is still under SVN
+and not easy available to the average Joe User, even if
+there are plenty of things that would be useful to the average
+Joe User, such as py.test (I would not talk about py.test since
+it has been discussed elsewhere pretty well, but I definitely
+like it a lot).
+
+I certainly hope that part of this very substantial effort will go back
+into the standard library someday.
+
+It is interesting to notice that Armin and I were the two speakers to
+use non-traditional presentation materials.
+
+Whereas I have just shown simple HTML slides generated by a hand-cooked
+Python script, he made a very effective usage of Pygame, so you
+got the impression type inference was as easy as playing PacMan.
+His presentation is available for download, at the reportlab link
+I mentioned before, so everybody can see it. Armin also used GraphViz to
+plot the output of PyPy type-inferencer (I am not sure of the right term
+to use here, just as I am not sure of what "interpreter" and "compiler"
+means anymore) and he got really nice-looking results. I am a
+known evangelist of the "dot" language,
+http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html,
+so I very much liked the approach.
+
+It is also fun to notice that both Armin's and my talk were
+the two metacircular talks of the conference: Armin was talking about
+implementing a programming language using itself as implementation
+language (let me remind that PyPy stands for Python in Python) whereas
+I presented a web application to demonstrate doctest which was used to
+doctest itself.
+
+Well, that's all for the moment. I have written enough for today, now
+it is time to start playing with greenlets. They are extremely cool,
+I don't know where I want to use them yet, but I am pretty sure I'll
+figure out something eventually ;)
+
+
+ Michele Simionato
diff --git a/pypers/oxford/after-the-event-3.txt b/pypers/oxford/after-the-event-3.txt
new file mode 100755
index 0000000..b727bd5
--- /dev/null
+++ b/pypers/oxford/after-the-event-3.txt
@@ -0,0 +1,77 @@
+In the previous post I have talked only about the PyPy project.
+But there was very interesting things even for people living in
+the real world. So, let's talk about them.
+
+ACCU Conference (PyUK) 2005: a personal view (part 3)
+=======================================================
+
+Installation & Deployment of Python applications
+------------------------------------------------------
+
+I should really thank Andy Robinson for the great job he
+did as chairman and organizer of the Python track, as well
+for the choice of the talks. He really covered everything
+from the most abstract and suble matters to the most concrete
+and real life practical things. So we had a couple of talks
+on installing and deploying Python applications.
+
+I really liked the one by Nathan Yergler, presenting his work
+on Creatives Common applications. His talk was 0% marketing and
+100% sound advice and very concrete help on what works and what
+does not work if you are trying to distribute a multiplatform
+application based on wxPython.
+
+In the same vein, Andy Robinson gave a talk on the challenges
+Reportlab faced with the installation and maintenance of their
+toolkit on many platforms. The message I got is that distutils is
+not enough, and that at the end they had to develop your own
+installation software.
+
+This is sad, but I recognized the truth in what Nathan and
+Yergler were saying: installation and deployement are
+higly non-trivial and time-consuming activities which
+are now more complex than needed. Andy said he took
+inspiration from what Ant did in the Java world, when
+writing his own installation tools and he made a made
+strong advocacy for Ant, saying that we should have something like that
+in standard library. I do not know Ant personally, but I have
+use distutils recently and I must say that whereas is quite
+at some things, it is not that easy to build on top of it.
+In practice, it is just faster to write a custom tool than
+to use distutils.
+
+
+Comparison of Graphics Toolkits
+-------------------------------------------------
+
+We got a really nice demonstration about Qt Designer by
+John Pinner, supplemented by a lightening talk by Trolltech
+(which must have the youngest and prettiest girl-engineers
+out there). I am not really capable of using such tools
+(I would probably be more at home with an s-expression
+representation of the widgets) but still I was pretty
+impressed by the easy of use. Plus, after a few click,
+you get an impressively clean piece of Python code
+with all the classes and the objects corresponding
+to the pictures you just drawn.
+
+There was also a comparable presentation by Alex Tweedly about
+PythonCard, but I missed it since I attended a Agile talk by
+an Italian friend I met there.
+
+Web Frameworks
+-------------------------------------------------
+
+There was a talk about Zope3 which I was very interested in,
+and a lightening talk about CherryPy by Ron Collins.
+Plus Andy Robinsons discussing the Reportlab hand-made
+Web framework, and the problems they got testing it.
+
+This is a subject I was very interested to hear about,
+since I have been experimenting with the subject. Unfortunately,
+it looks like everybody is trying to solve the issue in
+similar ways (essentially building custom tools on top of urllib).
+I discovered various simple applications to do Web scripting
+but nothing standard or widely used. This is certainly an area
+where the standard library could be extended.
+
diff --git a/pypers/oxford/all.html b/pypers/oxford/all.html
new file mode 100755
index 0000000..56f2314
--- /dev/null
+++ b/pypers/oxford/all.html
@@ -0,0 +1,2366 @@
+<?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.3.10: http://docutils.sourceforge.net/" />
+<title>Lectures on Advanced Python Programming</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-06-06 15:09:07 +0200 (Mon, 06 Jun 2005) $
+:Version: $Revision: 3442 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+*/
+
+/* "! important" is used here to override other ``margin-top`` and
+ ``margin-bottom`` styles that are later in the stylesheet or
+ more specific. See http://www.w3.org/TR/CSS1#the-cascade */
+.first {
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin-left: 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left {
+ clear: left }
+
+img.align-right {
+ clear: right }
+
+img.borderless {
+ border: 0 }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font-family: serif ;
+ font-size: 100% }
+
+pre.line-block {
+ font-family: serif ;
+ font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+ margin-left: 2em ;
+ margin-right: 2em ;
+ background-color: #eeeeee }
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid thin gray }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid thin black }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+tt.docutils {
+ background-color: #eeeeee }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="lectures-on-advanced-python-programming">
+<h1 class="title">Lectures on Advanced Python Programming</h1>
+<img alt="accu2005.png" src="accu2005.png" />
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="field-name">Given:</th><td class="field-body">19 April 2005</td>
+</tr>
+<tr class="field"><th class="field-name">Revised:</th><td class="field-body">7 September 2005</td>
+</tr>
+</tbody>
+</table>
+<div class="contents topic" id="contents">
+<p class="topic-title first"><a name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#lecture-1-loops-i-e-iterators-generators" id="id1" name="id1">Lecture 1: Loops (i.e. iterators &amp; generators)</a><ul>
+<li><a class="reference" href="#part-i-iterators" id="id2" name="id2">Part I: iterators</a><ul>
+<li><a class="reference" href="#iterators-are-everywhere" id="id3" name="id3">Iterators are everywhere</a></li>
+<li><a class="reference" href="#iterables-and-iterators" id="id4" name="id4">Iterables and iterators</a></li>
+<li><a class="reference" href="#simpler-way-to-get-an-iterator" id="id5" name="id5">Simpler way to get an iterator</a></li>
+<li><a class="reference" href="#sentinel-syntax-iter-callable-sentinel" id="id6" name="id6">Sentinel syntax iter(callable, sentinel)</a></li>
+<li><a class="reference" href="#second-simpler-way-to-get-an-iterator-generator-expressions" id="id7" name="id7">Second simpler way to get an iterator: generator-expressions</a></li>
+<li><a class="reference" href="#iteration-caveats" id="id8" name="id8">Iteration caveats</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#part-ii-generators" id="id9" name="id9">Part II: generators</a><ul>
+<li><a class="reference" href="#a-simple-recipe-skip-redundant" id="id10" name="id10">A simple recipe: skip redundant</a></li>
+<li><a class="reference" href="#another-real-life-example-working-with-nested-structures" id="id11" name="id11">Another real life example: working with nested structures</a></li>
+<li><a class="reference" href="#another-typical-use-case-for-generators-parsers" id="id12" name="id12">Another typical use case for generators: parsers</a></li>
+<li><a class="reference" href="#other-kinds-of-iterables" id="id13" name="id13">Other kinds of iterables</a></li>
+<li><a class="reference" href="#the-itertools-module" id="id14" name="id14">The itertools module</a></li>
+<li><a class="reference" href="#anytrue" id="id15" name="id15">anyTrue</a></li>
+<li><a class="reference" href="#chop" id="id16" name="id16">chop</a></li>
+<li><a class="reference" href="#tee" id="id17" name="id17">tee</a></li>
+<li><a class="reference" href="#grouping-and-sorting" id="id18" name="id18">Grouping and sorting</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a class="reference" href="#lecture-2-objects-delegation-inheritance" id="id19" name="id19">Lecture 2: Objects (delegation &amp; inheritance)</a><ul>
+<li><a class="reference" href="#part-i-delegation" id="id20" name="id20">Part I: delegation</a><ul>
+<li><a class="reference" href="#accessing-simple-attributes" id="id21" name="id21">Accessing simple attributes</a></li>
+<li><a class="reference" href="#accessing-methods" id="id22" name="id22">Accessing methods</a></li>
+<li><a class="reference" href="#converting-functions-into-methods" id="id23" name="id23">Converting functions into methods</a></li>
+<li><a class="reference" href="#hack-a-very-slick-adder" id="id24" name="id24">Hack: a very slick adder</a></li>
+<li><a class="reference" href="#descriptor-protocol" id="id25" name="id25">Descriptor Protocol</a></li>
+<li><a class="reference" href="#multilingual-attribute" id="id26" name="id26">Multilingual attribute</a></li>
+<li><a class="reference" href="#another-use-case-for-properties-storing-users" id="id27" name="id27">Another use case for properties: storing users</a></li>
+<li><a class="reference" href="#low-level-delegation-via-getattribute" id="id28" name="id28">Low-level delegation via __getattribute__</a></li>
+<li><a class="reference" href="#traditional-delegation-via-getattr" id="id29" name="id29">Traditional delegation via __getattr__</a></li>
+<li><a class="reference" href="#keyword-dictionaries-with-getattr-setattr" id="id30" name="id30">Keyword dictionaries with __getattr__/__setattr__</a></li>
+<li><a class="reference" href="#delegation-to-special-methods-caveat" id="id31" name="id31">Delegation to special methods caveat</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#part-ii-inheritance" id="id32" name="id32">Part II: Inheritance</a><ul>
+<li><a class="reference" href="#why-you-need-to-know-about-mi-even-if-you-do-not-use-it" id="id33" name="id33">Why you need to know about MI even if you do not use it</a></li>
+<li><a class="reference" href="#a-few-details-about-super-not-the-whole-truth" id="id34" name="id34">A few details about <tt class="docutils literal"><span class="pre">super</span></tt> (not the whole truth)</a></li>
+<li><a class="reference" href="#subclassing-built-in-types-new-vs-init" id="id35" name="id35">Subclassing built-in types; __new__ vs. __init__</a></li>
+<li><a class="reference" href="#be-careful-when-using-new-with-mutable-types" id="id36" name="id36">Be careful when using __new__ with mutable types</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a class="reference" href="#lecture-3-magic-i-e-decorators-and-metaclasses" id="id37" name="id37">Lecture 3: Magic (i.e. decorators and metaclasses)</a><ul>
+<li><a class="reference" href="#part-i-decorators" id="id38" name="id38">Part I: decorators</a><ul>
+<li><a class="reference" href="#a-typical-decorator-traced" id="id39" name="id39">A typical decorator: traced</a></li>
+<li><a class="reference" href="#a-decorator-factory-timed" id="id40" name="id40">A decorator factory: Timed</a></li>
+<li><a class="reference" href="#a-powerful-decorator-pattern" id="id41" name="id41">A powerful decorator pattern</a></li>
+<li><a class="reference" href="#a-deferred-decorator" id="id42" name="id42">A deferred decorator</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#part-ii-metaclasses" id="id43" name="id43">Part II: metaclasses</a><ul>
+<li><a class="reference" href="#rejuvenating-old-style-classes" id="id44" name="id44">Rejuvenating old-style classes</a></li>
+<li><a class="reference" href="#a-typical-metaclass-example-metatracer" id="id45" name="id45">A typical metaclass example: MetaTracer</a></li>
+<li><a class="reference" href="#real-life-example-check-overriding" id="id46" name="id46">Real life example: check overriding</a></li>
+<li><a class="reference" href="#logfile" id="id47" name="id47">LogFile</a></li>
+<li><a class="reference" href="#cooperative-hierarchies" id="id48" name="id48">Cooperative hierarchies</a></li>
+<li><a class="reference" href="#metaclass-enhanced-modules" id="id49" name="id49">Metaclass-enhanced modules</a></li>
+<li><a class="reference" href="#magic-properties" id="id50" name="id50">Magic properties</a></li>
+<li><a class="reference" href="#hack-evil-properties" id="id51" name="id51">Hack: evil properties</a></li>
+<li><a class="reference" href="#why-i-suggest-not-to-use-metaclasses-in-production-code" id="id52" name="id52">Why I suggest <em>not</em> to use metaclasses in production code</a></li>
+<li><a class="reference" href="#is-there-an-automatic-way-of-solving-the-conflict" id="id53" name="id53">Is there an automatic way of solving the conflict?</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="lecture-1-loops-i-e-iterators-generators">
+<h1><a class="toc-backref" href="#id1" name="lecture-1-loops-i-e-iterators-generators">Lecture 1: Loops (i.e. iterators &amp; generators)</a></h1>
+<div class="section" id="part-i-iterators">
+<h2><a class="toc-backref" href="#id2" name="part-i-iterators">Part I: iterators</a></h2>
+<div class="section" id="iterators-are-everywhere">
+<h3><a class="toc-backref" href="#id3" name="iterators-are-everywhere">Iterators are everywhere</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; for i in 1, 2, 3:
+... print i
+1
+2
+3
+</pre>
+<p>The 'for' loop is using <em>iterators</em> internally:</p>
+<pre class="literal-block">
+it = iter((1,2,3))
+while True:
+ try:
+ print it.next()
+ except StopIteration:
+ break
+</pre>
+</div>
+<div class="section" id="iterables-and-iterators">
+<h3><a class="toc-backref" href="#id4" name="iterables-and-iterators">Iterables and iterators</a></h3>
+<p><em>Iterable</em> = anything you can loop over = any sequence + any object with an __iter__ method;</p>
+<p>Not every sequence has an __iter__ method:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; &quot;hello&quot;.__iter__()
+Traceback (most recent call last):
+ ...
+AttributeError: 'str' object has no attribute '__iter__'
+</pre>
+<p><em>Iterator</em> = any object with a .next method and an __iter__ method returning self</p>
+</div>
+<div class="section" id="simpler-way-to-get-an-iterator">
+<h3><a class="toc-backref" href="#id5" name="simpler-way-to-get-an-iterator">Simpler way to get an iterator</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = iter(&quot;hello&quot;)
+&gt;&gt;&gt; it.next()
+'h'
+&gt;&gt;&gt; it.next()
+'e'
+&gt;&gt;&gt; it.next()
+'l'
+&gt;&gt;&gt; it.next()
+'l'
+&gt;&gt;&gt; it.next()
+'o'
+&gt;&gt;&gt; it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+</pre>
+</div>
+<div class="section" id="sentinel-syntax-iter-callable-sentinel">
+<h3><a class="toc-backref" href="#id6" name="sentinel-syntax-iter-callable-sentinel">Sentinel syntax iter(callable, sentinel)</a></h3>
+<p>Example:</p>
+<pre class="literal-block">
+$ echo -e &quot;value1\nvalue2\nEND\n&quot; &gt; data.txt
+$ python -c &quot;print list(iter(file('data.txt').readline, 'END\n'))&quot;
+['value1\n', 'value2\n']
+</pre>
+<p>Beware of infinite iterators:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; repeat = iter(lambda : &quot;some value&quot;, &quot;&quot;)
+&gt;&gt;&gt; repeat.next()
+'some value'
+</pre>
+</div>
+<div class="section" id="second-simpler-way-to-get-an-iterator-generator-expressions">
+<h3><a class="toc-backref" href="#id7" name="second-simpler-way-to-get-an-iterator-generator-expressions">Second simpler way to get an iterator: generator-expressions</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; squares = (i*i for i in range(1,11))
+&gt;&gt;&gt; list(squares)
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
+</pre>
+<p>Excessive parenthesis can be skipped, so use</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dict((i, i*i) for i in range(1,11))
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+</pre>
+<p>instead of</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dict([(i, i*i) for i in range(1,11)])
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+</pre>
+<p>(as usual, the most elegant version is the most efficient).</p>
+</div>
+<div class="section" id="iteration-caveats">
+<h3><a class="toc-backref" href="#id8" name="iteration-caveats">Iteration caveats</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; ls = [i for i in (1,2,3)]
+&gt;&gt;&gt; i
+3
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (j for j in (1,2,3))
+&gt;&gt;&gt; j
+Traceback (most recent call last):
+ ...
+NameError: name 'j' is not defined
+</pre>
+<p>A subtler example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; ls = [lambda :i for i in (1,2,3)]
+&gt;&gt;&gt; ls[0]()
+3
+</pre>
+<p>instead</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (lambda :i for i in (1,2,3))
+&gt;&gt;&gt; it.next()()
+1
+&gt;&gt;&gt; it.next()()
+2
+&gt;&gt;&gt; it.next()()
+3
+</pre>
+<p><em>seems</em> to be working but it is not really the case:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (lambda :i for i in (1,2,3))
+&gt;&gt;&gt; f1 = it.next()
+&gt;&gt;&gt; f2 = it.next()
+&gt;&gt;&gt; f3 = it.next()
+&gt;&gt;&gt; f1()
+3
+</pre>
+<p>The reason is that Python does LATE binding <em>always</em>. The solution is ugly:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = list(lambda i=i:i for i in (1,2,3))
+&gt;&gt;&gt; it[0]()
+1
+&gt;&gt;&gt; it[1]()
+2
+&gt;&gt;&gt; it[2]()
+3
+</pre>
+</div>
+</div>
+<div class="section" id="part-ii-generators">
+<h2><a class="toc-backref" href="#id9" name="part-ii-generators">Part II: generators</a></h2>
+<p>Trivial example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def gen123(): # &quot;function&quot; which returns an iterator over the values 1, 2, 3
+... yield 1
+... yield 2
+... yield 3
+...
+&gt;&gt;&gt; it = gen123()
+&gt;&gt;&gt; it.next()
+1
+&gt;&gt;&gt; it.next()
+2
+&gt;&gt;&gt; it.next()
+3
+&gt;&gt;&gt; it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+</pre>
+<p>Real life example: using generators to generate HTML tables</p>
+<pre class="literal-block">
+#&lt;htmltable.py&gt;
+
+def HTMLTablegen(table):
+ yield &quot;&lt;table&gt;&quot;
+ for row in table:
+ yield &quot;&lt;tr&gt;&quot;
+ for col in row:
+ yield &quot;&lt;td&gt;%s&lt;/td&gt;&quot; % col
+ yield &quot;&lt;/tr&gt;&quot;
+ yield &quot;&lt;/table&gt;&quot;
+
+def test():
+ return &quot;\n&quot;.join(HTMLTablegen([[&quot;Row&quot;, &quot;City&quot;],
+ [1,'London'], [2, 'Oxford']]))
+
+if __name__ == &quot;__main__&quot;: # example
+ print test()
+
+#&lt;/htmltable.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from htmltable import test
+&gt;&gt;&gt; print test()
+&lt;table&gt;
+&lt;tr&gt;
+&lt;td&gt;Row&lt;/td&gt;
+&lt;td&gt;City&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;1&lt;/td&gt;
+&lt;td&gt;London&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;2&lt;/td&gt;
+&lt;td&gt;Oxford&lt;/td&gt;
+&lt;/tr&gt;
+&lt;/table&gt;
+</pre>
+<div class="section" id="a-simple-recipe-skip-redundant">
+<h3><a class="toc-backref" href="#id10" name="a-simple-recipe-skip-redundant">A simple recipe: skip redundant</a></h3>
+<p>How to remove duplicates by keeping the order:</p>
+<pre class="literal-block">
+#&lt;skip_redundant.py&gt;
+
+def skip_redundant(iterable, skipset=None):
+ &quot;Redundant items are repeated items or items in the original skipset.&quot;
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+#&lt;/skip_redundant.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from skip_redundant import skip_redundant
+&gt;&gt;&gt; print list(skip_redundant(&quot;&lt;hello, world&gt;&quot;, skipset=set(&quot;&lt;&gt;&quot;)))
+['h', 'e', 'l', 'o', ',', ' ', 'w', 'r', 'd']
+</pre>
+</div>
+<div class="section" id="another-real-life-example-working-with-nested-structures">
+<h3><a class="toc-backref" href="#id11" name="another-real-life-example-working-with-nested-structures">Another real life example: working with nested structures</a></h3>
+<pre class="literal-block">
+#&lt;walk.py&gt;
+
+def walk(iterable, level=0):
+ for obj in iterable:
+ if not hasattr(obj, &quot;__iter__&quot;): # atomic object
+ yield obj, level
+ else: # composed object: iterate again
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+def flatten(iterable):
+ return (obj for obj, level in walk(iterable))
+
+def pprint(iterable):
+ for obj, level in walk(iterable):
+ print &quot; &quot;*level, obj
+
+#&lt;/walk.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from walk import flatten, pprint
+&gt;&gt;&gt; nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+&gt;&gt;&gt; pprint(nested_ls)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+&gt;&gt;&gt; pprint(flatten(nested_ls))
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+</pre>
+</div>
+<div class="section" id="another-typical-use-case-for-generators-parsers">
+<h3><a class="toc-backref" href="#id12" name="another-typical-use-case-for-generators-parsers">Another typical use case for generators: parsers</a></h3>
+<p>A very stripped down parser for nested expressions</p>
+<pre class="literal-block">
+#&lt;sexpr2indent.py&gt;
+&quot;&quot;&quot;A simple s-expression formatter.&quot;&quot;&quot;
+
+import re
+
+def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r&quot;(?P&lt;paren_beg&gt;\()|(?P&lt;paren_end&gt;\))&quot;)
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == &quot;paren_beg&quot;:
+ nesting_level += 1
+ elif match.lastgroup == &quot;paren_end&quot;:
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+def sexpr_indent(sexpr):
+ for nesting, text in parse(sexpr.replace(&quot;\n&quot;, &quot;&quot;)):
+ if text.strip(): print &quot; &quot;*nesting, text
+
+#&lt;/sexpr2indent.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from sexpr2indent import sexpr_indent
+&gt;&gt;&gt; sexpr_indent(&quot;&quot;&quot;\
+... (html (head (title Example)) (body (h1 s-expr formatter example)
+... (a (&#64; (href http://www.example.com)) A link)))&quot;&quot;&quot;)
+... #doctest: +NORMALIZE_WHITESPACE
+ html
+ head
+ title Example
+ body
+ h1 s-expr formatter example
+ a
+ &#64;
+ href http://www.example.com
+ A link
+</pre>
+</div>
+<div class="section" id="other-kinds-of-iterables">
+<h3><a class="toc-backref" href="#id13" name="other-kinds-of-iterables">Other kinds of iterables</a></h3>
+<p>The following class generates iterable which are not iterators:</p>
+<pre class="literal-block">
+#&lt;reiterable.py&gt;
+
+class ReIter(object):
+ &quot;A re-iterable object.&quot;
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+#&lt;/reiterable.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from reiterable import ReIter
+&gt;&gt;&gt; rit = ReIter()
+&gt;&gt;&gt; list(rit)
+[1, 2, 3]
+&gt;&gt;&gt; list(rit) # it is reiterable!
+[1, 2, 3]
+</pre>
+</div>
+<div class="section" id="the-itertools-module">
+<h3><a class="toc-backref" href="#id14" name="the-itertools-module">The itertools module</a></h3>
+<blockquote>
+<ul class="simple">
+<li>count([n]) --&gt; n, n+1, n+2, ...</li>
+<li>cycle(p) --&gt; p0, p1, ... plast, p0, p1, ...</li>
+<li>repeat(elem [,n]) --&gt; elem, elem, elem, ... endlessly or up to n times</li>
+<li>izip(p, q, ...) --&gt; (p[0], q[0]), (p[1], q[1]), ...</li>
+<li>ifilter(pred, seq) --&gt; elements of seq where pred(elem) is True</li>
+<li>ifilterfalse(pred, seq) --&gt; elements of seq where pred(elem) is False</li>
+<li>islice(seq, [start,] stop [, step]) --&gt; elements from seq[start:stop:step]</li>
+<li>imap(fun, p, q, ...) --&gt; fun(p0, q0), fun(p1, q1), ...</li>
+<li>starmap(fun, seq) --&gt; fun(*seq[0]), fun(*seq[1]), ...</li>
+<li>tee(it, n=2) --&gt; (it1, it2 , ... itn) splits one iterator into n</li>
+<li>chain(p, q, ...) --&gt; p0, p1, ... plast, q0, q1, ...</li>
+<li>takewhile(pred, seq) --&gt; seq[0], seq[1], until pred fails</li>
+<li>dropwhile(pred, seq) --&gt; seq[n], seq[n+1], starting when pred fails</li>
+<li>groupby(iterable[, keyfunc]) --&gt; sub-iterators grouped by value of keyfunc(v)</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="anytrue">
+<h3><a class="toc-backref" href="#id15" name="anytrue">anyTrue</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; import itertools
+&gt;&gt;&gt; def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+...
+&gt;&gt;&gt; fname = &quot;picture.gif&quot;
+&gt;&gt;&gt; anyTrue(fname.endswith, &quot;.jpg .gif .png&quot;.split())
+True
+</pre>
+<p>AnyTrue does <em>short-circuit</em>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def is3(i):
+... print &quot;i=%s&quot; % i
+... return i == 3
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True
+</pre>
+</div>
+<div class="section" id="chop">
+<h3><a class="toc-backref" href="#id16" name="chop">chop</a></h3>
+<p>You want to chop an iterable in batches of a given size:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from chop import chop
+&gt;&gt;&gt; list(chop([1, 2, 3, 4], 2))
+[[1, 2], [3, 4]]
+&gt;&gt;&gt; list(chop([1, 2, 3, 4, 5, 6, 7],3))
+[[1, 2, 3], [4, 5, 6], [7]]
+</pre>
+<p>Here is a possible implementation:</p>
+<pre class="literal-block">
+#&lt;chop.py&gt;
+
+# see also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279
+
+import itertools
+
+def chop(iterable, batchsize):
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+#&lt;/chop.py&gt;
+</pre>
+<p>For people thinking Python is too readable, here is a one-liner:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chop = lambda it, n : itertools.izip(*(iter(it),)*n)
+...
+&gt;&gt;&gt; list(chop([1,2,3,4], 2))
+[(1, 2), (3, 4)]
+</pre>
+</div>
+<div class="section" id="tee">
+<h3><a class="toc-backref" href="#id17" name="tee">tee</a></h3>
+<p>To make copies of iterables; typically used in parsers:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from itertools import tee, chain, izip
+&gt;&gt;&gt; chars, prevs = tee(&quot;abc&quot;)
+&gt;&gt;&gt; prevs = chain([None], prevs)
+&gt;&gt;&gt; for char, prev in izip(chars, prevs):
+... print char, prev
+...
+a None
+b a
+c b
+</pre>
+</div>
+<div class="section" id="grouping-and-sorting">
+<h3><a class="toc-backref" href="#id18" name="grouping-and-sorting">Grouping and sorting</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; from itertools import groupby
+&gt;&gt;&gt; from operator import itemgetter
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; NAME, AGE = 0, 1
+&gt;&gt;&gt; query_result = (&quot;Smith&quot;, 34), (&quot;Donaldson&quot;, 34), (&quot;Lee&quot;, 22), (&quot;Orr&quot;, 22)
+</pre>
+<p>Grouping together people of the same age:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for k, g in groupby(query_result, key=itemgetter(AGE)):
+... print k, list(g)
+...
+34 [('Smith', 34), ('Donaldson', 34)]
+22 [('Lee', 22), ('Orr', 22)]
+</pre>
+<p>Sorting by name:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for tup in sorted(query_result, key=itemgetter(NAME)):
+... print tup
+('Donaldson', 34)
+('Lee', 22)
+('Orr', 22)
+('Smith', 34)
+</pre>
+</div>
+</div>
+</div>
+<div class="section" id="lecture-2-objects-delegation-inheritance">
+<h1><a class="toc-backref" href="#id19" name="lecture-2-objects-delegation-inheritance">Lecture 2: Objects (delegation &amp; inheritance)</a></h1>
+<div class="section" id="part-i-delegation">
+<h2><a class="toc-backref" href="#id20" name="part-i-delegation">Part I: delegation</a></h2>
+<p>Understanding how attribute access works: internal delegation via <em>descriptors</em></p>
+<div class="section" id="accessing-simple-attributes">
+<h3><a class="toc-backref" href="#id21" name="accessing-simple-attributes">Accessing simple attributes</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... a = 2
+... def __init__(self, x):
+... self.x = x
+...
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; c = C(1)
+&gt;&gt;&gt; c.x
+1
+&gt;&gt;&gt; c.a
+2
+</pre>
+<p>We are retrieving</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.__dict__[&quot;x&quot;]
+1
+</pre>
+<p>If there is nothing in c.__dict__, Python looks at C.__dict__:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print c.__dict__.get(&quot;a&quot;)
+None
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__[&quot;a&quot;]
+2
+</pre>
+<p>If there is nothing in C.__dict__, Python looks at the superclasses according
+to the MRO (see part II).</p>
+</div>
+<div class="section" id="accessing-methods">
+<h3><a class="toc-backref" href="#id22" name="accessing-methods">Accessing methods</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.__init__ #doctest: +ELLIPSIS
+&lt;bound method C.__init__ of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<p>since __init__ is not in c.__dict__ Python looks in the class dictionary
+and finds</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__[&quot;__init__&quot;] #doctest: +ELLIPSIS
+&lt;function __init__ at 0x...&gt;
+</pre>
+<p>Then it magically converts the function into a method bound to the instance
+&quot;c&quot;.</p>
+<p>NOTE: this mechanism works for new-style classes only.</p>
+<p>The old-style mechanism is less consistent and the attribute lookup of special
+methods is special: (*)</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): pass
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.__str__ = lambda : &quot;hello!&quot;
+&gt;&gt;&gt; print c #doctest: +ELLIPSIS
+&lt;__main__.C object at ...&gt;
+</pre>
+<p>whereas for old-style classes</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C: pass
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.__str__ = lambda : &quot;hello!&quot;
+&gt;&gt;&gt; print c
+hello!
+</pre>
+<p>the special method is looked for in the instance dictionary too.</p>
+<p>(*) modulo a very subtle difference for __getattr__-delegated special methods,
+see later.</p>
+</div>
+<div class="section" id="converting-functions-into-methods">
+<h3><a class="toc-backref" href="#id23" name="converting-functions-into-methods">Converting functions into methods</a></h3>
+<p>It is possible to convert a function into a bound or unbound method
+by invoking the <tt class="docutils literal"><span class="pre">__get__</span></tt> special method:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): pass
+&gt;&gt;&gt; f.__get__ #doctest: +ELLIPSIS
+&lt;method-wrapper object at 0x...&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): pass
+...
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(self): pass
+...
+&gt;&gt;&gt; f.__get__(C(), C) #doctest: +ELLIPSIS
+&lt;bound method C.f of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__(None, C)
+&lt;unbound method C.f&gt;
+</pre>
+<p>Functions are the simplest example of <em>descriptors</em>.</p>
+<p>Access to methods works since internally Python transforms</p>
+<blockquote>
+<tt class="docutils literal"><span class="pre">c.__init__</span> <span class="pre">-&gt;</span> <span class="pre">type(c).__dict__['__init__'].__get__(c,</span> <span class="pre">type(c))</span></tt></blockquote>
+<p>Note: not <em>all</em> functions are descriptors:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from operator import add
+&gt;&gt;&gt; add.__get__
+Traceback (most recent call last):
+ ...
+AttributeError: 'builtin_function_or_method' object has no attribute '__get__'
+</pre>
+</div>
+<div class="section" id="hack-a-very-slick-adder">
+<h3><a class="toc-backref" href="#id24" name="hack-a-very-slick-adder">Hack: a very slick adder</a></h3>
+<p>The descriptor protocol can be (ab)used as a way to avoid the late binding
+issue in for loops:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def add(x,y):
+... return x + y
+&gt;&gt;&gt; closures = [add.__get__(i) for i in range(10)]
+&gt;&gt;&gt; closures[5](1000)
+1005
+</pre>
+<p>Notice: operator.add will not work.</p>
+</div>
+<div class="section" id="descriptor-protocol">
+<h3><a class="toc-backref" href="#id25" name="descriptor-protocol">Descriptor Protocol</a></h3>
+<p>Everything at <a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">http://users.rcn.com/python/download/Descriptor.htm</a></p>
+<p>Formally:</p>
+<pre class="literal-block">
+descr.__get__(self, obj, type=None) --&gt; value
+descr.__set__(self, obj, value) --&gt; None
+descr.__delete__(self, obj) --&gt; None
+</pre>
+<p>Examples of custom descriptors:</p>
+<pre class="literal-block">
+#&lt;descriptor.py&gt;
+
+
+class AttributeDescriptor(object):
+ def __get__(self, obj, cls=None):
+ if obj is None and cls is None:
+ raise TypeError(&quot;__get__(None, None) is invalid&quot;)
+ elif obj is None:
+ return self.get_from_class(cls)
+ else:
+ return self.get_from_obj(obj)
+ def get_from_class(self, cls):
+ print &quot;Getting %s from %s&quot; % (self, cls)
+ def get_from_obj(self, obj):
+ print &quot;Getting %s from %s&quot; % (self, obj)
+
+
+class Staticmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func
+ get_from_obj = get_from_class
+
+
+class Classmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func.__get__(cls, type(cls))
+ def get_from_obj(self, obj):
+ return self.get_from_class(obj.__class__)
+
+class C(object):
+ s = Staticmethod(lambda : 1)
+ c = Classmethod(lambda cls : cls.__name__)
+
+c = C()
+
+assert C.s() == c.s() == 1
+assert C.c() == c.c() == &quot;C&quot;
+
+#&lt;/descriptor.py&gt;
+</pre>
+</div>
+<div class="section" id="multilingual-attribute">
+<h3><a class="toc-backref" href="#id26" name="multilingual-attribute">Multilingual attribute</a></h3>
+<p>Inspirated by a question in the Italian Newsgroup:</p>
+<pre class="literal-block">
+#&lt;multilingual.py&gt;
+
+import sys
+from descriptor import AttributeDescriptor
+
+class MultilingualAttribute(AttributeDescriptor):
+ &quot;&quot;&quot;When a MultilingualAttribute is accessed, you get the translation
+ corresponding to the currently selected language.
+ &quot;&quot;&quot;
+ def __init__(self, **translations):
+ self.trans = translations
+ def get_from_class(self, cls):
+ return self.trans[getattr(cls, &quot;language&quot;, None) or
+ sys.modules[cls.__module__].language]
+ def get_from_obj(self, obj):
+ return self.trans[getattr(obj, &quot;language&quot;, None) or
+ sys.modules[obj.__class__.__module__].language]
+
+
+language = &quot;en&quot;
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+class WebApplication(object):
+ error_msg = MultilingualAttribute(
+ en=&quot;You cannot access this page&quot;,
+ it=&quot;Questa pagina non e' accessibile&quot;,
+ fr=&quot;Vous ne pouvez pas acceder cette page&quot;,)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ self.language = language or getattr(self.__class__, &quot;language&quot;, None)
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
+app = WebApplication()
+assert app.show_page() == &quot;You cannot access this page&quot;
+
+app.language = &quot;fr&quot;
+assert app.show_page() == &quot;Vous ne pouvez pas acceder cette page&quot;
+
+app.language = &quot;it&quot;
+assert app.show_page() == &quot;Questa pagina non e' accessibile&quot;
+
+app.language = &quot;en&quot;
+assert app.show_page() == &quot;You cannot access this page&quot;
+
+#&lt;/multilingual.py&gt;
+</pre>
+<p>The same can be done with properties:</p>
+<pre class="literal-block">
+#&lt;multilingualprop.py&gt;
+
+language = &quot;en&quot;
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+def multilingualProperty(**trans):
+ def get(self):
+ return trans[self.language]
+ def set(self, value):
+ trans[self.language] = value
+ return property(get, set)
+
+class WebApplication(object):
+ language = language
+ error_msg = multilingualProperty(
+ en=&quot;You cannot access this page&quot;,
+ it=&quot;Questa pagina non e' accessibile&quot;,
+ fr=&quot;Vous ne pouvez pas acceder cette page&quot;,)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ if language: self.language = self.language
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+#&lt;/multilingualprop.py&gt;
+</pre>
+<p>This also gives the possibility to set the error messages.</p>
+<p>The difference with the descriptor approach</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from multilingual import WebApplication
+&gt;&gt;&gt; app = WebApplication()
+&gt;&gt;&gt; print app.error_msg
+You cannot access this page
+&gt;&gt;&gt; print WebApplication.error_msg
+You cannot access this page
+</pre>
+<p>is that with properties there is no nice access from the class:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from multilingualprop import WebApplication
+&gt;&gt;&gt; WebApplication.error_msg #doctest: +ELLIPSIS
+&lt;property object at ...&gt;
+</pre>
+</div>
+<div class="section" id="another-use-case-for-properties-storing-users">
+<h3><a class="toc-backref" href="#id27" name="another-use-case-for-properties-storing-users">Another use case for properties: storing users</a></h3>
+<p>Consider a library providing a simple User class:</p>
+<pre class="literal-block">
+#&lt;crypt_user.py&gt;
+
+class User(object):
+ def __init__(self, username, password):
+ self.username, self.password = username, password
+
+#&lt;/crypt_user.py&gt;
+</pre>
+<p>The User objects are stored in a database as they are.
+For security purpose, in a second version of the library it is
+decided to crypt the password, so that only crypted passwords
+are stored in the database. With properties, it is possible to
+implement this functionality without changing the source code for
+the User class:</p>
+<pre class="literal-block">
+#&lt;crypt_user.py&gt;
+
+from crypt import crypt
+
+def cryptedAttribute(seed=&quot;x&quot;):
+ def get(self):
+ return getattr(self, &quot;_pw&quot;, None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+User.password = cryptedAttribute()
+</pre>
+<p>#&lt;/crypt_user.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from crypt_user import User
+&gt;&gt;&gt; u = User(&quot;michele&quot;, &quot;secret&quot;)
+&gt;&gt;&gt; print u.password
+xxZREZpkHZpkI
+</pre>
+<p>Notice the property factory approach used here.</p>
+</div>
+<div class="section" id="low-level-delegation-via-getattribute">
+<h3><a class="toc-backref" href="#id28" name="low-level-delegation-via-getattribute">Low-level delegation via __getattribute__</a></h3>
+<p>Attribute access is managed by the__getattribute__ special method:</p>
+<pre class="literal-block">
+#&lt;tracedaccess.py&gt;
+
+class TracedAccess(object):
+ def __getattribute__(self, name):
+ print &quot;Accessing %s&quot; % name
+ return object.__getattribute__(self, name)
+
+
+class C(TracedAccess):
+ s = staticmethod(lambda : 'staticmethod')
+ c = classmethod(lambda cls: 'classmethod')
+ m = lambda self: 'method'
+ a = &quot;hello&quot;
+
+#&lt;/tracedaccess.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracedaccess import C
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; print c.s()
+Accessing s
+staticmethod
+&gt;&gt;&gt; print c.c()
+Accessing c
+classmethod
+&gt;&gt;&gt; print c.m()
+Accessing m
+method
+&gt;&gt;&gt; print c.a
+Accessing a
+hello
+&gt;&gt;&gt; print c.__init__ #doctest: +ELLIPSIS
+Accessing __init__
+&lt;method-wrapper object at 0x...&gt;
+&gt;&gt;&gt; try: c.x
+... except AttributeError, e: print e
+...
+Accessing x
+'C' object has no attribute 'x'
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.y = 'y'
+&gt;&gt;&gt; c.y
+Accessing y
+'y'
+</pre>
+<p>You are probably familiar with <tt class="docutils literal"><span class="pre">__getattr__</span></tt> which is similar
+to <tt class="docutils literal"><span class="pre">__getattribute__</span></tt>, but it is called <em>only for missing attributes</em>.</p>
+</div>
+<div class="section" id="traditional-delegation-via-getattr">
+<h3><a class="toc-backref" href="#id29" name="traditional-delegation-via-getattr">Traditional delegation via __getattr__</a></h3>
+<p>Realistic use case in &quot;object publishing&quot;:</p>
+<pre class="literal-block">
+#&lt;webapp.py&gt;
+
+class WebApplication(object):
+ def __getattr__(self, name):
+ return name.capitalize()
+
+
+app = WebApplication()
+
+assert app.page1 == 'Page1'
+assert app.page2 == 'Page2'
+
+#&lt;/webapp.py&gt;
+</pre>
+<p>Here is another use case in HTML generation:</p>
+<pre class="literal-block">
+#&lt;XMLtag.py&gt;
+
+def makeattr(dict_or_list_of_pairs):
+ dic = dict(dict_or_list_of_pairs)
+ return &quot; &quot;.join('%s=&quot;%s&quot;' % (k, dic[k]) for k in dic) # simplistic
+
+class XMLTag(object):
+ def __getattr__(self, name):
+ def tag(value, **attr):
+ &quot;&quot;&quot;value can be a string or a sequence of strings.&quot;&quot;&quot;
+ if hasattr(value, &quot;__iter__&quot;): # is iterable
+ value = &quot; &quot;.join(value)
+ return &quot;&lt;%s %s&gt;%s&lt;/%s&gt;&quot; % (name, makeattr(attr), value, name)
+ return tag
+
+class XMLShortTag(object):
+ def __getattr__(self, name):
+ def tag(**attr):
+ return &quot;&lt;%s %s /&gt;&quot; % (name, makeattr(attr))
+ return tag
+
+tag = XMLTag()
+tg = XMLShortTag()
+
+#&lt;/XMLtag.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from XMLtag import tag, tg
+&gt;&gt;&gt; print tag.a(&quot;example.com&quot;, href=&quot;http://www.example.com&quot;)
+&lt;a href=&quot;http://www.example.com&quot;&gt;example.com&lt;/a&gt;
+&gt;&gt;&gt; print tg.br(**{'class':&quot;br_style&quot;})
+&lt;br class=&quot;br_style&quot; /&gt;
+</pre>
+</div>
+<div class="section" id="keyword-dictionaries-with-getattr-setattr">
+<h3><a class="toc-backref" href="#id30" name="keyword-dictionaries-with-getattr-setattr">Keyword dictionaries with __getattr__/__setattr__</a></h3>
+<pre class="literal-block">
+#&lt;kwdict.py&gt;
+
+class kwdict(dict): # or UserDict, to make it to work with Zope
+ &quot;&quot;&quot;A typing shortcut used in place of a keyword dictionary.&quot;&quot;&quot;
+ def __getattr__(self, name):
+ return self[name]
+ def __setattr__(self, name, value):
+ self[name] = value
+
+#&lt;/kwdict.py&gt;
+</pre>
+<p>And now for a completely different solution:</p>
+<pre class="literal-block">
+#&lt;dictwrapper.py&gt;
+
+class DictWrapper(object):
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+#&lt;/dictwrapper.py&gt;
+</pre>
+</div>
+<div class="section" id="delegation-to-special-methods-caveat">
+<h3><a class="toc-backref" href="#id31" name="delegation-to-special-methods-caveat">Delegation to special methods caveat</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWrapper(object):
+... def __init__(self, ls):
+... self._list = ls
+... def __getattr__(self, name):
+... if name == &quot;__getitem__&quot;: # special method
+... return self._list.__getitem__
+... elif name == &quot;reverse&quot;: # regular method
+... return self._list.reverse
+... else:
+... raise AttributeError(&quot;%r is not defined&quot; % name)
+...
+&gt;&gt;&gt; lw = ListWrapper([0,1,2])
+&gt;&gt;&gt; print lw.x
+Traceback (most recent call last):
+ ...
+AttributeError: 'x' is not defined
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; lw.reverse()
+&gt;&gt;&gt; print lw.__getitem__(0)
+2
+&gt;&gt;&gt; print lw.__getitem__(1)
+1
+&gt;&gt;&gt; print lw.__getitem__(2)
+0
+&gt;&gt;&gt; print lw[0]
+Traceback (most recent call last):
+ ...
+TypeError: unindexable object
+</pre>
+</div>
+</div>
+<div class="section" id="part-ii-inheritance">
+<h2><a class="toc-backref" href="#id32" name="part-ii-inheritance">Part II: Inheritance</a></h2>
+<p>The major changes in inheritance from Python 2.1 to 2.2+ are:</p>
+<ol class="arabic simple">
+<li>you can subclass built-in types (as a consequence the constructor__new__
+has been exposed to the user, to help subclassing immutable types);</li>
+<li>the Method Resolution Order (MRO) has changed;</li>
+<li>now Python allows <em>cooperative method calls</em>, i.e. we have <em>super</em>.</li>
+</ol>
+<div class="section" id="why-you-need-to-know-about-mi-even-if-you-do-not-use-it">
+<h3><a class="toc-backref" href="#id33" name="why-you-need-to-know-about-mi-even-if-you-do-not-use-it">Why you need to know about MI even if you do not use it</a></h3>
+<p>In principle, the last two changes are relevant only if you use multiple
+inheritance. If you use single inheritance only, you don't need <tt class="docutils literal"><span class="pre">super</span></tt>:
+you can just name the superclass.
+However, somebody else may want to use your class in a MI hierarchy,
+and you would make her life difficult if you don't use <tt class="docutils literal"><span class="pre">super</span></tt>.</p>
+<p>My SI hierarchy:</p>
+<pre class="literal-block">
+#&lt;why_super.py&gt;
+
+class Base(object):
+ def __init__(self):
+ print &quot;B.__init__&quot;
+
+class MyClass(Base):
+ &quot;I do not cooperate with others&quot;
+ def __init__(self):
+ print &quot;MyClass.__init__&quot;
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+#&lt;/why_super.py&gt;
+</pre>
+<p>Her MI hierarchy:</p>
+<pre class="literal-block">
+#&lt;why_super.py&gt;
+
+class Mixin(Base):
+ &quot;I am cooperative with others&quot;
+ def __init__(self):
+ print &quot;Mixin.__init__&quot;
+ super(Mixin, self).__init__()
+
+class HerClass(MyClass, Mixin):
+ &quot;I am supposed to be cooperative too&quot;
+ def __init__(self):
+ print &quot;HerClass.__init__&quot;
+ super(HerClass, self).__init__()
+
+#&lt;/why_super.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from why_super import HerClass
+&gt;&gt;&gt; h = HerClass() # Mixin.__init__ is not called!
+HerClass.__init__
+MyClass.__init__
+B.__init__
+</pre>
+<blockquote>
+<pre class="literal-block">
+ 4 object
+ |
+ 3 Base
+ / \
+1 MyClass 2 Mixin
+ \ /
+ 0 HerClass
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; [ancestor.__name__ for ancestor in HerClass.mro()]
+['HerClass', 'MyClass', 'Mixin', 'Base', 'object']
+</pre>
+<p>In order to be polite versus your future users, you should use <tt class="docutils literal"><span class="pre">super</span></tt>
+always. This adds a cognitive burden even for people not using MI :-(</p>
+<p>Notice that there is no good comprehensive reference on <tt class="docutils literal"><span class="pre">super</span></tt> (yet)
+Your best bet is still <a class="reference" href="http://www.python.org/2.2.3/descrintro.html#cooperation">http://www.python.org/2.2.3/descrintro.html#cooperation</a></p>
+<p>The MRO instead is explained here: <a class="reference" href="http://www.python.org/2.3/mro.html">http://www.python.org/2.3/mro.html</a></p>
+<p>Notice that I DO NOT recommand Multiple Inheritance.</p>
+<p>More often than not you are better off using composition/delegation/wrapping,
+etc.</p>
+<p>See Zope 2 -&gt; Zope 3 experience.</p>
+</div>
+<div class="section" id="a-few-details-about-super-not-the-whole-truth">
+<h3><a class="toc-backref" href="#id34" name="a-few-details-about-super-not-the-whole-truth">A few details about <tt class="docutils literal docutils literal"><span class="pre">super</span></tt> (not the whole truth)</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... def __init__(self): print &quot;B.__init__&quot;
+...
+&gt;&gt;&gt; class C(B):
+... def __init__(self): print &quot;C.__init__&quot;
+...
+&gt;&gt;&gt; c = C()
+C.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">instance)</span></tt>, where <tt class="docutils literal"><span class="pre">instance</span></tt> is an instance of <tt class="docutils literal"><span class="pre">cls</span></tt> or of
+a subclass of <tt class="docutils literal"><span class="pre">cls</span></tt>, retrieves the right method in the MRO:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__ #doctest: +ELLIPSIS
+&lt;bound method C.__init__ of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__.im_func is B.__init__.im_func
+True
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__()
+B.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">subclass)</span></tt> works for unbound methods:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).__init__
+&lt;unbound method C.__init__&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).__init__.im_func is B.__init__.im_func
+True
+&gt;&gt;&gt; super(C, C).__init__(c)
+B.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">subclass)</span></tt> is also necessary for classmethods and staticmethods.
+Properties and custom descriptorsw works too:</p>
+<pre class="literal-block">
+#&lt;super_ex.py&gt;
+
+from descriptor import AttributeDescriptor
+
+class B(object):
+ &#64;staticmethod
+ def sm(): return &quot;staticmethod&quot;
+
+ &#64;classmethod
+ def cm(cls): return cls.__name__
+
+ p = property()
+ a = AttributeDescriptor()
+
+class C(B): pass
+
+#&lt;/super_ex.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from super_ex import C
+</pre>
+<p>Staticmethod usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).sm #doctest: +ELLIPSIS
+&lt;function sm at 0x...&gt;
+&gt;&gt;&gt; super(C, C).sm()
+'staticmethod'
+</pre>
+<p>Classmethod usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C()).cm
+&lt;bound method type.cm of &lt;class 'super_ex.C'&gt;&gt;
+&gt;&gt;&gt; super(C, C).cm() # C is automatically passed
+'C'
+</pre>
+<p>Property usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C, C).p #doctest: +ELLIPSIS
+&lt;property object at 0x...&gt;
+&gt;&gt;&gt; super(C, C).a #doctest: +ELLIPSIS
+Getting &lt;descriptor.AttributeDescriptor object at 0x...&gt; from &lt;class 'super_ex.C'&gt;
+</pre>
+<p><tt class="docutils literal"><span class="pre">super</span></tt> does not work with old-style classes, however you can use the
+following trick:</p>
+<pre class="literal-block">
+#&lt;super_old_new.py&gt;
+class O:
+ def __init__(self):
+ print &quot;O.__init__&quot;
+
+class N(O, object):
+ def __init__(self):
+ print &quot;N.__init__&quot;
+ super(N, self).__init__()
+
+#&lt;/super_old_new.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from super_old_new import N
+&gt;&gt;&gt; new = N()
+N.__init__
+O.__init__
+</pre>
+<p>There are dozens of tricky points concerning <tt class="docutils literal"><span class="pre">super</span></tt>, be warned!</p>
+</div>
+<div class="section" id="subclassing-built-in-types-new-vs-init">
+<h3><a class="toc-backref" href="#id35" name="subclassing-built-in-types-new-vs-init">Subclassing built-in types; __new__ vs. __init__</a></h3>
+<pre class="literal-block">
+#&lt;point.py&gt;
+
+class NotWorkingPoint(tuple):
+ def __init__(self, x, y):
+ super(NotWorkingPoint, self).__init__((x,y))
+ self.x, self.y = x, y
+
+#&lt;/point.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from point import NotWorkingPoint
+&gt;&gt;&gt; p = NotWorkingPoint(2,3)
+Traceback (most recent call last):
+ ...
+TypeError: tuple() takes at most 1 argument (2 given)
+</pre>
+<pre class="literal-block">
+#&lt;point.py&gt;
+
+class Point(tuple):
+ def __new__(cls, x, y):
+ return super(Point, cls).__new__(cls, (x,y))
+ def __init__(self, x, y):
+ super(Point, self).__init__((x, y))
+ self.x, self.y = x, y
+
+#&lt;/point.py&gt;
+</pre>
+<p>Notice that__new__ is a staticmethod, not a classmethod, so one needs
+to pass the class explicitely.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from point import Point
+&gt;&gt;&gt; p = Point(2,3)
+&gt;&gt;&gt; print p, p.x, p.y
+(2, 3) 2 3
+</pre>
+</div>
+<div class="section" id="be-careful-when-using-new-with-mutable-types">
+<h3><a class="toc-backref" href="#id36" name="be-careful-when-using-new-with-mutable-types">Be careful when using __new__ with mutable types</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWithDefault(list):
+... def __new__(cls):
+... return super(ListWithDefault, cls).__new__(cls, [&quot;hello&quot;])
+...
+&gt;&gt;&gt; print ListWithDefault() # beware! NOT [&quot;hello&quot;]!
+[]
+</pre>
+<p>Reason: lists are re-initialized to empty lists in list.__init__!</p>
+<p>Instead</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWithDefault(list):
+... def __init__(self):
+... super(ListWithDefault, self).__init__([&quot;hello&quot;])
+...
+&gt;&gt;&gt; print ListWithDefault() # works!
+['hello']
+</pre>
+</div>
+</div>
+</div>
+<div class="section" id="lecture-3-magic-i-e-decorators-and-metaclasses">
+<h1><a class="toc-backref" href="#id37" name="lecture-3-magic-i-e-decorators-and-metaclasses">Lecture 3: Magic (i.e. decorators and metaclasses)</a></h1>
+<div class="section" id="part-i-decorators">
+<h2><a class="toc-backref" href="#id38" name="part-i-decorators">Part I: decorators</a></h2>
+<p>Decorators are just sugar: their functionality was already in the language</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def s(): pass
+&gt;&gt;&gt; s = staticmethod(s)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; &#64;staticmethod
+... def s(): pass
+...
+</pre>
+<p>However sugar <em>does</em> matter.</p>
+<div class="section" id="a-typical-decorator-traced">
+<h3><a class="toc-backref" href="#id39" name="a-typical-decorator-traced">A typical decorator: traced</a></h3>
+<pre class="literal-block">
+#&lt;traced.py&gt;
+
+def traced(func):
+ def tracedfunc(*args, **kw):
+ print &quot;calling %s.%s&quot; % (func.__module__, func.__name__)
+ return func(*args, **kw)
+ tracedfunc.__name__ = func.__name__
+ return tracedfunc
+
+&#64;traced
+def f(): pass
+
+#&lt;/traced.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from traced import f
+&gt;&gt;&gt; f()
+calling traced.f
+</pre>
+</div>
+<div class="section" id="a-decorator-factory-timed">
+<h3><a class="toc-backref" href="#id40" name="a-decorator-factory-timed">A decorator factory: Timed</a></h3>
+<pre class="literal-block">
+#&lt;timed.py&gt;
+
+import sys, time
+
+class Timed(object):
+ &quot;&quot;&quot;Decorator factory: each decorator object wraps a function and
+ executes it many times (default 100 times).
+ The average time spent in one iteration, expressed in milliseconds,
+ is stored in the attributes wrappedfunc.time and wrappedfunc.clocktime,
+ and displayed into a log file which defaults to stdout.
+ &quot;&quot;&quot;
+ def __init__(self, repeat=100, logfile=sys.stdout):
+ self.repeat = repeat
+ self.logfile = logfile
+ def __call__(self, func):
+ def wrappedfunc(*args, **kw):
+ fullname = &quot;%s.%s ...&quot; % (func.__module__, func.func_name)
+ print &gt;&gt; self.logfile, 'Executing %s' % fullname.ljust(30),
+ time1 = time.time()
+ clocktime1 = time.clock()
+ for i in xrange(self.repeat):
+ res = func(*args,**kw) # executes func self.repeat times
+ time2 = time.time()
+ clocktime2 = time.clock()
+ wrappedfunc.time = 1000*(time2-time1)/self.repeat
+ wrappedfunc.clocktime = 1000*(clocktime2 - clocktime1)/self.repeat
+ print &gt;&gt; self.logfile, \
+ 'Real time: %s ms;' % self.rounding(wrappedfunc.time),
+ print &gt;&gt; self.logfile, \
+ 'Clock time: %s ms' % self.rounding(wrappedfunc.clocktime)
+ return res
+ wrappedfunc.func_name = func.func_name
+ wrappedfunc.__module__ = func.__module__
+ return wrappedfunc
+ &#64;staticmethod
+ def rounding(float_):
+ &quot;Three digits rounding for small numbers, 1 digit rounding otherwise.&quot;
+ if float_ &lt; 10.:
+ return &quot;%5.3f&quot; % float_
+ else:
+ return &quot;%5.1f&quot; % float_
+
+#&lt;/timed.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from timed import Timed
+&gt;&gt;&gt; from random import sample
+&gt;&gt;&gt; example_ls = sample(xrange(1000000), 1000)
+&gt;&gt;&gt; &#64;Timed()
+... def list_sort(ls):
+... ls.sort()
+...
+&gt;&gt;&gt; list_sort(example_ls) #doctest: +ELLIPSIS
+Executing __main__.list_sort ... Real time: 0... ms; Clock time: 0... ms
+</pre>
+</div>
+<div class="section" id="a-powerful-decorator-pattern">
+<h3><a class="toc-backref" href="#id41" name="a-powerful-decorator-pattern">A powerful decorator pattern</a></h3>
+<pre class="literal-block">
+#&lt;traced_function2.py&gt;
+
+from decorators import decorator
+
+def trace(f, *args, **kw):
+ print &quot;calling %s with args %s, %s&quot; % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+traced_function = decorator(trace)
+
+&#64;traced_function
+def f1(x):
+ pass
+
+&#64;traced_function
+def f2(x, y):
+ pass
+
+#&lt;/traced_function2.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from traced_function2 import traced_function, f1, f2
+&gt;&gt;&gt; f1(1)
+calling f1 with args (1,), {}
+&gt;&gt;&gt; f2(1,2)
+calling f2 with args (1, 2), {}
+</pre>
+<p>works with pydoc:</p>
+<pre class="literal-block">
+$ pydoc2.4 traced_function2.f2
+Help on function f1 in traced_function2:
+
+traced_function2.f1 = f1(x)
+
+$ pydoc2.4 traced_function2.f2
+Help on function f2 in traced_function2:
+
+traced_function2.f2 = f2(x, y)
+</pre>
+<p>Here is the source code:</p>
+<pre class="literal-block">
+#&lt;decorators.py&gt;
+
+import inspect, itertools
+
+def getinfo(func):
+ &quot;&quot;&quot;Return an info dictionary containing:
+ - name (the name of the function : str)
+ - argnames (the names of the arguments : list)
+ - defarg (the values of the default arguments : list)
+ - fullsign (the full signature : str)
+ - shortsign (the short signature : str)
+ - arg0 ... argn (shortcuts for the names of the arguments)
+
+ &gt;&gt; def f(self, x=1, y=2, *args, **kw): pass
+
+ &gt;&gt; info = getinfo(f)
+
+ &gt;&gt; info[&quot;name&quot;]
+ 'f'
+ &gt;&gt; info[&quot;argnames&quot;]
+ ['self', 'x', 'y', 'args', 'kw']
+
+ &gt;&gt; info[&quot;defarg&quot;]
+ (1, 2)
+
+ &gt;&gt; info[&quot;shortsign&quot;]
+ 'self, x, y, *args, **kw'
+
+ &gt;&gt; info[&quot;fullsign&quot;]
+ 'self, x=defarg[0], y=defarg[1], *args, **kw'
+
+ &gt;&gt; info[&quot;arg0&quot;], info[&quot;arg1&quot;], info[&quot;arg2&quot;], info[&quot;arg3&quot;], info[&quot;arg4&quot;]
+ ('self', 'x', 'y', 'args', 'kw')
+ &quot;&quot;&quot;
+ assert inspect.ismethod(func) or inspect.isfunction(func)
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+ argnames = list(regargs)
+ if varargs: argnames.append(varargs)
+ if varkwargs: argnames.append(varkwargs)
+ counter = itertools.count()
+ fullsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: &quot;=defarg[%i]&quot; % counter.next())[1:-1]
+ shortsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: &quot;&quot;)[1:-1]
+ dic = dict((&quot;arg%s&quot; % n, name) for n, name in enumerate(argnames))
+ dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
+ fullsign = fullsign, defarg = func.func_defaults or ())
+ return dic
+
+def _contains_reserved_names(dic): # helper
+ return &quot;_call_&quot; in dic or &quot;_func_&quot; in dic
+
+def _decorate(func, caller):
+ &quot;&quot;&quot;Takes a function and a caller and returns the function
+ decorated with that caller. The decorated function is obtained
+ by evaluating a lambda function with the correct signature.
+ &quot;&quot;&quot;
+ infodict = getinfo(func)
+ assert not _contains_reserved_names(infodict[&quot;argnames&quot;]), \
+ &quot;You cannot use _call_ or _func_ as argument names!&quot;
+ execdict=dict(_func_=func, _call_=caller, defarg=func.func_defaults or ())
+ if func.__name__ == &quot;&lt;lambda&gt;&quot;:
+ lambda_src = &quot;lambda %(fullsign)s: _call_(_func_, %(shortsign)s)&quot; \
+ % infodict
+ dec_func = eval(lambda_src, execdict)
+ else:
+ func_src = &quot;&quot;&quot;def %(name)s(%(fullsign)s):
+ return _call_(_func_, %(shortsign)s)&quot;&quot;&quot; % infodict
+ exec func_src in execdict
+ dec_func = execdict[func.__name__]
+ dec_func.__doc__ = func.__doc__
+ dec_func.__dict__ = func.__dict__
+ return dec_func
+
+class decorator(object):
+ &quot;&quot;&quot;General purpose decorator factory: takes a caller function as
+input and returns a decorator. A caller function is any function like this::
+
+ def caller(func, *args, **kw):
+ # do something
+ return func(*args, **kw)
+
+Here is an example of usage:
+
+ &gt;&gt; &#64;decorator
+ .. def chatty(f, *args, **kw):
+ .. print &quot;Calling %r&quot; % f.__name__
+ .. return f(*args, **kw)
+
+ &gt;&gt; &#64;chatty
+ .. def f(): pass
+ ..
+ &gt;&gt; f()
+ Calling 'f'
+ &quot;&quot;&quot;
+ def __init__(self, caller):
+ self.caller = caller
+ def __call__(self, func):
+ return _decorate(func, self.caller)
+
+
+#&lt;/decorators.py&gt;
+</pre>
+<p>The possibilities of this pattern are endless.</p>
+</div>
+<div class="section" id="a-deferred-decorator">
+<h3><a class="toc-backref" href="#id42" name="a-deferred-decorator">A deferred decorator</a></h3>
+<p>You want to execute a procedure only after a certain time delay (for instance
+for use within an asyncronous Web framework):</p>
+<pre class="literal-block">
+#&lt;deferred.py&gt;
+&quot;Deferring the execution of a procedure (function returning None)&quot;
+
+import threading
+from decorators import decorator
+
+def deferred(nsec):
+ def call_later(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(call_later)
+
+&#64;deferred(2)
+def hello():
+ print &quot;hello&quot;
+
+if __name__ == &quot;__main__&quot;:
+ hello()
+ print &quot;Calling hello() ...&quot;
+
+
+#&lt;/deferred.py&gt;
+
+$ python deferred.py
+</pre>
+<p>Show an example of an experimental decorator based web framework
+(doctester_frontend).</p>
+</div>
+</div>
+<div class="section" id="part-ii-metaclasses">
+<h2><a class="toc-backref" href="#id43" name="part-ii-metaclasses">Part II: metaclasses</a></h2>
+<p>Metaclasses are there! Consider this example from a recent post on c.l.py:</p>
+<pre class="literal-block">
+#&lt;BaseClass.py&gt;
+
+class BaseClass(object):
+ &quot;Do something&quot;
+
+#&lt;/BaseClass.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; import BaseClass # instead of 'from BaseClass import BaseClass'
+&gt;&gt;&gt; class C(BaseClass): pass
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ module.__init__() takes at most 2 arguments (3 given)
+</pre>
+<p>The reason for the error is that class <tt class="docutils literal"><span class="pre">C(BaseClass):</span> <span class="pre">pass</span></tt> is
+actually calling the <tt class="docutils literal"><span class="pre">type</span></tt> metaclass with three arguments:</p>
+<pre class="literal-block">
+C = type(&quot;C&quot;, (BaseClass,), {})
+</pre>
+<p><tt class="docutils literal"><span class="pre">type.__new__</span></tt> tries to use <tt class="docutils literal"><span class="pre">type(BaseClass)</span></tt> as metaclass,
+but since BaseClass here is a module, and <tt class="docutils literal"><span class="pre">ModuleType</span></tt> is not
+a metaclass, it cannot work. The error message reflects a conflict with
+the signature of ModuleType which requires two parameters and not three.</p>
+<p>So even if you don't use them, you may want to know they exist.</p>
+<div class="section" id="rejuvenating-old-style-classes">
+<h3><a class="toc-backref" href="#id44" name="rejuvenating-old-style-classes">Rejuvenating old-style classes</a></h3>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Old: pass
+&gt;&gt;&gt; print type(Old)
+&lt;type 'classobj'&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; __metaclass__ = type # to rejuvenate class
+&gt;&gt;&gt; class NotOld: pass
+...
+&gt;&gt;&gt; print NotOld.__class__
+&lt;type 'type'&gt;
+</pre>
+</div>
+<div class="section" id="a-typical-metaclass-example-metatracer">
+<h3><a class="toc-backref" href="#id45" name="a-typical-metaclass-example-metatracer">A typical metaclass example: MetaTracer</a></h3>
+<pre class="literal-block">
+#&lt;metatracer.py&gt;
+
+import inspect
+from decorators import decorator
+
+&#64;decorator
+def traced(meth, *args, **kw):
+ cls = meth.__cls__
+ modname = meth.__module__ or cls.__module__
+ print &quot;calling %s.%s.%s&quot; % (modname, cls.__name__, meth.__name__)
+ return meth(*args, **kw)
+
+class MetaTracer(type):
+ def __init__(cls, name, bases, dic):
+ super(MetaTracer, cls).__init__(name, bases, dic)
+ for k, v in dic.iteritems():
+ if inspect.isfunction(v):
+ v.__cls__ = cls # so we know in which class v was defined
+ setattr(cls, k, traced(v))
+
+#&lt;/metatracer.py&gt;
+</pre>
+<p>Usage: exploring classes in the standard library</p>
+<pre class="literal-block">
+#&lt;dictmixin.py&gt;
+
+from metatracer import MetaTracer
+from UserDict import DictMixin
+
+class TracedDM(DictMixin, object):
+ __metaclass__ = MetaTracer
+ def __getitem__(self, item):
+ return item
+ def keys(self):
+ return [1,2,3]
+
+#&lt;/dictmixin.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from dictmixin import TracedDM
+&gt;&gt;&gt; print TracedDM()
+calling dictmixin.TracedDM.keys
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+{1: 1, 2: 2, 3: 3}
+</pre>
+</div>
+<div class="section" id="real-life-example-check-overriding">
+<h3><a class="toc-backref" href="#id46" name="real-life-example-check-overriding">Real life example: check overriding</a></h3>
+<pre class="literal-block">
+#&lt;check_overriding.py&gt;
+
+class Base(object):
+ a = 0
+
+class CheckOverriding(type):
+ &quot;Prints a message if we are overriding a name.&quot;
+ def __new__(mcl, name, bases, dic):
+ for name, val in dic.iteritems():
+ if name.startswith(&quot;__&quot;) and name.endswith(&quot;__&quot;):
+ continue # ignore special names
+ a_base_has_name = True in (hasattr(base, name) for base in bases)
+ if a_base_has_name:
+ print &quot;AlreadyDefinedNameWarning: &quot; + name
+ return super(CheckOverriding, mcl).__new__(mcl, name, bases, dic)
+
+class MyClass(Base):
+ __metaclass__ = CheckOverriding
+ a = 1
+
+class ChildClass(MyClass):
+ a = 2
+</pre>
+<p>#&lt;/check_overriding.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import check_overriding
+AlreadyDefinedNameWarning: a
+AlreadyDefinedNameWarning: a
+</pre>
+</div>
+<div class="section" id="logfile">
+<h3><a class="toc-backref" href="#id47" name="logfile">LogFile</a></h3>
+<pre class="literal-block">
+#&lt;logfile.py&gt;
+
+import subprocess
+
+def memoize(func):
+ memoize_dic = {}
+ def wrapped_func(*args):
+ if args in memoize_dic:
+ return memoize_dic[args]
+ else:
+ result = func(*args)
+ memoize_dic[args] = result
+ return result
+ wrapped_func.__name__ = func.__name__
+ wrapped_func.__doc__ = func.__doc__
+ wrapped_func.__dict__ = func.__dict__
+ return wrapped_func
+
+class Memoize(type): # Singleton is a special case of Memoize
+ &#64;memoize
+ def __call__(cls, *args):
+ return super(Memoize, cls).__call__(*args)
+
+class LogFile(file):
+ &quot;&quot;&quot;Open a file for append.&quot;&quot;&quot;
+ __metaclass__ = Memoize
+ def __init__(self, name = &quot;/tmp/err.log&quot;):
+ self.viewer_cmd = 'xterm -e less'.split()
+ super(LogFile, self).__init__(name, &quot;a&quot;)
+
+ def display(self, *ls):
+ &quot;Use 'less' to display the log file in a separate xterm.&quot;
+ print &gt;&gt; self, &quot;\n&quot;.join(map(str, ls)); self.flush()
+ subprocess.call(self.viewer_cmd + [self.name])
+
+ def reset(self):
+ &quot;Erase the log file.&quot;
+ print &gt;&gt; file(self.name, &quot;w&quot;)
+
+if __name__ == &quot;__main__&quot;: # test
+ print &gt;&gt; LogFile(), &quot;hello&quot;
+ print &gt;&gt; LogFile(), &quot;world&quot;
+ LogFile().display()
+
+#&lt;/logfile.py&gt;
+
+$ python logfile.py
+</pre>
+</div>
+<div class="section" id="cooperative-hierarchies">
+<h3><a class="toc-backref" href="#id48" name="cooperative-hierarchies">Cooperative hierarchies</a></h3>
+<pre class="literal-block">
+#&lt;cooperative_init.py&gt;
+
+&quot;&quot;&quot;Given a hierarchy, makes __init__ cooperative.
+The only change needed is to add a line
+
+ __metaclass__ = CooperativeInit
+
+to the base class of your hierarchy.&quot;&quot;&quot;
+
+from decorators import decorator
+
+class CooperativeInit(type):
+ def __init__(cls, name, bases, dic):
+
+ &#64;decorator
+ def make_cooperative(__init__, self, *args, **kw):
+ super(cls, self).__init__(*args, **kw)
+ __init__(self, *args, **kw)
+
+ __init__ = dic.get(&quot;__init__&quot;)
+ if __init__:
+ cls.__init__ = make_cooperative(__init__)
+
+class Base:
+ __metaclass__ = CooperativeInit
+ def __init__(self):
+ print &quot;B.__init__&quot;
+
+class C1(Base):
+ def __init__(self):
+ print &quot;C1.__init__&quot;
+
+class C2(Base):
+ def __init__(self):
+ print &quot;C2.__init__&quot;
+
+class D(C1, C2):
+ def __init__(self):
+ print &quot;D.__init__&quot;
+
+#&lt;/cooperative_init.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from cooperative_init import D
+&gt;&gt;&gt; d = D()
+B.__init__
+C2.__init__
+C1.__init__
+D.__init__
+</pre>
+</div>
+<div class="section" id="metaclass-enhanced-modules">
+<h3><a class="toc-backref" href="#id49" name="metaclass-enhanced-modules">Metaclass-enhanced modules</a></h3>
+<pre class="literal-block">
+#&lt;import_with_metaclass.py&gt;
+&quot;&quot;&quot;
+``import_with_metaclass(metaclass, modulepath)`` generates
+a new module from and old module, by enhancing all of its classes.
+This is not perfect, but it should give you a start.&quot;&quot;&quot;
+
+import os, sys, inspect, types
+
+def import_with_metaclass(metaclass, modulepath):
+ modname = os.path.basename(modulepath)[:-3] # simplistic
+ mod = types.ModuleType(modname)
+ locs = dict(
+ __module__ = modname,
+ __metaclass__ = metaclass,
+ object = metaclass(&quot;object&quot;, (), {}))
+ execfile(modulepath, locs)
+ for k, v in locs.iteritems():
+ if inspect.isclass(v): # otherwise it would be &quot;__builtin__&quot;
+ v.__module__ = &quot;__dynamic__&quot;
+ setattr(mod, k, v)
+ return mod
+</pre>
+<p>#&lt;/import_with_metaclass.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from import_with_metaclass import import_with_metaclass
+&gt;&gt;&gt; from metatracer import MetaTracer
+&gt;&gt;&gt; traced_optparse = import_with_metaclass(MetaTracer,
+... &quot;/usr/lib/python2.4/optparse.py&quot;)
+&gt;&gt;&gt; op = traced_optparse.OptionParser()
+calling __dynamic__.OptionParser.__init__
+calling __dynamic__.OptionContainer.__init__
+calling __dynamic__.OptionParser._create_option_list
+calling __dynamic__.OptionContainer._create_option_mappings
+calling __dynamic__.OptionContainer.set_conflict_handler
+calling __dynamic__.OptionContainer.set_description
+calling __dynamic__.OptionParser.set_usage
+calling __dynamic__.IndentedHelpFormatter.__init__
+calling __dynamic__.HelpFormatter.__init__
+calling __dynamic__.HelpFormatter.set_parser
+calling __dynamic__.OptionParser._populate_option_list
+calling __dynamic__.OptionParser._add_help_option
+calling __dynamic__.OptionContainer.add_option
+calling __dynamic__.Option.__init__
+calling __dynamic__.Option._check_opt_strings
+calling __dynamic__.Option._set_opt_strings
+calling __dynamic__.Option._set_attrs
+calling __dynamic__.OptionContainer._check_conflict
+calling __dynamic__.OptionParser._init_parsing_state
+</pre>
+<p>traced_optparse is a dynamically generated module not leaving in the
+file system.</p>
+</div>
+<div class="section" id="magic-properties">
+<h3><a class="toc-backref" href="#id50" name="magic-properties">Magic properties</a></h3>
+<pre class="literal-block">
+#&lt;magicprop.py&gt;
+
+class MagicProperties(type):
+ def __init__(cls, name, bases, dic):
+ prop_names = set(name[3:] for name in dic
+ if name.startswith(&quot;get&quot;)
+ or name.startswith(&quot;set&quot;))
+ for name in prop_names:
+ getter = getattr(cls, &quot;get&quot; + name, None)
+ setter = getattr(cls, &quot;set&quot; + name, None)
+ setattr(cls, name, property(getter, setter))
+
+class Base(object):
+ __metaclass__ = MagicProperties
+ def getx(self):
+ return self._x
+ def setx(self, value):
+ self._x = value
+
+class Child(Base):
+ def getx(self):
+ print &quot;getting x&quot;
+ return super(Child, self).getx()
+ def setx(self, value):
+ print &quot;setting x&quot;
+ super(Child, self).setx(value)
+
+#&lt;/magicprop.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from magicprop import Child
+&gt;&gt;&gt; c = Child()
+&gt;&gt;&gt; c.x = 1
+setting x
+&gt;&gt;&gt; print c.x
+getting x
+1
+</pre>
+</div>
+<div class="section" id="hack-evil-properties">
+<h3><a class="toc-backref" href="#id51" name="hack-evil-properties">Hack: evil properties</a></h3>
+<pre class="literal-block">
+#&lt;evilprop.py&gt;
+
+def convert2property(name, bases, d):
+ return property(d.get('get'), d.get('set'),
+ d.get('del'),d.get('__doc__'))
+
+class C(object):
+ class x:
+ &quot;&quot;&quot;An evil test property&quot;&quot;&quot;
+ __metaclass__ = convert2property
+ def get(self):
+ print 'Getting %s' % self._x
+ return self._x
+ def set(self, value):
+ self._x = value
+ print 'Setting to', value
+
+#&lt;/evilprop.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from evilprop import C
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.x = 5
+Setting to 5
+&gt;&gt;&gt; c.x
+Getting 5
+5
+&gt;&gt;&gt; print C.x.__doc__
+An evil test property
+</pre>
+</div>
+<div class="section" id="why-i-suggest-not-to-use-metaclasses-in-production-code">
+<h3><a class="toc-backref" href="#id52" name="why-i-suggest-not-to-use-metaclasses-in-production-code">Why I suggest <em>not</em> to use metaclasses in production code</a></h3>
+<blockquote>
+<ul class="simple">
+<li>there are very few good use case for metaclasses in production code
+(i.e. 99% of time you don't need them)</li>
+<li>they put a cognitive burden on the developer;</li>
+<li>a design without metaclasses is less magic and likely more robust;</li>
+<li>a design with metaclasses makes it difficult to use other metaclasses
+for debugging.</li>
+</ul>
+</blockquote>
+<p>As far as I know, string.Template is the only metaclass-enhanced class
+in the standard library; the metaclass is used to give the possibility to
+change the defaults:</p>
+<pre class="literal-block">
+delimiter = '$'
+idpattern = r'[_a-z][_a-z0-9]*'
+</pre>
+<p>in subclasses of Template.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from string import Template
+&gt;&gt;&gt; from metatracer import MetaTracer
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = MetaTracer
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>Solution: use a consistent metaclass</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class GoodMeta(MetaTracer, type(Template)): pass
+...
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = GoodMeta
+</pre>
+</div>
+<div class="section" id="is-there-an-automatic-way-of-solving-the-conflict">
+<h3><a class="toc-backref" href="#id53" name="is-there-an-automatic-way-of-solving-the-conflict">Is there an automatic way of solving the conflict?</a></h3>
+<p>Yes, but you really need to be a metaclass wizard.</p>
+<p><a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197</a></p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from noconflict import classmaker
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = classmaker((MetaTracer,))
+&gt;&gt;&gt; print type(TracedTemplate)
+&lt;class 'noconflict._MetaTracer_TemplateMetaclass'&gt;
+</pre>
+<pre class="literal-block">
+#&lt;noconflict.py&gt;
+
+import inspect, types, __builtin__
+from skip_redundant import skip_redundant
+
+memoized_metaclasses_map = {}
+
+# utility function
+def remove_redundant(metaclasses):
+ skipset = set([types.ClassType])
+ for meta in metaclasses: # determines the metaclasses to be skipped
+ skipset.update(inspect.getmro(meta)[1:])
+ return tuple(skip_redundant(metaclasses, skipset))
+
+##################################################################
+## now the core of the module: two mutually recursive functions ##
+##################################################################
+
+def get_noconflict_metaclass(bases, left_metas, right_metas):
+ &quot;&quot;&quot;Not intended to be used outside of this module, unless you know
+ what you are doing.&quot;&quot;&quot;
+ # make tuple of needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ needed_metas = remove_redundant(metas)
+
+ # return existing confict-solving meta, if any
+ if needed_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[needed_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ elif not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ # check for recursion, can happen i.e. for Zope ExtensionClasses
+ elif needed_metas == bases:
+ raise TypeError(&quot;Incompatible root metatypes&quot;, needed_metas)
+ else: # gotta work ...
+ metaname = '_' + ''.join([m.__name__ for m in needed_metas])
+ meta = classmaker()(metaname, needed_metas, {})
+ memoized_metaclasses_map[needed_metas] = meta
+ return meta
+
+def classmaker(left_metas=(), right_metas=()):
+ def make_class(name, bases, adict):
+ metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
+ return metaclass(name, bases, adict)
+ return make_class
+
+#################################################################
+## and now a conflict-safe replacement for 'type' ##
+#################################################################
+
+__type__=__builtin__.type # the aboriginal 'type'
+# left available in case you decide to rebind __builtin__.type
+
+class safetype(__type__):
+ # this is REALLY DEEP MAGIC
+ &quot;&quot;&quot;Overrides the ``__new__`` method of the ``type`` metaclass, making the
+ generation of classes conflict-proof.&quot;&quot;&quot;
+ def __new__(mcl, *args):
+ nargs = len(args)
+ if nargs == 1: # works as __builtin__.type
+ return __type__(args[0])
+ elif nargs == 3: # creates the class using the appropriate metaclass
+ n, b, d = args # name, bases and dictionary
+ meta = get_noconflict_metaclass(b, (mcl,), ())
+ if meta is mcl: # meta is trivial, dispatch to the default __new__
+ return super(safetype, mcl).__new__(mcl, n, b, d)
+ else: # non-trivial metaclass, dispatch to the right __new__
+ # (it will take a second round) # print mcl, meta
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
+
+#&lt;/noconflict.py&gt;
+</pre>
+</div>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/all.rst b/pypers/oxford/all.rst
new file mode 100755
index 0000000..dee7230
--- /dev/null
+++ b/pypers/oxford/all.rst
@@ -0,0 +1,2006 @@
+Lectures on Advanced Python Programming
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. image:: accu2005.png
+
+:Author: Michele Simionato
+:Given: 19 April 2005
+:Revised: 7 September 2005
+
+.. contents::
+
+
+
+Lecture 1: Loops (i.e. iterators & generators)
+==============================================
+
+Part I: iterators
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Iterators are everywhere
+--------------------------------
+
+>>> for i in 1, 2, 3:
+... print i
+1
+2
+3
+
+The 'for' loop is using *iterators* internally::
+
+ it = iter((1,2,3))
+ while True:
+ try:
+ print it.next()
+ except StopIteration:
+ break
+
+Iterables and iterators
+--------------------------
+
+*Iterable* = anything you can loop over = any sequence + any object with an __iter__ method;
+
+Not every sequence has an __iter__ method:
+
+>>> "hello".__iter__()
+Traceback (most recent call last):
+ ...
+AttributeError: 'str' object has no attribute '__iter__'
+
+*Iterator* = any object with a .next method and an __iter__ method returning self
+
+Simpler way to get an iterator
+--------------------------------------------------------
+
+>>> it = iter("hello")
+>>> it.next()
+'h'
+>>> it.next()
+'e'
+>>> it.next()
+'l'
+>>> it.next()
+'l'
+>>> it.next()
+'o'
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+
+Sentinel syntax iter(callable, sentinel)
+--------------------------------------------
+
+Example::
+
+ $ echo -e "value1\nvalue2\nEND\n" > data.txt
+ $ python -c "print list(iter(file('data.txt').readline, 'END\n'))"
+ ['value1\n', 'value2\n']
+
+Beware of infinite iterators:
+
+>>> repeat = iter(lambda : "some value", "")
+>>> repeat.next()
+'some value'
+
+Second simpler way to get an iterator: generator-expressions
+-------------------------------------------------------------
+
+>>> squares = (i*i for i in range(1,11))
+>>> list(squares)
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
+
+Excessive parenthesis can be skipped, so use
+
+>>> dict((i, i*i) for i in range(1,11))
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+
+instead of
+
+>>> dict([(i, i*i) for i in range(1,11)])
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+
+(as usual, the most elegant version is the most efficient).
+
+Iteration caveats
+--------------------------
+
+>>> ls = [i for i in (1,2,3)]
+>>> i
+3
+
+>>> it = (j for j in (1,2,3))
+>>> j
+Traceback (most recent call last):
+ ...
+NameError: name 'j' is not defined
+
+A subtler example:
+
+>>> ls = [lambda :i for i in (1,2,3)]
+>>> ls[0]()
+3
+
+instead
+
+>>> it = (lambda :i for i in (1,2,3))
+>>> it.next()()
+1
+>>> it.next()()
+2
+>>> it.next()()
+3
+
+*seems* to be working but it is not really the case:
+
+>>> it = (lambda :i for i in (1,2,3))
+>>> f1 = it.next()
+>>> f2 = it.next()
+>>> f3 = it.next()
+>>> f1()
+3
+
+The reason is that Python does LATE binding *always*. The solution is ugly:
+
+>>> it = list(lambda i=i:i for i in (1,2,3))
+>>> it[0]()
+1
+>>> it[1]()
+2
+>>> it[2]()
+3
+
+Part II: generators
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Trivial example:
+
+>>> def gen123(): # "function" which returns an iterator over the values 1, 2, 3
+... yield 1
+... yield 2
+... yield 3
+...
+>>> it = gen123()
+>>> it.next()
+1
+>>> it.next()
+2
+>>> it.next()
+3
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+
+Real life example: using generators to generate HTML tables
+
+::
+
+ #<htmltable.py>
+
+ def HTMLTablegen(table):
+ yield "<table>"
+ for row in table:
+ yield "<tr>"
+ for col in row:
+ yield "<td>%s</td>" % col
+ yield "</tr>"
+ yield "</table>"
+
+ def test():
+ return "\n".join(HTMLTablegen([["Row", "City"],
+ [1,'London'], [2, 'Oxford']]))
+
+ if __name__ == "__main__": # example
+ print test()
+
+ #</htmltable.py>
+
+>>> from htmltable import test
+>>> print test()
+<table>
+<tr>
+<td>Row</td>
+<td>City</td>
+</tr>
+<tr>
+<td>1</td>
+<td>London</td>
+</tr>
+<tr>
+<td>2</td>
+<td>Oxford</td>
+</tr>
+</table>
+
+A simple recipe: skip redundant
+---------------------------------
+
+How to remove duplicates by keeping the order::
+
+ #<skip_redundant.py>
+
+ def skip_redundant(iterable, skipset=None):
+ "Redundant items are repeated items or items in the original skipset."
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+ #</skip_redundant.py>
+
+>>> from skip_redundant import skip_redundant
+>>> print list(skip_redundant("<hello, world>", skipset=set("<>")))
+['h', 'e', 'l', 'o', ',', ' ', 'w', 'r', 'd']
+
+Another real life example: working with nested structures
+----------------------------------------------------------
+
+::
+
+ #<walk.py>
+
+ def walk(iterable, level=0):
+ for obj in iterable:
+ if not hasattr(obj, "__iter__"): # atomic object
+ yield obj, level
+ else: # composed object: iterate again
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+ def flatten(iterable):
+ return (obj for obj, level in walk(iterable))
+
+ def pprint(iterable):
+ for obj, level in walk(iterable):
+ print " "*level, obj
+
+ #</walk.py>
+
+>>> from walk import flatten, pprint
+>>> nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+>>> pprint(nested_ls)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+>>> pprint(flatten(nested_ls))
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+
+Another typical use case for generators: parsers
+---------------------------------------------------------
+
+A very stripped down parser for nested expressions
+
+::
+
+ #<sexpr2indent.py>
+ """A simple s-expression formatter."""
+
+ import re
+
+ def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r"(?P<paren_beg>\()|(?P<paren_end>\))")
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == "paren_beg":
+ nesting_level += 1
+ elif match.lastgroup == "paren_end":
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+ def sexpr_indent(sexpr):
+ for nesting, text in parse(sexpr.replace("\n", "")):
+ if text.strip(): print " "*nesting, text
+
+ #</sexpr2indent.py>
+
+>>> from sexpr2indent import sexpr_indent
+>>> sexpr_indent("""\
+... (html (head (title Example)) (body (h1 s-expr formatter example)
+... (a (@ (href http://www.example.com)) A link)))""")
+... #doctest: +NORMALIZE_WHITESPACE
+ html
+ head
+ title Example
+ body
+ h1 s-expr formatter example
+ a
+ @
+ href http://www.example.com
+ A link
+
+
+Other kinds of iterables
+------------------------------------------------
+
+The following class generates iterable which are not iterators:
+::
+
+ #<reiterable.py>
+
+ class ReIter(object):
+ "A re-iterable object."
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+ #</reiterable.py>
+
+>>> from reiterable import ReIter
+>>> rit = ReIter()
+>>> list(rit)
+[1, 2, 3]
+>>> list(rit) # it is reiterable!
+[1, 2, 3]
+
+The itertools module
+----------------------------------------------------
+
+ - count([n]) --> n, n+1, n+2, ...
+ - cycle(p) --> p0, p1, ... plast, p0, p1, ...
+ - repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times
+ - izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...
+ - ifilter(pred, seq) --> elements of seq where pred(elem) is True
+ - ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False
+ - islice(seq, [start,] stop [, step]) --> elements from seq[start:stop:step]
+ - imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...
+ - starmap(fun, seq) --> fun(\*seq[0]), fun(\*seq[1]), ...
+ - tee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n
+ - chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...
+ - takewhile(pred, seq) --> seq[0], seq[1], until pred fails
+ - dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails
+ - groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)
+
+anyTrue
+------------------------------
+
+>>> import itertools
+>>> def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+...
+>>> fname = "picture.gif"
+>>> anyTrue(fname.endswith, ".jpg .gif .png".split())
+True
+
+AnyTrue does *short-circuit*:
+
+>>> def is3(i):
+... print "i=%s" % i
+... return i == 3
+
+>>> anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True
+
+chop
+----------------------
+
+You want to chop an iterable in batches of a given size:
+
+>>> from chop import chop
+>>> list(chop([1, 2, 3, 4], 2))
+[[1, 2], [3, 4]]
+>>> list(chop([1, 2, 3, 4, 5, 6, 7],3))
+[[1, 2, 3], [4, 5, 6], [7]]
+
+Here is a possible implementation::
+
+ #<chop.py>
+
+ # see also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279
+
+ import itertools
+
+ def chop(iterable, batchsize):
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+ #</chop.py>
+
+For people thinking Python is too readable, here is a one-liner:
+
+>>> chop = lambda it, n : itertools.izip(*(iter(it),)*n)
+...
+>>> list(chop([1,2,3,4], 2))
+[(1, 2), (3, 4)]
+
+tee
+-----------------------
+
+To make copies of iterables; typically used in parsers:
+
+>>> from itertools import tee, chain, izip
+>>> chars, prevs = tee("abc")
+>>> prevs = chain([None], prevs)
+>>> for char, prev in izip(chars, prevs):
+... print char, prev
+...
+a None
+b a
+c b
+
+Grouping and sorting
+----------------------
+
+>>> from itertools import groupby
+>>> from operator import itemgetter
+
+>>> NAME, AGE = 0, 1
+>>> query_result = ("Smith", 34), ("Donaldson", 34), ("Lee", 22), ("Orr", 22)
+
+Grouping together people of the same age:
+
+>>> for k, g in groupby(query_result, key=itemgetter(AGE)):
+... print k, list(g)
+...
+34 [('Smith', 34), ('Donaldson', 34)]
+22 [('Lee', 22), ('Orr', 22)]
+
+Sorting by name:
+
+>>> for tup in sorted(query_result, key=itemgetter(NAME)):
+... print tup
+('Donaldson', 34)
+('Lee', 22)
+('Orr', 22)
+('Smith', 34)
+
+
+
+Lecture 2: Objects (delegation & inheritance)
+==============================================
+
+Part I: delegation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Understanding how attribute access works: internal delegation via *descriptors*
+
+Accessing simple attributes
+--------------------------------
+
+>>> class C(object):
+... a = 2
+... def __init__(self, x):
+... self.x = x
+...
+
+>>> c = C(1)
+>>> c.x
+1
+>>> c.a
+2
+
+We are retrieving
+
+>>> c.__dict__["x"]
+1
+
+If there is nothing in c.__dict__, Python looks at C.__dict__:
+
+>>> print c.__dict__.get("a")
+None
+
+>>> C.__dict__["a"]
+2
+
+If there is nothing in C.__dict__, Python looks at the superclasses according
+to the MRO (see part II).
+
+Accessing methods
+--------------------------------------------------------
+
+>>> c.__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>
+
+since __init__ is not in c.__dict__ Python looks in the class dictionary
+and finds
+
+>>> C.__dict__["__init__"] #doctest: +ELLIPSIS
+<function __init__ at 0x...>
+
+Then it magically converts the function into a method bound to the instance
+"c".
+
+NOTE: this mechanism works for new-style classes only.
+
+The old-style mechanism is less consistent and the attribute lookup of special
+methods is special: (*)
+
+>>> class C(object): pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c #doctest: +ELLIPSIS
+<__main__.C object at ...>
+
+whereas for old-style classes
+
+>>> class C: pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c
+hello!
+
+the special method is looked for in the instance dictionary too.
+
+(*) modulo a very subtle difference for __getattr__-delegated special methods,
+see later.
+
+Converting functions into methods
+-------------------------------------
+
+It is possible to convert a function into a bound or unbound method
+by invoking the ``__get__`` special method:
+
+>>> def f(x): pass
+>>> f.__get__ #doctest: +ELLIPSIS
+<method-wrapper object at 0x...>
+
+>>> class C(object): pass
+...
+
+>>> def f(self): pass
+...
+>>> f.__get__(C(), C) #doctest: +ELLIPSIS
+<bound method C.f of <__main__.C object at 0x...>>
+
+>>> f.__get__(None, C)
+<unbound method C.f>
+
+Functions are the simplest example of *descriptors*.
+
+Access to methods works since internally Python transforms
+
+ ``c.__init__ -> type(c).__dict__['__init__'].__get__(c, type(c))``
+
+
+Note: not *all* functions are descriptors:
+
+>>> from operator import add
+>>> add.__get__
+Traceback (most recent call last):
+ ...
+AttributeError: 'builtin_function_or_method' object has no attribute '__get__'
+
+Hack: a very slick adder
+-----------------------------
+
+The descriptor protocol can be (ab)used as a way to avoid the late binding
+issue in for loops:
+
+>>> def add(x,y):
+... return x + y
+>>> closures = [add.__get__(i) for i in range(10)]
+>>> closures[5](1000)
+1005
+
+Notice: operator.add will not work.
+
+Descriptor Protocol
+----------------------
+
+Everything at http://users.rcn.com/python/download/Descriptor.htm
+
+Formally::
+
+ descr.__get__(self, obj, type=None) --> value
+ descr.__set__(self, obj, value) --> None
+ descr.__delete__(self, obj) --> None
+
+Examples of custom descriptors::
+
+ #<descriptor.py>
+
+
+ class AttributeDescriptor(object):
+ def __get__(self, obj, cls=None):
+ if obj is None and cls is None:
+ raise TypeError("__get__(None, None) is invalid")
+ elif obj is None:
+ return self.get_from_class(cls)
+ else:
+ return self.get_from_obj(obj)
+ def get_from_class(self, cls):
+ print "Getting %s from %s" % (self, cls)
+ def get_from_obj(self, obj):
+ print "Getting %s from %s" % (self, obj)
+
+
+ class Staticmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func
+ get_from_obj = get_from_class
+
+
+ class Classmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func.__get__(cls, type(cls))
+ def get_from_obj(self, obj):
+ return self.get_from_class(obj.__class__)
+
+ class C(object):
+ s = Staticmethod(lambda : 1)
+ c = Classmethod(lambda cls : cls.__name__)
+
+ c = C()
+
+ assert C.s() == c.s() == 1
+ assert C.c() == c.c() == "C"
+
+ #</descriptor.py>
+
+Multilingual attribute
+----------------------
+
+Inspirated by a question in the Italian Newsgroup::
+
+ #<multilingual.py>
+
+ import sys
+ from descriptor import AttributeDescriptor
+
+ class MultilingualAttribute(AttributeDescriptor):
+ """When a MultilingualAttribute is accessed, you get the translation
+ corresponding to the currently selected language.
+ """
+ def __init__(self, **translations):
+ self.trans = translations
+ def get_from_class(self, cls):
+ return self.trans[getattr(cls, "language", None) or
+ sys.modules[cls.__module__].language]
+ def get_from_obj(self, obj):
+ return self.trans[getattr(obj, "language", None) or
+ sys.modules[obj.__class__.__module__].language]
+
+
+ language = "en"
+
+ # a dummy User class
+ class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+ class WebApplication(object):
+ error_msg = MultilingualAttribute(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ self.language = language or getattr(self.__class__, "language", None)
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
+ app = WebApplication()
+ assert app.show_page() == "You cannot access this page"
+
+ app.language = "fr"
+ assert app.show_page() == "Vous ne pouvez pas acceder cette page"
+
+ app.language = "it"
+ assert app.show_page() == "Questa pagina non e' accessibile"
+
+ app.language = "en"
+ assert app.show_page() == "You cannot access this page"
+
+ #</multilingual.py>
+
+The same can be done with properties::
+
+ #<multilingualprop.py>
+
+ language = "en"
+
+ # a dummy User class
+ class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+ def multilingualProperty(**trans):
+ def get(self):
+ return trans[self.language]
+ def set(self, value):
+ trans[self.language] = value
+ return property(get, set)
+
+ class WebApplication(object):
+ language = language
+ error_msg = multilingualProperty(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ if language: self.language = self.language
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+ #</multilingualprop.py>
+
+This also gives the possibility to set the error messages.
+
+The difference with the descriptor approach
+
+>>> from multilingual import WebApplication
+>>> app = WebApplication()
+>>> print app.error_msg
+You cannot access this page
+>>> print WebApplication.error_msg
+You cannot access this page
+
+is that with properties there is no nice access from the class:
+
+>>> from multilingualprop import WebApplication
+>>> WebApplication.error_msg #doctest: +ELLIPSIS
+<property object at ...>
+
+Another use case for properties: storing users
+------------------------------------------------------------
+
+Consider a library providing a simple User class::
+
+ #<crypt_user.py>
+
+ class User(object):
+ def __init__(self, username, password):
+ self.username, self.password = username, password
+
+ #</crypt_user.py>
+
+The User objects are stored in a database as they are.
+For security purpose, in a second version of the library it is
+decided to crypt the password, so that only crypted passwords
+are stored in the database. With properties, it is possible to
+implement this functionality without changing the source code for
+the User class::
+
+ #<crypt_user.py>
+
+ from crypt import crypt
+
+ def cryptedAttribute(seed="x"):
+ def get(self):
+ return getattr(self, "_pw", None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+ User.password = cryptedAttribute()
+
+#</crypt_user.py>
+
+>>> from crypt_user import User
+>>> u = User("michele", "secret")
+>>> print u.password
+xxZREZpkHZpkI
+
+Notice the property factory approach used here.
+
+Low-level delegation via __getattribute__
+------------------------------------------------------------------
+
+Attribute access is managed by the__getattribute__ special method::
+
+ #<tracedaccess.py>
+
+ class TracedAccess(object):
+ def __getattribute__(self, name):
+ print "Accessing %s" % name
+ return object.__getattribute__(self, name)
+
+
+ class C(TracedAccess):
+ s = staticmethod(lambda : 'staticmethod')
+ c = classmethod(lambda cls: 'classmethod')
+ m = lambda self: 'method'
+ a = "hello"
+
+ #</tracedaccess.py>
+
+>>> from tracedaccess import C
+>>> c = C()
+>>> print c.s()
+Accessing s
+staticmethod
+>>> print c.c()
+Accessing c
+classmethod
+>>> print c.m()
+Accessing m
+method
+>>> print c.a
+Accessing a
+hello
+>>> print c.__init__ #doctest: +ELLIPSIS
+Accessing __init__
+<method-wrapper object at 0x...>
+>>> try: c.x
+... except AttributeError, e: print e
+...
+Accessing x
+'C' object has no attribute 'x'
+
+>>> c.y = 'y'
+>>> c.y
+Accessing y
+'y'
+
+You are probably familiar with ``__getattr__`` which is similar
+to ``__getattribute__``, but it is called *only for missing attributes*.
+
+Traditional delegation via __getattr__
+--------------------------------------------------------
+
+Realistic use case in "object publishing"::
+
+ #<webapp.py>
+
+ class WebApplication(object):
+ def __getattr__(self, name):
+ return name.capitalize()
+
+
+ app = WebApplication()
+
+ assert app.page1 == 'Page1'
+ assert app.page2 == 'Page2'
+
+ #</webapp.py>
+
+Here is another use case in HTML generation::
+
+ #<XMLtag.py>
+
+ def makeattr(dict_or_list_of_pairs):
+ dic = dict(dict_or_list_of_pairs)
+ return " ".join('%s="%s"' % (k, dic[k]) for k in dic) # simplistic
+
+ class XMLTag(object):
+ def __getattr__(self, name):
+ def tag(value, **attr):
+ """value can be a string or a sequence of strings."""
+ if hasattr(value, "__iter__"): # is iterable
+ value = " ".join(value)
+ return "<%s %s>%s</%s>" % (name, makeattr(attr), value, name)
+ return tag
+
+ class XMLShortTag(object):
+ def __getattr__(self, name):
+ def tag(**attr):
+ return "<%s %s />" % (name, makeattr(attr))
+ return tag
+
+ tag = XMLTag()
+ tg = XMLShortTag()
+
+ #</XMLtag.py>
+
+>>> from XMLtag import tag, tg
+>>> print tag.a("example.com", href="http://www.example.com")
+<a href="http://www.example.com">example.com</a>
+>>> print tg.br(**{'class':"br_style"})
+<br class="br_style" />
+
+Keyword dictionaries with __getattr__/__setattr__
+---------------------------------------------------
+::
+
+ #<kwdict.py>
+
+ class kwdict(dict): # or UserDict, to make it to work with Zope
+ """A typing shortcut used in place of a keyword dictionary."""
+ def __getattr__(self, name):
+ return self[name]
+ def __setattr__(self, name, value):
+ self[name] = value
+
+ #</kwdict.py>
+
+And now for a completely different solution::
+
+ #<dictwrapper.py>
+
+ class DictWrapper(object):
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+ #</dictwrapper.py>
+
+
+Delegation to special methods caveat
+--------------------------------------
+
+>>> class ListWrapper(object):
+... def __init__(self, ls):
+... self._list = ls
+... def __getattr__(self, name):
+... if name == "__getitem__": # special method
+... return self._list.__getitem__
+... elif name == "reverse": # regular method
+... return self._list.reverse
+... else:
+... raise AttributeError("%r is not defined" % name)
+...
+>>> lw = ListWrapper([0,1,2])
+>>> print lw.x
+Traceback (most recent call last):
+ ...
+AttributeError: 'x' is not defined
+
+>>> lw.reverse()
+>>> print lw.__getitem__(0)
+2
+>>> print lw.__getitem__(1)
+1
+>>> print lw.__getitem__(2)
+0
+>>> print lw[0]
+Traceback (most recent call last):
+ ...
+TypeError: unindexable object
+
+
+Part II: Inheritance
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+The major changes in inheritance from Python 2.1 to 2.2+ are:
+
+1. you can subclass built-in types (as a consequence the constructor__new__
+ has been exposed to the user, to help subclassing immutable types);
+2. the Method Resolution Order (MRO) has changed;
+3. now Python allows *cooperative method calls*, i.e. we have *super*.
+
+Why you need to know about MI even if you do not use it
+-----------------------------------------------------------
+
+In principle, the last two changes are relevant only if you use multiple
+inheritance. If you use single inheritance only, you don't need ``super``:
+you can just name the superclass.
+However, somebody else may want to use your class in a MI hierarchy,
+and you would make her life difficult if you don't use ``super``.
+
+My SI hierarchy::
+
+ #<why_super.py>
+
+ class Base(object):
+ def __init__(self):
+ print "B.__init__"
+
+ class MyClass(Base):
+ "I do not cooperate with others"
+ def __init__(self):
+ print "MyClass.__init__"
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+ #</why_super.py>
+
+Her MI hierarchy::
+
+ #<why_super.py>
+
+ class Mixin(Base):
+ "I am cooperative with others"
+ def __init__(self):
+ print "Mixin.__init__"
+ super(Mixin, self).__init__()
+
+ class HerClass(MyClass, Mixin):
+ "I am supposed to be cooperative too"
+ def __init__(self):
+ print "HerClass.__init__"
+ super(HerClass, self).__init__()
+
+ #</why_super.py>
+
+>>> from why_super import HerClass
+>>> h = HerClass() # Mixin.__init__ is not called!
+HerClass.__init__
+MyClass.__init__
+B.__init__
+
+ ::
+
+ 4 object
+ |
+ 3 Base
+ / \
+ 1 MyClass 2 Mixin
+ \ /
+ 0 HerClass
+
+>>> [ancestor.__name__ for ancestor in HerClass.mro()]
+['HerClass', 'MyClass', 'Mixin', 'Base', 'object']
+
+In order to be polite versus your future users, you should use ``super``
+always. This adds a cognitive burden even for people not using MI :-(
+
+Notice that there is no good comprehensive reference on ``super`` (yet)
+Your best bet is still http://www.python.org/2.2.3/descrintro.html#cooperation
+
+The MRO instead is explained here: http://www.python.org/2.3/mro.html
+
+Notice that I DO NOT recommand Multiple Inheritance.
+
+More often than not you are better off using composition/delegation/wrapping,
+etc.
+
+See Zope 2 -> Zope 3 experience.
+
+A few details about ``super`` (not the whole truth)
+------------------------------------------------------------------------------
+
+>>> class B(object):
+... def __init__(self): print "B.__init__"
+...
+>>> class C(B):
+... def __init__(self): print "C.__init__"
+...
+>>> c = C()
+C.__init__
+
+``super(cls, instance)``, where ``instance`` is an instance of ``cls`` or of
+a subclass of ``cls``, retrieves the right method in the MRO:
+
+>>> super(C, c).__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>
+
+>>> super(C, c).__init__.im_func is B.__init__.im_func
+True
+
+>>> super(C, c).__init__()
+B.__init__
+
+``super(cls, subclass)`` works for unbound methods:
+
+>>> super(C, C).__init__
+<unbound method C.__init__>
+
+>>> super(C, C).__init__.im_func is B.__init__.im_func
+True
+>>> super(C, C).__init__(c)
+B.__init__
+
+``super(cls, subclass)`` is also necessary for classmethods and staticmethods.
+Properties and custom descriptorsw works too::
+
+ #<super_ex.py>
+
+ from descriptor import AttributeDescriptor
+
+ class B(object):
+ @staticmethod
+ def sm(): return "staticmethod"
+
+ @classmethod
+ def cm(cls): return cls.__name__
+
+ p = property()
+ a = AttributeDescriptor()
+
+ class C(B): pass
+
+ #</super_ex.py>
+
+>>> from super_ex import C
+
+Staticmethod usage:
+
+>>> super(C, C).sm #doctest: +ELLIPSIS
+<function sm at 0x...>
+>>> super(C, C).sm()
+'staticmethod'
+
+Classmethod usage:
+
+>>> super(C, C()).cm
+<bound method type.cm of <class 'super_ex.C'>>
+>>> super(C, C).cm() # C is automatically passed
+'C'
+
+Property usage:
+
+>>> print super(C, C).p #doctest: +ELLIPSIS
+<property object at 0x...>
+>>> super(C, C).a #doctest: +ELLIPSIS
+Getting <descriptor.AttributeDescriptor object at 0x...> from <class 'super_ex.C'>
+
+``super`` does not work with old-style classes, however you can use the
+following trick::
+
+ #<super_old_new.py>
+ class O:
+ def __init__(self):
+ print "O.__init__"
+
+ class N(O, object):
+ def __init__(self):
+ print "N.__init__"
+ super(N, self).__init__()
+
+ #</super_old_new.py>
+
+>>> from super_old_new import N
+>>> new = N()
+N.__init__
+O.__init__
+
+There are dozens of tricky points concerning ``super``, be warned!
+
+Subclassing built-in types; __new__ vs. __init__
+-----------------------------------------------------
+
+::
+
+ #<point.py>
+
+ class NotWorkingPoint(tuple):
+ def __init__(self, x, y):
+ super(NotWorkingPoint, self).__init__((x,y))
+ self.x, self.y = x, y
+
+ #</point.py>
+
+>>> from point import NotWorkingPoint
+>>> p = NotWorkingPoint(2,3)
+Traceback (most recent call last):
+ ...
+TypeError: tuple() takes at most 1 argument (2 given)
+
+::
+
+ #<point.py>
+
+ class Point(tuple):
+ def __new__(cls, x, y):
+ return super(Point, cls).__new__(cls, (x,y))
+ def __init__(self, x, y):
+ super(Point, self).__init__((x, y))
+ self.x, self.y = x, y
+
+ #</point.py>
+
+Notice that__new__ is a staticmethod, not a classmethod, so one needs
+to pass the class explicitely.
+
+>>> from point import Point
+>>> p = Point(2,3)
+>>> print p, p.x, p.y
+(2, 3) 2 3
+
+Be careful when using __new__ with mutable types
+------------------------------------------------
+
+>>> class ListWithDefault(list):
+... def __new__(cls):
+... return super(ListWithDefault, cls).__new__(cls, ["hello"])
+...
+>>> print ListWithDefault() # beware! NOT ["hello"]!
+[]
+
+Reason: lists are re-initialized to empty lists in list.__init__!
+
+Instead
+
+>>> class ListWithDefault(list):
+... def __init__(self):
+... super(ListWithDefault, self).__init__(["hello"])
+...
+>>> print ListWithDefault() # works!
+['hello']
+
+
+
+Lecture 3: Magic (i.e. decorators and metaclasses)
+================================================================
+
+Part I: decorators
++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Decorators are just sugar: their functionality was already in the language
+
+>>> def s(): pass
+>>> s = staticmethod(s)
+
+>>> @staticmethod
+... def s(): pass
+...
+
+However sugar *does* matter.
+
+A typical decorator: traced
+-----------------------------
+::
+
+ #<traced.py>
+
+ def traced(func):
+ def tracedfunc(*args, **kw):
+ print "calling %s.%s" % (func.__module__, func.__name__)
+ return func(*args, **kw)
+ tracedfunc.__name__ = func.__name__
+ return tracedfunc
+
+ @traced
+ def f(): pass
+
+ #</traced.py>
+
+>>> from traced import f
+>>> f()
+calling traced.f
+
+A decorator factory: Timed
+------------------------------------------
+
+::
+
+ #<timed.py>
+
+ import sys, time
+
+ class Timed(object):
+ """Decorator factory: each decorator object wraps a function and
+ executes it many times (default 100 times).
+ The average time spent in one iteration, expressed in milliseconds,
+ is stored in the attributes wrappedfunc.time and wrappedfunc.clocktime,
+ and displayed into a log file which defaults to stdout.
+ """
+ def __init__(self, repeat=100, logfile=sys.stdout):
+ self.repeat = repeat
+ self.logfile = logfile
+ def __call__(self, func):
+ def wrappedfunc(*args, **kw):
+ fullname = "%s.%s ..." % (func.__module__, func.func_name)
+ print >> self.logfile, 'Executing %s' % fullname.ljust(30),
+ time1 = time.time()
+ clocktime1 = time.clock()
+ for i in xrange(self.repeat):
+ res = func(*args,**kw) # executes func self.repeat times
+ time2 = time.time()
+ clocktime2 = time.clock()
+ wrappedfunc.time = 1000*(time2-time1)/self.repeat
+ wrappedfunc.clocktime = 1000*(clocktime2 - clocktime1)/self.repeat
+ print >> self.logfile, \
+ 'Real time: %s ms;' % self.rounding(wrappedfunc.time),
+ print >> self.logfile, \
+ 'Clock time: %s ms' % self.rounding(wrappedfunc.clocktime)
+ return res
+ wrappedfunc.func_name = func.func_name
+ wrappedfunc.__module__ = func.__module__
+ return wrappedfunc
+ @staticmethod
+ def rounding(float_):
+ "Three digits rounding for small numbers, 1 digit rounding otherwise."
+ if float_ < 10.:
+ return "%5.3f" % float_
+ else:
+ return "%5.1f" % float_
+
+ #</timed.py>
+
+>>> from timed import Timed
+>>> from random import sample
+>>> example_ls = sample(xrange(1000000), 1000)
+>>> @Timed()
+... def list_sort(ls):
+... ls.sort()
+...
+>>> list_sort(example_ls) #doctest: +ELLIPSIS
+Executing __main__.list_sort ... Real time: 0... ms; Clock time: 0... ms
+
+
+A powerful decorator pattern
+--------------------------------
+::
+
+ #<traced_function2.py>
+
+ from decorators import decorator
+
+ def trace(f, *args, **kw):
+ print "calling %s with args %s, %s" % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+ traced_function = decorator(trace)
+
+ @traced_function
+ def f1(x):
+ pass
+
+ @traced_function
+ def f2(x, y):
+ pass
+
+ #</traced_function2.py>
+
+>>> from traced_function2 import traced_function, f1, f2
+>>> f1(1)
+calling f1 with args (1,), {}
+>>> f2(1,2)
+calling f2 with args (1, 2), {}
+
+works with pydoc::
+
+ $ pydoc2.4 traced_function2.f2
+ Help on function f1 in traced_function2:
+
+ traced_function2.f1 = f1(x)
+
+ $ pydoc2.4 traced_function2.f2
+ Help on function f2 in traced_function2:
+
+ traced_function2.f2 = f2(x, y)
+
+Here is the source code::
+
+ #<decorators.py>
+
+ import inspect, itertools
+
+ def getinfo(func):
+ """Return an info dictionary containing:
+ - name (the name of the function : str)
+ - argnames (the names of the arguments : list)
+ - defarg (the values of the default arguments : list)
+ - fullsign (the full signature : str)
+ - shortsign (the short signature : str)
+ - arg0 ... argn (shortcuts for the names of the arguments)
+
+ >> def f(self, x=1, y=2, *args, **kw): pass
+
+ >> info = getinfo(f)
+
+ >> info["name"]
+ 'f'
+ >> info["argnames"]
+ ['self', 'x', 'y', 'args', 'kw']
+
+ >> info["defarg"]
+ (1, 2)
+
+ >> info["shortsign"]
+ 'self, x, y, *args, **kw'
+
+ >> info["fullsign"]
+ 'self, x=defarg[0], y=defarg[1], *args, **kw'
+
+ >> info["arg0"], info["arg1"], info["arg2"], info["arg3"], info["arg4"]
+ ('self', 'x', 'y', 'args', 'kw')
+ """
+ assert inspect.ismethod(func) or inspect.isfunction(func)
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+ argnames = list(regargs)
+ if varargs: argnames.append(varargs)
+ if varkwargs: argnames.append(varkwargs)
+ counter = itertools.count()
+ fullsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "=defarg[%i]" % counter.next())[1:-1]
+ shortsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "")[1:-1]
+ dic = dict(("arg%s" % n, name) for n, name in enumerate(argnames))
+ dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
+ fullsign = fullsign, defarg = func.func_defaults or ())
+ return dic
+
+ def _contains_reserved_names(dic): # helper
+ return "_call_" in dic or "_func_" in dic
+
+ def _decorate(func, caller):
+ """Takes a function and a caller and returns the function
+ decorated with that caller. The decorated function is obtained
+ by evaluating a lambda function with the correct signature.
+ """
+ infodict = getinfo(func)
+ assert not _contains_reserved_names(infodict["argnames"]), \
+ "You cannot use _call_ or _func_ as argument names!"
+ execdict=dict(_func_=func, _call_=caller, defarg=func.func_defaults or ())
+ if func.__name__ == "<lambda>":
+ lambda_src = "lambda %(fullsign)s: _call_(_func_, %(shortsign)s)" \
+ % infodict
+ dec_func = eval(lambda_src, execdict)
+ else:
+ func_src = """def %(name)s(%(fullsign)s):
+ return _call_(_func_, %(shortsign)s)""" % infodict
+ exec func_src in execdict
+ dec_func = execdict[func.__name__]
+ dec_func.__doc__ = func.__doc__
+ dec_func.__dict__ = func.__dict__
+ return dec_func
+
+ class decorator(object):
+ """General purpose decorator factory: takes a caller function as
+ input and returns a decorator. A caller function is any function like this::
+
+ def caller(func, *args, **kw):
+ # do something
+ return func(*args, **kw)
+
+ Here is an example of usage:
+
+ >> @decorator
+ .. def chatty(f, *args, **kw):
+ .. print "Calling %r" % f.__name__
+ .. return f(*args, **kw)
+
+ >> @chatty
+ .. def f(): pass
+ ..
+ >> f()
+ Calling 'f'
+ """
+ def __init__(self, caller):
+ self.caller = caller
+ def __call__(self, func):
+ return _decorate(func, self.caller)
+
+
+ #</decorators.py>
+
+The possibilities of this pattern are endless.
+
+A deferred decorator
+-----------------------
+
+You want to execute a procedure only after a certain time delay (for instance
+for use within an asyncronous Web framework)::
+
+
+ #<deferred.py>
+ "Deferring the execution of a procedure (function returning None)"
+
+ import threading
+ from decorators import decorator
+
+ def deferred(nsec):
+ def call_later(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(call_later)
+
+ @deferred(2)
+ def hello():
+ print "hello"
+
+ if __name__ == "__main__":
+ hello()
+ print "Calling hello() ..."
+
+
+ #</deferred.py>
+
+ $ python deferred.py
+
+Show an example of an experimental decorator based web framework
+(doctester_frontend).
+
+Part II: metaclasses
+++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Metaclasses are there! Consider this example from a recent post on c.l.py::
+
+ #<BaseClass.py>
+
+ class BaseClass(object):
+ "Do something"
+
+ #</BaseClass.py>
+
+>>> import BaseClass # instead of 'from BaseClass import BaseClass'
+>>> class C(BaseClass): pass
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ module.__init__() takes at most 2 arguments (3 given)
+
+The reason for the error is that class ``C(BaseClass): pass`` is
+actually calling the ``type`` metaclass with three arguments::
+
+ C = type("C", (BaseClass,), {})
+
+``type.__new__`` tries to use ``type(BaseClass)`` as metaclass,
+but since BaseClass here is a module, and ``ModuleType`` is not
+a metaclass, it cannot work. The error message reflects a conflict with
+the signature of ModuleType which requires two parameters and not three.
+
+So even if you don't use them, you may want to know they exist.
+
+Rejuvenating old-style classes
+--------------------------------------------
+
+>>> class Old: pass
+>>> print type(Old)
+<type 'classobj'>
+
+>>> __metaclass__ = type # to rejuvenate class
+>>> class NotOld: pass
+...
+>>> print NotOld.__class__
+<type 'type'>
+
+A typical metaclass example: MetaTracer
+----------------------------------------
+
+::
+
+ #<metatracer.py>
+
+ import inspect
+ from decorators import decorator
+
+ @decorator
+ def traced(meth, *args, **kw):
+ cls = meth.__cls__
+ modname = meth.__module__ or cls.__module__
+ print "calling %s.%s.%s" % (modname, cls.__name__, meth.__name__)
+ return meth(*args, **kw)
+
+ class MetaTracer(type):
+ def __init__(cls, name, bases, dic):
+ super(MetaTracer, cls).__init__(name, bases, dic)
+ for k, v in dic.iteritems():
+ if inspect.isfunction(v):
+ v.__cls__ = cls # so we know in which class v was defined
+ setattr(cls, k, traced(v))
+
+ #</metatracer.py>
+
+Usage: exploring classes in the standard library
+
+::
+
+ #<dictmixin.py>
+
+ from metatracer import MetaTracer
+ from UserDict import DictMixin
+
+ class TracedDM(DictMixin, object):
+ __metaclass__ = MetaTracer
+ def __getitem__(self, item):
+ return item
+ def keys(self):
+ return [1,2,3]
+
+ #</dictmixin.py>
+
+>>> from dictmixin import TracedDM
+>>> print TracedDM()
+calling dictmixin.TracedDM.keys
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+{1: 1, 2: 2, 3: 3}
+
+Real life example: check overriding
+-------------------------------------
+::
+
+ #<check_overriding.py>
+
+ class Base(object):
+ a = 0
+
+ class CheckOverriding(type):
+ "Prints a message if we are overriding a name."
+ def __new__(mcl, name, bases, dic):
+ for name, val in dic.iteritems():
+ if name.startswith("__") and name.endswith("__"):
+ continue # ignore special names
+ a_base_has_name = True in (hasattr(base, name) for base in bases)
+ if a_base_has_name:
+ print "AlreadyDefinedNameWarning: " + name
+ return super(CheckOverriding, mcl).__new__(mcl, name, bases, dic)
+
+ class MyClass(Base):
+ __metaclass__ = CheckOverriding
+ a = 1
+
+ class ChildClass(MyClass):
+ a = 2
+
+#</check_overriding.py>
+
+>>> import check_overriding
+AlreadyDefinedNameWarning: a
+AlreadyDefinedNameWarning: a
+
+LogFile
+---------------------------------------------
+::
+
+ #<logfile.py>
+
+ import subprocess
+
+ def memoize(func):
+ memoize_dic = {}
+ def wrapped_func(*args):
+ if args in memoize_dic:
+ return memoize_dic[args]
+ else:
+ result = func(*args)
+ memoize_dic[args] = result
+ return result
+ wrapped_func.__name__ = func.__name__
+ wrapped_func.__doc__ = func.__doc__
+ wrapped_func.__dict__ = func.__dict__
+ return wrapped_func
+
+ class Memoize(type): # Singleton is a special case of Memoize
+ @memoize
+ def __call__(cls, *args):
+ return super(Memoize, cls).__call__(*args)
+
+ class LogFile(file):
+ """Open a file for append."""
+ __metaclass__ = Memoize
+ def __init__(self, name = "/tmp/err.log"):
+ self.viewer_cmd = 'xterm -e less'.split()
+ super(LogFile, self).__init__(name, "a")
+
+ def display(self, *ls):
+ "Use 'less' to display the log file in a separate xterm."
+ print >> self, "\n".join(map(str, ls)); self.flush()
+ subprocess.call(self.viewer_cmd + [self.name])
+
+ def reset(self):
+ "Erase the log file."
+ print >> file(self.name, "w")
+
+ if __name__ == "__main__": # test
+ print >> LogFile(), "hello"
+ print >> LogFile(), "world"
+ LogFile().display()
+
+ #</logfile.py>
+
+ $ python logfile.py
+
+Cooperative hierarchies
+------------------------------
+
+::
+
+ #<cooperative_init.py>
+
+ """Given a hierarchy, makes __init__ cooperative.
+ The only change needed is to add a line
+
+ __metaclass__ = CooperativeInit
+
+ to the base class of your hierarchy."""
+
+ from decorators import decorator
+
+ class CooperativeInit(type):
+ def __init__(cls, name, bases, dic):
+
+ @decorator
+ def make_cooperative(__init__, self, *args, **kw):
+ super(cls, self).__init__(*args, **kw)
+ __init__(self, *args, **kw)
+
+ __init__ = dic.get("__init__")
+ if __init__:
+ cls.__init__ = make_cooperative(__init__)
+
+ class Base:
+ __metaclass__ = CooperativeInit
+ def __init__(self):
+ print "B.__init__"
+
+ class C1(Base):
+ def __init__(self):
+ print "C1.__init__"
+
+ class C2(Base):
+ def __init__(self):
+ print "C2.__init__"
+
+ class D(C1, C2):
+ def __init__(self):
+ print "D.__init__"
+
+ #</cooperative_init.py>
+
+>>> from cooperative_init import D
+>>> d = D()
+B.__init__
+C2.__init__
+C1.__init__
+D.__init__
+
+Metaclass-enhanced modules
+----------------------------------------------------------------
+
+::
+
+ #<import_with_metaclass.py>
+ """
+ ``import_with_metaclass(metaclass, modulepath)`` generates
+ a new module from and old module, by enhancing all of its classes.
+ This is not perfect, but it should give you a start."""
+
+ import os, sys, inspect, types
+
+ def import_with_metaclass(metaclass, modulepath):
+ modname = os.path.basename(modulepath)[:-3] # simplistic
+ mod = types.ModuleType(modname)
+ locs = dict(
+ __module__ = modname,
+ __metaclass__ = metaclass,
+ object = metaclass("object", (), {}))
+ execfile(modulepath, locs)
+ for k, v in locs.iteritems():
+ if inspect.isclass(v): # otherwise it would be "__builtin__"
+ v.__module__ = "__dynamic__"
+ setattr(mod, k, v)
+ return mod
+
+#</import_with_metaclass.py>
+
+>>> from import_with_metaclass import import_with_metaclass
+>>> from metatracer import MetaTracer
+>>> traced_optparse = import_with_metaclass(MetaTracer,
+... "/usr/lib/python2.4/optparse.py")
+>>> op = traced_optparse.OptionParser()
+calling __dynamic__.OptionParser.__init__
+calling __dynamic__.OptionContainer.__init__
+calling __dynamic__.OptionParser._create_option_list
+calling __dynamic__.OptionContainer._create_option_mappings
+calling __dynamic__.OptionContainer.set_conflict_handler
+calling __dynamic__.OptionContainer.set_description
+calling __dynamic__.OptionParser.set_usage
+calling __dynamic__.IndentedHelpFormatter.__init__
+calling __dynamic__.HelpFormatter.__init__
+calling __dynamic__.HelpFormatter.set_parser
+calling __dynamic__.OptionParser._populate_option_list
+calling __dynamic__.OptionParser._add_help_option
+calling __dynamic__.OptionContainer.add_option
+calling __dynamic__.Option.__init__
+calling __dynamic__.Option._check_opt_strings
+calling __dynamic__.Option._set_opt_strings
+calling __dynamic__.Option._set_attrs
+calling __dynamic__.OptionContainer._check_conflict
+calling __dynamic__.OptionParser._init_parsing_state
+
+traced_optparse is a dynamically generated module not leaving in the
+file system.
+
+Magic properties
+--------------------
+::
+
+ #<magicprop.py>
+
+ class MagicProperties(type):
+ def __init__(cls, name, bases, dic):
+ prop_names = set(name[3:] for name in dic
+ if name.startswith("get")
+ or name.startswith("set"))
+ for name in prop_names:
+ getter = getattr(cls, "get" + name, None)
+ setter = getattr(cls, "set" + name, None)
+ setattr(cls, name, property(getter, setter))
+
+ class Base(object):
+ __metaclass__ = MagicProperties
+ def getx(self):
+ return self._x
+ def setx(self, value):
+ self._x = value
+
+ class Child(Base):
+ def getx(self):
+ print "getting x"
+ return super(Child, self).getx()
+ def setx(self, value):
+ print "setting x"
+ super(Child, self).setx(value)
+
+ #</magicprop.py>
+
+>>> from magicprop import Child
+>>> c = Child()
+>>> c.x = 1
+setting x
+>>> print c.x
+getting x
+1
+
+Hack: evil properties
+------------------------------------
+
+::
+
+ #<evilprop.py>
+
+ def convert2property(name, bases, d):
+ return property(d.get('get'), d.get('set'),
+ d.get('del'),d.get('__doc__'))
+
+ class C(object):
+ class x:
+ """An evil test property"""
+ __metaclass__ = convert2property
+ def get(self):
+ print 'Getting %s' % self._x
+ return self._x
+ def set(self, value):
+ self._x = value
+ print 'Setting to', value
+
+ #</evilprop.py>
+
+>>> from evilprop import C
+>>> c = C()
+>>> c.x = 5
+Setting to 5
+>>> c.x
+Getting 5
+5
+>>> print C.x.__doc__
+An evil test property
+
+Why I suggest *not* to use metaclasses in production code
+---------------------------------------------------------
+
+ + there are very few good use case for metaclasses in production code
+ (i.e. 99% of time you don't need them)
+
+ + they put a cognitive burden on the developer;
+
+ + a design without metaclasses is less magic and likely more robust;
+
+ + a design with metaclasses makes it difficult to use other metaclasses
+ for debugging.
+
+As far as I know, string.Template is the only metaclass-enhanced class
+in the standard library; the metaclass is used to give the possibility to
+change the defaults::
+
+ delimiter = '$'
+ idpattern = r'[_a-z][_a-z0-9]*'
+
+in subclasses of Template.
+
+>>> from string import Template
+>>> from metatracer import MetaTracer
+>>> class TracedTemplate(Template):
+... __metaclass__ = MetaTracer
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+Solution: use a consistent metaclass
+
+>>> class GoodMeta(MetaTracer, type(Template)): pass
+...
+>>> class TracedTemplate(Template):
+... __metaclass__ = GoodMeta
+
+
+Is there an automatic way of solving the conflict?
+---------------------------------------------------------------------
+
+Yes, but you really need to be a metaclass wizard.
+
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+
+>>> from noconflict import classmaker
+>>> class TracedTemplate(Template):
+... __metaclass__ = classmaker((MetaTracer,))
+>>> print type(TracedTemplate)
+<class 'noconflict._MetaTracer_TemplateMetaclass'>
+
+::
+
+ #<noconflict.py>
+
+ import inspect, types, __builtin__
+ from skip_redundant import skip_redundant
+
+ memoized_metaclasses_map = {}
+
+ # utility function
+ def remove_redundant(metaclasses):
+ skipset = set([types.ClassType])
+ for meta in metaclasses: # determines the metaclasses to be skipped
+ skipset.update(inspect.getmro(meta)[1:])
+ return tuple(skip_redundant(metaclasses, skipset))
+
+ ##################################################################
+ ## now the core of the module: two mutually recursive functions ##
+ ##################################################################
+
+ def get_noconflict_metaclass(bases, left_metas, right_metas):
+ """Not intended to be used outside of this module, unless you know
+ what you are doing."""
+ # make tuple of needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ needed_metas = remove_redundant(metas)
+
+ # return existing confict-solving meta, if any
+ if needed_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[needed_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ elif not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ # check for recursion, can happen i.e. for Zope ExtensionClasses
+ elif needed_metas == bases:
+ raise TypeError("Incompatible root metatypes", needed_metas)
+ else: # gotta work ...
+ metaname = '_' + ''.join([m.__name__ for m in needed_metas])
+ meta = classmaker()(metaname, needed_metas, {})
+ memoized_metaclasses_map[needed_metas] = meta
+ return meta
+
+ def classmaker(left_metas=(), right_metas=()):
+ def make_class(name, bases, adict):
+ metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
+ return metaclass(name, bases, adict)
+ return make_class
+
+ #################################################################
+ ## and now a conflict-safe replacement for 'type' ##
+ #################################################################
+
+ __type__=__builtin__.type # the aboriginal 'type'
+ # left available in case you decide to rebind __builtin__.type
+
+ class safetype(__type__):
+ # this is REALLY DEEP MAGIC
+ """Overrides the ``__new__`` method of the ``type`` metaclass, making the
+ generation of classes conflict-proof."""
+ def __new__(mcl, *args):
+ nargs = len(args)
+ if nargs == 1: # works as __builtin__.type
+ return __type__(args[0])
+ elif nargs == 3: # creates the class using the appropriate metaclass
+ n, b, d = args # name, bases and dictionary
+ meta = get_noconflict_metaclass(b, (mcl,), ())
+ if meta is mcl: # meta is trivial, dispatch to the default __new__
+ return super(safetype, mcl).__new__(mcl, n, b, d)
+ else: # non-trivial metaclass, dispatch to the right __new__
+ # (it will take a second round) # print mcl, meta
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
+
+ #</noconflict.py>
diff --git a/pypers/oxford/all.tex b/pypers/oxford/all.tex
new file mode 100755
index 0000000..c6be548
--- /dev/null
+++ b/pypers/oxford/all.tex
@@ -0,0 +1,2395 @@
+\documentclass[10pt,a4paper,english]{article}
+\usepackage{babel}
+\usepackage{ae}
+\usepackage{aeguill}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage{ifthen}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[DIV12]{typearea}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newlength{\locallinewidth}
+\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}}
+\newlength{\lineblockindentation}
+\setlength{\lineblockindentation}{2.5em}
+\newenvironment{lineblock}[1]
+{\begin{list}{}
+ {\setlength{\partopsep}{\parskip}
+ \addtolength{\partopsep}{\baselineskip}
+ \topsep0pt\itemsep0.15\baselineskip\parsep0pt
+ \leftmargin#1}
+ \raggedright}
+{\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 ~}}
+\newcommand{\titlereference}[1]{\textsl{#1}}
+% end of "some commands"
+\input{style.tex}
+\title{Lectures on Advanced Python Programming}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Lectures on Advanced Python Programming}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+
+\includegraphics{accu2005.ps}
+\begin{quote}
+\begin{description}
+\item [Author:]
+Michele Simionato
+
+
+\item [Given:]
+19 April 2005
+
+
+\item [Revised:]
+7 September 2005
+
+
+\end{description}
+\end{quote}
+\hypertarget{contents}{}
+\pdfbookmark[0]{Contents}{contents}
+\subsubsection*{~\hfill Contents\hfill ~}
+\begin{list}{}{}
+\item {} \href{\#lecture-1-loops-i-e-iterators-generators}{Lecture 1: Loops (i.e. iterators {\&} generators)}
+\begin{list}{}{}
+\item {} \href{\#part-i-iterators}{Part I: iterators}
+\begin{list}{}{}
+\item {} \href{\#iterators-are-everywhere}{Iterators are everywhere}
+
+\item {} \href{\#iterables-and-iterators}{Iterables and iterators}
+
+\item {} \href{\#simpler-way-to-get-an-iterator}{Simpler way to get an iterator}
+
+\item {} \href{\#sentinel-syntax-iter-callable-sentinel}{Sentinel syntax iter(callable, sentinel)}
+
+\item {} \href{\#second-simpler-way-to-get-an-iterator-generator-expressions}{Second simpler way to get an iterator: generator-expressions}
+
+\item {} \href{\#iteration-caveats}{Iteration caveats}
+
+\end{list}
+
+\item {} \href{\#part-ii-generators}{Part II: generators}
+\begin{list}{}{}
+\item {} \href{\#a-simple-recipe-skip-redundant}{A simple recipe: skip redundant}
+
+\item {} \href{\#another-real-life-example-working-with-nested-structures}{Another real life example: working with nested structures}
+
+\item {} \href{\#another-typical-use-case-for-generators-parsers}{Another typical use case for generators: parsers}
+
+\item {} \href{\#other-kinds-of-iterables}{Other kinds of iterables}
+
+\item {} \href{\#the-itertools-module}{The itertools module}
+
+\item {} \href{\#anytrue}{anyTrue}
+
+\item {} \href{\#chop}{chop}
+
+\item {} \href{\#tee}{tee}
+
+\item {} \href{\#grouping-and-sorting}{Grouping and sorting}
+
+\end{list}
+
+\end{list}
+
+\item {} \href{\#lecture-2-objects-delegation-inheritance}{Lecture 2: Objects (delegation {\&} inheritance)}
+\begin{list}{}{}
+\item {} \href{\#part-i-delegation}{Part I: delegation}
+\begin{list}{}{}
+\item {} \href{\#accessing-simple-attributes}{Accessing simple attributes}
+
+\item {} \href{\#accessing-methods}{Accessing methods}
+
+\item {} \href{\#converting-functions-into-methods}{Converting functions into methods}
+
+\item {} \href{\#hack-a-very-slick-adder}{Hack: a very slick adder}
+
+\item {} \href{\#descriptor-protocol}{Descriptor Protocol}
+
+\item {} \href{\#multilingual-attribute}{Multilingual attribute}
+
+\item {} \href{\#another-use-case-for-properties-storing-users}{Another use case for properties: storing users}
+
+\item {} \href{\#low-level-delegation-via-getattribute}{Low-level delegation via {\_}{\_}getattribute{\_}{\_}}
+
+\item {} \href{\#traditional-delegation-via-getattr}{Traditional delegation via {\_}{\_}getattr{\_}{\_}}
+
+\item {} \href{\#keyword-dictionaries-with-getattr-setattr}{Keyword dictionaries with {\_}{\_}getattr{\_}{\_}/{\_}{\_}setattr{\_}{\_}}
+
+\item {} \href{\#delegation-to-special-methods-caveat}{Delegation to special methods caveat}
+
+\end{list}
+
+\item {} \href{\#part-ii-inheritance}{Part II: Inheritance}
+\begin{list}{}{}
+\item {} \href{\#why-you-need-to-know-about-mi-even-if-you-do-not-use-it}{Why you need to know about MI even if you do not use it}
+
+\item {} \href{\#a-few-details-about-super-not-the-whole-truth}{A few details about \texttt{super} (not the whole truth)}
+
+\item {} \href{\#subclassing-built-in-types-new-vs-init}{Subclassing built-in types; {\_}{\_}new{\_}{\_} vs. {\_}{\_}init{\_}{\_}}
+
+\item {} \href{\#be-careful-when-using-new-with-mutable-types}{Be careful when using {\_}{\_}new{\_}{\_} with mutable types}
+
+\end{list}
+
+\end{list}
+
+\item {} \href{\#lecture-3-magic-i-e-decorators-and-metaclasses}{Lecture 3: Magic (i.e. decorators and metaclasses)}
+\begin{list}{}{}
+\item {} \href{\#part-i-decorators}{Part I: decorators}
+\begin{list}{}{}
+\item {} \href{\#a-typical-decorator-traced}{A typical decorator: traced}
+
+\item {} \href{\#a-decorator-factory-timed}{A decorator factory: Timed}
+
+\item {} \href{\#a-powerful-decorator-pattern}{A powerful decorator pattern}
+
+\item {} \href{\#a-deferred-decorator}{A deferred decorator}
+
+\end{list}
+
+\item {} \href{\#part-ii-metaclasses}{Part II: metaclasses}
+\begin{list}{}{}
+\item {} \href{\#rejuvenating-old-style-classes}{Rejuvenating old-style classes}
+
+\item {} \href{\#a-typical-metaclass-example-metatracer}{A typical metaclass example: MetaTracer}
+
+\item {} \href{\#real-life-example-check-overriding}{Real life example: check overriding}
+
+\item {} \href{\#logfile}{LogFile}
+
+\item {} \href{\#cooperative-hierarchies}{Cooperative hierarchies}
+
+\item {} \href{\#metaclass-enhanced-modules}{Metaclass-enhanced modules}
+
+\item {} \href{\#magic-properties}{Magic properties}
+
+\item {} \href{\#hack-evil-properties}{Hack: evil properties}
+
+\item {} \href{\#why-i-suggest-not-to-use-metaclasses-in-production-code}{Why I suggest \emph{not} to use metaclasses in production code}
+
+\item {} \href{\#is-there-an-automatic-way-of-solving-the-conflict}{Is there an automatic way of solving the conflict?}
+
+\end{list}
+
+\end{list}
+
+\end{list}
+
+
+
+%___________________________________________________________________________
+
+\hypertarget{lecture-1-loops-i-e-iterators-generators}{}
+\pdfbookmark[0]{Lecture 1: Loops (i.e. iterators {\&} generators)}{lecture-1-loops-i-e-iterators-generators}
+\section*{Lecture 1: Loops (i.e. iterators {\&} generators)}
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-i-iterators}{}
+\pdfbookmark[1]{Part I: iterators}{part-i-iterators}
+\subsection*{Part I: iterators}
+
+
+%___________________________________________________________________________
+
+\hypertarget{iterators-are-everywhere}{}
+\pdfbookmark[2]{Iterators are everywhere}{iterators-are-everywhere}
+\subsubsection*{Iterators are everywhere}
+\begin{verbatim}>>> for i in 1, 2, 3:
+... print i
+1
+2
+3\end{verbatim}
+
+The 'for' loop is using \emph{iterators} internally:
+\begin{quote}{\ttfamily \raggedright \noindent
+it~=~iter((1,2,3))~\\
+while~True:~\\
+~~~~try:~\\
+~~~~~~~~print~it.next()~\\
+~~~~except~StopIteration:~\\
+~~~~~~~~break
+}\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{iterables-and-iterators}{}
+\pdfbookmark[2]{Iterables and iterators}{iterables-and-iterators}
+\subsubsection*{Iterables and iterators}
+
+\emph{Iterable} = anything you can loop over = any sequence + any object with an {\_}{\_}iter{\_}{\_} method;
+
+Not every sequence has an {\_}{\_}iter{\_}{\_} method:
+\begin{verbatim}>>> "hello".__iter__()
+Traceback (most recent call last):
+ ...
+AttributeError: 'str' object has no attribute '__iter__'\end{verbatim}
+
+\emph{Iterator} = any object with a .next method and an {\_}{\_}iter{\_}{\_} method returning self
+
+
+%___________________________________________________________________________
+
+\hypertarget{simpler-way-to-get-an-iterator}{}
+\pdfbookmark[2]{Simpler way to get an iterator}{simpler-way-to-get-an-iterator}
+\subsubsection*{Simpler way to get an iterator}
+\begin{verbatim}>>> it = iter("hello")
+>>> it.next()
+'h'
+>>> it.next()
+'e'
+>>> it.next()
+'l'
+>>> it.next()
+'l'
+>>> it.next()
+'o'
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{sentinel-syntax-iter-callable-sentinel}{}
+\pdfbookmark[2]{Sentinel syntax iter(callable, sentinel)}{sentinel-syntax-iter-callable-sentinel}
+\subsubsection*{Sentinel syntax iter(callable, sentinel)}
+
+Example:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}~echo~-e~"value1{\textbackslash}nvalue2{\textbackslash}nEND{\textbackslash}n"~>~data.txt~\\
+{\$}~python~-c~"print~list(iter(file('data.txt').readline,~'END{\textbackslash}n'))"~\\
+{[}'value1{\textbackslash}n',~'value2{\textbackslash}n']
+}\end{quote}
+
+Beware of infinite iterators:
+\begin{verbatim}>>> repeat = iter(lambda : "some value", "")
+>>> repeat.next()
+'some value'\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{second-simpler-way-to-get-an-iterator-generator-expressions}{}
+\pdfbookmark[2]{Second simpler way to get an iterator: generator-expressions}{second-simpler-way-to-get-an-iterator-generator-expressions}
+\subsubsection*{Second simpler way to get an iterator: generator-expressions}
+\begin{verbatim}>>> squares = (i*i for i in range(1,11))
+>>> list(squares)
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\end{verbatim}
+
+Excessive parenthesis can be skipped, so use
+\begin{verbatim}>>> dict((i, i*i) for i in range(1,11))
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}\end{verbatim}
+
+instead of
+\begin{verbatim}>>> dict([(i, i*i) for i in range(1,11)])
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}\end{verbatim}
+
+(as usual, the most elegant version is the most efficient).
+
+
+%___________________________________________________________________________
+
+\hypertarget{iteration-caveats}{}
+\pdfbookmark[2]{Iteration caveats}{iteration-caveats}
+\subsubsection*{Iteration caveats}
+\begin{verbatim}>>> ls = [i for i in (1,2,3)]
+>>> i
+3\end{verbatim}
+\begin{verbatim}>>> it = (j for j in (1,2,3))
+>>> j
+Traceback (most recent call last):
+ ...
+NameError: name 'j' is not defined\end{verbatim}
+
+A subtler example:
+\begin{verbatim}>>> ls = [lambda :i for i in (1,2,3)]
+>>> ls[0]()
+3\end{verbatim}
+
+instead
+\begin{verbatim}>>> it = (lambda :i for i in (1,2,3))
+>>> it.next()()
+1
+>>> it.next()()
+2
+>>> it.next()()
+3\end{verbatim}
+
+\emph{seems} to be working but it is not really the case:
+\begin{verbatim}>>> it = (lambda :i for i in (1,2,3))
+>>> f1 = it.next()
+>>> f2 = it.next()
+>>> f3 = it.next()
+>>> f1()
+3\end{verbatim}
+
+The reason is that Python does LATE binding \emph{always}. The solution is ugly:
+\begin{verbatim}>>> it = list(lambda i=i:i for i in (1,2,3))
+>>> it[0]()
+1
+>>> it[1]()
+2
+>>> it[2]()
+3\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-ii-generators}{}
+\pdfbookmark[1]{Part II: generators}{part-ii-generators}
+\subsection*{Part II: generators}
+
+Trivial example:
+\begin{verbatim}>>> def gen123(): # "function" which returns an iterator over the values 1, 2, 3
+... yield 1
+... yield 2
+... yield 3
+...
+>>> it = gen123()
+>>> it.next()
+1
+>>> it.next()
+2
+>>> it.next()
+3
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration\end{verbatim}
+
+Real life example: using generators to generate HTML tables
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<htmltable.py>~\\
+~\\
+def~HTMLTablegen(table):~\\
+~~~~yield~"<table>"~\\
+~~~~for~row~in~table:~\\
+~~~~~~~yield~"<tr>"~\\
+~~~~~~~for~col~in~row:~\\
+~~~~~~~~~~~yield~"<td>{\%}s</td>"~{\%}~col~\\
+~~~~~~~yield~"</tr>"~\\
+~~~~yield~"</table>"~\\
+~\\
+def~test():~\\
+~~~~return~"{\textbackslash}n".join(HTMLTablegen({[}{[}"Row",~"City"],~~\\
+~~~~~~~~~~~~~~~~~~~~~~~{[}1,'London'],~{[}2,~'Oxford']]))~\\
+~\\
+if~{\_}{\_}name{\_}{\_}~==~"{\_}{\_}main{\_}{\_}":~{\#}~example~\\
+~~~~print~test()~\\
+~\\
+{\#}</htmltable.py>
+}\end{quote}
+\begin{verbatim}>>> from htmltable import test
+>>> print test()
+<table>
+<tr>
+<td>Row</td>
+<td>City</td>
+</tr>
+<tr>
+<td>1</td>
+<td>London</td>
+</tr>
+<tr>
+<td>2</td>
+<td>Oxford</td>
+</tr>
+</table>\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-simple-recipe-skip-redundant}{}
+\pdfbookmark[2]{A simple recipe: skip redundant}{a-simple-recipe-skip-redundant}
+\subsubsection*{A simple recipe: skip redundant}
+
+How to remove duplicates by keeping the order:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<skip{\_}redundant.py>~\\
+~\\
+def~skip{\_}redundant(iterable,~skipset=None):~\\
+~~~"Redundant~items~are~repeated~items~or~items~in~the~original~skipset."~\\
+~~~if~skipset~is~None:~skipset~=~set()~\\
+~~~for~item~in~iterable:~\\
+~~~~~~~if~item~not~in~skipset:~\\
+~~~~~~~~~~~skipset.add(item)~\\
+~~~~~~~~~~~yield~item~\\
+~~~~~~~~~~\\
+{\#}</skip{\_}redundant.py>
+}\end{quote}
+\begin{verbatim}>>> from skip_redundant import skip_redundant
+>>> print list(skip_redundant("<hello, world>", skipset=set("<>")))
+['h', 'e', 'l', 'o', ',', ' ', 'w', 'r', 'd']\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{another-real-life-example-working-with-nested-structures}{}
+\pdfbookmark[2]{Another real life example: working with nested structures}{another-real-life-example-working-with-nested-structures}
+\subsubsection*{Another real life example: working with nested structures}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<walk.py>~\\
+~\\
+def~walk(iterable,~level=0):~\\
+~~~~for~obj~in~iterable:~\\
+~~~~~~~~if~not~hasattr(obj,~"{\_}{\_}iter{\_}{\_}"):~{\#}~atomic~object~\\
+~~~~~~~~~~~~yield~obj,~level~\\
+~~~~~~~~else:~{\#}~composed~object:~iterate~again~\\
+~~~~~~~~~~~~for~subobj,~lvl~in~walk(obj,~level~+~1):~\\
+~~~~~~~~~~~~~~~~yield~subobj,~lvl~\\
+~\\
+def~flatten(iterable):~\\
+~~~~return~(obj~for~obj,~level~in~walk(iterable))~\\
+~~~~~~~~\\
+def~pprint(iterable):~\\
+~~~~for~obj,~level~in~walk(iterable):~\\
+~~~~~~~~print~"~"*level,~obj~\\
+~~~~~~~~\\
+{\#}</walk.py>
+}\end{quote}
+\begin{verbatim}>>> from walk import flatten, pprint
+>>> nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+>>> pprint(nested_ls)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+>>> pprint(flatten(nested_ls))
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{another-typical-use-case-for-generators-parsers}{}
+\pdfbookmark[2]{Another typical use case for generators: parsers}{another-typical-use-case-for-generators-parsers}
+\subsubsection*{Another typical use case for generators: parsers}
+
+A very stripped down parser for nested expressions
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<sexpr2indent.py>~\\
+"{}"{}"A~simple~s-expression~formatter."{}"{}"~\\
+~\\
+import~re~\\
+~\\
+def~parse(sexpr):~\\
+~~~~position~=~0~\\
+~~~~nesting{\_}level~=~0~\\
+~~~~paren~=~re.compile(r"(?P<paren{\_}beg>{\textbackslash}()|(?P<paren{\_}end>{\textbackslash}))")~\\
+~~~~while~True:~\\
+~~~~~~~~match~=~paren.search(sexpr,~position)~\\
+~~~~~~~~if~match:~\\
+~~~~~~~~~~~~yield~nesting{\_}level,~sexpr{[}position:~match.start()]~\\
+~~~~~~~~~~~~if~match.lastgroup~==~"paren{\_}beg":~\\
+~~~~~~~~~~~~~~~~nesting{\_}level~+=~1~\\
+~~~~~~~~~~~~elif~match.lastgroup~==~"paren{\_}end":~\\
+~~~~~~~~~~~~~~~~nesting{\_}level~-=~1~\\
+~~~~~~~~~~~~position~=~match.end()~\\
+~~~~~~~~else:~\\
+~~~~~~~~~~~~break~\\
+~\\
+def~sexpr{\_}indent(sexpr):~\\
+~~~~for~nesting,~text~in~parse(sexpr.replace("{\textbackslash}n",~"{}")):~\\
+~~~~~~~~if~text.strip():~~print~"~"*nesting,~text~\\
+~\\
+{\#}</sexpr2indent.py>
+}\end{quote}
+\begin{verbatim}>>> from sexpr2indent import sexpr_indent
+>>> sexpr_indent("""\
+... (html (head (title Example)) (body (h1 s-expr formatter example)
+... (a (@ (href http://www.example.com)) A link)))""")
+... #doctest: +NORMALIZE_WHITESPACE
+ html
+ head
+ title Example
+ body
+ h1 s-expr formatter example
+ a
+ @
+ href http://www.example.com
+ A link\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{other-kinds-of-iterables}{}
+\pdfbookmark[2]{Other kinds of iterables}{other-kinds-of-iterables}
+\subsubsection*{Other kinds of iterables}
+
+The following class generates iterable which are not iterators:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<reiterable.py>~\\
+~\\
+class~ReIter(object):~\\
+~~~~"A~re-iterable~object."~\\
+~~~~def~{\_}{\_}iter{\_}{\_}(self):~\\
+~~~~~~~~yield~1~\\
+~~~~~~~~yield~2~\\
+~~~~~~~~yield~3~\\
+~\\
+{\#}</reiterable.py>
+}\end{quote}
+\begin{verbatim}>>> from reiterable import ReIter
+>>> rit = ReIter()
+>>> list(rit)
+[1, 2, 3]
+>>> list(rit) # it is reiterable!
+[1, 2, 3]\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-itertools-module}{}
+\pdfbookmark[2]{The itertools module}{the-itertools-module}
+\subsubsection*{The itertools module}
+\begin{quote}
+\begin{itemize}
+\item {}
+count({[}n]) -{}-{\textgreater} n, n+1, n+2, ...
+
+\item {}
+cycle(p) -{}-{\textgreater} p0, p1, ... plast, p0, p1, ...
+
+\item {}
+repeat(elem {[},n]) -{}-{\textgreater} elem, elem, elem, ... endlessly or up to n times
+
+\item {}
+izip(p, q, ...) -{}-{\textgreater} (p{[}0], q{[}0]), (p{[}1], q{[}1]), ...
+
+\item {}
+ifilter(pred, seq) -{}-{\textgreater} elements of seq where pred(elem) is True
+
+\item {}
+ifilterfalse(pred, seq) -{}-{\textgreater} elements of seq where pred(elem) is False
+
+\item {}
+islice(seq, {[}start,] stop {[}, step]) -{}-{\textgreater} elements from seq{[}start:stop:step]
+
+\item {}
+imap(fun, p, q, ...) -{}-{\textgreater} fun(p0, q0), fun(p1, q1), ...
+
+\item {}
+starmap(fun, seq) -{}-{\textgreater} fun(*seq{[}0]), fun(*seq{[}1]), ...
+
+\item {}
+tee(it, n=2) -{}-{\textgreater} (it1, it2 , ... itn) splits one iterator into n
+
+\item {}
+chain(p, q, ...) -{}-{\textgreater} p0, p1, ... plast, q0, q1, ...
+
+\item {}
+takewhile(pred, seq) -{}-{\textgreater} seq{[}0], seq{[}1], until pred fails
+
+\item {}
+dropwhile(pred, seq) -{}-{\textgreater} seq{[}n], seq{[}n+1], starting when pred fails
+
+\item {}
+groupby(iterable{[}, keyfunc]) -{}-{\textgreater} sub-iterators grouped by value of keyfunc(v)
+
+\end{itemize}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{anytrue}{}
+\pdfbookmark[2]{anyTrue}{anytrue}
+\subsubsection*{anyTrue}
+\begin{verbatim}>>> import itertools
+>>> def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+...
+>>> fname = "picture.ps"
+>>> anyTrue(fname.endswith, ".jpg .ps .ps".split())
+True\end{verbatim}
+
+AnyTrue does \emph{short-circuit}:
+\begin{verbatim}>>> def is3(i):
+... print "i=%s" % i
+... return i == 3\end{verbatim}
+\begin{verbatim}>>> anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{chop}{}
+\pdfbookmark[2]{chop}{chop}
+\subsubsection*{chop}
+
+You want to chop an iterable in batches of a given size:
+\begin{verbatim}>>> from chop import chop
+>>> list(chop([1, 2, 3, 4], 2))
+[[1, 2], [3, 4]]
+>>> list(chop([1, 2, 3, 4, 5, 6, 7],3))
+[[1, 2, 3], [4, 5, 6], [7]]\end{verbatim}
+
+Here is a possible implementation:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<chop.py>~\\
+~\\
+{\#}~see~also~http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279~\\
+~\\
+import~itertools~\\
+~\\
+def~chop(iterable,~batchsize):~\\
+~~~~it~=~iter(iterable)~\\
+~~~~while~True:~\\
+~~~~~~~~batch~=~list(itertools.islice(it,~batchsize))~\\
+~~~~~~~~if~batch:~yield~batch~\\
+~~~~~~~~else:~break~\\
+~\\
+{\#}</chop.py>
+}\end{quote}
+
+For people thinking Python is too readable, here is a one-liner:
+\begin{verbatim}>>> chop = lambda it, n : itertools.izip(*(iter(it),)*n)
+...
+>>> list(chop([1,2,3,4], 2))
+[(1, 2), (3, 4)]\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{tee}{}
+\pdfbookmark[2]{tee}{tee}
+\subsubsection*{tee}
+
+To make copies of iterables; typically used in parsers:
+\begin{verbatim}>>> from itertools import tee, chain, izip
+>>> chars, prevs = tee("abc")
+>>> prevs = chain([None], prevs)
+>>> for char, prev in izip(chars, prevs):
+... print char, prev
+...
+a None
+b a
+c b\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{grouping-and-sorting}{}
+\pdfbookmark[2]{Grouping and sorting}{grouping-and-sorting}
+\subsubsection*{Grouping and sorting}
+\begin{verbatim}>>> from itertools import groupby
+>>> from operator import itemgetter\end{verbatim}
+\begin{verbatim}>>> NAME, AGE = 0, 1
+>>> query_result = ("Smith", 34), ("Donaldson", 34), ("Lee", 22), ("Orr", 22)\end{verbatim}
+
+Grouping together people of the same age:
+\begin{verbatim}>>> for k, g in groupby(query_result, key=itemgetter(AGE)):
+... print k, list(g)
+...
+34 [('Smith', 34), ('Donaldson', 34)]
+22 [('Lee', 22), ('Orr', 22)]\end{verbatim}
+
+Sorting by name:
+\begin{verbatim}>>> for tup in sorted(query_result, key=itemgetter(NAME)):
+... print tup
+('Donaldson', 34)
+('Lee', 22)
+('Orr', 22)
+('Smith', 34)\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{lecture-2-objects-delegation-inheritance}{}
+\pdfbookmark[0]{Lecture 2: Objects (delegation {\&} inheritance)}{lecture-2-objects-delegation-inheritance}
+\section*{Lecture 2: Objects (delegation {\&} inheritance)}
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-i-delegation}{}
+\pdfbookmark[1]{Part I: delegation}{part-i-delegation}
+\subsection*{Part I: delegation}
+
+Understanding how attribute access works: internal delegation via \emph{descriptors}
+
+
+%___________________________________________________________________________
+
+\hypertarget{accessing-simple-attributes}{}
+\pdfbookmark[2]{Accessing simple attributes}{accessing-simple-attributes}
+\subsubsection*{Accessing simple attributes}
+\begin{verbatim}>>> class C(object):
+... a = 2
+... def __init__(self, x):
+... self.x = x
+...\end{verbatim}
+\begin{verbatim}>>> c = C(1)
+>>> c.x
+1
+>>> c.a
+2\end{verbatim}
+
+We are retrieving
+\begin{verbatim}>>> c.__dict__["x"]
+1\end{verbatim}
+
+If there is nothing in c.{\_}{\_}dict{\_}{\_}, Python looks at C.{\_}{\_}dict{\_}{\_}:
+\begin{verbatim}>>> print c.__dict__.get("a")
+None\end{verbatim}
+\begin{verbatim}>>> C.__dict__["a"]
+2\end{verbatim}
+
+If there is nothing in C.{\_}{\_}dict{\_}{\_}, Python looks at the superclasses according
+to the MRO (see part II).
+
+
+%___________________________________________________________________________
+
+\hypertarget{accessing-methods}{}
+\pdfbookmark[2]{Accessing methods}{accessing-methods}
+\subsubsection*{Accessing methods}
+\begin{verbatim}>>> c.__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>\end{verbatim}
+
+since {\_}{\_}init{\_}{\_} is not in c.{\_}{\_}dict{\_}{\_} Python looks in the class dictionary
+and finds
+\begin{verbatim}>>> C.__dict__["__init__"] #doctest: +ELLIPSIS
+<function __init__ at 0x...>\end{verbatim}
+
+Then it magically converts the function into a method bound to the instance
+``c''.
+
+NOTE: this mechanism works for new-style classes only.
+
+The old-style mechanism is less consistent and the attribute lookup of special
+methods is special: (*)
+\begin{verbatim}>>> class C(object): pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c #doctest: +ELLIPSIS
+<__main__.C object at ...>\end{verbatim}
+
+whereas for old-style classes
+\begin{verbatim}>>> class C: pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c
+hello!\end{verbatim}
+
+the special method is looked for in the instance dictionary too.
+
+(*) modulo a very subtle difference for {\_}{\_}getattr{\_}{\_}-delegated special methods,
+see later.
+
+
+%___________________________________________________________________________
+
+\hypertarget{converting-functions-into-methods}{}
+\pdfbookmark[2]{Converting functions into methods}{converting-functions-into-methods}
+\subsubsection*{Converting functions into methods}
+
+It is possible to convert a function into a bound or unbound method
+by invoking the \texttt{{\_}{\_}get{\_}{\_}} special method:
+\begin{verbatim}>>> def f(x): pass
+>>> f.__get__ #doctest: +ELLIPSIS
+<method-wrapper object at 0x...>\end{verbatim}
+\begin{verbatim}>>> class C(object): pass
+...\end{verbatim}
+\begin{verbatim}>>> def f(self): pass
+...
+>>> f.__get__(C(), C) #doctest: +ELLIPSIS
+<bound method C.f of <__main__.C object at 0x...>>\end{verbatim}
+\begin{verbatim}>>> f.__get__(None, C)
+<unbound method C.f>\end{verbatim}
+
+Functions are the simplest example of \emph{descriptors}.
+
+Access to methods works since internally Python transforms
+\begin{quote}
+
+\texttt{c.{\_}{\_}init{\_}{\_} -> type(c).{\_}{\_}dict{\_}{\_}{[}'{\_}{\_}init{\_}{\_}'].{\_}{\_}get{\_}{\_}(c, type(c))}
+\end{quote}
+
+Note: not \emph{all} functions are descriptors:
+\begin{verbatim}>>> from operator import add
+>>> add.__get__
+Traceback (most recent call last):
+ ...
+AttributeError: 'builtin_function_or_method' object has no attribute '__get__'\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{hack-a-very-slick-adder}{}
+\pdfbookmark[2]{Hack: a very slick adder}{hack-a-very-slick-adder}
+\subsubsection*{Hack: a very slick adder}
+
+The descriptor protocol can be (ab)used as a way to avoid the late binding
+issue in for loops:
+\begin{verbatim}>>> def add(x,y):
+... return x + y
+>>> closures = [add.__get__(i) for i in range(10)]
+>>> closures[5](1000)
+1005\end{verbatim}
+
+Notice: operator.add will not work.
+
+
+%___________________________________________________________________________
+
+\hypertarget{descriptor-protocol}{}
+\pdfbookmark[2]{Descriptor Protocol}{descriptor-protocol}
+\subsubsection*{Descriptor Protocol}
+
+Everything at \href{http://users.rcn.com/python/download/Descriptor.htm}{http://users.rcn.com/python/download/Descriptor.htm}
+
+Formally:
+\begin{quote}{\ttfamily \raggedright \noindent
+descr.{\_}{\_}get{\_}{\_}(self,~obj,~type=None)~-{}->~value~\\
+descr.{\_}{\_}set{\_}{\_}(self,~obj,~value)~-{}->~None~\\
+descr.{\_}{\_}delete{\_}{\_}(self,~obj)~-{}->~None
+}\end{quote}
+
+Examples of custom descriptors:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<descriptor.py>~\\
+~\\
+~\\
+class~AttributeDescriptor(object):~\\
+~~~def~{\_}{\_}get{\_}{\_}(self,~obj,~cls=None):~\\
+~~~~~~~if~obj~is~None~and~cls~is~None:~\\
+~~~~~~~~~~~raise~TypeError("{\_}{\_}get{\_}{\_}(None,~None)~is~invalid")~\\
+~~~~~~~elif~obj~is~None:~\\
+~~~~~~~~~~~return~self.get{\_}from{\_}class(cls)~\\
+~~~~~~~else:~\\
+~~~~~~~~~~~return~self.get{\_}from{\_}obj(obj)~\\
+~~~def~get{\_}from{\_}class(self,~cls):~\\
+~~~~~~~print~"Getting~{\%}s~from~{\%}s"~{\%}~(self,~cls)~\\
+~~~def~get{\_}from{\_}obj(self,~obj):~\\
+~~~~~~~print~"Getting~{\%}s~from~{\%}s"~{\%}~(self,~obj)~\\
+~\\
+~\\
+class~Staticmethod(AttributeDescriptor):~\\
+~~~def~{\_}{\_}init{\_}{\_}(self,~func):~\\
+~~~~~~~self.func~=~func~\\
+~~~def~get{\_}from{\_}class(self,~cls):~\\
+~~~~~~~return~self.func~\\
+~~~get{\_}from{\_}obj~=~get{\_}from{\_}class~\\
+~\\
+~\\
+class~Classmethod(AttributeDescriptor):~\\
+~~~def~{\_}{\_}init{\_}{\_}(self,~func):~\\
+~~~~~~~self.func~=~func~\\
+~~~def~get{\_}from{\_}class(self,~cls):~\\
+~~~~~~~return~self.func.{\_}{\_}get{\_}{\_}(cls,~type(cls))~\\
+~~~def~get{\_}from{\_}obj(self,~obj):~\\
+~~~~~~~return~self.get{\_}from{\_}class(obj.{\_}{\_}class{\_}{\_})~\\
+~\\
+class~C(object):~\\
+~~~s~=~Staticmethod(lambda~:~1)~\\
+~~~c~=~Classmethod(lambda~cls~:~cls.{\_}{\_}name{\_}{\_})~\\
+~\\
+c~=~C()~\\
+~\\
+assert~C.s()~==~c.s()~==~1~\\
+assert~C.c()~==~c.c()~==~"C"~\\
+~\\
+{\#}</descriptor.py>
+}\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{multilingual-attribute}{}
+\pdfbookmark[2]{Multilingual attribute}{multilingual-attribute}
+\subsubsection*{Multilingual attribute}
+
+Inspirated by a question in the Italian Newsgroup:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<multilingual.py>~\\
+~\\
+import~sys~\\
+from~descriptor~import~AttributeDescriptor~\\
+~\\
+class~MultilingualAttribute(AttributeDescriptor):~\\
+~~~~"{}"{}"When~a~MultilingualAttribute~is~accessed,~you~get~the~translation~~\\
+~~~~corresponding~to~the~currently~selected~language.~\\
+~~~~"{}"{}"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~**translations):~\\
+~~~~~~~~self.trans~=~translations~\\
+~~~~def~get{\_}from{\_}class(self,~cls):~\\
+~~~~~~~~return~self.trans{[}getattr(cls,~"language",~None)~or~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~sys.modules{[}cls.{\_}{\_}module{\_}{\_}].language]~\\
+~~~~def~get{\_}from{\_}obj(self,~obj):~\\
+~~~~~~~~return~self.trans{[}getattr(obj,~"language",~None)~or~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~sys.modules{[}obj.{\_}{\_}class{\_}{\_}.{\_}{\_}module{\_}{\_}].language]~\\
+~~~~~~\\
+~\\
+language~=~"en"~\\
+~\\
+{\#}~a~dummy~User~class~\\
+class~DefaultUser(object):~\\
+~~~~def~has{\_}permission(self):~\\
+~~~~~~~~return~False~\\
+~~~~\\
+class~WebApplication(object):~\\
+~~~~error{\_}msg~=~MultilingualAttribute(~\\
+~~~~~~~~en="You~cannot~access~this~page",~\\
+~~~~~~~~it="Questa~pagina~non~e'~accessibile",~\\
+~~~~~~~~fr="Vous~ne~pouvez~pas~acceder~cette~page",)~\\
+~~~~user~=~DefaultUser()~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~language=None):~\\
+~~~~~~~~self.language~=~language~or~getattr(self.{\_}{\_}class{\_}{\_},~"language",~None)~\\
+~~~~def~show{\_}page(self):~\\
+~~~~~~~~if~not~self.user.has{\_}permission():~\\
+~~~~~~~~~~~~return~self.error{\_}msg~\\
+~\\
+~\\
+app~=~WebApplication()~\\
+assert~app.show{\_}page()~==~"You~cannot~access~this~page"~\\
+~\\
+app.language~=~"fr"~\\
+assert~app.show{\_}page()~==~"Vous~ne~pouvez~pas~acceder~cette~page"~\\
+~\\
+app.language~=~"it"~\\
+assert~app.show{\_}page()~==~"Questa~pagina~non~e'~accessibile"~\\
+~\\
+app.language~=~"en"~\\
+assert~app.show{\_}page()~==~"You~cannot~access~this~page"~\\
+~\\
+{\#}</multilingual.py>
+}\end{quote}
+
+The same can be done with properties:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<multilingualprop.py>~\\
+~\\
+language~=~"en"~\\
+~\\
+{\#}~a~dummy~User~class~\\
+class~DefaultUser(object):~\\
+~~~~def~has{\_}permission(self):~\\
+~~~~~~~~return~False~\\
+~~~~\\
+def~multilingualProperty(**trans):~\\
+~~~~def~get(self):~\\
+~~~~~~~~return~trans{[}self.language]~\\
+~~~~def~set(self,~value):~\\
+~~~~~~~~trans{[}self.language]~=~value~~\\
+~~~~return~property(get,~set)~\\
+~\\
+class~WebApplication(object):~\\
+~~~~language~=~language~\\
+~~~~error{\_}msg~=~multilingualProperty(~\\
+~~~~~~~~en="You~cannot~access~this~page",~\\
+~~~~~~~~it="Questa~pagina~non~e'~accessibile",~\\
+~~~~~~~~fr="Vous~ne~pouvez~pas~acceder~cette~page",)~\\
+~~~~user~=~DefaultUser()~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~language=None):~\\
+~~~~~~~~if~language:~self.language~=~self.language~\\
+~~~~def~show{\_}page(self):~\\
+~~~~~~~~if~not~self.user.has{\_}permission():~\\
+~~~~~~~~~~~~return~self.error{\_}msg~\\
+~\\
+{\#}</multilingualprop.py>
+}\end{quote}
+
+This also gives the possibility to set the error messages.
+
+The difference with the descriptor approach
+\begin{verbatim}>>> from multilingual import WebApplication
+>>> app = WebApplication()
+>>> print app.error_msg
+You cannot access this page
+>>> print WebApplication.error_msg
+You cannot access this page\end{verbatim}
+
+is that with properties there is no nice access from the class:
+\begin{verbatim}>>> from multilingualprop import WebApplication
+>>> WebApplication.error_msg #doctest: +ELLIPSIS
+<property object at ...>\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{another-use-case-for-properties-storing-users}{}
+\pdfbookmark[2]{Another use case for properties: storing users}{another-use-case-for-properties-storing-users}
+\subsubsection*{Another use case for properties: storing users}
+
+Consider a library providing a simple User class:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<crypt{\_}user.py>~\\
+~\\
+class~User(object):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~username,~password):~\\
+~~~~~~~~self.username,~self.password~=~username,~password~\\
+~\\
+{\#}</crypt{\_}user.py>
+}\end{quote}
+
+The User objects are stored in a database as they are.
+For security purpose, in a second version of the library it is
+decided to crypt the password, so that only crypted passwords
+are stored in the database. With properties, it is possible to
+implement this functionality without changing the source code for
+the User class:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<crypt{\_}user.py>~\\
+~\\
+from~crypt~import~crypt~\\
+~\\
+def~cryptedAttribute(seed="x"):~\\
+~~~~def~get(self):~\\
+~~~~~~~~return~getattr(self,~"{\_}pw",~None)~\\
+~~~~def~set(self,~value):~\\
+~~~~~~~~self.{\_}pw~=~crypt(value,~seed)~\\
+~~~~return~property(get,~set)~\\
+~~~~\\
+User.password~=~cryptedAttribute()
+}\end{quote}
+
+{\#}{\textless}/crypt{\_}user.py{\textgreater}
+\begin{verbatim}>>> from crypt_user import User
+>>> u = User("michele", "secret")
+>>> print u.password
+xxZREZpkHZpkI\end{verbatim}
+
+Notice the property factory approach used here.
+
+
+%___________________________________________________________________________
+
+\hypertarget{low-level-delegation-via-getattribute}{}
+\pdfbookmark[2]{Low-level delegation via {\_}{\_}getattribute{\_}{\_}}{low-level-delegation-via-getattribute}
+\subsubsection*{Low-level delegation via {\_}{\_}getattribute{\_}{\_}}
+
+Attribute access is managed by the{\_}{\_}getattribute{\_}{\_} special method:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<tracedaccess.py>~\\
+~\\
+class~TracedAccess(object):~\\
+~~~~def~{\_}{\_}getattribute{\_}{\_}(self,~name):~\\
+~~~~~~~~print~"Accessing~{\%}s"~{\%}~name~\\
+~~~~~~~~return~object.{\_}{\_}getattribute{\_}{\_}(self,~name)~\\
+~\\
+~\\
+class~C(TracedAccess):~\\
+~~~~s~=~staticmethod(lambda~:~'staticmethod')~\\
+~~~~c~=~classmethod(lambda~cls:~'classmethod')~\\
+~~~~m~=~lambda~self:~'method'~\\
+~~~~a~=~"hello"~\\
+~\\
+{\#}</tracedaccess.py>
+}\end{quote}
+\begin{verbatim}>>> from tracedaccess import C
+>>> c = C()
+>>> print c.s()
+Accessing s
+staticmethod
+>>> print c.c()
+Accessing c
+classmethod
+>>> print c.m()
+Accessing m
+method
+>>> print c.a
+Accessing a
+hello
+>>> print c.__init__ #doctest: +ELLIPSIS
+Accessing __init__
+<method-wrapper object at 0x...>
+>>> try: c.x
+... except AttributeError, e: print e
+...
+Accessing x
+'C' object has no attribute 'x'\end{verbatim}
+\begin{verbatim}>>> c.y = 'y'
+>>> c.y
+Accessing y
+'y'\end{verbatim}
+
+You are probably familiar with \texttt{{\_}{\_}getattr{\_}{\_}} which is similar
+to \texttt{{\_}{\_}getattribute{\_}{\_}}, but it is called \emph{only for missing attributes}.
+
+
+%___________________________________________________________________________
+
+\hypertarget{traditional-delegation-via-getattr}{}
+\pdfbookmark[2]{Traditional delegation via {\_}{\_}getattr{\_}{\_}}{traditional-delegation-via-getattr}
+\subsubsection*{Traditional delegation via {\_}{\_}getattr{\_}{\_}}
+
+Realistic use case in ``object publishing'':
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<webapp.py>~\\
+~\\
+class~WebApplication(object):~\\
+~~~~def~{\_}{\_}getattr{\_}{\_}(self,~name):~\\
+~~~~~~~~return~name.capitalize()~\\
+~\\
+~\\
+app~=~WebApplication()~\\
+~\\
+assert~app.page1~==~'Page1'~\\
+assert~app.page2~==~'Page2'~\\
+~\\
+{\#}</webapp.py>
+}\end{quote}
+
+Here is another use case in HTML generation:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<XMLtag.py>~\\
+~\\
+def~makeattr(dict{\_}or{\_}list{\_}of{\_}pairs):~\\
+~~~~dic~=~dict(dict{\_}or{\_}list{\_}of{\_}pairs)~~\\
+~~~~return~"~".join('{\%}s="{\%}s"'~{\%}~(k,~dic{[}k])~for~k~in~dic)~{\#}~simplistic~\\
+~\\
+class~XMLTag(object):~\\
+~~~~def~{\_}{\_}getattr{\_}{\_}(self,~name):~\\
+~~~~~~~~def~tag(value,~**attr):~\\
+~~~~~~~~~~~~"{}"{}"value~can~be~a~string~or~a~sequence~of~strings."{}"{}"~\\
+~~~~~~~~~~~~if~hasattr(value,~"{\_}{\_}iter{\_}{\_}"):~{\#}~is~iterable~\\
+~~~~~~~~~~~~~~~~value~=~"~".join(value)~\\
+~~~~~~~~~~~~return~"<{\%}s~{\%}s>{\%}s</{\%}s>"~{\%}~(name,~makeattr(attr),~value,~name)~\\
+~~~~~~~~return~tag~\\
+~\\
+class~XMLShortTag(object):~\\
+~~~~def~{\_}{\_}getattr{\_}{\_}(self,~name):~\\
+~~~~~~~~def~tag(**attr):~\\
+~~~~~~~~~~~~return~"<{\%}s~{\%}s~/>"~{\%}~(name,~makeattr(attr))~\\
+~~~~~~~~return~tag~\\
+~\\
+tag~=~XMLTag()~\\
+tg~=~XMLShortTag()~\\
+~\\
+{\#}</XMLtag.py>
+}\end{quote}
+\begin{verbatim}>>> from XMLtag import tag, tg
+>>> print tag.a("example.com", href="http://www.example.com")
+<a href="http://www.example.com">example.com</a>
+>>> print tg.br(**{'class':"br_style"})
+<br class="br_style" />\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{keyword-dictionaries-with-getattr-setattr}{}
+\pdfbookmark[2]{Keyword dictionaries with {\_}{\_}getattr{\_}{\_}/{\_}{\_}setattr{\_}{\_}}{keyword-dictionaries-with-getattr-setattr}
+\subsubsection*{Keyword dictionaries with {\_}{\_}getattr{\_}{\_}/{\_}{\_}setattr{\_}{\_}}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<kwdict.py>~\\
+~\\
+class~kwdict(dict):~{\#}~or~UserDict,~to~make~it~to~work~with~Zope~\\
+~~~~"{}"{}"A~typing~shortcut~used~in~place~of~a~keyword~dictionary."{}"{}"~\\
+~~~~def~{\_}{\_}getattr{\_}{\_}(self,~name):~\\
+~~~~~~~~return~self{[}name]~\\
+~~~~def~{\_}{\_}setattr{\_}{\_}(self,~name,~value):~\\
+~~~~~~~~self{[}name]~=~value~\\
+~\\
+{\#}</kwdict.py>
+}\end{quote}
+
+And now for a completely different solution:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<dictwrapper.py>~\\
+~\\
+class~DictWrapper(object):~~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~**kw):~\\
+~~~~~~~~self.{\_}{\_}dict{\_}{\_}.update(kw)~\\
+~\\
+{\#}</dictwrapper.py>
+}\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{delegation-to-special-methods-caveat}{}
+\pdfbookmark[2]{Delegation to special methods caveat}{delegation-to-special-methods-caveat}
+\subsubsection*{Delegation to special methods caveat}
+\begin{verbatim}>>> class ListWrapper(object):
+... def __init__(self, ls):
+... self._list = ls
+... def __getattr__(self, name):
+... if name == "__getitem__": # special method
+... return self._list.__getitem__
+... elif name == "reverse": # regular method
+... return self._list.reverse
+... else:
+... raise AttributeError("%r is not defined" % name)
+...
+>>> lw = ListWrapper([0,1,2])
+>>> print lw.x
+Traceback (most recent call last):
+ ...
+AttributeError: 'x' is not defined\end{verbatim}
+\begin{verbatim}>>> lw.reverse()
+>>> print lw.__getitem__(0)
+2
+>>> print lw.__getitem__(1)
+1
+>>> print lw.__getitem__(2)
+0
+>>> print lw[0]
+Traceback (most recent call last):
+ ...
+TypeError: unindexable object\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-ii-inheritance}{}
+\pdfbookmark[1]{Part II: Inheritance}{part-ii-inheritance}
+\subsection*{Part II: Inheritance}
+
+The major changes in inheritance from Python 2.1 to 2.2+ are:
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1}.}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+you can subclass built-in types (as a consequence the constructor{\_}{\_}new{\_}{\_}
+has been exposed to the user, to help subclassing immutable types);
+
+\item {}
+the Method Resolution Order (MRO) has changed;
+
+\item {}
+now Python allows \emph{cooperative method calls}, i.e. we have \emph{super}.
+
+\end{list}
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-you-need-to-know-about-mi-even-if-you-do-not-use-it}{}
+\pdfbookmark[2]{Why you need to know about MI even if you do not use it}{why-you-need-to-know-about-mi-even-if-you-do-not-use-it}
+\subsubsection*{Why you need to know about MI even if you do not use it}
+
+In principle, the last two changes are relevant only if you use multiple
+inheritance. If you use single inheritance only, you don't need \texttt{super}:
+you can just name the superclass.
+However, somebody else may want to use your class in a MI hierarchy,
+and you would make her life difficult if you don't use \texttt{super}.
+
+My SI hierarchy:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<why{\_}super.py>~\\
+~\\
+class~Base(object):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"B.{\_}{\_}init{\_}{\_}"~\\
+~\\
+class~MyClass(Base):~\\
+~~~~"I~do~not~cooperate~with~others"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"MyClass.{\_}{\_}init{\_}{\_}"~\\
+~~~~~~~~Base.{\_}{\_}init{\_}{\_}(self)~~{\#}instead~of~super(MyClass,~self).{\_}{\_}init{\_}{\_}()~\\
+~\\
+{\#}</why{\_}super.py>
+}\end{quote}
+
+Her MI hierarchy:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<why{\_}super.py>~\\
+~\\
+class~Mixin(Base):~\\
+~~~~"I~am~cooperative~with~others"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"Mixin.{\_}{\_}init{\_}{\_}"~\\
+~~~~~~~~super(Mixin,~self).{\_}{\_}init{\_}{\_}()~\\
+~\\
+class~HerClass(MyClass,~Mixin):~\\
+~~~~"I~am~supposed~to~be~cooperative~too"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"HerClass.{\_}{\_}init{\_}{\_}"~\\
+~~~~~~~~super(HerClass,~self).{\_}{\_}init{\_}{\_}()~\\
+~\\
+{\#}</why{\_}super.py>
+}\end{quote}
+\begin{verbatim}>>> from why_super import HerClass
+>>> h = HerClass() # Mixin.__init__ is not called!
+HerClass.__init__
+MyClass.__init__
+B.__init__\end{verbatim}
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~4~object~~~\\
+~~~~~~~~~~|~\\
+~~~~~~~3~Base~\\
+~~~~~~/~~~~~~{\textbackslash}~\\
+1~MyClass~~2~Mixin~\\
+~~~~~~~{\textbackslash}~~~~~/~\\
+~~~~~0~HerClass
+}\end{quote}
+\end{quote}
+\begin{verbatim}>>> [ancestor.__name__ for ancestor in HerClass.mro()]
+['HerClass', 'MyClass', 'Mixin', 'Base', 'object']\end{verbatim}
+
+In order to be polite versus your future users, you should use \texttt{super}
+always. This adds a cognitive burden even for people not using MI :-(
+
+Notice that there is no good comprehensive reference on \texttt{super} (yet)
+Your best bet is still \href{http://www.python.org/2.2.3/descrintro.html\#cooperation}{http://www.python.org/2.2.3/descrintro.html{\#}cooperation}
+
+The MRO instead is explained here: \href{http://www.python.org/2.3/mro.html}{http://www.python.org/2.3/mro.html}
+
+Notice that I DO NOT recommand Multiple Inheritance.
+
+More often than not you are better off using composition/delegation/wrapping,
+etc.
+
+See Zope 2 -{\textgreater} Zope 3 experience.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-few-details-about-super-not-the-whole-truth}{}
+\pdfbookmark[2]{A few details about super (not the whole truth)}{a-few-details-about-super-not-the-whole-truth}
+\subsubsection*{A few details about \texttt{super} (not the whole truth)}
+\begin{verbatim}>>> class B(object):
+... def __init__(self): print "B.__init__"
+...
+>>> class C(B):
+... def __init__(self): print "C.__init__"
+...
+>>> c = C()
+C.__init__\end{verbatim}
+
+\texttt{super(cls, instance)}, where \texttt{instance} is an instance of \texttt{cls} or of
+a subclass of \texttt{cls}, retrieves the right method in the MRO:
+\begin{verbatim}>>> super(C, c).__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>\end{verbatim}
+\begin{verbatim}>>> super(C, c).__init__.im_func is B.__init__.im_func
+True\end{verbatim}
+\begin{verbatim}>>> super(C, c).__init__()
+B.__init__\end{verbatim}
+
+\texttt{super(cls, subclass)} works for unbound methods:
+\begin{verbatim}>>> super(C, C).__init__
+<unbound method C.__init__>\end{verbatim}
+\begin{verbatim}>>> super(C, C).__init__.im_func is B.__init__.im_func
+True
+>>> super(C, C).__init__(c)
+B.__init__\end{verbatim}
+
+\texttt{super(cls, subclass)} is also necessary for classmethods and staticmethods.
+Properties and custom descriptorsw works too:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<super{\_}ex.py>~\\
+~\\
+from~descriptor~import~AttributeDescriptor~\\
+~\\
+class~B(object):~\\
+~~~@staticmethod~\\
+~~~def~sm():~return~"staticmethod"~\\
+~\\
+~~~@classmethod~\\
+~~~def~cm(cls):~return~cls.{\_}{\_}name{\_}{\_}~\\
+~\\
+~~~p~=~property()~\\
+~~~a~=~AttributeDescriptor()~\\
+~\\
+class~C(B):~pass~\\
+~\\
+{\#}</super{\_}ex.py>
+}\end{quote}
+\begin{verbatim}>>> from super_ex import C\end{verbatim}
+
+Staticmethod usage:
+\begin{verbatim}>>> super(C, C).sm #doctest: +ELLIPSIS
+<function sm at 0x...>
+>>> super(C, C).sm()
+'staticmethod'\end{verbatim}
+
+Classmethod usage:
+\begin{verbatim}>>> super(C, C()).cm
+<bound method type.cm of <class 'super_ex.C'>>
+>>> super(C, C).cm() # C is automatically passed
+'C'\end{verbatim}
+
+Property usage:
+\begin{verbatim}>>> print super(C, C).p #doctest: +ELLIPSIS
+<property object at 0x...>
+>>> super(C, C).a #doctest: +ELLIPSIS
+Getting <descriptor.AttributeDescriptor object at 0x...> from <class 'super_ex.C'>\end{verbatim}
+
+\texttt{super} does not work with old-style classes, however you can use the
+following trick:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<super{\_}old{\_}new.py>~\\
+class~O:~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"O.{\_}{\_}init{\_}{\_}"~\\
+~\\
+class~N(O,~object):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"N.{\_}{\_}init{\_}{\_}"~\\
+~~~~~~~~super(N,~self).{\_}{\_}init{\_}{\_}()~\\
+~\\
+{\#}</super{\_}old{\_}new.py>
+}\end{quote}
+\begin{verbatim}>>> from super_old_new import N
+>>> new = N()
+N.__init__
+O.__init__\end{verbatim}
+
+There are dozens of tricky points concerning \texttt{super}, be warned!
+
+
+%___________________________________________________________________________
+
+\hypertarget{subclassing-built-in-types-new-vs-init}{}
+\pdfbookmark[2]{Subclassing built-in types; {\_}{\_}new{\_}{\_} vs. {\_}{\_}init{\_}{\_}}{subclassing-built-in-types-new-vs-init}
+\subsubsection*{Subclassing built-in types; {\_}{\_}new{\_}{\_} vs. {\_}{\_}init{\_}{\_}}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<point.py>~\\
+~\\
+class~NotWorkingPoint(tuple):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~x,~y):~\\
+~~~~~~~~super(NotWorkingPoint,~self).{\_}{\_}init{\_}{\_}((x,y))~\\
+~~~~~~~~self.x,~self.y~=~x,~y~\\
+~\\
+{\#}</point.py>
+}\end{quote}
+\begin{verbatim}>>> from point import NotWorkingPoint
+>>> p = NotWorkingPoint(2,3)
+Traceback (most recent call last):
+ ...
+TypeError: tuple() takes at most 1 argument (2 given)\end{verbatim}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<point.py>~\\
+~\\
+class~Point(tuple):~\\
+~~~~def~{\_}{\_}new{\_}{\_}(cls,~x,~y):~\\
+~~~~~~~~return~super(Point,~cls).{\_}{\_}new{\_}{\_}(cls,~(x,y))~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~x,~y):~\\
+~~~~~~~~super(Point,~self).{\_}{\_}init{\_}{\_}((x,~y))~\\
+~~~~~~~~self.x,~self.y~=~x,~y~\\
+~\\
+{\#}</point.py>
+}\end{quote}
+
+Notice that{\_}{\_}new{\_}{\_} is a staticmethod, not a classmethod, so one needs
+to pass the class explicitely.
+\begin{verbatim}>>> from point import Point
+>>> p = Point(2,3)
+>>> print p, p.x, p.y
+(2, 3) 2 3\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{be-careful-when-using-new-with-mutable-types}{}
+\pdfbookmark[2]{Be careful when using {\_}{\_}new{\_}{\_} with mutable types}{be-careful-when-using-new-with-mutable-types}
+\subsubsection*{Be careful when using {\_}{\_}new{\_}{\_} with mutable types}
+\begin{verbatim}>>> class ListWithDefault(list):
+... def __new__(cls):
+... return super(ListWithDefault, cls).__new__(cls, ["hello"])
+...
+>>> print ListWithDefault() # beware! NOT ["hello"]!
+[]\end{verbatim}
+
+Reason: lists are re-initialized to empty lists in list.{\_}{\_}init{\_}{\_}!
+
+Instead
+\begin{verbatim}>>> class ListWithDefault(list):
+... def __init__(self):
+... super(ListWithDefault, self).__init__(["hello"])
+...
+>>> print ListWithDefault() # works!
+['hello']\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{lecture-3-magic-i-e-decorators-and-metaclasses}{}
+\pdfbookmark[0]{Lecture 3: Magic (i.e. decorators and metaclasses)}{lecture-3-magic-i-e-decorators-and-metaclasses}
+\section*{Lecture 3: Magic (i.e. decorators and metaclasses)}
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-i-decorators}{}
+\pdfbookmark[1]{Part I: decorators}{part-i-decorators}
+\subsection*{Part I: decorators}
+
+Decorators are just sugar: their functionality was already in the language
+\begin{verbatim}>>> def s(): pass
+>>> s = staticmethod(s)\end{verbatim}
+\begin{verbatim}>>> @staticmethod
+... def s(): pass
+...\end{verbatim}
+
+However sugar \emph{does} matter.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-typical-decorator-traced}{}
+\pdfbookmark[2]{A typical decorator: traced}{a-typical-decorator-traced}
+\subsubsection*{A typical decorator: traced}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<traced.py>~\\
+~\\
+def~traced(func):~\\
+~~~~def~tracedfunc(*args,~**kw):~\\
+~~~~~~~~print~"calling~{\%}s.{\%}s"~{\%}~(func.{\_}{\_}module{\_}{\_},~func.{\_}{\_}name{\_}{\_})~\\
+~~~~~~~~return~func(*args,~**kw)~\\
+~~~~tracedfunc.{\_}{\_}name{\_}{\_}~=~func.{\_}{\_}name{\_}{\_}~\\
+~~~~return~tracedfunc~\\
+~\\
+@traced~\\
+def~f():~pass~\\
+~\\
+{\#}</traced.py>
+}\end{quote}
+\begin{verbatim}>>> from traced import f
+>>> f()
+calling traced.f\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-decorator-factory-timed}{}
+\pdfbookmark[2]{A decorator factory: Timed}{a-decorator-factory-timed}
+\subsubsection*{A decorator factory: Timed}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<timed.py>~\\
+~\\
+import~sys,~time~\\
+~\\
+class~Timed(object):~\\
+~~~~"{}"{}"Decorator~factory:~each~decorator~object~wraps~a~function~and~~\\
+~~~~executes~it~many~times~(default~100~times).~\\
+~~~~The~average~time~spent~in~one~iteration,~expressed~in~milliseconds,~~\\
+~~~~is~stored~in~the~attributes~wrappedfunc.time~and~wrappedfunc.clocktime,~\\
+~~~~and~displayed~into~a~log~file~which~defaults~to~stdout.~\\
+~~~~"{}"{}"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~repeat=100,~logfile=sys.stdout):~\\
+~~~~~~~~self.repeat~=~repeat~\\
+~~~~~~~~self.logfile~=~logfile~\\
+~~~~def~{\_}{\_}call{\_}{\_}(self,~func):~\\
+~~~~~~~~def~wrappedfunc(*args,~**kw):~\\
+~~~~~~~~~~~~fullname~=~"{\%}s.{\%}s~..."~{\%}~(func.{\_}{\_}module{\_}{\_},~func.func{\_}name)~\\
+~~~~~~~~~~~~print~>{}>~self.logfile,~'Executing~{\%}s'~{\%}~fullname.ljust(30),~\\
+~~~~~~~~~~~~time1~=~time.time()~\\
+~~~~~~~~~~~~clocktime1~=~time.clock()~\\
+~~~~~~~~~~~~for~i~in~xrange(self.repeat):~\\
+~~~~~~~~~~~~~~~~res~=~func(*args,**kw)~{\#}~executes~func~self.repeat~times~\\
+~~~~~~~~~~~~time2~=~time.time()~\\
+~~~~~~~~~~~~clocktime2~=~time.clock()~\\
+~~~~~~~~~~~~wrappedfunc.time~=~1000*(time2-time1)/self.repeat~\\
+~~~~~~~~~~~~wrappedfunc.clocktime~=~1000*(clocktime2~-~clocktime1)/self.repeat~\\
+~~~~~~~~~~~~print~>{}>~self.logfile,~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~'Real~time:~{\%}s~ms;'~{\%}~self.rounding(wrappedfunc.time),~\\
+~~~~~~~~~~~~print~>{}>~self.logfile,~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~'Clock~time:~{\%}s~ms'~{\%}~self.rounding(wrappedfunc.clocktime)~\\
+~~~~~~~~~~~~return~res~\\
+~~~~~~~~wrappedfunc.func{\_}name~=~func.func{\_}name~\\
+~~~~~~~~wrappedfunc.{\_}{\_}module{\_}{\_}~=~func.{\_}{\_}module{\_}{\_}~\\
+~~~~~~~~return~wrappedfunc~\\
+~~~~@staticmethod~\\
+~~~~def~rounding(float{\_}):~\\
+~~~~~~~~"Three~digits~rounding~for~small~numbers,~1~digit~rounding~otherwise."~\\
+~~~~~~~~if~float{\_}~<~10.:~\\
+~~~~~~~~~~~~return~"{\%}5.3f"~{\%}~float{\_}~\\
+~~~~~~~~else:~\\
+~~~~~~~~~~~~return~"{\%}5.1f"~{\%}~float{\_}~\\
+~~~~\\
+{\#}</timed.py>
+}\end{quote}
+\begin{verbatim}>>> from timed import Timed
+>>> from random import sample
+>>> example_ls = sample(xrange(1000000), 1000)
+>>> @Timed()
+... def list_sort(ls):
+... ls.sort()
+...
+>>> list_sort(example_ls) #doctest: +ELLIPSIS
+Executing __main__.list_sort ... Real time: 0... ms; Clock time: 0... ms\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-powerful-decorator-pattern}{}
+\pdfbookmark[2]{A powerful decorator pattern}{a-powerful-decorator-pattern}
+\subsubsection*{A powerful decorator pattern}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<traced{\_}function2.py>~\\
+~\\
+from~decorators~import~decorator~\\
+~\\
+def~trace(f,~*args,~**kw):~\\
+~~~~print~"calling~{\%}s~with~args~{\%}s,~{\%}s"~{\%}~(f.func{\_}name,~args,~kw)~~\\
+~~~~return~f(*args,~**kw)~\\
+~\\
+traced{\_}function~=~decorator(trace)~\\
+~\\
+@traced{\_}function~\\
+def~f1(x):~\\
+~~~~pass~\\
+~\\
+@traced{\_}function~\\
+def~f2(x,~y):~\\
+~~~~pass~\\
+~\\
+{\#}</traced{\_}function2.py>
+}\end{quote}
+\begin{verbatim}>>> from traced_function2 import traced_function, f1, f2
+>>> f1(1)
+calling f1 with args (1,), {}
+>>> f2(1,2)
+calling f2 with args (1, 2), {}\end{verbatim}
+
+works with pydoc:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}~pydoc2.4~traced{\_}function2.f2~~~\\
+Help~on~function~f1~in~traced{\_}function2:~\\
+~\\
+traced{\_}function2.f1~=~f1(x)~\\
+~\\
+{\$}~pydoc2.4~traced{\_}function2.f2~~~\\
+Help~on~function~f2~in~traced{\_}function2:~\\
+~\\
+traced{\_}function2.f2~=~f2(x,~y)
+}\end{quote}
+
+Here is the source code:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<decorators.py>~\\
+~\\
+def~getinfo(func):~\\
+~~~~"{}"{}"Return~an~info~dictionary~containing:~\\
+~~~~-~name~(the~name~of~the~function~:~str)~\\
+~~~~-~argnames~(the~names~of~the~arguments~:~list)~\\
+~~~~-~defarg~(the~values~of~the~default~arguments~:~list)~\\
+~~~~-~fullsign~(the~full~signature~:~str)~\\
+~~~~-~shortsign~(the~short~signature~:~str)~\\
+~~~~-~arg0~...~argn~(shortcuts~for~the~names~of~the~arguments)~\\
+~\\
+~~~~>{}>{}>~def~f(self,~x=1,~y=2,~*args,~**kw):~pass~\\
+~\\
+~~~~>{}>{}>~info~=~getinfo(f)~\\
+~\\
+~~~~>{}>{}>~info{[}"name"]~\\
+~~~~'f'~\\
+~~~~>{}>{}>~info{[}"argnames"]~\\
+~~~~{[}'self',~'x',~'y',~'args',~'kw']~\\
+~~~~~\\
+~~~~>{}>{}>~info{[}"defarg"]~\\
+~~~~(1,~2)~\\
+~\\
+~~~~>{}>{}>~info{[}"shortsign"]~\\
+~~~~'self,~x,~y,~*args,~**kw'~\\
+~~~~~\\
+~~~~>{}>{}>~info{[}"fullsign"]~\\
+~~~~'self,~x=defarg{[}0],~y=defarg{[}1],~*args,~**kw'~\\
+~\\
+~~~~>{}>{}>~info{[}"arg0"],~info{[}"arg1"],~info{[}"arg2"],~info{[}"arg3"],~info{[}"arg4"]~\\
+~~~~('self',~'x',~'y',~'args',~'kw')~\\
+~~~~"{}"{}"~\\
+~~~~assert~inspect.ismethod(func)~or~inspect.isfunction(func)~\\
+~~~~regargs,~varargs,~varkwargs,~defaults~=~inspect.getargspec(func)~\\
+~~~~argnames~=~list(regargs)~\\
+~~~~if~varargs:~argnames.append(varargs)~\\
+~~~~if~varkwargs:~argnames.append(varkwargs)~\\
+~~~~counter~=~itertools.count()~\\
+~~~~fullsign~=~inspect.formatargspec(~\\
+~~~~~~~~regargs,~varargs,~varkwargs,~defaults,~\\
+~~~~~~~~formatvalue=lambda~value:~"=defarg{[}{\%}i]"~{\%}~counter.next()){[}1:-1]~\\
+~~~~shortsign~=~inspect.formatargspec(~\\
+~~~~~~~~regargs,~varargs,~varkwargs,~defaults,~\\
+~~~~~~~~formatvalue=lambda~value:~"{}"){[}1:-1]~\\
+~~~~dic~=~dict(("arg{\%}s"~{\%}~n,~name)~for~n,~name~in~enumerate(argnames))~\\
+~~~~dic.update(name=func.{\_}{\_}name{\_}{\_},~argnames=argnames,~shortsign=shortsign,~\\
+~~~~~~~~fullsign~=~fullsign,~defarg~=~func.func{\_}defaults~or~())~\\
+~~~~return~dic~\\
+~\\
+def~{\_}contains{\_}reserved{\_}names(dic):~{\#}~helper~\\
+~~~~return~"{\_}call{\_}"~in~dic~or~"{\_}func{\_}"~in~dic~\\
+~\\
+def~{\_}decorate(func,~caller):~\\
+~~~~"{}"{}"Takes~a~function~and~a~caller~and~returns~the~function~\\
+~~~~decorated~with~that~caller.~The~decorated~function~is~obtained~\\
+~~~~by~evaluating~a~lambda~function~with~the~correct~signature.~\\
+~~~~"{}"{}"~\\
+~~~~infodict~=~getinfo(func)~\\
+~~~~assert~not~{\_}contains{\_}reserved{\_}names(infodict{[}"argnames"]),~{\textbackslash}~\\
+~~~~~~~~~~~"You~cannot~use~{\_}call{\_}~or~{\_}func{\_}~as~argument~names!"~\\
+~~~~execdict=dict({\_}func{\_}=func,~{\_}call{\_}=caller,~defarg=func.func{\_}defaults~or~())~\\
+~~~~if~func.{\_}{\_}name{\_}{\_}~==~"<lambda>":~\\
+~~~~~~~~lambda{\_}src~=~"lambda~{\%}(fullsign)s:~{\_}call{\_}({\_}func{\_},~{\%}(shortsign)s)"~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~~{\%}~infodict~\\
+~~~~~~~~dec{\_}func~=~eval(lambda{\_}src,~execdict)~\\
+~~~~else:~\\
+~~~~~~~~func{\_}src~=~"{}"{}"def~{\%}(name)s({\%}(fullsign)s):~\\
+~~~~~~~~return~{\_}call{\_}({\_}func{\_},~{\%}(shortsign)s)"{}"{}"~{\%}~infodict~\\
+~~~~~~~~exec~func{\_}src~in~execdict~~\\
+~~~~~~~~dec{\_}func~=~execdict{[}func.{\_}{\_}name{\_}{\_}]~\\
+~~~~{\#}import~sys;~print~>{}>~sys.stderr,~func{\_}src~{\#}~for~debugging~~\\
+~~~~dec{\_}func.{\_}{\_}doc{\_}{\_}~=~func.{\_}{\_}doc{\_}{\_}~\\
+~~~~dec{\_}func.{\_}{\_}dict{\_}{\_}~=~func.{\_}{\_}dict{\_}{\_}~\\
+~~~~return~dec{\_}func~\\
+~\\
+class~decorator(object):~\\
+~~~~"{}"{}"General~purpose~decorator~factory:~takes~a~caller~function~as~\\
+input~and~returns~a~decorator.~A~caller~function~is~any~function~like~this::~\\
+~\\
+~~~~def~caller(func,~*args,~**kw):~\\
+~~~~~~~~{\#}~do~something~\\
+~~~~~~~~return~func(*args,~**kw)~\\
+~~~~~\\
+Here~is~an~example~of~usage:~\\
+~\\
+~~~~>{}>{}>~@decorator~\\
+~~~~...~def~chatty(f,~*args,~**kw):~\\
+~~~~...~~~~~print~"Calling~{\%}r"~{\%}~f.{\_}{\_}name{\_}{\_}~\\
+~~~~...~~~~~return~f(*args,~**kw)~\\
+~~~~~\\
+~~~~>{}>{}>~@chatty~\\
+~~~~...~def~f():~pass~\\
+~~~~...~\\
+~~~~>{}>{}>~f()~\\
+~~~~Calling~'f'~\\
+~~~~"{}"{}"~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~caller):~\\
+~~~~~~~~self.caller~=~caller~\\
+~~~~def~{\_}{\_}call{\_}{\_}(self,~func):~\\
+~~~~~~~~return~{\_}decorate(func,~self.caller)~\\
+~\\
+~\\
+{\#}</decorators.py>
+}\end{quote}
+
+The possibilities of this pattern are endless.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-deferred-decorator}{}
+\pdfbookmark[2]{A deferred decorator}{a-deferred-decorator}
+\subsubsection*{A deferred decorator}
+
+You want to execute a procedure only after a certain time delay (for instance
+for use within an asyncronous Web framework):
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<deferred.py>~\\
+"Deferring~the~execution~of~a~procedure~(function~returning~None)"~\\
+~\\
+import~threading~\\
+from~decorators~import~decorator~\\
+~\\
+def~deferred(nsec):~\\
+~~~~def~call{\_}later(func,~*args,~**kw):~\\
+~~~~~~~~return~threading.Timer(nsec,~func,~args,~kw).start()~\\
+~~~~return~decorator(call{\_}later)~\\
+~\\
+@deferred(2)~\\
+def~hello():~\\
+~~~~print~"hello"~\\
+~\\
+if~{\_}{\_}name{\_}{\_}~==~"{\_}{\_}main{\_}{\_}":~\\
+~~~~hello()~~~~~\\
+~~~~print~"Calling~hello()~..."~\\
+~~~~\\
+~\\
+{\#}</deferred.py>~\\
+~\\
+{\$}~python~deferred.py
+}\end{quote}
+
+Show an example of an experimental decorator based web framework
+(doctester{\_}frontend).
+
+
+%___________________________________________________________________________
+
+\hypertarget{part-ii-metaclasses}{}
+\pdfbookmark[1]{Part II: metaclasses}{part-ii-metaclasses}
+\subsection*{Part II: metaclasses}
+
+Metaclasses are there! Consider this example from a recent post on c.l.py:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<BaseClass.py>~\\
+~\\
+class~BaseClass(object):~\\
+~~~"Do~something"~\\
+~\\
+{\#}</BaseClass.py>
+}\end{quote}
+\begin{verbatim}>>> import BaseClass # instead of 'from BaseClass import BaseClass'
+>>> class C(BaseClass): pass
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ module.__init__() takes at most 2 arguments (3 given)\end{verbatim}
+
+The reason for the error is that class \texttt{C(BaseClass): pass} is
+actually calling the \texttt{type} metaclass with three arguments:
+\begin{quote}{\ttfamily \raggedright \noindent
+C~=~type("C",~(BaseClass,),~{\{}{\}})
+}\end{quote}
+
+\texttt{type.{\_}{\_}new{\_}{\_}} tries to use \texttt{type(BaseClass)} as metaclass,
+but since BaseClass here is a module, and \texttt{ModuleType} is not
+a metaclass, it cannot work. The error message reflects a conflict with
+the signature of ModuleType which requires two parameters and not three.
+
+So even if you don't use them, you may want to know they exist.
+
+
+%___________________________________________________________________________
+
+\hypertarget{rejuvenating-old-style-classes}{}
+\pdfbookmark[2]{Rejuvenating old-style classes}{rejuvenating-old-style-classes}
+\subsubsection*{Rejuvenating old-style classes}
+\begin{verbatim}>>> class Old: pass
+>>> print type(Old)
+<type 'classobj'>\end{verbatim}
+\begin{verbatim}>>> __metaclass__ = type # to rejuvenate class
+>>> class NotOld: pass
+...
+>>> print NotOld.__class__
+<type 'type'>\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-typical-metaclass-example-metatracer}{}
+\pdfbookmark[2]{A typical metaclass example: MetaTracer}{a-typical-metaclass-example-metatracer}
+\subsubsection*{A typical metaclass example: MetaTracer}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<metatracer.py>~\\
+~\\
+import~inspect~\\
+from~decorators~import~decorator~\\
+~\\
+@decorator~\\
+def~traced(meth,~*args,~**kw):~\\
+~~~~cls~=~meth.{\_}{\_}cls{\_}{\_}~\\
+~~~~modname~=~meth.{\_}{\_}module{\_}{\_}~or~cls.{\_}{\_}module{\_}{\_}~\\
+~~~~print~"calling~{\%}s.{\%}s.{\%}s"~{\%}~(modname,~cls.{\_}{\_}name{\_}{\_},~meth.{\_}{\_}name{\_}{\_})~\\
+~~~~return~meth(*args,~**kw)~\\
+~\\
+class~MetaTracer(type):~~~~~~~~~~~~~\\
+~~~~def~{\_}{\_}init{\_}{\_}(cls,~name,~bases,~dic):~\\
+~~~~~~~~super(MetaTracer,~cls).{\_}{\_}init{\_}{\_}(name,~bases,~dic)~\\
+~~~~~~~~for~k,~v~in~dic.iteritems():~\\
+~~~~~~~~~~~~if~inspect.isfunction(v):~\\
+~~~~~~~~~~~~~~~~v.{\_}{\_}cls{\_}{\_}~=~cls~{\#}~so~we~know~in~which~class~v~was~defined~\\
+~~~~~~~~~~~~~~~~setattr(cls,~k,~traced(v))~\\
+~\\
+{\#}</metatracer.py>
+}\end{quote}
+
+Usage: exploring classes in the standard library
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<dictmixin.py>~\\
+~\\
+from~metatracer~import~MetaTracer~\\
+from~UserDict~import~DictMixin~\\
+~\\
+class~TracedDM(DictMixin,~object):~\\
+~~~~{\_}{\_}metaclass{\_}{\_}~=~MetaTracer~\\
+~~~~def~{\_}{\_}getitem{\_}{\_}(self,~item):~\\
+~~~~~~~~return~item~\\
+~~~~def~keys(self):~~\\
+~~~~~~~~return~{[}1,2,3]~\\
+~\\
+{\#}</dictmixin.py>
+}\end{quote}
+\begin{verbatim}>>> from dictmixin import TracedDM
+>>> print TracedDM()
+calling dictmixin.TracedDM.keys
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+{1: 1, 2: 2, 3: 3}\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{real-life-example-check-overriding}{}
+\pdfbookmark[2]{Real life example: check overriding}{real-life-example-check-overriding}
+\subsubsection*{Real life example: check overriding}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<check{\_}overriding.py>~\\
+~\\
+class~Base(object):~\\
+~~~~a~=~0~\\
+~\\
+class~CheckOverriding(type):~\\
+~~~~"Prints~a~message~if~we~are~overriding~a~name."~\\
+~~~~def~{\_}{\_}new{\_}{\_}(mcl,~name,~bases,~dic):~\\
+~~~~~~~~for~name,~val~in~dic.iteritems():~\\
+~~~~~~~~~~~~if~name.startswith("{\_}{\_}")~and~name.endswith("{\_}{\_}"):~~\\
+~~~~~~~~~~~~~~~~continue~{\#}~ignore~special~names~\\
+~~~~~~~~~~~~a{\_}base{\_}has{\_}name~=~True~in~(hasattr(base,~name)~for~base~in~bases)~\\
+~~~~~~~~~~~~if~a{\_}base{\_}has{\_}name:~\\
+~~~~~~~~~~~~~~~~print~"AlreadyDefinedNameWarning:~"~+~name~\\
+~~~~~~~~return~super(CheckOverriding,~mcl).{\_}{\_}new{\_}{\_}(mcl,~name,~bases,~dic)~\\
+~\\
+class~MyClass(Base):~\\
+~~~~{\_}{\_}metaclass{\_}{\_}~=~CheckOverriding~\\
+~~~~a~=~1~\\
+~\\
+class~ChildClass(MyClass):~\\
+~~~~a~=~2
+}\end{quote}
+
+{\#}{\textless}/check{\_}overriding.py{\textgreater}
+\begin{verbatim}>>> import check_overriding
+AlreadyDefinedNameWarning: a
+AlreadyDefinedNameWarning: a\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{logfile}{}
+\pdfbookmark[2]{LogFile}{logfile}
+\subsubsection*{LogFile}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<logfile.py>~\\
+~\\
+import~subprocess~\\
+~\\
+def~memoize(func):~\\
+~~~~memoize{\_}dic~=~{\{}{\}}~\\
+~~~~def~wrapped{\_}func(*args):~\\
+~~~~~~~~if~args~in~memoize{\_}dic:~\\
+~~~~~~~~~~~~return~memoize{\_}dic{[}args]~\\
+~~~~~~~~else:~\\
+~~~~~~~~~~~~result~=~func(*args)~\\
+~~~~~~~~~~~~memoize{\_}dic{[}args]~=~result~\\
+~~~~~~~~~~~~return~result~\\
+~~~~wrapped{\_}func.{\_}{\_}name{\_}{\_}~=~func.{\_}{\_}name{\_}{\_}~\\
+~~~~wrapped{\_}func.{\_}{\_}doc{\_}{\_}~=~func.{\_}{\_}doc{\_}{\_}~\\
+~~~~wrapped{\_}func.{\_}{\_}dict{\_}{\_}~=~func.{\_}{\_}dict{\_}{\_}~\\
+~~~~return~wrapped{\_}func~\\
+~\\
+class~Memoize(type):~{\#}~Singleton~is~a~special~case~of~Memoize~\\
+~~~~@memoize~\\
+~~~~def~{\_}{\_}call{\_}{\_}(cls,~*args):~\\
+~~~~~~~~return~super(Memoize,~cls).{\_}{\_}call{\_}{\_}(*args)~\\
+~\\
+class~LogFile(file):~\\
+~~~~"{}"{}"Open~a~file~for~append."{}"{}"~\\
+~~~~{\_}{\_}metaclass{\_}{\_}~=~Memoize~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self,~name~=~"/tmp/err.log"):~\\
+~~~~~~~~self.viewer{\_}cmd~=~'xterm~-e~less'.split()~\\
+~~~~~~~~super(LogFile,~self).{\_}{\_}init{\_}{\_}(name,~"a")~\\
+~\\
+~~~~def~display(self,~*ls):~\\
+~~~~~~~~"Use~'less'~to~display~the~log~file~in~a~separate~xterm."~\\
+~~~~~~~~print~>{}>~self,~"{\textbackslash}n".join(map(str,~ls));~self.flush()~\\
+~~~~~~~~subprocess.call(self.viewer{\_}cmd~+~{[}self.name])~\\
+~\\
+~~~~def~reset(self):~\\
+~~~~~~~~"Erase~the~log~file."~\\
+~~~~~~~~print~>{}>~file(self.name,~"w")~\\
+~\\
+if~{\_}{\_}name{\_}{\_}~==~"{\_}{\_}main{\_}{\_}":~{\#}~test~\\
+~~~~print~>{}>~LogFile(),~"hello"~\\
+~~~~print~>{}>~LogFile(),~"world"~\\
+~~~~LogFile().display()~\\
+~\\
+{\#}</logfile.py>~\\
+~\\
+{\$}~python~logfile.py
+}\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{cooperative-hierarchies}{}
+\pdfbookmark[2]{Cooperative hierarchies}{cooperative-hierarchies}
+\subsubsection*{Cooperative hierarchies}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<cooperative{\_}init.py>~\\
+~\\
+"{}"{}"Given~a~hierarchy,~makes~{\_}{\_}init{\_}{\_}~cooperative.~\\
+The~only~change~needed~is~to~add~a~line~\\
+~\\
+~~~{\_}{\_}metaclass{\_}{\_}~=~CooperativeInit~\\
+~\\
+to~the~base~class~of~your~hierarchy."{}"{}"~\\
+~\\
+from~decorators~import~decorator~~\\
+~\\
+class~CooperativeInit(type):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(cls,~name,~bases,~dic):~\\
+~\\
+~~~~~~~~@decorator~\\
+~~~~~~~~def~make{\_}cooperative({\_}{\_}init{\_}{\_},~self,~*args,~**kw):~\\
+~~~~~~~~~~~~super(cls,~self).{\_}{\_}init{\_}{\_}(*args,~**kw)~\\
+~~~~~~~~~~~~{\_}{\_}init{\_}{\_}(self,~*args,~**kw)~\\
+~\\
+~~~~~~~~{\_}{\_}init{\_}{\_}~=~dic.get("{\_}{\_}init{\_}{\_}")~\\
+~~~~~~~~if~{\_}{\_}init{\_}{\_}:~\\
+~~~~~~~~~~~~cls.{\_}{\_}init{\_}{\_}~=~make{\_}cooperative({\_}{\_}init{\_}{\_})~\\
+~\\
+class~Base:~\\
+~~~~{\_}{\_}metaclass{\_}{\_}~=~CooperativeInit~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"B.{\_}{\_}init{\_}{\_}"~\\
+~\\
+class~C1(Base):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"C1.{\_}{\_}init{\_}{\_}"~\\
+~\\
+class~C2(Base):~\\
+~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~print~"C2.{\_}{\_}init{\_}{\_}"~\\
+~\\
+class~D(C1,~C2):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(self):~\\
+~~~~~~~~print~"D.{\_}{\_}init{\_}{\_}"~\\
+~\\
+{\#}</cooperative{\_}init.py>
+}\end{quote}
+\begin{verbatim}>>> from cooperative_init import D
+>>> d = D()
+B.__init__
+C2.__init__
+C1.__init__
+D.__init__\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclass-enhanced-modules}{}
+\pdfbookmark[2]{Metaclass-enhanced modules}{metaclass-enhanced-modules}
+\subsubsection*{Metaclass-enhanced modules}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<import{\_}with{\_}metaclass.py>~\\
+"{}"{}"~\\
+`{}`import{\_}with{\_}metaclass(metaclass,~modulepath)`{}`~generates~\\
+a~new~module~from~and~old~module,~by~enhancing~all~of~its~classes.~\\
+This~is~not~perfect,~but~it~should~give~you~a~start."{}"{}"~\\
+~\\
+import~os,~sys,~inspect,~types~\\
+~\\
+def~import{\_}with{\_}metaclass(metaclass,~modulepath):~\\
+~~~~modname~=~os.path.basename(modulepath){[}:-3]~{\#}~simplistic~\\
+~~~~mod~=~types.ModuleType(modname)~\\
+~~~~locs~=~dict(~\\
+~~~~~~~~{\_}{\_}module{\_}{\_}~=~modname,~\\
+~~~~~~~~{\_}{\_}metaclass{\_}{\_}~=~metaclass,~\\
+~~~~~~~~object~=~metaclass("object",~(),~{\{}{\}}))~\\
+~~~~execfile(modulepath,~locs)~\\
+~~~~for~k,~v~in~locs.iteritems():~\\
+~~~~~~~~if~inspect.isclass(v):~{\#}~otherwise~it~would~be~"{\_}{\_}builtin{\_}{\_}"~\\
+~~~~~~~~~~~~v.{\_}{\_}module{\_}{\_}~=~"{\_}{\_}dynamic{\_}{\_}"~\\
+~~~~~~~~setattr(mod,~k,~v)~\\
+~~~~return~mod
+}\end{quote}
+
+{\#}{\textless}/import{\_}with{\_}metaclass.py{\textgreater}
+\begin{verbatim}>>> from import_with_metaclass import import_with_metaclass
+>>> from metatracer import MetaTracer
+>>> traced_optparse = import_with_metaclass(MetaTracer,
+... "/usr/lib/python2.4/optparse.py")
+>>> op = traced_optparse.OptionParser()
+calling __dynamic__.OptionParser.__init__
+calling __dynamic__.OptionContainer.__init__
+calling __dynamic__.OptionParser._create_option_list
+calling __dynamic__.OptionContainer._create_option_mappings
+calling __dynamic__.OptionContainer.set_conflict_handler
+calling __dynamic__.OptionContainer.set_description
+calling __dynamic__.OptionParser.set_usage
+calling __dynamic__.IndentedHelpFormatter.__init__
+calling __dynamic__.HelpFormatter.__init__
+calling __dynamic__.HelpFormatter.set_parser
+calling __dynamic__.OptionParser._populate_option_list
+calling __dynamic__.OptionParser._add_help_option
+calling __dynamic__.OptionContainer.add_option
+calling __dynamic__.Option.__init__
+calling __dynamic__.Option._check_opt_strings
+calling __dynamic__.Option._set_opt_strings
+calling __dynamic__.Option._set_attrs
+calling __dynamic__.OptionContainer._check_conflict
+calling __dynamic__.OptionParser._init_parsing_state\end{verbatim}
+
+traced{\_}optparse is a dynamically generated module not leaving in the
+file system.
+
+
+%___________________________________________________________________________
+
+\hypertarget{magic-properties}{}
+\pdfbookmark[2]{Magic properties}{magic-properties}
+\subsubsection*{Magic properties}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<magicprop.py>~\\
+~\\
+class~MagicProperties(type):~\\
+~~~~def~{\_}{\_}init{\_}{\_}(cls,~name,~bases,~dic):~\\
+~~~~~~~~prop{\_}names~=~set(name{[}3:]~for~name~in~dic~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~if~name.startswith("get")~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~or~name.startswith("set"))~\\
+~~~~~~~~for~name~in~prop{\_}names:~\\
+~~~~~~~~~~~~getter~=~getattr(cls,~"get"~+~name,~None)~\\
+~~~~~~~~~~~~setter~=~getattr(cls,~"set"~+~name,~None)~\\
+~~~~~~~~~~~~setattr(cls,~name,~property(getter,~setter))~\\
+~\\
+class~Base(object):~\\
+~~~~{\_}{\_}metaclass{\_}{\_}~=~MagicProperties~\\
+~~~~def~getx(self):~\\
+~~~~~~~~return~self.{\_}x~\\
+~~~~def~setx(self,~value):~\\
+~~~~~~~~self.{\_}x~=~value~\\
+~\\
+class~Child(Base):~\\
+~~~~def~getx(self):~\\
+~~~~~~~~print~"getting~x"~\\
+~~~~~~~~return~super(Child,~self).getx()~~\\
+~~~~def~setx(self,~value):~\\
+~~~~~~~~print~"setting~x"~\\
+~~~~~~~~super(Child,~self).setx(value)~~\\
+~\\
+{\#}</magicprop.py>
+}\end{quote}
+\begin{verbatim}>>> from magicprop import Child
+>>> c = Child()
+>>> c.x = 1
+setting x
+>>> print c.x
+getting x
+1\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{hack-evil-properties}{}
+\pdfbookmark[2]{Hack: evil properties}{hack-evil-properties}
+\subsubsection*{Hack: evil properties}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<evilprop.py>~\\
+~\\
+def~convert2property(name,~bases,~d):~\\
+~~~~return~property(d.get('get'),~d.get('set'),~\\
+~~~~~~~~~~~~~~~~~~~~d.get('del'),d.get('{\_}{\_}doc{\_}{\_}'))~\\
+~\\
+class~C(object):~\\
+~~~~class~x:~\\
+~~~~~~~~"{}"{}"An~evil~test~property"{}"{}"~\\
+~~~~~~~~{\_}{\_}metaclass{\_}{\_}~=~convert2property~\\
+~~~~~~~~def~get(self):~\\
+~~~~~~~~~~~~print~'Getting~{\%}s'~{\%}~self.{\_}x~\\
+~~~~~~~~~~~~return~self.{\_}x~\\
+~~~~~~~~def~set(self,~value):~\\
+~~~~~~~~~~~~self.{\_}x~=~value~\\
+~~~~~~~~~~~~print~'Setting~to',~value~\\
+~\\
+{\#}</evilprop.py>
+}\end{quote}
+\begin{verbatim}>>> from evilprop import C
+>>> c = C()
+>>> c.x = 5
+Setting to 5
+>>> c.x
+Getting 5
+5
+>>> print C.x.__doc__
+An evil test property\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-i-suggest-not-to-use-metaclasses-in-production-code}{}
+\pdfbookmark[2]{Why I suggest not to use metaclasses in production code}{why-i-suggest-not-to-use-metaclasses-in-production-code}
+\subsubsection*{Why I suggest \emph{not} to use metaclasses in production code}
+\begin{quote}
+\begin{itemize}
+\item {}
+there are very few good use case for metaclasses in production code
+(i.e. 99{\%} of time you don't need them)
+
+\item {}
+they put a cognitive burden on the developer;
+
+\item {}
+a design without metaclasses is less magic and likely more robust;
+
+\item {}
+a design with metaclasses makes it difficult to use other metaclasses
+for debugging.
+
+\end{itemize}
+\end{quote}
+
+As far as I know, string.Template is the only metaclass-enhanced class
+in the standard library; the metaclass is used to give the possibility to
+change the defaults:
+\begin{quote}{\ttfamily \raggedright \noindent
+delimiter~=~'{\$}'~\\
+idpattern~=~r'{[}{\_}a-z]{[}{\_}a-z0-9]*'
+}\end{quote}
+
+in subclasses of Template.
+\begin{verbatim}>>> from string import Template
+>>> from metatracer import MetaTracer
+>>> class TracedTemplate(Template):
+... __metaclass__ = MetaTracer
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases\end{verbatim}
+
+Solution: use a consistent metaclass
+\begin{verbatim}>>> class GoodMeta(MetaTracer, type(Template)): pass
+...
+>>> class TracedTemplate(Template):
+... __metaclass__ = GoodMeta\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{is-there-an-automatic-way-of-solving-the-conflict}{}
+\pdfbookmark[2]{Is there an automatic way of solving the conflict?}{is-there-an-automatic-way-of-solving-the-conflict}
+\subsubsection*{Is there an automatic way of solving the conflict?}
+
+Yes, but you really need to be a metaclass wizard.
+
+\href{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197}{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197}
+\begin{verbatim}>>> from noconflict import classmaker
+>>> class TracedTemplate(Template):
+... __metaclass__ = classmaker((MetaTracer,))
+>>> print type(TracedTemplate)
+<class 'noconflict._MetaTracer_TemplateMetaclass'>\end{verbatim}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<noconflict.py>~\\
+~\\
+import~inspect,~types,~{\_}{\_}builtin{\_}{\_}~\\
+from~skip{\_}redundant~import~skip{\_}redundant~\\
+~\\
+memoized{\_}metaclasses{\_}map~=~{\{}{\}}~\\
+~\\
+{\#}~utility~function~\\
+def~remove{\_}redundant(metaclasses):~\\
+~~~skipset~=~set({[}types.ClassType])~\\
+~~~for~meta~in~metaclasses:~{\#}~determines~the~metaclasses~to~be~skipped~\\
+~~~~~~~skipset.update(inspect.getmro(meta){[}1:])~\\
+~~~return~tuple(skip{\_}redundant(metaclasses,~skipset))~\\
+~\\
+{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}~\\
+{\#}{\#}~now~the~core~of~the~module:~two~mutually~recursive~functions~{\#}{\#}~\\
+{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}~\\
+~\\
+def~get{\_}noconflict{\_}metaclass(bases,~left{\_}metas,~right{\_}metas):~\\
+~~~~"{}"{}"Not~intended~to~be~used~outside~of~this~module,~unless~you~know~\\
+~~~~what~you~are~doing."{}"{}"~\\
+~~~~{\#}~make~tuple~of~needed~metaclasses~in~specified~priority~order~\\
+~~~~metas~=~left{\_}metas~+~tuple(map(type,~bases))~+~right{\_}metas~\\
+~~~~needed{\_}metas~=~remove{\_}redundant(metas)~\\
+~\\
+~~~~{\#}~return~existing~confict-solving~meta,~if~any~\\
+~~~~if~needed{\_}metas~in~memoized{\_}metaclasses{\_}map:~\\
+~~~~~~return~memoized{\_}metaclasses{\_}map{[}needed{\_}metas]~\\
+~~~~{\#}~nope:~compute,~memoize~and~return~needed~conflict-solving~meta~\\
+~~~~elif~not~needed{\_}metas:~~~~~~~~~{\#}~wee,~a~trivial~case,~happy~us~\\
+~~~~~~~~meta~=~type~\\
+~~~~elif~len(needed{\_}metas)~==~1:~{\#}~another~trivial~case~\\
+~~~~~~~meta~=~needed{\_}metas{[}0]~\\
+~~~~{\#}~check~for~recursion,~can~happen~i.e.~for~Zope~ExtensionClasses~\\
+~~~~elif~needed{\_}metas~==~bases:~~\\
+~~~~~~~~raise~TypeError("Incompatible~root~metatypes",~needed{\_}metas)~\\
+~~~~else:~{\#}~gotta~work~...~\\
+~~~~~~~~metaname~=~'{\_}'~+~'{}'.join({[}m.{\_}{\_}name{\_}{\_}~for~m~in~needed{\_}metas])~\\
+~~~~~~~~meta~=~classmaker()(metaname,~needed{\_}metas,~{\{}{\}})~\\
+~~~~memoized{\_}metaclasses{\_}map{[}needed{\_}metas]~=~meta~\\
+~~~~return~meta~\\
+~\\
+def~classmaker(left{\_}metas=(),~right{\_}metas=()):~\\
+~~~def~make{\_}class(name,~bases,~adict):~\\
+~~~~~~~metaclass~=~get{\_}noconflict{\_}metaclass(bases,~left{\_}metas,~right{\_}metas)~\\
+~~~~~~~return~metaclass(name,~bases,~adict)~\\
+~~~return~make{\_}class~\\
+~\\
+{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}~\\
+{\#}{\#}~and~now~a~conflict-safe~replacement~for~'type'~~~~~~~~~~~~~~{\#}{\#}~~\\
+{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}{\#}~\\
+~\\
+{\_}{\_}type{\_}{\_}={\_}{\_}builtin{\_}{\_}.type~{\#}~the~aboriginal~'type'~\\
+{\#}~left~available~in~case~you~decide~to~rebind~{\_}{\_}builtin{\_}{\_}.type~\\
+~\\
+class~safetype({\_}{\_}type{\_}{\_}):~\\
+~~~~{\#}~this~is~REALLY~DEEP~MAGIC~\\
+~~~~"{}"{}"Overrides~the~`{}`{\_}{\_}new{\_}{\_}`{}`~method~of~the~`{}`type`{}`~metaclass,~making~the~\\
+~~~~generation~of~classes~conflict-proof."{}"{}"~\\
+~~~~def~{\_}{\_}new{\_}{\_}(mcl,~*args):~\\
+~~~~~~~~nargs~=~len(args)~\\
+~~~~~~~~if~nargs~==~1:~{\#}~works~as~{\_}{\_}builtin{\_}{\_}.type~\\
+~~~~~~~~~~~~return~{\_}{\_}type{\_}{\_}(args{[}0])~~\\
+~~~~~~~~elif~nargs~==~3:~{\#}~creates~the~class~using~the~appropriate~metaclass~\\
+~~~~~~~~~~~~n,~b,~d~=~args~{\#}~name,~bases~and~dictionary~\\
+~~~~~~~~~~~~meta~=~get{\_}noconflict{\_}metaclass(b,~(mcl,),~())~~\\
+~~~~~~~~~~~~if~meta~is~mcl:~{\#}~meta~is~trivial,~dispatch~to~the~default~{\_}{\_}new{\_}{\_}~\\
+~~~~~~~~~~~~~~~~return~super(safetype,~mcl).{\_}{\_}new{\_}{\_}(mcl,~n,~b,~d)~\\
+~~~~~~~~~~~~else:~{\#}~non-trivial~metaclass,~dispatch~to~the~right~{\_}{\_}new{\_}{\_}~\\
+~~~~~~~~~~~~~~~~{\#}~(it~will~take~a~second~round)~{\#}~print~mcl,~meta~\\
+~~~~~~~~~~~~~~~~return~super(mcl,~meta).{\_}{\_}new{\_}{\_}(meta,~n,~b,~d)~\\
+~~~~~~~~else:~\\
+~~~~~~~~~~~~raise~TypeError('{\%}s()~takes~1~or~3~arguments'~{\%}~mcl.{\_}{\_}name{\_}{\_})~\\
+~\\
+{\#}</noconflict.py>
+}\end{quote}
+
+\end{document}
+
diff --git a/pypers/oxford/callsupermethod.py b/pypers/oxford/callsupermethod.py
new file mode 100755
index 0000000..5565ad5
--- /dev/null
+++ b/pypers/oxford/callsupermethod.py
@@ -0,0 +1,123 @@
+class B(object):
+ def __new__(cls, *args, **kw):
+ print "B.__new__"
+ return super(B, cls).__new__(cls, *args, **kw)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ super(B, self).__init__(*args, **kw)
+
+########################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+from magicsuper import object
+
+class B(object):
+ def __new__(cls, *args, **kw):
+ print "B.__new__"
+ return callsupermethod(cls, *args, **kw)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ callsupermethod(*args, **kw)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @staticmethod
+ def sm():
+ print "B.sm"
+ @classmethod
+ def cm(cls):
+ print cls.__name__
+
+
+class C(B):
+ def __new__(cls, *args, **kw):
+ print args, kw
+ return callsupermethod(cls, *args, **kw)
+ @staticmethod
+ def sm():
+ callsupermethod()
+ @classmethod
+ def cm(cls):
+ callsupermethod()
+
+
+c = C(1, x=2)
+c.sm()
+c.cm()
diff --git a/pypers/oxford/check_overriding.py b/pypers/oxford/check_overriding.py
new file mode 100755
index 0000000..5157757
--- /dev/null
+++ b/pypers/oxford/check_overriding.py
@@ -0,0 +1,24 @@
+# check_overriding.py
+
+class Base(object):
+ a = 0
+
+class CheckOverriding(type):
+ "Prints a message if we are overriding a name."
+ def __new__(mcl, name, bases, dic):
+ for name, val in dic.iteritems():
+ if name.startswith("__") and name.endswith("__"):
+ continue # ignore special names
+ a_base_has_name = True in (hasattr(base, name) for base in bases)
+ if a_base_has_name:
+ print "AlreadyDefinedNameWarning: " + name
+ return super(CheckOverriding, mcl).__new__(mcl, name, bases, dic)
+
+class MyClass(Base):
+ __metaclass__ = CheckOverriding
+ a = 1
+
+class ChildClass(MyClass):
+ a = 2
+
+
diff --git a/pypers/oxford/chop.py b/pypers/oxford/chop.py
new file mode 100755
index 0000000..3325d52
--- /dev/null
+++ b/pypers/oxford/chop.py
@@ -0,0 +1,14 @@
+# chop.py
+
+# see also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279
+
+import itertools
+
+def chop(iterable, batchsize):
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+
diff --git a/pypers/oxford/cooperative_init.py b/pypers/oxford/cooperative_init.py
new file mode 100755
index 0000000..0118115
--- /dev/null
+++ b/pypers/oxford/cooperative_init.py
@@ -0,0 +1,41 @@
+# cooperative_init.py
+
+"""Given a hierarchy, makes __init__ cooperative.
+The only change needed is to add a line
+
+ __metaclass__ = CooperativeInit
+
+to the base class of your hierarchy."""
+
+from decorators import decorator
+
+class CooperativeInit(type):
+ def __init__(cls, name, bases, dic):
+
+ @decorator
+ def make_cooperative(__init__, self, *args, **kw):
+ super(cls, self).__init__(*args, **kw)
+ __init__(self, *args, **kw)
+
+ __init__ = dic.get("__init__")
+ if __init__:
+ cls.__init__ = make_cooperative(__init__)
+
+class Base:
+ __metaclass__ = CooperativeInit
+ def __init__(self):
+ print "B.__init__"
+
+class C1(Base):
+ def __init__(self):
+ print "C1.__init__"
+
+class C2(Base):
+ def __init__(self):
+ print "C2.__init__"
+
+class D(C1, C2):
+ def __init__(self):
+ print "D.__init__"
+
+
diff --git a/pypers/oxford/cript_user.py b/pypers/oxford/cript_user.py
new file mode 100755
index 0000000..f17a903
--- /dev/null
+++ b/pypers/oxford/cript_user.py
@@ -0,0 +1,17 @@
+# cript_user.py
+
+from crypt import crypt
+
+def cryptedAttribute(seed):
+ def get(self):
+ return getattr(self, "_pw", None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+class User(object):
+ pw = cryptedAttribute("a")
+ def __init__(self, un, pw):
+ self.un, self.pw = un, pw
+
+
diff --git a/pypers/oxford/crypt_user.py b/pypers/oxford/crypt_user.py
new file mode 100755
index 0000000..ce23b6a
--- /dev/null
+++ b/pypers/oxford/crypt_user.py
@@ -0,0 +1,20 @@
+# crypt_user.py
+
+class User(object):
+ def __init__(self, username, password):
+ self.username, self.password = username, password
+
+
+
+from crypt import crypt
+
+def cryptedAttribute(seed="x"):
+ def get(self):
+ return getattr(self, "_pw", None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+User.password = cryptedAttribute()
+
+
diff --git a/pypers/oxford/custom_iterable.py b/pypers/oxford/custom_iterable.py
new file mode 100755
index 0000000..d4c9aa0
--- /dev/null
+++ b/pypers/oxford/custom_iterable.py
@@ -0,0 +1,9 @@
+# custom_iterable.py
+class MyIter(object):
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+it = MyIter(); # print it, iter(it)
+
diff --git a/pypers/oxford/data.txt b/pypers/oxford/data.txt
new file mode 100755
index 0000000..d0e1ec9
--- /dev/null
+++ b/pypers/oxford/data.txt
@@ -0,0 +1,3 @@
+value1
+value2
+END
diff --git a/pypers/oxford/dec.py b/pypers/oxford/dec.py
new file mode 100755
index 0000000..1220550
--- /dev/null
+++ b/pypers/oxford/dec.py
@@ -0,0 +1,25 @@
+class with_attrs(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ def __call__(self, func):
+ print "the decorator was called with %s" % self.kw
+ def wrapped_func():
+ print "%s was called" % func.__name__
+ return func()
+ wrapped_func.__dict__.update(self.kw)
+ return wrapped_func
+
+@with_attrs(author="M.S.", date="2005-04-19")
+def long_named_function():
+ print "do something"
+ return "ok"
+
+#long_named_function = decorator(long_named_function)
+
+
+print long_named_function.author
+print long_named_function.date
+
+print "-" * 77
+
+print long_named_function()
diff --git a/pypers/oxford/decorate.py b/pypers/oxford/decorate.py
new file mode 100755
index 0000000..a5663a3
--- /dev/null
+++ b/pypers/oxford/decorate.py
@@ -0,0 +1,3 @@
+# decorate.py
+
+
diff --git a/pypers/oxford/decorators.py b/pypers/oxford/decorators.py
new file mode 100755
index 0000000..d393658
--- /dev/null
+++ b/pypers/oxford/decorators.py
@@ -0,0 +1,104 @@
+# decorators.py
+
+import inspect, itertools
+
+def getinfo(func):
+ """Return an info dictionary containing:
+ - name (the name of the function : str)
+ - argnames (the names of the arguments : list)
+ - defarg (the values of the default arguments : list)
+ - fullsign (the full signature : str)
+ - shortsign (the short signature : str)
+ - arg0 ... argn (shortcuts for the names of the arguments)
+
+ >> def f(self, x=1, y=2, *args, **kw): pass
+
+ >> info = getinfo(f)
+
+ >> info["name"]
+ 'f'
+ >> info["argnames"]
+ ['self', 'x', 'y', 'args', 'kw']
+
+ >> info["defarg"]
+ (1, 2)
+
+ >> info["shortsign"]
+ 'self, x, y, *args, **kw'
+
+ >> info["fullsign"]
+ 'self, x=defarg[0], y=defarg[1], *args, **kw'
+
+ >> info["arg0"], info["arg1"], info["arg2"], info["arg3"], info["arg4"]
+ ('self', 'x', 'y', 'args', 'kw')
+ """
+ assert inspect.ismethod(func) or inspect.isfunction(func)
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+ argnames = list(regargs)
+ if varargs: argnames.append(varargs)
+ if varkwargs: argnames.append(varkwargs)
+ counter = itertools.count()
+ fullsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "=defarg[%i]" % counter.next())[1:-1]
+ shortsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "")[1:-1]
+ dic = dict(("arg%s" % n, name) for n, name in enumerate(argnames))
+ dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
+ fullsign = fullsign, defarg = func.func_defaults or ())
+ return dic
+
+def _contains_reserved_names(dic): # helper
+ return "_call_" in dic or "_func_" in dic
+
+def _decorate(func, caller):
+ """Takes a function and a caller and returns the function
+ decorated with that caller. The decorated function is obtained
+ by evaluating a lambda function with the correct signature.
+ """
+ infodict = getinfo(func)
+ assert not _contains_reserved_names(infodict["argnames"]), \
+ "You cannot use _call_ or _func_ as argument names!"
+ execdict=dict(_func_=func, _call_=caller, defarg=func.func_defaults or ())
+ if func.__name__ == "<lambda>":
+ lambda_src = "lambda %(fullsign)s: _call_(_func_, %(shortsign)s)" \
+ % infodict
+ dec_func = eval(lambda_src, execdict)
+ else:
+ func_src = """def %(name)s(%(fullsign)s):
+ return _call_(_func_, %(shortsign)s)""" % infodict
+ exec func_src in execdict
+ dec_func = execdict[func.__name__]
+ dec_func.__doc__ = func.__doc__
+ dec_func.__dict__ = func.__dict__
+ return dec_func
+
+class decorator(object):
+ """General purpose decorator factory: takes a caller function as
+input and returns a decorator. A caller function is any function like this::
+
+ def caller(func, *args, **kw):
+ # do something
+ return func(*args, **kw)
+
+Here is an example of usage:
+
+ >> @decorator
+ .. def chatty(f, *args, **kw):
+ .. print "Calling %r" % f.__name__
+ .. return f(*args, **kw)
+
+ >> @chatty
+ .. def f(): pass
+ ..
+ >> f()
+ Calling 'f'
+ """
+ def __init__(self, caller):
+ self.caller = caller
+ def __call__(self, func):
+ return _decorate(func, self.caller)
+
+
+
diff --git a/pypers/oxford/defaultdict.py b/pypers/oxford/defaultdict.py
new file mode 100755
index 0000000..ce14508
--- /dev/null
+++ b/pypers/oxford/defaultdict.py
@@ -0,0 +1,21 @@
+
+def defaultdict(default, dictclass=dict):
+ class defdict(dictclass):
+ def __getitem__(self, key):
+ try:
+ return super(defdict, self).__getitem__(key)
+ except KeyError:
+ return self.setdefault(key, default)
+ return defdict
+
+d = defaultdict(0)()
+d["x"] += 1
+d["x"] += 1
+d["y"] += 1
+print d
+
+d = defaultdict(list())()
+d["x"].append(1)
+d["x"].append(2)
+d["y"].append(1)
+print d
diff --git a/pypers/oxford/defaultdict2.py b/pypers/oxford/defaultdict2.py
new file mode 100755
index 0000000..cf77b4c
--- /dev/null
+++ b/pypers/oxford/defaultdict2.py
@@ -0,0 +1,20 @@
+def defaultdict(defaultfactory, dictclass=dict):
+ class defdict(dictclass):
+ def __getitem__(self, key):
+ try:
+ return super(defdict, self).__getitem__(key)
+ except KeyError:
+ return self.setdefault(key, defaultfactory())
+ return defdict
+
+d = defaultdict(int)()
+d["x"] += 1
+d["x"] += 1
+d["y"] += 1
+print d
+
+d = defaultdict(list)()
+d["x"].append(1)
+d["x"].append(2)
+d["y"].append(1)
+print d
diff --git a/pypers/oxford/deferred.py b/pypers/oxford/deferred.py
new file mode 100755
index 0000000..653421e
--- /dev/null
+++ b/pypers/oxford/deferred.py
@@ -0,0 +1,21 @@
+# deferred.py
+"Deferring the execution of a procedure (function returning None)"
+
+import threading
+from decorators import decorator
+
+def deferred(nsec):
+ def call_later(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(call_later)
+
+@deferred(2)
+def hello():
+ print "hello"
+
+if __name__ == "__main__":
+ hello()
+ print "Calling hello() ..."
+
+
+
diff --git a/pypers/oxford/descriptor.py b/pypers/oxford/descriptor.py
new file mode 100755
index 0000000..4b7c6a9
--- /dev/null
+++ b/pypers/oxford/descriptor.py
@@ -0,0 +1,43 @@
+# descriptor.py
+
+
+class AttributeDescriptor(object):
+ def __get__(self, obj, cls=None):
+ if obj is None and cls is None:
+ raise TypeError("__get__(None, None) is invalid")
+ elif obj is None:
+ return self.get_from_class(cls)
+ else:
+ return self.get_from_obj(obj)
+ def get_from_class(self, cls):
+ print "Getting %s from %s" % (self, cls)
+ def get_from_obj(self, obj):
+ print "Getting %s from %s" % (self, obj)
+
+
+class Staticmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func
+ get_from_obj = get_from_class
+
+
+class Classmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func.__get__(cls, type(cls))
+ def get_from_obj(self, obj):
+ return self.get_from_class(obj.__class__)
+
+class C(object):
+ s = Staticmethod(lambda : 1)
+ c = Classmethod(lambda cls : cls.__name__)
+
+c = C()
+
+assert C.s() == c.s() == 1
+assert C.c() == c.c() == "C"
+
+
diff --git a/pypers/oxford/dictmixin.py b/pypers/oxford/dictmixin.py
new file mode 100755
index 0000000..d91a9f2
--- /dev/null
+++ b/pypers/oxford/dictmixin.py
@@ -0,0 +1,13 @@
+# dictmixin.py
+
+from metatracer import MetaTracer
+from UserDict import DictMixin
+
+class TracedDM(DictMixin, object):
+ __metaclass__ = MetaTracer
+ def __getitem__(self, item):
+ return item
+ def keys(self):
+ return [1,2,3]
+
+
diff --git a/pypers/oxford/dictwrapper.py b/pypers/oxford/dictwrapper.py
new file mode 100755
index 0000000..7ee8daa
--- /dev/null
+++ b/pypers/oxford/dictwrapper.py
@@ -0,0 +1,7 @@
+# dictwrapper.py
+
+class DictWrapper(object):
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+
diff --git a/pypers/oxford/doctest_talk/Makefile b/pypers/oxford/doctest_talk/Makefile
new file mode 100755
index 0000000..77a6cc0
--- /dev/null
+++ b/pypers/oxford/doctest_talk/Makefile
@@ -0,0 +1,5 @@
+talk: talk.txt
+ python2.4 maketalk.py talk.txt
+test: P01.html
+ tidy P01.html > /dev/null 2> x.txt; less x.txt
+
diff --git a/pypers/oxford/doctest_talk/P01.html b/pypers/oxford/doctest_talk/P01.html
new file mode 100755
index 0000000..a3a9e4d
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P01.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P01</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P02.html'>Next</a></td> <td bgcolor="lightblue"><a href='P25.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P01</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Automatic testing in Python: wonderful doctest!</h1><br/>
+
+<center>
+
+ ACCU Conference 2005 <br/> <br/>
+
+ 22 Apr 2005 <br/> <br/>
+
+ Michele Simionato <br/> <br/>
+
+ michele.simionato@gmail.com <br/> <br/>
+
+</center></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P02.html b/pypers/oxford/doctest_talk/P02.html
new file mode 100755
index 0000000..b187e50
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P02.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P02</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>Next</a></td> <td bgcolor="lightblue"><a href='P01.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P02</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Summary</h1><br/>
+
+<ul>
+ <li> What is automatic testing? </li>
+ <li> Why automatic testing is better? </li>
+ <li> Which kind of automatic testing? </li>
+ <li> How does it work, in practice? </li>
+ <li> What's the message?</li>
+<ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P03.html b/pypers/oxford/doctest_talk/P03.html
new file mode 100755
index 0000000..dbfc5f8
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P03.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P03</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P04.html'>Next</a></td> <td bgcolor="lightblue"><a href='P02.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P03</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>What is automatic testing</h1><br/>
+
+Any methodology that allows you to test
+your application mechanically, repeatedly
+and in a <em>controlled reproducible</em> way.</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P04.html b/pypers/oxford/doctest_talk/P04.html
new file mode 100755
index 0000000..b05ff1a
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P04.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P04</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>Next</a></td> <td bgcolor="lightblue"><a href='P03.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P04</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Automatic testing is better (1)</h1><br/>
+
+When doing manual testing typically you spend
+
+<center><h2>
+
+ 1 hour of coding + 10 hours of testing/debugging
+
+</center></h2>
+
+on the other hand ...</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P05.html b/pypers/oxford/doctest_talk/P05.html
new file mode 100755
index 0000000..62b628a
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P05.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P05</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P06.html'>Next</a></td> <td bgcolor="lightblue"><a href='P04.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P05</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Automatic testing is better (2)</h1><br/>
+
+... when doing automatic testing typically you spend
+
+<br/> <br/>
+<center><h2>
+
+ 1 hour of coding + 10 hours of testing/debugging !
+
+</center></h2></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P06.html b/pypers/oxford/doctest_talk/P06.html
new file mode 100755
index 0000000..f4a18cb
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P06.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P06</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>Next</a></td> <td bgcolor="lightblue"><a href='P05.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P06</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>However ...</h1><br/>
+
+Think about six month later!
+ <br/><br/>
+<center><em>
+
+ there is a difference</em>
+
+ <h2><u>Refactoring!</u><h2>
+
+</center></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P07.html b/pypers/oxford/doctest_talk/P07.html
new file mode 100755
index 0000000..b6c31ae
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P07.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P07</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P08.html'>Next</a></td> <td bgcolor="lightblue"><a href='P06.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P07</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Automatic testing in Python</h1><br/>
+
+There are two standard testing frameworks in Python:
+
+<ol>
+ <li> unittest </li>
+ <li> doctest </li>
+</ol>
+
+Which one should I use?</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P08.html b/pypers/oxford/doctest_talk/P08.html
new file mode 100755
index 0000000..6c6c43e
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P08.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P08</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>Next</a></td> <td bgcolor="lightblue"><a href='P07.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P08</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Well,</h1><br/>
+
+since my talk has <em>doctest</em> in the title ...
+
+ ;-)</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P09.html b/pypers/oxford/doctest_talk/P09.html
new file mode 100755
index 0000000..0e92531
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P09.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P09</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P10.html'>Next</a></td> <td bgcolor="lightblue"><a href='P08.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P09</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>More seriously ...</h1><br/>
+
+Use different testing frameworks; each one has advantages
+and disadvantages; use combinations of them; invent your
+own testing procedure.
+
+I use combinations of
+
+<ul>
+ <li> unittest </li>
+ <li> doctest </li>
+ <li> custom tests </li>
+ <li> Makefile driven tests </li>
+ <li> et al. </li>
+</ul>
+
+doctest emphasis is on <em>documentation</em></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P10.html b/pypers/oxford/doctest_talk/P10.html
new file mode 100755
index 0000000..19c6e36
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P10.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P10</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>Next</a></td> <td bgcolor="lightblue"><a href='P09.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P10</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>What is doctest?</h1><br/>
+
+In its simplest form (which I do not use that much) doctest allows
+you to include tests in the docstrings of your application.</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P11.html b/pypers/oxford/doctest_talk/P11.html
new file mode 100755
index 0000000..e838716
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P11.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P11</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P12.html'>Next</a></td> <td bgcolor="lightblue"><a href='P10.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P11</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Example</h1><br/>
+<pre># split.py
+import re
+SEP = re.compile(r"\s*[,;]\s*")
+
+def split(text):
+ """Split a string taking as separators "," ";".
+ Example:
+ >>> from split import split
+ >>> split("hello, world!; welcome to PyUK!")
+ ['hello', 'world!', 'welcome to PyUK!']
+ """
+ return SEP.split(text)
+
+if __name__ == "__main__":
+ import doctest; doctest.testmod()
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P12.html b/pypers/oxford/doctest_talk/P12.html
new file mode 100755
index 0000000..230eb27
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P12.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P12</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>Next</a></td> <td bgcolor="lightblue"><a href='P11.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P12</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Running doctest in verbose mode</h1><br/>
+
+<pre>
+$ python split.py -v
+Running __main__.__doc__
+0 of 0 examples failed in __main__.__doc__
+Running __main__.split.__doc__
+Trying: from split import split
+Expecting: nothing
+ok
+Trying: split("hello, world!; welcome to Oxford!")
+Expecting: ['hello', 'world!', 'welcome to Oxford!']
+ok
+0 of 2 examples failed in __main__.split.__doc__
+1 items had no tests:
+ __main__
+1 items passed all tests:
+ 2 tests in __main__.split
+2 tests in 2 items.
+2 passed and 0 failed.
+Test passed.
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P13.html b/pypers/oxford/doctest_talk/P13.html
new file mode 100755
index 0000000..b2fe415
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P13.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P13</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P14.html'>Next</a></td> <td bgcolor="lightblue"><a href='P12.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P13</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Why I do not use the docstring approach</h1><br/>
+
+<ul>
+<li> It makes you end up with very large docstrings</li>
+
+<li> It abuses the original purpose of docstrings</li>
+
+<li> It conflates two different aspects (code and tests on the code)</li>
+
+<li> It is much easier to write the documentation in a separate
+ text file </li>
+
+<li> Testing should be done by an external tool anyway </li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P14.html b/pypers/oxford/doctest_talk/P14.html
new file mode 100755
index 0000000..5c16abf
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P14.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P14</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>Next</a></td> <td bgcolor="lightblue"><a href='P13.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P14</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>How I use doctest</h1><br/>
+
+I hacked inside doctest and wrote a custom utility
+to extract doctests from documentation files since
+
+<ul>
+ <li>I like keeping the documentation on a separate rst file</li>
+
+ <li>there is no sync problem since you run the tests all the time</li>
+
+ <li>it is useful for writing articles ...</li>
+
+ <li> ... but also documentation for internal usage in the company</li>
+</ul>
+
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052
+
+<pre>
+$ python -m doctester < split.txt
+doctest: run 4 tests, failed 0
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P15.html b/pypers/oxford/doctest_talk/P15.html
new file mode 100755
index 0000000..a6843e3
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P15.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P15</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P16.html'>Next</a></td> <td bgcolor="lightblue"><a href='P14.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P15</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Testing the doctester frontend</h1><br/>
+
+<pre>
+>>> from ms.webtester import start_server, stop_server
+>>> from ms.http_utils import urlopen
+>>> baseurl = "http://localhost:7080/"
+>>> home = "/home/micheles/md/python/quixote/"
+
+>>> start_server(home + "doctester_frontend.py")
+>>> import time; time.sleep(2) # wait a bit
+
+Making a POST:
+
+>>> res = urlopen(baseurl, dict(txt=">>> 1 + 1\n2")).read()
+>>> assert "tests" in res
+>>> stop_server()
+
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P16.html b/pypers/oxford/doctest_talk/P16.html
new file mode 100755
index 0000000..d5b4541
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P16.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P16</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>Next</a></td> <td bgcolor="lightblue"><a href='P15.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P16</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Managing exceptions</h1><br/>
+
+It is possible to test that your program raises the exception you
+expect:
+
+<pre>
+
+$ echo "# split cannot work on a list
+>>> from split import split
+>>> split([])
+Traceback (most recent call last):
+ ...
+TypeError: expected string or buffer
+" > x.txt
+
+$ doct x.txt
+x.txt: 2 tests passed in 0.01 seconds
+
+</pre>
+
+(notice however that relying on exception messages may be risky)</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P17.html b/pypers/oxford/doctest_talk/P17.html
new file mode 100755
index 0000000..da93de5
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P17.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P17</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P18.html'>Next</a></td> <td bgcolor="lightblue"><a href='P16.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P17</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>When tests fail</h1><br/>
+
+<pre>
+
+$ cat split-failure.txt
+An example of failed text:
+
+>>> from split import split
+>>> split("hello, world")
+['hello', ' world']
+
+$ doct split-failure.txt
+*****************************************************************
+Failure in example: split("hello, world")
+from line #5 of split-failure.txt
+Expected: ['hello', ' world']
+Got: ['hello', 'world']
+
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P18.html b/pypers/oxford/doctest_talk/P18.html
new file mode 100755
index 0000000..0acc512
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P18.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P18</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>Next</a></td> <td bgcolor="lightblue"><a href='P17.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P18</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Converting doctests to unittests</h1><br/>
+
+<pre>
+ import unittest
+ import doctest
+ import my_module_with_doctests
+
+ suite = doctest.DocTestSuite(my_module_with_doctests)
+ runner = unittest.TextTestRunner()
+ runner.run(suite)
+</pre>
+
+<h2>For Python 2.3+<h2></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P19.html b/pypers/oxford/doctest_talk/P19.html
new file mode 100755
index 0000000..744a43b
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P19.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P19</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P20.html'>Next</a></td> <td bgcolor="lightblue"><a href='P18.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P19</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>doctest is becoming even better</h1><br/>
+
+With Python 2.4 you can run doctests on external text files:
+
+<pre>
+ import doctest, unittest
+ doctest.testfile(my_documentation_file, package=mypackage)
+</pre>
+
+you can also convert these doctests into unittests:
+
+<pre>
+ import doctest, unittest
+ suite = doctest.DocFileSuite(my_documentation_file, package=mypackage)
+ unittest.TextTestRunner().run(suite)
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P20.html b/pypers/oxford/doctest_talk/P20.html
new file mode 100755
index 0000000..8aea60d
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P20.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P20</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>Next</a></td> <td bgcolor="lightblue"><a href='P19.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P20</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Python 2.4 recognizes blank lines</h1><br/>
+
+Blank lines can be marked with &lt;BLANKLINE&gt; :
+<pre>
+>>> print 'foo\n\nbar\n'
+foo
+&lt;BLANKLINE&gt;
+bar
+&lt;BLANKLINE&gt;
+
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P21.html b/pypers/oxford/doctest_talk/P21.html
new file mode 100755
index 0000000..34a3aa5
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P21.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P21</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P22.html'>Next</a></td> <td bgcolor="lightblue"><a href='P20.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P21</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Python 2.4 recognizes flags!</h1><br/>
+
+<ul>
+<li> If the ellipsis flag is used, then '...' can be used to
+ elide substrings in the desired output: <pre>
+>>> print range(1000) #doctest: +ELLIPSIS
+[0, 1, 2, ..., 999]
+
+</pre></li>
+
+<li>
+ If the whitespace normalization flag is used, then
+ differences in whitespace are ignored.<pre>
+>>> print range(20) #doctest: +NORMALIZE_WHITESPACE
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+12, 13, 14, 15, 16, 17, 18, 19]
+
+</pre></li>
+
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P22.html b/pypers/oxford/doctest_talk/P22.html
new file mode 100755
index 0000000..929bbb5
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P22.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P22</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>Next</a></td> <td bgcolor="lightblue"><a href='P21.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P22</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Zope experience</h1><br/>
+
+Literal quote from the PyCON doctest talk:
+
+<ul>
+<li> ~ 5600 tests (~3500 in Zope 3, ~1300 in ZODB, ~800 in Zope 2)</li>
+<li> we wrote lots of tests before we knew what we were doing</li>
+<li> debugging failed tests is really hard when intent is unclear</li>
+<li> often refactor or reimplement tests to make them clearer</li>
+<li> most new tests are doctest based</li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P23.html b/pypers/oxford/doctest_talk/P23.html
new file mode 100755
index 0000000..4ab3427
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P23.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P23</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P24.html'>Next</a></td> <td bgcolor="lightblue"><a href='P22.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P23</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Conclusion (1): good reasons to use doctest</h1><br/>
+
+<quote>
+"Test coverage is important, but test readability is much more important"
+</quote>
+
+<em>-- Tim Peters and Jim Fulton</em> <br/> <br/>
+
+doctest is good since:
+
+<ol>
+ <li> it is easy to understand, to explain and to use </li>
+
+ <li> it makes you improve the quality of your documentation </li>
+
+ <li> it can be converted to unittest anyway </li>
+
+</ol></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P24.html b/pypers/oxford/doctest_talk/P24.html
new file mode 100755
index 0000000..276299f
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P24.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P24</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Next</a></td> <td bgcolor="lightblue"><a href='P23.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P24</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Conclusion (2): the message of this talk</h1><br/>
+
+Automatic testing is good for tons of practical reasons, but also
+because:
+
+<ol>
+
+<li>It teaches you <em>discipline</em> </li>
+
+<li>It makes you
+ <em>think differently</em> </li>
+
+<li>It is more <em>fun!</em> </li>
+
+</ol></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P25.html b/pypers/oxford/doctest_talk/P25.html
new file mode 100755
index 0000000..daae306
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P25.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P25</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>Next</a></td> <td bgcolor="lightblue"><a href='P24.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P25</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>References</h1><br/>
+
+<ul>
+
+<li>The standard library documentation
+http://docs.python.org/lib/module-doctest.html </li>
+
+<li> The doctest talk by Tim Peters and Jim Fulton
+http://www.python.org/pycon/dc2004/papers/4/</li>
+
+<li> doctest.py <em>(use the source, Luke!)</em></li>
+</ul>
+</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/P26.html b/pypers/oxford/doctest_talk/P26.html
new file mode 100755
index 0000000..cb376d4
--- /dev/null
+++ b/pypers/oxford/doctest_talk/P26.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P26</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><small>
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><img src = "accu2005.png" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>Next</a></td> <td bgcolor="lightblue"><a href='P25.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P26.html'>Last</a></td> <td bgcolor="lightblue"><a href='P01.html'>First</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">Current</td> <td bgcolor="lightblue">P26</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>P01</a></td> <td bgcolor="lightblue"><a href='P02.html'>P02</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</a></td> <td bgcolor="lightblue"><a href='P04.html'>P04</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</a></td> <td bgcolor="lightblue"><a href='P06.html'>P06</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</a></td> <td bgcolor="lightblue"><a href='P08.html'>P08</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</a></td> <td bgcolor="lightblue"><a href='P10.html'>P10</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</a></td> <td bgcolor="lightblue"><a href='P12.html'>P12</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</a></td> <td bgcolor="lightblue"><a href='P14.html'>P14</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</a></td> <td bgcolor="lightblue"><a href='P16.html'>P16</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P17.html'>P17</a></td> <td bgcolor="lightblue"><a href='P18.html'>P18</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P19.html'>P19</a></td> <td bgcolor="lightblue"><a href='P20.html'>P20</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P21.html'>P21</a></td> <td bgcolor="lightblue"><a href='P22.html'>P22</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P23.html'>P23</a></td> <td bgcolor="lightblue"><a href='P24.html'>P24</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P25.html'>P25</a></td> <td bgcolor="lightblue"><a href='P26.html'>P26</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>References</h1><br/>
+
+<ul>
+
+<li>The standard library documentation
+http://docs.python.org/lib/module-doctest.html </li>
+
+<li> The doctest talk by Tim Peters and Jim Fulton
+http://www.python.org/pycon/dc2004/papers/4/</li>
+
+<li> doctest.py <em>(use the source, Luke!)</em></li>
+</ul>
+</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/oxford/doctest_talk/abstract.txt b/pypers/oxford/doctest_talk/abstract.txt
new file mode 100755
index 0000000..e671aae
--- /dev/null
+++ b/pypers/oxford/doctest_talk/abstract.txt
@@ -0,0 +1,11 @@
+Automatic testing proved extremely effective in
+helping code design, improving code reliability,
+and enabling code refactoring.
+Therefore, most modern languages provide a
+standard testing framework.
+Python is no exception and provide a powerful
+Smalltalk/Java-inspired unittest framework.
+However, Python also provides a *second*, less known
+but arguably *better*, testing framework called doctest..
+In this talk I will show to the audience the wonders of
+doctest, explaining why it is so cool and deserving attention.
diff --git a/pypers/oxford/doctest_talk/doct_pkg.py b/pypers/oxford/doctest_talk/doct_pkg.py
new file mode 100755
index 0000000..331fcca
--- /dev/null
+++ b/pypers/oxford/doctest_talk/doct_pkg.py
@@ -0,0 +1,22 @@
+import test_pkg, doctest, os
+
+import sys
+from ms.file_utils import ifiles
+
+pkg = __import__("test_pkg")
+
+pkg_name = pkg.__name__
+csd = os.path.dirname(test_pkg.__path__[0]) # current search directory
+os.chdir(csd)
+
+print "Testing package", pkg_name
+
+total_fail, total_ok = 0, 0
+for f in ifiles(pkg_name, lambda f: f.endswith(".py"), abs_path=True):
+ f = f[:-3].replace("/", ".")
+ fail, ok = doctest.testmod(__import__(f, globals(), locals(), [f]))
+ total_fail += fail
+ total_ok += ok
+print "Failed %s, passed %s" % (total_fail, total_ok)
+# doctest.testmod(test_pkg) # only tests __init__
+# doctest.run_docstring_examples(test_pkg, globals()) # idem
diff --git a/pypers/oxford/doctest_talk/doctester_frontend.py b/pypers/oxford/doctest_talk/doctester_frontend.py
new file mode 100755
index 0000000..0a1acc7
--- /dev/null
+++ b/pypers/oxford/doctest_talk/doctester_frontend.py
@@ -0,0 +1,44 @@
+from ms.quixote_utils_exp import Website, htmlpage, FormPage
+from doctester import runtests
+from StringIO import StringIO
+import sys
+
+class DoctestPage(FormPage):
+ form_config = """\
+ [text]
+ name: txt
+ title: Doctester input
+
+ [checkbox]
+ name: verbose
+ title: Verbose
+
+ [submit]
+ name: test
+ value: test!"""
+
+ @htmlpage()
+ def exitpage(self):
+ if self.form["verbose"]:
+ yield "<pre>%s</pre>" % self.out.getvalue()
+ else:
+ yield "%(tests)s tests, %(fail)s failed" % vars(self)
+
+ @htmlpage()
+ def errorpage(self):
+ yield self.form.get_widget('txt').error
+ yield "<pre>%s</pre>" % self.out.getvalue()
+
+ def checker(self):
+ sys.stdout_orig = sys.stdout
+ sys.stdout = self.out = StringIO()
+ txt, verbose = self.form["txt"] or "", self.form["verbose"]
+ self.fail, self.tests = runtests(txt, verbose=verbose)
+ sys.stdout = sys.stdout_orig
+ if self.fail:
+ self.form.set_error("txt", "Doctester error")
+
+publisher = Website(_q_index=DoctestPage("doctester")).publisher()
+
+if __name__ == "__main__":
+ publisher.run_show(port=7080)
diff --git a/pypers/oxford/doctest_talk/doctester_frontend.txt b/pypers/oxford/doctest_talk/doctester_frontend.txt
new file mode 100755
index 0000000..72233d4
--- /dev/null
+++ b/pypers/oxford/doctest_talk/doctester_frontend.txt
@@ -0,0 +1,23 @@
+A simple example of how to doctest a Web application
+--------------------------------------------------------
+
+A few imports and settings:
+
+>>> from ms.webtester import start_server, stop_server
+>>> from ms.http_utils import urlopen
+>>> baseurl = "http://localhost:7080/"
+>>> server = "/home/micheles/md/python/quixote/doctester_frontend.py"
+
+Starting the server:
+
+>>> start_server(server)
+>>> import time; time.sleep(2) # wait a bit
+
+Making a POST:
+
+>>> res = urlopen(baseurl, dict(txt=">>> 1 + 1\n2")).read()
+>>> assert "tests" in res
+
+We are done:
+
+>>> stop_server()
diff --git a/pypers/oxford/doctest_talk/ex24.py b/pypers/oxford/doctest_talk/ex24.py
new file mode 100755
index 0000000..ce45cf8
--- /dev/null
+++ b/pypers/oxford/doctest_talk/ex24.py
@@ -0,0 +1,17 @@
+"""
+>>> print "Hello, World!"
+Hello, World!
+
+>>> print "Hello World! 2" #doctest: +ELLIPSIS
+Hello ... 2
+>>> print range(1000) #doctest: +ELLIPSIS
+[0, 1, 2, ..., 999]
+
+>>> print "ciao come va nina?" #doctest: +ELLIPSIS
+ciao ... nina?
+
+"""
+
+if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
diff --git a/pypers/oxford/doctest_talk/ex_inner.py b/pypers/oxford/doctest_talk/ex_inner.py
new file mode 100755
index 0000000..b59dc3d
--- /dev/null
+++ b/pypers/oxford/doctest_talk/ex_inner.py
@@ -0,0 +1,16 @@
+# split.py
+import re
+
+def outer():
+ def split(text, sep = re.compile(r"\s*[,;]\s*")):
+ """Split a string taking as separators "," ";".
+ Example:
+ >>> from split import split
+ >>> split("hello, world!; welcome to the Italian Code Jam!")
+ ['hello', 'world!', 'welcome to the Italian Code Jam!']
+ """
+ return sep.split(text)
+
+if __name__ == "__main__":
+ import __main__, doctest
+ doctest.testmod(__main__)
diff --git a/pypers/oxford/doctest_talk/index.html b/pypers/oxford/doctest_talk/index.html
new file mode 100755
index 0000000..870e229
--- /dev/null
+++ b/pypers/oxford/doctest_talk/index.html
@@ -0,0 +1,16 @@
+<?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.3.4: http://docutils.sourceforge.net/" />
+<title>Partecs Training: Internal Documentation</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<h1 class="title">Partecs Training: Internal Documentation</h1>
+<div class="document" id="partecs-training-internal-documentation">
+<p><a class="reference" href="http://wiki.partecs.com/Developers/PartecsTraining/P01.html">Michele's slides for the Italian Code Jam conference</a></p>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/doctest_talk/index.txt b/pypers/oxford/doctest_talk/index.txt
new file mode 100755
index 0000000..8988e3e
--- /dev/null
+++ b/pypers/oxford/doctest_talk/index.txt
@@ -0,0 +1,6 @@
+Partecs Training: Internal Documentation
+=========================================
+
+`Michele's slides for the Italian Code Jam conference`__
+
+__ http://wiki.partecs.com/Developers/PartecsTraining/P01.html
diff --git a/pypers/oxford/doctest_talk/maketalk.py b/pypers/oxford/doctest_talk/maketalk.py
new file mode 100755
index 0000000..abd3394
--- /dev/null
+++ b/pypers/oxford/doctest_talk/maketalk.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+import exc_debugger, webbrowser
+from ms.html_utils import makelink, TableList, tidy
+import os, sys, re
+INCLUDE = re.compile(r"\n.. include::\s+([\w\./]+)")
+
+BGCOLOR = "lightblue"
+CSS = """
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+"""
+
+class HTMLDocument(list):
+ def __init__(self, ls = []):
+ super(HTMLDocument, self).__init__(ls)
+ self.reorder()
+ def reorder(self):
+ """Generates circular 'prev' and 'next' tabs."""
+ self.npages = len(self)
+ self.pageindex = []
+ for i, page in enumerate(self):
+ page.prev = self[i-1].namext
+ if i == self.npages-1: i = -1
+ page.next = self[i+1].namext
+ page.first = self[0].namext
+ page.last = self[-1].namext
+ page.document = self
+ self.pageindex.append(page)
+
+class HTMLPage(object):
+ def __init__(self, id_txt):
+ self.BGCOLOR = BGCOLOR
+ id, txt = id_txt
+ if isinstance(id, int):
+ self.name = "P%02d" % (id + 1)
+ else:
+ self.name = id
+ self.namext = self.name + ".html"
+ lines = txt.splitlines()
+ if lines: # empty page
+ self.title = lines[0]
+ self.txt = "\n".join(lines[2:])
+ else:
+ self.title = ""
+ self.txt = ""
+ self.txt = "<h1>%s</h1><br/>\n" % self.title + \
+ INCLUDE.sub(lambda m: "<pre>%s</pre>" %
+ file(m.group(1)).read(), self.txt)
+ self.head = """
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>%s</title>
+ %s
+ </head>""" % (self.name, CSS)
+ def html(self):
+ self.body = """\n<body bgcolor="%(BGCOLOR)s">\n
+ %(txt)s
+ </body>
+ """ % vars(self)
+ return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>%s\n</html>""" % (self.head + self.body)
+ def __str__(self):
+ box = TableList.make(
+ makelink(self.next, "Next"),
+ makelink(self.prev, "Prev"),
+ makelink(self.last, "Last"),
+ makelink(self.first, "First"),
+ "Current",
+ self.name,
+ border = 0,
+ color = BGCOLOR)
+ logo = TableList.col(
+ '<img src = "accu2005.png" alt = "logo">',
+ border = 0,
+ color = BGCOLOR)
+ links = [makelink(page.namext, page.name)
+ for page in self.document.pageindex]
+ opts = dict(border = 0, color = BGCOLOR, ncols=2)
+ index = TableList.make(*links, **opts)
+ self.txt = str(
+ TableList.row("<small>%s</small>" % TableList.col(box, index, logo,
+ border = 0, color = BGCOLOR),
+ self.txt, border = 0, color = BGCOLOR))
+ return self.html()
+ #return tidy(self.html(), "-i")[0]
+
+def main(fname):
+ BLANK = re.compile(r"\n\n\n\s*")
+ chunks = BLANK.split(file(fname).read())
+ pages = HTMLDocument(map(HTMLPage, enumerate(chunks)))
+ for page in pages:
+ print >> file(page.namext, "w"), page
+ #os.system("xterm -e elinks P01.html&")
+ webbrowser.open("file:%s/P01.html" % os.getcwd())
+
+if __name__ == "__main__":
+ main(sys.argv[1])
diff --git a/pypers/oxford/doctest_talk/more.txt b/pypers/oxford/doctest_talk/more.txt
new file mode 100755
index 0000000..0e0b7fc
--- /dev/null
+++ b/pypers/oxford/doctest_talk/more.txt
@@ -0,0 +1,33 @@
+
+Doctest and exceptions
+--------------------------------------
+
+<pre>
+
+>>> from partecs_voting import vote_validator
+>>> from partecs_voting.util import TheVoteIsClosedError
+>>> validate = vote_validator(
+... choices = ["A", "B", "C"],
+... OpeningDate = "Sun Aug 29 06:00:00 2004",
+... ClosingDate = "Sun Aug 30 22:00:00 2004")
+>>> try: # assuming you run this after Aug 29 2004
+... validate("A")
+... except TheVoteIsClosedError:
+... print "The vote is closed!"
+The vote is closed!
+
+</pre>
+
+Real example
+----------------------------------------------------------
+
+<pre>
+
+>>> from partecs_voting import make_ballot
+>>> ballot=make_ballot(["A", "B"], [["A"], ["A"], ["B"]], "BordaBallot")
+>>> print ballot.getresults()
+ALL RESULTS:
+A 2
+B 1
+
+</pre>
diff --git a/pypers/oxford/doctest_talk/refresh.txt b/pypers/oxford/doctest_talk/refresh.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/oxford/doctest_talk/refresh.txt
diff --git a/pypers/oxford/doctest_talk/split-failure.txt b/pypers/oxford/doctest_talk/split-failure.txt
new file mode 100755
index 0000000..0bce3e2
--- /dev/null
+++ b/pypers/oxford/doctest_talk/split-failure.txt
@@ -0,0 +1,5 @@
+An example of failed text:
+
+>>> from split import split
+>>> split("hello, world")
+['hello', ' world']
diff --git a/pypers/oxford/doctest_talk/split-failure_txt.py b/pypers/oxford/doctest_talk/split-failure_txt.py
new file mode 100755
index 0000000..6819fb5
--- /dev/null
+++ b/pypers/oxford/doctest_talk/split-failure_txt.py
@@ -0,0 +1,8 @@
+"""
+An example of failed text:
+
+>>> from split import split
+>>> split("hello, world")
+['hello', ' world']
+
+"""
diff --git a/pypers/oxford/doctest_talk/split.html b/pypers/oxford/doctest_talk/split.html
new file mode 100755
index 0000000..3cc867a
--- /dev/null
+++ b/pypers/oxford/doctest_talk/split.html
@@ -0,0 +1,32 @@
+<?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.3.4: http://docutils.sourceforge.net/" />
+<title>Documentation for the 'split' module</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<h1 class="title">Documentation for the 'split' module</h1>
+<div class="document" id="documentation-for-the-split-module">
+<p>The module contains a 'split' function, which
+splits a string taking as separators &quot;,&quot; and &quot;;&quot;.
+This is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from split import split
+&gt;&gt;&gt; split(&quot;hello, world!; welcome to the Italian Code Jam!&quot;)
+['hello', 'world!', 'welcome to the Italian Code Jam!']
+</pre>
+<p>Notice that 'split' eats whitespaces:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; split(&quot;hello , world&quot;)
+['hello', 'world']
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; split(&quot;hello , ; world&quot;)
+['hello', '', 'world']
+</pre>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/doctest_talk/split.py b/pypers/oxford/doctest_talk/split.py
new file mode 100755
index 0000000..fda986c
--- /dev/null
+++ b/pypers/oxford/doctest_talk/split.py
@@ -0,0 +1,15 @@
+# split.py
+import re
+SEP = re.compile(r"\s*[,;]\s*")
+
+def split(text):
+ """Split a string taking as separators "," ";".
+ Example:
+ >>> from split import split
+ >>> split("hello, world!; welcome to PyUK!")
+ ['hello', 'world!', 'welcome to PyUK!']
+ """
+ return SEP.split(text)
+
+if __name__ == "__main__":
+ import doctest; doctest.testmod()
diff --git a/pypers/oxford/doctest_talk/split.txt b/pypers/oxford/doctest_talk/split.txt
new file mode 100755
index 0000000..8e3e8fe
--- /dev/null
+++ b/pypers/oxford/doctest_talk/split.txt
@@ -0,0 +1,18 @@
+Documentation for the 'split' module
+=====================================
+
+The module contains a 'split' function, which
+splits a string taking as separators "," and ";".
+This is an example of usage:
+
+>>> from split import split
+>>> split("hello, world!; welcome to the Italian Code Jam!")
+['hello', 'world!', 'welcome to the Italian Code Jam!']
+
+Notice that 'split' eats whitespaces:
+
+>>> split("hello , world")
+['hello', 'world']
+
+>>> split("hello , ; world")
+['hello', '', 'world']
diff --git a/pypers/oxford/doctest_talk/talk.txt b/pypers/oxford/doctest_talk/talk.txt
new file mode 100755
index 0000000..c3b2991
--- /dev/null
+++ b/pypers/oxford/doctest_talk/talk.txt
@@ -0,0 +1,403 @@
+Automatic testing in Python: wonderful doctest!
+===============================================
+
+<center>
+
+ ACCU Conference 2005 <br/> <br/>
+
+ 22 Apr 2005 <br/> <br/>
+
+ Michele Simionato <br/> <br/>
+
+ michele.simionato@gmail.com <br/> <br/>
+
+</center>
+
+
+Summary
+------------
+
+<ul>
+ <li> What is automatic testing? </li>
+ <li> Why automatic testing is better? </li>
+ <li> Which kind of automatic testing? </li>
+ <li> How does it work, in practice? </li>
+ <li> What's the message?</li>
+<ul>
+
+
+What is automatic testing
+-------------------------
+
+Any methodology that allows you to test
+your application mechanically, repeatedly
+and in a <em>controlled reproducible</em> way.
+
+
+Automatic testing is better (1)
+-----------------------------------
+
+When doing manual testing typically you spend
+
+<center><h2>
+
+ 1 hour of coding + 10 hours of testing/debugging
+
+</center></h2>
+
+on the other hand ...
+
+
+Automatic testing is better (2)
+-----------------------------------
+
+... when doing automatic testing typically you spend
+
+<br/> <br/>
+<center><h2>
+
+ 1 hour of coding + 10 hours of testing/debugging !
+
+</center></h2>
+
+
+However ...
+---------------------------------------
+
+Think about six month later!
+ <br/><br/>
+<center><em>
+
+ there is a difference</em>
+
+ <h2><u>Refactoring!</u><h2>
+
+</center>
+
+
+Automatic testing in Python
+-------------------------------
+
+There are two standard testing frameworks in Python:
+
+<ol>
+ <li> unittest </li>
+ <li> doctest </li>
+</ol>
+
+Which one should I use?
+
+
+Well,
+-------------------------
+
+since my talk has <em>doctest</em> in the title ...
+
+ ;-)
+
+
+More seriously ...
+--------------------------
+
+Use different testing frameworks; each one has advantages
+and disadvantages; use combinations of them; invent your
+own testing procedure.
+
+I use combinations of
+
+<ul>
+ <li> unittest </li>
+ <li> doctest </li>
+ <li> custom tests </li>
+ <li> Makefile driven tests </li>
+ <li> et al. </li>
+</ul>
+
+doctest emphasis is on <em>documentation</em>
+
+
+What is doctest?
+--------------------------------
+
+In its simplest form (which I do not use that much) doctest allows
+you to include tests in the docstrings of your application.
+
+
+Example
+-------
+
+.. include:: split.py
+
+
+Running doctest in verbose mode
+--------------------------------------------------------------------
+
+<pre>
+$ python split.py -v
+Running __main__.__doc__
+0 of 0 examples failed in __main__.__doc__
+Running __main__.split.__doc__
+Trying: from split import split
+Expecting: nothing
+ok
+Trying: split("hello, world!; welcome to Oxford!")
+Expecting: ['hello', 'world!', 'welcome to Oxford!']
+ok
+0 of 2 examples failed in __main__.split.__doc__
+1 items had no tests:
+ __main__
+1 items passed all tests:
+ 2 tests in __main__.split
+2 tests in 2 items.
+2 passed and 0 failed.
+Test passed.
+</pre>
+
+
+Why I do not use the docstring approach
+-------------------------------------------------------
+
+<ul>
+<li> It makes you end up with very large docstrings</li>
+
+<li> It abuses the original purpose of docstrings</li>
+
+<li> It conflates two different aspects (code and tests on the code)</li>
+
+<li> It is much easier to write the documentation in a separate
+ text file </li>
+
+<li> Testing should be done by an external tool anyway </li>
+</ul>
+
+
+How I use doctest
+---------------------
+
+I hacked inside doctest and wrote a custom utility
+to extract doctests from documentation files since
+
+<ul>
+ <li>I like keeping the documentation on a separate rst file</li>
+
+ <li>there is no sync problem since you run the tests all the time</li>
+
+ <li>it is useful for writing articles ...</li>
+
+ <li> ... but also documentation for internal usage in the company</li>
+</ul>
+
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052
+
+<pre>
+$ python -m doctester < split.txt
+doctest: run 4 tests, failed 0
+</pre>
+
+
+Testing the doctester frontend
+--------------------------------------------------
+
+<pre>
+>>> from ms.webtester import start_server, stop_server
+>>> from ms.http_utils import urlopen
+>>> baseurl = "http://localhost:7080/"
+>>> home = "/home/micheles/md/python/quixote/"
+
+>>> start_server(home + "doctester_frontend.py")
+>>> import time; time.sleep(2) # wait a bit
+
+Making a POST:
+
+>>> res = urlopen(baseurl, dict(txt=">>> 1 + 1\n2")).read()
+>>> assert "tests" in res
+>>> stop_server()
+
+</pre>
+
+
+Managing exceptions
+--------------------------------------------------------------
+
+It is possible to test that your program raises the exception you
+expect:
+
+<pre>
+
+$ echo "# split cannot work on a list
+>>> from split import split
+>>> split([])
+Traceback (most recent call last):
+ ...
+TypeError: expected string or buffer
+" > x.txt
+
+$ doct x.txt
+x.txt: 2 tests passed in 0.01 seconds
+
+</pre>
+
+(notice however that relying on exception messages may be risky)
+
+
+When tests fail
+-----------------------------------------------------------------
+
+<pre>
+
+$ cat split-failure.txt
+An example of failed text:
+
+>>> from split import split
+>>> split("hello, world")
+['hello', ' world']
+
+$ doct split-failure.txt
+*****************************************************************
+Failure in example: split("hello, world")
+from line #5 of split-failure.txt
+Expected: ['hello', ' world']
+Got: ['hello', 'world']
+
+</pre>
+
+
+Converting doctests to unittests
+------------------------------------------------------------------
+
+<pre>
+ import unittest
+ import doctest
+ import my_module_with_doctests
+
+ suite = doctest.DocTestSuite(my_module_with_doctests)
+ runner = unittest.TextTestRunner()
+ runner.run(suite)
+</pre>
+
+<h2>For Python 2.3+<h2>
+
+
+doctest is becoming even better
+----------------------------------------------------
+
+With Python 2.4 you can run doctests on external text files:
+
+<pre>
+ import doctest, unittest
+ doctest.testfile(my_documentation_file, package=mypackage)
+</pre>
+
+you can also convert these doctests into unittests:
+
+<pre>
+ import doctest, unittest
+ suite = doctest.DocFileSuite(my_documentation_file, package=mypackage)
+ unittest.TextTestRunner().run(suite)
+</pre>
+
+
+Python 2.4 recognizes blank lines
+--------------------------------------------------------------
+
+Blank lines can be marked with &lt;BLANKLINE&gt; :
+<pre>
+>>> print 'foo\n\nbar\n'
+foo
+&lt;BLANKLINE&gt;
+bar
+&lt;BLANKLINE&gt;
+
+</pre>
+
+
+Python 2.4 recognizes flags!
+--------------------------------------------------------------
+
+<ul>
+<li> If the ellipsis flag is used, then '...' can be used to
+ elide substrings in the desired output: <pre>
+>>> print range(1000) #doctest: +ELLIPSIS
+[0, 1, 2, ..., 999]
+
+</pre></li>
+
+<li>
+ If the whitespace normalization flag is used, then
+ differences in whitespace are ignored.<pre>
+>>> print range(20) #doctest: +NORMALIZE_WHITESPACE
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+12, 13, 14, 15, 16, 17, 18, 19]
+
+</pre></li>
+
+</ul>
+
+
+Zope experience
+----------------------------------------------------------------
+
+Literal quote from the PyCON doctest talk:
+
+<ul>
+<li> ~ 5600 tests (~3500 in Zope 3, ~1300 in ZODB, ~800 in Zope 2)</li>
+<li> we wrote lots of tests before we knew what we were doing</li>
+<li> debugging failed tests is really hard when intent is unclear</li>
+<li> often refactor or reimplement tests to make them clearer</li>
+<li> most new tests are doctest based</li>
+</ul>
+
+
+Conclusion (1): good reasons to use doctest
+-----------------------------------------------------------
+
+<quote>
+"Test coverage is important, but test readability is much more important"
+</quote>
+
+<em>-- Tim Peters and Jim Fulton</em> <br/> <br/>
+
+doctest is good since:
+
+<ol>
+ <li> it is easy to understand, to explain and to use </li>
+
+ <li> it makes you improve the quality of your documentation </li>
+
+ <li> it can be converted to unittest anyway </li>
+
+</ol>
+
+
+Conclusion (2): the message of this talk
+-----------------------------------------------------------
+
+Automatic testing is good for tons of practical reasons, but also
+because:
+
+<ol>
+
+<li>It teaches you <em>discipline</em> </li>
+
+<li>It makes you
+ <em>think differently</em> </li>
+
+<li>It is more <em>fun!</em> </li>
+
+</ol>
+
+
+References
+-----------------------------------------------------------
+
+<ul>
+
+<li>The standard library documentation
+http://docs.python.org/lib/module-doctest.html </li>
+
+<li> The doctest talk by Tim Peters and Jim Fulton
+http://www.python.org/pycon/dc2004/papers/4/</li>
+
+<li> doctest.py <em>(use the source, Luke!)</em></li>
+</ul>
+
diff --git a/pypers/oxford/doctest_talk/test_pkg/__init__.py b/pypers/oxford/doctest_talk/test_pkg/__init__.py
new file mode 100755
index 0000000..3963e63
--- /dev/null
+++ b/pypers/oxford/doctest_talk/test_pkg/__init__.py
@@ -0,0 +1,6 @@
+"""Trying to doctest a package.
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/oxford/doctest_talk/test_pkg/a.py b/pypers/oxford/doctest_talk/test_pkg/a.py
new file mode 100755
index 0000000..ef2abf0
--- /dev/null
+++ b/pypers/oxford/doctest_talk/test_pkg/a.py
@@ -0,0 +1,7 @@
+"""
+Module a
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/oxford/doctest_talk/test_pkg/b.py b/pypers/oxford/doctest_talk/test_pkg/b.py
new file mode 100755
index 0000000..f17cd98
--- /dev/null
+++ b/pypers/oxford/doctest_talk/test_pkg/b.py
@@ -0,0 +1,7 @@
+"""
+Module b
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/oxford/doctest_talk/testfile_ex.py b/pypers/oxford/doctest_talk/testfile_ex.py
new file mode 100755
index 0000000..3d85fcc
--- /dev/null
+++ b/pypers/oxford/doctest_talk/testfile_ex.py
@@ -0,0 +1,11 @@
+import doctest, unittest
+suite = doctest.DocFileSuite("example.txt", package='ms.test')
+unittest.TextTestRunner().run(suite)
+
+# alternatively
+
+#import types
+#f = file("/home/micheles/md/scripts/ms/test/example.txt")
+#mod = types.ModuleType("example", f.read())
+#suite = doctest.DocTestSuite(mod)
+#unittest.TextTestRunner().run(suite)
diff --git a/pypers/oxford/doctest_talk/the_story.txt b/pypers/oxford/doctest_talk/the_story.txt
new file mode 100755
index 0000000..ccdbdbd
--- /dev/null
+++ b/pypers/oxford/doctest_talk/the_story.txt
@@ -0,0 +1,23 @@
+- a physicist perspective
+- the story of this talk
+- making the slides
+- what I wanted to realize
+- shallow knowledge
+- browser requirements
+- a word about security
+- the preparation of my lectures
+- emacs
+- importance of documentation
+- the right mindset
+- the doctester at work
+- real life experience with outsourcing
+- if you want to start a new company ...
+- /home/micheles/code/branches/1.3/lib/python/partecs_voting/tests
+- doctest vs. unittest
+- show the slides till page 6
+- a word about converting to testing methodologies
+- doctesting web applications
+- doctest 2 unittest
+- zope experience
+- cleverness vs. wisdom
+- three things to remember
diff --git a/pypers/oxford/doctest_talk/x.html b/pypers/oxford/doctest_talk/x.html
new file mode 100755
index 0000000..57bfa5f
--- /dev/null
+++ b/pypers/oxford/doctest_talk/x.html
@@ -0,0 +1,203 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+ <meta name="generator" content=
+ "HTML Tidy for Linux/x86 (vers 1st March 2004), see www.w3.org">
+ <meta name="generator" content="Generated by Python">
+
+ <title>P01</title>
+ <style type="text/css">
+ body { font-size: 160%; }
+ </style>
+</head>
+
+<body bgcolor="lightblue">
+ <table border="0">
+ <tr>
+ <td bgcolor="lightblue">
+ <table border="0">
+ <tr>
+ <td bgcolor="lightblue">
+ <table border="0">
+ <tr>
+ <td bgcolor="lightblue"><img src=
+ "cjlogo.jpg"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue">
+ <table border="0">
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P02.html">Next</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P26.html">Prev</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P01.html">First</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P26.html">Last</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P01.html">P01</a></td>
+
+ <td bgcolor="lightblue"></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"></td>
+
+ <td bgcolor="lightblue"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue">
+ <table border="0">
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P01.html">P01</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P02.html">P02</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P03.html">P03</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P04.html">P04</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P05.html">P05</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P06.html">P06</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P07.html">P07</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P08.html">P08</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P09.html">P09</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P10.html">P10</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P11.html">P11</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P12.html">P12</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P13.html">P13</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P14.html">P14</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P15.html">P15</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P16.html">P16</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P17.html">P17</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P18.html">P18</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P19.html">P19</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P20.html">P20</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P21.html">P21</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P22.html">P22</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P23.html">P23</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P24.html">P24</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"><a href=
+ "P25.html">P25</a></td>
+
+ <td bgcolor="lightblue"><a href=
+ "P26.html">P26</a></td>
+ </tr>
+
+ <tr>
+ <td bgcolor="lightblue"></td>
+
+ <td bgcolor="lightblue"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+
+ <td bgcolor="lightblue">
+ <h1>Automatic testing in Python: wonderful
+ doctest!</h1><br>
+
+ <center>
+ Italian Code Jam<br>
+ <br>
+ 09 Oct 2004<br>
+ <br>
+ Michele Simionato<br>
+ <br>
+ m.simionato@partecs.com<br>
+ <br>
+ Partecs s.r.l.
+ </center><br>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/pypers/oxford/doctest_talk/x.py b/pypers/oxford/doctest_talk/x.py
new file mode 100755
index 0000000..6c263e8
--- /dev/null
+++ b/pypers/oxford/doctest_talk/x.py
@@ -0,0 +1,19 @@
+r"""
+>>> print "foo\n\nbar\n"
+foo
+<BLANKLINE>
+bar
+<BLANKLINE>
+
+>>> print range(1000) #doctest: +ELLIPSIS
+[0, 1, 2, ..., 999]
+
+>>> print range(20) #doctest: +NORMALIZE_WHITESPACE
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+12, 13, 14, 15, 16, 17, 18, 19]
+
+"""
+
+if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
diff --git a/pypers/oxford/doctest_talk/x.txt b/pypers/oxford/doctest_talk/x.txt
new file mode 100755
index 0000000..2bf5990
--- /dev/null
+++ b/pypers/oxford/doctest_talk/x.txt
@@ -0,0 +1,55 @@
+line 17 column 27 - Warning: missing </small> before <table>
+line 95 column 1 - Warning: discarding unexpected </small>
+line 12 column 1 - Warning: <body> attribute "bgcolor" has invalid value "lightblue"
+line 17 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 20 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 23 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 29 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 32 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 32 column 60 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 35 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 35 column 60 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 38 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 38 column 40 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 41 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 41 column 33 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 47 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 50 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 50 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 53 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 53 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 56 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 56 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 59 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 59 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 62 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 62 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 65 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 65 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 68 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 68 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 71 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 71 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 74 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 74 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 77 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 77 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 80 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 80 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 83 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 83 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 86 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 86 column 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 89 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 89 column 33 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 95 column 15 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 17 column 27 - Warning: trimming empty <small>
+Info: Doctype given is "-//W3C//DTD HTML 4.01//EN"
+Info: Document content looks like HTML 4.01 Transitional
+46 warnings, 0 errors were found!
+
+
+To learn more about HTML Tidy see http://tidy.sourceforge.net
+Please send bug reports to html-tidy@w3.org
+HTML and CSS specifications are available from http://www.w3.org/
+Lobby your company to join W3C, see http://www.w3.org/Consortium
diff --git a/pypers/oxford/doctester.py b/pypers/oxford/doctester.py
new file mode 100755
index 0000000..ec4f964
--- /dev/null
+++ b/pypers/oxford/doctester.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# Author: michele.simionato@gmail.com
+"""\
+Filter passing stdin through doctest. Example of usage:
+$ doctester.py -v < file.txt
+"""
+import sys, doctest, textwrap, re, types
+#import warnings;warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+# regular expressions to identify code blocks of the form
+#<scriptname.py> ... </scriptname.py>
+DOTNAME = r'\b[a-zA-Z_][\w\.]*', # identifier with or without dots
+SCRIPT = re.compile(r'(?s)#<(%s)>(.*?)#</\1>' % DOTNAME)
+
+# a simple utility to extract the scripts contained in the original text
+def scripts(txt):
+ for MO in SCRIPT.finditer(txt):
+ yield MO.group(1), textwrap.dedent(MO.group(2))
+
+# save the scripts in the current directory
+def savescripts(txt):
+ scriptdict = {}
+ for scriptname, script in scripts(txt): # read scripts
+ if scriptname not in scriptdict:
+ scriptdict[scriptname] = script
+ else:
+ scriptdict[scriptname] += script
+ for scriptname in scriptdict: # save scripts
+ code = '# ' + scriptname + scriptdict[scriptname]
+ print >> file(scriptname, 'w'), code
+
+# based on a clever trick: it converts the original text into the docstring of
+# the main module; works both for Python 2.3 and 2.4;
+# main is needed to keep global variables in it (for instance to keep
+# threads working)
+def runtests(txt, verbose=False):
+ savescripts(txt)
+ try:
+ main = __import__("_main_")
+ except ImportError:
+ main = types.ModuleType("__main__")
+ main.__doc__ = txt
+ failed, tot = doctest.testmod(main, verbose=verbose)
+ doctest.master = None # cleanup the DocTestRunner
+ # needed to avoid a warning in case of multiple calls of runtests
+ if not verbose:
+ print >> sys.stderr, "doctest: run %s tests, failed %s" % (tot, failed)
+ # remove scripts
+ return failed, tot
+
+if __name__ == '__main__':
+ try: set # need sets for option parsing
+ except NameError: import sets; set = sets.Set # for Python 2.3
+ valid_options = set("-v -h".split())
+ options = set(sys.argv[1:])
+ assert options < valid_options, "Unrecognized option"
+ if "-h" in options: # print usage message and exit
+ sys.exit(__doc__)
+ runtests(sys.stdin.read(), "-v" in options)
diff --git a/pypers/oxford/dynamic_pages.py b/pypers/oxford/dynamic_pages.py
new file mode 100755
index 0000000..0e6369f
--- /dev/null
+++ b/pypers/oxford/dynamic_pages.py
@@ -0,0 +1,9 @@
+class WebApplication(object):
+ def __getattr__(self, name):
+ return "Page %s" % name
+
+
+app = WebApplication()
+
+print app.page1
+print app.page2
diff --git a/pypers/oxford/evilprop.py b/pypers/oxford/evilprop.py
new file mode 100755
index 0000000..655df20
--- /dev/null
+++ b/pypers/oxford/evilprop.py
@@ -0,0 +1,18 @@
+# evilprop.py
+
+def convert2property(name, bases, d):
+ return property(d.get('get'), d.get('set'),
+ d.get('del'),d.get('__doc__'))
+
+class C(object):
+ class x:
+ """An evil test property"""
+ __metaclass__ = convert2property
+ def get(self):
+ print 'Getting %s' % self._x
+ return self._x
+ def set(self, value):
+ self._x = value
+ print 'Setting to', value
+
+
diff --git a/pypers/oxford/ex.py b/pypers/oxford/ex.py
new file mode 100755
index 0000000..3868a1e
--- /dev/null
+++ b/pypers/oxford/ex.py
@@ -0,0 +1,26 @@
+class Base(object):
+ def __init__(self):
+ print "B.__init__"
+
+class MyClass(Base):
+ "I do not cooperate with others"
+ def __init__(self):
+ print "MyClass.__init__"
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+
+class Mixin(Base):
+ "I am cooperative with others"
+ def __init__(self):
+ print "Mixin.__init__"
+ super(Mixin, self).__init__()
+
+
+class HerClass(MyClass, Mixin):
+ "I am cooperative too"
+ def __init__(self):
+ print "HerClass.__init__"
+ super(HerClass, self).__init__()
+
+
+h = HerClass()
diff --git a/pypers/oxford/flatten.py b/pypers/oxford/flatten.py
new file mode 100755
index 0000000..0695967
--- /dev/null
+++ b/pypers/oxford/flatten.py
@@ -0,0 +1,27 @@
+
+## def flatten(container):
+## for obj in container:
+## if hasattr(obj, "__iter__"):
+## for el in flatten(obj):
+## yield el
+## else:
+## yield obj
+
+def walk(container, level=0):
+ for obj in container:
+ if not hasattr(obj, "__iter__"):
+ yield obj, level
+ else:
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+def pprint(container):
+ for obj, level in walk(container):
+ print " "*level, obj
+
+def flatten(container):
+ return (obj for obj, level in walk(container))
+
+nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+pprint(nested_ls)
+pprint(flatten(nested_ls))
diff --git a/pypers/oxford/for_loop.py b/pypers/oxford/for_loop.py
new file mode 100755
index 0000000..fc062d1
--- /dev/null
+++ b/pypers/oxford/for_loop.py
@@ -0,0 +1,15 @@
+result = []
+for i in range(10):
+ result.append(lambda : i)
+
+
+# could be converted in
+
+result = []
+def for_block(i):
+ result.append(lambda : i)
+for i in range(10): for_block(i)
+
+#func = list(lambda : i for i in range(10))
+#print func[0]()
+print result[1]()
diff --git a/pypers/oxford/frontpage.txt b/pypers/oxford/frontpage.txt
new file mode 100755
index 0000000..160418d
--- /dev/null
+++ b/pypers/oxford/frontpage.txt
@@ -0,0 +1,10 @@
+Lectures on Advanced Python Programming
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. image:: accu2005.png
+
+:Author: Michele Simionato
+:Given: 19 April 2005
+:Revised: 7 September 2005
+
+.. contents::
diff --git a/pypers/oxford/gen_with_attr.py b/pypers/oxford/gen_with_attr.py
new file mode 100755
index 0000000..ee1afd5
--- /dev/null
+++ b/pypers/oxford/gen_with_attr.py
@@ -0,0 +1,21 @@
+class MyIter(object):
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+it = MyIter()
+for i in it: print i
+
+i0 = MyIter()
+i1 = iter(i0)
+i2 = iter(i1)
+
+print i0 is i1
+print i1 is i2
+
+it = MyIter()
+print it, iter(it)
+print iter(it), iter(iter(it))
+
+
diff --git a/pypers/oxford/getlevel.py b/pypers/oxford/getlevel.py
new file mode 100755
index 0000000..e86d50c
--- /dev/null
+++ b/pypers/oxford/getlevel.py
@@ -0,0 +1,9 @@
+def getlevel(obj, lvl, i):
+ if lvl == 0:
+ return obj[i]
+ else:
+ return getlevel(obj[i], lvl-1)
+
+ls = [[0, [1, [2], 3], 4, 5]]
+
+print getlevel(ls, 0, 1)
diff --git a/pypers/oxford/htmltable.py b/pypers/oxford/htmltable.py
new file mode 100755
index 0000000..ed46745
--- /dev/null
+++ b/pypers/oxford/htmltable.py
@@ -0,0 +1,19 @@
+# htmltable.py
+
+def HTMLTablegen(table):
+ yield "<table>"
+ for row in table:
+ yield "<tr>"
+ for col in row:
+ yield "<td>%s</td>" % col
+ yield "</tr>"
+ yield "</table>"
+
+def test():
+ return "\n".join(HTMLTablegen([["Row", "City"],
+ [1,'London'], [2, 'Oxford']]))
+
+if __name__ == "__main__": # example
+ print test()
+
+
diff --git a/pypers/oxford/import_with_meta.py b/pypers/oxford/import_with_meta.py
new file mode 100755
index 0000000..92ef395
--- /dev/null
+++ b/pypers/oxford/import_with_meta.py
@@ -0,0 +1,18 @@
+# import_with_metaclass.py
+
+import inspect, types
+
+def import_with_metaclass(metaclass, modname):
+ "Module importer substituting custom metaclass"
+ dct = {'__module__' : modname}
+ mod = __import__(modname)
+ for key, val in mod.__dict__.items():
+ if inspect.isclass(val):
+ if isinstance(val, types.ClassType):
+ bases = (val, object) # convert old-style to new-style
+ else:
+ bases = (val,)
+ setattr(mod, key, metaclass(key, bases, dct))
+ return mod
+
+
diff --git a/pypers/oxford/import_with_metaclass.py b/pypers/oxford/import_with_metaclass.py
new file mode 100755
index 0000000..bc7175c
--- /dev/null
+++ b/pypers/oxford/import_with_metaclass.py
@@ -0,0 +1,23 @@
+# import_with_metaclass.py
+"""
+``import_with_metaclass(metaclass, modulepath)`` generates
+a new module from and old module, by enhancing all of its classes.
+This is not perfect, but it should give you a start."""
+
+import os, sys, inspect, types
+
+def import_with_metaclass(metaclass, modulepath):
+ modname = os.path.basename(modulepath)[:-3] # simplistic
+ mod = types.ModuleType(modname)
+ locs = dict(
+ __module__ = modname,
+ __metaclass__ = metaclass,
+ object = metaclass("object", (), {}))
+ execfile(modulepath, locs)
+ for k, v in locs.iteritems():
+ if inspect.isclass(v): # otherwise it would be "__builtin__"
+ v.__module__ = "__dynamic__"
+ setattr(mod, k, v)
+ return mod
+
+
diff --git a/pypers/oxford/index.txt b/pypers/oxford/index.txt
new file mode 100755
index 0000000..66c7cd2
--- /dev/null
+++ b/pypers/oxford/index.txt
@@ -0,0 +1,83 @@
+ACCU Conference 2005 - Advanced Python Course Program
+=========================================================
+
+This course is intended for intermediate Python programmers who want to
+raise their knowledge of modern Python to an advanced/expert level.
+
+The course will begin by discussing in detail how Python
+works behing the scenes (what really happens when you
+write a for loop? what really happens when you access an attribute?
+what really happens when you define a class?) and explaining a few
+tricky/subtle points. Then, I will move to examples of real life use
+cases where the advanced techniques explained here have been useful to me,
+as well to cases where I have decided *not* to use those techniques.
+To keep the attention of the public, as well as for exemplification
+purposes, some time will be spent discussing a few cool hacks and recipes
+from the second edition of the Python Cookbook.
+
+The six hour course is split in three sessions on the same day.
+The session titles are:
+
+ 1. Loops (i.e. iterators & generators)
+ 2. Objects (i.e. delegation & inheritance)
+ 3. Magic (i.e. decorators & metaclasses)
+
+Lecture 1 is probably the most useful to the average Python programmer;
+lecture 2 is of interest to programmers who wants to unveil the secrets
+of the new-style object model; lecture 3 delves into the dark side of Python.
+
+Lecture 1: Loops (i.e. iterators & generators)
+-----------------------------------------------------------
+
+- the seemingly trivial 'for' loop;
+- iterables and iterators;
+- ex: read_data
+- generator-comprehension;
+- generators;
+- real life ex: generating HTML, walk
+- parsers;
+- the itertools module;
+- ex: chop, skip_redundant
+- sorting iterables;
+- iteration caveats;
+- final ex: adder
+
+Lecture 2: Objects (i.e. delegation & inheritance)
+------------------------------------------------------------------
+
++ delegation
+
+ - descriptors;
+ - relationship between functions and methods;
+ - staticmethods & classmethods;
+ - ex: MultilingualAttribute
+ - properties;
+ - ex: crypted passwords;
+ - customized attribute access;
+ - ex: kwdict, DictWrapper, XMLTag
+ - __getattr__ subtilities;
+
++ inheritance
+
+ - subclassing builtins; __new__ vs. __init__;
+ - private & protected variables;
+ - multiple inheritance;
+ - usage of super and a few caveats;
+ - ex. DictMixin, ChainMap, interp;
+ - operator overriding;
+
+Lecture 3: Magic (i.e. decorators & metaclasses)
+-----------------------------------------------------------
+
+- decorators:
+- ex: tracing, memoize, timed, HTMLgen;
+- metaclasses: why you should know they exist;
+- a typical usage: tracing
+- real life ex: logfile, check overriding, private/public
+- additional real life ex: cmd, plug-in;
+- playing with the language: restricting access, super sugar
+- a few evil hacks (properties, PEP312)
+- caveats: meta-attributes vs. class attributes;
+- ex: __name__ (and super)
+- why you should not use metaclasses in production code;
+- metaclass conflicts;
diff --git a/pypers/oxford/infix.py b/pypers/oxford/infix.py
new file mode 100755
index 0000000..bebdaa0
--- /dev/null
+++ b/pypers/oxford/infix.py
@@ -0,0 +1,53 @@
+# infix.py
+
+class Infix:
+ def __init__(self, function):
+ self.function = function
+ def __ror__(self, other):
+ return Infix(lambda x, self=self, other=other: self.function(other, x))
+ def __or__(self, other):
+ return self.function(other)
+ def __rlshift__(self, other):
+ return Infix(lambda x, self=self, other=other: self.function(other, x))
+ def __rshift__(self, other):
+ return self.function(other)
+ def __call__(self, value1, value2):
+ return self.function(value1, value2)
+
+# Examples
+
+# simple multiplication
+x=Infix(lambda x,y: x*y)
+print 2 |x| 4
+# => 8
+
+# class checking
+isa=Infix(lambda x,y: x.__class__==y.__class__)
+print [1,2,3] |isa| []
+print [1,2,3] <<isa>> []
+# => True
+
+# inclusion checking
+is_in=Infix(lambda x,y: y.has_key(x))
+print 1 |is_in| {1:'one'}
+print 1 <<is_in>> {1:'one'}
+# => True
+
+# an infix div operator
+import operator
+div=Infix(operator.div)
+print 10 |div| (4 |div| 2)
+# => 5
+
+# functional programming (not working in jython, use the "curry" recipe! )
+def curry(f,x):
+ def curried_function(*args, **kw):
+ return f(*((x,)+args),**kw)
+ return curried_function
+curry=Infix(curry)
+
+add5= operator.add |curry| 5
+print add5(6)
+# => 11
+
+
diff --git a/pypers/oxford/interp.py b/pypers/oxford/interp.py
new file mode 100755
index 0000000..cdf7c3e
--- /dev/null
+++ b/pypers/oxford/interp.py
@@ -0,0 +1,45 @@
+# interp.py
+
+import UserDict
+
+class Chainmap(UserDict.DictMixin):
+ """Combine multiple mappings for sequential lookup. Raymond Hettinger,
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305268 """
+
+ def __init__(self, *maps):
+ self._maps = maps
+
+ def __getitem__(self, key):
+ for mapping in self._maps:
+ try:
+ return mapping[key]
+ except KeyError:
+ pass
+ raise KeyError(key)
+
+
+
+import sys
+from string import Template
+
+def interp(text, repldic=None, safe_substitute=True):
+ caller = sys._getframe(1)
+ if repldic:
+ mapping = Chainmap(repldic, caller.f_locals, caller.f_globals)
+ else:
+ mapping = Chainmap(caller.f_locals, caller.f_globals)
+ t = Template(text)
+ if safe_substitute:
+ return t.safe_substitute(mapping)
+ else:
+ return t.substitute(mapping)
+
+## Example:
+
+language="Python"
+
+def printmsg():
+ opinion = "favorite"
+ print interp("My $opinion language is $language.")
+
+
diff --git a/pypers/oxford/kwdict.py b/pypers/oxford/kwdict.py
new file mode 100755
index 0000000..33e2265
--- /dev/null
+++ b/pypers/oxford/kwdict.py
@@ -0,0 +1,10 @@
+# kwdict.py
+
+class kwdict(dict): # or UserDict, to make it to work with Zope
+ """A typing shortcut used in place of a keyword dictionary."""
+ def __getattr__(self, name):
+ return self[name]
+ def __setattr__(self, name, value):
+ self[name] = value
+
+
diff --git a/pypers/oxford/latebinding.py b/pypers/oxford/latebinding.py
new file mode 100755
index 0000000..b4668ff
--- /dev/null
+++ b/pypers/oxford/latebinding.py
@@ -0,0 +1,11 @@
+def toplevel():
+ a = 1
+ def f():
+ print a
+ a = 2
+ f()
+
+toplevel()
+
+func = list(lambda : i for i in range(10))
+print func[0]()
diff --git a/pypers/oxford/latebinding.scm b/pypers/oxford/latebinding.scm
new file mode 100755
index 0000000..27858ec
--- /dev/null
+++ b/pypers/oxford/latebinding.scm
@@ -0,0 +1,8 @@
+(define (toplevel)
+ (define a 1)
+ (define (f)
+ (display a))
+ (set! a 2)
+ (f))
+
+(toplevel)
diff --git a/pypers/oxford/lazy.txt b/pypers/oxford/lazy.txt
new file mode 100755
index 0000000..7db1f7c
--- /dev/null
+++ b/pypers/oxford/lazy.txt
@@ -0,0 +1,14 @@
+>>> import itertools
+>>> def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+
+>>> def is3(i):
+... print "i=%s" % i
+... return i == 3
+
+>>> anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True
diff --git a/pypers/oxford/logfile.py b/pypers/oxford/logfile.py
new file mode 100755
index 0000000..4545b50
--- /dev/null
+++ b/pypers/oxford/logfile.py
@@ -0,0 +1,45 @@
+# logfile.py
+
+import subprocess
+
+def memoize(func):
+ memoize_dic = {}
+ def wrapped_func(*args):
+ if args in memoize_dic:
+ return memoize_dic[args]
+ else:
+ result = func(*args)
+ memoize_dic[args] = result
+ return result
+ wrapped_func.__name__ = func.__name__
+ wrapped_func.__doc__ = func.__doc__
+ wrapped_func.__dict__ = func.__dict__
+ return wrapped_func
+
+class Memoize(type): # Singleton is a special case of Memoize
+ @memoize
+ def __call__(cls, *args):
+ return super(Memoize, cls).__call__(*args)
+
+class LogFile(file):
+ """Open a file for append."""
+ __metaclass__ = Memoize
+ def __init__(self, name = "/tmp/err.log"):
+ self.viewer_cmd = 'xterm -e less'.split()
+ super(LogFile, self).__init__(name, "a")
+
+ def display(self, *ls):
+ "Use 'less' to display the log file in a separate xterm."
+ print >> self, "\n".join(map(str, ls)); self.flush()
+ subprocess.call(self.viewer_cmd + [self.name])
+
+ def reset(self):
+ "Erase the log file."
+ print >> file(self.name, "w")
+
+if __name__ == "__main__": # test
+ print >> LogFile(), "hello"
+ print >> LogFile(), "world"
+ LogFile().display()
+
+
diff --git a/pypers/oxford/loops.html b/pypers/oxford/loops.html
new file mode 100755
index 0000000..7c55eac
--- /dev/null
+++ b/pypers/oxford/loops.html
@@ -0,0 +1,753 @@
+<?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.3.10: http://docutils.sourceforge.net/" />
+<title>Lecture 1: Loops (i.e. iterators &amp; generators)</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-06-06 15:09:07 +0200 (Mon, 06 Jun 2005) $
+:Version: $Revision: 3442 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+*/
+
+/* "! important" is used here to override other ``margin-top`` and
+ ``margin-bottom`` styles that are later in the stylesheet or
+ more specific. See http://www.w3.org/TR/CSS1#the-cascade */
+.first {
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin-left: 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left {
+ clear: left }
+
+img.align-right {
+ clear: right }
+
+img.borderless {
+ border: 0 }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font-family: serif ;
+ font-size: 100% }
+
+pre.line-block {
+ font-family: serif ;
+ font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+ margin-left: 2em ;
+ margin-right: 2em ;
+ background-color: #eeeeee }
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid thin gray }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid thin black }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+tt.docutils {
+ background-color: #eeeeee }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="lecture-1-loops-i-e-iterators-generators">
+<h1 class="title">Lecture 1: Loops (i.e. iterators &amp; generators)</h1>
+<div class="section" id="part-i-iterators">
+<h1><a name="part-i-iterators">Part I: iterators</a></h1>
+<div class="section" id="iterators-are-everywhere">
+<h2><a name="iterators-are-everywhere">Iterators are everywhere</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; for i in 1, 2, 3:
+... print i
+1
+2
+3
+</pre>
+<p>The 'for' loop is using <em>iterators</em> internally:</p>
+<pre class="literal-block">
+it = iter((1,2,3))
+while True:
+ try:
+ print it.next()
+ except StopIteration:
+ break
+</pre>
+</div>
+<div class="section" id="iterables-and-iterators">
+<h2><a name="iterables-and-iterators">Iterables and iterators</a></h2>
+<p><em>Iterable</em> = anything you can loop over = any sequence + any object with an __iter__ method;</p>
+<p>Not every sequence has an __iter__ method:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; &quot;hello&quot;.__iter__()
+Traceback (most recent call last):
+ ...
+AttributeError: 'str' object has no attribute '__iter__'
+</pre>
+<p><em>Iterator</em> = any object with a .next method and an __iter__ method returning self</p>
+</div>
+<div class="section" id="simpler-way-to-get-an-iterator">
+<h2><a name="simpler-way-to-get-an-iterator">Simpler way to get an iterator</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = iter(&quot;hello&quot;)
+&gt;&gt;&gt; it.next()
+'h'
+&gt;&gt;&gt; it.next()
+'e'
+&gt;&gt;&gt; it.next()
+'l'
+&gt;&gt;&gt; it.next()
+'l'
+&gt;&gt;&gt; it.next()
+'o'
+&gt;&gt;&gt; it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+</pre>
+</div>
+<div class="section" id="sentinel-syntax-iter-callable-sentinel">
+<h2><a name="sentinel-syntax-iter-callable-sentinel">Sentinel syntax iter(callable, sentinel)</a></h2>
+<p>Example:</p>
+<pre class="literal-block">
+$ echo -e &quot;value1\nvalue2\nEND\n&quot; &gt; data.txt
+$ python -c &quot;print list(iter(file('data.txt').readline, 'END\n'))&quot;
+['value1\n', 'value2\n']
+</pre>
+<p>Beware of infinite iterators:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; repeat = iter(lambda : &quot;some value&quot;, &quot;&quot;)
+&gt;&gt;&gt; repeat.next()
+'some value'
+</pre>
+</div>
+<div class="section" id="second-simpler-way-to-get-an-iterator-generator-expressions">
+<h2><a name="second-simpler-way-to-get-an-iterator-generator-expressions">Second simpler way to get an iterator: generator-expressions</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; squares = (i*i for i in range(1,11))
+&gt;&gt;&gt; list(squares)
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
+</pre>
+<p>Excessive parenthesis can be skipped, so use</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dict((i, i*i) for i in range(1,11))
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+</pre>
+<p>instead of</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dict([(i, i*i) for i in range(1,11)])
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+</pre>
+<p>(as usual, the most elegant version is the most efficient).</p>
+</div>
+<div class="section" id="iteration-caveats">
+<h2><a name="iteration-caveats">Iteration caveats</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; ls = [i for i in (1,2,3)]
+&gt;&gt;&gt; i
+3
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (j for j in (1,2,3))
+&gt;&gt;&gt; j
+Traceback (most recent call last):
+ ...
+NameError: name 'j' is not defined
+</pre>
+<p>A subtler example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; ls = [lambda :i for i in (1,2,3)]
+&gt;&gt;&gt; ls[0]()
+3
+</pre>
+<p>instead</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (lambda :i for i in (1,2,3))
+&gt;&gt;&gt; it.next()()
+1
+&gt;&gt;&gt; it.next()()
+2
+&gt;&gt;&gt; it.next()()
+3
+</pre>
+<p><em>seems</em> to be working but it is not really the case:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = (lambda :i for i in (1,2,3))
+&gt;&gt;&gt; f1 = it.next()
+&gt;&gt;&gt; f2 = it.next()
+&gt;&gt;&gt; f3 = it.next()
+&gt;&gt;&gt; f1()
+3
+</pre>
+<p>The reason is that Python does LATE binding <em>always</em>. The solution is ugly:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = list(lambda i=i:i for i in (1,2,3))
+&gt;&gt;&gt; it[0]()
+1
+&gt;&gt;&gt; it[1]()
+2
+&gt;&gt;&gt; it[2]()
+3
+</pre>
+</div>
+</div>
+<div class="section" id="part-ii-generators">
+<h1><a name="part-ii-generators">Part II: generators</a></h1>
+<p>Trivial example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def gen123(): # &quot;function&quot; which returns an iterator over the values 1, 2, 3
+... yield 1
+... yield 2
+... yield 3
+...
+&gt;&gt;&gt; it = gen123()
+&gt;&gt;&gt; it.next()
+1
+&gt;&gt;&gt; it.next()
+2
+&gt;&gt;&gt; it.next()
+3
+&gt;&gt;&gt; it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+</pre>
+<p>Real life example: using generators to generate HTML tables</p>
+<pre class="literal-block">
+#&lt;htmltable.py&gt;
+
+def HTMLTablegen(table):
+ yield &quot;&lt;table&gt;&quot;
+ for row in table:
+ yield &quot;&lt;tr&gt;&quot;
+ for col in row:
+ yield &quot;&lt;td&gt;%s&lt;/td&gt;&quot; % col
+ yield &quot;&lt;/tr&gt;&quot;
+ yield &quot;&lt;/table&gt;&quot;
+
+def test():
+ return &quot;\n&quot;.join(HTMLTablegen([[&quot;Row&quot;, &quot;City&quot;],
+ [1,'London'], [2, 'Oxford']]))
+
+if __name__ == &quot;__main__&quot;: # example
+ print test()
+
+#&lt;/htmltable.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from htmltable import test
+&gt;&gt;&gt; print test()
+&lt;table&gt;
+&lt;tr&gt;
+&lt;td&gt;Row&lt;/td&gt;
+&lt;td&gt;City&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;1&lt;/td&gt;
+&lt;td&gt;London&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;2&lt;/td&gt;
+&lt;td&gt;Oxford&lt;/td&gt;
+&lt;/tr&gt;
+&lt;/table&gt;
+</pre>
+<div class="section" id="a-simple-recipe-skip-redundant">
+<h2><a name="a-simple-recipe-skip-redundant">A simple recipe: skip redundant</a></h2>
+<p>How to remove duplicates by keeping the order:</p>
+<pre class="literal-block">
+#&lt;skip_redundant.py&gt;
+
+def skip_redundant(iterable, skipset=None):
+ &quot;Redundant items are repeated items or items in the original skipset.&quot;
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+#&lt;/skip_redundant.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from skip_redundant import skip_redundant
+&gt;&gt;&gt; print list(skip_redundant(&quot;&lt;hello, world&gt;&quot;, skipset=set(&quot;&lt;&gt;&quot;)))
+['h', 'e', 'l', 'o', ',', ' ', 'w', 'r', 'd']
+</pre>
+</div>
+<div class="section" id="another-real-life-example-working-with-nested-structures">
+<h2><a name="another-real-life-example-working-with-nested-structures">Another real life example: working with nested structures</a></h2>
+<pre class="literal-block">
+#&lt;walk.py&gt;
+
+def walk(iterable, level=0):
+ for obj in iterable:
+ if not hasattr(obj, &quot;__iter__&quot;): # atomic object
+ yield obj, level
+ else: # composed object: iterate again
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+def flatten(iterable):
+ return (obj for obj, level in walk(iterable))
+
+def pprint(iterable):
+ for obj, level in walk(iterable):
+ print &quot; &quot;*level, obj
+
+#&lt;/walk.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from walk import flatten, pprint
+&gt;&gt;&gt; nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+&gt;&gt;&gt; pprint(nested_ls)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+&gt;&gt;&gt; pprint(flatten(nested_ls))
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+</pre>
+</div>
+<div class="section" id="another-typical-use-case-for-generators-parsers">
+<h2><a name="another-typical-use-case-for-generators-parsers">Another typical use case for generators: parsers</a></h2>
+<p>A very stripped down parser for nested expressions</p>
+<pre class="literal-block">
+#&lt;sexpr2indent.py&gt;
+&quot;&quot;&quot;A simple s-expression formatter.&quot;&quot;&quot;
+
+import re
+
+def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r&quot;(?P&lt;paren_beg&gt;\()|(?P&lt;paren_end&gt;\))&quot;)
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == &quot;paren_beg&quot;:
+ nesting_level += 1
+ elif match.lastgroup == &quot;paren_end&quot;:
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+def sexpr_indent(sexpr):
+ for nesting, text in parse(sexpr.replace(&quot;\n&quot;, &quot;&quot;)):
+ if text.strip(): print &quot; &quot;*nesting, text
+
+#&lt;/sexpr2indent.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from sexpr2indent import sexpr_indent
+&gt;&gt;&gt; sexpr_indent(&quot;&quot;&quot;\
+... (html (head (title Example)) (body (h1 s-expr formatter example)
+... (a (&#64; (href http://www.example.com)) A link)))&quot;&quot;&quot;)
+... #doctest: +NORMALIZE_WHITESPACE
+ html
+ head
+ title Example
+ body
+ h1 s-expr formatter example
+ a
+ &#64;
+ href http://www.example.com
+ A link
+</pre>
+</div>
+<div class="section" id="other-kinds-of-iterables">
+<h2><a name="other-kinds-of-iterables">Other kinds of iterables</a></h2>
+<p>The following class generates iterable which are not iterators:</p>
+<pre class="literal-block">
+#&lt;reiterable.py&gt;
+
+class ReIter(object):
+ &quot;A re-iterable object.&quot;
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+#&lt;/reiterable.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from reiterable import ReIter
+&gt;&gt;&gt; rit = ReIter()
+&gt;&gt;&gt; list(rit)
+[1, 2, 3]
+&gt;&gt;&gt; list(rit) # it is reiterable!
+[1, 2, 3]
+</pre>
+</div>
+<div class="section" id="the-itertools-module">
+<h2><a name="the-itertools-module">The itertools module</a></h2>
+<blockquote>
+<ul class="simple">
+<li>count([n]) --&gt; n, n+1, n+2, ...</li>
+<li>cycle(p) --&gt; p0, p1, ... plast, p0, p1, ...</li>
+<li>repeat(elem [,n]) --&gt; elem, elem, elem, ... endlessly or up to n times</li>
+<li>izip(p, q, ...) --&gt; (p[0], q[0]), (p[1], q[1]), ...</li>
+<li>ifilter(pred, seq) --&gt; elements of seq where pred(elem) is True</li>
+<li>ifilterfalse(pred, seq) --&gt; elements of seq where pred(elem) is False</li>
+<li>islice(seq, [start,] stop [, step]) --&gt; elements from seq[start:stop:step]</li>
+<li>imap(fun, p, q, ...) --&gt; fun(p0, q0), fun(p1, q1), ...</li>
+<li>starmap(fun, seq) --&gt; fun(*seq[0]), fun(*seq[1]), ...</li>
+<li>tee(it, n=2) --&gt; (it1, it2 , ... itn) splits one iterator into n</li>
+<li>chain(p, q, ...) --&gt; p0, p1, ... plast, q0, q1, ...</li>
+<li>takewhile(pred, seq) --&gt; seq[0], seq[1], until pred fails</li>
+<li>dropwhile(pred, seq) --&gt; seq[n], seq[n+1], starting when pred fails</li>
+<li>groupby(iterable[, keyfunc]) --&gt; sub-iterators grouped by value of keyfunc(v)</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="anytrue">
+<h2><a name="anytrue">anyTrue</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; import itertools
+&gt;&gt;&gt; def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+...
+&gt;&gt;&gt; fname = &quot;picture.gif&quot;
+&gt;&gt;&gt; anyTrue(fname.endswith, &quot;.jpg .gif .png&quot;.split())
+True
+</pre>
+<p>AnyTrue does <em>short-circuit</em>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def is3(i):
+... print &quot;i=%s&quot; % i
+... return i == 3
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True
+</pre>
+</div>
+<div class="section" id="chop">
+<h2><a name="chop">chop</a></h2>
+<p>You want to chop an iterable in batches of a given size:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from chop import chop
+&gt;&gt;&gt; list(chop([1, 2, 3, 4], 2))
+[[1, 2], [3, 4]]
+&gt;&gt;&gt; list(chop([1, 2, 3, 4, 5, 6, 7],3))
+[[1, 2, 3], [4, 5, 6], [7]]
+</pre>
+<p>Here is a possible implementation:</p>
+<pre class="literal-block">
+#&lt;chop.py&gt;
+
+# see also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279
+
+import itertools
+
+def chop(iterable, batchsize):
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+#&lt;/chop.py&gt;
+</pre>
+<p>For people thinking Python is too readable, here is a one-liner:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chop = lambda it, n : itertools.izip(*(iter(it),)*n)
+...
+&gt;&gt;&gt; list(chop([1,2,3,4], 2))
+[(1, 2), (3, 4)]
+</pre>
+</div>
+<div class="section" id="tee">
+<h2><a name="tee">tee</a></h2>
+<p>To make copies of iterables; typically used in parsers:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from itertools import tee, chain, izip
+&gt;&gt;&gt; chars, prevs = tee(&quot;abc&quot;)
+&gt;&gt;&gt; prevs = chain([None], prevs)
+&gt;&gt;&gt; for char, prev in izip(chars, prevs):
+... print char, prev
+...
+a None
+b a
+c b
+</pre>
+</div>
+<div class="section" id="grouping-and-sorting">
+<h2><a name="grouping-and-sorting">Grouping and sorting</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; from itertools import groupby
+&gt;&gt;&gt; from operator import itemgetter
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; NAME, AGE = 0, 1
+&gt;&gt;&gt; query_result = (&quot;Smith&quot;, 34), (&quot;Donaldson&quot;, 34), (&quot;Lee&quot;, 22), (&quot;Orr&quot;, 22)
+</pre>
+<p>Grouping together people of the same age:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for k, g in groupby(query_result, key=itemgetter(AGE)):
+... print k, list(g)
+...
+34 [('Smith', 34), ('Donaldson', 34)]
+22 [('Lee', 22), ('Orr', 22)]
+</pre>
+<p>Sorting by name:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for tup in sorted(query_result, key=itemgetter(NAME)):
+... print tup
+('Donaldson', 34)
+('Lee', 22)
+('Orr', 22)
+('Smith', 34)
+</pre>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/loops.txt b/pypers/oxford/loops.txt
new file mode 100755
index 0000000..cb0bfcb
--- /dev/null
+++ b/pypers/oxford/loops.txt
@@ -0,0 +1,457 @@
+Lecture 1: Loops (i.e. iterators & generators)
+==============================================
+
+Part I: iterators
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Iterators are everywhere
+--------------------------------
+
+>>> for i in 1, 2, 3:
+... print i
+1
+2
+3
+
+The 'for' loop is using *iterators* internally::
+
+ it = iter((1,2,3))
+ while True:
+ try:
+ print it.next()
+ except StopIteration:
+ break
+
+Iterables and iterators
+--------------------------
+
+*Iterable* = anything you can loop over = any sequence + any object with an __iter__ method;
+
+Not every sequence has an __iter__ method:
+
+>>> "hello".__iter__()
+Traceback (most recent call last):
+ ...
+AttributeError: 'str' object has no attribute '__iter__'
+
+*Iterator* = any object with a .next method and an __iter__ method returning self
+
+Simpler way to get an iterator
+--------------------------------------------------------
+
+>>> it = iter("hello")
+>>> it.next()
+'h'
+>>> it.next()
+'e'
+>>> it.next()
+'l'
+>>> it.next()
+'l'
+>>> it.next()
+'o'
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+
+Sentinel syntax iter(callable, sentinel)
+--------------------------------------------
+
+Example::
+
+ $ echo -e "value1\nvalue2\nEND\n" > data.txt
+ $ python -c "print list(iter(file('data.txt').readline, 'END\n'))"
+ ['value1\n', 'value2\n']
+
+Beware of infinite iterators:
+
+>>> repeat = iter(lambda : "some value", "")
+>>> repeat.next()
+'some value'
+
+Second simpler way to get an iterator: generator-expressions
+-------------------------------------------------------------
+
+>>> squares = (i*i for i in range(1,11))
+>>> list(squares)
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
+
+Excessive parenthesis can be skipped, so use
+
+>>> dict((i, i*i) for i in range(1,11))
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+
+instead of
+
+>>> dict([(i, i*i) for i in range(1,11)])
+{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
+
+(as usual, the most elegant version is the most efficient).
+
+Iteration caveats
+--------------------------
+
+>>> ls = [i for i in (1,2,3)]
+>>> i
+3
+
+>>> it = (j for j in (1,2,3))
+>>> j
+Traceback (most recent call last):
+ ...
+NameError: name 'j' is not defined
+
+A subtler example:
+
+>>> ls = [lambda :i for i in (1,2,3)]
+>>> ls[0]()
+3
+
+instead
+
+>>> it = (lambda :i for i in (1,2,3))
+>>> it.next()()
+1
+>>> it.next()()
+2
+>>> it.next()()
+3
+
+*seems* to be working but it is not really the case:
+
+>>> it = (lambda :i for i in (1,2,3))
+>>> f1 = it.next()
+>>> f2 = it.next()
+>>> f3 = it.next()
+>>> f1()
+3
+
+The reason is that Python does LATE binding *always*. The solution is ugly:
+
+>>> it = list(lambda i=i:i for i in (1,2,3))
+>>> it[0]()
+1
+>>> it[1]()
+2
+>>> it[2]()
+3
+
+Part II: generators
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Trivial example:
+
+>>> def gen123(): # "function" which returns an iterator over the values 1, 2, 3
+... yield 1
+... yield 2
+... yield 3
+...
+>>> it = gen123()
+>>> it.next()
+1
+>>> it.next()
+2
+>>> it.next()
+3
+>>> it.next()
+Traceback (most recent call last):
+ ...
+StopIteration
+
+Real life example: using generators to generate HTML tables
+
+::
+
+ #<htmltable.py>
+
+ def HTMLTablegen(table):
+ yield "<table>"
+ for row in table:
+ yield "<tr>"
+ for col in row:
+ yield "<td>%s</td>" % col
+ yield "</tr>"
+ yield "</table>"
+
+ def test():
+ return "\n".join(HTMLTablegen([["Row", "City"],
+ [1,'London'], [2, 'Oxford']]))
+
+ if __name__ == "__main__": # example
+ print test()
+
+ #</htmltable.py>
+
+>>> from htmltable import test
+>>> print test()
+<table>
+<tr>
+<td>Row</td>
+<td>City</td>
+</tr>
+<tr>
+<td>1</td>
+<td>London</td>
+</tr>
+<tr>
+<td>2</td>
+<td>Oxford</td>
+</tr>
+</table>
+
+A simple recipe: skip redundant
+---------------------------------
+
+How to remove duplicates by keeping the order::
+
+ #<skip_redundant.py>
+
+ def skip_redundant(iterable, skipset=None):
+ "Redundant items are repeated items or items in the original skipset."
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+ #</skip_redundant.py>
+
+>>> from skip_redundant import skip_redundant
+>>> print list(skip_redundant("<hello, world>", skipset=set("<>")))
+['h', 'e', 'l', 'o', ',', ' ', 'w', 'r', 'd']
+
+Another real life example: working with nested structures
+----------------------------------------------------------
+
+::
+
+ #<walk.py>
+
+ def walk(iterable, level=0):
+ for obj in iterable:
+ if not hasattr(obj, "__iter__"): # atomic object
+ yield obj, level
+ else: # composed object: iterate again
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+ def flatten(iterable):
+ return (obj for obj, level in walk(iterable))
+
+ def pprint(iterable):
+ for obj, level in walk(iterable):
+ print " "*level, obj
+
+ #</walk.py>
+
+>>> from walk import flatten, pprint
+>>> nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+>>> pprint(nested_ls)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+>>> pprint(flatten(nested_ls))
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+
+Another typical use case for generators: parsers
+---------------------------------------------------------
+
+A very stripped down parser for nested expressions
+
+::
+
+ #<sexpr2indent.py>
+ """A simple s-expression formatter."""
+
+ import re
+
+ def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r"(?P<paren_beg>\()|(?P<paren_end>\))")
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == "paren_beg":
+ nesting_level += 1
+ elif match.lastgroup == "paren_end":
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+ def sexpr_indent(sexpr):
+ for nesting, text in parse(sexpr.replace("\n", "")):
+ if text.strip(): print " "*nesting, text
+
+ #</sexpr2indent.py>
+
+>>> from sexpr2indent import sexpr_indent
+>>> sexpr_indent("""\
+... (html (head (title Example)) (body (h1 s-expr formatter example)
+... (a (@ (href http://www.example.com)) A link)))""")
+... #doctest: +NORMALIZE_WHITESPACE
+ html
+ head
+ title Example
+ body
+ h1 s-expr formatter example
+ a
+ @
+ href http://www.example.com
+ A link
+
+
+Other kinds of iterables
+------------------------------------------------
+
+The following class generates iterable which are not iterators:
+::
+
+ #<reiterable.py>
+
+ class ReIter(object):
+ "A re-iterable object."
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+ #</reiterable.py>
+
+>>> from reiterable import ReIter
+>>> rit = ReIter()
+>>> list(rit)
+[1, 2, 3]
+>>> list(rit) # it is reiterable!
+[1, 2, 3]
+
+The itertools module
+----------------------------------------------------
+
+ - count([n]) --> n, n+1, n+2, ...
+ - cycle(p) --> p0, p1, ... plast, p0, p1, ...
+ - repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times
+ - izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...
+ - ifilter(pred, seq) --> elements of seq where pred(elem) is True
+ - ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False
+ - islice(seq, [start,] stop [, step]) --> elements from seq[start:stop:step]
+ - imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...
+ - starmap(fun, seq) --> fun(\*seq[0]), fun(\*seq[1]), ...
+ - tee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n
+ - chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...
+ - takewhile(pred, seq) --> seq[0], seq[1], until pred fails
+ - dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails
+ - groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)
+
+anyTrue
+------------------------------
+
+>>> import itertools
+>>> def anyTrue(predicate, iterable):
+... return True in itertools.imap(predicate, iterable)
+...
+>>> fname = "picture.gif"
+>>> anyTrue(fname.endswith, ".jpg .gif .png".split())
+True
+
+AnyTrue does *short-circuit*:
+
+>>> def is3(i):
+... print "i=%s" % i
+... return i == 3
+
+>>> anyTrue(is3, range(10))
+i=0
+i=1
+i=2
+i=3
+True
+
+chop
+----------------------
+
+You want to chop an iterable in batches of a given size:
+
+>>> from chop import chop
+>>> list(chop([1, 2, 3, 4], 2))
+[[1, 2], [3, 4]]
+>>> list(chop([1, 2, 3, 4, 5, 6, 7],3))
+[[1, 2, 3], [4, 5, 6], [7]]
+
+Here is a possible implementation::
+
+ #<chop.py>
+
+ # see also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303279
+
+ import itertools
+
+ def chop(iterable, batchsize):
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+ #</chop.py>
+
+For people thinking Python is too readable, here is a one-liner:
+
+>>> chop = lambda it, n : itertools.izip(*(iter(it),)*n)
+...
+>>> list(chop([1,2,3,4], 2))
+[(1, 2), (3, 4)]
+
+tee
+-----------------------
+
+To make copies of iterables; typically used in parsers:
+
+>>> from itertools import tee, chain, izip
+>>> chars, prevs = tee("abc")
+>>> prevs = chain([None], prevs)
+>>> for char, prev in izip(chars, prevs):
+... print char, prev
+...
+a None
+b a
+c b
+
+Grouping and sorting
+----------------------
+
+>>> from itertools import groupby
+>>> from operator import itemgetter
+
+>>> NAME, AGE = 0, 1
+>>> query_result = ("Smith", 34), ("Donaldson", 34), ("Lee", 22), ("Orr", 22)
+
+Grouping together people of the same age:
+
+>>> for k, g in groupby(query_result, key=itemgetter(AGE)):
+... print k, list(g)
+...
+34 [('Smith', 34), ('Donaldson', 34)]
+22 [('Lee', 22), ('Orr', 22)]
+
+Sorting by name:
+
+>>> for tup in sorted(query_result, key=itemgetter(NAME)):
+... print tup
+('Donaldson', 34)
+('Lee', 22)
+('Orr', 22)
+('Smith', 34)
diff --git a/pypers/oxford/magic.html b/pypers/oxford/magic.html
new file mode 100755
index 0000000..048c237
--- /dev/null
+++ b/pypers/oxford/magic.html
@@ -0,0 +1,717 @@
+<?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.3.7: http://docutils.sourceforge.net/" />
+<title>Lecture 3: Magic (i.e. decorators and metaclasses)</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="lecture-3-magic-i-e-decorators-and-metaclasses">
+<h1 class="title">Lecture 3: Magic (i.e. decorators and metaclasses)</h1>
+<div class="section" id="part-i-decorators">
+<h1><a name="part-i-decorators">Part I: decorators</a></h1>
+<p>Decorators are just sugar: their functionality was already in the language</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def s(): pass
+&gt;&gt;&gt; s = staticmethod(s)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; &#64;staticmethod
+... def s(): pass
+...
+</pre>
+<p>However sugar <em>does</em> matter.</p>
+<div class="section" id="a-typical-decorator-traced">
+<h2><a name="a-typical-decorator-traced">A typical decorator: traced</a></h2>
+<pre class="literal-block">
+#&lt;traced.py&gt;
+
+def traced(func):
+ def tracedfunc(*args, **kw):
+ print &quot;calling %s.%s&quot; % (func.__module__, func.__name__)
+ return func(*args, **kw)
+ tracedfunc.__name__ = func.__name__
+ return tracedfunc
+
+&#64;traced
+def f(): pass
+
+#&lt;/traced.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from traced import f
+&gt;&gt;&gt; f()
+calling traced.f
+</pre>
+</div>
+<div class="section" id="a-decorator-factory-timed">
+<h2><a name="a-decorator-factory-timed">A decorator factory: Timed</a></h2>
+<pre class="literal-block">
+#&lt;timed.py&gt;
+
+import sys, time
+
+class Timed(object):
+ &quot;&quot;&quot;Decorator factory: each decorator object wraps a function and
+ executes it many times (default 100 times).
+ The average time spent in one iteration, expressed in milliseconds,
+ is stored in the attributes wrappedfunc.time and wrappedfunc.clocktime,
+ and displayed into a log file which defaults to stdout.
+ &quot;&quot;&quot;
+ def __init__(self, repeat=100, logfile=sys.stdout):
+ self.repeat = repeat
+ self.logfile = logfile
+ def __call__(self, func):
+ def wrappedfunc(*args, **kw):
+ fullname = &quot;%s.%s ...&quot; % (func.__module__, func.func_name)
+ print &gt;&gt; self.logfile, 'Executing %s' % fullname.ljust(30),
+ time1 = time.time()
+ clocktime1 = time.clock()
+ for i in xrange(self.repeat):
+ res = func(*args,**kw) # executes func self.repeat times
+ time2 = time.time()
+ clocktime2 = time.clock()
+ wrappedfunc.time = 1000*(time2-time1)/self.repeat
+ wrappedfunc.clocktime = 1000*(clocktime2 - clocktime1)/self.repeat
+ print &gt;&gt; self.logfile, \
+ 'Real time: %s ms;' % self.rounding(wrappedfunc.time),
+ print &gt;&gt; self.logfile, \
+ 'Clock time: %s ms' % self.rounding(wrappedfunc.clocktime)
+ return res
+ wrappedfunc.func_name = func.func_name
+ wrappedfunc.__module__ = func.__module__
+ return wrappedfunc
+ &#64;staticmethod
+ def rounding(float_):
+ &quot;Three digits rounding for small numbers, 1 digit rounding otherwise.&quot;
+ if float_ &lt; 10.:
+ return &quot;%5.3f&quot; % float_
+ else:
+ return &quot;%5.1f&quot; % float_
+
+#&lt;/timed.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from timed import Timed
+&gt;&gt;&gt; from random import sample
+&gt;&gt;&gt; example_ls = sample(xrange(1000000), 1000)
+&gt;&gt;&gt; &#64;Timed()
+... def list_sort(ls):
+... ls.sort()
+...
+&gt;&gt;&gt; list_sort(example_ls) #doctest: +ELLIPSIS
+Executing __main__.list_sort ... Real time: 0... ms; Clock time: 0... ms
+</pre>
+</div>
+<div class="section" id="towards-decorator-patterns">
+<h2><a name="towards-decorator-patterns">Towards decorator patterns</a></h2>
+<pre class="literal-block">
+#&lt;traced_function2.py&gt;
+
+from decorators import decorator
+
+def trace(f, *args, **kw):
+ print &quot;calling %s with args %s, %s&quot; % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+traced_function = decorator(trace)
+
+&#64;traced_function
+def f1(x):
+ pass
+
+&#64;traced_function
+def f2(x, y):
+ pass
+
+#&lt;/traced_function2.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from traced_function2 import traced_function, f1, f2
+&gt;&gt;&gt; f1(1)
+calling f1 with args (1,), {}
+&gt;&gt;&gt; f2(1,2)
+calling f2 with args (1, 2), {}
+</pre>
+<p>works with pydoc:</p>
+<pre class="literal-block">
+$ pydoc2.4 traced_function2.f2
+Help on function f1 in traced_function2:
+
+traced_function2.f1 = f1(x)
+
+$ pydoc2.4 traced_function2.f2
+Help on function f2 in traced_function2:
+
+traced_function2.f2 = f2(x, y)
+</pre>
+<p>Here is the source code:</p>
+<pre class="literal-block">
+#&lt;decorators.py&gt;
+
+import inspect
+
+def _signature_gen(func, rm_defaults=False):
+ argnames, varargs, varkwargs, defaults = inspect.getargspec(func)
+ argdefs = defaults or ()
+ n_args = func.func_code.co_argcount
+ n_default_args = len(argdefs)
+ n_non_default_args = n_args - n_default_args
+ non_default_names = argnames[:n_non_default_args]
+ default_names = argnames[n_non_default_args:]
+ for name in non_default_names:
+ yield &quot;%s&quot; % name
+ for i, name in enumerate(default_names):
+ if rm_defaults:
+ yield name
+ else:
+ yield &quot;%s = arg[%s]&quot; % (name, i)
+ if varargs:
+ yield &quot;*%s&quot; % varargs
+ if varkwargs:
+ yield &quot;**%s&quot; % varkwargs
+
+def _decorate(func, caller):
+ signature = &quot;, &quot;.join(_signature_gen(func))
+ variables = &quot;, &quot;.join(_signature_gen(func, rm_defaults=True))
+ lambda_src = &quot;lambda %s: call(func, %s)&quot; % (signature, variables)
+ evaldict = dict(func=func, call=caller, arg=func.func_defaults or ())
+ dec_func = eval(lambda_src, evaldict)
+ dec_func.__name__ = func.__name__
+ dec_func.__doc__ = func.__doc__
+ dec_func.__dict__ = func.__dict__ # copy if you want to avoid sharing
+ return dec_func
+
+class decorator(object):
+ &quot;&quot;&quot;General purpose decorator factory, taking a caller function as
+ input. A caller function is any function like this:
+
+ def caller(func, *args, **kw):
+ # do something
+ return func(*args, **kw)
+ &quot;&quot;&quot;
+ def __init__(self, caller):
+ self.caller = caller
+ def __call__(self, func):
+ return _decorate(func, self.caller)
+
+#&lt;/decorators.py&gt;
+</pre>
+<p>The possibilities of this pattern are endless:</p>
+<pre class="literal-block">
+#&lt;deferred.py&gt;
+
+import threading
+from decorators import decorator
+
+def deferred(nsec):
+ def inner_deferred(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(inner_deferred)
+
+&#64;deferred(2)
+def hello():
+ print &quot;hello&quot;
+
+#&lt;deferred.py&gt;
+</pre>
+<p>Show an example of an experimental decorator based web framework
+(doctester_frontend).</p>
+</div>
+</div>
+<div class="section" id="part-ii-metaclasses">
+<h1><a name="part-ii-metaclasses">Part II: metaclasses</a></h1>
+<p>Metaclasses are there! Consider this example from a recent post on c.l.py:</p>
+<pre class="literal-block">
+#&lt;BaseClass.py&gt;
+
+class BaseClass(object):
+ &quot;Do something&quot;
+
+#&lt;/BaseClass.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; import BaseClass # instead of 'from BaseClass import BaseClass'
+&gt;&gt;&gt; class C(BaseClass): pass
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ module.__init__() takes at most 2 arguments (3 given)
+</pre>
+<p>The reason for the error is that class <tt class="docutils literal"><span class="pre">C(BaseClass):</span> <span class="pre">pass</span></tt> is
+actually calling the <tt class="docutils literal"><span class="pre">type</span></tt> metaclass with three arguments:</p>
+<pre class="literal-block">
+C = type(&quot;C&quot;, (BaseClass,), {})
+</pre>
+<p><tt class="docutils literal"><span class="pre">type.__new__</span></tt> tries to use <tt class="docutils literal"><span class="pre">type(BaseClass)</span></tt> as metaclass,
+but since BaseClass here is a module, and <tt class="docutils literal"><span class="pre">ModuleType</span></tt> is not
+a metaclass, it cannot work. The error message reflects a conflict with
+the signature of ModuleType which requires two parameters and not three.</p>
+<p>So even if you don't use them, you may want to know they exist.</p>
+<div class="section" id="rejuvenating-old-style-classes">
+<h2><a name="rejuvenating-old-style-classes">Rejuvenating old-style classes</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Old: pass
+&gt;&gt;&gt; print type(Old)
+&lt;type 'classobj'&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; __metaclass__ = type # to rejuvenate class
+&gt;&gt;&gt; class NotOld: pass
+...
+&gt;&gt;&gt; print NotOld.__class__
+&lt;type 'type'&gt;
+</pre>
+</div>
+<div class="section" id="a-typical-metaclass-example-metatracer">
+<h2><a name="a-typical-metaclass-example-metatracer">A typical metaclass example: MetaTracer</a></h2>
+<pre class="literal-block">
+#&lt;metatracer.py&gt;
+import inspect
+
+def trace(meth, cls):
+ def traced(*args, **kw):
+ modname = meth.__module__ or cls.__module__
+ print &quot;calling %s.%s.%s&quot; % (modname, cls.__name__, meth.__name__)
+ return meth(*args, **kw)
+ traced.__name__ = meth.__name__
+ return traced
+
+class MetaTracer(type):
+ def __init__(cls, name, bases, dic):
+ super(MetaTracer, cls).__init__(name, bases, dic)
+ for k, v in dic.iteritems():
+ if inspect.isfunction(v):
+ setattr(cls, k, trace(v, cls))
+
+#&lt;/metatracer.py&gt;
+</pre>
+<p>Usage: exploring classes in the standard library</p>
+<pre class="literal-block">
+#&lt;dictmixin.py&gt;
+
+from metatracer import MetaTracer
+from UserDict import DictMixin
+
+class TracedDM(DictMixin, object):
+ __metaclass__ = MetaTracer
+ def __getitem__(self, item):
+ return item
+ def keys(self):
+ return [1,2,3]
+
+#&lt;/dictmixin.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from dictmixin import TracedDM
+&gt;&gt;&gt; print TracedDM()
+calling dictmixin.TracedDM.keys
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+{1: 1, 2: 2, 3: 3}
+</pre>
+</div>
+<div class="section" id="real-life-example-check-overriding">
+<h2><a name="real-life-example-check-overriding">Real life example: check overriding</a></h2>
+<pre class="literal-block">
+#&lt;check_overriding.py&gt;
+
+class Base(object):
+ a = 0
+
+class CheckOverriding(type):
+ &quot;Prints a message if we are overriding a name.&quot;
+ def __new__(mcl, name, bases, dic):
+ for name, val in dic.iteritems():
+ if name.startswith(&quot;__&quot;) and name.endswith(&quot;__&quot;):
+ continue # ignore special names
+ a_base_has_name = True in (hasattr(base, name) for base in bases)
+ if a_base_has_name:
+ print &quot;AlreadyDefinedNameWarning: &quot; + name
+ return super(CheckOverriding, mcl).__new__(mcl, name, bases, dic)
+
+class MyClass(Base):
+ __metaclass__ = CheckOverriding
+ a = 1
+
+class ChildClass(MyClass):
+ a = 2
+</pre>
+<p>#&lt;/check_overriding.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import check_overriding
+AlreadyDefinedNameWarning: a
+AlreadyDefinedNameWarning: a
+</pre>
+</div>
+<div class="section" id="logfile">
+<h2><a name="logfile">LogFile</a></h2>
+<pre class="literal-block">
+#&lt;logfile.py&gt;
+
+import subprocess
+
+def memoize(func):
+ memoize_dic = {}
+ def wrapped_func(*args):
+ if args in memoize_dic:
+ return memoize_dic[args]
+ else:
+ result = func(*args)
+ memoize_dic[args] = result
+ return result
+ wrapped_func.__name__ = func.__name__
+ wrapped_func.__doc__ = func.__doc__
+ wrapped_func.__dict__ = func.__dict__
+ return wrapped_func
+
+class Memoize(type): # Singleton is a special case of Memoize
+ &#64;memoize
+ def __call__(cls, *args):
+ return super(Memoize, cls).__call__(*args)
+
+class LogFile(file):
+ &quot;&quot;&quot;Open a file for append.&quot;&quot;&quot;
+ __metaclass__ = Memoize
+ def __init__(self, name = &quot;/tmp/err.log&quot;):
+ self.viewer_cmd = 'xterm -e less'.split()
+ super(LogFile, self).__init__(name, &quot;a&quot;)
+
+ def display(self, *ls):
+ &quot;Use 'less' to display the log file in a separate xterm.&quot;
+ print &gt;&gt; self, &quot;\n&quot;.join(map(str, ls)); self.flush()
+ subprocess.call(self.viewer_cmd + [self.name])
+
+ def reset(self):
+ &quot;Erase the log file.&quot;
+ print &gt;&gt; file(self.name, &quot;w&quot;)
+
+if __name__ == &quot;__main__&quot;: # test
+ print &gt;&gt; LogFile(), &quot;hello&quot;
+ print &gt;&gt; LogFile(), &quot;world&quot;
+ LogFile().display()
+
+#&lt;/logfile.py&gt;
+
+$ python logfile.py
+</pre>
+</div>
+<div class="section" id="cooperative-hierarchies">
+<h2><a name="cooperative-hierarchies">Cooperative hierarchies</a></h2>
+<pre class="literal-block">
+#&lt;cooperative_init.py&gt;
+
+&quot;&quot;&quot;Given a hierarchy, makes __init__ cooperative.
+The only change needed is to add a line
+
+ __metaclass__ = CooperativeInit
+
+to the base class of your hierarchy.&quot;&quot;&quot;
+
+from decorators import decorator
+
+def make_cooperative_init(cls, name, bases, dic):
+
+ def call_cooperatively(__init__, self, *args, **kw):
+ super(cls, self).__init__(*args, **kw)
+ __init__(self, *args, **kw)
+
+ __init__ = cls.__dict__.get(&quot;__init__&quot;)
+ if __init__:
+ cls.__init__ = decorator(call_cooperatively)(__init__)
+
+class CooperativeInit(type):
+ __init__ = make_cooperative_init
+
+class Base:
+ __metaclass__ = CooperativeInit
+ def __init__(self):
+ print &quot;B.__init__&quot;
+
+class C1(Base):
+ def __init__(self):
+ print &quot;C1.__init__&quot;
+
+class C2(Base):
+ def __init__(self):
+ print &quot;C2.__init__&quot;
+
+class D(C1, C2):
+ def __init__(self):
+ print &quot;D.__init__&quot;
+
+#&lt;/cooperative_init.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from cooperative_init import D
+&gt;&gt;&gt; d = D()
+B.__init__
+C2.__init__
+C1.__init__
+D.__init__
+</pre>
+</div>
+<div class="section" id="metaclass-enhanced-modules">
+<h2><a name="metaclass-enhanced-modules">Metaclass-enhanced modules</a></h2>
+<pre class="literal-block">
+#&lt;import_with_metaclass.py&gt;
+&quot;&quot;&quot;
+``import_with_metaclass(metaclass, modulepath)`` generates
+a new module from and old module, by enhancing all of its classes.
+This is not perfect, but it should give you a start.&quot;&quot;&quot;
+
+import os, sys, inspect, types
+
+def import_with_metaclass(metaclass, modulepath):
+ modname = os.path.basename(modulepath)[:-3] # simplistic
+ mod = types.ModuleType(modname)
+ locs = dict(
+ __module__ = modname,
+ __file__ = modulepath,
+ __metaclass__ = metaclass,
+ object = metaclass(&quot;object&quot;, (), {}))
+ execfile(modulepath, locs)
+ for k, v in locs.iteritems():
+ setattr(mod, k, v)
+ return mod
+</pre>
+<p>#&lt;/import_with_metaclass.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from import_with_metaclass import import_with_metaclass
+&gt;&gt;&gt; from tracer import MetaTracer
+&gt;&gt;&gt; traced_optparse = import_with_metaclass(MetaTracer,
+... &quot;/usr/lib/python2.4/optparse.py&quot;)
+&gt;&gt;&gt; op = traced_optparse.OptionParser()
+calling __main__.OptionParser.__init__
+calling __main__.OptionContainer.__init__
+calling __main__.OptionParser._create_option_list
+calling __main__.OptionContainer._create_option_mappings
+calling __main__.OptionContainer.set_conflict_handler
+calling __main__.OptionContainer.set_description
+calling __main__.OptionParser.set_usage
+calling __main__.IndentedHelpFormatter.__init__
+calling __main__.HelpFormatter.__init__
+calling __main__.HelpFormatter.set_parser
+calling __main__.OptionParser._populate_option_list
+calling __main__.OptionParser._add_help_option
+calling __main__.OptionContainer.add_option
+calling __main__.Option.__init__
+calling __main__.Option._check_opt_strings
+calling __main__.Option._set_opt_strings
+calling __main__.Option._set_attrs
+calling __main__.OptionContainer._check_conflict
+calling __main__.OptionParser._init_parsing_state
+</pre>
+</div>
+<div class="section" id="magic-properties">
+<h2><a name="magic-properties">Magic properties</a></h2>
+<pre class="literal-block">
+#&lt;magicprop.py&gt;
+
+class MagicProperties(type):
+ def __init__(cls, name, bases, dic):
+ prop_names = set(name[3:] for name in dic
+ if name.startswith(&quot;get&quot;)
+ or name.startswith(&quot;set&quot;))
+ for name in prop_names:
+ getter = getattr(cls, &quot;get&quot; + name, None)
+ setter = getattr(cls, &quot;set&quot; + name, None)
+ setattr(cls, name, property(getter, setter))
+
+class Base(object):
+ __metaclass__ = MagicProperties
+ def getx(self):
+ return self._x
+ def setx(self, value):
+ self._x = value
+
+class Child(Base):
+ def getx(self):
+ print &quot;getting x&quot;
+ return super(Child, self).getx()
+ def setx(self, value):
+ print &quot;setting x&quot;
+ super(Child, self).setx(value)
+
+#&lt;/magicprop.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from magicprop import Child
+&gt;&gt;&gt; c = Child()
+&gt;&gt;&gt; c.x = 1
+setting x
+&gt;&gt;&gt; print c.x
+getting x
+1
+</pre>
+</div>
+<div class="section" id="hack-evil-properties">
+<h2><a name="hack-evil-properties">Hack: evil properties</a></h2>
+<pre class="literal-block">
+#&lt;evilprop.py&gt;
+
+def convert2property(name, bases, d):
+ return property(d.get('get'), d.get('set'),
+ d.get('del'),d.get('__doc__'))
+
+class C(object):
+ class x:
+ &quot;&quot;&quot;An evil test property&quot;&quot;&quot;
+ __metaclass__ = convert2property
+ def get(self):
+ print 'Getting %s' % self._x
+ return self._x
+ def set(self, value):
+ self._x = value
+ print 'Setting to', value
+
+#&lt;/evilprop.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from evilprop import C
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.x = 5
+Setting to 5
+&gt;&gt;&gt; c.x
+Getting 5
+5
+&gt;&gt;&gt; print C.x.__doc__
+An evil test property
+</pre>
+</div>
+<div class="section" id="why-i-suggest-not-to-use-metaclasses-in-production-code">
+<h2><a name="why-i-suggest-not-to-use-metaclasses-in-production-code">Why I suggest <em>not</em> to use metaclasses in production code</a></h2>
+<blockquote>
+<ul class="simple">
+<li>there are very few good use case for metaclasses in production code
+(i.e. 99% of time you don't need them)</li>
+<li>they put a cognitive burden on the developer;</li>
+<li>a design without metaclasses is less magic and likely more robust;</li>
+<li>a design with metaclasses makes it difficult to use other metaclasses
+for debugging.</li>
+</ul>
+</blockquote>
+<p>As far as I know, string.Template is the only metaclass-enhanced class
+in the standard library; the metaclass is used to give the possibility to
+change the defaults:</p>
+<pre class="literal-block">
+delimiter = '$'
+idpattern = r'[_a-z][_a-z0-9]*'
+</pre>
+<p>in subclasses of Template.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from string import Template
+&gt;&gt;&gt; from tracer import MetaTracer
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = MetaTracer
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>Solution: use a consistent metaclass</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class GoodMeta(MetaTracer, type(Template)): pass
+...
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = GoodMeta
+</pre>
+</div>
+<div class="section" id="is-there-an-automatic-way-of-solving-the-conflict">
+<h2><a name="is-there-an-automatic-way-of-solving-the-conflict">Is there an automatic way of solving the conflict?</a></h2>
+<p>Yes, but you really need to be a metaclass wizard.</p>
+<p><a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197</a></p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from noconflict import classmaker
+&gt;&gt;&gt; class TracedTemplate(Template):
+... __metaclass__ = classmaker((MetaTracer,))
+&gt;&gt;&gt; print type(TracedTemplate)
+&lt;class 'noconflict._MetaTracer_TemplateMetaclass'&gt;
+</pre>
+<pre class="literal-block">
+#&lt;noconflict.py&gt;
+
+import inspect, types, __builtin__
+from skip_redundant import skip_redundant
+
+memoized_metaclasses_map = {}
+
+# utility function
+def remove_redundant(metaclasses):
+ skipset = set([types.ClassType])
+ for meta in metaclasses: # determines the metaclasses to be skipped
+ skipset.update(inspect.getmro(meta)[1:])
+ return tuple(skip_redundant(metaclasses, skipset))
+
+##################################################################
+## now the core of the module: two mutually recursive functions ##
+##################################################################
+
+def get_noconflict_metaclass(bases, left_metas, right_metas):
+ &quot;&quot;&quot;Not intended to be used outside of this module, unless you know
+ what you are doing.&quot;&quot;&quot;
+ # make tuple of needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ needed_metas = remove_redundant(metas)
+
+ # return existing confict-solving meta, if any
+ if needed_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[needed_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ elif not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ # check for recursion, can happen i.e. for Zope ExtensionClasses
+ elif needed_metas == bases:
+ raise TypeError(&quot;Incompatible root metatypes&quot;, needed_metas)
+ else: # gotta work ...
+ metaname = '_' + ''.join([m.__name__ for m in needed_metas])
+ meta = classmaker()(metaname, needed_metas, {})
+ memoized_metaclasses_map[needed_metas] = meta
+ return meta
+
+def classmaker(left_metas=(), right_metas=()):
+ def make_class(name, bases, adict):
+ metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
+ return metaclass(name, bases, adict)
+ return make_class
+
+#################################################################
+## and now a conflict-safe replacement for 'type' ##
+#################################################################
+
+__type__=__builtin__.type # the aboriginal 'type'
+# left available in case you decide to rebind __builtin__.type
+
+class safetype(__type__):
+ # this is REALLY DEEP MAGIC
+ &quot;&quot;&quot;Overrides the ``__new__`` method of the ``type`` metaclass, making the
+ generation of classes conflict-proof.&quot;&quot;&quot;
+ def __new__(mcl, *args):
+ nargs = len(args)
+ if nargs == 1: # works as __builtin__.type
+ return __type__(args[0])
+ elif nargs == 3: # creates the class using the appropriate metaclass
+ n, b, d = args # name, bases and dictionary
+ meta = get_noconflict_metaclass(b, (mcl,), ())
+ if meta is mcl: # meta is trivial, dispatch to the default __new__
+ return super(safetype, mcl).__new__(mcl, n, b, d)
+ else: # non-trivial metaclass, dispatch to the right __new__
+ # (it will take a second round) # print mcl, meta
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
+
+#&lt;/noconflict.py&gt;
+</pre>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/magic.txt b/pypers/oxford/magic.txt
new file mode 100755
index 0000000..9437ac5
--- /dev/null
+++ b/pypers/oxford/magic.txt
@@ -0,0 +1,783 @@
+Lecture 3: Magic (i.e. decorators and metaclasses)
+================================================================
+
+Part I: decorators
++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Decorators are just sugar: their functionality was already in the language
+
+>>> def s(): pass
+>>> s = staticmethod(s)
+
+>>> @staticmethod
+... def s(): pass
+...
+
+However sugar *does* matter.
+
+A typical decorator: traced
+-----------------------------
+::
+
+ #<traced.py>
+
+ def traced(func):
+ def tracedfunc(*args, **kw):
+ print "calling %s.%s" % (func.__module__, func.__name__)
+ return func(*args, **kw)
+ tracedfunc.__name__ = func.__name__
+ return tracedfunc
+
+ @traced
+ def f(): pass
+
+ #</traced.py>
+
+>>> from traced import f
+>>> f()
+calling traced.f
+
+A decorator factory: Timed
+------------------------------------------
+
+::
+
+ #<timed.py>
+
+ import sys, time
+
+ class Timed(object):
+ """Decorator factory: each decorator object wraps a function and
+ executes it many times (default 100 times).
+ The average time spent in one iteration, expressed in milliseconds,
+ is stored in the attributes wrappedfunc.time and wrappedfunc.clocktime,
+ and displayed into a log file which defaults to stdout.
+ """
+ def __init__(self, repeat=100, logfile=sys.stdout):
+ self.repeat = repeat
+ self.logfile = logfile
+ def __call__(self, func):
+ def wrappedfunc(*args, **kw):
+ fullname = "%s.%s ..." % (func.__module__, func.func_name)
+ print >> self.logfile, 'Executing %s' % fullname.ljust(30),
+ time1 = time.time()
+ clocktime1 = time.clock()
+ for i in xrange(self.repeat):
+ res = func(*args,**kw) # executes func self.repeat times
+ time2 = time.time()
+ clocktime2 = time.clock()
+ wrappedfunc.time = 1000*(time2-time1)/self.repeat
+ wrappedfunc.clocktime = 1000*(clocktime2 - clocktime1)/self.repeat
+ print >> self.logfile, \
+ 'Real time: %s ms;' % self.rounding(wrappedfunc.time),
+ print >> self.logfile, \
+ 'Clock time: %s ms' % self.rounding(wrappedfunc.clocktime)
+ return res
+ wrappedfunc.func_name = func.func_name
+ wrappedfunc.__module__ = func.__module__
+ return wrappedfunc
+ @staticmethod
+ def rounding(float_):
+ "Three digits rounding for small numbers, 1 digit rounding otherwise."
+ if float_ < 10.:
+ return "%5.3f" % float_
+ else:
+ return "%5.1f" % float_
+
+ #</timed.py>
+
+>>> from timed import Timed
+>>> from random import sample
+>>> example_ls = sample(xrange(1000000), 1000)
+>>> @Timed()
+... def list_sort(ls):
+... ls.sort()
+...
+>>> list_sort(example_ls) #doctest: +ELLIPSIS
+Executing __main__.list_sort ... Real time: 0... ms; Clock time: 0... ms
+
+
+A powerful decorator pattern
+--------------------------------
+::
+
+ #<traced_function2.py>
+
+ from decorators import decorator
+
+ def trace(f, *args, **kw):
+ print "calling %s with args %s, %s" % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+ traced_function = decorator(trace)
+
+ @traced_function
+ def f1(x):
+ pass
+
+ @traced_function
+ def f2(x, y):
+ pass
+
+ #</traced_function2.py>
+
+>>> from traced_function2 import traced_function, f1, f2
+>>> f1(1)
+calling f1 with args (1,), {}
+>>> f2(1,2)
+calling f2 with args (1, 2), {}
+
+works with pydoc::
+
+ $ pydoc2.4 traced_function2.f2
+ Help on function f1 in traced_function2:
+
+ traced_function2.f1 = f1(x)
+
+ $ pydoc2.4 traced_function2.f2
+ Help on function f2 in traced_function2:
+
+ traced_function2.f2 = f2(x, y)
+
+Here is the source code::
+
+ #<decorators.py>
+
+ import inspect, itertools
+
+ def getinfo(func):
+ """Return an info dictionary containing:
+ - name (the name of the function : str)
+ - argnames (the names of the arguments : list)
+ - defarg (the values of the default arguments : list)
+ - fullsign (the full signature : str)
+ - shortsign (the short signature : str)
+ - arg0 ... argn (shortcuts for the names of the arguments)
+
+ >> def f(self, x=1, y=2, *args, **kw): pass
+
+ >> info = getinfo(f)
+
+ >> info["name"]
+ 'f'
+ >> info["argnames"]
+ ['self', 'x', 'y', 'args', 'kw']
+
+ >> info["defarg"]
+ (1, 2)
+
+ >> info["shortsign"]
+ 'self, x, y, *args, **kw'
+
+ >> info["fullsign"]
+ 'self, x=defarg[0], y=defarg[1], *args, **kw'
+
+ >> info["arg0"], info["arg1"], info["arg2"], info["arg3"], info["arg4"]
+ ('self', 'x', 'y', 'args', 'kw')
+ """
+ assert inspect.ismethod(func) or inspect.isfunction(func)
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+ argnames = list(regargs)
+ if varargs: argnames.append(varargs)
+ if varkwargs: argnames.append(varkwargs)
+ counter = itertools.count()
+ fullsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "=defarg[%i]" % counter.next())[1:-1]
+ shortsign = inspect.formatargspec(
+ regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "")[1:-1]
+ dic = dict(("arg%s" % n, name) for n, name in enumerate(argnames))
+ dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
+ fullsign = fullsign, defarg = func.func_defaults or ())
+ return dic
+
+ def _contains_reserved_names(dic): # helper
+ return "_call_" in dic or "_func_" in dic
+
+ def _decorate(func, caller):
+ """Takes a function and a caller and returns the function
+ decorated with that caller. The decorated function is obtained
+ by evaluating a lambda function with the correct signature.
+ """
+ infodict = getinfo(func)
+ assert not _contains_reserved_names(infodict["argnames"]), \
+ "You cannot use _call_ or _func_ as argument names!"
+ execdict=dict(_func_=func, _call_=caller, defarg=func.func_defaults or ())
+ if func.__name__ == "<lambda>":
+ lambda_src = "lambda %(fullsign)s: _call_(_func_, %(shortsign)s)" \
+ % infodict
+ dec_func = eval(lambda_src, execdict)
+ else:
+ func_src = """def %(name)s(%(fullsign)s):
+ return _call_(_func_, %(shortsign)s)""" % infodict
+ exec func_src in execdict
+ dec_func = execdict[func.__name__]
+ dec_func.__doc__ = func.__doc__
+ dec_func.__dict__ = func.__dict__
+ return dec_func
+
+ class decorator(object):
+ """General purpose decorator factory: takes a caller function as
+ input and returns a decorator. A caller function is any function like this::
+
+ def caller(func, *args, **kw):
+ # do something
+ return func(*args, **kw)
+
+ Here is an example of usage:
+
+ >> @decorator
+ .. def chatty(f, *args, **kw):
+ .. print "Calling %r" % f.__name__
+ .. return f(*args, **kw)
+
+ >> @chatty
+ .. def f(): pass
+ ..
+ >> f()
+ Calling 'f'
+ """
+ def __init__(self, caller):
+ self.caller = caller
+ def __call__(self, func):
+ return _decorate(func, self.caller)
+
+
+ #</decorators.py>
+
+The possibilities of this pattern are endless.
+
+A deferred decorator
+-----------------------
+
+You want to execute a procedure only after a certain time delay (for instance
+for use within an asyncronous Web framework)::
+
+
+ #<deferred.py>
+ "Deferring the execution of a procedure (function returning None)"
+
+ import threading
+ from decorators import decorator
+
+ def deferred(nsec):
+ def call_later(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(call_later)
+
+ @deferred(2)
+ def hello():
+ print "hello"
+
+ if __name__ == "__main__":
+ hello()
+ print "Calling hello() ..."
+
+
+ #</deferred.py>
+
+ $ python deferred.py
+
+Show an example of an experimental decorator based web framework
+(doctester_frontend).
+
+Part II: metaclasses
+++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Metaclasses are there! Consider this example from a recent post on c.l.py::
+
+ #<BaseClass.py>
+
+ class BaseClass(object):
+ "Do something"
+
+ #</BaseClass.py>
+
+>>> import BaseClass # instead of 'from BaseClass import BaseClass'
+>>> class C(BaseClass): pass
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ module.__init__() takes at most 2 arguments (3 given)
+
+The reason for the error is that class ``C(BaseClass): pass`` is
+actually calling the ``type`` metaclass with three arguments::
+
+ C = type("C", (BaseClass,), {})
+
+``type.__new__`` tries to use ``type(BaseClass)`` as metaclass,
+but since BaseClass here is a module, and ``ModuleType`` is not
+a metaclass, it cannot work. The error message reflects a conflict with
+the signature of ModuleType which requires two parameters and not three.
+
+So even if you don't use them, you may want to know they exist.
+
+Rejuvenating old-style classes
+--------------------------------------------
+
+>>> class Old: pass
+>>> print type(Old)
+<type 'classobj'>
+
+>>> __metaclass__ = type # to rejuvenate class
+>>> class NotOld: pass
+...
+>>> print NotOld.__class__
+<type 'type'>
+
+A typical metaclass example: MetaTracer
+----------------------------------------
+
+::
+
+ #<metatracer.py>
+
+ import inspect
+ from decorators import decorator
+
+ @decorator
+ def traced(meth, *args, **kw):
+ cls = meth.__cls__
+ modname = meth.__module__ or cls.__module__
+ print "calling %s.%s.%s" % (modname, cls.__name__, meth.__name__)
+ return meth(*args, **kw)
+
+ class MetaTracer(type):
+ def __init__(cls, name, bases, dic):
+ super(MetaTracer, cls).__init__(name, bases, dic)
+ for k, v in dic.iteritems():
+ if inspect.isfunction(v):
+ v.__cls__ = cls # so we know in which class v was defined
+ setattr(cls, k, traced(v))
+
+ #</metatracer.py>
+
+Usage: exploring classes in the standard library
+
+::
+
+ #<dictmixin.py>
+
+ from metatracer import MetaTracer
+ from UserDict import DictMixin
+
+ class TracedDM(DictMixin, object):
+ __metaclass__ = MetaTracer
+ def __getitem__(self, item):
+ return item
+ def keys(self):
+ return [1,2,3]
+
+ #</dictmixin.py>
+
+>>> from dictmixin import TracedDM
+>>> print TracedDM()
+calling dictmixin.TracedDM.keys
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+calling dictmixin.TracedDM.__getitem__
+{1: 1, 2: 2, 3: 3}
+
+Real life example: check overriding
+-------------------------------------
+::
+
+ #<check_overriding.py>
+
+ class Base(object):
+ a = 0
+
+ class CheckOverriding(type):
+ "Prints a message if we are overriding a name."
+ def __new__(mcl, name, bases, dic):
+ for name, val in dic.iteritems():
+ if name.startswith("__") and name.endswith("__"):
+ continue # ignore special names
+ a_base_has_name = True in (hasattr(base, name) for base in bases)
+ if a_base_has_name:
+ print "AlreadyDefinedNameWarning: " + name
+ return super(CheckOverriding, mcl).__new__(mcl, name, bases, dic)
+
+ class MyClass(Base):
+ __metaclass__ = CheckOverriding
+ a = 1
+
+ class ChildClass(MyClass):
+ a = 2
+
+#</check_overriding.py>
+
+>>> import check_overriding
+AlreadyDefinedNameWarning: a
+AlreadyDefinedNameWarning: a
+
+LogFile
+---------------------------------------------
+::
+
+ #<logfile.py>
+
+ import subprocess
+
+ def memoize(func):
+ memoize_dic = {}
+ def wrapped_func(*args):
+ if args in memoize_dic:
+ return memoize_dic[args]
+ else:
+ result = func(*args)
+ memoize_dic[args] = result
+ return result
+ wrapped_func.__name__ = func.__name__
+ wrapped_func.__doc__ = func.__doc__
+ wrapped_func.__dict__ = func.__dict__
+ return wrapped_func
+
+ class Memoize(type): # Singleton is a special case of Memoize
+ @memoize
+ def __call__(cls, *args):
+ return super(Memoize, cls).__call__(*args)
+
+ class LogFile(file):
+ """Open a file for append."""
+ __metaclass__ = Memoize
+ def __init__(self, name = "/tmp/err.log"):
+ self.viewer_cmd = 'xterm -e less'.split()
+ super(LogFile, self).__init__(name, "a")
+
+ def display(self, *ls):
+ "Use 'less' to display the log file in a separate xterm."
+ print >> self, "\n".join(map(str, ls)); self.flush()
+ subprocess.call(self.viewer_cmd + [self.name])
+
+ def reset(self):
+ "Erase the log file."
+ print >> file(self.name, "w")
+
+ if __name__ == "__main__": # test
+ print >> LogFile(), "hello"
+ print >> LogFile(), "world"
+ LogFile().display()
+
+ #</logfile.py>
+
+ $ python logfile.py
+
+Cooperative hierarchies
+------------------------------
+
+::
+
+ #<cooperative_init.py>
+
+ """Given a hierarchy, makes __init__ cooperative.
+ The only change needed is to add a line
+
+ __metaclass__ = CooperativeInit
+
+ to the base class of your hierarchy."""
+
+ from decorators import decorator
+
+ class CooperativeInit(type):
+ def __init__(cls, name, bases, dic):
+
+ @decorator
+ def make_cooperative(__init__, self, *args, **kw):
+ super(cls, self).__init__(*args, **kw)
+ __init__(self, *args, **kw)
+
+ __init__ = dic.get("__init__")
+ if __init__:
+ cls.__init__ = make_cooperative(__init__)
+
+ class Base:
+ __metaclass__ = CooperativeInit
+ def __init__(self):
+ print "B.__init__"
+
+ class C1(Base):
+ def __init__(self):
+ print "C1.__init__"
+
+ class C2(Base):
+ def __init__(self):
+ print "C2.__init__"
+
+ class D(C1, C2):
+ def __init__(self):
+ print "D.__init__"
+
+ #</cooperative_init.py>
+
+>>> from cooperative_init import D
+>>> d = D()
+B.__init__
+C2.__init__
+C1.__init__
+D.__init__
+
+Metaclass-enhanced modules
+----------------------------------------------------------------
+
+::
+
+ #<import_with_metaclass.py>
+ """
+ ``import_with_metaclass(metaclass, modulepath)`` generates
+ a new module from and old module, by enhancing all of its classes.
+ This is not perfect, but it should give you a start."""
+
+ import os, sys, inspect, types
+
+ def import_with_metaclass(metaclass, modulepath):
+ modname = os.path.basename(modulepath)[:-3] # simplistic
+ mod = types.ModuleType(modname)
+ locs = dict(
+ __module__ = modname,
+ __metaclass__ = metaclass,
+ object = metaclass("object", (), {}))
+ execfile(modulepath, locs)
+ for k, v in locs.iteritems():
+ if inspect.isclass(v): # otherwise it would be "__builtin__"
+ v.__module__ = "__dynamic__"
+ setattr(mod, k, v)
+ return mod
+
+#</import_with_metaclass.py>
+
+>>> from import_with_metaclass import import_with_metaclass
+>>> from metatracer import MetaTracer
+>>> traced_optparse = import_with_metaclass(MetaTracer,
+... "/usr/lib/python2.4/optparse.py")
+>>> op = traced_optparse.OptionParser()
+calling __dynamic__.OptionParser.__init__
+calling __dynamic__.OptionContainer.__init__
+calling __dynamic__.OptionParser._create_option_list
+calling __dynamic__.OptionContainer._create_option_mappings
+calling __dynamic__.OptionContainer.set_conflict_handler
+calling __dynamic__.OptionContainer.set_description
+calling __dynamic__.OptionParser.set_usage
+calling __dynamic__.IndentedHelpFormatter.__init__
+calling __dynamic__.HelpFormatter.__init__
+calling __dynamic__.HelpFormatter.set_parser
+calling __dynamic__.OptionParser._populate_option_list
+calling __dynamic__.OptionParser._add_help_option
+calling __dynamic__.OptionContainer.add_option
+calling __dynamic__.Option.__init__
+calling __dynamic__.Option._check_opt_strings
+calling __dynamic__.Option._set_opt_strings
+calling __dynamic__.Option._set_attrs
+calling __dynamic__.OptionContainer._check_conflict
+calling __dynamic__.OptionParser._init_parsing_state
+
+traced_optparse is a dynamically generated module not leaving in the
+file system.
+
+Magic properties
+--------------------
+::
+
+ #<magicprop.py>
+
+ class MagicProperties(type):
+ def __init__(cls, name, bases, dic):
+ prop_names = set(name[3:] for name in dic
+ if name.startswith("get")
+ or name.startswith("set"))
+ for name in prop_names:
+ getter = getattr(cls, "get" + name, None)
+ setter = getattr(cls, "set" + name, None)
+ setattr(cls, name, property(getter, setter))
+
+ class Base(object):
+ __metaclass__ = MagicProperties
+ def getx(self):
+ return self._x
+ def setx(self, value):
+ self._x = value
+
+ class Child(Base):
+ def getx(self):
+ print "getting x"
+ return super(Child, self).getx()
+ def setx(self, value):
+ print "setting x"
+ super(Child, self).setx(value)
+
+ #</magicprop.py>
+
+>>> from magicprop import Child
+>>> c = Child()
+>>> c.x = 1
+setting x
+>>> print c.x
+getting x
+1
+
+Hack: evil properties
+------------------------------------
+
+::
+
+ #<evilprop.py>
+
+ def convert2property(name, bases, d):
+ return property(d.get('get'), d.get('set'),
+ d.get('del'),d.get('__doc__'))
+
+ class C(object):
+ class x:
+ """An evil test property"""
+ __metaclass__ = convert2property
+ def get(self):
+ print 'Getting %s' % self._x
+ return self._x
+ def set(self, value):
+ self._x = value
+ print 'Setting to', value
+
+ #</evilprop.py>
+
+>>> from evilprop import C
+>>> c = C()
+>>> c.x = 5
+Setting to 5
+>>> c.x
+Getting 5
+5
+>>> print C.x.__doc__
+An evil test property
+
+Why I suggest *not* to use metaclasses in production code
+---------------------------------------------------------
+
+ + there are very few good use case for metaclasses in production code
+ (i.e. 99% of time you don't need them)
+
+ + they put a cognitive burden on the developer;
+
+ + a design without metaclasses is less magic and likely more robust;
+
+ + a design with metaclasses makes it difficult to use other metaclasses
+ for debugging.
+
+As far as I know, string.Template is the only metaclass-enhanced class
+in the standard library; the metaclass is used to give the possibility to
+change the defaults::
+
+ delimiter = '$'
+ idpattern = r'[_a-z][_a-z0-9]*'
+
+in subclasses of Template.
+
+>>> from string import Template
+>>> from metatracer import MetaTracer
+>>> class TracedTemplate(Template):
+... __metaclass__ = MetaTracer
+...
+Traceback (most recent call last):
+ ...
+TypeError: Error when calling the metaclass bases
+ metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+Solution: use a consistent metaclass
+
+>>> class GoodMeta(MetaTracer, type(Template)): pass
+...
+>>> class TracedTemplate(Template):
+... __metaclass__ = GoodMeta
+
+
+Is there an automatic way of solving the conflict?
+---------------------------------------------------------------------
+
+Yes, but you really need to be a metaclass wizard.
+
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+
+>>> from noconflict import classmaker
+>>> class TracedTemplate(Template):
+... __metaclass__ = classmaker((MetaTracer,))
+>>> print type(TracedTemplate)
+<class 'noconflict._MetaTracer_TemplateMetaclass'>
+
+::
+
+ #<noconflict.py>
+
+ import inspect, types, __builtin__
+ from skip_redundant import skip_redundant
+
+ memoized_metaclasses_map = {}
+
+ # utility function
+ def remove_redundant(metaclasses):
+ skipset = set([types.ClassType])
+ for meta in metaclasses: # determines the metaclasses to be skipped
+ skipset.update(inspect.getmro(meta)[1:])
+ return tuple(skip_redundant(metaclasses, skipset))
+
+ ##################################################################
+ ## now the core of the module: two mutually recursive functions ##
+ ##################################################################
+
+ def get_noconflict_metaclass(bases, left_metas, right_metas):
+ """Not intended to be used outside of this module, unless you know
+ what you are doing."""
+ # make tuple of needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ needed_metas = remove_redundant(metas)
+
+ # return existing confict-solving meta, if any
+ if needed_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[needed_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ elif not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ # check for recursion, can happen i.e. for Zope ExtensionClasses
+ elif needed_metas == bases:
+ raise TypeError("Incompatible root metatypes", needed_metas)
+ else: # gotta work ...
+ metaname = '_' + ''.join([m.__name__ for m in needed_metas])
+ meta = classmaker()(metaname, needed_metas, {})
+ memoized_metaclasses_map[needed_metas] = meta
+ return meta
+
+ def classmaker(left_metas=(), right_metas=()):
+ def make_class(name, bases, adict):
+ metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
+ return metaclass(name, bases, adict)
+ return make_class
+
+ #################################################################
+ ## and now a conflict-safe replacement for 'type' ##
+ #################################################################
+
+ __type__=__builtin__.type # the aboriginal 'type'
+ # left available in case you decide to rebind __builtin__.type
+
+ class safetype(__type__):
+ # this is REALLY DEEP MAGIC
+ """Overrides the ``__new__`` method of the ``type`` metaclass, making the
+ generation of classes conflict-proof."""
+ def __new__(mcl, *args):
+ nargs = len(args)
+ if nargs == 1: # works as __builtin__.type
+ return __type__(args[0])
+ elif nargs == 3: # creates the class using the appropriate metaclass
+ n, b, d = args # name, bases and dictionary
+ meta = get_noconflict_metaclass(b, (mcl,), ())
+ if meta is mcl: # meta is trivial, dispatch to the default __new__
+ return super(safetype, mcl).__new__(mcl, n, b, d)
+ else: # non-trivial metaclass, dispatch to the right __new__
+ # (it will take a second round) # print mcl, meta
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
+
+ #</noconflict.py>
diff --git a/pypers/oxford/magicprop.py b/pypers/oxford/magicprop.py
new file mode 100755
index 0000000..c536b27
--- /dev/null
+++ b/pypers/oxford/magicprop.py
@@ -0,0 +1,28 @@
+# magicprop.py
+
+class MagicProperties(type):
+ def __init__(cls, name, bases, dic):
+ prop_names = set(name[3:] for name in dic
+ if name.startswith("get")
+ or name.startswith("set"))
+ for name in prop_names:
+ getter = getattr(cls, "get" + name, None)
+ setter = getattr(cls, "set" + name, None)
+ setattr(cls, name, property(getter, setter))
+
+class Base(object):
+ __metaclass__ = MagicProperties
+ def getx(self):
+ return self._x
+ def setx(self, value):
+ self._x = value
+
+class Child(Base):
+ def getx(self):
+ print "getting x"
+ return super(Child, self).getx()
+ def setx(self, value):
+ print "setting x"
+ super(Child, self).setx(value)
+
+
diff --git a/pypers/oxford/magicsuper.py b/pypers/oxford/magicsuper.py
new file mode 100755
index 0000000..0842f82
--- /dev/null
+++ b/pypers/oxford/magicsuper.py
@@ -0,0 +1,104 @@
+"""MagicSuper: an example of metaclass recompiling the source code.
+This provides Python with a ``callsupermethod`` macro simplifying
+the cooperative call syntax.
+Examples:
+
+from magicsuper import object
+
+class B(object):
+ def __new__(cls, *args, **kw):
+ print "B.__new__"
+ return callsupermethod(cls)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ callsupermethod(*args, **kw)
+ @staticmethod
+ def sm():
+ print "B.sm"
+ @classmethod
+ def cm(cls):
+ print cls.__name__
+
+
+class C(B):
+ def __new__(cls, *args, **kw):
+ print args, kw
+ return callsupermethod(cls, *args, **kw)
+ @staticmethod
+ def sm():
+ callsupermethod()
+ @classmethod
+ def cm(cls):
+ callsupermethod()
+
+c = C()
+c.cm()
+c.sm()
+"""
+
+import inspect, textwrap
+
+class MagicSuper(type):
+ def __init__(cls, clsname, bases, dic):
+ clsmodule = __import__(cls.__module__) #assume cls is defined in source
+ for name, value in dic.iteritems():
+ # __new__ is seen as a function in the dic, so it has
+ # to be converted explicitly into a staticmethod;
+ # ordinary staticmethods don't type-dispatch on their
+ # first argument, so use 'super(cls, cls)' for them.
+ was_staticmethod = False
+ if isinstance(value, staticmethod):
+ value = value.__get__("dummy") # convert to function
+ was_staticmethod = True
+ elif isinstance(value, classmethod):
+ value = value.__get__("dummy").im_func # convert to function
+ if inspect.isfunction(value):
+ if was_staticmethod:
+ first_arg = clsname
+ else:
+ first_arg = inspect.getargspec(value)[0][0]
+ source = textwrap.dedent(inspect.getsource(value))
+ if not 'callsupermethod' in source: continue
+ source = source.replace(
+ 'callsupermethod', 'super(%s, %s).%s'
+ % (clsname, first_arg, name))
+ #print source # debug
+ exec source in clsmodule.__dict__, dic # modifies dic
+ if name == "__new__":
+ dic[name] = staticmethod(dic[name])
+ setattr(cls, name, dic[name])
+
+object = MagicSuper("object", (), {})
+type = MagicSuper("type", (type,), {})
+
+# example:
+
+class B(object):
+ def __new__(cls, *args, **kw):
+ return callsupermethod(cls)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ callsupermethod(*args, **kw)
+ @staticmethod
+ def sm():
+ print "B.sm"
+ @classmethod
+ def cm(cls):
+ print cls.__name__
+
+
+class C(B):
+ def __new__(cls, *args, **kw):
+ print args, kw
+ return callsupermethod(cls, *args, **kw)
+ @staticmethod
+ def sm():
+ callsupermethod()
+ @classmethod
+ def cm(cls):
+ callsupermethod()
+
+if __name__ == "__main__":
+ c = C(1, x=2)
+ c.sm()
+ c.cm()
diff --git a/pypers/oxford/martelli.txt b/pypers/oxford/martelli.txt
new file mode 100755
index 0000000..76a5926
--- /dev/null
+++ b/pypers/oxford/martelli.txt
@@ -0,0 +1,37 @@
+Design Patterns, Idioms, and Other Wonders of Today's Python
+presented by Alex Martelli and Anna Ravenscroft
+
+The Second Edition of the Python Cookbook is out -- just
+in time for the ACCU conference! Of course, we couldn't fit in the
+book all the in-depth analysis and explanations we'd have liked
+to. So, for this seminar, we picked some of our favourite stuff from
+the book, and beefed it up with a thorough grounding in the relevant
+language mechanisms, examples big and small, and related materials. We
+explore in depth a variety of design choices that today's Python makes
+available to you.
+
+Learn about Design Patterns and other Object-Oriented idioms and
+mechanisms. Python is a multi-paradigm language, but OOP is its core
+paradigm. Understand the pros and cons of your alternatives: When
+should you use closures, rather than callable instances? When is
+inheritance OK, and when is it better to hold-and-delegate? What
+classical Design Patterns are built-in to Python, and which others are
+appropriate to consider, when?
+
+Iterators and Generators underlie Python's new approach to looping --
+it's not your grandparents' loop any more! Learn how to encapsulate
+the underlying logic of your control structures and make it
+reusable. See how itertools can turn the "abstraction penalty" typical
+of other languages into an abstraction _bonus_, making your code
+faster at the same time as more abstract and general.
+
+Descriptors and Metaclasses are the underpinnings of today's Python's
+OOP -- Python exposes them and lets you customize them for your own
+purposes. Add Decorators, the new syntax just introduced in Python 2.4
+(a systematic application of a crucial use case for higher-order
+functions), and you'll see why the working title of that chapter was
+"Black Magic"... Learn important use cases for each of these advanced
+mechanisms.
+
+Prerequisites: you need a solid grasp of Python fundamentals to start
+with. Course objectives: you'll walk out of this a Python wizard!
diff --git a/pypers/oxford/metatracer.py b/pypers/oxford/metatracer.py
new file mode 100755
index 0000000..253fb7c
--- /dev/null
+++ b/pypers/oxford/metatracer.py
@@ -0,0 +1,21 @@
+# metatracer.py
+
+import inspect
+from decorators import decorator
+
+@decorator
+def traced(meth, *args, **kw):
+ cls = meth.__cls__
+ modname = meth.__module__ or cls.__module__
+ print "calling %s.%s.%s" % (modname, cls.__name__, meth.__name__)
+ return meth(*args, **kw)
+
+class MetaTracer(type):
+ def __init__(cls, name, bases, dic):
+ super(MetaTracer, cls).__init__(name, bases, dic)
+ for k, v in dic.iteritems():
+ if inspect.isfunction(v):
+ v.__cls__ = cls # so we know in which class v was defined
+ setattr(cls, k, traced(v))
+
+
diff --git a/pypers/oxford/metatracer2.py b/pypers/oxford/metatracer2.py
new file mode 100755
index 0000000..602b964
--- /dev/null
+++ b/pypers/oxford/metatracer2.py
@@ -0,0 +1,28 @@
+
+from ms.lang_utils import WrapMethods, decorator
+
+# to be called from the metaclass
+@decorator
+def tracer(func, *args, **kw):
+ cls = func.__cls__
+ modname = func.__module__ or cls.__module__
+ print "calling %s.%s.%s" % (modname, cls.__name__, func.__name__)
+ return func(*args, **kw)
+
+class Traced(tracer.metaclass):
+ def __init__(cls, name, bases, dic):
+ for name, attr in dic.iteritems():
+ if not name.startswith("__"):
+ setattr(cls, name, attr)
+
+print tracer.metaclass
+
+class C:
+ __metaclass__ = Traced
+ def __init__(self):
+ pass
+ def f(self):
+ pass
+
+c = C()
+c.f()
diff --git a/pypers/oxford/mro.html b/pypers/oxford/mro.html
new file mode 100755
index 0000000..d49609c
--- /dev/null
+++ b/pypers/oxford/mro.html
@@ -0,0 +1,788 @@
+<?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.3.7: http://docutils.sourceforge.net/" />
+<title>The Python 2.3 Method Resolution Order</title>
+<meta name="author" content="Michele Simionato" />
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="the-python-2-3-method-resolution-order">
+<h1 class="title">The Python 2.3 Method Resolution Order</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr><th class="docinfo-name">Version:</th>
+<td>1.4</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&#64;libero.it">michelesimionato&#64;libero.it</a></td>
+</tr>
+<tr><th class="docinfo-name">Address:</th>
+<td><pre class="address">
+Department of Physics and Astronomy
+210 Allen Hall Pittsburgh PA 15260 U.S.A.
+</pre>
+</td></tr>
+<tr class="field"><th class="docinfo-name">Home-page:</th><td class="field-body"><a class="reference" href="http://www.phyast.pitt.edu/~micheles/">http://www.phyast.pitt.edu/~micheles/</a></td>
+</tr>
+</tbody>
+</table>
+<div class="abstract topic">
+<p class="topic-title first">Abstract</p>
+<p><em>This document is intended for Python programmers who want to
+understand the C3 Method Resolution Order used in Python 2.3.
+Although it is not intended for newbies, it is quite pedagogical with
+many worked out examples. I am not aware of other publicly available
+documents with the same scope, therefore it should be useful.</em></p>
+</div>
+<p>Disclaimer:</p>
+<blockquote>
+I donate this document to the Python Software Foundation, under the
+Python 2.3 license. As usual in these circumstances, I warn the
+reader that what follows <em>should</em> be correct, but I don't give any
+warranty. Use it at your own risk and peril!</blockquote>
+<p>Acknowledgments:</p>
+<blockquote>
+All the people of the Python mailing list who sent me their support.
+Paul Foley who pointed out various imprecisions and made me to add the
+part on local precedence ordering. David Goodger for help with the
+formatting in reStructuredText. David Mertz for help with the editing.
+Joan G. Stark for the pythonic pictures. Finally, Guido van Rossum who
+enthusiastically added this document to the official Python 2.3 home-page.</blockquote>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ .-=-. .--.
+ __ .' '. / &quot; )
+ _ .' '. / .-. \ / .-'\
+ ( \ / .-. \ / / \ \ / / ^
+ \ `-` / \ `-' / \ `-` /
+jgs`-.-` '.____.' `.____.'
+</pre>
+</blockquote>
+<div class="section" id="the-beginning">
+<h1><a name="the-beginning">The beginning</a></h1>
+<blockquote>
+<em>Felix qui potuit rerum cognoscere causas</em> -- Virgilius</blockquote>
+<p>Everything started with a post by Samuele Pedroni to the Python
+development mailing list <a class="footnote-reference" href="#id4" id="id1" name="id1">[1]</a>. In his post, Samuele showed that the
+Python 2.2 method resolution order is not monotonic and he proposed to
+replace it with the C3 method resolution order. Guido agreed with his
+arguments and therefore now Python 2.3 uses C3. The C3 method itself
+has nothing to do with Python, since it was invented by people working
+on Dylan and it is described in a paper intended for lispers <a class="footnote-reference" href="#id5" id="id2" name="id2">[2]</a>. The
+present paper gives a (hopefully) readable discussion of the C3
+algorithm for Pythonistas who want to understand the reasons for the
+change.</p>
+<p>First of all, let me point out that what I am going to say only applies
+to the <em>new style classes</em> introduced in Python 2.2: <em>classic classes</em>
+maintain their old method resolution order, depth first and then left to
+right. Therefore, there is no breaking of old code for classic classes;
+and even if in principle there could be breaking of code for Python 2.2
+new style classes, in practice the cases in which the C3 resolution
+order differs from the Python 2.2 method resolution order are so rare
+that no real breaking of code is expected. Therefore:</p>
+<blockquote>
+<em>Don't be scared!</em></blockquote>
+<p>Moreover, unless you make strong use of multiple inheritance and you
+have non-trivial hierarchies, you don't need to understand the C3
+algorithm, and you can easily skip this paper. On the other hand, if
+you really want to know how multiple inheritance works, then this paper
+is for you. The good news is that things are not as complicated as you
+might expect.</p>
+<p>Let me begin with some basic definitions.</p>
+<ol class="arabic simple">
+<li>Given a class C in a complicated multiple inheritance hierarchy, it
+is a non-trivial task to specify the order in which methods are
+overridden, i.e. to specify the order of the ancestors of C.</li>
+<li>The list of the ancestors of a class C, including the class itself,
+ordered from the nearest ancestor to the furthest, is called the
+class precedence list or the <em>linearization</em> of C.</li>
+<li>The <em>Method Resolution Order</em> (MRO) is the set of rules that
+construct the linearization. In the Python literature, the idiom
+&quot;the MRO of C&quot; is also used as a synonymous for the linearization of
+the class C.</li>
+<li>For instance, in the case of single inheritance hierarchy, if C is a
+subclass of C1, and C1 is a subclass of C2, then the linearization of
+C is simply the list [C, C1 , C2]. However, with multiple
+inheritance hierarchies, the construction of the linearization is
+more cumbersome, since it is more difficult to construct a
+linearization that respects <em>local precedence ordering</em> and
+<em>monotonicity</em>.</li>
+<li>I will discuss the local precedence ordering later, but I can give
+the definition of monotonicity here. A MRO is monotonic when the
+following is true: <em>if C1 precedes C2 in the linearization of C,
+then C1 precedes C2 in the linearization of any subclass of C</em>.
+Otherwise, the innocuous operation of deriving a new class could
+change the resolution order of methods, potentially introducing very
+subtle bugs. Examples where this happens will be shown later.</li>
+<li>Not all classes admit a linearization. There are cases, in
+complicated hierarchies, where it is not possible to derive a class
+such that its linearization respects all the desired properties.</li>
+</ol>
+<p>Here I give an example of this situation. Consider the hierarchy</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; O = object
+&gt;&gt;&gt; class X(O): pass
+&gt;&gt;&gt; class Y(O): pass
+&gt;&gt;&gt; class A(X,Y): pass
+&gt;&gt;&gt; class B(Y,X): pass
+</pre>
+</blockquote>
+<p>which can be represented with the following inheritance graph, where I
+have denoted with O the <tt class="docutils literal"><span class="pre">object</span></tt> class, which is the beginning of any
+hierarchy for new style classes:</p>
+<blockquote>
+<pre class="literal-block">
+ -----------
+| |
+| O |
+| / \ |
+ - X Y /
+ | / | /
+ | / |/
+ A B
+ \ /
+ ?
+</pre>
+</blockquote>
+<p>In this case, it is not possible to derive a new class C from A and B,
+since X precedes Y in A, but Y precedes X in B, therefore the method
+resolution order would be ambiguous in C.</p>
+<p>Python 2.3 raises an exception in this situation (TypeError: MRO
+conflict among bases Y, X) forbidding the naive programmer from creating
+ambiguous hierarchies. Python 2.2 instead does not raise an exception,
+but chooses an <em>ad hoc</em> ordering (CABXYO in this case).</p>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ _ .-=-. .-==-.
+ { } __ .' O o '. / -&lt;' )
+ { } .' O'. / o .-. O \ / .--v`
+ { } / .-. o\ /O / \ o\ /O /
+ \ `-` / \ O`-'o / \ O`-`o /
+jgs `-.-` '.____.' `.____.'
+</pre>
+</blockquote>
+</div>
+<div class="section" id="the-c3-method-resolution-order">
+<h1><a name="the-c3-method-resolution-order">The C3 Method Resolution Order</a></h1>
+<p>Let me introduce a few simple notations which will be useful for the
+following discussion. I will use the shortcut notation</p>
+<blockquote>
+C1 C2 ... CN</blockquote>
+<p>to indicate the list of classes [C1, C2, ... , CN].</p>
+<p>The <em>head</em> of the list is its first element:</p>
+<blockquote>
+head = C1</blockquote>
+<p>whereas the <em>tail</em> is the rest of the list:</p>
+<blockquote>
+tail = C2 ... CN.</blockquote>
+<p>I shall also use the notation</p>
+<blockquote>
+C + (C1 C2 ... CN) = C C1 C2 ... CN</blockquote>
+<p>to denote the sum of the lists [C] + [C1, C2, ... ,CN].</p>
+<p>Now I can explain how the MRO works in Python 2.3.</p>
+<p>Consider a class C in a multiple inheritance hierarchy, with C
+inheriting from the base classes B1, B2, ... , BN. We want to
+compute the linearization L[C] of the class C. The rule is the
+following:</p>
+<blockquote>
+<em>the linearization of C is the sum of C plus the merge of the
+linearizations of the parents and the list of the parents.</em></blockquote>
+<p>In symbolic notation:</p>
+<blockquote>
+L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)</blockquote>
+<p>In particular, if C is the <tt class="docutils literal"><span class="pre">object</span></tt> class, which has no parents, the
+linearization is trivial:</p>
+<blockquote>
+L[object] = object.</blockquote>
+<p>However, in general one has to compute the merge according to the following
+prescription:</p>
+<blockquote>
+<em>take the head of the first list, i.e L[B1][0]; if this head is not in
+the tail of any of the other lists, then add it to the linearization
+of C and remove it from the lists in the merge, otherwise look at the
+head of the next list and take it, if it is a good head. Then repeat
+the operation until all the class are removed or it is impossible to
+find good heads. In this case, it is impossible to construct the
+merge, Python 2.3 will refuse to create the class C and will raise an
+exception.</em></blockquote>
+<p>This prescription ensures that the merge operation <em>preserves</em> the
+ordering, if the ordering can be preserved. On the other hand, if the
+order cannot be preserved (as in the example of serious order
+disagreement discussed above) then the merge cannot be computed.</p>
+<p>The computation of the merge is trivial if C has only one parent
+(single inheritance); in this case</p>
+<blockquote>
+L[C(B)] = C + merge(L[B],B) = C + L[B]</blockquote>
+<p>However, in the case of multiple inheritance things are more cumbersome
+and I don't expect you can understand the rule without a couple of
+examples ;-)</p>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ .-'-.
+ /' `\
+ /' _.-.-._ `\
+ | (|) (|) |
+ | \__&quot;__/ |
+ \ |v.v| /
+ \ | | | /
+ `\ |=^-| /'
+ `|=-=|'
+ | - |
+ |= |
+ |-=-|
+ _.-=-=|= -|=-=-._
+ ( |___| )
+ ( `-=-=-=-=-=-=-=-` )
+ (`-=-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-`)
+jgs `-=-=-=-=-=-=-`
+</pre>
+</blockquote>
+</div>
+<div class="section" id="examples">
+<h1><a name="examples">Examples</a></h1>
+<p>First example. Consider the following hierarchy:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; O = object
+&gt;&gt;&gt; class F(O): pass
+&gt;&gt;&gt; class E(O): pass
+&gt;&gt;&gt; class D(O): pass
+&gt;&gt;&gt; class C(D,F): pass
+&gt;&gt;&gt; class B(D,E): pass
+&gt;&gt;&gt; class A(B,C): pass
+</pre>
+</blockquote>
+<p>In this case the inheritance graph can be drawn as</p>
+<blockquote>
+<pre class="literal-block">
+ 6
+ ---
+Level 3 | O | (more general)
+ / --- \
+ / | \ |
+ / | \ |
+ / | \ |
+ --- --- --- |
+Level 2 3 | D | 4| E | | F | 5 |
+ --- --- --- |
+ \ \ _ / | |
+ \ / \ _ | |
+ \ / \ | |
+ --- --- |
+Level 1 1 | B | | C | 2 |
+ --- --- |
+ \ / |
+ \ / \ /
+ ---
+Level 0 0 | A | (more specialized)
+ ---
+</pre>
+</blockquote>
+<p>The linearizations of O,D,E and F are trivial:</p>
+<blockquote>
+<pre class="literal-block">
+L[O] = O
+L[D] = D O
+L[E] = E O
+L[F] = F O
+</pre>
+</blockquote>
+<p>The linearization of B can be computed as</p>
+<blockquote>
+<pre class="literal-block">
+L[B] = B + merge(DO, EO, DE)
+</pre>
+</blockquote>
+<p>We see that D is a good head, therefore we take it and we are reduced to
+compute <tt class="docutils literal"><span class="pre">merge(O,EO,E)</span></tt>. Now O is not a good head, since it is in the
+tail of the sequence EO. In this case the rule says that we have to
+skip to the next sequence. Then we see that E is a good head; we take
+it and we are reduced to compute <tt class="docutils literal"><span class="pre">merge(O,O)</span></tt> which gives O. Therefore</p>
+<blockquote>
+<pre class="literal-block">
+L[B] = B D E O
+</pre>
+</blockquote>
+<p>Using the same procedure one finds:</p>
+<blockquote>
+<pre class="literal-block">
+L[C] = C + merge(DO,FO,DF)
+ = C + D + merge(O,FO,F)
+ = C + D + F + merge(O,O)
+ = C D F O
+</pre>
+</blockquote>
+<p>Now we can compute:</p>
+<blockquote>
+<pre class="literal-block">
+L[A] = A + merge(BDEO,CDFO,BC)
+ = A + B + merge(DEO,CDFO,C)
+ = A + B + C + merge(DEO,DFO)
+ = A + B + C + D + merge(EO,FO)
+ = A + B + C + D + E + merge(O,FO)
+ = A + B + C + D + E + F + merge(O,O)
+ = A B C D E F O
+</pre>
+</blockquote>
+<p>In this example, the linearization is ordered in a pretty nice way
+according to the inheritance level, in the sense that lower levels (i.e.
+more specialized classes) have higher precedence (see the inheritance
+graph). However, this is not the general case.</p>
+<p>I leave as an exercise for the reader to compute the linearization for
+my second example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; O = object
+&gt;&gt;&gt; class F(O): pass
+&gt;&gt;&gt; class E(O): pass
+&gt;&gt;&gt; class D(O): pass
+&gt;&gt;&gt; class C(D,F): pass
+&gt;&gt;&gt; class B(E,D): pass
+&gt;&gt;&gt; class A(B,C): pass
+</pre>
+</blockquote>
+<p>The only difference with the previous example is the change B(D,E) --&gt;
+B(E,D); however even such a little modification completely changes the
+ordering of the hierarchy</p>
+<blockquote>
+<pre class="literal-block">
+ 6
+ ---
+Level 3 | O |
+ / --- \
+ / | \
+ / | \
+ / | \
+ --- --- ---
+Level 2 2 | E | 4 | D | | F | 5
+ --- --- ---
+ \ / \ /
+ \ / \ /
+ \ / \ /
+ --- ---
+Level 1 1 | B | | C | 3
+ --- ---
+ \ /
+ \ /
+ ---
+Level 0 0 | A |
+ ---
+</pre>
+</blockquote>
+<p>Notice that the class E, which is in the second level of the hierarchy,
+precedes the class C, which is in the first level of the hierarchy, i.e.
+E is more specialized than C, even if it is in a higher level.</p>
+<p>A lazy programmer can obtain the MRO directly from Python 2.2, since in
+this case it coincides with the Python 2.3 linearization. It is enough
+to invoke the .mro() method of class A:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; A.mro()
+(&lt;class '__main__.A'&gt;, &lt;class '__main__.B'&gt;, &lt;class '__main__.E'&gt;,
+&lt;class '__main__.C'&gt;, &lt;class '__main__.D'&gt;, &lt;class '__main__.F'&gt;,
+&lt;type 'object'&gt;)
+</pre>
+</blockquote>
+<p>Finally, let me consider the example discussed in the first section,
+involving a serious order disagreement. In this case, it is
+straightforward to compute the linearizations of O, X, Y, A and B:</p>
+<blockquote>
+<pre class="literal-block">
+L[O] = 0
+L[X] = X O
+L[Y] = Y O
+L[A] = A X Y O
+L[B] = B Y X O
+</pre>
+</blockquote>
+<p>However, it is impossible to compute the linearization for a class C
+that inherits from A and B:</p>
+<blockquote>
+<pre class="literal-block">
+L[C] = C + merge(AXYO, BYXO, AB)
+ = C + A + merge(XYO, BYXO, B)
+ = C + A + B + merge(XYO, YXO)
+</pre>
+</blockquote>
+<p>At this point we cannot merge the lists XYO and YXO, since X is in the
+tail of YXO whereas Y is in the tail of XYO: therefore there are no
+good heads and the C3 algorithm stops. Python 2.3 raises an error and
+refuses to create the class C.</p>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ __
+ (\ .-. .-. /_&quot;)
+ \\_//^\\_//^\\_//
+jgs `&quot;` `&quot;` `&quot;`
+</pre>
+</blockquote>
+</div>
+<div class="section" id="bad-method-resolution-orders">
+<h1><a name="bad-method-resolution-orders">Bad Method Resolution Orders</a></h1>
+<p>A MRO is <em>bad</em> when it breaks such fundamental properties as local
+precedence ordering and monotonicity. In this section, I will show
+that both the MRO for classic classes and the MRO for new style classes
+in Python 2.2 are bad.</p>
+<p>It is easier to start with the local precedence ordering. Consider the
+following example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; F=type('Food',(),{'remember2buy':'spam'})
+&gt;&gt;&gt; E=type('Eggs',(F,),{'remember2buy':'eggs'})
+&gt;&gt;&gt; G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!
+</pre>
+</blockquote>
+<p>with inheritance diagram</p>
+<blockquote>
+<pre class="literal-block">
+ O
+ |
+(buy spam) F
+ | \
+ | E (buy eggs)
+ | /
+ G
+
+ (buy eggs or spam ?)
+</pre>
+</blockquote>
+<p>We see that class G inherits from F and E, with F <em>before</em> E: therefore
+we would expect the attribute <em>G.remember2buy</em> to be inherited by
+<em>F.rembermer2buy</em> and not by <em>E.remember2buy</em>: nevertheless Python 2.2
+gives</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; G.remember2buy
+'eggs'
+</pre>
+</blockquote>
+<p>This is a breaking of local precedence ordering since the order in the
+local precedence list, i.e. the list of the parents of G, is not
+preserved in the Python 2.2 linearization of G:</p>
+<blockquote>
+<pre class="literal-block">
+L[G,P22]= G E F object # F *follows* E
+</pre>
+</blockquote>
+<p>One could argue that the reason why F follows E in the Python 2.2
+linearization is that F is less specialized than E, since F is the
+superclass of E; nevertheless the breaking of local precedence ordering
+is quite non-intuitive and error prone. This is particularly true since
+it is a different from old style classes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class F: remember2buy='spam'
+&gt;&gt;&gt; class E(F): remember2buy='eggs'
+&gt;&gt;&gt; class G(F,E): pass
+&gt;&gt;&gt; G.remember2buy
+'spam'
+</pre>
+</blockquote>
+<p>In this case the MRO is GFEF and the local precedence ordering is
+preserved.</p>
+<p>As a general rule, hierarchies such as the previous one should be
+avoided, since it is unclear if F should override E or viceversa.
+Python 2.3 solves the ambiguity by raising an exception in the creation
+of class G, effectively stopping the programmer from generating
+ambiguous hierarchies. The reason for that is that the C3 algorithm
+fails when the merge</p>
+<blockquote>
+<pre class="literal-block">
+merge(FO,EFO,FE)
+</pre>
+</blockquote>
+<p>cannot be computed, because F is in the tail of EFO and E is in the tail
+of FE.</p>
+<p>The real solution is to design a non-ambiguous hierarchy, i.e. to derive
+G from E and F (the more specific first) and not from F and E; in this
+case the MRO is GEF without any doubt.</p>
+<blockquote>
+<pre class="literal-block">
+ O
+ |
+ F (spam)
+ / |
+(eggs) E |
+ \ |
+ G
+ (eggs, no doubt)
+</pre>
+</blockquote>
+<p>Python 2.3 forces the programmer to write good hierarchies (or, at
+least, less error-prone ones).</p>
+<p>On a related note, let me point out that the Python 2.3 algorithm is
+smart enough to recognize obvious mistakes, as the duplication of
+classes in the list of parents:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A(object): pass
+&gt;&gt;&gt; class C(A,A): pass # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: duplicate base class A
+</pre>
+</blockquote>
+<p>Python 2.2 (both for classic classes and new style classes) in this
+situation, would not raise any exception.</p>
+<p>Finally, I would like to point out two lessons we have learned from this
+example:</p>
+<ol class="arabic simple">
+<li>despite the name, the MRO determines the resolution order of
+attributes, not only of methods;</li>
+<li>the default food for Pythonistas is spam ! (but you already knew
+that ;-)</li>
+</ol>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ __
+ (\ .-. .-. /_&quot;)
+ \\_//^\\_//^\\_//
+jgs `&quot;` `&quot;` `&quot;`
+</pre>
+</blockquote>
+<p>Having discussed the issue of local precedence ordering, let me now
+consider the issue of monotonicity. My goal is to show that neither the
+MRO for classic classes nor that for Python 2.2 new style classes is
+monotonic.</p>
+<p>To prove that the MRO for classic classes is non-monotonic is rather
+trivial, it is enough to look at the diamond diagram:</p>
+<blockquote>
+<pre class="literal-block">
+ C
+ / \
+ / \
+A B
+ \ /
+ \ /
+ D
+</pre>
+</blockquote>
+<p>One easily discerns the inconsistency:</p>
+<blockquote>
+<pre class="literal-block">
+L[B,P21] = B C # B precedes C : B's methods win
+L[D,P21] = D A C B C # B follows C : C's methods win!
+</pre>
+</blockquote>
+<p>On the other hand, there are no problems with the Python 2.2 and 2.3
+MROs, they give both</p>
+<blockquote>
+<pre class="literal-block">
+L[D] = D A B C
+</pre>
+</blockquote>
+<p>Guido points out in his essay <a class="footnote-reference" href="#id6" id="id3" name="id3">[3]</a> that the classic MRO is not so bad in
+practice, since one can typically avoids diamonds for classic classes.
+But all new style classes inherit from <tt class="docutils literal"><span class="pre">object</span></tt>, therefore diamonds are
+unavoidable and inconsistencies shows up in every multiple inheritance
+graph.</p>
+<p>The MRO of Python 2.2 makes breaking monotonicity difficult, but not
+impossible. The following example, originally provided by Samuele
+Pedroni, shows that the MRO of Python 2.2 is non-monotonic:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A(object): pass
+&gt;&gt;&gt; class B(object): pass
+&gt;&gt;&gt; class C(object): pass
+&gt;&gt;&gt; class D(object): pass
+&gt;&gt;&gt; class E(object): pass
+&gt;&gt;&gt; class K1(A,B,C): pass
+&gt;&gt;&gt; class K2(D,B,E): pass
+&gt;&gt;&gt; class K3(D,A): pass
+&gt;&gt;&gt; class Z(K1,K2,K3): pass
+</pre>
+</blockquote>
+<p>Here are the linearizations according to the C3 MRO (the reader should
+verify these linearizations as an exercise and draw the inheritance
+diagram ;-)</p>
+<blockquote>
+<pre class="literal-block">
+L[A] = A O
+L[B] = B O
+L[C] = C O
+L[D] = D O
+L[E] = E O
+L[K1]= K1 A B C O
+L[K2]= K2 D B E O
+L[K3]= K3 D A O
+L[Z] = Z K1 K2 K3 D A B C E O
+</pre>
+</blockquote>
+<p>Python 2.2 gives exactly the same linearizations for A, B, C, D, E, K1,
+K2 and K3, but a different linearization for Z:</p>
+<blockquote>
+<pre class="literal-block">
+L[Z,P22] = Z K1 K3 A K2 D B C E O
+</pre>
+</blockquote>
+<p>It is clear that this linearization is <em>wrong</em>, since A comes before D
+whereas in the linearization of K3 A comes <em>after</em> D. In other words, in
+K3 methods derived by D override methods derived by A, but in Z, which
+still is a subclass of K3, methods derived by A override methods derived
+by D! This is a violation of monotonicity. Moreover, the Python 2.2
+linearization of Z is also inconsistent with local precedence ordering,
+since the local precedence list of the class Z is [K1, K2, K3] (K2
+precedes K3), whereas in the linearization of Z K2 <em>follows</em> K3. These
+problems explain why the 2.2 rule has been dismissed in favor of the C3
+rule.</p>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ __
+ (\ .-. .-. .-. .-. .-. .-. .-. .-. /_&quot;)
+ \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
+jgs `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;`
+</pre>
+</blockquote>
+</div>
+<div class="section" id="the-end">
+<h1><a name="the-end">The end</a></h1>
+<p>This section is for the impatient reader, who skipped all the previous
+sections and jumped immediately to the end. This section is for the
+lazy programmer too, who didn't want to exercise her/his brain.
+Finally, it is for the programmer with some hubris, otherwise s/he would
+not be reading a paper on the C3 method resolution order in multiple
+inheritance hierarchies ;-) These three virtues taken all together (and
+<em>not</em> separately) deserve a prize: the prize is a short Python 2.2
+script that allows you to compute the 2.3 MRO without risk to your
+brain. Simply change the last line to play with the various examples I
+have discussed in this paper.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;mro.py&gt;
+
+&quot;&quot;&quot;C3 algorithm by Samuele Pedroni (with readability enhanced by me).&quot;&quot;&quot;
+
+class __metaclass__(type):
+ &quot;All classes are metamagically modified to be nicely printed&quot;
+ __repr__ = lambda cls: cls.__name__
+
+class ex_2:
+ &quot;Serious order disagreement&quot; #From Guido
+ class O: pass
+ class X(O): pass
+ class Y(O): pass
+ class A(X,Y): pass
+ class B(Y,X): pass
+ try:
+ class Z(A,B): pass #creates Z(A,B) in Python 2.2
+ except TypeError:
+ pass # Z(A,B) cannot be created in Python 2.3
+
+class ex_5:
+ &quot;My first example&quot;
+ class O: pass
+ class F(O): pass
+ class E(O): pass
+ class D(O): pass
+ class C(D,F): pass
+ class B(D,E): pass
+ class A(B,C): pass
+
+class ex_6:
+ &quot;My second example&quot;
+ class O: pass
+ class F(O): pass
+ class E(O): pass
+ class D(O): pass
+ class C(D,F): pass
+ class B(E,D): pass
+ class A(B,C): pass
+
+class ex_9:
+ &quot;Difference between Python 2.2 MRO and C3&quot; #From Samuele
+ class O: pass
+ class A(O): pass
+ class B(O): pass
+ class C(O): pass
+ class D(O): pass
+ class E(O): pass
+ class K1(A,B,C): pass
+ class K2(D,B,E): pass
+ class K3(D,A): pass
+ class Z(K1,K2,K3): pass
+
+def merge(seqs):
+ print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
+ res = []; i=0
+ while 1:
+ nonemptyseqs=[seq for seq in seqs if seq]
+ if not nonemptyseqs: return res
+ i+=1; print '\n',i,'round: candidates...',
+ for seq in nonemptyseqs: # find merge candidates among seq heads
+ cand = seq[0]; print ' ',cand,
+ nothead=[s for s in nonemptyseqs if cand in s[1:]]
+ if nothead: cand=None #reject candidate
+ else: break
+ if not cand: raise &quot;Inconsistent hierarchy&quot;
+ res.append(cand)
+ for seq in nonemptyseqs: # remove cand
+ if seq[0] == cand: del seq[0]
+
+def mro(C):
+ &quot;Compute the class precedence list (mro) according to C3&quot;
+ return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])
+
+def print_mro(C):
+ print '\nMRO[%s]=%s' % (C,mro(C))
+ print '\nP22 MRO[%s]=%s' % (C,C.mro())
+
+print_mro(ex_9.Z)
+
+#&lt;/mro.py&gt;
+</pre>
+</blockquote>
+<p>That's all folks,</p>
+<blockquote>
+enjoy !</blockquote>
+<hr class="docutils" />
+<blockquote>
+<pre class="literal-block">
+ __
+ (&quot;_\ .-. .-. .-. .-. .-. .-. .-. .-. /)
+ \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
+jgs `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;` `&quot;`
+</pre>
+</blockquote>
+</div>
+<div class="section" id="resources">
+<h1><a name="resources">Resources</a></h1>
+<table class="docutils footnote" frame="void" id="id4" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id4">[1]</a></td><td>The thread on python-dev started by Samuele Pedroni:
+<a class="reference" href="http://mail.python.org/pipermail/python-dev/2002-October/029035.html">http://mail.python.org/pipermail/python-dev/2002-October/029035.html</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id5" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2" name="id5">[2]</a></td><td>The paper <em>A Monotonic Superclass Linearization for Dylan</em>:
+<a class="reference" href="http://www.webcom.com/haahr/dylan/linearization-oopsla96.html">http://www.webcom.com/haahr/dylan/linearization-oopsla96.html</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id6" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3" name="id6">[3]</a></td><td>Guido van Rossum's essay, <em>Unifying types and classes in Python 2.2</em>:
+<a class="reference" href="http://www.python.org/2.2.2/descrintro.html">http://www.python.org/2.2.2/descrintro.html</a></td></tr>
+</tbody>
+</table>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/mro.tex b/pypers/oxford/mro.tex
new file mode 100755
index 0000000..1ad82c4
--- /dev/null
+++ b/pypers/oxford/mro.tex
@@ -0,0 +1,988 @@
+\documentclass[10pt,a4paper,english]{article}
+\usepackage{babel}
+\usepackage{ae}
+\usepackage{aeguill}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage{ifthen}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[DIV12]{typearea}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newlength{\locallinewidth}
+\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}}
+\newlength{\lineblockindentation}
+\setlength{\lineblockindentation}{2.5em}
+\newenvironment{lineblock}[1]
+{\begin{list}{}
+ {\setlength{\partopsep}{\parskip}
+ \addtolength{\partopsep}{\baselineskip}
+ \topsep0pt\itemsep0.15\baselineskip\parsep0pt
+ \leftmargin#1}
+ \raggedright}
+{\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 ~}}
+\newcommand{\titlereference}[1]{\textsl{#1}}
+% end of "some commands"
+\input{style.tex}
+\title{The Python 2.3 Method Resolution Order}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={The Python 2.3 Method Resolution Order},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Version}: &
+ 1.4 \\
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{E-mail}: &
+ michelesimionato@libero.it \\
+\textbf{Address}: &
+ {\raggedright
+Department of Physics and Astronomy~\\
+210 Allen Hall Pittsburgh PA 15260 U.S.A. } \\
+\textbf{Home-page}: &
+ http://www.phyast.pitt.edu/{\textasciitilde}micheles/ \\
+\end{tabularx}
+\end{center}
+
+\setlength{\locallinewidth}{\linewidth}
+
+
+\subsubsection*{~\hfill Abstract\hfill ~}
+
+\emph{This document is intended for Python programmers who want to
+understand the C3 Method Resolution Order used in Python 2.3.
+Although it is not intended for newbies, it is quite pedagogical with
+many worked out examples. I am not aware of other publicly available
+documents with the same scope, therefore it should be useful.}
+
+
+Disclaimer:
+\begin{quote}
+
+I donate this document to the Python Software Foundation, under the
+Python 2.3 license. As usual in these circumstances, I warn the
+reader that what follows \emph{should} be correct, but I don't give any
+warranty. Use it at your own risk and peril!
+\end{quote}
+
+Acknowledgments:
+\begin{quote}
+
+All the people of the Python mailing list who sent me their support.
+Paul Foley who pointed out various imprecisions and made me to add the
+part on local precedence ordering. David Goodger for help with the
+formatting in reStructuredText. David Mertz for help with the editing.
+Joan G. Stark for the pythonic pictures. Finally, Guido van Rossum who
+enthusiastically added this document to the official Python 2.3 home-page.
+\end{quote}
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~.-=-.~~~~~~~~~~.-{}-.~\\
+~~~~~~~~~~{\_}{\_}~~~~~~~~.'~~~~~'.~~~~~~~/~~"~)~\\
+~~{\_}~~~~~.'~~'.~~~~~/~~~.-.~~~{\textbackslash}~~~~~/~~.-'{\textbackslash}~\\
+~(~{\textbackslash}~~~/~.-.~~{\textbackslash}~~~/~~~/~~~{\textbackslash}~~~{\textbackslash}~~~/~~/~~~~{\textasciicircum}~\\
+~~{\textbackslash}~`-`~/~~~{\textbackslash}~~`-'~~~/~~~~~{\textbackslash}~~~`-`~~/~\\
+jgs`-.-`~~~~~'.{\_}{\_}{\_}{\_}.'~~~~~~~`.{\_}{\_}{\_}{\_}.'
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-beginning}{}
+\pdfbookmark[0]{The beginning}{the-beginning}
+\section*{The beginning}
+\begin{quote}
+
+\emph{Felix qui potuit rerum cognoscere causas} -{}- Virgilius
+\end{quote}
+
+Everything started with a post by Samuele Pedroni to the Python
+development mailing list [\hyperlink{id4}{1}]. In his post, Samuele showed that the
+Python 2.2 method resolution order is not monotonic and he proposed to
+replace it with the C3 method resolution order. Guido agreed with his
+arguments and therefore now Python 2.3 uses C3. The C3 method itself
+has nothing to do with Python, since it was invented by people working
+on Dylan and it is described in a paper intended for lispers [\hyperlink{id5}{2}]. The
+present paper gives a (hopefully) readable discussion of the C3
+algorithm for Pythonistas who want to understand the reasons for the
+change.
+
+First of all, let me point out that what I am going to say only applies
+to the \emph{new style classes} introduced in Python 2.2: \emph{classic classes}
+maintain their old method resolution order, depth first and then left to
+right. Therefore, there is no breaking of old code for classic classes;
+and even if in principle there could be breaking of code for Python 2.2
+new style classes, in practice the cases in which the C3 resolution
+order differs from the Python 2.2 method resolution order are so rare
+that no real breaking of code is expected. Therefore:
+\begin{quote}
+
+\emph{Don't be scared!}
+\end{quote}
+
+Moreover, unless you make strong use of multiple inheritance and you
+have non-trivial hierarchies, you don't need to understand the C3
+algorithm, and you can easily skip this paper. On the other hand, if
+you really want to know how multiple inheritance works, then this paper
+is for you. The good news is that things are not as complicated as you
+might expect.
+
+Let me begin with some basic definitions.
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1})}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Given a class C in a complicated multiple inheritance hierarchy, it
+is a non-trivial task to specify the order in which methods are
+overridden, i.e. to specify the order of the ancestors of C.
+
+\item {}
+The list of the ancestors of a class C, including the class itself,
+ordered from the nearest ancestor to the furthest, is called the
+class precedence list or the \emph{linearization} of C.
+
+\item {}
+The \emph{Method Resolution Order} (MRO) is the set of rules that
+construct the linearization. In the Python literature, the idiom
+``the MRO of C'' is also used as a synonymous for the linearization of
+the class C.
+
+\item {}
+For instance, in the case of single inheritance hierarchy, if C is a
+subclass of C1, and C1 is a subclass of C2, then the linearization of
+C is simply the list {[}C, C1 , C2]. However, with multiple
+inheritance hierarchies, the construction of the linearization is
+more cumbersome, since it is more difficult to construct a
+linearization that respects \emph{local precedence ordering} and
+\emph{monotonicity}.
+
+\item {}
+I will discuss the local precedence ordering later, but I can give
+the definition of monotonicity here. A MRO is monotonic when the
+following is true: \emph{if C1 precedes C2 in the linearization of C,
+then C1 precedes C2 in the linearization of any subclass of C}.
+Otherwise, the innocuous operation of deriving a new class could
+change the resolution order of methods, potentially introducing very
+subtle bugs. Examples where this happens will be shown later.
+
+\item {}
+Not all classes admit a linearization. There are cases, in
+complicated hierarchies, where it is not possible to derive a class
+such that its linearization respects all the desired properties.
+
+\end{list}
+
+Here I give an example of this situation. Consider the hierarchy
+\begin{quote}
+\begin{verbatim}>>> O = object
+>>> class X(O): pass
+>>> class Y(O): pass
+>>> class A(X,Y): pass
+>>> class B(Y,X): pass\end{verbatim}
+\end{quote}
+
+which can be represented with the following inheritance graph, where I
+have denoted with O the \texttt{object} class, which is the beginning of any
+hierarchy for new style classes:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-~\\
+|~~~~~~~~~~~|~\\
+|~~~~O~~~~~~|~\\
+|~~/~~~{\textbackslash}~~~~|~\\
+~-~X~~~~Y~~/~\\
+~~~|~~/~|~/~\\
+~~~|~/~~|/~\\
+~~~A~~~~B~\\
+~~~{\textbackslash}~~~/~\\
+~~~~~?
+}\end{quote}
+\end{quote}
+
+In this case, it is not possible to derive a new class C from A and B,
+since X precedes Y in A, but Y precedes X in B, therefore the method
+resolution order would be ambiguous in C.
+
+Python 2.3 raises an exception in this situation (TypeError: MRO
+conflict among bases Y, X) forbidding the naive programmer from creating
+ambiguous hierarchies. Python 2.2 instead does not raise an exception,
+but chooses an \emph{ad hoc} ordering (CABXYO in this case).
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~{\_}~~~~~~~~~~~~~~~~~~~.-=-.~~~~~~~~~~.-==-.~\\
+~~~{\{}~{\}}~~~~~~{\_}{\_}~~~~~~~~.'~O~o~'.~~~~~~~/~~-<'~)~\\
+~~~{\{}~{\}}~~~~.'~O'.~~~~~/~o~.-.~O~{\textbackslash}~~~~~/~~.-{}-v`~\\
+~~~{\{}~{\}}~~~/~.-.~o{\textbackslash}~~~/O~~/~~~{\textbackslash}~~o{\textbackslash}~~~/O~/~\\
+~~~~{\textbackslash}~`-`~/~~~{\textbackslash}~O`-'o~~/~~~~~{\textbackslash}~~O`-`o~/~\\
+jgs~~`-.-`~~~~~'.{\_}{\_}{\_}{\_}.'~~~~~~~`.{\_}{\_}{\_}{\_}.'
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-c3-method-resolution-order}{}
+\pdfbookmark[0]{The C3 Method Resolution Order}{the-c3-method-resolution-order}
+\section*{The C3 Method Resolution Order}
+
+Let me introduce a few simple notations which will be useful for the
+following discussion. I will use the shortcut notation
+\begin{quote}
+
+C1 C2 ... CN
+\end{quote}
+
+to indicate the list of classes {[}C1, C2, ... , CN].
+
+The \emph{head} of the list is its first element:
+\begin{quote}
+
+head = C1
+\end{quote}
+
+whereas the \emph{tail} is the rest of the list:
+\begin{quote}
+
+tail = C2 ... CN.
+\end{quote}
+
+I shall also use the notation
+\begin{quote}
+
+C + (C1 C2 ... CN) = C C1 C2 ... CN
+\end{quote}
+
+to denote the sum of the lists {[}C] + {[}C1, C2, ... ,CN].
+
+Now I can explain how the MRO works in Python 2.3.
+
+Consider a class C in a multiple inheritance hierarchy, with C
+inheriting from the base classes B1, B2, ... , BN. We want to
+compute the linearization L{[}C] of the class C. The rule is the
+following:
+\begin{quote}
+
+\emph{the linearization of C is the sum of C plus the merge of the
+linearizations of the parents and the list of the parents.}
+\end{quote}
+
+In symbolic notation:
+\begin{quote}
+
+L{[}C(B1 ... BN)] = C + merge(L{[}B1] ... L{[}BN], B1 ... BN)
+\end{quote}
+
+In particular, if C is the \texttt{object} class, which has no parents, the
+linearization is trivial:
+\begin{quote}
+
+L{[}object] = object.
+\end{quote}
+
+However, in general one has to compute the merge according to the following
+prescription:
+\begin{quote}
+
+\emph{take the head of the first list, i.e L{[}B1]{[}0]; if this head is not in
+the tail of any of the other lists, then add it to the linearization
+of C and remove it from the lists in the merge, otherwise look at the
+head of the next list and take it, if it is a good head. Then repeat
+the operation until all the class are removed or it is impossible to
+find good heads. In this case, it is impossible to construct the
+merge, Python 2.3 will refuse to create the class C and will raise an
+exception.}
+\end{quote}
+
+This prescription ensures that the merge operation \emph{preserves} the
+ordering, if the ordering can be preserved. On the other hand, if the
+order cannot be preserved (as in the example of serious order
+disagreement discussed above) then the merge cannot be computed.
+
+The computation of the merge is trivial if C has only one parent
+(single inheritance); in this case
+\begin{quote}
+
+L{[}C(B)] = C + merge(L{[}B],B) = C + L{[}B]
+\end{quote}
+
+However, in the case of multiple inheritance things are more cumbersome
+and I don't expect you can understand the rule without a couple of
+examples ;-)
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~.-'-.~\\
+~~~~~~~~/'~~~~~`{\textbackslash}~\\
+~~~~~~/'~{\_}.-.-.{\_}~`{\textbackslash}~\\
+~~~~~|~~(|)~~~(|)~~|~\\
+~~~~~|~~~{\textbackslash}{\_}{\_}"{\_}{\_}/~~~|~\\
+~~~~~{\textbackslash}~~~~|v.v|~~~~/~\\
+~~~~~~{\textbackslash}~~~|~|~|~~~/~\\
+~~~~~~~`{\textbackslash}~|={\textasciicircum}-|~/'~\\
+~~~~~~~~~`|=-=|'~\\
+~~~~~~~~~~|~-~|~\\
+~~~~~~~~~~|=~~|~\\
+~~~~~~~~~~|-=-|~\\
+~~~~{\_}.-=-=|=~-|=-=-.{\_}~\\
+~~~(~~~~~~|{\_}{\_}{\_}|~~~~~~)~\\
+~~(~`-=-=-=-=-=-=-=-`~)~\\
+~~(`-=-=-=-=-=-=-=-=-`)~\\
+~~(`-=-=-=-=-=-=-=-=-`)~\\
+~~~(`-=-=-=-=-=-=-=-`)~\\
+~~~~(`-=-=-=-=-=-=-`)~\\
+jgs~~`-=-=-=-=-=-=-`
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{examples}{}
+\pdfbookmark[0]{Examples}{examples}
+\section*{Examples}
+
+First example. Consider the following hierarchy:
+\begin{quote}
+\begin{verbatim}>>> O = object
+>>> class F(O): pass
+>>> class E(O): pass
+>>> class D(O): pass
+>>> class C(D,F): pass
+>>> class B(D,E): pass
+>>> class A(B,C): pass\end{verbatim}
+\end{quote}
+
+In this case the inheritance graph can be drawn as
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~~~~~6~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~\\
+Level~3~~~~~~~~~~~~~~~~~|~O~|~~~~~~~~~~~~~~~~~~(more~general)~\\
+~~~~~~~~~~~~~~~~~~~~~~/~~-{}-{}-~~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~~/~~~~|~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~/~~~~~|~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~/~~~~~~|~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~-{}-{}-~~~~-{}-{}-~~~~-{}-{}-~~~~~~~~~~~~~~~~~~~|~\\
+Level~2~~~~~~~~3~|~D~|~4|~E~|~~|~F~|~5~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~-{}-{}-~~~~-{}-{}-~~~~-{}-{}-~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~{\textbackslash}~~{\textbackslash}~{\_}~/~~~~~~~|~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~{\textbackslash}~{\_}~~~~|~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~/~~~~~~{\textbackslash}~~|~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~~-{}-{}-~~~~~~~~~~~~~~~~~~~~|~\\
+Level~1~~~~~~~~~~~~1~|~B~|~~~~|~C~|~2~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~~-{}-{}-~~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~/~~~~~~~~~~~~~~~~~~~~~~|~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~/~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~\\
+Level~0~~~~~~~~~~~~~~~~~0~|~A~|~~~~~~~~~~~~~~~~(more~specialized)~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-
+}\end{quote}
+\end{quote}
+
+The linearizations of O,D,E and F are trivial:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}O]~=~O~\\
+L{[}D]~=~D~O~\\
+L{[}E]~=~E~O~\\
+L{[}F]~=~F~O
+}\end{quote}
+\end{quote}
+
+The linearization of B can be computed as
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}B]~=~B~+~merge(DO,~EO,~DE)
+}\end{quote}
+\end{quote}
+
+We see that D is a good head, therefore we take it and we are reduced to
+compute \texttt{merge(O,EO,E)}. Now O is not a good head, since it is in the
+tail of the sequence EO. In this case the rule says that we have to
+skip to the next sequence. Then we see that E is a good head; we take
+it and we are reduced to compute \texttt{merge(O,O)} which gives O. Therefore
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}B]~=~~B~D~E~O
+}\end{quote}
+\end{quote}
+
+Using the same procedure one finds:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}C]~=~C~+~merge(DO,FO,DF)~\\
+~~~~~=~C~+~D~+~merge(O,FO,F)~\\
+~~~~~=~C~+~D~+~F~+~merge(O,O)~\\
+~~~~~=~C~D~F~O
+}\end{quote}
+\end{quote}
+
+Now we can compute:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}A]~=~A~+~merge(BDEO,CDFO,BC)~\\
+~~~~~=~A~+~B~+~merge(DEO,CDFO,C)~\\
+~~~~~=~A~+~B~+~C~+~merge(DEO,DFO)~\\
+~~~~~=~A~+~B~+~C~+~D~+~merge(EO,FO)~\\
+~~~~~=~A~+~B~+~C~+~D~+~E~+~merge(O,FO)~\\
+~~~~~=~A~+~B~+~C~+~D~+~E~+~F~+~merge(O,O)~\\
+~~~~~=~A~B~C~D~E~F~O
+}\end{quote}
+\end{quote}
+
+In this example, the linearization is ordered in a pretty nice way
+according to the inheritance level, in the sense that lower levels (i.e.
+more specialized classes) have higher precedence (see the inheritance
+graph). However, this is not the general case.
+
+I leave as an exercise for the reader to compute the linearization for
+my second example:
+\begin{quote}
+\begin{verbatim}>>> O = object
+>>> class F(O): pass
+>>> class E(O): pass
+>>> class D(O): pass
+>>> class C(D,F): pass
+>>> class B(E,D): pass
+>>> class A(B,C): pass\end{verbatim}
+\end{quote}
+
+The only difference with the previous example is the change B(D,E) -{}-{\textgreater}
+B(E,D); however even such a little modification completely changes the
+ordering of the hierarchy
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~~~~~~6~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~\\
+Level~3~~~~~~~~~~~~~~~~~~|~O~|~\\
+~~~~~~~~~~~~~~~~~~~~~~~/~~-{}-{}-~~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~~~/~~~~|~~~~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~~/~~~~~|~~~~~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~/~~~~~~|~~~~~~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~-{}-{}-~~~~-{}-{}-~\\
+Level~2~~~~~~~~2~|~E~|~4~|~D~|~~|~F~|~5~\\
+~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~-{}-{}-~~~~-{}-{}-~\\
+~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~/~{\textbackslash}~~~~~/~\\
+~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~~~{\textbackslash}~~~/~\\
+~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~/~~~~~{\textbackslash}~/~\\
+~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~-{}-{}-~\\
+Level~1~~~~~~~~~~~~1~|~B~|~~~|~C~|~3~\\
+~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~~~~~-{}-{}-~\\
+~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~/~\\
+~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~/~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-~\\
+Level~0~~~~~~~~~~~~~~~~0~|~A~|~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~~-{}-{}-
+}\end{quote}
+\end{quote}
+
+Notice that the class E, which is in the second level of the hierarchy,
+precedes the class C, which is in the first level of the hierarchy, i.e.
+E is more specialized than C, even if it is in a higher level.
+
+A lazy programmer can obtain the MRO directly from Python 2.2, since in
+this case it coincides with the Python 2.3 linearization. It is enough
+to invoke the .mro() method of class A:
+\begin{quote}
+\begin{verbatim}>>> A.mro()
+(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
+<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>,
+<type 'object'>)\end{verbatim}
+\end{quote}
+
+Finally, let me consider the example discussed in the first section,
+involving a serious order disagreement. In this case, it is
+straightforward to compute the linearizations of O, X, Y, A and B:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}O]~=~0~\\
+L{[}X]~=~X~O~\\
+L{[}Y]~=~Y~O~\\
+L{[}A]~=~A~X~Y~O~\\
+L{[}B]~=~B~Y~X~O
+}\end{quote}
+\end{quote}
+
+However, it is impossible to compute the linearization for a class C
+that inherits from A and B:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}C]~=~C~+~merge(AXYO,~BYXO,~AB)~\\
+~~~~~=~C~+~A~+~merge(XYO,~BYXO,~B)~\\
+~~~~~=~C~+~A~+~B~+~merge(XYO,~YXO)
+}\end{quote}
+\end{quote}
+
+At this point we cannot merge the lists XYO and YXO, since X is in the
+tail of YXO whereas Y is in the tail of XYO: therefore there are no
+good heads and the C3 algorithm stops. Python 2.3 raises an error and
+refuses to create the class C.
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~{\_}{\_}~\\
+~~~~({\textbackslash}~~~.-.~~~.-.~~~/{\_}")~\\
+~~~~~{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//~\\
+jgs~~~`"`~~~`"`~~~`"`
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{bad-method-resolution-orders}{}
+\pdfbookmark[0]{Bad Method Resolution Orders}{bad-method-resolution-orders}
+\section*{Bad Method Resolution Orders}
+
+A MRO is \emph{bad} when it breaks such fundamental properties as local
+precedence ordering and monotonicity. In this section, I will show
+that both the MRO for classic classes and the MRO for new style classes
+in Python 2.2 are bad.
+
+It is easier to start with the local precedence ordering. Consider the
+following example:
+\begin{quote}
+\begin{verbatim}>>> F=type('Food',(),{'remember2buy':'spam'})
+>>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
+>>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!\end{verbatim}
+\end{quote}
+
+with inheritance diagram
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~O~\\
+~~~~~~~~~~~~~|~\\
+(buy~spam)~~~F~\\
+~~~~~~~~~~~~~|~{\textbackslash}~\\
+~~~~~~~~~~~~~|~E~~~(buy~eggs)~\\
+~~~~~~~~~~~~~|~/~\\
+~~~~~~~~~~~~~G~\\
+~\\
+~~~~~~(buy~eggs~or~spam~?)
+}\end{quote}
+\end{quote}
+
+We see that class G inherits from F and E, with F \emph{before} E: therefore
+we would expect the attribute \emph{G.remember2buy} to be inherited by
+\emph{F.rembermer2buy} and not by \emph{E.remember2buy}: nevertheless Python 2.2
+gives
+\begin{quote}
+\begin{verbatim}>>> G.remember2buy
+'eggs'\end{verbatim}
+\end{quote}
+
+This is a breaking of local precedence ordering since the order in the
+local precedence list, i.e. the list of the parents of G, is not
+preserved in the Python 2.2 linearization of G:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}G,P22]=~G~E~F~object~~~{\#}~F~*follows*~E
+}\end{quote}
+\end{quote}
+
+One could argue that the reason why F follows E in the Python 2.2
+linearization is that F is less specialized than E, since F is the
+superclass of E; nevertheless the breaking of local precedence ordering
+is quite non-intuitive and error prone. This is particularly true since
+it is a different from old style classes:
+\begin{quote}
+\begin{verbatim}>>> class F: remember2buy='spam'
+>>> class E(F): remember2buy='eggs'
+>>> class G(F,E): pass
+>>> G.remember2buy
+'spam'\end{verbatim}
+\end{quote}
+
+In this case the MRO is GFEF and the local precedence ordering is
+preserved.
+
+As a general rule, hierarchies such as the previous one should be
+avoided, since it is unclear if F should override E or viceversa.
+Python 2.3 solves the ambiguity by raising an exception in the creation
+of class G, effectively stopping the programmer from generating
+ambiguous hierarchies. The reason for that is that the C3 algorithm
+fails when the merge
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+merge(FO,EFO,FE)
+}\end{quote}
+\end{quote}
+
+cannot be computed, because F is in the tail of EFO and E is in the tail
+of FE.
+
+The real solution is to design a non-ambiguous hierarchy, i.e. to derive
+G from E and F (the more specific first) and not from F and E; in this
+case the MRO is GEF without any doubt.
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~O~\\
+~~~~~~~~~~~|~\\
+~~~~~~~~~~~F~(spam)~\\
+~~~~~~~~~/~|~\\
+(eggs)~~~E~|~\\
+~~~~~~~~~{\textbackslash}~|~\\
+~~~~~~~~~~~G~\\
+~~~~~~~~~~~~~(eggs,~no~doubt)
+}\end{quote}
+\end{quote}
+
+Python 2.3 forces the programmer to write good hierarchies (or, at
+least, less error-prone ones).
+
+On a related note, let me point out that the Python 2.3 algorithm is
+smart enough to recognize obvious mistakes, as the duplication of
+classes in the list of parents:
+\begin{quote}
+\begin{verbatim}>>> class A(object): pass
+>>> class C(A,A): pass # error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: duplicate base class A\end{verbatim}
+\end{quote}
+
+Python 2.2 (both for classic classes and new style classes) in this
+situation, would not raise any exception.
+
+Finally, I would like to point out two lessons we have learned from this
+example:
+\newcounter{listcnt2}
+\begin{list}{\arabic{listcnt2}.}
+{
+\usecounter{listcnt2}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+despite the name, the MRO determines the resolution order of
+attributes, not only of methods;
+
+\item {}
+the default food for Pythonistas is spam ! (but you already knew
+that ;-)
+
+\end{list}
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~{\_}{\_}~\\
+~~~~({\textbackslash}~~~.-.~~~.-.~~~/{\_}")~\\
+~~~~~{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//~\\
+jgs~~~`"`~~~`"`~~~`"`
+}\end{quote}
+\end{quote}
+
+Having discussed the issue of local precedence ordering, let me now
+consider the issue of monotonicity. My goal is to show that neither the
+MRO for classic classes nor that for Python 2.2 new style classes is
+monotonic.
+
+To prove that the MRO for classic classes is non-monotonic is rather
+trivial, it is enough to look at the diamond diagram:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~C~\\
+~~/~{\textbackslash}~\\
+~/~~~{\textbackslash}~\\
+A~~~~~B~\\
+~{\textbackslash}~~~/~\\
+~~{\textbackslash}~/~\\
+~~~D
+}\end{quote}
+\end{quote}
+
+One easily discerns the inconsistency:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}B,P21]~=~B~C~~~~~~~~{\#}~B~precedes~C~:~B's~methods~win~\\
+L{[}D,P21]~=~D~A~C~B~C~~{\#}~B~follows~C~~:~C's~methods~win!
+}\end{quote}
+\end{quote}
+
+On the other hand, there are no problems with the Python 2.2 and 2.3
+MROs, they give both
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}D]~=~D~A~B~C
+}\end{quote}
+\end{quote}
+
+Guido points out in his essay [\hyperlink{id6}{3}] that the classic MRO is not so bad in
+practice, since one can typically avoids diamonds for classic classes.
+But all new style classes inherit from \texttt{object}, therefore diamonds are
+unavoidable and inconsistencies shows up in every multiple inheritance
+graph.
+
+The MRO of Python 2.2 makes breaking monotonicity difficult, but not
+impossible. The following example, originally provided by Samuele
+Pedroni, shows that the MRO of Python 2.2 is non-monotonic:
+\begin{quote}
+\begin{verbatim}>>> class A(object): pass
+>>> class B(object): pass
+>>> class C(object): pass
+>>> class D(object): pass
+>>> class E(object): pass
+>>> class K1(A,B,C): pass
+>>> class K2(D,B,E): pass
+>>> class K3(D,A): pass
+>>> class Z(K1,K2,K3): pass\end{verbatim}
+\end{quote}
+
+Here are the linearizations according to the C3 MRO (the reader should
+verify these linearizations as an exercise and draw the inheritance
+diagram ;-)
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}A]~=~A~O~\\
+L{[}B]~=~B~O~\\
+L{[}C]~=~C~O~\\
+L{[}D]~=~D~O~\\
+L{[}E]~=~E~O~\\
+L{[}K1]=~K1~A~B~C~O~\\
+L{[}K2]=~K2~D~B~E~O~\\
+L{[}K3]=~K3~D~A~O~\\
+L{[}Z]~=~Z~K1~K2~K3~D~A~B~C~E~O
+}\end{quote}
+\end{quote}
+
+Python 2.2 gives exactly the same linearizations for A, B, C, D, E, K1,
+K2 and K3, but a different linearization for Z:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+L{[}Z,P22]~=~Z~K1~K3~A~K2~D~B~C~E~O
+}\end{quote}
+\end{quote}
+
+It is clear that this linearization is \emph{wrong}, since A comes before D
+whereas in the linearization of K3 A comes \emph{after} D. In other words, in
+K3 methods derived by D override methods derived by A, but in Z, which
+still is a subclass of K3, methods derived by A override methods derived
+by D! This is a violation of monotonicity. Moreover, the Python 2.2
+linearization of Z is also inconsistent with local precedence ordering,
+since the local precedence list of the class Z is {[}K1, K2, K3] (K2
+precedes K3), whereas in the linearization of Z K2 \emph{follows} K3. These
+problems explain why the 2.2 rule has been dismissed in favor of the C3
+rule.
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\_}{\_}~\\
+~~~({\textbackslash}~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~/{\_}")~\\
+~~~~{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//~\\
+jgs~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-end}{}
+\pdfbookmark[0]{The end}{the-end}
+\section*{The end}
+
+This section is for the impatient reader, who skipped all the previous
+sections and jumped immediately to the end. This section is for the
+lazy programmer too, who didn't want to exercise her/his brain.
+Finally, it is for the programmer with some hubris, otherwise s/he would
+not be reading a paper on the C3 method resolution order in multiple
+inheritance hierarchies ;-) These three virtues taken all together (and
+\emph{not} separately) deserve a prize: the prize is a short Python 2.2
+script that allows you to compute the 2.3 MRO without risk to your
+brain. Simply change the last line to play with the various examples I
+have discussed in this paper.
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<mro.py>~\\
+~\\
+"{}"{}"C3~algorithm~by~Samuele~Pedroni~(with~readability~enhanced~by~me)."{}"{}"~\\
+~\\
+class~{\_}{\_}metaclass{\_}{\_}(type):~\\
+~~~~"All~classes~are~metamagically~modified~to~be~nicely~printed"~\\
+~~~~{\_}{\_}repr{\_}{\_}~=~lambda~cls:~cls.{\_}{\_}name{\_}{\_}~\\
+~\\
+class~ex{\_}2:~\\
+~~~~"Serious~order~disagreement"~{\#}From~Guido~\\
+~~~~class~O:~pass~\\
+~~~~class~X(O):~pass~\\
+~~~~class~Y(O):~pass~\\
+~~~~class~A(X,Y):~pass~\\
+~~~~class~B(Y,X):~pass~\\
+~~~~try:~\\
+~~~~~~~~class~Z(A,B):~pass~{\#}creates~Z(A,B)~in~Python~2.2~\\
+~~~~except~TypeError:~\\
+~~~~~~~~pass~{\#}~Z(A,B)~cannot~be~created~in~Python~2.3~\\
+~\\
+class~ex{\_}5:~\\
+~~~~"My~first~example"~\\
+~~~~class~O:~pass~\\
+~~~~class~F(O):~pass~\\
+~~~~class~E(O):~pass~\\
+~~~~class~D(O):~pass~\\
+~~~~class~C(D,F):~pass~\\
+~~~~class~B(D,E):~pass~\\
+~~~~class~A(B,C):~pass~\\
+~\\
+class~ex{\_}6:~\\
+~~~~"My~second~example"~\\
+~~~~class~O:~pass~\\
+~~~~class~F(O):~pass~\\
+~~~~class~E(O):~pass~\\
+~~~~class~D(O):~pass~\\
+~~~~class~C(D,F):~pass~\\
+~~~~class~B(E,D):~pass~\\
+~~~~class~A(B,C):~pass~\\
+~\\
+class~ex{\_}9:~\\
+~~~~"Difference~between~Python~2.2~MRO~and~C3"~{\#}From~Samuele~\\
+~~~~class~O:~pass~\\
+~~~~class~A(O):~pass~\\
+~~~~class~B(O):~pass~\\
+~~~~class~C(O):~pass~\\
+~~~~class~D(O):~pass~\\
+~~~~class~E(O):~pass~\\
+~~~~class~K1(A,B,C):~pass~\\
+~~~~class~K2(D,B,E):~pass~\\
+~~~~class~K3(D,A):~pass~\\
+~~~~class~Z(K1,K2,K3):~pass~\\
+~\\
+def~merge(seqs):~\\
+~~~~print~'{\textbackslash}n{\textbackslash}nCPL{[}{\%}s]={\%}s'~{\%}~(seqs{[}0]{[}0],seqs),~\\
+~~~~res~=~{[}];~i=0~\\
+~~~~while~1:~\\
+~~~~~~nonemptyseqs={[}seq~for~seq~in~seqs~if~seq]~\\
+~~~~~~if~not~nonemptyseqs:~return~res~\\
+~~~~~~i+=1;~print~'{\textbackslash}n',i,'round:~candidates...',~\\
+~~~~~~for~seq~in~nonemptyseqs:~{\#}~find~merge~candidates~among~seq~heads~\\
+~~~~~~~~~~cand~=~seq{[}0];~print~'~',cand,~\\
+~~~~~~~~~~nothead={[}s~for~s~in~nonemptyseqs~if~cand~in~s{[}1:]]~\\
+~~~~~~~~~~if~nothead:~cand=None~{\#}reject~candidate~\\
+~~~~~~~~~~else:~break~\\
+~~~~~~if~not~cand:~raise~"Inconsistent~hierarchy"~\\
+~~~~~~res.append(cand)~\\
+~~~~~~for~seq~in~nonemptyseqs:~{\#}~remove~cand~\\
+~~~~~~~~~~if~seq{[}0]~==~cand:~del~seq{[}0]~\\
+~\\
+def~mro(C):~\\
+~~~~"Compute~the~class~precedence~list~(mro)~according~to~C3"~\\
+~~~~return~merge({[}{[}C]]+map(mro,C.{\_}{\_}bases{\_}{\_})+{[}list(C.{\_}{\_}bases{\_}{\_})])~\\
+~\\
+def~print{\_}mro(C):~\\
+~~~~print~'{\textbackslash}nMRO{[}{\%}s]={\%}s'~{\%}~(C,mro(C))~\\
+~~~~print~'{\textbackslash}nP22~MRO{[}{\%}s]={\%}s'~{\%}~(C,C.mro())~\\
+~\\
+print{\_}mro(ex{\_}9.Z)~\\
+~\\
+{\#}</mro.py>
+}\end{quote}
+\end{quote}
+
+That's all folks,
+\begin{quote}
+
+enjoy !
+\end{quote}
+
+
+%___________________________________________________________________________
+\hspace*{\fill}\hrulefill\hspace*{\fill}
+
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+~~~~{\_}{\_}~\\
+~~~("{\_}{\textbackslash}~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~.-.~~~/)~\\
+~~~~~~{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//{\textasciicircum}{\textbackslash}{\textbackslash}{\_}//~\\
+jgs~~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`~~~`"`
+}\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{resources}{}
+\pdfbookmark[0]{Resources}{resources}
+\section*{Resources}
+\begin{figure}[b]\hypertarget{id4}[1]
+The thread on python-dev started by Samuele Pedroni:
+\href{http://mail.python.org/pipermail/python-dev/2002-October/029035.html}{http://mail.python.org/pipermail/python-dev/2002-October/029035.html}
+\end{figure}
+\begin{figure}[b]\hypertarget{id5}[2]
+The paper \emph{A Monotonic Superclass Linearization for Dylan}:
+\href{http://www.webcom.com/haahr/dylan/linearization-oopsla96.html}{http://www.webcom.com/haahr/dylan/linearization-oopsla96.html}
+\end{figure}
+\begin{figure}[b]\hypertarget{id6}[3]
+Guido van Rossum's essay, \emph{Unifying types and classes in Python 2.2}:
+\href{http://www.python.org/2.2.2/descrintro.html}{http://www.python.org/2.2.2/descrintro.html}
+\end{figure}
+
+\end{document}
+
diff --git a/pypers/oxford/mro.txt b/pypers/oxford/mro.txt
new file mode 100755
index 0000000..9fb59ff
--- /dev/null
+++ b/pypers/oxford/mro.txt
@@ -0,0 +1,787 @@
+The Python 2.3 Method Resolution Order
+======================================
+
+:Version: 1.4
+:Author: Michele Simionato
+:E-mail: michelesimionato@libero.it
+:Address: Department of Physics and Astronomy
+ 210 Allen Hall Pittsburgh PA 15260 U.S.A.
+:Home-page: http://www.phyast.pitt.edu/~micheles/
+
+:Abstract:
+
+ *This document is intended for Python programmers who want to
+ understand the C3 Method Resolution Order used in Python 2.3.
+ Although it is not intended for newbies, it is quite pedagogical with
+ many worked out examples. I am not aware of other publicly available
+ documents with the same scope, therefore it should be useful.*
+
+Disclaimer:
+
+ I donate this document to the Python Software Foundation, under the
+ Python 2.3 license. As usual in these circumstances, I warn the
+ reader that what follows *should* be correct, but I don't give any
+ warranty. Use it at your own risk and peril!
+
+Acknowledgments:
+
+ All the people of the Python mailing list who sent me their support.
+ Paul Foley who pointed out various imprecisions and made me to add the
+ part on local precedence ordering. David Goodger for help with the
+ formatting in reStructuredText. David Mertz for help with the editing.
+ Joan G. Stark for the pythonic pictures. Finally, Guido van Rossum who
+ enthusiastically added this document to the official Python 2.3 home-page.
+
+----
+
+ ::
+
+
+
+ .-=-. .--.
+ __ .' '. / " )
+ _ .' '. / .-. \ / .-'\
+ ( \ / .-. \ / / \ \ / / ^
+ \ `-` / \ `-' / \ `-` /
+ jgs`-.-` '.____.' `.____.'
+
+
+The beginning
+-------------
+
+ *Felix qui potuit rerum cognoscere causas* -- Virgilius
+
+Everything started with a post by Samuele Pedroni to the Python
+development mailing list [#]_. In his post, Samuele showed that the
+Python 2.2 method resolution order is not monotonic and he proposed to
+replace it with the C3 method resolution order. Guido agreed with his
+arguments and therefore now Python 2.3 uses C3. The C3 method itself
+has nothing to do with Python, since it was invented by people working
+on Dylan and it is described in a paper intended for lispers [#]_. The
+present paper gives a (hopefully) readable discussion of the C3
+algorithm for Pythonistas who want to understand the reasons for the
+change.
+
+First of all, let me point out that what I am going to say only applies
+to the *new style classes* introduced in Python 2.2: *classic classes*
+maintain their old method resolution order, depth first and then left to
+right. Therefore, there is no breaking of old code for classic classes;
+and even if in principle there could be breaking of code for Python 2.2
+new style classes, in practice the cases in which the C3 resolution
+order differs from the Python 2.2 method resolution order are so rare
+that no real breaking of code is expected. Therefore:
+
+ *Don't be scared!*
+
+Moreover, unless you make strong use of multiple inheritance and you
+have non-trivial hierarchies, you don't need to understand the C3
+algorithm, and you can easily skip this paper. On the other hand, if
+you really want to know how multiple inheritance works, then this paper
+is for you. The good news is that things are not as complicated as you
+might expect.
+
+Let me begin with some basic definitions.
+
+1) Given a class C in a complicated multiple inheritance hierarchy, it
+ is a non-trivial task to specify the order in which methods are
+ overridden, i.e. to specify the order of the ancestors of C.
+
+2) The list of the ancestors of a class C, including the class itself,
+ ordered from the nearest ancestor to the furthest, is called the
+ class precedence list or the *linearization* of C.
+
+3) The *Method Resolution Order* (MRO) is the set of rules that
+ construct the linearization. In the Python literature, the idiom
+ "the MRO of C" is also used as a synonymous for the linearization of
+ the class C.
+
+4) For instance, in the case of single inheritance hierarchy, if C is a
+ subclass of C1, and C1 is a subclass of C2, then the linearization of
+ C is simply the list [C, C1 , C2]. However, with multiple
+ inheritance hierarchies, the construction of the linearization is
+ more cumbersome, since it is more difficult to construct a
+ linearization that respects *local precedence ordering* and
+ *monotonicity*.
+
+5) I will discuss the local precedence ordering later, but I can give
+ the definition of monotonicity here. A MRO is monotonic when the
+ following is true: *if C1 precedes C2 in the linearization of C,
+ then C1 precedes C2 in the linearization of any subclass of C*.
+ Otherwise, the innocuous operation of deriving a new class could
+ change the resolution order of methods, potentially introducing very
+ subtle bugs. Examples where this happens will be shown later.
+
+6) Not all classes admit a linearization. There are cases, in
+ complicated hierarchies, where it is not possible to derive a class
+ such that its linearization respects all the desired properties.
+
+Here I give an example of this situation. Consider the hierarchy
+
+ >>> O = object
+ >>> class X(O): pass
+ >>> class Y(O): pass
+ >>> class A(X,Y): pass
+ >>> class B(Y,X): pass
+
+which can be represented with the following inheritance graph, where I
+have denoted with O the ``object`` class, which is the beginning of any
+hierarchy for new style classes:
+
+ ::
+
+ -----------
+ | |
+ | O |
+ | / \ |
+ - X Y /
+ | / | /
+ | / |/
+ A B
+ \ /
+ ?
+
+In this case, it is not possible to derive a new class C from A and B,
+since X precedes Y in A, but Y precedes X in B, therefore the method
+resolution order would be ambiguous in C.
+
+Python 2.3 raises an exception in this situation (TypeError: MRO
+conflict among bases Y, X) forbidding the naive programmer from creating
+ambiguous hierarchies. Python 2.2 instead does not raise an exception,
+but chooses an *ad hoc* ordering (CABXYO in this case).
+
+----
+
+ ::
+
+ _ .-=-. .-==-.
+ { } __ .' O o '. / -<' )
+ { } .' O'. / o .-. O \ / .--v`
+ { } / .-. o\ /O / \ o\ /O /
+ \ `-` / \ O`-'o / \ O`-`o /
+ jgs `-.-` '.____.' `.____.'
+
+
+The C3 Method Resolution Order
+------------------------------
+
+Let me introduce a few simple notations which will be useful for the
+following discussion. I will use the shortcut notation
+
+ C1 C2 ... CN
+
+to indicate the list of classes [C1, C2, ... , CN].
+
+The *head* of the list is its first element:
+
+ head = C1
+
+whereas the *tail* is the rest of the list:
+
+ tail = C2 ... CN.
+
+I shall also use the notation
+
+ C + (C1 C2 ... CN) = C C1 C2 ... CN
+
+to denote the sum of the lists [C] + [C1, C2, ... ,CN].
+
+Now I can explain how the MRO works in Python 2.3.
+
+Consider a class C in a multiple inheritance hierarchy, with C
+inheriting from the base classes B1, B2, ... , BN. We want to
+compute the linearization L[C] of the class C. The rule is the
+following:
+
+ *the linearization of C is the sum of C plus the merge of the
+ linearizations of the parents and the list of the parents.*
+
+In symbolic notation:
+
+ L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
+
+In particular, if C is the ``object`` class, which has no parents, the
+linearization is trivial:
+
+ L[object] = object.
+
+However, in general one has to compute the merge according to the following
+prescription:
+
+ *take the head of the first list, i.e L[B1][0]; if this head is not in
+ the tail of any of the other lists, then add it to the linearization
+ of C and remove it from the lists in the merge, otherwise look at the
+ head of the next list and take it, if it is a good head. Then repeat
+ the operation until all the class are removed or it is impossible to
+ find good heads. In this case, it is impossible to construct the
+ merge, Python 2.3 will refuse to create the class C and will raise an
+ exception.*
+
+This prescription ensures that the merge operation *preserves* the
+ordering, if the ordering can be preserved. On the other hand, if the
+order cannot be preserved (as in the example of serious order
+disagreement discussed above) then the merge cannot be computed.
+
+The computation of the merge is trivial if C has only one parent
+(single inheritance); in this case
+
+ L[C(B)] = C + merge(L[B],B) = C + L[B]
+
+However, in the case of multiple inheritance things are more cumbersome
+and I don't expect you can understand the rule without a couple of
+examples ;-)
+
+----
+
+ ::
+
+ .-'-.
+ /' `\
+ /' _.-.-._ `\
+ | (|) (|) |
+ | \__"__/ |
+ \ |v.v| /
+ \ | | | /
+ `\ |=^-| /'
+ `|=-=|'
+ | - |
+ |= |
+ |-=-|
+ _.-=-=|= -|=-=-._
+ ( |___| )
+ ( `-=-=-=-=-=-=-=-` )
+ (`-=-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-=-`)
+ (`-=-=-=-=-=-=-`)
+ jgs `-=-=-=-=-=-=-`
+
+
+Examples
+--------
+
+First example. Consider the following hierarchy:
+
+ >>> O = object
+ >>> class F(O): pass
+ >>> class E(O): pass
+ >>> class D(O): pass
+ >>> class C(D,F): pass
+ >>> class B(D,E): pass
+ >>> class A(B,C): pass
+
+In this case the inheritance graph can be drawn as
+
+ ::
+
+ 6
+ ---
+ Level 3 | O | (more general)
+ / --- \
+ / | \ |
+ / | \ |
+ / | \ |
+ --- --- --- |
+ Level 2 3 | D | 4| E | | F | 5 |
+ --- --- --- |
+ \ \ _ / | |
+ \ / \ _ | |
+ \ / \ | |
+ --- --- |
+ Level 1 1 | B | | C | 2 |
+ --- --- |
+ \ / |
+ \ / \ /
+ ---
+ Level 0 0 | A | (more specialized)
+ ---
+
+
+The linearizations of O,D,E and F are trivial:
+
+ ::
+
+ L[O] = O
+ L[D] = D O
+ L[E] = E O
+ L[F] = F O
+
+The linearization of B can be computed as
+
+ ::
+
+ L[B] = B + merge(DO, EO, DE)
+
+We see that D is a good head, therefore we take it and we are reduced to
+compute ``merge(O,EO,E)``. Now O is not a good head, since it is in the
+tail of the sequence EO. In this case the rule says that we have to
+skip to the next sequence. Then we see that E is a good head; we take
+it and we are reduced to compute ``merge(O,O)`` which gives O. Therefore
+
+ ::
+
+ L[B] = B D E O
+
+Using the same procedure one finds:
+
+ ::
+
+ L[C] = C + merge(DO,FO,DF)
+ = C + D + merge(O,FO,F)
+ = C + D + F + merge(O,O)
+ = C D F O
+
+Now we can compute:
+
+ ::
+
+ L[A] = A + merge(BDEO,CDFO,BC)
+ = A + B + merge(DEO,CDFO,C)
+ = A + B + C + merge(DEO,DFO)
+ = A + B + C + D + merge(EO,FO)
+ = A + B + C + D + E + merge(O,FO)
+ = A + B + C + D + E + F + merge(O,O)
+ = A B C D E F O
+
+In this example, the linearization is ordered in a pretty nice way
+according to the inheritance level, in the sense that lower levels (i.e.
+more specialized classes) have higher precedence (see the inheritance
+graph). However, this is not the general case.
+
+I leave as an exercise for the reader to compute the linearization for
+my second example:
+
+ >>> O = object
+ >>> class F(O): pass
+ >>> class E(O): pass
+ >>> class D(O): pass
+ >>> class C(D,F): pass
+ >>> class B(E,D): pass
+ >>> class A(B,C): pass
+
+The only difference with the previous example is the change B(D,E) -->
+B(E,D); however even such a little modification completely changes the
+ordering of the hierarchy
+
+ ::
+
+ 6
+ ---
+ Level 3 | O |
+ / --- \
+ / | \
+ / | \
+ / | \
+ --- --- ---
+ Level 2 2 | E | 4 | D | | F | 5
+ --- --- ---
+ \ / \ /
+ \ / \ /
+ \ / \ /
+ --- ---
+ Level 1 1 | B | | C | 3
+ --- ---
+ \ /
+ \ /
+ ---
+ Level 0 0 | A |
+ ---
+
+
+Notice that the class E, which is in the second level of the hierarchy,
+precedes the class C, which is in the first level of the hierarchy, i.e.
+E is more specialized than C, even if it is in a higher level.
+
+A lazy programmer can obtain the MRO directly from Python 2.2, since in
+this case it coincides with the Python 2.3 linearization. It is enough
+to invoke the .mro() method of class A:
+
+ >>> A.mro()
+ (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
+ <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>,
+ <type 'object'>)
+
+Finally, let me consider the example discussed in the first section,
+involving a serious order disagreement. In this case, it is
+straightforward to compute the linearizations of O, X, Y, A and B:
+
+ ::
+
+ L[O] = 0
+ L[X] = X O
+ L[Y] = Y O
+ L[A] = A X Y O
+ L[B] = B Y X O
+
+However, it is impossible to compute the linearization for a class C
+that inherits from A and B:
+
+ ::
+
+ L[C] = C + merge(AXYO, BYXO, AB)
+ = C + A + merge(XYO, BYXO, B)
+ = C + A + B + merge(XYO, YXO)
+
+At this point we cannot merge the lists XYO and YXO, since X is in the
+tail of YXO whereas Y is in the tail of XYO: therefore there are no
+good heads and the C3 algorithm stops. Python 2.3 raises an error and
+refuses to create the class C.
+
+----
+
+ ::
+
+ __
+ (\ .-. .-. /_")
+ \\_//^\\_//^\\_//
+ jgs `"` `"` `"`
+
+
+Bad Method Resolution Orders
+----------------------------
+
+A MRO is *bad* when it breaks such fundamental properties as local
+precedence ordering and monotonicity. In this section, I will show
+that both the MRO for classic classes and the MRO for new style classes
+in Python 2.2 are bad.
+
+It is easier to start with the local precedence ordering. Consider the
+following example:
+
+ >>> F=type('Food',(),{'remember2buy':'spam'})
+ >>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
+ >>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!
+
+with inheritance diagram
+
+ ::
+
+ O
+ |
+ (buy spam) F
+ | \
+ | E (buy eggs)
+ | /
+ G
+
+ (buy eggs or spam ?)
+
+
+We see that class G inherits from F and E, with F *before* E: therefore
+we would expect the attribute *G.remember2buy* to be inherited by
+*F.rembermer2buy* and not by *E.remember2buy*: nevertheless Python 2.2
+gives
+
+ >>> G.remember2buy
+ 'eggs'
+
+This is a breaking of local precedence ordering since the order in the
+local precedence list, i.e. the list of the parents of G, is not
+preserved in the Python 2.2 linearization of G:
+
+ ::
+
+ L[G,P22]= G E F object # F *follows* E
+
+One could argue that the reason why F follows E in the Python 2.2
+linearization is that F is less specialized than E, since F is the
+superclass of E; nevertheless the breaking of local precedence ordering
+is quite non-intuitive and error prone. This is particularly true since
+it is a different from old style classes:
+
+ >>> class F: remember2buy='spam'
+ >>> class E(F): remember2buy='eggs'
+ >>> class G(F,E): pass
+ >>> G.remember2buy
+ 'spam'
+
+In this case the MRO is GFEF and the local precedence ordering is
+preserved.
+
+As a general rule, hierarchies such as the previous one should be
+avoided, since it is unclear if F should override E or viceversa.
+Python 2.3 solves the ambiguity by raising an exception in the creation
+of class G, effectively stopping the programmer from generating
+ambiguous hierarchies. The reason for that is that the C3 algorithm
+fails when the merge
+
+ ::
+
+ merge(FO,EFO,FE)
+
+cannot be computed, because F is in the tail of EFO and E is in the tail
+of FE.
+
+The real solution is to design a non-ambiguous hierarchy, i.e. to derive
+G from E and F (the more specific first) and not from F and E; in this
+case the MRO is GEF without any doubt.
+
+ ::
+
+ O
+ |
+ F (spam)
+ / |
+ (eggs) E |
+ \ |
+ G
+ (eggs, no doubt)
+
+
+Python 2.3 forces the programmer to write good hierarchies (or, at
+least, less error-prone ones).
+
+On a related note, let me point out that the Python 2.3 algorithm is
+smart enough to recognize obvious mistakes, as the duplication of
+classes in the list of parents:
+
+ >>> class A(object): pass
+ >>> class C(A,A): pass # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: duplicate base class A
+
+Python 2.2 (both for classic classes and new style classes) in this
+situation, would not raise any exception.
+
+Finally, I would like to point out two lessons we have learned from this
+example:
+
+1. despite the name, the MRO determines the resolution order of
+ attributes, not only of methods;
+
+2. the default food for Pythonistas is spam ! (but you already knew
+ that ;-)
+
+----
+
+ ::
+
+ __
+ (\ .-. .-. /_")
+ \\_//^\\_//^\\_//
+ jgs `"` `"` `"`
+
+
+Having discussed the issue of local precedence ordering, let me now
+consider the issue of monotonicity. My goal is to show that neither the
+MRO for classic classes nor that for Python 2.2 new style classes is
+monotonic.
+
+To prove that the MRO for classic classes is non-monotonic is rather
+trivial, it is enough to look at the diamond diagram:
+
+ ::
+
+
+ C
+ / \
+ / \
+ A B
+ \ /
+ \ /
+ D
+
+One easily discerns the inconsistency:
+
+ ::
+
+ L[B,P21] = B C # B precedes C : B's methods win
+ L[D,P21] = D A C B C # B follows C : C's methods win!
+
+On the other hand, there are no problems with the Python 2.2 and 2.3
+MROs, they give both
+
+ ::
+
+ L[D] = D A B C
+
+Guido points out in his essay [#]_ that the classic MRO is not so bad in
+practice, since one can typically avoids diamonds for classic classes.
+But all new style classes inherit from ``object``, therefore diamonds are
+unavoidable and inconsistencies shows up in every multiple inheritance
+graph.
+
+The MRO of Python 2.2 makes breaking monotonicity difficult, but not
+impossible. The following example, originally provided by Samuele
+Pedroni, shows that the MRO of Python 2.2 is non-monotonic:
+
+ >>> class A(object): pass
+ >>> class B(object): pass
+ >>> class C(object): pass
+ >>> class D(object): pass
+ >>> class E(object): pass
+ >>> class K1(A,B,C): pass
+ >>> class K2(D,B,E): pass
+ >>> class K3(D,A): pass
+ >>> class Z(K1,K2,K3): pass
+
+Here are the linearizations according to the C3 MRO (the reader should
+verify these linearizations as an exercise and draw the inheritance
+diagram ;-)
+
+ ::
+
+ L[A] = A O
+ L[B] = B O
+ L[C] = C O
+ L[D] = D O
+ L[E] = E O
+ L[K1]= K1 A B C O
+ L[K2]= K2 D B E O
+ L[K3]= K3 D A O
+ L[Z] = Z K1 K2 K3 D A B C E O
+
+Python 2.2 gives exactly the same linearizations for A, B, C, D, E, K1,
+K2 and K3, but a different linearization for Z:
+
+ ::
+
+ L[Z,P22] = Z K1 K3 A K2 D B C E O
+
+It is clear that this linearization is *wrong*, since A comes before D
+whereas in the linearization of K3 A comes *after* D. In other words, in
+K3 methods derived by D override methods derived by A, but in Z, which
+still is a subclass of K3, methods derived by A override methods derived
+by D! This is a violation of monotonicity. Moreover, the Python 2.2
+linearization of Z is also inconsistent with local precedence ordering,
+since the local precedence list of the class Z is [K1, K2, K3] (K2
+precedes K3), whereas in the linearization of Z K2 *follows* K3. These
+problems explain why the 2.2 rule has been dismissed in favor of the C3
+rule.
+
+----
+
+ ::
+
+ __
+ (\ .-. .-. .-. .-. .-. .-. .-. .-. /_")
+ \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
+ jgs `"` `"` `"` `"` `"` `"` `"` `"` `"`
+
+
+
+The end
+-------
+
+This section is for the impatient reader, who skipped all the previous
+sections and jumped immediately to the end. This section is for the
+lazy programmer too, who didn't want to exercise her/his brain.
+Finally, it is for the programmer with some hubris, otherwise s/he would
+not be reading a paper on the C3 method resolution order in multiple
+inheritance hierarchies ;-) These three virtues taken all together (and
+*not* separately) deserve a prize: the prize is a short Python 2.2
+script that allows you to compute the 2.3 MRO without risk to your
+brain. Simply change the last line to play with the various examples I
+have discussed in this paper.
+
+ ::
+
+ #<mro.py>
+
+ """C3 algorithm by Samuele Pedroni (with readability enhanced by me)."""
+
+ class __metaclass__(type):
+ "All classes are metamagically modified to be nicely printed"
+ __repr__ = lambda cls: cls.__name__
+
+ class ex_2:
+ "Serious order disagreement" #From Guido
+ class O: pass
+ class X(O): pass
+ class Y(O): pass
+ class A(X,Y): pass
+ class B(Y,X): pass
+ try:
+ class Z(A,B): pass #creates Z(A,B) in Python 2.2
+ except TypeError:
+ pass # Z(A,B) cannot be created in Python 2.3
+
+ class ex_5:
+ "My first example"
+ class O: pass
+ class F(O): pass
+ class E(O): pass
+ class D(O): pass
+ class C(D,F): pass
+ class B(D,E): pass
+ class A(B,C): pass
+
+ class ex_6:
+ "My second example"
+ class O: pass
+ class F(O): pass
+ class E(O): pass
+ class D(O): pass
+ class C(D,F): pass
+ class B(E,D): pass
+ class A(B,C): pass
+
+ class ex_9:
+ "Difference between Python 2.2 MRO and C3" #From Samuele
+ class O: pass
+ class A(O): pass
+ class B(O): pass
+ class C(O): pass
+ class D(O): pass
+ class E(O): pass
+ class K1(A,B,C): pass
+ class K2(D,B,E): pass
+ class K3(D,A): pass
+ class Z(K1,K2,K3): pass
+
+ def merge(seqs):
+ print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
+ res = []; i=0
+ while 1:
+ nonemptyseqs=[seq for seq in seqs if seq]
+ if not nonemptyseqs: return res
+ i+=1; print '\n',i,'round: candidates...',
+ for seq in nonemptyseqs: # find merge candidates among seq heads
+ cand = seq[0]; print ' ',cand,
+ nothead=[s for s in nonemptyseqs if cand in s[1:]]
+ if nothead: cand=None #reject candidate
+ else: break
+ if not cand: raise "Inconsistent hierarchy"
+ res.append(cand)
+ for seq in nonemptyseqs: # remove cand
+ if seq[0] == cand: del seq[0]
+
+ def mro(C):
+ "Compute the class precedence list (mro) according to C3"
+ return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])
+
+ def print_mro(C):
+ print '\nMRO[%s]=%s' % (C,mro(C))
+ print '\nP22 MRO[%s]=%s' % (C,C.mro())
+
+ print_mro(ex_9.Z)
+
+ #</mro.py>
+
+That's all folks,
+
+ enjoy !
+
+
+----
+
+ ::
+
+
+ __
+ ("_\ .-. .-. .-. .-. .-. .-. .-. .-. /)
+ \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
+ jgs `"` `"` `"` `"` `"` `"` `"` `"` `"`
+
+
+Resources
+---------
+
+.. [#] The thread on python-dev started by Samuele Pedroni:
+ http://mail.python.org/pipermail/python-dev/2002-October/029035.html
+
+.. [#] The paper *A Monotonic Superclass Linearization for Dylan*:
+ http://www.webcom.com/haahr/dylan/linearization-oopsla96.html
+
+.. [#] Guido van Rossum's essay, *Unifying types and classes in Python 2.2*:
+ http://www.python.org/2.2.2/descrintro.html
diff --git a/pypers/oxford/multilingual.py b/pypers/oxford/multilingual.py
new file mode 100755
index 0000000..bed973e
--- /dev/null
+++ b/pypers/oxford/multilingual.py
@@ -0,0 +1,52 @@
+# multilingual.py
+
+import sys
+from descriptor import AttributeDescriptor
+
+class MultilingualAttribute(AttributeDescriptor):
+ """When a MultilingualAttribute is accessed, you get the translation
+ corresponding to the currently selected language.
+ """
+ def __init__(self, **translations):
+ self.trans = translations
+ def get_from_class(self, cls):
+ return self.trans[getattr(cls, "language", None) or
+ sys.modules[cls.__module__].language]
+ def get_from_obj(self, obj):
+ return self.trans[getattr(obj, "language", None) or
+ sys.modules[obj.__class__.__module__].language]
+
+
+language = "en"
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+class WebApplication(object):
+ error_msg = MultilingualAttribute(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ self.language = language or getattr(self.__class__, "language", None)
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
+app = WebApplication()
+assert app.show_page() == "You cannot access this page"
+
+app.language = "fr"
+assert app.show_page() == "Vous ne pouvez pas acceder cette page"
+
+app.language = "it"
+assert app.show_page() == "Questa pagina non e' accessibile"
+
+app.language = "en"
+assert app.show_page() == "You cannot access this page"
+
+
diff --git a/pypers/oxford/multilingualprop.py b/pypers/oxford/multilingualprop.py
new file mode 100755
index 0000000..9eb2a49
--- /dev/null
+++ b/pypers/oxford/multilingualprop.py
@@ -0,0 +1,30 @@
+# multilingualprop.py
+
+language = "en"
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+def multilingualProperty(**trans):
+ def get(self):
+ return trans[self.language]
+ def set(self, value):
+ trans[self.language] = value
+ return property(get, set)
+
+class WebApplication(object):
+ language = language
+ error_msg = multilingualProperty(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ if language: self.language = self.language
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
diff --git a/pypers/oxford/namedtuple.py b/pypers/oxford/namedtuple.py
new file mode 100755
index 0000000..aea1049
--- /dev/null
+++ b/pypers/oxford/namedtuple.py
@@ -0,0 +1,31 @@
+# use operator.itemgetter if we're in 2.4, roll our own if we're in 2.3
+try:
+ from operator import itemgetter
+except ImportError:
+ def itemgetter(i):
+ def getter(self): return self[i]
+ return getter
+def superTuple(typename, *attribute_names):
+ " create and return a subclass of `tuple', with named attributes "
+ # make the subclass with appropriate __new__ and __repr__ specials
+ nargs = len(attribute_names)
+ class supertup(tuple):
+ __slots__ = () # save memory, we don't need per-instance dict
+ def __new__(cls, *args):
+ if len(args) != nargs:
+ raise TypeError, \
+ '%s takes exactly %d arguments (%d given)' % (
+ typename, nargs, len(args))
+ return tuple.__new__(cls, args)
+ def __repr__(self):
+ return '%s(%s)' % (typename, ', '.join(map(repr, self)))
+ # add a few key touches to our new subclass of `tuple'
+ for index, attr_name in enumerate(attribute_names):
+ setattr(supertup, attr_name, property(itemgetter(index)))
+ supertup.__name__ = typename
+ return supertup
+
+Point = superTuple('Point', 'x', 'y')
+p = Point(1,2)
+print p
+print p.x,p.y
diff --git a/pypers/oxford/noconflict.py b/pypers/oxford/noconflict.py
new file mode 100755
index 0000000..6a42e77
--- /dev/null
+++ b/pypers/oxford/noconflict.py
@@ -0,0 +1,75 @@
+# noconflict.py
+
+import inspect, types, __builtin__
+from skip_redundant import skip_redundant
+
+memoized_metaclasses_map = {}
+
+# utility function
+def remove_redundant(metaclasses):
+ skipset = set([types.ClassType])
+ for meta in metaclasses: # determines the metaclasses to be skipped
+ skipset.update(inspect.getmro(meta)[1:])
+ return tuple(skip_redundant(metaclasses, skipset))
+
+##################################################################
+## now the core of the module: two mutually recursive functions ##
+##################################################################
+
+def get_noconflict_metaclass(bases, left_metas, right_metas):
+ """Not intended to be used outside of this module, unless you know
+ what you are doing."""
+ # make tuple of needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ needed_metas = remove_redundant(metas)
+
+ # return existing confict-solving meta, if any
+ if needed_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[needed_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ elif not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ # check for recursion, can happen i.e. for Zope ExtensionClasses
+ elif needed_metas == bases:
+ raise TypeError("Incompatible root metatypes", needed_metas)
+ else: # gotta work ...
+ metaname = '_' + ''.join([m.__name__ for m in needed_metas])
+ meta = classmaker()(metaname, needed_metas, {})
+ memoized_metaclasses_map[needed_metas] = meta
+ return meta
+
+def classmaker(left_metas=(), right_metas=()):
+ def make_class(name, bases, adict):
+ metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
+ return metaclass(name, bases, adict)
+ return make_class
+
+#################################################################
+## and now a conflict-safe replacement for 'type' ##
+#################################################################
+
+__type__=__builtin__.type # the aboriginal 'type'
+# left available in case you decide to rebind __builtin__.type
+
+class safetype(__type__):
+ # this is REALLY DEEP MAGIC
+ """Overrides the ``__new__`` method of the ``type`` metaclass, making the
+ generation of classes conflict-proof."""
+ def __new__(mcl, *args):
+ nargs = len(args)
+ if nargs == 1: # works as __builtin__.type
+ return __type__(args[0])
+ elif nargs == 3: # creates the class using the appropriate metaclass
+ n, b, d = args # name, bases and dictionary
+ meta = get_noconflict_metaclass(b, (mcl,), ())
+ if meta is mcl: # meta is trivial, dispatch to the default __new__
+ return super(safetype, mcl).__new__(mcl, n, b, d)
+ else: # non-trivial metaclass, dispatch to the right __new__
+ # (it will take a second round) # print mcl, meta
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
+
+
diff --git a/pypers/oxford/non_cooperative.py b/pypers/oxford/non_cooperative.py
new file mode 100755
index 0000000..6c7b293
--- /dev/null
+++ b/pypers/oxford/non_cooperative.py
@@ -0,0 +1,13 @@
+# non_cooperative.py
+
+class B1(object):
+ def __init__(self, **kw):
+ print "B1.__init__"
+ super(B1, self).__init__(**kw)
+
+class B2(object):
+ def __init__(self, **kw):
+ print "B2.__init__"
+ super(B2, self).__init__(**kw)
+
+
diff --git a/pypers/oxford/not_cooperative.txt b/pypers/oxford/not_cooperative.txt
new file mode 100755
index 0000000..9017713
--- /dev/null
+++ b/pypers/oxford/not_cooperative.txt
@@ -0,0 +1,120 @@
+
+Background: I am preparing my lecture for the Oxford ACCU conference
+and I wanted to show how __getattribute__ can be used to trace
+attribute access. Then I discovered that composing the TracedAttribute
+mixin with a built-in type does not work reliabily, where it works
+perfectly fine with custom classes.
+
+Methods of builtin types are usually cooperative.
+Consider for instance this example:
+
+#<cooperative.py>
+
+class B1(object):
+ def __init__(self, **kw):
+ print "B1.__init__"
+ super(B1, self).__init__(**kw)
+
+class B2(object):
+ def __init__(self, **kw):
+ print "B2.__init__"
+ super(B2, self).__init__(**kw)
+
+#</cooperative.py>
+
+>>> from cooperative import B1, B2
+>>> class C(B1, B2, str):
+... pass
+>>> c = C()
+B1.__init__
+B2.__init__
+
+str.__init__, B1.__init__ and B2.__init__ are cooperative, as expected.
+Moreover,
+
+>>> class C(B1, str, B2):
+... pass
+>>> c = C()
+B1.__init__
+B2.__init__
+
+and
+
+>>> class C(str, B1, B2):
+... pass
+>>> c = C()
+B1.__init__
+B2.__init__
+
+also work as expected.
+
+However str.__getattribute__ (the same for int.__getattribute__)
+is only apparently cooperative with B1.__getattribute__ and
+B2.__getattribute__. Consider this other example:
+
+#<trace_builtin.py>
+
+class TracedAccess1(object):
+ def __getattribute__(self, name):
+ print "1: accessing %s" % name
+ return super(TracedAccess1, self).__getattribute__(name)
+
+class TracedAccess2(object):
+ def __getattribute__(self, name):
+ print "2: accessing %s" % name
+ return super(TracedAccess2, self).__getattribute__(name)
+
+class B(object):
+ def __init__(self, *args):
+ super(B, self).__init__(*args)
+
+
+#</trace_builtin.py>
+
+This *seems* to work:
+
+>>> from trace_builtin import TracedAccess1, TracedAccess2
+>>> class C(TracedAccess1, TracedAccess2, str):
+... pass
+>>> cinit = C().__init__
+1: accessing __init__
+2: accessing __init__
+
+However, if I change the order of the bases, I get
+
+>>> class C(TracedAccess1, str, TracedAccess2):
+... pass
+>>> cinit = C().__init__
+1: accessing __init__
+
+and
+
+>>> class C(str, TracedAccess1, TracedAccess2):
+... pass
+>>> cinit = C().__init__
+
+so str.__getattribute__ (or int.__getattribute__) is not
+cooperative.
+
+There is no such a problem for a custom type, such as B:
+
+>>> from trace_builtin import B
+>>> class C(TracedAccess1, TracedAccess2, B):
+... pass
+>>> cinit = C().__init__
+1: accessing __init__
+2: accessing __init__
+
+>>> class C(TracedAccess1, B, TracedAccess2):
+... pass
+>>> cinit = C().__init__
+1: accessing __init__
+2: accessing __init__
+
+>>> class C(B, TracedAccess1, TracedAccess2):
+... pass
+>>> cinit = C().__init__
+1: accessing __init__
+2: accessing __init__
+
+Here, everything works fine. Can somebody share some light on this?
diff --git a/pypers/oxford/objects.html b/pypers/oxford/objects.html
new file mode 100755
index 0000000..644fb0a
--- /dev/null
+++ b/pypers/oxford/objects.html
@@ -0,0 +1,743 @@
+<?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.3.7: http://docutils.sourceforge.net/" />
+<title>Lecture 2: Objects (delegation &amp; inheritance)</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="lecture-2-objects-delegation-inheritance">
+<h1 class="title">Lecture 2: Objects (delegation &amp; inheritance)</h1>
+<div class="section" id="part-i-delegation">
+<h1><a name="part-i-delegation">Part I: delegation</a></h1>
+<p>Understanding how attribute access works: internal delegation via <em>descriptors</em></p>
+<div class="section" id="accessing-simple-attributes">
+<h2><a name="accessing-simple-attributes">Accessing simple attributes</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... a = 2
+... def __init__(self, x):
+... self.x = x
+...
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; c = C(1)
+&gt;&gt;&gt; c.x
+1
+&gt;&gt;&gt; c.a
+2
+</pre>
+<p>We are retrieving</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.__dict__[&quot;x&quot;]
+1
+</pre>
+<p>If there is nothing in c.__dict__, Python looks at C.__dict__:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print c.__dict__.get(&quot;a&quot;)
+None
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__[&quot;a&quot;]
+2
+</pre>
+<p>If there is nothing in C.__dict__, Python looks at the superclasses according
+to the MRO (see part II).</p>
+</div>
+<div class="section" id="accessing-methods">
+<h2><a name="accessing-methods">Accessing methods</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.__init__ #doctest: +ELLIPSIS
+&lt;bound method C.__init__ of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<p>since __init__ is not in c.__dict__ Python looks in the class dictionary
+and finds</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__[&quot;__init__&quot;] #doctest: +ELLIPSIS
+&lt;function __init__ at 0x...&gt;
+</pre>
+<p>Then it magically converts the function into a method bound to the instance
+&quot;c&quot;.</p>
+<p>NOTE: this mechanism works for new-style classes only.</p>
+<p>The old-style mechanism is less consistent and the attribute lookup of special
+methods is special: (*)</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): pass
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.__str__ = lambda : &quot;hello!&quot;
+&gt;&gt;&gt; print c #doctest: +ELLIPSIS
+&lt;__main__.C object at ...&gt;
+</pre>
+<p>whereas for old-style classes</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C: pass
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; c.__str__ = lambda : &quot;hello!&quot;
+&gt;&gt;&gt; print c
+hello!
+</pre>
+<p>the special method is looked for in the instance dictionary too.</p>
+<p>(*) modulo a very subtle difference for __getattr__-delegated special methods,
+see later.</p>
+</div>
+<div class="section" id="converting-functions-into-methods">
+<h2><a name="converting-functions-into-methods">Converting functions into methods</a></h2>
+<p>It is possible to convert a function into a bound or unbound method
+by invoking the <tt class="docutils literal"><span class="pre">__get__</span></tt> special method:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): pass
+&gt;&gt;&gt; f.__get__ #doctest: +ELLIPSIS
+&lt;method-wrapper object at 0x...&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): pass
+...
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(self): pass
+...
+&gt;&gt;&gt; f.__get__(C(), C) #doctest: +ELLIPSIS
+&lt;bound method C.f of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__(None, C)
+&lt;unbound method C.f&gt;
+</pre>
+<p>Functions are the simplest example of <em>descriptors</em>.</p>
+<p>Access to methods works since internally Python transforms</p>
+<blockquote>
+<tt class="docutils literal"><span class="pre">c.__init__</span> <span class="pre">-&gt;</span> <span class="pre">type(c).__dict__['__init__'].__get__(c,</span> <span class="pre">type(c))</span></tt></blockquote>
+<p>Note: not <em>all</em> functions are descriptors:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from operator import add
+&gt;&gt;&gt; add.__get__
+Traceback (most recent call last):
+ ...
+AttributeError: 'builtin_function_or_method' object has no attribute '__get__'
+</pre>
+</div>
+<div class="section" id="hack-a-very-slick-adder">
+<h2><a name="hack-a-very-slick-adder">Hack: a very slick adder</a></h2>
+<p>The descriptor protocol can be (ab)used as a way to avoid the late binding
+issue in for loops:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def add(x,y):
+... return x + y
+&gt;&gt;&gt; closures = [add.__get__(i) for i in range(10)]
+&gt;&gt;&gt; closures[5](1000)
+1005
+</pre>
+<p>Notice: operator.add will not work.</p>
+</div>
+<div class="section" id="descriptor-protocol">
+<h2><a name="descriptor-protocol">Descriptor Protocol</a></h2>
+<p>Everything at <a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">http://users.rcn.com/python/download/Descriptor.htm</a></p>
+<p>Formally:</p>
+<pre class="literal-block">
+descr.__get__(self, obj, type=None) --&gt; value
+descr.__set__(self, obj, value) --&gt; None
+descr.__delete__(self, obj) --&gt; None
+</pre>
+<p>Examples of custom descriptors:</p>
+<pre class="literal-block">
+#&lt;descriptor.py&gt;
+
+
+class AttributeDescriptor(object):
+ def __get__(self, obj, cls=None):
+ if obj is None and cls is None:
+ raise TypeError(&quot;__get__(None, None) is invalid&quot;)
+ elif obj is None:
+ return self.get_from_class(cls)
+ else:
+ return self.get_from_obj(obj)
+ def get_from_class(self, cls):
+ print &quot;Getting %s from %s&quot; % (self, cls)
+ def get_from_obj(self, obj):
+ print &quot;Getting %s from %s&quot; % (self, obj)
+
+
+class Staticmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func
+ get_from_obj = get_from_class
+
+
+class Classmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func.__get__(cls, type(cls))
+ def get_from_obj(self, obj):
+ return self.get_from_class(obj.__class__)
+
+class C(object):
+ s = Staticmethod(lambda : 1)
+ c = Classmethod(lambda cls : cls.__name__)
+
+c = C()
+
+assert C.s() == c.s() == 1
+assert C.c() == c.c() == &quot;C&quot;
+
+#&lt;/descriptor.py&gt;
+</pre>
+</div>
+<div class="section" id="multilingual-attribute">
+<h2><a name="multilingual-attribute">Multilingual attribute</a></h2>
+<p>Inspirated by a question in the Italian Newsgroup:</p>
+<pre class="literal-block">
+#&lt;multilingual.py&gt;
+
+import sys
+from descriptor import AttributeDescriptor
+
+class MultilingualAttribute(AttributeDescriptor):
+ &quot;&quot;&quot;When a MultilingualAttribute is accessed, you get the translation
+ corresponding to the currently selected language.
+ &quot;&quot;&quot;
+ def __init__(self, **translations):
+ self.trans = translations
+ def get_from_class(self, cls):
+ return self.trans[getattr(cls, &quot;language&quot;, None) or
+ sys.modules[cls.__module__].language]
+ def get_from_obj(self, obj):
+ return self.trans[getattr(obj, &quot;language&quot;, None) or
+ sys.modules[obj.__class__.__module__].language]
+
+
+language = &quot;en&quot;
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+class WebApplication(object):
+ error_msg = MultilingualAttribute(
+ en=&quot;You cannot access this page&quot;,
+ it=&quot;Questa pagina non e' accessibile&quot;,
+ fr=&quot;Vous ne pouvez pas acceder cette page&quot;,)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ self.language = language or getattr(self.__class__, &quot;language&quot;, None)
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
+app = WebApplication()
+assert app.show_page() == &quot;You cannot access this page&quot;
+
+app.language = &quot;fr&quot;
+assert app.show_page() == &quot;Vous ne pouvez pas acceder cette page&quot;
+
+app.language = &quot;it&quot;
+assert app.show_page() == &quot;Questa pagina non e' accessibile&quot;
+
+app.language = &quot;en&quot;
+assert app.show_page() == &quot;You cannot access this page&quot;
+
+#&lt;/multilingual.py&gt;
+</pre>
+<p>The same can be done with properties:</p>
+<pre class="literal-block">
+#&lt;multilingualprop.py&gt;
+
+language = &quot;en&quot;
+
+# a dummy User class
+class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+def multilingualProperty(**trans):
+ def get(self):
+ return trans[self.language]
+ def set(self, value):
+ trans[self.language] = value
+ return property(get, set)
+
+class WebApplication(object):
+ language = language
+ error_msg = multilingualProperty(
+ en=&quot;You cannot access this page&quot;,
+ it=&quot;Questa pagina non e' accessibile&quot;,
+ fr=&quot;Vous ne pouvez pas acceder cette page&quot;,)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ if language: self.language = self.language
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+#&lt;/multilingualprop.py&gt;
+</pre>
+<p>This also gives the possibility to set the error messages.</p>
+<p>The difference with the descriptor approach</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from multilingual import WebApplication
+&gt;&gt;&gt; app = WebApplication()
+&gt;&gt;&gt; print app.error_msg
+You cannot access this page
+&gt;&gt;&gt; print WebApplication.error_msg
+You cannot access this page
+</pre>
+<p>is that with properties there is no nice access from the class:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from multilingualprop import WebApplication
+&gt;&gt;&gt; WebApplication.error_msg #doctest: +ELLIPSIS
+&lt;property object at ...&gt;
+</pre>
+</div>
+<div class="section" id="another-use-case-for-properties-storing-users">
+<h2><a name="another-use-case-for-properties-storing-users">Another use case for properties: storing users</a></h2>
+<p>Consider a library providing a simple User class:</p>
+<pre class="literal-block">
+#&lt;crypt_user.py&gt;
+
+class User(object):
+ def __init__(self, username, password):
+ self.username, self.password = username, password
+
+#&lt;/crypt_user.py&gt;
+</pre>
+<p>The User objects are stored in a database as they are.
+For security purpose, in a second version of the library it is
+decided to crypt the password, so that only crypted passwords
+are stored in the database. With properties, it is possible to
+implement this functionality without changing the source code for
+the User class:</p>
+<pre class="literal-block">
+#&lt;crypt_user.py&gt;
+
+from crypt import crypt
+
+def cryptedAttribute(seed=&quot;x&quot;):
+ def get(self):
+ return getattr(self, &quot;_pw&quot;, None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+User.password = cryptedAttribute()
+</pre>
+<p>#&lt;/crypt_user.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from crypt_user import User
+&gt;&gt;&gt; u = User(&quot;michele&quot;, &quot;secret&quot;)
+&gt;&gt;&gt; print u.password
+xxZREZpkHZpkI
+</pre>
+<p>Notice the property factory approach used here.</p>
+</div>
+<div class="section" id="low-level-delegation-via-getattribute">
+<h2><a name="low-level-delegation-via-getattribute">Low-level delegation via __getattribute__</a></h2>
+<p>Attribute access is managed by the__getattribute__ special method:</p>
+<pre class="literal-block">
+#&lt;tracedaccess.py&gt;
+
+class TracedAccess(object):
+ def __getattribute__(self, name):
+ print &quot;Accessing %s&quot; % name
+ return object.__getattribute__(self, name)
+
+
+class C(TracedAccess):
+ s = staticmethod(lambda : 'staticmethod')
+ c = classmethod(lambda cls: 'classmethod')
+ m = lambda self: 'method'
+ a = &quot;hello&quot;
+
+#&lt;/tracedaccess.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracedaccess import C
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; print c.s()
+Accessing s
+staticmethod
+&gt;&gt;&gt; print c.c()
+Accessing c
+classmethod
+&gt;&gt;&gt; print c.m()
+Accessing m
+method
+&gt;&gt;&gt; print c.a
+Accessing a
+hello
+&gt;&gt;&gt; print c.__init__ #doctest: +ELLIPSIS
+Accessing __init__
+&lt;method-wrapper object at 0x...&gt;
+&gt;&gt;&gt; try: c.x
+... except AttributeError, e: print e
+...
+Accessing x
+'C' object has no attribute 'x'
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.y = 'y'
+&gt;&gt;&gt; c.y
+Accessing y
+'y'
+</pre>
+<p>You are probably familiar with <tt class="docutils literal"><span class="pre">__getattr__</span></tt> which is similar
+to <tt class="docutils literal"><span class="pre">__getattribute__</span></tt>, but it is called <em>only for missing attributes</em>.</p>
+</div>
+<div class="section" id="traditional-delegation-via-getattr">
+<h2><a name="traditional-delegation-via-getattr">Traditional delegation via __getattr__</a></h2>
+<p>Realistic use case in &quot;object publishing&quot;:</p>
+<pre class="literal-block">
+#&lt;webapp.py&gt;
+
+class WebApplication(object):
+ def __getattr__(self, name):
+ return name.capitalize()
+
+
+app = WebApplication()
+
+assert app.page1 == 'Page1'
+assert app.page2 == 'Page2'
+
+#&lt;/webapp.py&gt;
+</pre>
+<p>Here is another use case in HTML generation:</p>
+<pre class="literal-block">
+#&lt;XMLtag.py&gt;
+
+def makeattr(dict_or_list_of_pairs):
+ dic = dict(dict_or_list_of_pairs)
+ return &quot; &quot;.join('%s=&quot;%s&quot;' % (k, dic[k]) for k in dic) # simplistic
+
+class XMLTag(object):
+ def __getattr__(self, name):
+ def tag(value, **attr):
+ &quot;&quot;&quot;value can be a string or a sequence of strings.&quot;&quot;&quot;
+ if hasattr(value, &quot;__iter__&quot;): # is iterable
+ value = &quot; &quot;.join(value)
+ return &quot;&lt;%s %s&gt;%s&lt;/%s&gt;&quot; % (name, makeattr(attr), value, name)
+ return tag
+
+class XMLShortTag(object):
+ def __getattr__(self, name):
+ def tag(**attr):
+ return &quot;&lt;%s %s /&gt;&quot; % (name, makeattr(attr))
+ return tag
+
+tag = XMLTag()
+tg = XMLShortTag()
+
+#&lt;/XMLtag.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from XMLtag import tag, tg
+&gt;&gt;&gt; print tag.a(&quot;example.com&quot;, href=&quot;http://www.example.com&quot;)
+&lt;a href=&quot;http://www.example.com&quot;&gt;example.com&lt;/a&gt;
+&gt;&gt;&gt; print tg.br(**{'class':&quot;br_style&quot;})
+&lt;br class=&quot;br_style&quot; /&gt;
+</pre>
+</div>
+<div class="section" id="keyword-dictionaries-with-getattr-setattr">
+<h2><a name="keyword-dictionaries-with-getattr-setattr">Keyword dictionaries with __getattr__/__setattr__</a></h2>
+<pre class="literal-block">
+#&lt;kwdict.py&gt;
+
+class kwdict(dict): # UserDict not dict, to make it to work with Zope
+ &quot;&quot;&quot;A typing shortcut used in place of a keyword dictionary.
+ It also has two useful 'fromfile' and 'fromstring' constructors
+ &quot;&quot;&quot;
+ def __getattr__(self, name):
+ return self[name]
+ def __setattr__(self, name, value):
+ self[name] = value
+
+#&lt;/kwdict.py&gt;
+</pre>
+<p>An now for a completely different solution:</p>
+<pre class="literal-block">
+#&lt;dictwrapper.py&gt;
+
+class DictWrapper(object):
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+#&lt;/dictwrapper.py&gt;
+</pre>
+</div>
+<div class="section" id="delegation-to-special-methods-caveat">
+<h2><a name="delegation-to-special-methods-caveat">Delegation to special methods caveat</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWrapper(object):
+... def __init__(self, ls):
+... self._list = ls
+... def __getattr__(self, name):
+... if name == &quot;__getitem__&quot;: # special method
+... return self._list.__getitem__
+... elif name == &quot;reverse&quot;: # regular method
+... return self._list.reverse
+... else:
+... raise AttributeError(&quot;%r is not defined&quot; % name)
+...
+&gt;&gt;&gt; lw = ListWrapper([0,1,2])
+&gt;&gt;&gt; print lw.x
+Traceback (most recent call last):
+ ...
+AttributeError: 'x' is not defined
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; lw.reverse()
+&gt;&gt;&gt; print lw.__getitem__(0)
+2
+&gt;&gt;&gt; print lw.__getitem__(1)
+1
+&gt;&gt;&gt; print lw.__getitem__(2)
+0
+&gt;&gt;&gt; print lw[0]
+Traceback (most recent call last):
+ ...
+TypeError: unindexable object
+</pre>
+</div>
+</div>
+<div class="section" id="part-ii-inheritance">
+<h1><a name="part-ii-inheritance">Part II: Inheritance</a></h1>
+<p>The major changes in inheritance from Python 2.1 to 2.2+ are:</p>
+<ol class="arabic simple">
+<li>you can subclass built-in types (as a consequence the constructor__new__
+has been exposed to the user, to help subclassing immutable types);</li>
+<li>the Method Resolution Order (MRO) has changed;</li>
+<li>now Python allows <em>cooperative method calls</em>, i.e. we have <em>super</em>.</li>
+</ol>
+<p>In principle, the last two changes are relevant only if you use multiple
+inheritance. If you use single inheritance only, you don't need <tt class="docutils literal"><span class="pre">super</span></tt>;
+you can just name the superclass or use tricks such as:</p>
+<pre class="literal-block">
+self.__class__.__base__.__init__(self, *args, **kw)
+</pre>
+<p>instead of:</p>
+<pre class="literal-block">
+super(CurrentClass, self).__init__(*args, **kw)
+</pre>
+<p>However, somebody else may want to use your class in a MI hierarchy,
+and you would make her life difficult if you don't use <tt class="docutils literal"><span class="pre">super</span></tt>:</p>
+<pre class="literal-block">
+#&lt;why_super.py&gt;
+
+class Base(object):
+ def __init__(self):
+ print &quot;B.__init__&quot;
+
+class MyClass(Base):
+ &quot;I do not cooperate with others&quot;
+ def __init__(self):
+ print &quot;MyClass.__init__&quot;
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+
+class Mixin(Base):
+ &quot;I am cooperative with others&quot;
+ def __init__(self):
+ print &quot;Mixin.__init__&quot;
+ super(Mixin, self).__init__()
+
+class HerClass(MyClass, Mixin):
+ &quot;I am cooperative too&quot;
+ def __init__(self):
+ print &quot;HerClass.__init__&quot;
+ super(HerClass, self).__init__()
+
+#&lt;/why_super.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from why_super import HerClass
+&gt;&gt;&gt; h = HerClass() # Mixin.__init__ is not called!
+HerClass.__init__
+MyClass.__init__
+B.__init__
+</pre>
+<p>So to be polite versus your future users you should use <tt class="docutils literal"><span class="pre">super</span></tt> always.
+This adds a cognitive burden even for people not using MI :-(</p>
+<p>Notice that there is no good comprehensive reference on <tt class="docutils literal"><span class="pre">super</span></tt> (yet)
+Your best bet is still <a class="reference" href="http://www.python.org/2.2.3/descrintro.html#cooperation">http://www.python.org/2.2.3/descrintro.html#cooperation</a></p>
+<p>The MRO instead is explained here: <a class="reference" href="http://www.python.org/2.3/mro.html">http://www.python.org/2.3/mro.html</a></p>
+<p>Notice that I DO NOT recommand Multiple Inheritance.</p>
+<p>More often than not you are better off using composition/delegation/wrapping,
+etc.</p>
+<p>See Zope 2 -&gt; Zope 3 experience.</p>
+<div class="section" id="elementary-introduction-to-the-most-sophisticated-descriptor-ever-super">
+<h2><a name="elementary-introduction-to-the-most-sophisticated-descriptor-ever-super">Elementary introduction to the most sophisticated descriptor ever: <em>super</em></a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... def __init__(self): print &quot;B.__init__&quot;
+...
+&gt;&gt;&gt; class C(B):
+... def __init__(self): print &quot;C.__init__&quot;
+...
+&gt;&gt;&gt; c = C()
+C.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">instance)</span></tt>, where <tt class="docutils literal"><span class="pre">instance</span></tt> is an instance of <tt class="docutils literal"><span class="pre">cls</span></tt> or of
+a subclass of <tt class="docutils literal"><span class="pre">cls</span></tt>, retrieves the right method in the MRO:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__ #doctest: +ELLIPSIS
+&lt;bound method C.__init__ of &lt;__main__.C object at 0x...&gt;&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__.im_func is B.__init__.im_func
+True
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, c).__init__()
+B.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">subclass)</span></tt> works for unbound methods:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).__init__
+&lt;unbound method C.__init__&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).__init__.im_func is B.__init__.im_func
+True
+&gt;&gt;&gt; super(C, C).__init__(c)
+B.__init__
+</pre>
+<p><tt class="docutils literal"><span class="pre">super(cls,</span> <span class="pre">subclass)</span></tt> is also necessary for classmethods and staticmethods.
+Properties and custom descriptorsw works too:</p>
+<pre class="literal-block">
+#&lt;super_ex.py&gt;
+
+from descriptor import AttributeDescriptor
+
+class B(object):
+ &#64;staticmethod
+ def sm(): return &quot;staticmethod&quot;
+
+ &#64;classmethod
+ def cm(cls): return cls.__name__
+
+ p = property()
+ a = AttributeDescriptor()
+
+class C(B): pass
+
+#&lt;/super_ex.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from super_ex import C
+</pre>
+<p>Staticmethod usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C).sm #doctest: +ELLIPSIS
+&lt;function sm at 0x...&gt;
+&gt;&gt;&gt; super(C, C).sm()
+'staticmethod'
+</pre>
+<p>Classmethod usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C, C()).cm
+&lt;bound method type.cm of &lt;class 'super_ex.C'&gt;&gt;
+&gt;&gt;&gt; super(C, C).cm() # C is automatically passed
+'C'
+</pre>
+<p>Property usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C, C).p #doctest: +ELLIPSIS
+&lt;property object at 0x...&gt;
+&gt;&gt;&gt; super(C, C).a #doctest: +ELLIPSIS
+Getting &lt;descriptor.AttributeDescriptor object at 0x...&gt; from &lt;class 'super_ex.C'&gt;
+</pre>
+<p><tt class="docutils literal"><span class="pre">super</span></tt> does not work with old-style classes, however you can use the
+following trick:</p>
+<pre class="literal-block">
+#&lt;super_old_new.py&gt;
+class O:
+ def __init__(self):
+ print &quot;O.__init__&quot;
+
+class N(O, object):
+ def __init__(self):
+ print &quot;N.__init__&quot;
+ super(N, self).__init__()
+</pre>
+<p>#&lt;/super_old_new.py&gt;</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from super_old_new import N
+&gt;&gt;&gt; new = N()
+N.__init__
+O.__init__
+</pre>
+<p>There are dozens of tricky points concerning <tt class="docutils literal"><span class="pre">super</span></tt>, be warned!</p>
+</div>
+<div class="section" id="subclassing-built-in-types-new-vs-init">
+<h2><a name="subclassing-built-in-types-new-vs-init">Subclassing built-in types; __new__ vs. __init__</a></h2>
+<pre class="literal-block">
+#&lt;point.py&gt;
+
+class NotWorkingPoint(tuple):
+ def __init__(self, x, y):
+ super(NotWorkingPoint, self).__init__((x,y))
+ self.x, self.y = x, y
+
+#&lt;/point.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from point import NotWorkingPoint
+&gt;&gt;&gt; p = NotWorkingPoint(2,3)
+Traceback (most recent call last):
+ ...
+TypeError: tuple() takes at most 1 argument (2 given)
+</pre>
+<blockquote>
+<p>#&lt;point.py&gt;</p>
+<dl class="docutils">
+<dt>class Point(tuple):</dt>
+<dd><dl class="first last docutils">
+<dt>def __new__(cls, x, y):</dt>
+<dd>return super(Point, cls).__new__(cls, (x,y))</dd>
+<dt>def __init__(self, x, y):</dt>
+<dd>super(Point, self).__init__((x, y))
+self.x, self.y = x, y</dd>
+</dl>
+</dd>
+</dl>
+<p>#&lt;/point.py&gt;</p>
+</blockquote>
+<p>Notice that__new__ is a staticmethod, not a classmethod, so one needs
+to pass the class explicitely.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from point import Point
+&gt;&gt;&gt; p = Point(2,3)
+&gt;&gt;&gt; print p, p.x, p.y
+(2, 3) 2 3
+</pre>
+</div>
+<div class="section" id="be-careful-when-using-new-with-mutable-types">
+<h2><a name="be-careful-when-using-new-with-mutable-types">Be careful when using __new__ with mutable types</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWithDefault(list):
+... def __new__(cls):
+... return super(ListWithDefault, cls).__new__(cls, [&quot;hello&quot;])
+...
+&gt;&gt;&gt; print ListWithDefault() # beware! NOT [&quot;hello&quot;]!
+[]
+</pre>
+<p>Reason: lists are re-initialized to empty lists in list.__init__!</p>
+<p>Instead</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ListWithDefault(list):
+... def __init__(self):
+... super(ListWithDefault, self).__init__([&quot;hello&quot;])
+...
+&gt;&gt;&gt; print ListWithDefault() # works!
+['hello']
+</pre>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/oxford/objects.txt b/pypers/oxford/objects.txt
new file mode 100755
index 0000000..d78e3a0
--- /dev/null
+++ b/pypers/oxford/objects.txt
@@ -0,0 +1,747 @@
+Lecture 2: Objects (delegation & inheritance)
+==============================================
+
+Part I: delegation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Understanding how attribute access works: internal delegation via *descriptors*
+
+Accessing simple attributes
+--------------------------------
+
+>>> class C(object):
+... a = 2
+... def __init__(self, x):
+... self.x = x
+...
+
+>>> c = C(1)
+>>> c.x
+1
+>>> c.a
+2
+
+We are retrieving
+
+>>> c.__dict__["x"]
+1
+
+If there is nothing in c.__dict__, Python looks at C.__dict__:
+
+>>> print c.__dict__.get("a")
+None
+
+>>> C.__dict__["a"]
+2
+
+If there is nothing in C.__dict__, Python looks at the superclasses according
+to the MRO (see part II).
+
+Accessing methods
+--------------------------------------------------------
+
+>>> c.__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>
+
+since __init__ is not in c.__dict__ Python looks in the class dictionary
+and finds
+
+>>> C.__dict__["__init__"] #doctest: +ELLIPSIS
+<function __init__ at 0x...>
+
+Then it magically converts the function into a method bound to the instance
+"c".
+
+NOTE: this mechanism works for new-style classes only.
+
+The old-style mechanism is less consistent and the attribute lookup of special
+methods is special: (*)
+
+>>> class C(object): pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c #doctest: +ELLIPSIS
+<__main__.C object at ...>
+
+whereas for old-style classes
+
+>>> class C: pass
+>>> c = C()
+>>> c.__str__ = lambda : "hello!"
+>>> print c
+hello!
+
+the special method is looked for in the instance dictionary too.
+
+(*) modulo a very subtle difference for __getattr__-delegated special methods,
+see later.
+
+Converting functions into methods
+-------------------------------------
+
+It is possible to convert a function into a bound or unbound method
+by invoking the ``__get__`` special method:
+
+>>> def f(x): pass
+>>> f.__get__ #doctest: +ELLIPSIS
+<method-wrapper object at 0x...>
+
+>>> class C(object): pass
+...
+
+>>> def f(self): pass
+...
+>>> f.__get__(C(), C) #doctest: +ELLIPSIS
+<bound method C.f of <__main__.C object at 0x...>>
+
+>>> f.__get__(None, C)
+<unbound method C.f>
+
+Functions are the simplest example of *descriptors*.
+
+Access to methods works since internally Python transforms
+
+ ``c.__init__ -> type(c).__dict__['__init__'].__get__(c, type(c))``
+
+
+Note: not *all* functions are descriptors:
+
+>>> from operator import add
+>>> add.__get__
+Traceback (most recent call last):
+ ...
+AttributeError: 'builtin_function_or_method' object has no attribute '__get__'
+
+Hack: a very slick adder
+-----------------------------
+
+The descriptor protocol can be (ab)used as a way to avoid the late binding
+issue in for loops:
+
+>>> def add(x,y):
+... return x + y
+>>> closures = [add.__get__(i) for i in range(10)]
+>>> closures[5](1000)
+1005
+
+Notice: operator.add will not work.
+
+Descriptor Protocol
+----------------------
+
+Everything at http://users.rcn.com/python/download/Descriptor.htm
+
+Formally::
+
+ descr.__get__(self, obj, type=None) --> value
+ descr.__set__(self, obj, value) --> None
+ descr.__delete__(self, obj) --> None
+
+Examples of custom descriptors::
+
+ #<descriptor.py>
+
+
+ class AttributeDescriptor(object):
+ def __get__(self, obj, cls=None):
+ if obj is None and cls is None:
+ raise TypeError("__get__(None, None) is invalid")
+ elif obj is None:
+ return self.get_from_class(cls)
+ else:
+ return self.get_from_obj(obj)
+ def get_from_class(self, cls):
+ print "Getting %s from %s" % (self, cls)
+ def get_from_obj(self, obj):
+ print "Getting %s from %s" % (self, obj)
+
+
+ class Staticmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func
+ get_from_obj = get_from_class
+
+
+ class Classmethod(AttributeDescriptor):
+ def __init__(self, func):
+ self.func = func
+ def get_from_class(self, cls):
+ return self.func.__get__(cls, type(cls))
+ def get_from_obj(self, obj):
+ return self.get_from_class(obj.__class__)
+
+ class C(object):
+ s = Staticmethod(lambda : 1)
+ c = Classmethod(lambda cls : cls.__name__)
+
+ c = C()
+
+ assert C.s() == c.s() == 1
+ assert C.c() == c.c() == "C"
+
+ #</descriptor.py>
+
+Multilingual attribute
+----------------------
+
+Inspirated by a question in the Italian Newsgroup::
+
+ #<multilingual.py>
+
+ import sys
+ from descriptor import AttributeDescriptor
+
+ class MultilingualAttribute(AttributeDescriptor):
+ """When a MultilingualAttribute is accessed, you get the translation
+ corresponding to the currently selected language.
+ """
+ def __init__(self, **translations):
+ self.trans = translations
+ def get_from_class(self, cls):
+ return self.trans[getattr(cls, "language", None) or
+ sys.modules[cls.__module__].language]
+ def get_from_obj(self, obj):
+ return self.trans[getattr(obj, "language", None) or
+ sys.modules[obj.__class__.__module__].language]
+
+
+ language = "en"
+
+ # a dummy User class
+ class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+ class WebApplication(object):
+ error_msg = MultilingualAttribute(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ self.language = language or getattr(self.__class__, "language", None)
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+
+ app = WebApplication()
+ assert app.show_page() == "You cannot access this page"
+
+ app.language = "fr"
+ assert app.show_page() == "Vous ne pouvez pas acceder cette page"
+
+ app.language = "it"
+ assert app.show_page() == "Questa pagina non e' accessibile"
+
+ app.language = "en"
+ assert app.show_page() == "You cannot access this page"
+
+ #</multilingual.py>
+
+The same can be done with properties::
+
+ #<multilingualprop.py>
+
+ language = "en"
+
+ # a dummy User class
+ class DefaultUser(object):
+ def has_permission(self):
+ return False
+
+ def multilingualProperty(**trans):
+ def get(self):
+ return trans[self.language]
+ def set(self, value):
+ trans[self.language] = value
+ return property(get, set)
+
+ class WebApplication(object):
+ language = language
+ error_msg = multilingualProperty(
+ en="You cannot access this page",
+ it="Questa pagina non e' accessibile",
+ fr="Vous ne pouvez pas acceder cette page",)
+ user = DefaultUser()
+ def __init__(self, language=None):
+ if language: self.language = self.language
+ def show_page(self):
+ if not self.user.has_permission():
+ return self.error_msg
+
+ #</multilingualprop.py>
+
+This also gives the possibility to set the error messages.
+
+The difference with the descriptor approach
+
+>>> from multilingual import WebApplication
+>>> app = WebApplication()
+>>> print app.error_msg
+You cannot access this page
+>>> print WebApplication.error_msg
+You cannot access this page
+
+is that with properties there is no nice access from the class:
+
+>>> from multilingualprop import WebApplication
+>>> WebApplication.error_msg #doctest: +ELLIPSIS
+<property object at ...>
+
+Another use case for properties: storing users
+------------------------------------------------------------
+
+Consider a library providing a simple User class::
+
+ #<crypt_user.py>
+
+ class User(object):
+ def __init__(self, username, password):
+ self.username, self.password = username, password
+
+ #</crypt_user.py>
+
+The User objects are stored in a database as they are.
+For security purpose, in a second version of the library it is
+decided to crypt the password, so that only crypted passwords
+are stored in the database. With properties, it is possible to
+implement this functionality without changing the source code for
+the User class::
+
+ #<crypt_user.py>
+
+ from crypt import crypt
+
+ def cryptedAttribute(seed="x"):
+ def get(self):
+ return getattr(self, "_pw", None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+ User.password = cryptedAttribute()
+
+#</crypt_user.py>
+
+>>> from crypt_user import User
+>>> u = User("michele", "secret")
+>>> print u.password
+xxZREZpkHZpkI
+
+Notice the property factory approach used here.
+
+Low-level delegation via __getattribute__
+------------------------------------------------------------------
+
+Attribute access is managed by the__getattribute__ special method::
+
+ #<tracedaccess.py>
+
+ class TracedAccess(object):
+ def __getattribute__(self, name):
+ print "Accessing %s" % name
+ return object.__getattribute__(self, name)
+
+
+ class C(TracedAccess):
+ s = staticmethod(lambda : 'staticmethod')
+ c = classmethod(lambda cls: 'classmethod')
+ m = lambda self: 'method'
+ a = "hello"
+
+ #</tracedaccess.py>
+
+>>> from tracedaccess import C
+>>> c = C()
+>>> print c.s()
+Accessing s
+staticmethod
+>>> print c.c()
+Accessing c
+classmethod
+>>> print c.m()
+Accessing m
+method
+>>> print c.a
+Accessing a
+hello
+>>> print c.__init__ #doctest: +ELLIPSIS
+Accessing __init__
+<method-wrapper object at 0x...>
+>>> try: c.x
+... except AttributeError, e: print e
+...
+Accessing x
+'C' object has no attribute 'x'
+
+>>> c.y = 'y'
+>>> c.y
+Accessing y
+'y'
+
+You are probably familiar with ``__getattr__`` which is similar
+to ``__getattribute__``, but it is called *only for missing attributes*.
+
+Traditional delegation via __getattr__
+--------------------------------------------------------
+
+Realistic use case in "object publishing"::
+
+ #<webapp.py>
+
+ class WebApplication(object):
+ def __getattr__(self, name):
+ return name.capitalize()
+
+
+ app = WebApplication()
+
+ assert app.page1 == 'Page1'
+ assert app.page2 == 'Page2'
+
+ #</webapp.py>
+
+Here is another use case in HTML generation::
+
+ #<XMLtag.py>
+
+ def makeattr(dict_or_list_of_pairs):
+ dic = dict(dict_or_list_of_pairs)
+ return " ".join('%s="%s"' % (k, dic[k]) for k in dic) # simplistic
+
+ class XMLTag(object):
+ def __getattr__(self, name):
+ def tag(value, **attr):
+ """value can be a string or a sequence of strings."""
+ if hasattr(value, "__iter__"): # is iterable
+ value = " ".join(value)
+ return "<%s %s>%s</%s>" % (name, makeattr(attr), value, name)
+ return tag
+
+ class XMLShortTag(object):
+ def __getattr__(self, name):
+ def tag(**attr):
+ return "<%s %s />" % (name, makeattr(attr))
+ return tag
+
+ tag = XMLTag()
+ tg = XMLShortTag()
+
+ #</XMLtag.py>
+
+>>> from XMLtag import tag, tg
+>>> print tag.a("example.com", href="http://www.example.com")
+<a href="http://www.example.com">example.com</a>
+>>> print tg.br(**{'class':"br_style"})
+<br class="br_style" />
+
+Keyword dictionaries with __getattr__/__setattr__
+---------------------------------------------------
+::
+
+ #<kwdict.py>
+
+ class kwdict(dict): # or UserDict, to make it to work with Zope
+ """A typing shortcut used in place of a keyword dictionary."""
+ def __getattr__(self, name):
+ return self[name]
+ def __setattr__(self, name, value):
+ self[name] = value
+
+ #</kwdict.py>
+
+And now for a completely different solution::
+
+ #<dictwrapper.py>
+
+ class DictWrapper(object):
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+ #</dictwrapper.py>
+
+
+Delegation to special methods caveat
+--------------------------------------
+
+>>> class ListWrapper(object):
+... def __init__(self, ls):
+... self._list = ls
+... def __getattr__(self, name):
+... if name == "__getitem__": # special method
+... return self._list.__getitem__
+... elif name == "reverse": # regular method
+... return self._list.reverse
+... else:
+... raise AttributeError("%r is not defined" % name)
+...
+>>> lw = ListWrapper([0,1,2])
+>>> print lw.x
+Traceback (most recent call last):
+ ...
+AttributeError: 'x' is not defined
+
+>>> lw.reverse()
+>>> print lw.__getitem__(0)
+2
+>>> print lw.__getitem__(1)
+1
+>>> print lw.__getitem__(2)
+0
+>>> print lw[0]
+Traceback (most recent call last):
+ ...
+TypeError: unindexable object
+
+
+Part II: Inheritance
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+The major changes in inheritance from Python 2.1 to 2.2+ are:
+
+1. you can subclass built-in types (as a consequence the constructor__new__
+ has been exposed to the user, to help subclassing immutable types);
+2. the Method Resolution Order (MRO) has changed;
+3. now Python allows *cooperative method calls*, i.e. we have *super*.
+
+Why you need to know about MI even if you do not use it
+-----------------------------------------------------------
+
+In principle, the last two changes are relevant only if you use multiple
+inheritance. If you use single inheritance only, you don't need ``super``:
+you can just name the superclass.
+However, somebody else may want to use your class in a MI hierarchy,
+and you would make her life difficult if you don't use ``super``.
+
+My SI hierarchy::
+
+ #<why_super.py>
+
+ class Base(object):
+ def __init__(self):
+ print "B.__init__"
+
+ class MyClass(Base):
+ "I do not cooperate with others"
+ def __init__(self):
+ print "MyClass.__init__"
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+ #</why_super.py>
+
+Her MI hierarchy::
+
+ #<why_super.py>
+
+ class Mixin(Base):
+ "I am cooperative with others"
+ def __init__(self):
+ print "Mixin.__init__"
+ super(Mixin, self).__init__()
+
+ class HerClass(MyClass, Mixin):
+ "I am supposed to be cooperative too"
+ def __init__(self):
+ print "HerClass.__init__"
+ super(HerClass, self).__init__()
+
+ #</why_super.py>
+
+>>> from why_super import HerClass
+>>> h = HerClass() # Mixin.__init__ is not called!
+HerClass.__init__
+MyClass.__init__
+B.__init__
+
+ ::
+
+ 4 object
+ |
+ 3 Base
+ / \
+ 1 MyClass 2 Mixin
+ \ /
+ 0 HerClass
+
+>>> [ancestor.__name__ for ancestor in HerClass.mro()]
+['HerClass', 'MyClass', 'Mixin', 'Base', 'object']
+
+In order to be polite versus your future users, you should use ``super``
+always. This adds a cognitive burden even for people not using MI :-(
+
+Notice that there is no good comprehensive reference on ``super`` (yet)
+Your best bet is still http://www.python.org/2.2.3/descrintro.html#cooperation
+
+The MRO instead is explained here: http://www.python.org/2.3/mro.html
+
+Notice that I DO NOT recommand Multiple Inheritance.
+
+More often than not you are better off using composition/delegation/wrapping,
+etc.
+
+See Zope 2 -> Zope 3 experience.
+
+A few details about ``super`` (not the whole truth)
+------------------------------------------------------------------------------
+
+>>> class B(object):
+... def __init__(self): print "B.__init__"
+...
+>>> class C(B):
+... def __init__(self): print "C.__init__"
+...
+>>> c = C()
+C.__init__
+
+``super(cls, instance)``, where ``instance`` is an instance of ``cls`` or of
+a subclass of ``cls``, retrieves the right method in the MRO:
+
+>>> super(C, c).__init__ #doctest: +ELLIPSIS
+<bound method C.__init__ of <__main__.C object at 0x...>>
+
+>>> super(C, c).__init__.im_func is B.__init__.im_func
+True
+
+>>> super(C, c).__init__()
+B.__init__
+
+``super(cls, subclass)`` works for unbound methods:
+
+>>> super(C, C).__init__
+<unbound method C.__init__>
+
+>>> super(C, C).__init__.im_func is B.__init__.im_func
+True
+>>> super(C, C).__init__(c)
+B.__init__
+
+``super(cls, subclass)`` is also necessary for classmethods and staticmethods.
+Properties and custom descriptorsw works too::
+
+ #<super_ex.py>
+
+ from descriptor import AttributeDescriptor
+
+ class B(object):
+ @staticmethod
+ def sm(): return "staticmethod"
+
+ @classmethod
+ def cm(cls): return cls.__name__
+
+ p = property()
+ a = AttributeDescriptor()
+
+ class C(B): pass
+
+ #</super_ex.py>
+
+>>> from super_ex import C
+
+Staticmethod usage:
+
+>>> super(C, C).sm #doctest: +ELLIPSIS
+<function sm at 0x...>
+>>> super(C, C).sm()
+'staticmethod'
+
+Classmethod usage:
+
+>>> super(C, C()).cm
+<bound method type.cm of <class 'super_ex.C'>>
+>>> super(C, C).cm() # C is automatically passed
+'C'
+
+Property usage:
+
+>>> print super(C, C).p #doctest: +ELLIPSIS
+<property object at 0x...>
+>>> super(C, C).a #doctest: +ELLIPSIS
+Getting <descriptor.AttributeDescriptor object at 0x...> from <class 'super_ex.C'>
+
+``super`` does not work with old-style classes, however you can use the
+following trick::
+
+ #<super_old_new.py>
+ class O:
+ def __init__(self):
+ print "O.__init__"
+
+ class N(O, object):
+ def __init__(self):
+ print "N.__init__"
+ super(N, self).__init__()
+
+ #</super_old_new.py>
+
+>>> from super_old_new import N
+>>> new = N()
+N.__init__
+O.__init__
+
+There are dozens of tricky points concerning ``super``, be warned!
+
+Subclassing built-in types; __new__ vs. __init__
+-----------------------------------------------------
+
+::
+
+ #<point.py>
+
+ class NotWorkingPoint(tuple):
+ def __init__(self, x, y):
+ super(NotWorkingPoint, self).__init__((x,y))
+ self.x, self.y = x, y
+
+ #</point.py>
+
+>>> from point import NotWorkingPoint
+>>> p = NotWorkingPoint(2,3)
+Traceback (most recent call last):
+ ...
+TypeError: tuple() takes at most 1 argument (2 given)
+
+::
+
+ #<point.py>
+
+ class Point(tuple):
+ def __new__(cls, x, y):
+ return super(Point, cls).__new__(cls, (x,y))
+ def __init__(self, x, y):
+ super(Point, self).__init__((x, y))
+ self.x, self.y = x, y
+
+ #</point.py>
+
+Notice that__new__ is a staticmethod, not a classmethod, so one needs
+to pass the class explicitely.
+
+>>> from point import Point
+>>> p = Point(2,3)
+>>> print p, p.x, p.y
+(2, 3) 2 3
+
+Be careful when using __new__ with mutable types
+------------------------------------------------
+
+>>> class ListWithDefault(list):
+... def __new__(cls):
+... return super(ListWithDefault, cls).__new__(cls, ["hello"])
+...
+>>> print ListWithDefault() # beware! NOT ["hello"]!
+[]
+
+Reason: lists are re-initialized to empty lists in list.__init__!
+
+Instead
+
+>>> class ListWithDefault(list):
+... def __init__(self):
+... super(ListWithDefault, self).__init__(["hello"])
+...
+>>> print ListWithDefault() # works!
+['hello']
diff --git a/pypers/oxford/other.txt b/pypers/oxford/other.txt
new file mode 100755
index 0000000..2b6d62e
--- /dev/null
+++ b/pypers/oxford/other.txt
@@ -0,0 +1,193 @@
+Mixin programming
+-----------------------------------
+
+::
+
+ #<interp.py>
+
+ import UserDict
+
+ class Chainmap(UserDict.DictMixin):
+ """Combine multiple mappings for sequential lookup. Raymond Hettinger,
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305268 """
+
+ def __init__(self, *maps):
+ self._maps = maps
+
+ def __getitem__(self, key):
+ for mapping in self._maps:
+ try:
+ return mapping[key]
+ except KeyError:
+ pass
+ raise KeyError(key)
+
+ #</interp.py>
+
+An application of chainmap
+---------------------------------
+::
+
+ #<interp.py>
+
+ import sys
+ from string import Template
+
+ def interp(text, repldic=None, safe_substitute=True):
+ caller = sys._getframe(1)
+ if repldic:
+ mapping = Chainmap(repldic, caller.f_locals, caller.f_globals)
+ else:
+ mapping = Chainmap(caller.f_locals, caller.f_globals)
+ t = Template(text)
+ if safe_substitute:
+ return t.safe_substitute(mapping)
+ else:
+ return t.substitute(mapping)
+
+ ## Example:
+
+ language="Python"
+
+ def printmsg():
+ opinion = "favorite"
+ print interp("My $opinion language is $language.")
+
+#</interp.py>
+
+>>> from interp import printmsg
+>>> printmsg()
+My favorite language is Python.
+
+
+Operator overloading
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/384122
+
+Thread "Puzzling OO design":
+
+http://groups-beta.google.com/group/comp.lang.python/browse_frm/thread/63fcfade7aae5d84/c43d73fabc73bd73#c43d73fabc73bd73
+
+
+Deferred
+-------------------------------------------
+
+
+
+
+
+
+
+
+
+
+
+Darker than you think: ``MagicSuper``
+------------------------------------------------
+
+#<magicsuper.py>
+
+"""MagicSuper: an example of metaclass recompiling the source code.
+This provides Python with a ``callsupermethod`` macro simplifying
+the cooperative call syntax.
+Examples:
+
+from magicsuper import object
+
+class B(object):
+ def __new__(cls, *args, **kw):
+ print "B.__new__"
+ return callsupermethod(cls)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ callsupermethod(*args, **kw)
+ @staticmethod
+ def sm():
+ print "B.sm"
+ @classmethod
+ def cm(cls):
+ print cls.__name__
+
+
+class C(B):
+ def __new__(cls, *args, **kw):
+ print args, kw
+ return callsupermethod(cls, *args, **kw)
+ @staticmethod
+ def sm():
+ callsupermethod()
+ @classmethod
+ def cm(cls):
+ callsupermethod()
+
+c = C()
+c.cm()
+c.sm()
+"""
+
+import inspect, textwrap
+
+class MagicSuper(type):
+ def __init__(cls, clsname, bases, dic):
+ clsmodule = __import__(cls.__module__)# assume cls is defined in source
+ for name, value in dic.iteritems():
+ # __new__ is seen as a function in the dic, so it has
+ # to be converted explicitly into a staticmethod;
+ # ordinary staticmethods don't type-dispatch on their
+ # first argument, so use 'super(cls, cls)' for them.
+ was_staticmethod = False
+ if isinstance(value, staticmethod):
+ value = value.__get__("dummy") # convert to function
+ was_staticmethod = True
+ elif isinstance(value, classmethod):
+ value = value.__get__("dummy").im_func # convert to function
+ if inspect.isfunction(value):
+ if was_staticmethod:
+ first_arg = clsname # super first argument
+ else:
+ first_arg = inspect.getargspec(value)[0][0]
+ source = textwrap.dedent(inspect.getsource(value))
+ if not 'callsupermethod' in source: continue
+ source = source.replace(
+ 'callsupermethod', 'super(%s, %s).%s'
+ % (clsname, first_arg, name))
+ # print source # debug
+ exec source in clsmodule.__dict__, dic # modifies dic
+ if name == "__new__":
+ dic[name] = staticmethod(dic[name])
+ setattr(cls, name, dic[name])
+
+object = MagicSuper("object", (), {})
+
+# example:
+
+class B(object):
+ def __new__(cls, *args, **kw):
+ return callsupermethod(cls)
+ def __init__(self, *args, **kw):
+ print "B.__init__"
+ callsupermethod(*args, **kw)
+ @staticmethod
+ def sm():
+ print "B.sm"
+ @classmethod
+ def cm(cls):
+ print cls.__name__
+
+
+class C(B):
+ def __new__(cls, *args, **kw):
+ print args, kw
+ return callsupermethod(cls, *args, **kw)
+ @staticmethod
+ def sm():
+ callsupermethod()
+ @classmethod
+ def cm(cls):
+ callsupermethod()
+
+
+c = C(1, x=2)
+c.sm()
+c.cm()
+
+#</magicsuper.py>
diff --git a/pypers/oxford/paleo.py b/pypers/oxford/paleo.py
new file mode 100755
index 0000000..1df4559
--- /dev/null
+++ b/pypers/oxford/paleo.py
@@ -0,0 +1,20 @@
+# paleo.py
+
+class Homo(object):
+ def can(self):
+ print "<%s> can:" % self.__class__.__name__
+ for attr in dir(self):
+ if attr.endswith('__can'): print getattr(self, attr)
+
+class HomoHabilis(Homo):
+ __can = " - make tools"
+
+class HomoSapiens(HomoHabilis):
+ __can = " - make abstractions"
+
+class HomoSapiensSapiens(HomoSapiens):
+ __can = " - make art"
+
+modernman = HomoSapiensSapiens()
+
+
diff --git a/pypers/oxford/parens2indent.py b/pypers/oxford/parens2indent.py
new file mode 100755
index 0000000..2823836
--- /dev/null
+++ b/pypers/oxford/parens2indent.py
@@ -0,0 +1,30 @@
+# parens2indent.py
+"""A simple s-expression parser."""
+
+import re
+
+def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r"(?P<paren_beg>\()|(?P<paren_end>\))")
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == "paren_beg":
+ nesting_level += 1
+ elif match.lastgroup == "paren_end":
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+def parens2indent(sexpr):
+ for nesting, text in parse(sexpr.replace("\n", "")):
+ if text.strip(): print " "*nesting, text
+
+parens2indent( """\
+(html (head (title Example)) (body (h1 s-expr formatter example)
+(a (href http://www.example.com) A link)))""")
+
+
diff --git a/pypers/oxford/passwd.py b/pypers/oxford/passwd.py
new file mode 100755
index 0000000..c25a129
--- /dev/null
+++ b/pypers/oxford/passwd.py
@@ -0,0 +1,31 @@
+class User(object):
+ def __init__(self, un, pw):
+ self.un, self.pw = un, pw
+
+from crypt import crypt
+
+def cryptedAttribute(seed):
+ def get(self):
+ return getattr(self, "_pw", None)
+ def set(self, value):
+ self._pw = crypt(value, seed)
+ return property(get, set)
+
+class User(object):
+ pw = cryptedAttribute("a")
+ def __init__(self, un, pw):
+ self.un, self.pw = un, pw
+
+
+class SpecificUser(User):
+ pw = cryptedAttribute("b")
+
+
+
+u = User("michele", "secret")
+print u.un, u.pw
+
+su = SpecificUser("michele", "secret")
+print su.un, su.pw
+
+print su._pw
diff --git a/pypers/oxford/point.py b/pypers/oxford/point.py
new file mode 100755
index 0000000..4145b0d
--- /dev/null
+++ b/pypers/oxford/point.py
@@ -0,0 +1,17 @@
+# point.py
+
+class NotWorkingPoint(tuple):
+ def __init__(self, x, y):
+ super(NotWorkingPoint, self).__init__((x,y))
+ self.x, self.y = x, y
+
+
+
+class Point(tuple):
+ def __new__(cls, x, y):
+ return super(Point, cls).__new__(cls, (x,y))
+ def __init__(self, x, y):
+ super(Point, self).__init__((x, y))
+ self.x, self.y = x, y
+
+
diff --git a/pypers/oxford/pro.py b/pypers/oxford/pro.py
new file mode 100755
index 0000000..b8ee7fa
--- /dev/null
+++ b/pypers/oxford/pro.py
@@ -0,0 +1,5 @@
+class O(object):
+ def __init__(self):
+ pass
+
+O()
diff --git a/pypers/oxford/program.txt b/pypers/oxford/program.txt
new file mode 100755
index 0000000..1f5b36f
--- /dev/null
+++ b/pypers/oxford/program.txt
@@ -0,0 +1,45 @@
+*Wonders of Today's Python* -- presented by Michele Simionato
+
+*Everything you always wanted to know about Python but were afraid to ask!*
+
+In the last few years, with the advent of the new-style object model and
+of iterators, generators, decorators and more, Python has undergone a silent
+revolution. Many of the idioms, patterns and techniques of the past have
+been replaced by better solutions. You may feel that the language
+is evolving at too rapid a pace for you, or that your Python skills are
+becoming obsolete. In this case don't worry: this seminar is for you!
+
+For this lectures, I have picked some of the most interesting new
+paradigmas in modern Python, and I discuss them through examples big
+and small, together with a thorough grounding in the relevant language
+mechanisms.
+
+Look at the variety of design choices that today's Python makes
+available to you, and learn when you should use the advanced
+techniques presented here and when you should not.
+
+Iterators and Generators underlie Python's new approach to looping --
+it's not your grandparents' loop any more! Learn how to encapsulate
+the underlying logic of your control structures and make it
+reusable. See how itertools can turn the "abstraction penalty" typical
+of other languages into an abstraction _bonus_, making your code
+faster at the same time as more abstract and general.
+
+Learn about Design Patterns and other Object-Oriented idioms and
+mechanisms. Python is a multi-paradigm language, but OOP is its core
+paradigm. Understand the pros and cons of your alternatives: When
+should you use closures, rather than callable instances? When is
+inheritance OK, and when is it better to hold-and-delegate? What
+classical Design Patterns are built-in to Python, and which others are
+appropriate to consider, when?
+
+Descriptors and Metaclasses are the underpinnings of today's Python's
+OOP -- Python exposes them and lets you customize them for your own
+purposes. Add Decorators, the new syntax just introduced in Python 2.4
+(a systematic application of a crucial use case for higher-order
+functions), and you'll see why the working title of that chapter was
+"Black Magic"... Learn important use cases for each of these advanced
+mechanisms.
+
+Prerequisites: you need a solid grasp of Python fundamentals to start
+with. Course objectives: you'll walk out of this a Python wizard!
diff --git a/pypers/oxford/quixote_ex.py b/pypers/oxford/quixote_ex.py
new file mode 100755
index 0000000..56421f3
--- /dev/null
+++ b/pypers/oxford/quixote_ex.py
@@ -0,0 +1,15 @@
+from ms.quixote_utils_exp import Website, htmlpage
+
+@htmlpage()
+def _q_index():
+ yield "This is the main page."
+ yield "You can access <a href='apage'>apage</a> too."
+
+@htmlpage()
+def apage():
+ return "hello!"
+
+publisher = Website(_q_index, apage).publisher()
+
+if __name__ == "__main__":
+ publisher.run_show()
diff --git a/pypers/oxford/reiterable.py b/pypers/oxford/reiterable.py
new file mode 100755
index 0000000..74b1bb8
--- /dev/null
+++ b/pypers/oxford/reiterable.py
@@ -0,0 +1,10 @@
+# reiterable.py
+
+class ReIter(object):
+ "A re-iterable object."
+ def __iter__(self):
+ yield 1
+ yield 2
+ yield 3
+
+
diff --git a/pypers/oxford/s_parser.py b/pypers/oxford/s_parser.py
new file mode 100755
index 0000000..e83517f
--- /dev/null
+++ b/pypers/oxford/s_parser.py
@@ -0,0 +1,42 @@
+# s-expr parser
+
+sexpr = """\
+(html
+(head
+ (title HTML as s-expr example))
+(body
+ (h1 HTML as s-expr example)
+ (a (href http://www.example.com) A link))
+html)
+"""
+
+import re, inspect
+
+def second_arg(func):
+ args = inspect.getargspec(func)[0]
+ if len(args) >= 2: return args[1]
+
+class MetaParser(type):
+ def __init__(cls, name, bases, dic):
+ groups = []
+ for n, f in dic.iteritems():
+ if inspect.isfunction(f) and second_arg(f) == "pattern":
+ groups.append("(?P<%s>%s)" % (n, f.func_defaults[0]))
+ rx = re.compile("|".join(groups))
+
+class BaseParser(object):
+ __metaclass__ = MetaParser
+ def __init__(self, sexpr):
+ self.sexpr = sexpr
+
+class SexprParser(BaseParser):
+ def paren_beg(self, pattern=r"\("):
+ return
+ def paren_end(self, pattern=r"\)"):
+ return
+ def __iter__(self):
+ seek = 0
+ searcher = lambda : paren.search(self.text, seek)
+ while True:
+ pass
+
diff --git a/pypers/oxford/sect.py b/pypers/oxford/sect.py
new file mode 100755
index 0000000..7bc154f
--- /dev/null
+++ b/pypers/oxford/sect.py
@@ -0,0 +1,12 @@
+from walk import walk, pprint
+from operator import itemgetter
+from itertools import groupby
+
+nested_ls = [1,[2,[3,[[[4,5],6]]]],7]
+pprint(nested_ls)
+
+d = dict.fromkeys(range(6), 0)
+levels = (lvl for obj, lvl in walk(nested_ls))
+
+for lvl, grp in groupby(walk(nested_ls), key=itemgetter(1)):
+ print lvl, list(grp)
diff --git a/pypers/oxford/setter.py b/pypers/oxford/setter.py
new file mode 100755
index 0000000..10aea13
--- /dev/null
+++ b/pypers/oxford/setter.py
@@ -0,0 +1,17 @@
+import re
+
+class Regexp(object):
+ def __init__(self, pattern):
+ self._rx = re.compile(pattern)
+
+class Setter(object):
+ def __setattr__(self, name, value):
+ val = "(?P<%s>%s)" % (name, value)
+ super(Setter, self).__setattr__(name, val)
+
+s = Setter()
+
+s.paren_beg = r"\("
+s.paren_end = r"\)"
+
+print s.__dict__
diff --git a/pypers/oxford/sexpr2indent.py b/pypers/oxford/sexpr2indent.py
new file mode 100755
index 0000000..63cd2cf
--- /dev/null
+++ b/pypers/oxford/sexpr2indent.py
@@ -0,0 +1,26 @@
+# sexpr2indent.py
+"""A simple s-expression formatter."""
+
+import re
+
+def parse(sexpr):
+ position = 0
+ nesting_level = 0
+ paren = re.compile(r"(?P<paren_beg>\()|(?P<paren_end>\))")
+ while True:
+ match = paren.search(sexpr, position)
+ if match:
+ yield nesting_level, sexpr[position: match.start()]
+ if match.lastgroup == "paren_beg":
+ nesting_level += 1
+ elif match.lastgroup == "paren_end":
+ nesting_level -= 1
+ position = match.end()
+ else:
+ break
+
+def sexpr_indent(sexpr):
+ for nesting, text in parse(sexpr.replace("\n", "")):
+ if text.strip(): print " "*nesting, text
+
+
diff --git a/pypers/oxford/skip_redundant.py b/pypers/oxford/skip_redundant.py
new file mode 100755
index 0000000..22e966b
--- /dev/null
+++ b/pypers/oxford/skip_redundant.py
@@ -0,0 +1,11 @@
+# skip_redundant.py
+
+def skip_redundant(iterable, skipset=None):
+ "Redundant items are repeated items or items in the original skipset."
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+
diff --git a/pypers/oxford/skip_rendundant.py b/pypers/oxford/skip_rendundant.py
new file mode 100755
index 0000000..9c143d9
--- /dev/null
+++ b/pypers/oxford/skip_rendundant.py
@@ -0,0 +1,11 @@
+# skip_rendundant.py
+
+def skip_redundant(iterable, skipset=None):
+ "Redundant items are repeated items or items in the original skipset."
+ if skipset is None: skipset = set()
+ for item in iterable:
+ if item not in skipset:
+ skipset.add(item)
+ yield item
+
+
diff --git a/pypers/oxford/super.py b/pypers/oxford/super.py
new file mode 100755
index 0000000..5021a67
--- /dev/null
+++ b/pypers/oxford/super.py
@@ -0,0 +1,26 @@
+class B(object):
+ def __init__(self):
+ print "B.__init__"
+
+class C(B):
+ def __init__(self):
+ #super(C, self).__init__()
+ B.__init__(self)
+ print "C.__init__"
+
+######################################
+
+class herB(object):
+ pass
+
+def __init__(self):
+ super(self.__class__, self).__init__()
+ print "herB.__init__"
+
+herB.__init__ = __init__
+
+class herC(herB, C):
+ pass
+
+
+c = herC()
diff --git a/pypers/oxford/super2.py b/pypers/oxford/super2.py
new file mode 100755
index 0000000..65ca9d0
--- /dev/null
+++ b/pypers/oxford/super2.py
@@ -0,0 +1,18 @@
+from magicsuper import object
+
+class B(object):
+ def __init__(self):
+ print "B.__init__"
+ super(B, self).__init__()
+
+
+class C(B):
+ def __init__(self):
+ print "C.__init__"
+ callsupermethod()
+
+
+c = C()
+
+
+
diff --git a/pypers/oxford/super_ex.py b/pypers/oxford/super_ex.py
new file mode 100755
index 0000000..8a84fee
--- /dev/null
+++ b/pypers/oxford/super_ex.py
@@ -0,0 +1,17 @@
+# super_ex.py
+
+from descriptor import AttributeDescriptor
+
+class B(object):
+ @staticmethod
+ def sm(): return "staticmethod"
+
+ @classmethod
+ def cm(cls): return cls.__name__
+
+ p = property()
+ a = AttributeDescriptor()
+
+class C(B): pass
+
+
diff --git a/pypers/oxford/super_old_new.py b/pypers/oxford/super_old_new.py
new file mode 100755
index 0000000..f078ead
--- /dev/null
+++ b/pypers/oxford/super_old_new.py
@@ -0,0 +1,11 @@
+# super_old_new.py
+class O:
+ def __init__(self):
+ print "O.__init__"
+
+class N(O, object):
+ def __init__(self):
+ print "N.__init__"
+ super(N, self).__init__()
+
+
diff --git a/pypers/oxford/supermeth.py b/pypers/oxford/supermeth.py
new file mode 100755
index 0000000..a135a81
--- /dev/null
+++ b/pypers/oxford/supermeth.py
@@ -0,0 +1,40 @@
+def supermeth(meth): # not working with classmethods
+ name = meth.__name__
+ cls = meth.im_class
+ inst = meth.im_self
+ print "****", type(meth), cls, inst
+ return getattr(super(cls, inst or cls), name)
+
+from super_ex import B
+
+
+class C1(B):
+ pass
+
+class C2(B):
+ def __init__(self):
+ print "C2.__init__"
+
+class D(C1, C2):
+ def __init__(self):
+ print "D.__init__"
+
+
+
+class E(D):
+ def __init__(self):
+ print "E.__init__"
+ supermeth(E.__init__)(self)
+ @classmethod
+ def cm(cls):
+ print super(E, cls).cm()
+ return supermeth(E.cm)(cls)
+ @staticmethod
+ def sm():
+ print super(E, E).sm()
+ return supermeth(E.sm)()
+
+e = E()
+print
+print E.sm()
+
diff --git a/pypers/oxford/threaded.py b/pypers/oxford/threaded.py
new file mode 100755
index 0000000..4acf435
--- /dev/null
+++ b/pypers/oxford/threaded.py
@@ -0,0 +1,15 @@
+import threading
+from decorators import decorator
+
+def deferred(nsec):
+ def inner_deferred(func, *args, **kw):
+ return threading.Timer(nsec, func, args, kw).start()
+ return decorator(inner_deferred)
+
+
+@deferred(2)
+def hello():
+ print "hello"
+
+print "Calling hello ..."
+hello()
diff --git a/pypers/oxford/timed.py b/pypers/oxford/timed.py
new file mode 100755
index 0000000..8d435f8
--- /dev/null
+++ b/pypers/oxford/timed.py
@@ -0,0 +1,43 @@
+# timed.py
+
+import sys, time
+
+class Timed(object):
+ """Decorator factory: each decorator object wraps a function and
+ executes it many times (default 100 times).
+ The average time spent in one iteration, expressed in milliseconds,
+ is stored in the attributes wrappedfunc.time and wrappedfunc.clocktime,
+ and displayed into a log file which defaults to stdout.
+ """
+ def __init__(self, repeat=100, logfile=sys.stdout):
+ self.repeat = repeat
+ self.logfile = logfile
+ def __call__(self, func):
+ def wrappedfunc(*args, **kw):
+ fullname = "%s.%s ..." % (func.__module__, func.func_name)
+ print >> self.logfile, 'Executing %s' % fullname.ljust(30),
+ time1 = time.time()
+ clocktime1 = time.clock()
+ for i in xrange(self.repeat):
+ res = func(*args,**kw) # executes func self.repeat times
+ time2 = time.time()
+ clocktime2 = time.clock()
+ wrappedfunc.time = 1000*(time2-time1)/self.repeat
+ wrappedfunc.clocktime = 1000*(clocktime2 - clocktime1)/self.repeat
+ print >> self.logfile, \
+ 'Real time: %s ms;' % self.rounding(wrappedfunc.time),
+ print >> self.logfile, \
+ 'Clock time: %s ms' % self.rounding(wrappedfunc.clocktime)
+ return res
+ wrappedfunc.func_name = func.func_name
+ wrappedfunc.__module__ = func.__module__
+ return wrappedfunc
+ @staticmethod
+ def rounding(float_):
+ "Three digits rounding for small numbers, 1 digit rounding otherwise."
+ if float_ < 10.:
+ return "%5.3f" % float_
+ else:
+ return "%5.1f" % float_
+
+
diff --git a/pypers/oxford/trace_builtin.py b/pypers/oxford/trace_builtin.py
new file mode 100755
index 0000000..d5ebdd3
--- /dev/null
+++ b/pypers/oxford/trace_builtin.py
@@ -0,0 +1,18 @@
+# trace_builtin.py
+
+class TracedAccess1(object):
+ def __getattribute__(self, name):
+ print "1: accessing %s" % name
+ return super(TracedAccess1, self).__getattribute__(name)
+
+class TracedAccess2(object):
+ def __getattribute__(self, name):
+ print "2: accessing %s" % name
+ return super(TracedAccess2, self).__getattribute__(name)
+
+class B(object):
+ def __init__(self, *args):
+ super(B, self).__init__(*args)
+
+
+
diff --git a/pypers/oxford/traced.py b/pypers/oxford/traced.py
new file mode 100755
index 0000000..4a6367e
--- /dev/null
+++ b/pypers/oxford/traced.py
@@ -0,0 +1,13 @@
+# traced.py
+
+def traced(func):
+ def tracedfunc(*args, **kw):
+ print "calling %s.%s" % (func.__module__, func.__name__)
+ return func(*args, **kw)
+ tracedfunc.__name__ = func.__name__
+ return tracedfunc
+
+@traced
+def f(): pass
+
+
diff --git a/pypers/oxford/traced_function2.py b/pypers/oxford/traced_function2.py
new file mode 100755
index 0000000..5942934
--- /dev/null
+++ b/pypers/oxford/traced_function2.py
@@ -0,0 +1,19 @@
+# traced_function2.py
+
+from decorators import decorator
+
+def trace(f, *args, **kw):
+ print "calling %s with args %s, %s" % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+traced_function = decorator(trace)
+
+@traced_function
+def f1(x):
+ pass
+
+@traced_function
+def f2(x, y):
+ pass
+
+
diff --git a/pypers/oxford/tracedaccess.py b/pypers/oxford/tracedaccess.py
new file mode 100755
index 0000000..51b4af1
--- /dev/null
+++ b/pypers/oxford/tracedaccess.py
@@ -0,0 +1,15 @@
+# tracedaccess.py
+
+class TracedAccess(object):
+ def __getattribute__(self, name):
+ print "Accessing %s" % name
+ return object.__getattribute__(self, name)
+
+
+class C(TracedAccess):
+ s = staticmethod(lambda : 'staticmethod')
+ c = classmethod(lambda cls: 'classmethod')
+ m = lambda self: 'method'
+ a = "hello"
+
+
diff --git a/pypers/oxford/transl.py b/pypers/oxford/transl.py
new file mode 100755
index 0000000..9f764ea
--- /dev/null
+++ b/pypers/oxford/transl.py
@@ -0,0 +1,23 @@
+def attributoTraducibile(**dic):
+ def get(self):
+ return dic[self.lingua]
+ def set(self, traduzione):
+ dic[self.lingua]= traduzione
+ return property(get, set)
+
+class Oggetto(object):
+ definizione = attributoTraducibile(it="vaso", en="potter")
+ tipologia = attributoTraducibile(it="antico", en="ancient")
+
+o = Oggetto()
+o.lingua = "it"
+print o.definizione
+o.lingua = "en"
+print o.definizione
+
+o.lingua = "it"
+o.definizione = "Vaso"
+print o.definizione
+o.lingua = "en"
+o.definizione = "Potter"
+print o.definizione
diff --git a/pypers/oxford/walk.py b/pypers/oxford/walk.py
new file mode 100755
index 0000000..253e574
--- /dev/null
+++ b/pypers/oxford/walk.py
@@ -0,0 +1,18 @@
+# walk.py
+
+def walk(iterable, level=0):
+ for obj in iterable:
+ if not hasattr(obj, "__iter__"): # atomic object
+ yield obj, level
+ else: # composed object: iterate again
+ for subobj, lvl in walk(obj, level + 1):
+ yield subobj, lvl
+
+def flatten(iterable):
+ return (obj for obj, level in walk(iterable))
+
+def pprint(iterable):
+ for obj, level in walk(iterable):
+ print " "*level, obj
+
+
diff --git a/pypers/oxford/webapp.py b/pypers/oxford/webapp.py
new file mode 100755
index 0000000..d93293e
--- /dev/null
+++ b/pypers/oxford/webapp.py
@@ -0,0 +1,13 @@
+# webapp.py
+
+class WebApplication(object):
+ def __getattr__(self, name):
+ return name.capitalize()
+
+
+app = WebApplication()
+
+assert app.page1 == 'Page1'
+assert app.page2 == 'Page2'
+
+
diff --git a/pypers/oxford/why_super.py b/pypers/oxford/why_super.py
new file mode 100755
index 0000000..2bbbeba
--- /dev/null
+++ b/pypers/oxford/why_super.py
@@ -0,0 +1,27 @@
+# why_super.py
+
+class Base(object):
+ def __init__(self):
+ print "B.__init__"
+
+class MyClass(Base):
+ "I do not cooperate with others"
+ def __init__(self):
+ print "MyClass.__init__"
+ Base.__init__(self) #instead of super(MyClass, self).__init__()
+
+
+
+class Mixin(Base):
+ "I am cooperative with others"
+ def __init__(self):
+ print "Mixin.__init__"
+ super(Mixin, self).__init__()
+
+class HerClass(MyClass, Mixin):
+ "I am supposed to be cooperative too"
+ def __init__(self):
+ print "HerClass.__init__"
+ super(HerClass, self).__init__()
+
+
diff --git a/pypers/oxford/wraplist.py b/pypers/oxford/wraplist.py
new file mode 100755
index 0000000..dc52136
--- /dev/null
+++ b/pypers/oxford/wraplist.py
@@ -0,0 +1,20 @@
+class ListWrapper(object):
+ def __init__(self, ls):
+ self._list = ls
+ def __getattr__(self, name):
+ if name == "__getitem__":
+ return self._list.__getitem__
+ elif name == "reverse":
+ return self._list.reverse
+ else:
+ return name
+
+lw = ListWrapper([0,1,2])
+
+print lw.x
+
+lw.reverse()
+print lw.__getitem__(0)
+print lw.__getitem__(1)
+print lw.__getitem__(2)
+print lw[0]
diff --git a/pypers/oxford/x.py b/pypers/oxford/x.py
new file mode 100755
index 0000000..7c6e342
--- /dev/null
+++ b/pypers/oxford/x.py
@@ -0,0 +1,24 @@
+class C(object):
+ x = 1
+ #def __getattr__(self, name):
+ # print "Trying to access a non-existing attribute"
+ # return name
+
+ def __getattribute__(self, name):
+ print "Trying to accessing %s" % name
+ return name
+
+ @staticmethod
+ def f(): pass
+
+ def m(self): pass
+
+
+
+
+
+c = C()
+
+print str(c)
+
+print getattr(c, "x")
diff --git a/pypers/oxford/y.py b/pypers/oxford/y.py
new file mode 100755
index 0000000..d0b153b
--- /dev/null
+++ b/pypers/oxford/y.py
@@ -0,0 +1,11 @@
+class Point(tuple):
+ @staticmethod
+ def __new__(cls, x, y):
+ return tuple.__new__(cls, [x, y])
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+p = Point(1,2)
+
+print p.x, p.y
diff --git a/pypers/oxford/z.py b/pypers/oxford/z.py
new file mode 100755
index 0000000..41cde45
--- /dev/null
+++ b/pypers/oxford/z.py
@@ -0,0 +1,8 @@
+class MyStr(str):
+ def __new__(cls, arg):
+ return str.__new__(cls, 1)
+
+
+print repr(MyStr("world"))
+
+print type(MyStr("world"))