summaryrefslogtreecommitdiff
path: root/pypers
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2007-12-02 11:13:11 +0000
committermichele.simionato <devnull@localhost>2007-12-02 11:13:11 +0000
commit20ce686b0193d67ea56823a30551140f88b3aee1 (patch)
tree76015e7e4dc0b000bd857a2bdba6fb7976ac29a7 /pypers
parentf08f40335ad7f0ac961f25dabaaed34c4d4bcc44 (diff)
downloadmicheles-20ce686b0193d67ea56823a30551140f88b3aee1.tar.gz
Commited all py papers into Google code
Diffstat (limited to 'pypers')
-rwxr-xr-xpypers/EXECUTEME.py11
-rwxr-xr-xpypers/MI.txt1381
-rwxr-xr-xpypers/Makefile78
-rwxr-xr-xpypers/README.txt4
-rw-r--r--pypers/adwi/talk.html408
-rw-r--r--pypers/adwi/talk.txt99
-rw-r--r--pypers/adwi/ui/default/blank.gifbin0 -> 49 bytes
-rw-r--r--pypers/adwi/ui/default/slides.js558
-rwxr-xr-xpypers/all.html11002
-rwxr-xr-xpypers/all.tex12166
-rwxr-xr-xpypers/all.txt10528
-rwxr-xr-xpypers/app1.txt1
-rwxr-xr-xpypers/app2.txt668
-rwxr-xr-xpypers/bolzano/add_to.py17
-rwxr-xr-xpypers/bolzano/biblioteca.py27
-rwxr-xr-xpypers/bolzano/cgi-bin/box_radio.py30
-rwxr-xr-xpypers/bolzano/cgi-bin/example1.py60
-rwxr-xr-xpypers/bolzano/cgi-bin/textarea.py22
-rwxr-xr-xpypers/bolzano/db/BooksOnline.py49
-rwxr-xr-xpypers/bolzano/db/bookdb.py63
-rwxr-xr-xpypers/bolzano/db/books87.txt109
-rwxr-xr-xpypers/bolzano/db/design.txt13
-rwxr-xr-xpypers/bolzano/db/esempio.txt3
-rwxr-xr-xpypers/bolzano/db/esempio2.txt2
-rwxr-xr-xpypers/bolzano/db/iter_utils.py151
-rwxr-xr-xpypers/bolzano/db/mysql/cycle.py66
-rwxr-xr-xpypers/bolzano/db/mysql/ex_dec.py11
-rwxr-xr-xpypers/bolzano/db/mysql/ex_gen.py19
-rwxr-xr-xpypers/bolzano/db/mysql/insert_books.py38
-rwxr-xr-xpypers/bolzano/db/mysql/iter_utils.py151
-rwxr-xr-xpypers/bolzano/db/mysql/memoize.py21
-rwxr-xr-xpypers/bolzano/db/mysql/memoize_simple.py18
-rwxr-xr-xpypers/bolzano/db/mysql/mysite.py21
-rwxr-xr-xpypers/bolzano/db/mysql/prova.py9
-rwxr-xr-xpypers/bolzano/db/mysql/quixote_utils.py191
-rwxr-xr-xpypers/bolzano/db/mysql/quixote_utils24.py259
-rwxr-xr-xpypers/bolzano/db/mysql/registration.py55
-rwxr-xr-xpypers/bolzano/db/mysql/stat_books.py32
-rwxr-xr-xpypers/bolzano/db/mysql/stat_books_OO.py41
-rwxr-xr-xpypers/bolzano/db/mysql/ui/HTMLTable.py47
-rwxr-xr-xpypers/bolzano/db/mysql/ui/__init__.py0
-rwxr-xr-xpypers/bolzano/db/mysql/ui/cycle.py66
-rwxr-xr-xpypers/bolzano/db/mysql/ui/iter_utils.py151
-rwxr-xr-xpypers/bolzano/db/mysql/ui/quixote_utils.py191
-rwxr-xr-xpypers/bolzano/db/mysql/ui/table.html23
-rwxr-xr-xpypers/bolzano/db/mysql/ui/x.html104
-rwxr-xr-xpypers/bolzano/db/mysql/user_passwd_db.py36
-rwxr-xr-xpypers/bolzano/db/mysql/website.py59
-rwxr-xr-xpypers/bolzano/db/mysql/x.html2
-rwxr-xr-xpypers/bolzano/db/nomi.txt4
-rwxr-xr-xpypers/bolzano/db/populate_db.py4
-rwxr-xr-xpypers/bolzano/db/quixote_utils.py185
-rwxr-xr-xpypers/bolzano/db/readbooks.py36
-rwxr-xr-xpypers/bolzano/db/simpledb.py71
-rwxr-xr-xpypers/bolzano/db/sqlbooks.py30
-rwxr-xr-xpypers/bolzano/db/sqlreader.py10
-rwxr-xr-xpypers/bolzano/gui/canvas.py18
-rwxr-xr-xpypers/bolzano/gui/canvas2.py19
-rwxr-xr-xpypers/bolzano/gui/keys.py15
-rwxr-xr-xpypers/bolzano/gui/menus.py33
-rwxr-xr-xpypers/bolzano/gui/menus2.py23
-rwxr-xr-xpypers/bolzano/kirby_ex.py5
-rwxr-xr-xpypers/bolzano/links.html58
-rwxr-xr-xpypers/bolzano/links.tex176
-rwxr-xr-xpypers/bolzano/links.txt44
-rwxr-xr-xpypers/bolzano/player/LabelWithImages.py14
-rwxr-xr-xpypers/bolzano/player/animated_text1.py33
-rwxr-xr-xpypers/bolzano/player/animated_text2.py38
-rwxr-xr-xpypers/bolzano/player/animeplayer.py40
-rwxr-xr-xpypers/bolzano/player/bind_example.py15
-rwxr-xr-xpypers/bolzano/player/call_mpg123.py6
-rwxr-xr-xpypers/bolzano/player/change_spacing.py12
-rwxr-xr-xpypers/bolzano/player/clickable_label.py12
-rwxr-xr-xpypers/bolzano/player/cycle.py13
-rwxr-xr-xpypers/bolzano/player/global.py9
-rwxr-xr-xpypers/bolzano/player/kw.py6
-rwxr-xr-xpypers/bolzano/player/label.py29
-rwxr-xr-xpypers/bolzano/player/listbox.py37
-rwxr-xr-xpypers/bolzano/player/musicbox.py53
-rwxr-xr-xpypers/bolzano/player/scrollbar.py17
-rwxr-xr-xpypers/bolzano/player/subprocess.py1165
-rwxr-xr-xpypers/bolzano/web/biancheria.py51
-rwxr-xr-xpypers/bolzano/web/cgi-bin/esempio1.py1
-rwxr-xr-xpypers/bolzano/web/cgi-bin/ex_form.html10
-rwxr-xr-xpypers/bolzano/web/cgi-bin/ex_form.py15
-rwxr-xr-xpypers/bolzano/web/cgi-bin/hello.py9
-rwxr-xr-xpypers/bolzano/web/cgi-bin/hello_quixote.py25
-rwxr-xr-xpypers/bolzano/web/cgi-bin/save_phonenumber.py8
-rwxr-xr-xpypers/bolzano/web/for.py16
-rwxr-xr-xpypers/bolzano/web/monitor.py29
-rwxr-xr-xpypers/bolzano/web/monitor_fork.py17
-rwxr-xr-xpypers/bolzano/web/monitor_thread.py27
-rwxr-xr-xpypers/bolzano/web/prova.html1
-rwxr-xr-xpypers/bolzano/web/q_forms.py27
-rwxr-xr-xpypers/bolzano/web/quixote_utils.py189
-rwxr-xr-xpypers/bolzano/web/viewer.py28
-rwxr-xr-xpypers/bolzano/web/viewer2.py35
-rwxr-xr-xpypers/bolzano/webplayer.py67
-rwxr-xr-xpypers/bug.txt31
-rwxr-xr-xpypers/classcreation.html236
-rwxr-xr-xpypers/classcreation.txt280
-rwxr-xr-xpypers/classes.txt1093
-rw-r--r--pypers/classinitializer/Makefile11
-rw-r--r--pypers/classinitializer/_main.py64
-rw-r--r--pypers/classinitializer/classinitializer.html530
-rw-r--r--pypers/classinitializer/classinitializer.tex696
-rw-r--r--pypers/classinitializer/classinitializer.txt517
-rwxr-xr-xpypers/classinitializer/doctester.py73
-rwxr-xr-xpypers/codeproc.py11
-rwxr-xr-xpypers/descr.html1085
-rwxr-xr-xpypers/descr.txt973
-rwxr-xr-xpypers/doctest_talk/Makefile5
-rwxr-xr-xpypers/doctest_talk/P01.html115
-rwxr-xr-xpypers/doctest_talk/P02.html110
-rwxr-xr-xpypers/doctest_talk/P03.html105
-rwxr-xr-xpypers/doctest_talk/P04.html111
-rwxr-xr-xpypers/doctest_talk/P05.html110
-rwxr-xr-xpypers/doctest_talk/P06.html111
-rwxr-xr-xpypers/doctest_talk/P07.html110
-rwxr-xr-xpypers/doctest_talk/P08.html105
-rwxr-xr-xpypers/doctest_talk/P09.html117
-rwxr-xr-xpypers/doctest_talk/P10.html104
-rwxr-xr-xpypers/doctest_talk/P11.html118
-rwxr-xr-xpypers/doctest_talk/P12.html122
-rwxr-xr-xpypers/doctest_talk/P13.html114
-rwxr-xr-xpypers/doctest_talk/P14.html122
-rwxr-xr-xpypers/doctest_talk/P15.html116
-rwxr-xr-xpypers/doctest_talk/P16.html113
-rwxr-xr-xpypers/doctest_talk/P17.html121
-rwxr-xr-xpypers/doctest_talk/P18.html119
-rwxr-xr-xpypers/doctest_talk/P19.html110
-rwxr-xr-xpypers/doctest_talk/P20.html118
-rwxr-xr-xpypers/doctest_talk/P21.html122
-rwxr-xr-xpypers/doctest_talk/P22.html111
-rwxr-xr-xpypers/doctest_talk/P23.html120
-rwxr-xr-xpypers/doctest_talk/P24.html111
-rwxr-xr-xpypers/doctest_talk/P25.html118
-rwxr-xr-xpypers/doctest_talk/P26.html115
-rwxr-xr-xpypers/doctest_talk/P27.html113
-rwxr-xr-xpypers/doctest_talk/P28.html116
-rwxr-xr-xpypers/doctest_talk/P29.html116
-rwxr-xr-xpypers/doctest_talk/README.txt2
-rwxr-xr-xpypers/doctest_talk/__init__.py7
-rwxr-xr-xpypers/doctest_talk/abstract.txt9
-rwxr-xr-xpypers/doctest_talk/ex24.py17
-rwxr-xr-xpypers/doctest_talk/ex_inner.py16
-rwxr-xr-xpypers/doctest_talk/index.html16
-rwxr-xr-xpypers/doctest_talk/index.txt6
-rwxr-xr-xpypers/doctest_talk/maketalk.py179
-rwxr-xr-xpypers/doctest_talk/more.txt33
-rwxr-xr-xpypers/doctest_talk/refresh.txt0
-rwxr-xr-xpypers/doctest_talk/split-failure.txt5
-rwxr-xr-xpypers/doctest_talk/split-failure_txt.py8
-rwxr-xr-xpypers/doctest_talk/split.html32
-rwxr-xr-xpypers/doctest_talk/split.py16
-rwxr-xr-xpypers/doctest_talk/split.txt18
-rwxr-xr-xpypers/doctest_talk/talk.html362
-rwxr-xr-xpypers/doctest_talk/talk.txt445
-rwxr-xr-xpypers/doctest_talk/x.html203
-rwxr-xr-xpypers/doctest_talk/x.py19
-rwxr-xr-xpypers/doctest_talk/x.txt57
-rwxr-xr-xpypers/dot/MROgraph.py103
-rwxr-xr-xpypers/dot/UML.py42
-rwxr-xr-xpypers/dot/dot.html347
-rwxr-xr-xpypers/dot/dot.py21
-rwxr-xr-xpypers/dot/dot.tex460
-rwxr-xr-xpypers/dot/dot.txt351
-rwxr-xr-xpypers/dot/drawBaseFolder.py3
-rwxr-xr-xpypers/dot/drawMRO.txt263
-rwxr-xr-xpypers/dot/drawclasses.py102
-rwxr-xr-xpypers/dot/drawzopefolder.py4
-rwxr-xr-xpypers/dot/err.txt2
-rwxr-xr-xpypers/dot/matcher.py19
-rwxr-xr-xpypers/dot/oldstyle.txt53
-rwxr-xr-xpypers/dot/samegraph.py30
-rwxr-xr-xpypers/dot/style.tex20
-rwxr-xr-xpypers/dot/zip.sh8
-rwxr-xr-xpypers/erf.py21
-rwxr-xr-xpypers/europython05/Quixote-2.0/__init__.py26
-rwxr-xr-xpypers/europython05/Quixote-2.0/config.py175
-rwxr-xr-xpypers/europython05/Quixote-2.0/demo/__init__.py10
-rwxr-xr-xpypers/europython05/Quixote-2.0/demo/altdemo.py205
-rwxr-xr-xpypers/europython05/Quixote-2.0/demo/mini_demo.py39
-rwxr-xr-xpypers/europython05/Quixote-2.0/directory.py109
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/INSTALL.txt32
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/Makefile31
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/PTL.txt264
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/demo.txt221
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/form2conversion.txt358
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/multi-threaded.txt39
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/programming.txt157
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/session-mgmt.txt323
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/static-files.txt51
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/upgrading.txt324
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/web-server.txt258
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/web-services.txt169
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/widgets.txt524
-rwxr-xr-xpypers/europython05/Quixote-2.0/doc/win32.txt14
-rwxr-xr-xpypers/europython05/Quixote-2.0/errors.py141
-rwxr-xr-xpypers/europython05/Quixote-2.0/form/__init__.py18
-rwxr-xr-xpypers/europython05/Quixote-2.0/form/compatibility.py101
-rwxr-xr-xpypers/europython05/Quixote-2.0/form/css.py76
-rwxr-xr-xpypers/europython05/Quixote-2.0/form/form.py365
-rwxr-xr-xpypers/europython05/Quixote-2.0/form/widget.py951
-rwxr-xr-xpypers/europython05/Quixote-2.0/form1/__init__.py34
-rwxr-xr-xpypers/europython05/Quixote-2.0/form1/form.py534
-rwxr-xr-xpypers/europython05/Quixote-2.0/form1/widget.py842
-rwxr-xr-xpypers/europython05/Quixote-2.0/html/__init__.py106
-rwxr-xr-xpypers/europython05/Quixote-2.0/html/_c_htmltext.c1019
-rwxr-xr-xpypers/europython05/Quixote-2.0/html/_py_htmltext.py213
-rwxr-xr-xpypers/europython05/Quixote-2.0/html/test/utest_html.py368
-rwxr-xr-xpypers/europython05/Quixote-2.0/http_request.py759
-rwxr-xr-xpypers/europython05/Quixote-2.0/http_response.py475
-rwxr-xr-xpypers/europython05/Quixote-2.0/logger.py92
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/__init__.py245
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/cimport.c483
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/install.py2
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/ptl_compile.py314
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/ptl_import.py148
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/ptlrun.py5
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/qx_distutils.py47
-rwxr-xr-xpypers/europython05/Quixote-2.0/ptl/test/utest_ptl.py58
-rwxr-xr-xpypers/europython05/Quixote-2.0/publish.py336
-rwxr-xr-xpypers/europython05/Quixote-2.0/publish1.py270
-rwxr-xr-xpypers/europython05/Quixote-2.0/sendmail.py273
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/__init__.py5
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/_fcgi.py466
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/cgi_server.py27
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/fastcgi_server.py28
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/medusa_server.py116
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/mod_python_handler.py106
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/scgi_server.py84
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/simple_server.py93
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/twisted_server.py147
-rwxr-xr-xpypers/europython05/Quixote-2.0/server/util.py32
-rwxr-xr-xpypers/europython05/Quixote-2.0/session.py567
-rwxr-xr-xpypers/europython05/Quixote-2.0/setup.py65
-rwxr-xr-xpypers/europython05/Quixote-2.0/test/__init__.py2
-rwxr-xr-xpypers/europython05/Quixote-2.0/test/ua_test.py28
-rwxr-xr-xpypers/europython05/Quixote-2.0/test/utest_request.py43
-rwxr-xr-xpypers/europython05/Quixote-2.0/util.py390
-rwxr-xr-xpypers/europython05/easytwill.py4
-rwxr-xr-xpypers/europython05/fig.html15
-rwxr-xr-xpypers/europython05/fig.txt1
-rwxr-xr-xpypers/europython05/hello.py7
-rwxr-xr-xpypers/europython05/lightening_talk.py57
-rwxr-xr-xpypers/europython05/table.html44
-rwxr-xr-xpypers/europython05/table.tex115
-rwxr-xr-xpypers/europython05/table.txt14
-rwxr-xr-xpypers/europython05/taste-python.html47
-rwxr-xr-xpypers/europython05/taste-python.txt40
-rwxr-xr-xpypers/europython05/test1.py1
-rw-r--r--pypers/europython06/cherrypy_ex.py10
-rw-r--r--pypers/europython06/dt_eval.py11
-rw-r--r--pypers/europython06/ipython-ask.txt20
-rw-r--r--pypers/europython06/talk.html629
-rw-r--r--pypers/europython06/talk.txt288
-rw-r--r--pypers/europython06/transac.py26
-rw-r--r--pypers/europython06/ui/default/blank.gifbin0 -> 49 bytes
-rw-r--r--pypers/europython06/ui/default/slides.js558
-rw-r--r--pypers/europython07/Scotch_Whisky_(aka).pngbin0 -> 508421 bytes
-rw-r--r--pypers/europython07/abstract-en.txt12
-rw-r--r--pypers/europython07/bio.txt14
-rw-r--r--pypers/europython07/evalexception_ex.py11
-rw-r--r--pypers/europython07/hello.py10
-rw-r--r--pypers/europython07/objectpublisher.py43
-rw-r--r--pypers/europython07/simpleplotter.py25
-rw-r--r--pypers/europython07/talk.html669
-rw-r--r--pypers/europython07/talk.txt374
-rw-r--r--pypers/europython07/ui/default/blank.gifbin0 -> 49 bytes
-rw-r--r--pypers/europython07/ui/default/ex.html13
-rw-r--r--pypers/europython07/ui/default/slides.js558
-rw-r--r--pypers/europython07/ui/default/statpro_logo.gifbin0 -> 3220 bytes
-rw-r--r--pypers/europython07/webplotter.py37
-rw-r--r--pypers/europython07/what-to-see.txt39
-rwxr-xr-xpypers/final.py15
-rwxr-xr-xpypers/first.txt963
-rwxr-xr-xpypers/frozen.py11
-rwxr-xr-xpypers/functions.txt1142
-rwxr-xr-xpypers/last.txt3
-rwxr-xr-xpypers/magic.txt1039
-rwxr-xr-xpypers/marelli/corso_py.txt144
-rwxr-xr-xpypers/marelli/deleting.py4
-rwxr-xr-xpypers/marelli/ex_thread.py20
-rwxr-xr-xpypers/marelli/frontpage.html16
-rwxr-xr-xpypers/marelli/mail/corso-python.tex164
-rwxr-xr-xpypers/marelli/mail/corso-python.txt57
-rwxr-xr-xpypers/marelli/mail/corso-python2.txt55
-rwxr-xr-xpypers/marelli/mail/polizza.txt28
-rwxr-xr-xpypers/marelli/mail/preventivo.txt40
-rwxr-xr-xpypers/marelli/mail/programma.txt96
-rwxr-xr-xpypers/marelli/materiale/README.txt14
-rwxr-xr-xpypers/marelli/materiale/corso.html1850
-rwxr-xr-xpypers/marelli/materiale/corso.txt1851
-rwxr-xr-xpypers/marelli/materiale/del_with_exc.py10
-rwxr-xr-xpypers/marelli/materiale/doctest_runner.py8
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/Makefile5
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P01.html110
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P02.html106
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P03.html102
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P04.html108
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P05.html107
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P06.html108
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P07.html107
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P08.html102
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P09.html114
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P10.html101
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P11.html114
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P12.html119
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P13.html111
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P14.html118
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P15.html115
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P16.html118
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P17.html116
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P18.html110
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P19.html113
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P20.html108
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P21.html117
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P22.html108
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P23.html115
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P24.html112
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P25.html110
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/P26.html113
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/abstract.txt11
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/doct_pkg.py22
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/doctester_frontend.py44
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/doctester_frontend.txt23
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/ex24.py17
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/ex_inner.py16
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/index.html16
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/index.txt6
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/maketalk.py98
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/more.txt33
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/refresh.txt0
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/split-failure.txt5
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/split-failure_txt.py8
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/split.html32
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/split.py15
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/split.txt18
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/talk.txt403
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/test_pkg/__init__.py6
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/test_pkg/a.py7
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/test_pkg/b.py7
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/testfile_ex.py11
-rwxr-xr-xpypers/marelli/materiale/doctest_talk/the_story.txt23
-rwxr-xr-xpypers/marelli/materiale/doctester.py71
-rwxr-xr-xpypers/marelli/materiale/esempio1.py20
-rwxr-xr-xpypers/marelli/materiale/esempio_banale.py12
-rwxr-xr-xpypers/marelli/materiale/example.py4
-rwxr-xr-xpypers/marelli/materiale/exc_debug.py19
-rwxr-xr-xpypers/marelli/materiale/gentable.py17
-rwxr-xr-xpypers/marelli/materiale/getattr_ex.py14
-rwxr-xr-xpypers/marelli/materiale/isnumber.py12
-rwxr-xr-xpypers/marelli/materiale/main.py14
-rwxr-xr-xpypers/marelli/materiale/maketable.py67
-rwxr-xr-xpypers/marelli/materiale/proc1a.py13
-rwxr-xr-xpypers/marelli/materiale/proc1b.py20
-rwxr-xr-xpypers/marelli/materiale/proc2a.py17
-rwxr-xr-xpypers/marelli/materiale/proc2b.py15
-rwxr-xr-xpypers/marelli/materiale/test_exc.py20
-rwxr-xr-xpypers/marelli/materiale/test_isnumber.py43
-rwxr-xr-xpypers/marelli/materiale/twisted_main.py37
-rwxr-xr-xpypers/marelli/modulo1/debug_me.py9
-rwxr-xr-xpypers/marelli/modulo1/try_finally.py17
-rwxr-xr-xpypers/marelli/modulo1/x.txt7
-rwxr-xr-xpypers/marelli/modulo2/TestLauncher.py2902
-rwxr-xr-xpypers/marelli/modulo2/maketable.py60
-rwxr-xr-xpypers/marelli/modulo2/mutable_immutable.py12
-rwxr-xr-xpypers/marelli/modulo2/prova.py1
-rwxr-xr-xpypers/marelli/modulo2/questionario-in-sol.txt139
-rwxr-xr-xpypers/marelli/modulo2/questionario-iniziale.txt23
-rwxr-xr-xpypers/marelli/modulo2/sort_ci.py25
-rwxr-xr-xpypers/marelli/modulo3/config.py6
-rwxr-xr-xpypers/marelli/modulo3/disaccoppiamento.txt1
-rwxr-xr-xpypers/marelli/modulo3/launcher_with_exec.py5
-rwxr-xr-xpypers/marelli/modulo3/lineinterpreter.py55
-rwxr-xr-xpypers/marelli/modulo3/runsongs.py31
-rwxr-xr-xpypers/marelli/modulo3/script_with_error.py1
-rwxr-xr-xpypers/marelli/modulo3/tester.py119
-rwxr-xr-xpypers/marelli/modulo3/tester_server.py6
-rwxr-xr-xpypers/marelli/modulo3/tester_server0.py60
-rwxr-xr-xpypers/marelli/modulo4/_main.py9
-rwxr-xr-xpypers/marelli/modulo4/code2utest.py32
-rwxr-xr-xpypers/marelli/modulo4/ex1.txt15
-rwxr-xr-xpypers/marelli/modulo4/ex2.txt5
-rwxr-xr-xpypers/marelli/modulo4/iter2thread.py15
-rwxr-xr-xpypers/marelli/modulo4/multi_iter.py22
-rwxr-xr-xpypers/marelli/modulo4/remote_tester_client.py46
-rwxr-xr-xpypers/marelli/modulo4/remote_tester_server.py59
-rwxr-xr-xpypers/marelli/modulo4/server_utest_1.py6
-rwxr-xr-xpypers/marelli/modulo4/server_utest_2.py8
-rwxr-xr-xpypers/marelli/modulo4/test2utest.py27
-rwxr-xr-xpypers/marelli/modulo4/test_1.py5
-rwxr-xr-xpypers/marelli/modulo4/test_2.py10
-rwxr-xr-xpypers/marelli/modulo4/test_3.py12
-rwxr-xr-xpypers/marelli/modulo4/test_parent_child/child.py4
-rwxr-xr-xpypers/marelli/modulo4/test_parent_child/parent.py9
-rwxr-xr-xpypers/marelli/modulo4/test_parent_child/test_kill_parent.sh7
-rwxr-xr-xpypers/marelli/modulo4/threads.txt22
-rwxr-xr-xpypers/marelli/modulo4/threads_ex.py26
-rwxr-xr-xpypers/marelli/modulo4/threads_twisted.py36
-rwxr-xr-xpypers/marelli/modulo4/utest_1.py6
-rwxr-xr-xpypers/marelli/modulo4/utest_2.py8
-rwxr-xr-xpypers/marelli/modulo4/x.py3
-rwxr-xr-xpypers/marelli/modulo5/fix-server.py48
-rwxr-xr-xpypers/marelli/modulo5/protected.py30
-rwxr-xr-xpypers/marelli/modulo5/protected2.py11
-rwxr-xr-xpypers/marelli/modulo5/threads_vs_gen.py37
-rwxr-xr-xpypers/marelli/programma-svolto.html409
-rwxr-xr-xpypers/marelli/programma-svolto.txt141
-rwxr-xr-xpypers/marelli/questionario-fin.txt6
-rwxr-xr-xpypers/marelli/questionario-iniziale.html303
-rwxr-xr-xpypers/marelli/scaletta.txt55
-rwxr-xr-xpypers/meta.txt1024
-rwxr-xr-xpypers/meta/fig1.fig28
-rwxr-xr-xpypers/meta/fig2.fig27
-rwxr-xr-xpypers/meta/fig3.fig32
-rwxr-xr-xpypers/meta/fig3.txt9
-rwxr-xr-xpypers/meta/fig4.fig37
-rwxr-xr-xpypers/meta/fill.py4
-rwxr-xr-xpypers/meta/meta1.html399
-rwxr-xr-xpypers/meta/meta1.txt469
-rwxr-xr-xpypers/meta/meta2.html528
-rwxr-xr-xpypers/meta/meta2.txt498
-rwxr-xr-xpypers/meta/meta_threading.py36
-rwxr-xr-xpypers/meta/metadd.txt196
-rwxr-xr-xpypers/meta/metatype.html197
-rwxr-xr-xpypers/meta/metatype.txt188
-rwxr-xr-xpypers/meta/metatype2.txt199
-rwxr-xr-xpypers/meta/noconf.py78
-rwxr-xr-xpypers/meta/noconf2.py88
-rwxr-xr-xpypers/meta/noconflict.py33
-rwxr-xr-xpypers/meta/noconflict.txt66
-rwxr-xr-xpypers/meta/noconflict_alex.py84
-rwxr-xr-xpypers/meta/proposal2.txt49
-rwxr-xr-xpypers/meta/safetype.txt160
-rwxr-xr-xpypers/meta/testnoconflict.py242
-rwxr-xr-xpypers/meta/testsafetype.py192
-rwxr-xr-xpypers/mro/Makefile3
-rwxr-xr-xpypers/mro/mettiinrete.py19
-rwxr-xr-xpypers/mro/mro.html794
-rwxr-xr-xpypers/mro/mro.py80
-rwxr-xr-xpypers/mro/mro.txt787
-rwxr-xr-xpypers/mymodule.py3
-rwxr-xr-xpypers/mysecondscript.py3
-rwxr-xr-xpypers/notbug.txt31
-rwxr-xr-xpypers/notes.txt56
-rwxr-xr-xpypers/nre.html123
-rwxr-xr-xpypers/objects.txt916
-rwxr-xr-xpypers/oldstuff.txt752
-rwxr-xr-xpypers/oopp.py0
-rwxr-xr-xpypers/optparse/cutted-stuff.txt58
-rwxr-xr-xpypers/optparse/example.py18
-rwxr-xr-xpypers/optparse/example0.py12
-rwxr-xr-xpypers/optparse/example1.py40
-rwxr-xr-xpypers/optparse/example2.py39
-rwxr-xr-xpypers/optparse/file1.txt8
-rwxr-xr-xpypers/optparse/file2.txt4
-rwxr-xr-xpypers/optparse/intro.txt83
-rwxr-xr-xpypers/optparse/invoice.html59
-rwxr-xr-xpypers/optparse/invoice.tex120
-rwxr-xr-xpypers/optparse/invoice.txt47
-rwxr-xr-xpypers/optparse/letter.txt7
-rwxr-xr-xpypers/optparse/optionparse.py64
-rwxr-xr-xpypers/optparse/paper.html453
-rwxr-xr-xpypers/optparse/paper.tex574
-rwxr-xr-xpypers/optparse/paper.txt453
-rwxr-xr-xpypers/optparse/paper0.txt457
-rwxr-xr-xpypers/optparse/paper2.html373
-rwxr-xr-xpypers/optparse/paper2.tex507
-rwxr-xr-xpypers/optparse/paper2.txt381
-rwxr-xr-xpypers/optparse/paper2it.txt390
-rwxr-xr-xpypers/optparse/revision.txt100
-rwxr-xr-xpypers/optparse/their-cuts.txt179
-rwxr-xr-xpypers/optparse/x.txt0
-rwxr-xr-xpypers/output.txt0
-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
-rwxr-xr-xpypers/pep318/Makefile23
-rwxr-xr-xpypers/pep318/README.txt48
-rwxr-xr-xpypers/pep318/__main__.html61
-rwxr-xr-xpypers/pep318/addtests.txt232
-rwxr-xr-xpypers/pep318/bug.py50
-rwxr-xr-xpypers/pep318/bug.txt22
-rwxr-xr-xpypers/pep318/chatty.py28
-rwxr-xr-xpypers/pep318/chatty1.py21
-rwxr-xr-xpypers/pep318/chatty2.py28
-rwxr-xr-xpypers/pep318/chatty3.py13
-rwxr-xr-xpypers/pep318/customdec.py68
-rwxr-xr-xpypers/pep318/decorators.html1534
-rwxr-xr-xpypers/pep318/decorators.py184
-rwxr-xr-xpypers/pep318/decorators.tex1733
-rwxr-xr-xpypers/pep318/decorators.txt1413
-rwxr-xr-xpypers/pep318/example.py16
-rwxr-xr-xpypers/pep318/example1.py29
-rwxr-xr-xpypers/pep318/example2.py22
-rwxr-xr-xpypers/pep318/example3.py22
-rwxr-xr-xpypers/pep318/example4.py20
-rwxr-xr-xpypers/pep318/example5.py20
-rwxr-xr-xpypers/pep318/example6.py14
-rwxr-xr-xpypers/pep318/example7.py15
-rwxr-xr-xpypers/pep318/example8.py16
-rwxr-xr-xpypers/pep318/example9.py19
-rwxr-xr-xpypers/pep318/lessmeta/decorators.py210
-rwxr-xr-xpypers/pep318/logged.py8
-rwxr-xr-xpypers/pep318/mod.py11
-rwxr-xr-xpypers/pep318/module.py13
-rwxr-xr-xpypers/pep318/moduledec.py51
-rwxr-xr-xpypers/pep318/moduledec.txt97
-rwxr-xr-xpypers/pep318/mydoc.html51
-rwxr-xr-xpypers/pep318/nonrecognized.py23
-rwxr-xr-xpypers/pep318/oopp.html324
-rwxr-xr-xpypers/pep318/oopp.tex573
-rwxr-xr-xpypers/pep318/post.txt21
-rwxr-xr-xpypers/pep318/printerr.py7
-rwxr-xr-xpypers/pep318/prnt.py7
-rwxr-xr-xpypers/pep318/pro.py28
-rwxr-xr-xpypers/pep318/pro1.py7
-rwxr-xr-xpypers/pep318/pro1.txt10
-rwxr-xr-xpypers/pep318/pro2.py24
-rwxr-xr-xpypers/pep318/pro2.txt3
-rwxr-xr-xpypers/pep318/psyco.tex201
-rwxr-xr-xpypers/pep318/pydoc.html508
-rwxr-xr-xpypers/pep318/safetype.html33
-rwxr-xr-xpypers/pep318/safetype.tex61
-rwxr-xr-xpypers/pep318/tracing.py25
-rwxr-xr-xpypers/pep318/working/README.txt45
-rwxr-xr-xpypers/pep318/working/chatty2.py27
-rwxr-xr-xpypers/pep318/working/customdec.py67
-rwxr-xr-xpypers/pep318/working/debugger.py17
-rwxr-xr-xpypers/pep318/working/decorators.html1337
-rwxr-xr-xpypers/pep318/working/decorators.py173
-rwxr-xr-xpypers/pep318/working/decorators.txt1346
-rwxr-xr-xpypers/pep318/working/doct.py60
-rwxr-xr-xpypers/pep318/working/example.py16
-rwxr-xr-xpypers/pep318/working/example1.py29
-rwxr-xr-xpypers/pep318/working/example2.py36
-rwxr-xr-xpypers/pep318/working/example4.py22
-rwxr-xr-xpypers/pep318/working/example5.py20
-rwxr-xr-xpypers/pep318/working/example6.py14
-rwxr-xr-xpypers/pep318/working/example9.py19
-rwxr-xr-xpypers/pep318/working/logged.py8
-rwxr-xr-xpypers/pep318/working/noconflict.py64
-rwxr-xr-xpypers/pep318/working/pep318.html1046
-rwxr-xr-xpypers/pep318/working/pep318.txt1048
-rwxr-xr-xpypers/pep318/working/pydoc.html504
-rwxr-xr-xpypers/pep318/working/tracing.py20
-rwxr-xr-xpypers/pep318/x.py17
-rwxr-xr-xpypers/pep318/x.txt7
-rwxr-xr-xpypers/preface.tex832
-rwxr-xr-xpypers/preface.txt660
-rwxr-xr-xpypers/pro.txt106
-rwxr-xr-xpypers/pro1.py28
-rwxr-xr-xpypers/pro2.py13
-rwxr-xr-xpypers/pro3.py0
-rwxr-xr-xpypers/pro4.py0
-rwxr-xr-xpypers/pro5.py10
-rwxr-xr-xpypers/pro6.py18
-rwxr-xr-xpypers/prog.txt482
-rwxr-xr-xpypers/prog_inter.py13
-rwxr-xr-xpypers/prova.txt4
-rwxr-xr-xpypers/pyj/python-subtilities.html187
-rwxr-xr-xpypers/pyj/python-subtilities.txt187
-rwxr-xr-xpypers/quixote/notes.txt16
-rwxr-xr-xpypers/recipes/Memoize.txt44
-rw-r--r--pypers/recipes/autoclose.py35
-rw-r--r--pypers/recipes/autoclose.txt89
-rw-r--r--pypers/recipes/autoclose_ex.py18
-rwxr-xr-xpypers/recipes/chop.py12
-rw-r--r--pypers/recipes/deallocating.py12
-rwxr-xr-xpypers/recipes/deferred.py41
-rwxr-xr-xpypers/recipes/deferreds.txt70
-rwxr-xr-xpypers/recipes/doct0.py94
-rwxr-xr-xpypers/recipes/doct24.py94
-rwxr-xr-xpypers/recipes/doctester.html130
-rwxr-xr-xpypers/recipes/doctester.py59
-rwxr-xr-xpypers/recipes/doctester.txt124
-rwxr-xr-xpypers/recipes/example_module.py9
-rwxr-xr-xpypers/recipes/frozen.py27
-rwxr-xr-xpypers/recipes/frozen.txt76
-rwxr-xr-xpypers/recipes/indented_lines.py21
-rwxr-xr-xpypers/recipes/noconflict.txt125
-rwxr-xr-xpypers/recipes/noconflict_alex.py95
-rwxr-xr-xpypers/recipes/optparse.html94
-rwxr-xr-xpypers/recipes/optparse.txt86
-rwxr-xr-xpypers/recipes/prova.txt3
-rwxr-xr-xpypers/recipes/solving_alex.txt209
-rwxr-xr-xpypers/recipes/superattr.py20
-rwxr-xr-xpypers/recipes/supersugar.py42
-rwxr-xr-xpypers/recipes/test/chop.txt27
-rwxr-xr-xpypers/recipes/test/chop2.txt27
-rwxr-xr-xpypers/recipes/testnoconflict.py245
-rwxr-xr-xpypers/regexp.txt0
-rwxr-xr-xpypers/remove.py11
-rwxr-xr-xpypers/secret.txt922
-rwxr-xr-xpypers/secret_inter.py34
-rw-r--r--pypers/simionato_talk/abstract-en.txt12
-rw-r--r--pypers/simionato_talk/abstract-scipy.txt14
-rw-r--r--pypers/simionato_talk/abstract.txt28
-rw-r--r--pypers/simionato_talk/badpricehistory.pngbin0 -> 4224 bytes
-rw-r--r--pypers/simionato_talk/badpricehistory2.pngbin0 -> 5693 bytes
-rw-r--r--pypers/simionato_talk/bio.txt14
-rw-r--r--pypers/simionato_talk/cdf-dist.pngbin0 -> 20911 bytes
-rw-r--r--pypers/simionato_talk/delta-cdf.pngbin0 -> 193776 bytes
-rw-r--r--pypers/simionato_talk/delta-dist.pngbin0 -> 12316 bytes
-rw-r--r--pypers/simionato_talk/delta_dist.py64
-rw-r--r--pypers/simionato_talk/error_trapper.py22
-rw-r--r--pypers/simionato_talk/evalexception_ex.py11
-rw-r--r--pypers/simionato_talk/formulas.html329
-rw-r--r--pypers/simionato_talk/formulas.tex48
-rw-r--r--pypers/simionato_talk/formulas.txt1
-rw-r--r--pypers/simionato_talk/hello.py10
-rw-r--r--pypers/simionato_talk/nonblocking.py48
-rw-r--r--pypers/simionato_talk/nongaussian.pngbin0 -> 21380 bytes
-rw-r--r--pypers/simionato_talk/objectpublisher.py46
-rw-r--r--pypers/simionato_talk/simpleplotter.py21
-rw-r--r--pypers/simionato_talk/talk.html1207
-rw-r--r--pypers/simionato_talk/talk.txt962
-rw-r--r--pypers/simionato_talk/test_cdf.py10
-rw-r--r--pypers/simionato_talk/ui/default/ex.html13
-rw-r--r--pypers/simionato_talk/ui/default/slides.js558
-rw-r--r--pypers/simionato_talk/webplotter.py36
-rwxr-xr-xpypers/style.tex20
-rwxr-xr-xpypers/super/chapman.txt103
-rwxr-xr-xpypers/super/cls_mcl.txt26
-rwxr-xr-xpypers/super/descr.py16
-rwxr-xr-xpypers/super/descr_example.py21
-rwxr-xr-xpypers/super/ex.py19
-rwxr-xr-xpypers/super/ex1.py28
-rwxr-xr-xpypers/super/ex2.py12
-rwxr-xr-xpypers/super/ex3.py19
-rwxr-xr-xpypers/super/ex4.py9
-rwxr-xr-xpypers/super/ex5.py18
-rwxr-xr-xpypers/super/ex_roth.py12
-rwxr-xr-xpypers/super/example1.py18
-rwxr-xr-xpypers/super/post.txt14
-rwxr-xr-xpypers/super/special_meth.py21
-rwxr-xr-xpypers/super/super.html564
-rwxr-xr-xpypers/super/super.txt564
-rwxr-xr-xpypers/super/super23.txt556
-rwxr-xr-xpypers/super/super24.txt536
-rwxr-xr-xpypers/test_oopp.py122
-rwxr-xr-xpypers/test_re.py13
-rwxr-xr-xpypers/trace.txt15
-rwxr-xr-xpypers/tracedaccess.py26
-rwxr-xr-xpypers/tracemain.py7
-rwxr-xr-xpypers/twill/abstract.txt10
-rwxr-xr-xpypers/twill/chromatic1.txt29
-rwxr-xr-xpypers/twill/errata.txt18
-rwxr-xr-xpypers/twill/test_qdemo.txt9
-rwxr-xr-xpypers/twill/testing_web_app.html584
-rwxr-xr-xpypers/twill/testing_web_app.tex725
-rwxr-xr-xpypers/twill/testing_web_app.txt579
-rw-r--r--pypers/twisted/Makefile2
-rw-r--r--pypers/twisted/P01.html93
-rw-r--r--pypers/twisted/P02.html92
-rw-r--r--pypers/twisted/P03.html95
-rw-r--r--pypers/twisted/P04.html101
-rw-r--r--pypers/twisted/P05.html97
-rw-r--r--pypers/twisted/P06.html98
-rw-r--r--pypers/twisted/P07.html91
-rw-r--r--pypers/twisted/P08.html101
-rw-r--r--pypers/twisted/P09.html92
-rw-r--r--pypers/twisted/P10.html88
-rw-r--r--pypers/twisted/P11.html92
-rw-r--r--pypers/twisted/P12.html91
-rw-r--r--pypers/twisted/P13.html113
-rw-r--r--pypers/twisted/P14.html95
-rw-r--r--pypers/twisted/P15.html115
-rw-r--r--pypers/twisted/P16.html91
-rw-r--r--pypers/twisted/config.py6
-rw-r--r--pypers/twisted/connect_mysql.py16
-rw-r--r--pypers/twisted/downloader1.py62
-rw-r--r--pypers/twisted/downloader2.py57
-rw-r--r--pypers/twisted/downloader3.py37
-rw-r--r--pypers/twisted/downloader4.py65
-rw-r--r--pypers/twisted/echoclient.py32
-rw-r--r--pypers/twisted/ex.py15
-rw-r--r--pypers/twisted/ex_thread.py20
-rw-r--r--pypers/twisted/hello_twisted.py17
-rw-r--r--pypers/twisted/maketalk.py97
-rw-r--r--pypers/twisted/million.py56
-rw-r--r--pypers/twisted/process_out.py24
-rw-r--r--pypers/twisted/talk.txt231
-rw-r--r--pypers/twisted/tester.py119
-rw-r--r--pypers/twisted/tester_server.py6
-rw-r--r--pypers/twisted/tk_mainloop.py54
-rw-r--r--pypers/twisted/web_downloader.py68
-rwxr-xr-xpypers/unicode/howto.txt26
-rw-r--r--pypers/wsgi/abstract-en.txt12
-rw-r--r--pypers/wsgi/abstract-scipy.txt14
-rw-r--r--pypers/wsgi/abstract.txt28
-rw-r--r--pypers/wsgi/badpricehistory.pngbin0 -> 4224 bytes
-rw-r--r--pypers/wsgi/badpricehistory2.pngbin0 -> 5693 bytes
-rw-r--r--pypers/wsgi/cdf-dist.pngbin0 -> 20911 bytes
-rw-r--r--pypers/wsgi/delta-cdf.pngbin0 -> 193776 bytes
-rw-r--r--pypers/wsgi/delta-dist.pngbin0 -> 12316 bytes
-rw-r--r--pypers/wsgi/delta_dist.py64
-rw-r--r--pypers/wsgi/err.txt0
-rw-r--r--pypers/wsgi/error_trapper.py22
-rw-r--r--pypers/wsgi/evalexception_ex.py11
-rw-r--r--pypers/wsgi/formulas.html329
-rw-r--r--pypers/wsgi/formulas.tex48
-rw-r--r--pypers/wsgi/formulas.txt1
-rw-r--r--pypers/wsgi/hello.py10
-rw-r--r--pypers/wsgi/limit999.pngbin0 -> 11173 bytes
-rw-r--r--pypers/wsgi/limit999.py37
-rw-r--r--pypers/wsgi/m_distribution.py6
-rw-r--r--pypers/wsgi/nonblocking.py48
-rw-r--r--pypers/wsgi/nongaussian.pngbin0 -> 21380 bytes
-rw-r--r--pypers/wsgi/notes.py32
-rw-r--r--pypers/wsgi/objectpublisher.py46
-rw-r--r--pypers/wsgi/rst2s5_math.py29
-rw-r--r--pypers/wsgi/simpleplotter.py21
-rw-r--r--pypers/wsgi/talk.html1207
-rw-r--r--pypers/wsgi/talk.txt962
-rw-r--r--pypers/wsgi/test_cdf.py10
-rw-r--r--pypers/wsgi/thread_log.py13
-rw-r--r--pypers/wsgi/ui/default-orig/ex.html13
-rw-r--r--pypers/wsgi/ui/default-orig/slides.js558
-rw-r--r--pypers/wsgi/ui/default/blank.gifbin0 -> 49 bytes
-rw-r--r--pypers/wsgi/ui/default/ex.html13
-rw-r--r--pypers/wsgi/ui/default/slides.js558
-rw-r--r--pypers/wsgi/var_vol.py91
-rw-r--r--pypers/wsgi/webplotter.py36
-rwxr-xr-xpypers/x.txt213
-rwxr-xr-xpypers/xx.txt1
-rw-r--r--pypers/yet-another-comparison-of-web-frameworks.txt292
879 files changed, 156070 insertions, 0 deletions
diff --git a/pypers/EXECUTEME.py b/pypers/EXECUTEME.py
new file mode 100755
index 0000000..722561c
--- /dev/null
+++ b/pypers/EXECUTEME.py
@@ -0,0 +1,11 @@
+print '*'*70
+print """This is frontend to the script "test.py" that will test the scripts
+contained in the book "Object Oriented Programming in Python" and will
+create a module called "oopp" needed to run properly the examples."""
+print '*'*70
+
+if raw_input("Do you want to continue? (y/n) ")!='y': raise SystemExit
+
+print; execfile('test.py')
+
+raw_input('Press Return to continue ...')
diff --git a/pypers/MI.txt b/pypers/MI.txt
new file mode 100755
index 0000000..e60b49a
--- /dev/null
+++ b/pypers/MI.txt
@@ -0,0 +1,1381 @@
+THE SUBTLETIES OF MULTIPLE INHERITANCE
+==========================================================================
+
+In chapter 4 we introduced the concept of multiple inheritance and discussed
+its simplest applications in absence of name collisions. When with methods
+with different names are derived from different classes multiple inheritance
+is pretty trivial. However, all kind of subtilites comes in presence of name
+clashing, i.e. when we multiply inherits different methods defined in different
+classes but with the *same* name.
+In order to understand what happens in this situation, it is essential to
+understand the concept of Method Resolution Order (MRO). For reader's
+convenience, I collect in this chapter some of the information
+reported in http://www.python.org/2.3/mro.html.
+
+A little bit of history: why Python 2.3 has changed the MRO
+------------------------------------------------------------------------------
+
+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, 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).
+
+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. In order to do that, we need the
+concept of *merging* lists, since the rule says that
+
+ *the linearization of C is the sum of C plus the merge of a) the
+ linearizations of the parents and b) the list of the parents.*
+
+In symbolic notation:
+
+ L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
+
+How is the merge computed? The rule is the following:
+
+ *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:
+
+1. C is the ``object`` class, which has no parents; in this case its
+ linearization coincides with itself,
+
+ L[object] = object.
+
+2. 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 ;-)
+
+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.
+
+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 #under Python 2.3 this is an error
+ '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 ;-)
+
+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.
+
+.. [#] 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
+
+.. [#] The (in)famous book on metaclasses, *Putting Metaclasses to Work*:
+ Ira R. Forman, Scott Danforth, Addison-Wesley 1999 (out of print,
+ but probably still available on http://www.amazon.com)
+
+
+Understanding the Method Resolution Order
+--------------------------------------------------------------------------
+
+The MRO of any given (new style) Python class is given
+by the special attribute ``__mro__``. Notice that since
+Python is an extremely dynamic language it is possible
+to delete and to generate whole classes at run time, therefore the MRO
+is a dynamic concept. For instance, let me show how it is possibile to
+remove a class from my
+paleoanthropological hierarchy: for instance I can
+replace the last class 'HomoSapiensSapiens' with 'HomoSapiensNeardenthalensis'
+(changing a class in the middle of the hierarchy would be more difficult). The
+following lines do the job dynamically:
+
+ >>> from oopp import *
+ >>> del HomoSapiensSapiens
+ >>> class HomoSapiensNeardenthalensis(HomoSapiens):
+ ... def can(self):
+ ... super(self.__this,self).can()
+ ... print " - make something"
+ >>> reflective(HomoSapiensNeardenthalensis)
+ >>> HomoSapiensNeardenthalensis().can()
+ HomoSapiensNeardenthalensis can:
+ - make tools
+ - make abstractions
+ - make something
+
+In this case the MRO of 'HomoSapiensNeardenthalensis', i.e. the list of
+all its ancestors, is
+
+ >>> HomoSapiensNeardenthalensis.__mro__
+ [<class '__main__.HomoSapiensNeardenthalensis'>,<class 'oopp.HomoSapiens'>,
+ <class 'oopp.HomoHabilis'>, <class 'oopp.Homo'>,
+ <class 'oopp.PrettyPrinted'>, <class 'oopp.object'>]
+
+The ``__mro__`` attribute gives the *linearization* of the class, i.e. the
+ordered list of its ancestors, starting from the class itself and ending
+with object. The linearization of a class is essential in order to specify
+the resolution order of methods and attributes, i.e. the Method Resolution
+Order (MRO). In the case of single inheritance hierarchies, such the
+paleonthropological example, the MRO is pretty obvious; on the contrary
+it is a quite non-trivial concept in the case of multiple inheritance
+hierarchies.
+
+For instance, let me reconsider my first example of multiple inheritance,
+the ``NonInstantiableClock`` class, inheriting from 'NonInstantiable' and
+'Clock'. I may represent the hierarchy with the following inheritance graph:
+
+ ::
+
+
+ -- object --
+ / (__new__) \
+ / \
+ / \
+ Clock NonInstantiable
+ (get_time) (__new__)
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ NonInstantiableClock
+ (get_time,__new__)
+
+
+The class ``Clock`` define a ``get_time`` method, whereas the class
+``NonInstantiable`` overrides the ``__new__`` method of the ``object`` class;
+the class ``NonInstantiableClock`` inherits ``get_time`` from 'Clock' and
+``__new__`` from 'NonInstantiable'.
+
+The linearization of 'NonInstantiableClock' is
+
+ >>> NonInstantiableClock.mro()
+ [<class '__main__.NonInstantiableClock'>, <class 'oopp.Clock'>,
+ <class 'oopp.NonInstantiable'>, <type 'object'>]
+
+
+In particular, since 'NonInstantiable' precedes 'object', its ``__new__``
+method overrides the ``object`` new method. However, with the MRO used before
+Python 2.2, the linearization would have been ``NonInstantiableClock, Clock,
+object, NonInstantiable, object`` and the ``__new__`` method of object would
+have (hypothetically, of course, since before Python 2.2 there was not
+``__new__`` method! ;-) overridden the ``__new__``
+method of ``NonInstantiable``, therefore ``NonInstantiableClock`` would
+have lost the property of being non-instantiable!
+
+This simple example shows that the choice of a correct Method Resolution
+Order is far from being obvious in general multiple inheritance hierarchies.
+After a false start in Python 2.2, (with a MRO failing in some subtle cases)
+Python 2.3 decided to adopt the so-called C3 MRO, invented by people working
+on Dylan (even if Dylan itself uses the MRO of Common Lisp CLOS). Since this
+is quite a technical matter, I defer the interested reader to appendix 2
+for a full discussion of the C3 algorithm.
+
+Here, I prefer to point out how the built-in
+``super`` object works in multiple inheritance situations. To this aim, it
+is convenient to define an utility function that retrieves the ancestors
+of a given class with respect to the MRO of one of its subclasses:
+
+ ::
+
+ #<oopp.py>
+
+ def ancestor(C,S=None):
+ """Returns the ancestors of the first argument with respect to the
+ MRO of the second argument. If the second argument is None, then
+ returns the MRO of the first argument."""
+ if C is object:
+ raise TypeError("There is no superclass of object")
+ elif S is None or S is C:
+ return list(C.__mro__)
+ elif issubclass(S,C): # typical case
+ mro=list(S.__mro__)
+ return mro[mro.index(C):] # compute the ancestors from the MRO of S
+ else:
+ raise TypeError("S must be a subclass of C")
+
+ #</oopp.py>
+
+Let me show how the function ``ancestor`` works.
+Consider the class ``Clock`` in isolation: then
+its direct superclass, i.e. the first ancestor, is ``object``,
+
+ >>> from oopp import *
+ >>> ancestor(Clock)[1]
+ <type 'object'>
+
+therefore ``super(Clock).__new__`` retrieves the ``object.__new__`` method:
+
+ >>> super(Clock).__new__
+ <built-in method __new__ of type object at 0x80e6fc0>
+
+Consider now the ``Clock`` class together with its subclass
+``NonInstantiableClock``:
+in this case the first ancestor of ``Clock``, *with respect to the MRO of
+'NonInstantiableClock'* is ``NonInstantiable``
+
+ >>> ancestor(Clock,NonInstantiableClock)[1]
+ <class 'oopp.NonInstantiable'>
+
+Therefore ``super(Clock,NonInstantiableClock).__new__`` retrieves the
+``NonInstantiable.__new__`` method:
+
+ >>> super(Clock,NonInstantiableClock).__new__
+ <function __new__ at 0x81b293c>
+ >>> NonInstantiable.__new__
+ <function __new__ at 0x81b293c>
+
+It must be pointed out that ``super(C,S)`` is equivalent but not the same
+than ``ancestor(C,S)[1]``, since it does not return the superclass:
+it returns a super object, instead:
+
+ >>> super(Clock,NonInstantiableClock)
+ <super: <class 'Clock'>, <type object>>
+
+ #<oopp.py>
+
+ #class Super(super):
+ # def __init__(self,C,S=None):
+ # super(Super,self).__init__(C,S)
+ # self.__name__="Super(%s)" % C.__name__
+
+ #</oopp.py>
+
+Finally, there is little quirk of super:
+
+ >>> class C(PrettyPrinted): pass
+ >>> s=super(C,C())
+ >>> s.__str__()
+
+but
+
+ >>> str(s) # idem for print s
+ "<super: <class 'C'>, <C object>>"
+
+Idem for non-pre-existing methods:
+
+ >>> class D(list): pass
+ ...
+ >>> s=super(D,D())
+ >>> s.__len__()
+ 0
+ >>> len(s) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: len() of unsized object
+
+The same problem comes with ``__getattr__``:
+
+ >>> class E(object):
+ ... def __getattr__(self,name):
+ ... if name=='__len__': return lambda:0
+ ...
+ >>> e=E()
+ >>> e.__len__()
+ 0
+ >>> len(e) # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: len() of unsized object
+
+Counting instances
+----------------------------------------------------------------------
+
+ .. line-block::
+
+ *Everything should be built top-down, except the first time.*
+ -- Alan Perlis
+
+Multiple inheritance adds a step further to the bottom-up philosophy and
+it makes appealing the idea of creating classes with the only
+purpose of being derived. Whereas in the top-down approach one starts
+with full featured standalone classes, to be further refined, in the
+mix-in approach one starts with bare bone classes, providing very simple
+or even trivial features, with the purpose of providing
+basic reusable components in multiple inheritance hierarchies.
+At the very end, the idea is to generate a library of *mixin* classes, to be
+composed with other classes. We already saw a couple of examples of
+mixin classes: 'NonInstantiable' and 'Customizable'. In this paragraph
+I will show three other examples: 'WithCounter','Singleton' and
+'AvoidDuplication'.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+``__init__`` method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting feature in a separate mix-in class: then the feature can be
+added to the original class via multiple inheritance, without touching
+the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+``__new__`` method of the counter class must me cooperative, and preferably
+via an anonymous super call.
+
+ ::
+
+ #<oopp.py>
+
+ class WithCounter(object):
+ """Mixin class counting the total number of its instances and storing
+ it in the class attribute counter."""
+
+ counter=0 # class attribute (or static attribute in C++/Java terms)
+
+ def __new__(cls,*args,**kw):
+ cls.counter+=1 # increments the class attribute
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ #anonymous cooperative call to the superclass's method __new__
+
+ reflective(WithCounter)
+
+ #</oopp.py>
+
+Each time an instance of 'WithCounter' is initialized, the counter 'count' is
+incremented and when 'WithCounter' is composed trough multiple inheritance,
+its '__new__' method cooperatively invokes the ``__new__`` method
+of the other components.
+
+For instance, I can use 'WithCounter' to implement a 'Singleton', i.e.
+a class that can have only one instance. This kind of classes can be
+obtained as follows:
+
+ ::
+
+ #<oopp.py>
+
+ class Singleton(WithCounter):
+ "If you inherit from me, you can only have one instance"
+ def __new__(cls,*args,**kw):
+ if cls.counter==0: #first call
+ cls.instance=super(cls.__this,cls).__new__(cls,*args,**kw)
+ return cls.instance
+
+ reflective(Singleton)
+
+ #</oopp.py>
+
+As an application, I can create a
+class ``SingleClock`` that inherits from ``Clock``
+*and* from ``Singleton``. This means that ``SingleClock`` is both a
+'Clock' and a 'Singleton', i.e. there can be only a clock:
+
+ >>> from oopp import Clock,Singleton
+ >>> class SingleClock(Clock,Singleton): pass
+ ...
+ >>> clock1=SingleClock()
+ >>> clock2=SingleClock()
+ >>> clock1 is clock2
+ True
+
+Instantiating many clocks is apparently possible (i.e. no error
+message is given) but you always obtain the same instance. This makes
+sense, since there is only one time on the system and a single
+clock is enough.
+
+A variation of the 'Singleton' is a class that generates a new
+instance only when a certain condition is satisfied. Suppose for instance
+one has a 'Disk' class, to be instantiated with the syntax
+``Disk(xpos,ypos,radius)``.
+It is clear that two disks with the same radius and the same position in
+the cartesian plane, are essentially the same disk (assuming there are no
+additional attributes such as the color). Therefore it is a vaste of memory
+to instantiate two separate objects to describe the same disk. To solve
+this problem, one possibility is to store in a list the calling arguments.
+When it is time to instanciate a new objects with arguments args = xpos,ypos,
+radius, Python should check if a disk with these arguments has already
+been instanciated: in this case that disk should be returned, not a new
+one. This logic can be elegantly implemented in a mix-in class such as the
+following (compare with the ``withmemory`` wrapper in chapter 2):
+
+ ::
+
+ #<oopp.py>
+
+ class AvoidDuplication(object):
+ def __new__(cls,*args,**kw):
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ __new__=withmemory(__new__) # collects the calls in __new__.result
+
+ reflective(AvoidDuplication)
+
+ #</oopp.py>
+
+Notice that 'AvoidDuplication' is introduced with the only purpose of
+giving its functionality to 'Disk': in order to reach this goal, it is enough
+to derive 'Disk' from this class and our previously
+introduced 'GeometricFigure' class by writing something like
+
+ >>> from oopp import *
+ >>> class Disk(GeometricFigure,AvoidDuplication):
+ ... def __init__(self,xpos,ypos,radius):
+ ... return super(Disk,self).__init__('(x-x0)**2+(y-y0)**2 <= r**2',
+ ... x0=xpos,y0=ypos,r=radius)
+
+Now, if we create a disk
+
+ >>> c1=Disk(0,0,10) #creates a disk of radius 10
+
+it is easy enough to check that trying to instantiate a new disk with the
+*same* arguments return the old disk:
+
+ >>> c2=Disk(0,0,10) #returns the *same* old disk
+ >>> c1 is c2
+ True
+
+Here, everything works, because through the
+cooperative ``super`` mechanism, ``Disk.__init__`` calls
+``AvoidDuplication.__init__`` that calls ``GeometricFigure.__init__``
+that in turns initialize the disk. Inverting the order of
+'AvoidDuplication' and 'GeometricFigure' would case a disaster, since
+``GeometricFigure.__init__`` would override ``AvoidDuplication.__init__``.
+
+Alternatively, one could use the object factory 'Makeobj' implemented in
+chapter 3:
+
+ >>> class NonDuplicatedFigure(GeometricFigure,AvoidDuplication): pass
+ >>> makedisk=Makeobj(NonDuplicatedFigure,'(x-x0)**2/4+(y-y0)**2 <= r**2')
+ >>> disk1=makedisk(x0=38,y0=7,r=5)
+ >>> disk2=makedisk(x0=38,y0=7,r=5)
+ >>> disk1 is disk2
+ True
+
+Remark: it is interesting to notice that the previous approach would not work
+for keyword arguments, directly, since dictionary are unhashable.
+
+The pizza-shop example
+----------------------------------------------------------------
+
+Now it is time to give a non-trivial example of multiple inheritance with
+cooperative and non-cooperative classes. The point is that multiple
+inheritance can easily leads to complicated hierarchies: where the
+resolution order of methods is far from being obvious and actually
+can give bad surprises.
+
+To explain the issue, let me extend the program for the pizza-shop owner of
+chapter 4, by following the bottom-up approach and using anonymous
+cooperative super calls.
+In this approach, one starts from the simplest thing.
+It is clear that the pizza-shop owner has interest in recording all the
+pizzas he sell.
+To this aim, he needs a class providing logging capabilities:
+each time a new instance is created, its features are stored in a log file. In
+order to count the total number of instances, 'WithLogger' must derive from
+the 'WithCounter' class. In order to have a nicely printed message,
+'WithLogger' must derive from 'PrettyPrinted'. Finally,
+since 'WithLogger' must be a general purpose
+class that I will reuse in other problem as a mixin class, it must be
+cooperative. 'WithLogger' can be implemented as follows:
+
+ ::
+
+ #<oopp.py>
+
+ class WithLogger(WithCounter,PrettyPrinted):
+ """WithLogger inherits from WithCounter the 'count' class attribute;
+ moreover it inherits '__str__' from PrettyPrinted"""
+ logfile=sys.stdout #default
+ verboselog=False #default
+ def __init__(self,*args,**kw):
+ super(self.__this,self).__init__(*args,**kw) # cooperative
+ dic=attributes(self) # non-special attributes dictionary
+ print >> self.logfile,'*'*77
+ print >> self.logfile, time.asctime()
+ print >> self.logfile, "%s. Created %s" % (type(self).counter,self)
+ if self.verboselog:
+ print >> self.logfile,"with accessibile non-special attributes:"
+ if not dic: print >> self.logfile,"<NOTHING>",
+ else: print >> self.logfile, pretty(dic)
+
+ reflective(WithLogger)
+
+ #</oopp.py>
+
+Here I could well use ``super(self.__this,self).__init__(*args,**kw)``
+instead of ``super(self.__this,self).__init__(*args,**kw)``, nevertheless
+the standard ``super`` works in this case and I can use it with better
+performances.
+Thanks to the power of multiple inheritance, we may give logging features
+to the 'CustomizablePizza' class defined in chapter 4
+with just one line of code:
+
+ >>> from oopp import *
+ >>> class Pizza(WithLogger,CustomizablePizza):
+ ... "Notice, WithLogger is before CustomizablePizza"
+ >>> Pizza.With(toppinglist=['tomato'])('small')
+ ****************************************************************************
+ Sat Feb 22 14:54:44 2003
+ 1. Created <Pizza>
+ <__main__.Pizza object at 0x816927c>
+
+It is also possible to have a more verbose output:
+
+ >>> Pizza.With(verboselog=True)
+ <class '__main__.Pizza'>
+ >>> Pizza('large')
+ ****************************************************************************
+ Sat Feb 22 14:59:51 2003
+ 1. Created <Pizza>
+ with accessibile non-special attributes:
+ With = <bound method type.customized of <class '__main__.Pizza'>>
+ baseprice = 1
+ count = 2
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <bound method Pizza.price of <__main__.Pizza object at 0x402f6c8c>>
+ size = large
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato']
+ toppings_price = <bound method Pizza.toppings_price of
+ <__main__.Pizza object at 0x402f6c8c>>
+ verboselog = True
+ with = <bound method Pizza.customized of
+ <__main__.Pizza object at 0x402f6c8c>>
+ <__main__.Pizza object at 0x401ce7ac>
+
+However, there is a problem here, since the output is '<Pizza>' and
+not the nice 'large pizza with tomato, cost $ 4.5' that we would
+expect from a child of 'CustomizablePizza'. The solution to the
+puzzle is given by the MRO:
+
+ >>> Pizza.mro()
+ [<class '__main__.Pizza'>, <class 'oopp.WithLogger'>,
+ <class 'oopp.WithCounter'>, <class 'oopp.PrettyPrinted'>,
+ <class 'oopp.CustomizablePizza'>, <class 'oopp.GenericPizza'>,
+ <class 'oopp.Customizable'>, <type 'object'>]
+
+The inheritance graph is rather complicated:
+
+ ::
+
+ object 7
+
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ 2 WithCounter PrettyPrinted 3 GenericPizza 5 Customizable 6
+ (__new__) (__str__,__init__) (__str__) /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / CustomizablePizza 4
+ \ / /
+ 1 WithLogger /
+ (__init__) /
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+
+ Pizza O
+
+
+As we see, the precedence in the resolution of methods is far from being
+trivial. It is denoted in the graph with numbers
+from 0 to 7: first the methods of 'Pizza' (level 0), then the methods of
+'WithLogger' (level 1), then the methods of 'WithCounter' (level 2), then
+the methods of 'PrettyPrinted' (level 3), then the methods of
+'CustomizablePizza' (level 4), then the methods of 'GenericPizza' (level 5),
+then the level of 'Customizable' (level 6), finally the 'object' methods
+(level 7).
+
+The reason why the MRO is so, can be understood by studying
+appendix 1.
+
+We see that the ``__init__`` methods of 'WithLogger' and
+the ``__new__`` method of 'WithCounter' are cooperative.
+``WithLogger.__init__``
+calls ``WithCounter.__init__`` that is
+inherited from ``CustomizablePizza.__init__`` which is not cooperative,
+but this is not dangerous since ``CustomizablePizza.__init__`` does not need
+to call any other ``__init__``.
+
+However, ``PrettyPrinted.__str__`` and ``GenericPizza.__str__`` are not
+cooperative and since 'PrettyPrinted' precedes 'GenericPizza', the
+``GenericPizza.__str__`` method is overridden, which is bad.
+
+If ``WithLogger.__init__`` and ``WithCounter.__new__`` were not
+cooperative, they would therefore badly breaking the program.
+
+The message is: when you inherit from both cooperative and non-cooperative
+classes, put cooperative classes first. The will be fair and will not
+blindly override methods of the non-cooperative classes.
+
+
+With multiple inheritance you can reuse old code a lot,
+however the price to pay, is to have a non-trivial hierarchy. If from
+the beginning we knew that 'Pizza' was needing a 'WithLogger',
+a 'WithCounter' and the
+ability to be 'Customizable' we could have put everything in an unique
+class. The problem is that in real life one never knows ;)
+Fortunately, Python dynamism allows to correct design mistakes
+
+Remark: in all text books about inheritance, the authors always stress
+that inheritance should be used as a "is-a" relation, not
+and "has-a" relation. In spite of this fact, I have decided to implement
+the concept of having a logger (or a counter) via a mixin class. One
+should not blindly believe text books ;)
+
+Fixing wrong hierarchies
+-----------------------------------------------------------------------------
+
+A typical metaprogramming technique, is the run-time modification of classes.
+As I said in a previous chapter, this feature can confuse the programmer and
+should not be abused (in particular it should not be used as a replacement
+of inheritance!); nevertheless, there applications where the ability of
+modifying classes at run time is invaluable: for instance,
+it can be used to correct design mistakes.
+
+In this case we would like the ``__str__ method`` of 'PrettyPrinted' to be
+overridden by ``GenericPizza.__str__``. Naively, this can be solved by
+putting 'WithLogger' after 'GenericPizza'. Unfortunately, doing so
+would cause ``GenericPizza.__init__`` to override ``WithLogger.__init__``,
+therefore by loosing logging capabilitiesr, unless countermeasures
+are taken.
+
+A valid countermeasure could be to replace the non-cooperative
+``GenericPizza.__init__`` with a cooperative one. This can miraculously
+done at run time in few lines of code:
+
+ ::
+
+ #<oopp.py>
+
+ def coop_init(self,size): # cooperative __init__ for GenericPizza
+ self.size=size
+ super(self._GenericPizza__this,self).__init__(size)
+
+ GenericPizza.__init__=coop_init # replace the old __init__
+
+ reflective(GenericPizza) # define GenericPizza.__this
+
+ #</oopp.py>
+
+Notice the usage of the fully qualified private attribute
+``self._GenericPizza__this`` inside ``coop_init``: since this function
+is defined outside any class, the automatica mangling mechanism cannot
+work and has to be implemented by hand. Notice also that
+``super(self._GenericPizza__this,self)`` could be replaced by
+``super(GenericPizza,self)``; however the simpler approach is
+less safe against possible future manipulations of the hierarchy.
+Suppose, for example, we want to create a copy of the hierarchy
+with the same name but slightly different features (actually,
+in chapter 8 we will implement a traced copy of the pizza hierarchy,
+useful for debugging purposes): then, using ``super(GenericPizza,self)``
+would raise an error, since self would be an instance of the traced
+hierarchy and ``GenericPizza`` the original nontraced class. Using
+the form ``super(self._GenericPizza__this,self)`` and making
+``self._GenericPizza__this`` pointing to the traced 'GenericPizza'
+class (actually this will happen automatically) the problems goes
+away.
+
+Now everything works if 'WithLogger' is put after 'CustomizablePizza'
+
+ >>> from oopp import *
+ >>> class PizzaWithLog(CustomizablePizza,WithLogger): pass
+ >>> PizzaWithLog.With(toppinglist=['tomato'])('large')
+ ****************************************************************************
+ Sun Apr 13 16:19:12 2003
+ 1. Created large pizza with tomato, cost $ 4.5
+ <class '__main__.PizzaWithLog'>
+
+The log correctly says ``Created large pizza with tomato, cost $ 4.5`` and not
+``Created <Pizza>`` as before since now ``GenericPizza.__str__``
+overrides ``PrettyPrinted.__str__``. Moreover, the hierarchy is logically
+better organized:
+
+ >>> PizzaWithLog.mro()
+ [<class '__main__.PizzaWithLog'>, <class 'oopp.CustomizablePizza'>,
+ <class 'oopp.GenericPizza'>, <class 'oopp.Customizable'>,
+ <class 'oopp.WithLogger'>, <class 'oopp.WithCounter'>,
+ <class 'oopp.PrettyPrinted'>, <type 'object'>]
+
+I leave as an exercise for the reader to make the ``__str__`` methods
+cooperative ;)
+
+Obviously, in this example it would have been better to correct the
+original hierarchy, by leaving 'Beautiful' instantiable from the beginning
+(that's why I said the 'Beautiful' is an example of wrong mix-in class):
+nevertheless, sometimes, one has do to with wrong hierarchies written by
+others, and it can be a pain to fix them, both directly by modifying the
+original source code, and indirectly
+by inheritance, since one must change all the names, in order to distinghish
+the original classes from the fixed ones. In those cases Python
+dynamism can save your life. This also allows you enhance original
+classes which are not wrong, but that simply don't do something you want
+to implement.
+
+Modifying classes at run-time can be trivial, as in the examples I have
+shown here, but can also be rather tricky, as in this example
+
+ >>> from oopp import PrettyPrinted
+ >>> class PrettyPrintedWouldBe(object): __str__ = PrettyPrinted.__str__
+ >>> print PrettyPrintedWouldBe() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: unbound method __str__() must be called with PrettyPrinted
+ instance as first argument (got nothing instead)
+
+As the error message says, the problem here, is that the
+``PrettyPrinted.__str__`` unbound method, has not received any argument.
+This is because in this
+form ``PrettyPrintedWouldBe.__str__`` has been defined as an attribute,
+not as a real method. The solution is to write
+
+ >>> class PrettyPrintedWouldBe(object):
+ ... __str__ = PrettyPrinted.__dict__['__str__']
+ ...
+ >>> print PrettyPrintedWouldBe() # now it works
+ <PrettyPrintedWouldBe>
+
+This kind of run-time modifications does not work when private variables
+are involved:
+
+ ::
+
+ #<changewithprivate.py>
+
+ class C(object):
+ __x='C.__init__'
+ def __init__(self):
+ print self.__x # okay
+
+ class D(object):
+ __x='D.__init__'
+ __init__=C.__dict__['__init__'] # error
+
+ class New:
+ class C(object):
+ __x='New.C.__init__'
+ __init__=C.__dict__['__init__'] # okay
+
+ C()
+ try: D()
+ except AttributeError,e: print e
+
+ #</changewithprivate.py>
+
+Gives as result
+
+ ::
+
+ C.__init__
+ 'D' object has no attribute '_C__x'
+ New.C.__init__
+
+The problem is that when ``C.__dict__['__init__']`` is compiled
+(to byte-code) ``self.__x`` is expanded to ``self._C__x``. However,
+when one invokes ``D.__init__``, a D-object is passed, which has
+a ``self._D__x`` attribute, but not a ``self._C__x`` attribute (unless
+'D' is a subclass of 'C'. Fortunately, Python wisdom
+
+ *Namespaces are one honking great idea -- let's do more of those!*
+
+suggests the right solution: to use a new class with the *same name*
+of the old one, but in a different namespace, in order to avoid
+confusion. The simplest way to generate a new namespace is to
+declare a new class (the class 'New' in this example): then 'New.C'
+becomes an inner class of 'New'. Since it has the same name of the
+original class, private variables are correctly expanded and one
+can freely exchange methods from 'C' to 'New.C' (and viceversa, too).
+
+Modifying hierarchies
+-------------------------------------------------------------------------
+
+ ::
+
+ def mod(cls): return cls
+
+ class New: pass
+
+ for c in HomoSapiensSapiens.__mro__:
+ setattr(New,c.__name__,mod(c))
+
+Inspecting Python code
+-------------------------------------------------------------------------
+
+how to inspect a class, by retrieving useful informations about its
+information.
+
+A first possibility is to use the standard ``help`` function.
+The problem of this approach is that ``help`` gives too much
+information.
+
+ ::
+
+ #<oopp.py>
+
+ #plaindata=
+ plainmethod=lambda m:m #identity function
+
+ class Get(object):
+ """Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod,
+ property, plainmethod, plaindata, returns the corresponding
+ attributes as a keyword dictionary. It works by internally calling
+ the routine inspect.classify_class_attrs. Notice that special data
+ attributes are not retrieved (this is by design)."""
+ def __init__(self,cls):
+ self.staticmethods=kwdict()
+ self.classmethods=kwdict()
+ self.properties=kwdict()
+ self.methods=kwdict()
+ self.data=kwdict()
+ for name, kind, klass, attr in inspect.classify_class_attrs(cls):
+ if kind=='static method':
+ self.staticmethods[name]=attr
+ elif kind=='class method':
+ self.classmethods[name]=attr
+ elif kind=='property':
+ self.properties[name]=attr
+ elif kind=='method':
+ self.methods[name]=attr
+ elif kind=='data':
+ if not special(name): self.data[name]=attr
+ def __call__(self,descr): #could be done with a dict
+ if descr==staticmethod: return self.staticmethods
+ elif descr==classmethod: return self.classmethods
+ elif descr==property: return self.properties
+ elif descr==plainmethod: return self.methods
+ elif descr==plaindata: return self.data
+ else: raise SystemExit("Invalid descriptor")
+
+ #</oopp.py>
+
+With similar tricks one can automatically recognize cooperative methods:
+#it is different, (better NOT to use descriptors)
+
+ ::
+
+ #<oopp.py>
+
+ #class Cooperative(Class):
+ # __metaclass__ = WithWrappingCapabilities
+ #
+ # def cooperative(method):
+ # """Calls both the superclass method and the class
+ # method (if the class has an explicit method).
+ # Works for methods returning None."""
+ # name,cls=Cooperative.parameters # fixed by the meta-metaclass
+ # def _(*args,**kw):
+ # getattr(super(cls,args[0]),name)(*args[1:],**kw)
+ # if method: method(*args,**kw) # call it
+ # return _
+ #
+ # cooperative=staticmethod(cooperative)
+
+
+ #</oopp.py>
+
+ ::
+
+ #<oopp.py>
+
+ def wrapH(cls):
+ for c in cls.__mro__[:-2]:
+ tracer.namespace=c.__name__
+ new=vars(c).get('__new__',None)
+ if new: c.__new__=tracedmethod(new)
+
+ #</oopp.py>
+
diff --git a/pypers/Makefile b/pypers/Makefile
new file mode 100755
index 0000000..91b2b3a
--- /dev/null
+++ b/pypers/Makefile
@@ -0,0 +1,78 @@
+default:
+ make allchapters
+preface.html: preface.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets preface.txt preface.html
+first.html: first.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets first.txt first.html
+functions.html: functions.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets functions.txt functions.html
+objects.html: objects.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets objects.txt objects.html
+classes.html: classes.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets classes.txt classes.html
+descr.html: descr.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets descr.txt descr.html
+MI.html: MI.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets MI.txt MI.html
+meta.html: meta.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets meta.txt meta.html
+secret.html: secret.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets secret.txt secret.html
+magic.html: magic.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets magic.txt magic.html
+prog.html: prog.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets prog.txt prog.html
+app1.html: app1.txt
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets app1.txt app1.html
+
+html:
+ python2 /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets all.txt all.html
+
+ps: preface.html first.html functions.html objects.html classes.html \
+ descr.html MI.html meta.html magic.html secret.html prog.html app1.html
+ htmldoc preface.html first.html functions.html objects.html \
+ classes.html descr.html MI.html meta.html magic.html secret.html \
+ prog.html app1.html -f OOPP.ps
+
+pdf: *.html
+ htmldoc preface.html first.html functions.html objects.html \
+ classes.html descr.html MI.html meta.html magic.html secret.html \
+ prog.html app1.html -f OOPP.pdf
+
+allchapters: preface.html first.html functions.html objects.html classes.html \
+ descr.html MI.html meta.html magic.html secret.html prog.html app1.html
+ make ps; make pdf; gv OOPP.ps&
+
+dist: preface.txt first.txt functions.txt objects.txt classes.txt descr.txt \
+ meta.txt magic.txt secret.txt prog.txt app1.txt README.txt \
+ EXECUTEME.py test.py
+ zip dist preface.txt first.txt functions.txt objects.txt classes.txt \
+ descr.txt meta.txt magic.txt secret.txt prog.txt app1.txt README.txt \
+ EXECUTEME.py test.py
+
+all.txt: preface.txt first.txt functions.txt objects.txt classes.txt descr.txt\
+ meta.txt magic.txt secret.txt prog.txt app1.txt
+ /home/micheles/md/scripts/cat.py preface.txt first.txt functions.txt\
+ objects.txt classes.txt descr.txt MI.txt meta.txt magic.txt secret.txt\
+ prog.txt app1.txt > all.txt
+
+all: all.txt
+ /home/micheles/md/scripts/rst.py all.txt
+ htmldoc all.html -f OOPP.ps
+ gv OOPP.ps
+
+all.tex: all.txt
+ /home/micheles/md/scripts/rst.py -tbx all
diff --git a/pypers/README.txt b/pypers/README.txt
new file mode 100755
index 0000000..46952e7
--- /dev/null
+++ b/pypers/README.txt
@@ -0,0 +1,4 @@
+You could find the message:
+Errors found in expat.py
+
+This on my Red Hat 7.2 machine, no problems on 7.3.
diff --git a/pypers/adwi/talk.html b/pypers/adwi/talk.html
new file mode 100644
index 0000000..a2bdd12
--- /dev/null
+++ b/pypers/adwi/talk.html
@@ -0,0 +1,408 @@
+<?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.4: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title></title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+<h2>StatPro Milan</h2>
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+
+</div>
+<div class="slide" id="rcare-internals">
+<h1>RCare Internals</h1>
+<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"><p class="first">Michele Simionato</p>
+</td>
+</tr>
+<tr class="field"><th class="field-name">date:</th><td class="field-body"><p class="first">2007-01-30</p>
+<p class="last"><strong>StatPro Milan</strong></p>
+</td>
+</tr>
+</tbody>
+</table>
+<!-- Definitions of interpreted text roles (classes) for S5/HTML data. -->
+<!-- This data file has been placed in the public domain. -->
+<!-- Colours
+======= -->
+<!-- Text Sizes
+========== -->
+<!-- Display in Slides (Presentation Mode) Only
+========================================== -->
+<!-- Display in Outline Mode Only
+============================ -->
+<!-- Display in Print Only
+===================== -->
+<!-- Incremental Display
+=================== -->
+</div>
+<div class="slide" id="some-numbers">
+<h1>Some numbers</h1>
+<p class="incremental">files [('.py', 273), ('.pt', 62), ('.zsql', 28)]</p>
+<p class="incremental">lines [('.py', 48327), ('.pt', 12124), ('.zsql', 1726)]</p>
+<p class="incremental">files, lines: (363, 62177)</p>
+</div>
+<div class="slide" id="where">
+<h1>Where?</h1>
+<ul class="incremental simple">
+<li>interaction with SRS (send_rhp.py, Issuer.py)</li>
+<li>interaction with SRM (MissingProductLogLoader.py)</li>
+<li>interaction with SDM (SRSTool)</li>
+<li>interaction with customers (report_tool, CheckProduct)</li>
+</ul>
+</div>
+<div class="slide" id="id1">
+<h1>RCare Internals</h1>
+<p class="incremental">or</p>
+<p class="incremental">how to write UIs</p>
+<p class="incremental">or</p>
+<p class="incremental">Real Men Don't Click! (TM)</p>
+</div>
+<div class="slide" id="examples">
+<h1>Examples</h1>
+<p class="incremental"><a class="reference" href="http://cvs2/logmanager">http://cvs2/logmanager</a></p>
+<p class="incremental"><a class="reference" href="http://simionm01:5555">http://simionm01:5555</a></p>
+<p class="incremental"><a class="reference" href="http://simionm01:8888">http://simionm01:8888</a></p>
+<p class="incremental"><a class="reference" href="http://sdm3.statpro.com:8080/rcare/issuer_manager">http://sdm3.statpro.com:8080/rcare/issuer_manager</a></p>
+</div>
+<div class="slide" id="zope-integration-1">
+<h1>Zope Integration (1)</h1>
+<p class="incremental">1 assume you have a Plone Site product</p>
+<p class="incremental">2 assume you have a Plone tool</p>
+<p class="incremental">3 assume the SitePolicy is installing the new tool</p>
+<p class="incremental">4 assume the SitePolicy is defining your users and roles</p>
+<p class="incremental">5 add a method to the Plone tool</p>
+</div>
+<div class="slide" id="zope-integration-2">
+<h1>Zope Integration (2)</h1>
+<p class="incremental">6 set the right permissions</p>
+<p class="incremental">7 make a new skins directory</p>
+<p class="incremental">8 write your template calling the tool method</p>
+<p class="incremental">9 possibly add a tab into Extensions/Install.py</p>
+<p class="incremental">10 restart the server</p>
+<p class="incremental">11 <em>regenerate</em> the Plone site</p>
+</div>
+<div class="slide" id="zope-integration-3">
+<h1>Zope Integration (3)</h1>
+<blockquote>
+... and <em>maybe</em> you are done!</blockquote>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/adwi/talk.txt b/pypers/adwi/talk.txt
new file mode 100644
index 0000000..48c92bd
--- /dev/null
+++ b/pypers/adwi/talk.txt
@@ -0,0 +1,99 @@
+RCare Internals
+-------------------
+
+:author: Michele Simionato
+:date: 2007-01-30
+
+
+ **StatPro Milan**
+
+.. include:: <s5defs.txt>
+.. footer:: StatPro Milan
+
+Some numbers
+---------------------------
+
+.. class:: incremental
+
+ files [('.py', 273), ('.pt', 62), ('.zsql', 28)]
+
+ lines [('.py', 48327), ('.pt', 12124), ('.zsql', 1726)]
+
+ files, lines: (363, 62177)
+
+Where?
+----------------------------------------
+
+.. class:: incremental
+
+- interaction with SRS (send_rhp.py, Issuer.py)
+- interaction with SRM (MissingProductLogLoader.py)
+- interaction with SDM (SRSTool)
+- interaction with customers (report_tool, CheckProduct)
+
+
+RCare Internals
+-------------------------
+
+.. class:: incremental
+
+ or
+
+ how to write UIs
+
+ or
+
+ Real Men Don't Click! (TM)
+
+
+Examples
+--------------------------
+
+.. class:: incremental
+
+ http://cvs2/logmanager
+
+ http://simionm01:5555
+
+ http://cvs2:8888
+
+ http://sdm3.statpro.com:8080/rcare/issuer_manager
+
+
+Zope Integration (1)
+-----------------------------
+
+.. class:: incremental
+
+ 1 assume you have a Plone Site product
+
+ 2 assume you have a Plone tool
+
+ 3 assume the SitePolicy is installing the new tool
+
+ 4 assume the SitePolicy is defining your users and roles
+
+ 5 add a method to the Plone tool
+
+Zope Integration (2)
+-----------------------------
+
+.. class:: incremental
+
+ 6 set the right permissions
+
+ 7 make a new skins directory
+
+ 8 write your template calling the tool method
+
+ 9 possibly add a tab into Extensions/Install.py
+
+ 10 restart the server
+
+ 11 *regenerate* the Plone site
+
+
+Zope Integration (3)
+-----------------------------
+
+ ... and *maybe* you are done!
diff --git a/pypers/adwi/ui/default/blank.gif b/pypers/adwi/ui/default/blank.gif
new file mode 100644
index 0000000..75b945d
--- /dev/null
+++ b/pypers/adwi/ui/default/blank.gif
Binary files differ
diff --git a/pypers/adwi/ui/default/slides.js b/pypers/adwi/ui/default/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/adwi/ui/default/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/all.html b/pypers/all.html
new file mode 100755
index 0000000..0d64b1f
--- /dev/null
+++ b/pypers/all.html
@@ -0,0 +1,11002 @@
+<?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>OBJECT ORIENTED PROGRAMMING IN PYTHON</title>
+<meta name="author" content="Michele Simionato" />
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<h1 class="title">OBJECT ORIENTED PROGRAMMING IN PYTHON</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>0.5</td></tr>
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference" href="mailto:mis6&#64;pitt.edu">mis6&#64;pitt.edu</a></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>
+<tr class="field"><th class="docinfo-name">Disclaimer:</th><td class="field-body">I release this book to the general public.
+It can be freely distributed if unchanged.
+As usual, I don't give any warranty: while I have tried hard to ensure the
+correctness of what follows, I disclaim any responsability in case of
+errors . Use it at your own risk and peril !</td>
+</tr>
+</tbody>
+</table>
+<div class="document" id="object-oriented-programming-in-python">
+<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="#preface" id="id49" name="id49">Preface</a><ul>
+<li><a class="reference" href="#the-philosophy-of-this-book" id="id50" name="id50">The philosophy of this book</a></li>
+<li><a class="reference" href="#for-who-this-book-in-intended" id="id51" name="id51">For who this book in intended</a></li>
+<li><a class="reference" href="#about-the-scripts-in-this-book" id="id52" name="id52">About the scripts in this book</a></li>
+<li><a class="reference" href="#conventions-used-in-this-book" id="id53" name="id53">Conventions used in this book</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#introduction" id="id54" name="id54">Introduction</a><ul>
+<li><a class="reference" href="#why-oop" id="id55" name="id55">Why OOP ?</a></li>
+<li><a class="reference" href="#why-python" id="id56" name="id56">Why Python ?</a></li>
+<li><a class="reference" href="#further-thoughts" id="id57" name="id57">Further thoughts</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#first-things-first" id="id58" name="id58">FIRST THINGS, FIRST</a><ul>
+<li><a class="reference" href="#what-s-an-object" id="id59" name="id59">What's an object?</a></li>
+<li><a class="reference" href="#objects-and-classes" id="id60" name="id60">Objects and classes</a></li>
+<li><a class="reference" href="#objects-have-attributes" id="id61" name="id61">Objects have attributes</a></li>
+<li><a class="reference" href="#objects-have-methods" id="id62" name="id62">Objects have methods</a></li>
+<li><a class="reference" href="#summing-objects" id="id63" name="id63">Summing objects</a></li>
+<li><a class="reference" href="#inspecting-objects" id="id64" name="id64">Inspecting objects</a></li>
+<li><a class="reference" href="#built-in-objects-iterators-and-generators" id="id65" name="id65">Built-in objects: iterators and generators</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-convenience-of-functions" id="id66" name="id66">THE CONVENIENCE OF FUNCTIONS</a><ul>
+<li><a class="reference" href="#id16" id="id67" name="id67">Introduction</a></li>
+<li><a class="reference" href="#a-few-useful-functions" id="id68" name="id68">A few useful functions</a></li>
+<li><a class="reference" href="#functions-are-objects" id="id69" name="id69">Functions are objects</a></li>
+<li><a class="reference" href="#profiling-functions" id="id70" name="id70">Profiling functions</a></li>
+<li><a class="reference" href="#about-python-speed" id="id71" name="id71">About Python speed</a></li>
+<li><a class="reference" href="#tracing-functions" id="id72" name="id72">Tracing functions</a></li>
+<li><a class="reference" href="#tracing-objects" id="id73" name="id73">Tracing objects</a></li>
+<li><a class="reference" href="#inspecting-functions" id="id74" name="id74">Inspecting functions</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-beauty-of-objects" id="id75" name="id75">THE BEAUTY OF OBJECTS</a><ul>
+<li><a class="reference" href="#user-defined-objects" id="id76" name="id76">User defined objects</a></li>
+<li><a class="reference" href="#objects-have-static-methods-and-classmethods" id="id77" name="id77">Objects have static methods and classmethods</a></li>
+<li><a class="reference" href="#objects-have-their-privacy" id="id78" name="id78">Objects have their privacy</a></li>
+<li><a class="reference" href="#objects-have-properties" id="id79" name="id79">Objects have properties</a></li>
+<li><a class="reference" href="#objects-have-special-methods" id="id80" name="id80">Objects have special methods</a></li>
+<li><a class="reference" href="#objects-can-be-called-added-subtracted" id="id81" name="id81">Objects can be called, added, subtracted, ...</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-power-of-classes" id="id82" name="id82">THE POWER OF CLASSES</a><ul>
+<li><a class="reference" href="#the-concept-of-inheritance" id="id83" name="id83">The concept of inheritance</a></li>
+<li><a class="reference" href="#inheritance-versus-run-time-class-modifications" id="id84" name="id84">Inheritance versus run-time class modifications</a></li>
+<li><a class="reference" href="#inheriting-from-built-in-types" id="id85" name="id85">Inheriting from built-in types</a></li>
+<li><a class="reference" href="#controlling-the-creation-of-objects" id="id86" name="id86">Controlling the creation of objects</a></li>
+<li><a class="reference" href="#multiple-inheritance" id="id87" name="id87">Multiple Inheritance</a></li>
+<li><a class="reference" href="#cooperative-hierarchies" id="id88" name="id88">Cooperative hierarchies</a></li>
+<li><a class="reference" href="#inheritance-and-privacy" id="id89" name="id89">Inheritance and privacy</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-sophistication-of-descriptors" id="id90" name="id90">THE SOPHISTICATION OF DESCRIPTORS</a><ul>
+<li><a class="reference" href="#motivation" id="id91" name="id91">Motivation</a></li>
+<li><a class="reference" href="#functions-versus-methods" id="id92" name="id92">Functions versus methods</a></li>
+<li><a class="reference" href="#methods-versus-functions" id="id93" name="id93">Methods versus functions</a></li>
+<li><a class="reference" href="#static-methods-and-class-methods" id="id94" name="id94">Static methods and class methods</a></li>
+<li><a class="reference" href="#properties" id="id95" name="id95">Properties</a></li>
+<li><a class="reference" href="#user-defined-attribute-descriptors" id="id96" name="id96">User-defined attribute descriptors</a></li>
+<li><a class="reference" href="#data-descriptors" id="id97" name="id97">Data descriptors</a></li>
+<li><a class="reference" href="#the-super-attribute-descriptor" id="id98" name="id98">The <tt class="literal"><span class="pre">super</span></tt> attribute descriptor</a></li>
+<li><a class="reference" href="#method-wrappers" id="id99" name="id99">Method wrappers</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-subtleties-of-multiple-inheritance" id="id100" name="id100">THE SUBTLETIES OF MULTIPLE INHERITANCE</a><ul>
+<li><a class="reference" href="#a-little-bit-of-history-why-python-2-3-has-changed-the-mro" id="id101" name="id101">A little bit of history: why Python 2.3 has changed the MRO</a></li>
+<li><a class="reference" href="#the-c3-method-resolution-order" id="id102" name="id102">The C3 Method Resolution Order</a></li>
+<li><a class="reference" href="#examples" id="id103" name="id103">Examples</a></li>
+<li><a class="reference" href="#bad-method-resolution-orders" id="id104" name="id104">Bad Method Resolution Orders</a></li>
+<li><a class="reference" href="#understanding-the-method-resolution-order" id="id105" name="id105">Understanding the Method Resolution Order</a></li>
+<li><a class="reference" href="#counting-instances" id="id106" name="id106">Counting instances</a></li>
+<li><a class="reference" href="#the-pizza-shop-example" id="id107" name="id107">The pizza-shop example</a></li>
+<li><a class="reference" href="#fixing-wrong-hierarchies" id="id108" name="id108">Fixing wrong hierarchies</a></li>
+<li><a class="reference" href="#modifying-hierarchies" id="id109" name="id109">Modifying hierarchies</a></li>
+<li><a class="reference" href="#inspecting-python-code" id="id110" name="id110">Inspecting Python code</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-magic-of-metaclasses-part-i" id="id111" name="id111">THE MAGIC OF METACLASSES - PART I</a><ul>
+<li><a class="reference" href="#metaclasses-as-class-factories" id="id112" name="id112">Metaclasses as class factories</a></li>
+<li><a class="reference" href="#metaclasses-as-class-modifiers" id="id113" name="id113">Metaclasses as class modifiers</a></li>
+<li><a class="reference" href="#a-few-caveats-about-the-usage-of-metaclasses" id="id114" name="id114">A few caveats about the usage of metaclasses</a></li>
+<li><a class="reference" href="#metaclasses-and-inheritance" id="id115" name="id115">Metaclasses and inheritance</a></li>
+<li><a class="reference" href="#conflicting-metaclasses" id="id116" name="id116">Conflicting metaclasses</a></li>
+<li><a class="reference" href="#cooperative-metaclasses" id="id117" name="id117">Cooperative metaclasses</a></li>
+<li><a class="reference" href="#metamethods-vs-class-methods" id="id118" name="id118">Metamethods vs class methods</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-magic-of-metaclasses-part-2" id="id119" name="id119">THE MAGIC OF METACLASSES - PART 2</a><ul>
+<li><a class="reference" href="#the-secrets-of-the-metaclass-hook" id="id120" name="id120">The secrets of the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook</a></li>
+<li><a class="reference" href="#anonymous-inner-metaclasses" id="id121" name="id121">Anonymous inner metaclasses</a></li>
+<li><a class="reference" href="#passing-parameters-to-meta-classes" id="id122" name="id122">Passing parameters to (meta) classes</a></li>
+<li><a class="reference" href="#meta-functions" id="id123" name="id123">Meta-functions</a></li>
+<li><a class="reference" href="#anonymous-cooperative-super-calls" id="id124" name="id124">Anonymous cooperative super calls</a></li>
+<li><a class="reference" href="#more-on-metaclasses-as-class-factories" id="id125" name="id125">More on metaclasses as class factories</a></li>
+<li><a class="reference" href="#programming-with-metaclasses" id="id126" name="id126">Programming with metaclasses</a></li>
+<li><a class="reference" href="#metaclass-aided-operator-overloading" id="id127" name="id127">Metaclass-aided operator overloading</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#advanced-metaprogramming-techniques" id="id128" name="id128">ADVANCED METAPROGRAMMING TECHNIQUES</a><ul>
+<li><a class="reference" href="#on-code-processing" id="id129" name="id129">On code processing</a></li>
+<li><a class="reference" href="#regular-expressions" id="id130" name="id130">Regular expressions</a></li>
+<li><a class="reference" href="#more-on-metaclasses-and-subclassing-built-in-types" id="id131" name="id131">More on metaclasses and subclassing built-in types</a></li>
+<li><a class="reference" href="#a-simple-state-machine" id="id132" name="id132">A simple state machine</a></li>
+<li><a class="reference" href="#creating-classes" id="id133" name="id133">Creating classes</a></li>
+<li><a class="reference" href="#modifying-modules" id="id134" name="id134">Modifying modules</a></li>
+<li><a class="reference" href="#metaclasses-and-attribute-descriptors" id="id135" name="id135">Metaclasses and attribute descriptors</a></li>
+<li><a class="reference" href="#id46" id="id136" name="id136">Modifying hierarchies</a></li>
+<li><a class="reference" href="#tracing-hierarchies" id="id137" name="id137">Tracing hierarchies</a></li>
+<li><a class="reference" href="#modifying-source-code" id="id138" name="id138">Modifying source code</a></li>
+<li><a class="reference" href="#metaclass-regenerated-hierarchies" id="id139" name="id139">Metaclass regenerated hierarchies</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-programmable-programming-language" id="id140" name="id140">THE PROGRAMMABLE PROGRAMMING LANGUAGE</a><ul>
+<li><a class="reference" href="#enhancing-the-python-language" id="id141" name="id141">Enhancing the Python language</a></li>
+<li><a class="reference" href="#restricting-python-dynamism" id="id142" name="id142">Restricting Python dynamism</a></li>
+<li><a class="reference" href="#changing-the-language-without-changing-the-language" id="id143" name="id143">Changing the language without changing the language</a></li>
+<li><a class="reference" href="#recognizing-magic-comments" id="id144" name="id144">Recognizing magic comments</a></li>
+<li><a class="reference" href="#interpreting-python-source-code-on-the-fly" id="id145" name="id145">Interpreting Python source code on the fly</a></li>
+<li><a class="reference" href="#implementing-lazy-evaluation" id="id146" name="id146">Implementing lazy evaluation</a></li>
+<li><a class="reference" href="#implementing-a-ternary-operator" id="id147" name="id147">Implementing a ternary operator</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="preface">
+<h1><a class="toc-backref" href="#id49" name="preface">Preface</a></h1>
+<blockquote>
+<pre class="line-block">
+<em>There is only one way to learn: trough examples</em>
+</pre>
+</blockquote>
+<div class="section" id="the-philosophy-of-this-book">
+<h2><a class="toc-backref" href="#id50" name="the-philosophy-of-this-book">The philosophy of this book</a></h2>
+<p>This book is written with the intent to help the programmer going trough
+the fascinating concepts of Object Oriented Programming (OOP), in their
+Python incarnation. Notice that I say to help, not to teach. Actually,
+I do not think that a book can teach OOP or any other non-trivial matter
+in Computer Science or other disciplines. Only the
+practice can teach: practice, then practice, and practice again.
+You must learn yourself from your experiments, not from the books.
+Nevertheless, books are useful. They cannot teach, but they can help.
+They should give you new ideas that you was not thinking about, they should
+show tricks you do not find in the manual, and in general they should be of
+some guidance in the uphill road to knowledge. That is the philosophy
+of this book. For this reason</p>
+<p>1. It is not comprehensive, not systematic;
+it is intended to give ideas and basis: from
+that the reader is expected to cover the missing part on his own,
+browsing the documentation, other sources and other books, and finally
+the definite autority, the source itself.</p>
+<p>2. It will not even try to teach the <em>best</em> practices. I will show what you can
+do with Python, not what you &quot;should&quot; do. Often I will show solutions that are
+not recommended. I am not a mammy saying this is
+good, this is bad, do this do that.</p>
+<p>3. You can only learn from your failures. If you think &quot;it should work, if I do
+X and Y&quot; and it works, then you have learned nothing new.
+You have merely verified
+that your previous knowledge was correct, but you haven't create a new
+knowledge. On the other hand, when you think &quot;it should work, if I do
+X and Y&quot; and it doesn't, then you have learned that your previous knowlegde
+was wrong or incomplete, and you are forced to learn something new to
+overcome the difficulty. For this reason, I think it is useful to report
+not only how to do something, but also to report how not to do something,
+showing the pitfalls of wrong approaches.</p>
+<p>That's in my opinion is the goal of a good book. I don't know if have
+reached this goal or not (the decision is up to the reader), but at least
+I have tried to follow these guidelines.</p>
+<p>Moreover, this is not a book on OOP,
+it is a book on OOP <em>in Python</em>.</p>
+<p>In other words, the point of view of this book is not
+to emphasize general topics of OOP that are exportable to other languages,
+but exactly the opposite: I want to emphasize specific techniques that one
+can only use in Python, or that are difficult to translate to other
+languages. Moreover, I will not provide comparisons with other
+languages (except for the section &quot;Why Python?&quot; in this introduction and
+in few selected other places),
+in order to keep the discussion focused.</p>
+<p>This choice comes from the initial motivation for this book, which was
+to fulfill a gap in the (otherwise excellent) Python documentation.
+The problem is that the available documentation still lacks an accessible
+reference of the new Python 2.2+ object-oriented features.
+Since myself I have learned Python and OOP from scratch,
+I have decided to write this book in order to fill that gap and
+help others.</p>
+<p>The emphasis in this book is not in giving
+solutions to specific problems (even if most of the recipes of this book
+can easily be tailored to solve real life concrete problems), it is in
+teaching how does it work, why it does work in some cases and why does
+not work in some other cases. Avoiding too specific problems has an
+additional bonus, since it allows me to use <em>short</em> examples (the majority
+of the scripts presented here is under 20-30 lines) which I think are
+best suited to teach a new matter <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a> . Notice, however, that whereas
+the majority of the scripts in this book are short, it is also true
+that they are pretty <em>dense</em>. The density is due to various reasons:</p>
+<ol class="arabic simple">
+<li>I am defining a lot of helper functions and classes, that are
+reused and enhanced during all the book.</li>
+<li>I am doing a strong use of inheritance, therefore a script at the
+end of the book can inherits from the classes defined through all
+the book;</li>
+<li>A ten line script involving metaclasses can easily perform the equivalent
+of generating hundreds of lines of code in a language without metaclasses
+such as Java or C++.</li>
+</ol>
+<p>To my knowledge, there are no other books covering the same topics with
+the same focus (be warned, however, that I haven't read so many Python
+books ;-). The two references that come closest to the present book are
+the <tt class="literal"><span class="pre">Python</span> <span class="pre">Cookbook</span></tt> by Alex Martelli and David Ascher, and
+Alex Martelli's <tt class="literal"><span class="pre">Python</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">Nutshell</span></tt>. They are quite recent books and
+therefore it covers (in much less detail) some of the 2.2 features that are
+the central topics to this book.
+However, the Cookbook reserves to OOP only one chapter and has a quite
+different philosophy from the present book, therefore there is
+practically no overlapping. Also <tt class="literal"><span class="pre">Python</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">Nutshell</span></tt> covers
+metaclasses in few pages, whereas half of this book is essentially
+dedied to them. This means that you can read both ;-)</p>
+<table class="footnote" frame="void" id="id2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>Readers that prefer the opposite philosophy of using longer,
+real life-like, examples, have already the excellent &quot;Dive into
+Python&quot; book <a class="reference" href="http://diveintopython.org/">http://diveintopython.org/</a> at their disposal. This is
+a very good book that I certainly recommend to any (experienced)
+Python programmer; it is also freely available (just like this ;-).
+However, the choice of arguments is quite different and there is
+essentially no overlap between my book and &quot;Dive into Python&quot;
+(therefore you can read both ;-).</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="for-who-this-book-in-intended">
+<h2><a class="toc-backref" href="#id51" name="for-who-this-book-in-intended">For who this book in intended</a></h2>
+<p>I have tried to make this tutorial useful to a large public of Pythonistas,
+i.e. both people with no previous experience of Object Oriented Programming
+and people with experience on OOP, but unfamiliar with the most
+recent Python 2.2-2.3 features (such as attribute descriptors,
+metaclasses, change of the MRO in multiple inheritance, etc).
+However, this is not a book for beginners: the non-experienced reader should
+check (at least) the Internet sites www.python.org/newbies.com and
+www.awaretek.com, that provide a nice collection of resources for Python
+newbies.</p>
+<p>These are my recommendations for the reader, according to her/his level:</p>
+<ol class="arabic simple">
+<li>If you are an absolute beginner, with no experience on programming,
+this book is <em>not</em> for you (yet ;-). Go to
+<a class="reference" href="http://www.python.org/doc/Newbies.html">http://www.python.org/doc/Newbies.html</a> and read one of the introductive
+texts listed there, then come back here. I recommend &quot;How to Think Like
+a Computer Scientist&quot;, available for free on the net (see
+<a class="reference" href="http://www.ibiblio.org/obp/thinkCSpy/">http://www.ibiblio.org/obp/thinkCSpy/</a>); I found it useful myself when
+I started learning Python; be warned, however, that it refers to the rather
+old Python version 1.5.2. There are also excellent books
+on the market (see <a class="reference" href="http://www.awaretek.com/plf.html">http://www.awaretek.com/plf.html</a>).
+<a class="reference" href="http://www.uselesspython.com/">http://www.uselesspython.com/</a> is a good resource to find recensions
+about available Python books. For free books, look at
+<a class="reference" href="http://www.tcfb.com/freetechbooks/bookphyton.html">http://www.tcfb.com/freetechbooks/bookphyton.html</a> .
+This is <em>not</em> another Python tutorial.</li>
+<li>If you know already (at least) another programming language, but you don't
+know Python, then this book is <em>not</em> for you (again ;-). Read the FAQ, the
+Python Tutorial and play a little with the Standard Library (all this
+material can be downloaded for free from <a class="reference" href="http://www.python.org">http://www.python.org</a>), then
+come back here.</li>
+<li>If you have passed steps 1 and 2, and you are confortable with Python
+at the level of simple procedural programming, but have no clue about
+objects and classes, <em>then</em> this book is for you. Read this book till
+the end and your knowledge of OOP will pass from zero to a quite advanced
+level (hopefully). Of course, you will have to play with the code in
+this book and write a lot of code on your own, first ;-)</li>
+<li>If you are confortable with Python and you also known OOP from other
+languages or from earlier version of Python, then this book is for
+you, too: you are ready to read the more advanced chapters.</li>
+<li>If you are a Python guru, then you should read the book, too. I expect
+you will find the errors and send me feedback, helping me to improve
+this tutorial.</li>
+</ol>
+</div>
+<div class="section" id="about-the-scripts-in-this-book">
+<h2><a class="toc-backref" href="#id52" name="about-the-scripts-in-this-book">About the scripts in this book</a></h2>
+<p>All the scripts in this book are free. You are expected to play
+with them, to modify them and to improve them.</p>
+<p>In order to facilitate the extraction of the scripts from the main text, both
+visually for the reader and automatically for Python, I use the
+convention of sandwiching the body of the example scripts in blocks like this</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;myfirstscript.py&gt;
+
+print &quot;Here Starts the Python Way to Object Oriented Programming !&quot;
+
+#&lt;/myfirstscript.py&gt;
+</pre>
+</blockquote>
+<p>You may extract the source of this script with the a Python program
+called &quot;test.py&quot; and provided in the distribution. Simply give the
+following command:</p>
+<blockquote>
+<pre class="literal-block">
+$ python test.py myfirstscript.py
+</pre>
+</blockquote>
+<p>This will create a file called &quot;myfirstscript.py&quot;, containing the
+source of <tt class="literal"><span class="pre">myfirstscript.py</span></tt>; moreover it will execute the script
+and write its output in a file called &quot;output.txt&quot;. I have tested
+all the scripts in this tutorial under Red Hat Linux 7.x and
+Windows 98SE. You should not have any problem in running them,
+but if a problem is there, &quot;test.py&quot; will probably discover it,
+even if, unfortunately, it will not provide the solution :-(.
+Notice that test.py requires Python 2.3+ to work, since most of
+the examples in this book heavily depends on the new features
+introduced in Python 2.2-2.3. Since the installation of Python
+2.3 is simple, quick and free, I think I am requiring to my readers
+who haven't upgraded yet a very little effort. This is well worth
+the pain since Python 2.3 fixes few bugs of 2.2 (notably in the subject of
+attribute descriptors and the <tt class="literal"><span class="pre">super</span></tt> built-in) that makes</p>
+<p>You may give more arguments to test.py, as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+$ python test.py myfirstscript.py mysecondscript.py
+</pre>
+</blockquote>
+<p>The output of both scripts will still be placed in the file &quot;output.txt&quot;.
+Notice that if you give an argument which is not the name of a script in the
+book, it will be simply ignored. Morever, if you will not give any argument,
+&quot;test.py&quot; will automatically executes all the tutorial scripts, writing their
+output in &quot;output.txt&quot; <a class="footnote-reference" href="#id4" id="id3" name="id3">[2]</a> . You may want to give a look at this file, once
+you have finished the tutorial. It also contains the source code of
+the scripts, for better readability.</p>
+<p>Many examples of this tutorial depend on utility functions defined
+in a external module called <tt class="literal"><span class="pre">oopp</span></tt> (<tt class="literal"><span class="pre">oopp</span></tt> is an obvious abbreviation
+for the title of the tutorial). The module <tt class="literal"><span class="pre">oopp</span></tt> is automatically generated
+by &quot;test.py&quot;, which works by extracting from the tutorial
+text blocks of code of the form <tt class="literal"><span class="pre">#&lt;oopp.py&gt;</span> <span class="pre">something</span> <span class="pre">#&lt;/oopp.py&gt;</span></tt>
+and saving them in a file called &quot;oopp.py&quot;.
+Let me give an example. A very recent enhancement to Python (in
+Python 2.3) has been the addition of a built-in boolean type with
+values True and False:</p>
+<blockquote>
+<pre class="literal-block">
+$ python
+Python 2.3a1 (#1, Jan 6 2003, 10:31:14)
+[GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)] on linux2
+Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
+&gt;&gt;&gt; 1+1==2
+True
+&gt;&gt;&gt; 1+1==3
+False
+&gt;&gt;&gt; type(True)
+&lt;type 'bool'&gt;
+&gt;&gt;&gt; type(False)
+&lt;type 'bool'&gt;
+</pre>
+</blockquote>
+<p>However, previous version of Python use the integers 1 and 0 for
+True and False respectively.</p>
+<blockquote>
+<pre class="literal-block">
+$ python
+Python 2.2 (#1, Apr 12 2002, 15:29:57)
+[GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-109)] on linux2
+Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
+&gt;&gt;&gt; 1+1==2
+1
+&gt;&gt;&gt; 1+1==3
+0
+</pre>
+</blockquote>
+<p>Following the 2.3 convension, in this tutorial I will use the names
+<tt class="literal"><span class="pre">True</span></tt> and <tt class="literal"><span class="pre">False</span></tt> to denotes the numbers 1 and 0 respectively.
+This is automatic in Python 2.2.1+, but not in Python 2.2. Therefore,
+for sake of compatibility, it is convenient to set the values <tt class="literal"><span class="pre">True</span></tt>
+and <tt class="literal"><span class="pre">False</span></tt> in our utility module:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import __builtin__
+try:
+ __builtin__.True #look if True is already defined
+except AttributeError: # if not add True and False to the builtins
+ __builtin__.True = 1
+ __builtin__.False = 0
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;mysecondscript.py&gt;
+
+import oopp
+print &quot;True =&quot;,True,
+print &quot;False =&quot;,False
+
+#&lt;/mysecondscript.py&gt;
+</pre>
+</blockquote>
+<p>The output is &quot;True = 1 False = 0&quot; under Python 2.2 and
+&quot;True = True False = False&quot; under Python 2.3+.</p>
+<table class="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="#id3" name="id4">[2]</a></td><td>&quot;test.py&quot;, invoked without arguments, does not create '.py' files,
+since I don't want to kludge the distribution with dozens of ten-line
+scripts. I expect you may want to save only few scripts as standalone
+programs, and cut and paste the others.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="conventions-used-in-this-book">
+<h2><a class="toc-backref" href="#id53" name="conventions-used-in-this-book">Conventions used in this book</a></h2>
+<p>Python expressions are denoted with monospaced fonts when in the text.
+Sections marked with an asterisk can be skipped in a first reading.
+Typically they have the purpose of clarifying some subtle point and
+are not needed for the rest of the book. These sections are intended
+for the advanced reader, but could confuse the beginner.
+An example is the section about the difference between methods and
+functions, or the difference between the inheritance constraint and
+the metaclass constraint.</p>
+</div>
+</div>
+<div class="section" id="introduction">
+<h1><a class="toc-backref" href="#id54" name="introduction">Introduction</a></h1>
+<blockquote>
+<pre class="line-block">
+<em>A language that doesn't affect the way you think about programming,
+is not worth knowing.</em> -- Alan Perlis
+</pre>
+</blockquote>
+<div class="section" id="why-oop">
+<h2><a class="toc-backref" href="#id55" name="why-oop">Why OOP ?</a></h2>
+<p>I guess some of my readers, like me, have started programming in the mid-80's,
+when traditional (i.e. non object-oriented) Basic and Pascal where popular as
+first languages. At the time OOP was not as pervasive in software development
+how it is now, most of the mainstream languages were non-object-oriented and
+C++ was just being released. That was a time when the transition from
+spaghetti-code to structured code was already well accomplished, but
+the transition from structured programming to (the first phase of)
+OOP was at the beginning.</p>
+<p>Nowaydays, we live in a similar time of transition . Today, the transition
+to (the first phase of) OOP is well accomplished and essentially all
+mainstream
+languages support some elementary form of OOP. To be clear, when I say
+mainstream langauges, I have in mind Java and C++: C is a remarkable
+exception to the rule, since it is mainstream but not object-oriented.</p>
+<p>However, both Java an C++ (I mean standard Java and C++, not special
+extension like DTS C++, that have quite powerful object oriented features)
+are quite poor object-oriented languages: they provides only the most
+elementary aspects of OOP, the features of the <em>first phase</em> of OOP.</p>
+<p>Hence, today the transition to the <em>second phase</em> of OOP is only at the
+beginning, i.e mainstream language are not yet really OO, but they will
+become OOP in the near future.</p>
+<p>By second phase of OOP I mean the phase in which the primary
+objects of concern for the programmer are no more the objects, but the
+metaobjects. In elementary OOP one works on objects, which have attributes
+and methods (the evolution of old-fashioned data and functions) defined
+by their classes; in the second phase of OOP one works on classes
+which behavior is described by metaclasses. We no more modify objects
+trough classes: nowadays we modify classes and class hierarchies
+through metaclasses and multiple inheritance.</p>
+<p>It would be tempting to represent the history of programming in the last
+quarter of century with an evolutionary table like that:</p>
+<table border class="table">
+<colgroup>
+<col width="33%" />
+<col width="27%" />
+<col width="30%" />
+<col width="10%" />
+</colgroup>
+<thead valign="bottom">
+<tr><th>~1975</th>
+<th>~1985</th>
+<th>~1995</th>
+<th>~2005</th>
+</tr>
+</thead>
+<tbody valign="top">
+<tr><td>procedural programming</td>
+<td>OOP1</td>
+<td>OOP2</td>
+<td>?</td>
+</tr>
+<tr><td>data,functions</td>
+<td>objects,classes</td>
+<td>classes,metaclasses</td>
+<td>?</td>
+</tr>
+</tbody>
+</table>
+<p>The problem is that table would be simply wrong, since in truth
+Smalltalk had metaclasses already 25 years ago! And also Lisp
+had <em>in nuce</em> everything a long <em>long</em> time ago.
+The truth is that certains languages where too much ahead of their
+time ;-)</p>
+<p>Therefore, today we already have all the ideas
+and the conceptual tools to go beyond the first phase of OOP
+(they where invented 20-30 years ago), nevertheless those ideas are
+not yet universally known, nor implemented in mainstream languages.</p>
+<p>Fortunately, there are good languages
+where you can access the bonus of the second phase of OOP (Smalltalk, CLOS,
+Dylan, ...): unfortunately
+most of them are academic and/or little known in the real world
+(often for purely commercial reasons, since typically languages are not
+chosen accordingly to their merits, helas!). Python is an exception to this
+rule, in the sense that it is an eminently practical language (it started
+as a scripting language to do Operating System administrative jobs),
+which is relatively known and used in that application niche (even if some
+people <em>wrongly</em> think that should not be used for 'serious' things).</p>
+<p>There are various reasons why most mainstream languages are rather
+poor languages, i.e. underfeatured languages (as Java) or powerful, but too
+tricky to use, as C++. Some are good reasons (for instance <em>efficiency</em>: if
+efficiency is the first concern, then poor languages can be much
+better suited to the goal: for instance Fortran for number crunching
+and C for system programming), some are less good (economical
+monopoly). There is nothing to do against these reasons: if you
+need efficiency, or if you are forced to use a proprietary language
+because it is the language used by your employer. However, if you
+are free from these restrictions, there is another reason why you
+could not choose to use a poweful language. The reason is that,
+till now, programmers working in the industrial world mostly had simple
+problems (I mean conceptually simple problems). In order to solve
+simple problems one does not need a powerful language, and the effort
+spent in learning it is not worth.</p>
+<p>However, nowadays the situations has changed. Now, with Internet and graphics
+programming everywhere, and object-oriented languages so widespread,
+now it is the time when actually people <em>needs</em> metaprogramming, the
+ability to changing classes and programs. Now everybody is programming
+in the large.</p>
+<p>In this situation, it is justified to spend some time to learn better
+way of programming. And of course, it is convenient to start from
+the language with the flattest learning curve of all.</p>
+</div>
+<div class="section" id="why-python">
+<h2><a class="toc-backref" href="#id56" name="why-python">Why Python ?</a></h2>
+<blockquote>
+<pre class="line-block">
+<em>In many ways, it's a dull language, borrowing solid old concepts from
+many other languages &amp; styles: boring syntax, unsurprising semantics,
+few automatic coercions, etc etc. But that's one of the things I like
+about it.</em> --Tim Peters on Python, 16 Sep 93
+</pre>
+</blockquote>
+<p>If you are reading this book, I assume you already have some experience
+with Python. If this is the case, you already know the obvious advantages
+of Python such as readability, easy of use and short development time.
+Nevertheless, you could only have used Python as a fast and simple
+scripting language. If you are in this situation, then your risk to
+have an incorrect opinion on the language like &quot;it is a nice little
+language, but too simple to be useful in 'real' applications&quot;. The
+truth is that Python is designed to be <em>simple</em>, and actually it
+is; but by no means it is a &quot;shallow&quot; language. Actually, it goes
+quite <em>deep</em>, but it takes some time to appreciate this fact.</p>
+<p>Let me contrast Python with Lisp, for instance. From the beginning,
+Lisp was intended to be a language for experts, for people with difficult
+problems to solve. The first
+users of Lisp were academicians, professors of CS and scientists.
+On the contrary, from the beginning Python
+was intended to be language for everybody (Python predecessor was ABC,
+a language invented to teach CS to children). Python makes great a first
+language for everybody, whereas Lisp would require especially
+clever and motivated students (and we all know that there is lack
+of them ;-)</p>
+<p>From this difference of origins, Python inherits an easy to learn syntax,
+whereas Lisp syntax is horrible for the beginner (even if not as
+horrible as C++ syntax ;-)</p>
+<blockquote>
+<pre class="line-block">
+<em>Macros are a powerful extension to weak languages.
+Powerful languages don't need macros by definition.</em>
+-- Christian Tismer on c.l.p. (referring to C)
+</pre>
+</blockquote>
+<p>Despite the differences, Python borrows quite a lot from Lisp and it
+is nearly as expressive as it (I say nearly since Python is
+not as powerful as Lisp: by tradition, Lisp has always been on the top of
+hierarchy of programming language with respect to power of abstraction).
+It is true that Python lacks some powerful Lisp features: for instance
+Python object model lacks multiple dispatching (for the time being ;-)
+and the language lacks Lisp macros (but this unlikely to change in the
+near future since Pythonistas see the lack of macro as a Good Thing <a class="footnote-reference" href="#id6" id="id5" name="id5">[3]</a>):
+nevertheless, the point is that Python is much <em>much</em> easier to learn.
+You have (nearly) all the power, but without the complexity.</p>
+<p>One of the reasons, is that Python
+try to be as <em>less</em> innovative as
+possible: it takes the proven good things from others, more innovative
+languages, and avoids their pitfalls. If you are an experienced
+programmer , it will be even easier to you to learn Python, since
+there is more or less nothing which is really original to Python.
+For instance:</p>
+<ol class="arabic simple">
+<li>the object model is took from languages that are good at it, such
+as Smalltalk;</li>
+<li>multiple inheritance has been modeled from languages good in it. such
+as CLOS and Dylan;</li>
+<li>regular expression follows the road opened by Perl;</li>
+<li>functional features are borrowed from functional languages;</li>
+<li>the idea of documentation strings come from Lisp;</li>
+<li>list comprehension come from Haskell;</li>
+<li>iterators and generators come from Icon;</li>
+<li>etc. etc. (many other points here)</li>
+</ol>
+<p>I thinks the really distinctive feature of Python with respect to
+any other serious language I know, is that Python is <em>easy</em>. You have the
+power (I mean power in conceptual sense, not computational power: in
+the sense of computational power the best languages are
+non-object-oriented ones)
+of the most powerful languages with a very little investement.
+In addition to that, Python has a relatively large user base
+(as compared to Smalltalk or Ruby, or the various fragmented Lisp
+communities). Of course,
+there is quite a difference between the user base of Python with
+respect to the user base of, let say, VisualBasic or Perl. But
+I would never take in consideration VisualBasic for anything serious,
+whereas Perl is too ugly for my taste ;-).
+Finally, Python is <em>practical</em>. With this I mean the fact that
+Python has libraries that
+allow the user to do nearly everything, since you can access all the C/C++
+libraries with little or no effort, and all the Java libraries, though the
+Python implementation known as Jython. In particular, one has the choice
+between many excellent GUI's trough PyQt, wxPython, Tkinter, etc.</p>
+<p>Python started as an Object Oriented Programming
+Languages from the beginning, nevertheless is was never intended to be
+a <em>pure</em> OOPL as SmallTalk or, more recently, Ruby. Python is a
+<em>multiparadigm</em>
+language such a Lisp, that you choose your programming style according
+to your problem: spaghetti-code, structured programming, functional
+programming, object-oriented programming are all supported. You can
+even write bad code in Python, even if it is less simple than in other
+languages ;-). Python is a language which has quite evolved in its twelve
+years of life (the first public release was released in February 1991)
+and many new features have been integrated in the language with time.
+In particular, Python 2.2 (released in 2002) was a major breakthrough
+in the history of the language
+for what concerns support to Object Oriented Programming (OOP).
+Before the 2.2 revolution, Python Object
+Orientation was good; now it is <em>excellent</em>. All the fundamental features
+of OOP, including pretty sophisticated ones, as metaclasses and multiple
+inheritance, have now a very good support (the only missing thing is
+multiple dispatching).</p>
+<table class="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="#id5" name="id6">[3]</a></td><td>Python lacks macros for an intentional design choice: many people
+in the community (including Guido itself) feel that macros are
+&quot;too powerful&quot;. If you give the user the freedom to create her
+own language, you must face at least three problems: i) the risk
+to split the original language in dozens of different dialects;
+ii) in collaborative projects, the individual programmer must
+spend an huge amount of time and effort would be spent in learning
+macro systems written by others; iii) not all users are good
+language designers: the programmer will have to fight with badly
+designed macro systems. Due to these problems, it seems unlikely
+that macros will be added to Python in the future.</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id7" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id8" name="id7">[4]</a></td><td>For a good comparison between Python and Lisp I remind the reader to
+the excellent Peter Norvig's article in
+<a class="reference" href="http://www.norvig.com/python-lisp.html">http://www.norvig.com/python-lisp.html</a></td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="further-thoughts">
+<h2><a class="toc-backref" href="#id57" name="further-thoughts">Further thoughts</a></h2>
+<p>Actually, the principal reasons why I begun studying
+Python was the documentation and the newsgroup: Python has an outstanding
+freely available documentation and an incredibly helpful newsgroup that
+make extremely easy to learn the language. If I had found a comparable
+free documentation/newsgroup for C++ or Lisp, I would have studied that
+languages instead.</p>
+<p>Unfortunately, the enormous development at the software level, had no
+correspondence with with an appropriate development of documentation.
+As a consequence, the many beatiful, powerful and extremely <em>useful</em>
+new features of Python 2.2+ object orientation are mostly remained
+confined to developers and power users: the average Python programmer
+has remained a little a part from the rapid development and she
+<em>wrongly</em> thinks she has no use for the new features. There have
+also been <em>protestations</em> of the users against developers of the
+kind &quot;please, stop adding thousands of complicated new extensions
+to the language for which we have no use&quot; !</p>
+<p>Extending a language is always a delicate thing to do, for a whole
+bunch of reasons:</p>
+<ol class="arabic simple">
+<li>once one extension is done, it is there <em>forever</em>.</li>
+</ol>
+<p>My experience has been the following.</p>
+<p>When I first read about metaclasses, in Guido's essay
+&quot;Unifying types and classes in Python 2.2&quot;, I thought &quot;Wow,
+classes of classes, cool concept, but how useful is it?
+Are metaclasses really providing some new functionality?
+What can I do with metaclasses that I cannot do without?&quot;</p>
+<p>Clearly, in these terms, the question is rather retorical, since in principle
+any Turing-complete programming languages contains all the features provided
+by metaclasses. Python metaclasses themselves are implemented in C, that has
+no metaclasses. Therefore, my real question was not &quot;What can I do
+with metaclasses that I cannot do without?&quot; but &quot;How big is the convenience
+provided by metaclasses, with respect to my typical applications?&quot;.</p>
+<p>The answer depends on the kind of problem you are considering. For certain
+classes of problems it can be <em>very</em> large, as I will show in this and in
+the next chapters.</p>
+<p>I think the biggest advantage of metaclasses is <em>elegance</em>. Altough it
+is true that most of what you can do with metaclasses, can be done without
+metaclasses, not using metaclasses can result in a much <em>uglier</em> solution.</p>
+<p>One needs difficult problems in order to appreciate the advantage
+of powerful methods.</p>
+<p>If all you need is to write few scripts for copying two or three files,
+there is no point in learning OOP.On the other hand, if you only
+write simple programs where you define only one of two classes, there
+is no point in using metaclasses. Metaclasses becomes relevant only
+when you have many classes, whole classes of classes with similar
+features that you want to modify.</p>
+<p>In this sense, metaprogramming is for experts only, i.e. with people
+with difficult problems. The point however, is that nowaydays,
+many persons have difficult problems.</p>
+<p>Finally, let me conclude this preface by recalling the
+gist of Python wisdom.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import this
+The Zen of Python, by Tim Peters
+.
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it.
+Although that way may not be obvious at first unless you're Dutch.
+Now is better than never.
+Although never is often better than *right* now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!
+</pre>
+</blockquote>
+</div>
+</div>
+<div class="section" id="first-things-first">
+<h1><a class="toc-backref" href="#id58" name="first-things-first">FIRST THINGS, FIRST</a></h1>
+<p>This is an introductory chapter, with the main purpose of fixing the
+terminology used in the sequel. In particular, I give the definitions
+of objects, classes, attributes and methods. I discuss a few examples
+and I show some of the most elementary Python introspection features.</p>
+<div class="section" id="what-s-an-object">
+<h2><a class="toc-backref" href="#id59" name="what-s-an-object">What's an object?</a></h2>
+<blockquote>
+<pre class="line-block">
+<em>So Everything Is An object.
+I'm sure the Smalltalkers are very happy :)</em>
+
+-- Michael Hudson on comp.lang.python
+</pre>
+</blockquote>
+<p>&quot;What's an object&quot; is the obvious question raised by anybody starting
+to learn Object Oriented Programming. The answer is simple: in Python,
+everything in an object!</p>
+<p>An operative definition is the following: an <em>object</em>
+is everything that can be labelled with an <em>object reference</em>.</p>
+<p>In practical terms, the object reference is implemented as
+the object memory address, that is an integer number which uniquely
+specify the object. There is a simple way to retrieve the object reference:
+to use the builtin <tt class="literal"><span class="pre">id</span></tt> function. Informations on <tt class="literal"><span class="pre">id</span></tt> can be retrieved
+via the <tt class="literal"><span class="pre">help</span></tt> function <a class="footnote-reference" href="#id7" id="id8" name="id8">[4]</a>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; help(id)
+Help on built-in function id:
+id(...)
+id(object) -&gt; integer
+Return the identity of an object. This is guaranteed to be unique among
+simultaneously existing objects. (Hint: it's the object's memory address.)
+</pre>
+</blockquote>
+<p>The reader is strongly encouraged to try the help function on everything
+(including help(help) ;-). This is the best way to learn how Python works,
+even <em>better</em> than reading the standard documentation, since the on-line
+help is often more update.</p>
+<p>Suppose for instance we wonder if the number <tt class="literal"><span class="pre">1</span></tt> is an object:
+it is easy enough to ask Python for the answer:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(1)
+135383880
+</pre>
+</blockquote>
+<p>Therefore the number 1 is a Python object and it is stored at the memory
+address 135383880, at least in my computer and during the current session.
+Notice that the object reference is a dynamic thing; nevertheless it
+is guaranteed to be unique and constant for a given object during its
+lifetime (two objects whose lifetimes are disjunct may have the same id()
+value, though).</p>
+<p>Here there are other examples of built-in objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(1L) # long
+1074483312
+&gt;&gt;&gt; id(1.0) #float
+135682468
+&gt;&gt;&gt; id(1j) # complex
+135623440
+&gt;&gt;&gt; id('1') #string
+1074398272
+&gt;&gt;&gt; id([1]) #list
+1074376588
+&gt;&gt;&gt; id((1,)) #tuple
+1074348844
+&gt;&gt;&gt; id({1:1}) # dict
+1074338100
+</pre>
+</blockquote>
+<p>Even functions are objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): return x #user-defined function
+&gt;&gt;&gt; id(f)
+1074292020
+&gt;&gt;&gt; g=lambda x: x #another way to define functions
+&gt;&gt;&gt; id(g)
+1074292468
+&gt;&gt;&gt; id(id) #id itself is a built-in function
+1074278668
+</pre>
+</blockquote>
+<p>Modules are objects, too:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import math
+&gt;&gt;&gt; id(math) #module of the standard library
+1074239068
+&gt;&gt;&gt; id(math.sqrt) #function of the standard library
+1074469420
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">help</span></tt> itself is an object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(help)
+1074373452
+</pre>
+</blockquote>
+<p>Finally, we may notice that the reserved keywords are not objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(print) #error
+File &quot;&lt;string&gt;&quot;, line 1
+ id(print) ^
+SyntaxError: invalid syntax
+</pre>
+</blockquote>
+<p>The operative definition is convenient since it gives a practical way
+to check if something is an object and, more importantly, if two
+objects are the same or not:</p>
+<blockquote>
+<!-- doctest -->
+<pre class="doctest-block">
+&gt;&gt;&gt; s1='spam'
+&gt;&gt;&gt; s2='spam'
+&gt;&gt;&gt; s1==s2
+True
+&gt;&gt;&gt; id(s1)==id(s2)
+True
+</pre>
+</blockquote>
+<p>A more elegant way of spelling <tt class="literal"><span class="pre">id(obj1)==id(obj2)</span></tt> is to use the
+keyword <tt class="literal"><span class="pre">is</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; s1 is s2
+True
+</pre>
+</blockquote>
+<p>However, I should warn the reader that sometimes <tt class="literal"><span class="pre">is</span></tt> can be surprising:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id([]) == id([])
+True
+&gt;&gt;&gt; [] is []
+False
+</pre>
+</blockquote>
+<p>This is happening because writing <tt class="literal"><span class="pre">id([])</span></tt> dynamically creates an unique
+object (a list) which goes away when you're finished with it. So when an
+expression needs both at the same time (<tt class="literal"><span class="pre">[]</span> <span class="pre">is</span> <span class="pre">[]</span></tt>), two unique objects
+are created, but when an expression doesn't need both at the same time
+(<tt class="literal"><span class="pre">id([])</span> <span class="pre">==</span> <span class="pre">id([])</span></tt>), an object gets created with an ID, is destroyed,
+and then a second object is created with the same ID (since the last one
+just got reclaimed) and their IDs compare equal. In other words, &quot;the
+ID is guaranteed to be unique <em>only</em> among simultaneously existing objects&quot;.</p>
+<p>Another surprise is the following:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; a=1
+&gt;&gt;&gt; b=1
+&gt;&gt;&gt; a is b
+True
+&gt;&gt;&gt; a=556
+&gt;&gt;&gt; b=556
+&gt;&gt;&gt; a is b
+False
+</pre>
+</blockquote>
+<p>The reason is that integers between 0 and 99 are pre-instantiated by the
+interpreter, whereas larger integers are recreated each time.</p>
+<p>Notice the difference between '==' and 'is':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 1L==1
+True
+</pre>
+</blockquote>
+<p>but</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 1L is 1
+False
+</pre>
+</blockquote>
+<p>since they are different objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(1L) # long 1
+135625536
+&gt;&gt;&gt; id(1) # int 1
+135286080
+</pre>
+</blockquote>
+<p>The disadvantage of the operative definition is that it gives little
+understanding of what an object can be used for. To this aim, I must
+introduce the concept of <em>class</em>.</p>
+<table class="footnote" frame="void" id="id9" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id10" name="id9">[5]</a></td><td>Actually <tt class="literal"><span class="pre">help</span></tt> is not a function but a callable object. The
+difference will be discussed in a following chapter.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="objects-and-classes">
+<h2><a class="toc-backref" href="#id60" name="objects-and-classes">Objects and classes</a></h2>
+<p>It is convenient to think of an object as an element of a set.</p>
+<p>It you think a bit, this is the most general definition that actually
+grasps what we mean by object in the common language.
+For instance, consider this book, &quot;Object Oriented Programming in Python&quot;:
+this book is an object, in the sense that it is a specific representative
+of the <em>class</em> of all possible books.
+According to this definition, objects are strictly related to classes, and
+actually we say that objects are <em>instances</em> of classes.</p>
+<p>Classes are nested: for
+instance this book belongs to the class of books about programming
+language, which is a subset of the class of all possible books;
+moreover we may further specify this book as a Python book; moreover
+we may specify this book as a Python 2.2+ book. There is no limit
+to the restrictions we may impose to our classes.
+On the other hand. it is convenient to have a &quot;mother&quot; class,
+such that any object belongs to it. All strongly Object Oriented
+Language have such a class <a class="footnote-reference" href="#id9" id="id10" name="id10">[5]</a>; in Python it is called <em>object</em>.</p>
+<p>The relation between objects and classes in Python can be investigated
+trough the built-in function <tt class="literal"><span class="pre">type</span></tt> <a class="footnote-reference" href="#id12" id="id11" name="id11">[6]</a> that gives the class of any
+Python object.</p>
+<p>Let me give some example:</p>
+<ol class="arabic simple">
+<li>Integers numbers are instances of the class <tt class="literal"><span class="pre">int</span></tt> or <tt class="literal"><span class="pre">long</span></tt>:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(1)
+&lt;type 'int'&gt;
+&gt;&gt;&gt; type(1L)
+&lt;type 'long'&gt;
+</pre>
+</blockquote>
+<ol class="arabic simple" start="2">
+<li>Floating point numbers are instances of the class <tt class="literal"><span class="pre">float</span></tt>:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(1.0)
+&lt;type 'float'&gt;
+</pre>
+</blockquote>
+<ol class="arabic simple" start="3">
+<li>Complex numbers are instances of the class <tt class="literal"><span class="pre">complex</span></tt>:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(1.0+1.0j)
+&lt;type 'complex'&gt;
+</pre>
+</blockquote>
+<ol class="arabic simple" start="4">
+<li>Strings are instances of the class <tt class="literal"><span class="pre">str</span></tt>:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type('1')
+&lt;type 'str'&gt;
+</pre>
+</blockquote>
+<ol class="arabic simple" start="5">
+<li>List, tuples and dictionaries are instances of <tt class="literal"><span class="pre">list</span></tt>, <tt class="literal"><span class="pre">tuple</span></tt> and
+<tt class="literal"><span class="pre">dict</span></tt> respectively:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type('1')
+&lt;type 'str'&gt;
+&gt;&gt;&gt; type([1])
+&lt;type 'list'&gt;
+&gt;&gt;&gt; type((1,))
+&lt;type 'tuple'&gt;
+&gt;&gt;&gt; type({1:1})
+&lt;type 'dict'&gt;
+</pre>
+</blockquote>
+<ol class="arabic simple" start="6">
+<li>User defined functions are instances of the <tt class="literal"><span class="pre">function</span></tt> built-in type</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(f)
+&lt;type 'function'&gt;
+&gt;&gt;&gt; type(g)
+&lt;type 'function'&gt;
+</pre>
+</blockquote>
+<p>All the previous types are subclasses of object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; for cl in int,long,float,str,list,tuple,dict: issubclass(cl,object)
+True
+True
+True
+True
+True
+True
+True
+</pre>
+</blockquote>
+<p>However, Python is not a 100% pure Object
+Oriented Programming language and its object model has still some minor
+warts, due to historical accidents.</p>
+<p>Paraphrasing George Orwell, we may say that in Python 2.2-2.3,
+all objects are equal, but some objects are more equal than others.
+Actually, we may distinguish Python objects in new style objects,
+or rich man objects, and old style objects, or poor man objects.
+New style objects are instances of new style classes whereas old
+style objects are instances of old style classes.
+The difference is that new style classes are subclasses of object whereas
+old style classes are not.</p>
+<p>Old style classes are there for sake of compatibility with previous
+releases of Python, but starting from Python 2.2 practically all built-in
+classes are new style classes.</p>
+<p>Instance of old style classes are called old style objects. I will give
+few examples of old style objects in the future.</p>
+<p>In this tutorial with the term
+object <em>tout court</em> we will mean new style objects, unless the contrary
+is explicitely stated.</p>
+<table class="footnote" frame="void" id="id12" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id11" name="id12">[6]</a></td><td>one may notice that C++ does not have such a class, but C++
+is <em>not</em> a strongly object oriented language ;-)</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id13" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id14" name="id13">[7]</a></td><td>Actually <tt class="literal"><span class="pre">type</span></tt> is not a function, but a metaclass; nevertheless,
+since this is an advanced concept, discussed in the fourth chapter;
+for the time being it is better to think of <tt class="literal"><span class="pre">type</span></tt> as a built-in
+function analogous to <tt class="literal"><span class="pre">id</span></tt>.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="objects-have-attributes">
+<h2><a class="toc-backref" href="#id61" name="objects-have-attributes">Objects have attributes</a></h2>
+<p>All objects have attributes describing their characteristics, that may
+be accessed via the dot notation</p>
+<blockquote>
+<pre class="literal-block">
+objectname.objectattribute
+</pre>
+</blockquote>
+<p>The dot notation is common to most Object Oriented programming languages,
+therefore the reader with a little of experience should find it not surprising
+at all (Python strongly believes in the Principle of Least Surprise). However,
+Python objects also have special attributes denoted by the double-double
+underscore notation</p>
+<blockquote>
+<pre class="literal-block">
+objectname.__specialattribute__
+</pre>
+</blockquote>
+<p>with the aim of helping the wonderful Python introspection features, that
+does not have correspondence in all OOP language.</p>
+<p>Consider for example the string literal &quot;spam&quot;. We may discover its
+class by looking at its special attribute <em>__class__</em>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 'spam'.__class__
+&lt;type 'str'&gt;
+</pre>
+</blockquote>
+<p>Using the <tt class="literal"><span class="pre">__class__</span></tt> attribute is not always equivalent to using the
+<tt class="literal"><span class="pre">type</span></tt> function, but it works for all built-in types. Consider for instance
+the number <em>1</em>: we may extract its class as follows:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; (1).__class__
+&lt;type 'int'&gt;
+</pre>
+</blockquote>
+<p>Notice that the parenthesis are needed to avoid confusion between the integer
+1 and the float (1.).</p>
+<p>The non-equivalence type/class is the key to distinguish new style objects from
+old style, since for old style objects <tt class="literal"><span class="pre">type(obj)&lt;&gt;obj.__class__</span></tt>.
+We may use this knowledge to make and utility function that discovers
+if an object is a &quot;real&quot; object (i.e. new style) or a poor man object:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def isnewstyle(obj):
+ try: #some objects may lack a __class__ attribute
+ obj.__class__
+ except AttributeError:
+ return False
+ else: #look if there is unification type/class
+ return type(obj) is obj.__class__
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Let us check this with various examples:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import isnewstyle
+&gt;&gt;&gt; isnewstyle(1)
+True
+&gt;&gt;&gt; isnewstyle(lambda x:x)
+True
+&gt;&gt;&gt; isnewstyle(id)
+True
+&gt;&gt;&gt; isnewstyle(type)
+True
+&gt;&gt;&gt; isnewstyle(isnewstyle)
+True
+&gt;&gt;&gt; import math
+&gt;&gt;&gt; isnewstyle(math)
+True
+&gt;&gt;&gt; isnewstyle(math.sqrt)
+True
+&gt;&gt;&gt; isnewstyle('hello')
+True
+</pre>
+</blockquote>
+<p>It is not obvious to find something which is not a real object,
+between the built-in objects, however it is possible. For instance,
+the <tt class="literal"><span class="pre">help</span></tt> &quot;function&quot; is an old style object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; isnewstyle(help)
+False
+</pre>
+</blockquote>
+<p>since</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; help.__class__
+&lt;class site._Helper at 0x8127c94&gt;
+</pre>
+</blockquote>
+<p>is different from</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(help)
+&lt;type 'instance'&gt;
+</pre>
+</blockquote>
+<p>Regular expression objects are even poorer objects with no <tt class="literal"><span class="pre">__class__</span></tt>
+attribute:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import re
+&gt;&gt;&gt; reobj=re.compile('somestring')
+&gt;&gt;&gt; isnewstyle(reobj)
+False
+&gt;&gt;&gt; type(reobj)
+&lt;type '_sre.SRE_Pattern'&gt;
+&gt;&gt;&gt; reobj.__class__ #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: __class__
+</pre>
+</blockquote>
+<p>There other special attributes other than <tt class="literal"><span class="pre">__class__</span></tt>; a particularly useful
+one is <tt class="literal"><span class="pre">__doc__</span></tt>, that contains informations on the class it
+refers to. Consider for instance the <tt class="literal"><span class="pre">str</span></tt> class: by looking at its
+<tt class="literal"><span class="pre">__doc__</span></tt> attribute we can get information on the usage of this class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str.__doc__
+str(object) -&gt; string
+Return a nice string representation of the object.
+If the argument is a string, the return value is the same object.
+</pre>
+</blockquote>
+<p>From that docstring we learn how to convert generic objects in strings;
+for instance we may convert numbers, lists, tuples and dictionaries:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str(1)
+'1'
+&gt;&gt;&gt; str([1])
+'[1]'
+&gt;&gt;&gt; str((1,))
+(1,)'
+&gt;&gt;&gt; str({1:1})
+'{1: 1}'
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">str</span></tt> is implicitely called each time we use the <tt class="literal"><span class="pre">print</span></tt> statement, since
+<tt class="literal"><span class="pre">print</span> <span class="pre">obj</span></tt> is actually syntactic sugar for <tt class="literal"><span class="pre">print</span> <span class="pre">str(obj)</span></tt>.</p>
+<p>Classes and modules have another interesting special attribute, the
+<tt class="literal"><span class="pre">__dict__</span></tt> attribute that gives the content of the class/module.
+For instance, the contents of the standard <tt class="literal"><span class="pre">math</span></tt> module can be retrieved
+as follows:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import math
+&gt;&gt;&gt; for key in math.__dict__: print key,
+...
+fmod atan pow __file__ cosh ldexp hypot sinh __name__ tan ceil asin cos
+e log fabs floor tanh sqrt __doc__ frexp atan2 modf exp acos pi log10 sin
+</pre>
+</blockquote>
+<p>Alternatively, one can use the built-in function <tt class="literal"><span class="pre">vars</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; vars(math) is math.__dict__
+True
+</pre>
+</blockquote>
+<p>This identity is true for any object with a <tt class="literal"><span class="pre">__dict__</span></tt> attribute.
+Two others interesting special attributes are <tt class="literal"><span class="pre">__doc__</span></tt></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print math.__doc__
+This module is always available. It provides access to the
+mathematical functions defined by the C standard.
+</pre>
+</blockquote>
+<p>and <tt class="literal"><span class="pre">__file__</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; math.__file__ #gives the file associated with the module
+'/usr/lib/python2.2/lib-dynload/mathmodule.so'
+</pre>
+</blockquote>
+</div>
+<div class="section" id="objects-have-methods">
+<h2><a class="toc-backref" href="#id62" name="objects-have-methods">Objects have methods</a></h2>
+<p>In addition to attributes, objects also have <em>methods</em>, i.e.
+functions attached to their classes <a class="footnote-reference" href="#id13" id="id14" name="id14">[7]</a>.
+Methods are also invoked with the dot notation, but
+they can be distinguished by attributes because they are typically
+called with parenthesis (this is a little simplistic, but it is enough for
+an introductory chapter). As a simple example, let me show the
+invocation of the <tt class="literal"><span class="pre">split</span></tt> method for a string object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; s='hello world!'
+&gt;&gt;&gt; s.split()
+['hello', 'world!']
+</pre>
+</blockquote>
+<p>In this example <tt class="literal"><span class="pre">s.split</span></tt> is called a <em>bount method</em>, since it is
+applied to the string object <tt class="literal"><span class="pre">s</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; s.split
+&lt;built-in method split of str object at 0x81572b8&gt;
+</pre>
+</blockquote>
+<p>An <em>unbound method</em>, instead, is applied to the class: in this case the
+unbound version of <tt class="literal"><span class="pre">split</span></tt> is applied to the <tt class="literal"><span class="pre">str</span></tt> class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str.split
+&lt;method 'split' of 'str' objects&gt;
+</pre>
+</blockquote>
+<p>A bound method is obtained from its corresponding unbound
+method by providing the object to the unbound method: for instance
+by providing <tt class="literal"><span class="pre">s</span></tt> to <tt class="literal"><span class="pre">str.split</span></tt> we obtain the same effect of <cite>s.split()</cite>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str.split(s)
+['hello', 'world!']
+</pre>
+</blockquote>
+<p>This operation is called <em>binding</em> in the Python literature: when write
+<tt class="literal"><span class="pre">str.split(s)</span></tt> we bind the unbound method <tt class="literal"><span class="pre">str.split</span></tt> to the object <tt class="literal"><span class="pre">s</span></tt>.
+It is interesting to recognize that the bound and unbound methods are
+<em>different</em> objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(str.split) # unbound method reference
+135414364
+&gt;&gt;&gt; id(s.split) # this is a different object!
+135611408
+</pre>
+</blockquote>
+<p>The unbound method (and therefore the bound method) has a <tt class="literal"><span class="pre">__doc__</span></tt>
+attribute explaining how it works:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print str.split.__doc__
+S.split([sep [,maxsplit]]) -&gt; list of strings
+Return a list of the words in the string S, using sep as the
+delimiter string. If maxsplit is given, at most maxsplit
+splits are done. If sep is not specified or is None, any
+whitespace string is a separator.
+</pre>
+</blockquote>
+<table class="footnote" frame="void" id="id15" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id17" name="id15">[8]</a></td><td>A precise definition will be given in chapter 5 that introduces the
+concept of attribute descriptors. There are subtle
+differences between functions and methods.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="summing-objects">
+<h2><a class="toc-backref" href="#id63" name="summing-objects">Summing objects</a></h2>
+<p>In a pure object-oriented world, there are no functions and everything is
+done trough methods. Python is not a pure OOP language, however quite a
+lot is done trough methods. For instance, it is quite interesting to analyze
+what happens when an apparently trivial statement such as</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 1+1
+2
+</pre>
+</blockquote>
+<p>is executed in an object-oriented world.</p>
+<p>The key to understand, is to notice that the number 1 is an object, specifically
+an instance of class <tt class="literal"><span class="pre">int</span></tt>: this means that that 1 inherits all the methods
+of the <tt class="literal"><span class="pre">int</span></tt> class. In particular it inherits a special method called
+<tt class="literal"><span class="pre">__add__</span></tt>: this means 1+1 is actually syntactic sugar for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; (1).__add__(1)
+2
+</pre>
+</blockquote>
+<p>which in turns is syntactic sugar for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; int.__add__(1,1)
+2
+</pre>
+</blockquote>
+<p>The same is true for subtraction, multiplication, division and other
+binary operations.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 'hello'*2
+'hellohello'
+&gt;&gt;&gt; (2).__mul__('hello')
+'hellohello'
+&gt;&gt;&gt; str.__mul__('hello',2)
+'hellohello'
+</pre>
+</blockquote>
+<p>However, notice that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str.__mul__(2,'hello') #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: descriptor '__mul__' requires a 'str' object but received a 'int'
+</pre>
+</blockquote>
+<p>The fact that operators are implemented as methods, is the key to
+<em>operator overloading</em>: in Python (as well as in other OOP languages)
+the user can redefine the operators. This is already done by default
+for some operators: for instance the operator <tt class="literal"><span class="pre">+</span></tt> is overloaded
+and works both for integers, floats, complex numbers and for strings.</p>
+</div>
+<div class="section" id="inspecting-objects">
+<h2><a class="toc-backref" href="#id64" name="inspecting-objects">Inspecting objects</a></h2>
+<p>In Python it is possible to retrieve most of the attributes and methods
+of an object by using the built-in function <tt class="literal"><span class="pre">dir()</span></tt>
+(try <tt class="literal"><span class="pre">help(dir)</span></tt> for more information).</p>
+<p>Let me consider the simplest case of a generic object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; obj=object()
+&gt;&gt;&gt; dir(obj)
+['__class__', '__delattr__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__']
+</pre>
+</blockquote>
+<p>As we see, there are plenty of attributes available
+even to a do nothing object; many of them are special attributes
+providing introspection capabilities which are not
+common to all programming languages. We have already discussed the
+meaning of some of the more obvious special attributes.
+The meaning of some of the others is quite non-obvious, however.
+The docstring is invaluable in providing some clue.</p>
+<p>Notice that there are special <em>hidden</em> attributes that cannot be retrieved
+with <tt class="literal"><span class="pre">dir()</span></tt>. For instance the <tt class="literal"><span class="pre">__name__</span></tt> attribute, returning the
+name of the object (defined for classes, modules and functions)
+and the <tt class="literal"><span class="pre">__subclasses__</span></tt> method, defined for classes and returning the
+list of immediate subclasses of a class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str.__name__
+'str'
+&gt;&gt;&gt; str.__subclasses__.__doc__
+'__subclasses__() -&gt; list of immediate subclasses'
+&gt;&gt;&gt; str.__subclasses__() # no subclasses of 'str' are currently defined
+[]
+</pre>
+</blockquote>
+<p>For instance by doing</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; obj.__getattribute__.__doc__
+&quot;x.__getattribute__('name') &lt;==&gt; x.name&quot;
+</pre>
+</blockquote>
+<p>we discover that the expression <tt class="literal"><span class="pre">x.name</span></tt> is syntactic sugar for</p>
+<blockquote>
+<tt class="literal"><span class="pre">x.__getattribute__('name')</span></tt></blockquote>
+<p>Another equivalent form which is more often used is</p>
+<blockquote>
+<tt class="literal"><span class="pre">getattr(x,'name')</span></tt></blockquote>
+<p>We may use this trick to make a function that retrieves all the
+attributes of an object except the special ones:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def special(name): return name.startswith('__') and name.endswith('__')
+
+def attributes(obj,condition=lambda n,v: not special(n)):
+ &quot;&quot;&quot;Returns a dictionary containing the accessible attributes of
+ an object. By default, returns the non-special attributes only.&quot;&quot;&quot;
+ dic={}
+ for attr in dir(obj):
+ try: v=getattr(obj,attr)
+ except: continue #attr is not accessible
+ if condition(attr,v): dic[attr]=v
+ return dic
+
+getall = lambda n,v: True
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice that certain attributes may be unaccessible (we will see how
+to make attributes unaccessible in a following chapter)
+and in this case they are simply ignored.
+For instance you may retrieve the regular (i.e. non special)
+attributes of the built-in functions:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import attributes
+&gt;&gt;&gt; attributes(f).keys()
+['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']
+</pre>
+</blockquote>
+<p>In the same vein of the <tt class="literal"><span class="pre">getattr</span></tt> function, there is a built-in
+<tt class="literal"><span class="pre">setattr</span></tt> function (that actually calls the <tt class="literal"><span class="pre">__setattr__</span></tt> built-in
+method), that allows the user to change the attributes and methods of
+and object. Informations on <tt class="literal"><span class="pre">setattr</span></tt> can be retrieved from the help
+function:</p>
+<blockquote>
+<pre class="literal-block">
+&gt;&gt;&gt; help(setattr)
+Help on built-in function setattr:
+setattr(...)
+setattr(object, name, value)
+Set a named attribute on an object; setattr(x, 'y', v) is equivalent to
+``x.y = v''.
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">setattr</span></tt> can be used to add attributes to an object:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import sys
+
+def customize(obj,errfile=None,**kw):
+ &quot;&quot;&quot;Adds attributes to an object, if possible. If not, writes an error
+ message on 'errfile'. If errfile is None, skips the exception.&quot;&quot;&quot;
+ for k in kw:
+ try:
+ setattr(obj,k,kw[k])
+ except: # setting error
+ if errfile:
+ print &gt;&gt; errfile,&quot;Error: %s cannot be set&quot; % k
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The attributes of built-in objects cannot be set, however:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import customize,sys
+&gt;&gt;&gt; customize(object(),errfile=sys.stdout,newattr='hello!') #error
+AttributeError: newattr cannot be set
+</pre>
+</blockquote>
+<p>On the other hand, the attributes of modules can be set:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import time
+&gt;&gt;&gt; customize(time,newattr='hello!')
+&gt;&gt;&gt; time.newattr
+'hello!'
+</pre>
+</blockquote>
+<p>Notice that this means we may enhances modules at run-time, but adding
+new routines, not only new data attributes.</p>
+<p>The <tt class="literal"><span class="pre">attributes</span></tt> and <tt class="literal"><span class="pre">customize</span></tt> functions work for any kind of objects;
+in particular, since classes are a special kind of objects, they work
+for classes, too. Here are the attributes of the <tt class="literal"><span class="pre">str</span></tt>, <tt class="literal"><span class="pre">list</span></tt> and
+<tt class="literal"><span class="pre">dict</span></tt> built-in types:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import attributes
+&gt;&gt;&gt; attributes(str).keys()
+['startswith', 'rjust', 'lstrip', 'swapcase', 'replace','encode',
+ 'endswith', 'splitlines', 'rfind', 'strip', 'isdigit', 'ljust',
+ 'capitalize', 'find', 'count', 'index', 'lower', 'translate','join',
+ 'center', 'isalnum','title', 'rindex', 'expandtabs', 'isspace',
+ 'decode', 'isalpha', 'split', 'rstrip', 'islower', 'isupper',
+ 'istitle', 'upper']
+&gt;&gt;&gt; attributes(list).keys()
+['append', 'count', 'extend', 'index', 'insert', 'pop',
+ 'remove', 'reverse', 'sort']
+&gt;&gt;&gt; attributes(dict).keys()
+['clear','copy','fromkeys', 'get', 'has_key', 'items','iteritems',
+ 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault',
+ 'update', 'values']
+</pre>
+</blockquote>
+<p>Classes and modules have a special attribute <tt class="literal"><span class="pre">__dict__</span></tt> giving the
+dictionary of their attributes. Since it is often a quite large dictionary,
+it is convenient to define an utility function printing this dictionary in a
+nice form:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def pretty(dic):
+ &quot;Returns a nice string representation for the dictionary&quot;
+ keys=dic.keys(); keys.sort() # sorts the keys
+ return '\n'.join(['%s = %s' % (k,dic[k]) for k in keys])
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>I encourage the use of this function in order to retrieve more
+information about the modules of the standard library:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import pretty
+&gt;&gt;&gt; import time #look at the 'time' standard library module
+&gt;&gt;&gt; print pretty(vars(time))
+__doc__ = This module provides various functions to manipulate time values.
+There are two standard representations of time. One is the number
+of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer
+or a floating point number (to represent fractions of seconds).
+The Epoch is system-defined; on Unix, it is generally January 1st, 1970.
+The actual value can be retrieved by calling gmtime(0).
+The other representation is a tuple of 9 integers giving local time.
+The tuple items are:
+ year (four digits, e.g. 1998)
+ month (1-12)
+ day (1-31)
+ hours (0-23)
+ minutes (0-59)
+ seconds (0-59)
+ weekday (0-6, Monday is 0)
+ Julian day (day in the year, 1-366)
+ DST (Daylight Savings Time) flag (-1, 0 or 1)
+If the DST flag is 0, the time is given in the regular time zone;
+if it is 1, the time is given in the DST time zone;
+if it is -1, mktime() should guess based on the date and time.
+Variables:
+timezone -- difference in seconds between UTC and local standard time
+altzone -- difference in seconds between UTC and local DST time
+daylight -- whether local time should reflect DST
+tzname -- tuple of (standard time zone name, DST time zone name)
+Functions:
+time() -- return current time in seconds since the Epoch as a float
+clock() -- return CPU time since process start as a float
+sleep() -- delay for a number of seconds given as a float
+gmtime() -- convert seconds since Epoch to UTC tuple
+localtime() -- convert seconds since Epoch to local time tuple
+asctime() -- convert time tuple to string
+ctime() -- convert time in seconds to string
+mktime() -- convert local time tuple to seconds since Epoch
+strftime() -- convert time tuple to string according to format specification
+strptime() -- parse string to time tuple according to format specification
+__file__ = /usr/local/lib/python2.3/lib-dynload/time.so
+__name__ = time
+accept2dyear = 1
+altzone = 14400
+asctime = &lt;built-in function asctime&gt;
+clock = &lt;built-in function clock&gt;
+ctime = &lt;built-in function ctime&gt;
+daylight = 1
+gmtime = &lt;built-in function gmtime&gt;
+localtime = &lt;built-in function localtime&gt;
+mktime = &lt;built-in function mktime&gt;
+newattr = hello!
+sleep = &lt;built-in function sleep&gt;
+strftime = &lt;built-in function strftime&gt;
+strptime = &lt;built-in function strptime&gt;
+struct_time = &lt;type 'time.struct_time'&gt;
+time = &lt;built-in function time&gt;
+timezone = 18000
+tzname = ('EST', 'EDT')
+</pre>
+</blockquote>
+<p>The list of the built-in Python types can be found in the <tt class="literal"><span class="pre">types</span></tt> module:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import types
+&gt;&gt;&gt; t_dict=dict([(k,v) for (k,v) in vars(types).iteritems()
+... if k.endswith('Type')])
+&gt;&gt;&gt; for t in t_dict: print t,
+...
+DictType IntType TypeType FileType CodeType XRangeType EllipsisType
+SliceType BooleanType ListType MethodType TupleType ModuleType FrameType
+StringType LongType BuiltinMethodType BufferType FloatType ClassType
+DictionaryType BuiltinFunctionType UnboundMethodType UnicodeType
+LambdaType DictProxyType ComplexType GeneratorType ObjectType
+FunctionType InstanceType NoneType TracebackType
+</pre>
+</blockquote>
+<p>For a pedagogical account of the most elementary
+Python introspection features,
+Patrick O' Brien:
+<a class="reference" href="http://www-106.ibm.com/developerworks/linux/library/l-pyint.html">http://www-106.ibm.com/developerworks/linux/library/l-pyint.html</a></p>
+</div>
+<div class="section" id="built-in-objects-iterators-and-generators">
+<h2><a class="toc-backref" href="#id65" name="built-in-objects-iterators-and-generators">Built-in objects: iterators and generators</a></h2>
+<p>At the end of the last section , I have used the <tt class="literal"><span class="pre">iteritems</span></tt> method
+of the dictionary, which returns an iterator:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; dict.iteritems.__doc__
+'D.iteritems() -&gt; an iterator over the (key, value) items of D'
+</pre>
+</blockquote>
+<p>Iterators (and generators) are new features of Python 2.2 and could not be
+familiar to all readers. However, since they are unrelated to OOP, they
+are outside the scope of this book and will not be discussed here in detail.
+Nevertheless, I will give a typical example of use of a generator, since
+this construct will be used in future chapters.</p>
+<p>At the syntactical level, a generator is a &quot;function&quot; with (at least one)
+<tt class="literal"><span class="pre">yield</span></tt> statement (notice that in Python 2.2 the <tt class="literal"><span class="pre">yield</span></tt> statement is
+enabled trough the <tt class="literal"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">generators</span></tt> syntax):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import re
+
+def generateblocks(regexp,text):
+ &quot;Generator splitting text in blocks according to regexp&quot;
+ start=0
+ for MO in regexp.finditer(text):
+ beg,end=MO.span()
+ yield text[start:beg] # actual text
+ yield text[beg:end] # separator
+ start=end
+ lastblock=text[start:]
+ if lastblock: yield lastblock; yield ''
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>In order to understand this example, the reader my want to refresh his/her
+understanding of regular expressions; since this is not a subject for
+this book, I simply remind the meaning of <tt class="literal"><span class="pre">finditer</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import re
+&gt;&gt;&gt; help(re.finditer)
+finditer(pattern, string)
+ Return an iterator over all non-overlapping matches in the
+ string. For each match, the iterator returns a match object.
+ Empty matches are included in the result.
+</pre>
+</blockquote>
+<p>Generators can be thought of as resumable functions that stop at the
+<tt class="literal"><span class="pre">yield</span></tt> statement and resume from the point where they left.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import generateblocks
+&gt;&gt;&gt; text='Python_Rules!'
+&gt;&gt;&gt; g=generateblocks(re.compile('_'),text)
+&gt;&gt;&gt; g
+&lt;generator object at 0x401b140c&gt;
+&gt;&gt;&gt; dir(g)
+['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
+ '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__',
+ '__repr__', '__setattr__', '__str__', 'gi_frame', 'gi_running', 'next']
+</pre>
+</blockquote>
+<p>Generator objects can be used as iterators in a <tt class="literal"><span class="pre">for</span></tt> loop.
+In this example the generator takes a text and a regular expression
+describing a fixed delimiter; then it splits the text in blocks
+according to the delimiter. For instance, if the delimiter is
+'_', the text 'Python Rules!' is splitted as 'Python', '_' and 'Rules!':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; for n, block in enumerate(g): print n, block
+...
+0 Python
+1
+2 Rules!
+3
+</pre>
+</blockquote>
+<p>This example also show the usage of the new Python 2.3 built-in <tt class="literal"><span class="pre">enumerate</span></tt>.</p>
+<p>Under the hood the <tt class="literal"><span class="pre">for</span></tt> loop is calling the generator via its
+<tt class="literal"><span class="pre">next</span></tt> method, until the <tt class="literal"><span class="pre">StopIteration</span></tt> exception is raised.
+For this reason a new call to the <tt class="literal"><span class="pre">for</span></tt> loop will have no effect:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; for n, block in enumerate(g): print n, block
+...
+</pre>
+</blockquote>
+<p>The point is that the generator has already yield its last element:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; g.next() # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+StopIteration
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">generateblocks</span></tt> always returns an even number of blocks; odd blocks
+are delimiters whereas even blocks are the intertwining text; there may be
+empty blocks, corresponding to the null string ''.</p>
+<p>It must be remarked the difference with the 'str.split' method</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; 'Python_Rules!'.split('_')
+['Python', 'Rules!']
+</pre>
+</blockquote>
+<p>and the regular expression split method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; re.compile('_').split('Python_Rules!')
+['Python', 'Rules!']
+</pre>
+</blockquote>
+<p>both returns lists with an odd number of elements and both miss the separator.
+The regular expression split method can catch the separator, if wanted,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; re.compile('(_)').split('Python_Rules!')
+['Python', '_', 'Rules!']
+</pre>
+</blockquote>
+<p>but still is different from the generator, since it returns a list. The
+difference is relevant if we want to split a very large text, since
+the generator avoids to build a very large list and thus it is much more
+memory efficient (it is faster, too). Moreover, <tt class="literal"><span class="pre">generateblocks</span></tt>
+works differently in the case of multiple groups:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; delim=re.compile('(_)|(!)') #delimiter is space or exclamation mark
+&gt;&gt;&gt; for n, block in enumerate(generateblocks(delim,text)):
+... print n, block
+0 Python
+1 _
+2 Rules
+3 !
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; delim.split(text)
+['Python', '_', None, 'Rules', None, '!', '']
+</pre>
+</blockquote>
+<p>gives various unwanted <tt class="literal"><span class="pre">None</span></tt> (which could be skipped with
+<tt class="literal"><span class="pre">[x</span> <span class="pre">for</span> <span class="pre">x</span> <span class="pre">in</span> <span class="pre">delim.split(text)</span> <span class="pre">if</span> <span class="pre">x</span> <span class="pre">is</span> <span class="pre">not</span> <span class="pre">None]</span></tt>); notice, that
+there are no differences (apart from the fact that <tt class="literal"><span class="pre">delim.split(text)</span></tt>
+has an odd number of elements) when one uses a single group regular expression:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; delim=re.compile('(_|!)')
+&gt;&gt;&gt; delim.split(text)
+['Python', '_', 'Rules', '!', '']
+</pre>
+</blockquote>
+<p>The reader unfamiliar with iterators and generators is encouraged
+to look at the standard documentation and other
+references. For instance, there are Alex Martelli's notes on iterators at
+<a class="reference" href="http://www.strakt.com/dev_talks.html">http://www.strakt.com/dev_talks.html</a>
+and there is a good article on generators by David Mertz
+<a class="reference" href="http://www-106.ibm.com/developerworks/linux/library/l-pycon.html">http://www-106.ibm.com/developerworks/linux/library/l-pycon.html</a></p>
+</div>
+</div>
+<div class="section" id="the-convenience-of-functions">
+<h1><a class="toc-backref" href="#id66" name="the-convenience-of-functions">THE CONVENIENCE OF FUNCTIONS</a></h1>
+<p>Functions are the most basic Python objects. They are also the simplest
+objects where one can apply the metaprogramming techniques that are
+the subject of this book. The tricks used in this chapter and the utility
+functions defined here will be used over all the book. Therefore this
+is an <em>essential</em> chapter.</p>
+<p>Since it is intended to be a gentle introduction, the tone will be
+informal.</p>
+<div class="section" id="id16">
+<h2><a class="toc-backref" href="#id67" name="id16">Introduction</a></h2>
+<p>One could be surprised that a text on OOP begins with a chapter on the
+well known old-fashioned functions. In some sense, this is also
+against the spirit of an important trend in OOP, which tries to
+shift the focus from functions to data. In pure OOP languages,
+there are no more functions, only methods. <a class="footnote-reference" href="#id15" id="id17" name="id17">[8]</a></p>
+<p>However, there are good reasons for that:</p>
+<ol class="arabic simple">
+<li>In Python, functions <em>are</em> objects. And particularly useful ones.</li>
+<li>Python functions are pretty powerful and all their secrets are probably
+<em>not</em> well known to the average Python programmer.</li>
+<li>In the solutions of many problems, you don't need the full apparatus
+of OOP: good old functions can be enough.</li>
+</ol>
+<p>Moreover, I am a believer in the multiparadigm approach to programming,
+in which you choose your tools according to your problem.
+With a bazooka you can kill a mosquito, yes, but this does not mean
+that you must use the bazooka <em>always</em>.
+In certain languages, you have no choice, and you must define
+a class (involving a lot of boiler plate code) even for the most trivial
+application. Python's philosophy is to keep simple things simple, but
+having the capability of doing even difficult things with a reasonable
+amount of effort. The message of this chapter will be: &quot;use functions when
+you don't need classes&quot;. Functions are good because:</p>
+<ol class="arabic simple">
+<li>They are easy to write (no boiler plate);</li>
+<li>They are easy to understand;</li>
+<li>They can be reused in your code;</li>
+<li>Functions are an essential building block in the construction of objects.</li>
+</ol>
+<p>Even if I think that OOP is an extremely effective strategy, with
+enormous advantages on design, maintanibility and reusability of code,
+nevertheless this book is <em>not</em> intended to be a panegyric of OOP. There
+are cases in which you don't need OOP. I think the critical parameter is
+the size of the program. These are the rules I follows usually (to be
+taken as indicative):</p>
+<ol class="arabic simple">
+<li>If I have to write a short script of 20-30 lines, that copies two or
+three files and prints some message, I use fast and dirty spaghetti-code;
+there is no use for OOP.</li>
+<li>If your script grows to one-hundred lines or more, I structure
+it write a few routines and a main program: but still I can live
+without OOP.</li>
+<li>If the script goes beyond the two hundred lines, I start
+collecting my routines in few classes.</li>
+<li>If the script goes beyond the five hundred lines, I split the program
+in various files and modules and convert it to a package.</li>
+<li>I never write a function longer than 50 lines, since 50 lines is more
+or less the size of a page in my editor, and I need to be able to
+see the entire function in a page.</li>
+</ol>
+<p>Of course your taste could be different and you could prefer to write a
+monolitic program of five thousand lines; however the average size of
+the modules in the Python standard library is of 111 lines.
+I think this is a <em>strong</em> suggestion towards
+a modular style of programming, which
+is <em>very</em> well supported in Python.</p>
+<p>The point is that OOP is especially useful for <em>large</em> programs: if you
+only use Python for short system administration scripts you may well
+live without OOP. Unfortunaly, as everybody knows, short scripts have
+an evil tendency to become medium size scripts, and medium size scripts
+have the even more evil tendency to become large scripts and possible
+even full featured applications ! For this reason it is very probable
+that at a certain moment you will feel the need for OOP.</p>
+<p>I remember my first big program, a long time ago: I wrote a program
+to draw mathematical functions in AmigaBasic. It was good and nice
+until it had size of few hundred lines; but when it passed a thousand
+of lines, it became rapidly unmanageable and unmaintenable. There where
+three problems:</p>
+<ol class="arabic simple">
+<li>I could not split the program in modules, as I wanted, due to the
+limitations of AmigaBasic;</li>
+<li>I was missing OOP to keep the logic of the program all together, but
+at the time I didn't know that;</li>
+<li>I was missing effective debugging techniques.</li>
+<li>I was missing effective refactoring tools.</li>
+</ol>
+<p>I am sure anybody who has ever written a large program has run in these
+limitations: and the biggest help of OOP is in overcoming these limitations.
+Obviously, miracles are impossible, and even object oriented programs can
+grow to a size where they become unmaintanable: the point is that the
+critical limit is much higher than the thousand lines of structured programs.
+I haven't yet reached the limit of unmanageability with Python. The fact
+that the standard library is 66492 lines long (as result from the total
+number of lines in <tt class="literal"><span class="pre">/usr/local/lib/python2.2/</span></tt>), but it is still manageable,
+give me an hope ;-)</p>
+<blockquote>
+<table class="footnote" frame="void" id="id18" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id19" name="id18">[9]</a></td><td>However, one could argue that having functions distinguished from
+methods is the best thing to do, even in a strongly object-oriented
+world. For instance, generic functions can be used to implement
+multimethods. See for instance Lisp, Dylan and MultiJava. This latter
+is forced to introduce the concept of function outside a class,
+foreign to traditional Java, just to implement multimethods.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="a-few-useful-functions">
+<h2><a class="toc-backref" href="#id68" name="a-few-useful-functions">A few useful functions</a></h2>
+<p>It is always a good idea to have a set of useful function collected in
+a user defined module. The first function we want to have in our module
+is the <tt class="literal"><span class="pre">do_nothing</span></tt> function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def do_nothing(*args,**kw): pass
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This function accept a variable number of arguments and keywords (I
+defer the reader to the standard documentation if she is unfamiliar
+with these concept; this is <em>not</em> another Python tutorial ;-) and
+return <tt class="literal"><span class="pre">None</span></tt>. It is very useful for debugging purposes, when in a
+complex program you may want concentrate your attention to few crucial
+functions and set the non-relevant functions to <tt class="literal"><span class="pre">do_nothing</span></tt> functions.</p>
+<p>A second function which is useful in developing programs is a timer
+function. Very ofter indeed, we may want to determine the bottleneck
+parts of a program, we are interested in profiling them and in seeing
+if we can improve the speed by improving the algorithm, or by using
+a Python &quot;compiler&quot; such as Psyco, or if really we need to write a C
+extension. In my experience, I never needed to write a C extension,
+since Python is fast enough. Nevertheless, to profile a program is
+always a good idea and Python provides a profiler module in the
+stardard library with this aim. Still, it is convenient to have
+a set of user defined functions to test the execution speed of
+few selected routines (whereas the standard profiler profiles everything).</p>
+<p>We see from the standard library documentation that
+the current time can be retrieved from the <tt class="literal"><span class="pre">time</span></tt> module: <a class="footnote-reference" href="#id18" id="id19" name="id19">[9]</a></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import time
+&gt;&gt;&gt; time.asctime()
+'Wed Jan 15 12:46:03 2003'
+</pre>
+</blockquote>
+<p>Since we are not interested in the date but only in the time, we need
+a function to extract it. This is easily implemented:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import time
+
+def get_time():
+ &quot;Return the time of the system in the format HH:MM:SS&quot;
+ return time.asctime().split()[3]
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import get_time
+&gt;&gt;&gt; get_time()
+'13:03:49'
+</pre>
+</blockquote>
+<p>Suppose, for instance, we want to know how much it takes to Python
+to write a Gigabyte of data. This can be a quite useful benchmark
+to have an idea of the I/O bottlenecks in our system. Since to take in memory
+a file of a Gigabyte can be quite problematic, let me compute the
+time spent in writing 1024 files of one Megabyte each. To this
+aim we need a <tt class="literal"><span class="pre">writefile</span></tt> function</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def writefile(fname,data):
+ f=file(fname,'w')
+ f.write(data)
+ f.close()
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>and timing function. The idea is to wrap the <tt class="literal"><span class="pre">writefile</span></tt> function in
+a <tt class="literal"><span class="pre">with_clock</span></tt> function as follows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def with_clock(func,n=1):
+ def _(*args,**kw): # this is a closure
+ print &quot;Process started on&quot;,get_time()
+ print ' .. please wait ..'
+ for i in range(n): func(*args,**kw)
+ print &quot;Process ended on&quot;,get_time()
+ return _
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The wrapper function <tt class="literal"><span class="pre">with_clock</span></tt> has converted the function <tt class="literal"><span class="pre">writefile</span></tt>
+in a function <tt class="literal"><span class="pre">with_clock(writefile)</span></tt> which has the same arguments
+of <tt class="literal"><span class="pre">writefile</span></tt>, but contains additional features: in this case
+timing capabilities. Technically speaking, the internal function <tt class="literal"><span class="pre">_</span></tt>
+is called a <em>closure</em>. Closures are very common in functional languages
+and can be used in Python too, with very little effort <a class="footnote-reference" href="#id21" id="id20" name="id20">[10]</a>.</p>
+<p>I will use closures very often in the following, and I will use
+the convention of denoting with &quot;_&quot; the inner
+function in the closure, since there is no reason of giving to it a
+descriptive name (the name 'with_clock' in the outer function
+is descriptive enough). For the same, reason I do not use a
+docstring for &quot;_&quot;. If Python would allow multistatement lambda
+functions, &quot;_&quot; would be a good candidate for an anonymous function.</p>
+<p>Here is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; data='*'*1024*1024 #one megabyte
+&gt;&gt;&gt; with_clock(writefile,n=1024)('datafile',data) #.
+Process started on 21:20:01
+ .. please wait ..
+Process ended on 21:20:57
+</pre>
+</blockquote>
+<p>This example shows that Python has written one Gigabyte of data (splitted in
+1024 chunks of one Megabyte each) in less than a minute. However,the
+result depends very much on the filesystem. I always suggest people
+to profile their programs, since one <em>always</em> find surprises.
+For instance, I have checked the performance of my laptop,
+a dual machine Windows 98 SE/ Red Hat Linux 7.3.
+The results are collected in the following table:</p>
+<blockquote>
+<table border class="table">
+<colgroup>
+<col width="27%" />
+<col width="34%" />
+<col width="39%" />
+</colgroup>
+<thead valign="bottom">
+<tr><th>Linux ext-3</th>
+<th>FAT under Linux</th>
+<th>FAT under Windows 98</th>
+</tr>
+</thead>
+<tbody valign="top">
+<tr><td>24-25 s</td>
+<td>56-58 s</td>
+<td>86-88 s</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p>We see that Linux is <em>much</em> faster: more than three times faster than
+Windows, using the same machine! Notice that the FAT filesystem under
+Linux (where it is <em>not</em> native) is remarkably faster than the FAT
+under Windows 98, where it is native !! I think that now my readers
+can begin to understand why this book has been written under Linux
+and why I <em>never</em> use Windows for programming (actually I use it only
+to see the DVD's ;-).</p>
+<p>I leave as an exercise for the reader to check the results on this
+script on their machine. Since my laptop is quite old, you will probably
+have much better performances (for instance on my linux desktop I can
+write a Gigabyte in less than 12 seconds!). However, there are <em>always</em>
+surprises: my desktop is a dual Windows 2000 machine with three different
+filesystems, Linux ext-2, FAT and NTFS. Surprisingly enough, the NT
+filesystem is the more inefficient for writing, <em>ten times slower</em>
+than Linux!</p>
+<blockquote>
+<table border class="table">
+<colgroup>
+<col width="27%" />
+<col width="34%" />
+<col width="39%" />
+</colgroup>
+<thead valign="bottom">
+<tr><th>Linux ext-2</th>
+<th>FAT under Win2000</th>
+<th>NTFS under Win2000</th>
+</tr>
+</thead>
+<tbody valign="top">
+<tr><td>11-12 s</td>
+<td>95-97 s</td>
+<td>117-120 s</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<table class="footnote" frame="void" id="id21" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id20" name="id21">[10]</a></td><td>Users of Python 2.3 can give a look to the new <tt class="literal"><span class="pre">datetime</span></tt> module,
+if they are looking for a sophisticated clock/calendar.</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id22" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id23" name="id22">[11]</a></td><td>There are good references on functional programming in Python;
+I suggest the Python Cookbook and the articles by David Mertz
+www.IBM.dW.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="functions-are-objects">
+<h2><a class="toc-backref" href="#id69" name="functions-are-objects">Functions are objects</a></h2>
+<p>As we said in the first chapter, objects have attributes accessible with the
+dot notation. This is not surprising at all. However, it could be
+surprising to realize that since Python functions are objects, they
+can have attributes, too. This could be surprising since this feature is quite
+uncommon: typically or i) the language is
+not object-oriented, and therefore functions are not objects, or ii)
+the language is strongly object-oriented and does not have functions, only
+methods. Python is a multiparadigm language (which I prefer to the
+term &quot;hybrid&quot; language), therefore it has functions that are objects,
+as in Lisp and other functional languages.
+Consider for instance the <tt class="literal"><span class="pre">get_time</span></tt> function.
+That function has at least an useful attribute, its doctring:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import get_time
+&gt;&gt;&gt; print get_time.func_doc
+Return the time of the system in the format HH:MM:SS
+</pre>
+</blockquote>
+<p>The docstring can also be obtained with the <tt class="literal"><span class="pre">help</span></tt> function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; help(get_time)
+Help on function get_time in module oopp:
+get_time()
+ Return the time of the system in the format HH:MM:SS
+</pre>
+</blockquote>
+<p>Therefore <tt class="literal"><span class="pre">help</span></tt> works on user-defined functions, too, not only on
+built-in functions. Notice that <tt class="literal"><span class="pre">help</span></tt> also returns the argument list of
+the function. For instance, this is
+the help message on the <tt class="literal"><span class="pre">round</span></tt> function that we will use in the
+following:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; help(round)
+Help on built-in function round:
+round(...)
+ round(number[, ndigits]) -&gt; floating point number
+ Round a number to a given precision in decimal digits (default 0
+ digits).This always returns a floating point number. Precision may
+ be negative.
+</pre>
+</blockquote>
+<p>I strongly recommend Python programmers to use docstrings, not
+only for clarity sake during the development, but especially because
+it is possible to automatically generate nice HTML documentation from
+the docstrings, by using the standard tool &quot;pydoc&quot;.</p>
+<p>One can easily add attributes to a function. For instance:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; get_time.more_doc='get_time invokes the function time.asctime'
+&gt;&gt;&gt; print get_time.more_doc
+get_time invokes the function time.asctime
+</pre>
+</blockquote>
+<p>Attributes can be functions, too:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def IamAfunction(): print &quot;I am a function attached to a function&quot;
+&gt;&gt;&gt; get_time.f=IamAfunction
+&gt;&gt;&gt; get_time.f()
+I am a function attached to a function
+</pre>
+</blockquote>
+<p>This is a quite impressive potentiality of Python functions, which has
+no direct equivalent in most other languages.</p>
+<p>One possible application is to fake C &quot;static&quot; variables. Suppose
+for instance we need a function remembering how may times it is
+called: we can simply use</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;double.py&gt;
+
+def double(x):
+ try: #look if double.counter is defined
+ double.counter
+ except AttributeError:
+ double.counter=0 #first call
+ double.counter+=1
+ return 2*x
+
+double(double(2))
+print &quot;double has been called %s times&quot; % double.counter
+
+#&lt;/double.py&gt;
+</pre>
+</blockquote>
+<p>with output <tt class="literal"><span class="pre">double</span> <span class="pre">has</span> <span class="pre">been</span> <span class="pre">called</span> <span class="pre">2</span> <span class="pre">times</span></tt>.
+A more elegant approach involves closures. A closure can enhance an
+ordinary function, providing to it the capability of remembering
+the results of its previous calls and avoiding the duplication of
+computations:</p>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def withmemory(f):
+ &quot;&quot;&quot;This closure invokes the callable object f only if need there is&quot;&quot;&quot;
+ argskw=[]; result=[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ i=argskw.index(akw)
+ except ValueError: # there is no previously stored result
+ res=f(*args,**kw) # returns the new result
+ argskw.append(akw) # update argskw
+ result.append(res) # update result
+ return res
+ else:
+ return result[i]
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+def memoize(f):
+ &quot;&quot;&quot;This closure remembers all f invocations&quot;&quot;&quot;
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+#&lt;/oopp.py&gt;
+</pre>
+<p>Now, if we call the wrapped function <tt class="literal"><span class="pre">f</span></tt> twice with the same arguments,
+Python can give the result without repeating the (possibly very long)
+computation.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x):
+... print 'called f'
+... return x*x
+&gt;&gt;&gt; wrapped_f=withmemory(f)
+&gt;&gt;&gt; wrapped_f(2) #first call with the argument 2; executes the computation
+called f
+4
+&gt;&gt;&gt; wrapped_f(2) #does not repeat the computation
+4
+&gt;&gt;&gt; wrapped_f.result
+[4]
+&gt;&gt;&gt; wrapped_f.argskw
+[((2,), {})]
+</pre>
+</blockquote>
+</div>
+<div class="section" id="profiling-functions">
+<h2><a class="toc-backref" href="#id70" name="profiling-functions">Profiling functions</a></h2>
+<p>The <tt class="literal"><span class="pre">with_clock</span></tt> function provided before was intended to be
+pedagogical; as such it is a quite poor solution to the
+problem of profiling a Python routine. A better solution involves
+using two others functions in the time library, <tt class="literal"><span class="pre">time.time()</span></tt>
+that gives that time in seconds elapsed from a given date, and
+<tt class="literal"><span class="pre">time.clock()</span></tt> that gives the time spent by the CPU in a given
+computation. Notice that <tt class="literal"><span class="pre">time.clock()</span></tt> has not an infinite
+precision (the precision depends on the system) and one
+should expect relatively big errors if the function runs in
+a very short time. That's the reason why it is convenient
+to execute multiple times short functions and divide the total
+time by the number of repetitions. Moreover, one should subtract the
+overhead do to the looping. This can be computed with the following
+routine:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def loop_overhead(N):
+ &quot;Computes the time spent in empty loop of N iterations&quot;
+ t0=time.clock()
+ for i in xrange(N): pass
+ return time.clock()-t0
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>For instance, on my laptop an empty loop of one million of iterations
+is performed in 1.3 seconds. Typically the loop overhead is negligible,
+whereas the real problem is the function overhead.</p>
+<p>Using the attribute trick discussed above, we may
+define a <tt class="literal"><span class="pre">with_timer</span></tt> function that enhances quite a bit
+<tt class="literal"><span class="pre">with_clock</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def with_timer(func, modulename='__main__', n=1, logfile=sys.stdout):
+ &quot;&quot;&quot;Wraps the function func and executes it n times (default n=1).
+ The average time spent in one iteration, express in milliseconds,
+ is stored in the attributes func.time and func.CPUtime, and saved
+ in a log file which defaults to the standard output.
+ &quot;&quot;&quot;
+ def _(*args,**kw): # anonymous function
+ time1=time.time()
+ CPUtime1=time.clock()
+ print 'Executing %s.%s ...' % (modulename,func.__name__),
+ for i in xrange(n): res=func(*args,**kw) # executes func n times
+ time2=time.time()
+ CPUtime2=time.clock()
+ func.time=1000*(time2-time1)/n
+ func.CPUtime=1000*(CPUtime2-CPUtime1-loop_overhead(n))/n
+ if func.CPUtime&lt;10: r=3 #better rounding
+ else: r=1 #default rounding
+ print &gt;&gt; logfile, 'Real time: %s ms' % round(func.time,r),
+ print &gt;&gt; logfile, ' CPU time: %s ms' % round(func.CPUtime,r)
+ return res
+ return _
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here it is an example of application:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import with_timer,writefile
+&gt;&gt;&gt; data='*'*1024*1024 #one megabyte
+&gt;&gt;&gt; with_timer(writefile,n=1024)('datafile',data) #.
+Executing writefile ... Real time: 60.0 ms CPU time: 42.2 ms
+</pre>
+</blockquote>
+<p>The CPU time can be quite different from the real time,
+as you can see in the following example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import time
+&gt;&gt;&gt; def sleep(): time.sleep(1)
+...
+&gt;&gt;&gt; with_timer(sleep)() #.
+Executing sleep ... Real time: 999.7 ms CPU time: 0.0 ms
+</pre>
+</blockquote>
+<p>We see that Python has run for 999.7 ms (i.e. 1 second, up to
+approximation errors in the system clock) during which the CPU has
+worked for 0.0 ms (i.e. the CPU took a rest ;-).
+The CPU time is the relevant time to use with the purpose of
+benchmarking Python speed.</p>
+<p>I should notice that the approach pursued in <tt class="literal"><span class="pre">with_timer</span></tt> is still
+quite simple. A better approach would be to
+plot the time versus the number of iteration, do a linear interpolation
+and extract the typical time for iteration from that. This allows
+to check visually that the machine is not doing something strange
+during the execution time and it is what
+I do in my personal benchmark routine; doing something similar is
+left as an exercise for the reader ;-).</p>
+<p>Another approach is to use the <tt class="literal"><span class="pre">timeit.py</span></tt> module (new in Python 2.3,
+but works also with Python 2.2):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import timeit,__main__,warnings
+
+warnings.filterwarnings('ignore',
+'import \* only allowed at module level',SyntaxWarning)
+
+def timeit_(stmt,setup='from __main__ import *',n=1000):
+ t=timeit.Timer(stmt,setup)
+ try: print t.repeat(number=n) # class timeit 3 times
+ except: t.print_exc()
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>It is often stated that Python is slow and quite ineffective
+in application involving hard computations. This is generally speaking
+true, but how bad is the situation ? To test the (in)efficiency of
+Python on number crunching, let me give a function to compute the
+Mandelbrot set, which I have found in the Python Frequently Asked
+Question (FAQ 4.15. <em>Is it possible to write obfuscated one-liners
+in Python?</em>).
+This function is due to Ulf Bartelt and you should ask him to know how
+does it work ;-)</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def mandelbrot(row,col):
+ &quot;Computes the Mandelbrot set in one line&quot;
+ return (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(
+ lambda x,y:x+y,map(lambda y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=
+ lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, Sx=Sx,Sy=Sy:reduce(
+ lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, i=i,
+ Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k&lt;=0)
+ or (x*x+y*y&gt;=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):
+ f(xc,yc,x,y,k,f):chr(64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),
+ range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(
+ -2.1, 0.7, -1.2, 1.2, 30, col, row)
+ # \___ ___/ \___ ___/ | | |_ lines on screen
+ # V V | |______ columns on screen
+ # | | |__________ maximum of &quot;iterations&quot;
+ # | |_________________ range on y axis
+ # |____________________________ range on x axis
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is the benchmark on my laptop:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import mandelbrot,with_timer
+&gt;&gt;&gt; row,col=24,75
+&gt;&gt;&gt; output=with_timer(mandelbrot,n=1)(row,col)
+Executing __main__.mandelbrot ... Real time: 427.9 ms CPU time: 410.0 ms
+&gt;&gt;&gt; for r in range(row): print output[r*col:(r+1)*col]
+...
+BBBBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC
+BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+BBB_____________________________________________________________OKIHGFFEEEE
+BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+</pre>
+</blockquote>
+<p>I am willing to concede that this code is not typical Python code and
+actually it could be an example of <em>bad</em> code, but I wanted a nice ASCII
+picture on my book ... :) Also, this prove that Python is not necessarily
+readable and easy to understand ;-)
+I leave for the courageous reader to convert the previous algorithm to C and
+measure the difference in speed ;-)</p>
+</div>
+<div class="section" id="about-python-speed">
+<h2><a class="toc-backref" href="#id71" name="about-python-speed">About Python speed</a></h2>
+<p>The best way to improved the speed is to improve the algorithm; in
+this sense Python is an ideal language since it allows you to test
+many algorithms in an incredibly short time: in other words, the time you
+would spend fighting with the compiler in other languages, in Python
+can be used to improve the algorithm.
+However in some cases, there is little to do: for instance, in many
+problems one has to run lots of loops, and Python loops are horribly
+inefficients as compared to C loops. In this case the simplest possibility
+is to use Psyco. Psyco is a specialing Python compiler written by Armin
+Rigo. It works for 386 based processors and allows Python to run loops at
+C speed. Installing Psyco requires $0.00 and ten minutes of your time:
+nine minutes to find the program, download it, and install it; one
+minute to understand how to use it.</p>
+<p>The following script explains both the usage and the advantages of Psyco:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;psyco1.py&gt;
+
+import oopp,sys
+try:
+ import psyco
+except ImportError:
+ print &quot;Psyco is not installed, sorry.&quot;
+else:
+ n=1000000 # 1,000,000 loops
+
+ without=oopp.loop_overhead(n)
+ print &quot;Without Psyco:&quot;,without
+
+ psyco.bind(oopp.loop_overhead) #compile the empty_loop
+
+ with=oopp.loop_overhead(n)
+ print &quot;With Psyco:&quot;,with
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+#&lt;/psyco1.py&gt;
+</pre>
+</blockquote>
+<p>The output is impressive:</p>
+<blockquote>
+<pre class="literal-block">
+Without Psyco: 1.3
+With Psyco: 0.02
+Speedup = 65.0x
+</pre>
+</blockquote>
+<p>Notice that repeating the test, you will obtain different speedups.
+On my laptop, the speedup for an empty loop of 10,000,000 of
+iteration is of the order of 70x, which is the same speed of a C loop,
+actually (I checked it). On my desktop, I have even found a speedup of
+94x !</p>
+<p>However, I must say that Psyco has some limitations. The problem is
+the function call overhead. Psyco enhances the overhead and in some
+programs it can even <em>worsen</em> the performance (this is way you should
+<em>never</em> use the <tt class="literal"><span class="pre">psyco.jit()</span></tt> function that wraps all the functions of
+your program: you should only wrap the bottleneck loops). Generally speaking,
+you should expect a much more modest improvement, a factor of 2 or 3
+is what I obtain usually in my programs.</p>
+<p>Look at this second example, which essentially measure the function
+call overhead by invoking the <tt class="literal"><span class="pre">do_nothing</span></tt> function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;psyco2.py&gt;
+
+import oopp
+try:
+ import psyco
+except ImportError:
+ print &quot;Psyco is not installed, sorry.&quot;
+else:
+ n=10000 # 10,000 loops
+
+ def do_nothing_loop():
+ for i in xrange(n): oopp.do_nothing()
+
+ print &quot;Without Psyco:\n&quot;
+ oopp.with_timer(do_nothing_loop,n=5)() #50,000 times
+
+ without=do_nothing_loop.CPUtime
+
+ psyco.bind(do_nothing_loop)
+ print &quot;With Psyco:\n&quot;
+ oopp.with_timer(do_nothing_loop,n=5)() #50,000 times
+
+ with=do_nothing_loop.CPUtime
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+#&lt;/psyco2.py&gt;
+</pre>
+</blockquote>
+<p>The output is less incredible:</p>
+<blockquote>
+<pre class="literal-block">
+Without Psyco:
+Executing do_nothing_loop ... Real time: 138.2 ms CPU time: 130.0 ms
+With Psyco:
+Executing do_nothing_loop ... Real time: 70.0 ms CPU time: 68.0 ms
+Speedup = 1.9x
+</pre>
+</blockquote>
+<p>However, this is still impressive, if you think that you can double
+the speed of your program by adding <em>a line</em> of code! Moreover this
+example is not fair since Psyco cannot improve very much the performance
+for loops invoking functions with a variable number of arguments. On the
+other hand, it can do quite a lot for loops invoking functions with
+a fixed number of arguments. I have checked that you can easily reach
+speedups of 20x (!). The only disadvantage is that a program invoking
+Psyco takes much more memory, than a normal Python program, but this
+is not a problem for most applications in nowadays computers.
+Therefore, often Psyco
+can save you the effort of going trough a C extension. In some cases,
+however, there is no hope: I leave as an exercise for the reader
+to check (at least the version 0.4.1 I am using now) is unable to
+improve the performance on the Mandelbrot set example. This proves
+that in the case bad code, there is no point in using a compiler:
+you have to improve the algorithm first !</p>
+<p>By the way, if you really want to go trough a C extension with a minimal
+departure from Python, you can use Pyrex by Greg Ewing. A Pyrex program
+is essentially a Python program with variable declarations that is
+automatically converted to C code. Alternatively, you can inline
+C functions is Python with <tt class="literal"><span class="pre">weave</span></tt> of ...
+Finally, if you want to access C/C++ libraries, there tools
+like Swig, Booster and others.</p>
+</div>
+<div class="section" id="tracing-functions">
+<h2><a class="toc-backref" href="#id72" name="tracing-functions">Tracing functions</a></h2>
+<p>Typically, a script contains many functions that call themselves each
+other when some conditions are satisfied. Also, typically during
+debugging things do not work the way we would like and it is not
+clear which functions are called, in which order they are called,
+and which parameters are passed. The best way to know all these
+informations, is to trace the functions in our script, and to write
+all the relevant informations in a log file. In order to keep the
+distinction between the traced functions and the original one, it
+is convenient to collect all the wrapped functions in a separate dictionary.
+The tracing of a single function can be done with a closure
+like this:</p>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def with_tracer(function,namespace='__main__',output=sys.stdout, indent=[0]):
+ &quot;&quot;&quot;Closure returning traced functions. It is typically invoked
+ trough an auxiliary function fixing the parameters of with_tracer.&quot;&quot;&quot;
+ def _(*args,**kw):
+ name=function.__name__
+ i=' '*indent[0]; indent[0]+=4 # increases indentation
+ output.write(&quot;%s[%s] Calling '%s' with arguments\n&quot; %
+ (i,namespace,name))
+ output.write(&quot;%s %s ...\n&quot; % (i,str(args)+str(kw)))
+ res=function(*args,**kw)
+ output.write(&quot;%s[%s.%s] called with result: %s\n&quot;
+ % (i,namespace,name,str(res)))
+ indent[0]-=4 # restores indentation
+ return res
+ return _ # the traced function
+
+#&lt;/oopp.py&gt;
+</pre>
+<p>Here is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import with_tracer
+&gt;&gt;&gt; def fact(n): # factorial function
+... if n==1: return 1
+... else: return n*fact(n-1)
+&gt;&gt;&gt; fact=with_tracer(fact)
+&gt;&gt;&gt; fact(3)
+[__main__] Calling 'fact' with arguments
+ (3,){} ...
+ [__main__] Calling 'fact' with arguments
+ (2,){} ...
+ [__main__] Calling 'fact' with arguments
+ (1,){} ...
+ [__main__.fact] called with result: 1
+ [__main__.fact] called with result: 2
+[__main__.fact] called with result: 6
+6
+</pre>
+</blockquote>
+<p>The logic behind <tt class="literal"><span class="pre">with_tracer</span></tt> should be clear; the only trick is the
+usage of a default list as a way to store a global indentation parameter.
+Since <tt class="literal"><span class="pre">indent</span></tt> is mutable, the value of <tt class="literal"><span class="pre">indent[0]</span></tt> changes at any
+recursive call of the traced function, resulting in a nested display.</p>
+<p>Typically, one wants to trace all the functions in a given module;
+this can be done trough the following function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+from types import *
+
+isfunction=lambda f: isinstance(f,(FunctionType,BuiltinFunctionType))
+
+def wrapfunctions(obj,wrapper,err=None,**options):
+ &quot;Traces the callable objects in an object with a dictionary&quot;
+ namespace=options.get('namespace',getattr(obj,'__name__',''))
+ output=options.get('output',sys.stdout)
+ dic=dict([(k,wrapper(v,namespace,output))
+ for k,v in attributes(obj).items() if isfunction(v)])
+ customize(obj,err,**dic)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice that 'wrapfunctions' accepts as first argument an object with
+a <tt class="literal"><span class="pre">__dict__</span></tt> attribute (such as a module or a class) or with some
+explicit attributes (such as a simple object) and modifies it. One can
+trace a module as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracemodule.py&gt;
+
+import oopp,random
+
+oopp.wrapfunctions(random,oopp.with_tracer)
+
+random.random()
+
+#&lt;/tracemodule.py&gt;
+</pre>
+</blockquote>
+<p>with output</p>
+<blockquote>
+<pre class="literal-block">
+[random] Calling 'random' with arguments
+(){} ...
+-&gt; 'random.random' called with result: 0.175450439202
+</pre>
+</blockquote>
+<p>The beauty of the present approach is its generality: 'wrap' can be
+used to add any kind of capabilities to a pre-existing module.
+For instance, we could time the functions in a module, with the
+purpose of looking at the bottlenecks. To this aim, it is enough
+to use a 'timer' nested closure:</p>
+<p>An example of calling is <tt class="literal"><span class="pre">wrapfunction(obj,timer,iterations=1)</span></tt>.</p>
+<p>We may also compose our closures; for instance one could define a
+<tt class="literal"><span class="pre">with_timer_and_tracer</span></tt> closure:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; with_timer_and_tracer=lambda f: with_timer(with_tracer(f))
+</pre>
+</blockquote>
+<p>It should be noticed that Python comes with a standard profiler
+(in my system it is located in <tt class="literal"><span class="pre">/usr/local/lib/python2.2/profile.py</span></tt>)
+that allows to profile a script or a module (try
+python /usr/local/lib/python2.2/profile.py oopp.py)</p>
+<p>or</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import profile; help(profile)
+</pre>
+</blockquote>
+<p>and see the on-line documentation.</p>
+</div>
+<div class="section" id="tracing-objects">
+<h2><a class="toc-backref" href="#id73" name="tracing-objects">Tracing objects</a></h2>
+<p>In this section, I will give a more sophisticated example, in which
+one can easily understand why the Python ability of changing methods and
+attributes during run-time, is so useful.
+As a preparation to the real example, let me
+first introduce an utility routine that allows the user
+to add tracing capabilities to a given object.
+Needless to say, this feature can be invaluable during debugging, or in trying
+to understand the behaviour of a program written by others.</p>
+<p>This routine is a little complex and needs some explanation.</p>
+<ol class="arabic simple">
+<li>The routine looks in the attributes of the object and try to access them.</li>
+<li>If the access is possible, the routines looks for methods (methods
+are recognized trough the <tt class="literal"><span class="pre">inspect.isroutine</span></tt> function in the
+standard library) and ignores regular attributes;</li>
+<li>The routine try to override the original methods with improved ones,
+that possess tracing capabilities;</li>
+<li>the traced method is obtained with the wrapping trick discussed before.</li>
+</ol>
+<p>I give now the real life example that I have anticipated before.
+Improvements and elaborations of this example can be useful to the
+professional programmer, too. Suppose you have an XML text you want
+to parse. Python provides excellent support for this kind of operation
+and various standard modules. One of the most common is the <tt class="literal"><span class="pre">expat</span></tt>
+module (see the standard library documentation for more).</p>
+<p>If you are just starting using the module, it is certainly useful
+to have a way of tracing its behaviour; this is especially true if
+you you find some unexpected error during the parsing of a document
+(and this may happens even if you are an experience programmer ;-).</p>
+<p>The tracing routine just defined can be used to trace the parser, as
+it is exemplified in the following short script:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;expat.py&gt;
+
+import oopp, xml.parsers.expat, sys
+
+# text to be parsed
+text_xml=&quot;&quot;&quot;\
+&lt;?xml version=&quot;1.0&quot;?&gt;
+&lt;parent id=&quot;dad&quot;&gt;
+&lt;child name=&quot;kid&quot;&gt;Text goes here&lt;/child&gt;
+&lt;/parent&gt;&quot;&quot;&quot;
+
+# a few do nothing functions
+def start(*args): pass
+def end(*args): pass
+def handler(*args): pass
+
+# a parser object
+p = xml.parsers.expat.ParserCreate()
+
+p.StartElementHandler = start
+p.EndElementHandler = end
+p.CharacterDataHandler = handler
+
+#adds tracing capabilities to p
+oopp.wrapfunctions(p,oopp.with_tracer, err=sys.stdout)
+
+p.Parse(text_xml)
+
+#&lt;/expat.py&gt;
+</pre>
+</blockquote>
+<p>The output is:</p>
+<blockquote>
+<pre class="literal-block">
+Error: SetBase cannot be set
+Error: Parse cannot be set
+Error: ParseFile cannot be set
+Error: GetBase cannot be set
+Error: SetParamEntityParsing cannot be set
+Error: ExternalEntityParserCreate cannot be set
+Error: GetInputContext cannot be set
+[] Calling 'start' with arguments
+ (u'parent', {u'id': u'dad'}){} ...
+[.start] called with result: None
+[] Calling 'handler' with arguments
+ (u'\n',){} ...
+[.handler] called with result: None
+[] Calling 'start' with arguments
+ (u'child', {u'name': u'kid'}){} ...
+[.start] called with result: None
+[] Calling 'handler' with arguments
+ (u'Text goes here',){} ...
+[.handler] called with result: None
+[] Calling 'end' with arguments
+ (u'child',){} ...
+[.end] called with result: None
+[] Calling 'handler' with arguments
+ (u'\n',){} ...
+[.handler] called with result: None
+[] Calling 'end' with arguments
+ (u'parent',){} ...
+[.end] called with result: None
+</pre>
+</blockquote>
+<p>This is a case where certain methods cannot be managed with
+<tt class="literal"><span class="pre">getattr/setattr</span></tt>, because they are internally coded in C: this
+explain the error messages at the beginning. I leave as an exercise
+for the reader to understand the rest ;-)</p>
+</div>
+<div class="section" id="inspecting-functions">
+<h2><a class="toc-backref" href="#id74" name="inspecting-functions">Inspecting functions</a></h2>
+<p>Python wonderful introspection features are really impressive when applied
+to functions. It is possible to extract a big deal of informations
+from a Python function, by looking at its associated <em>code object</em>.
+For instance, let me consider my, <tt class="literal"><span class="pre">do_nothing</span></tt> function: its associated
+code object can be extracted from the <tt class="literal"><span class="pre">func_code</span></tt> attribute:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; co=do_nothing.func_code # extracts the code object
+&gt;&gt;&gt; co
+&lt;code object do_nothing at 0x402c5d20, file &quot;oopp.py&quot;, line 48&gt;
+&gt;&gt;&gt; type(co)
+&lt;type 'code'&gt;
+</pre>
+</blockquote>
+<p>The code object is far being trivial: the docstring says it all:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print type(co).__doc__
+code(argcount, nlocals, stacksize, flags, codestring, constants, names,
+ varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])
+Create a code object. Not for the faint of heart.
+</pre>
+</blockquote>
+<p>In the case of my <tt class="literal"><span class="pre">do_nothing</span></tt> function, the code object
+possesses the following attributes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print pretty(attributes(co))
+co_argcount = 0
+co_cellvars = ()
+co_code = dS
+co_consts = (None,)
+co_filename = oopp.py
+co_firstlineno = 48
+co_flags = 15
+co_freevars = ()
+co_lnotab =
+co_name = do_nothing
+co_names = ()
+co_nlocals = 2
+co_stacksize = 1
+co_varnames = ('args', 'kw')
+</pre>
+</blockquote>
+<p>Some of these arguments are pretty technical and implementation dependent;
+however, some of these are pretty clear and useful:</p>
+<blockquote>
+<ul class="simple">
+<li>co_argcount is the total number of arguments</li>
+<li>co_filename is the name of the file where the function is defined</li>
+<li>co_firstlineno is the line number where the function is defined</li>
+<li>co_name is the name of the function</li>
+<li>co_varnames are the names</li>
+</ul>
+</blockquote>
+<p>The programmer that it is not a &quot;faint of heart&quot; can study
+the built-in documentation on code objects; s/he should try</p>
+<blockquote>
+<pre class="literal-block">
+for k,v in attributes(co).iteritems(): print k,':',v.__doc__,'\n'
+</pre>
+<p># does not work now !!</p>
+<pre class="literal-block">
+add=[lambda x,i=i: x+i for i in range(10)]
+
+&gt;&gt;&gt; def f(y):
+... return lambda x: x+y
+...
+&gt;&gt;&gt; f(1).func_closure #closure cell object
+(&lt;cell at 0x402b56bc: int object at 0x811d6c8&gt;,)
+</pre>
+</blockquote>
+<p>func.defaults, closure, etc.</p>
+<p>#how to extract (non-default) arguments as help does.</p>
+<p>print (lambda:None).func_code.co_filename</p>
+<p>One cannot change the name of a function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(): pass
+...
+&gt;&gt;&gt; f.__name__='ciao' # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: readonly attribute
+</pre>
+</blockquote>
+<p>However, one can create a copy with a different name:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def copyfunc(f,newname=None): # works under Python 2.3
+ if newname is None: newname=f.func_name # same name
+ return FunctionType(f.func_code, globals(), newname,
+ f.func_defaults, f.func_closure)
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; copyfunc(f,newname='f2')
+&lt;function f2 at 0x403e233c&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">copy</span></tt> module would not do the job:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import copy
+&gt;&gt;&gt; copy.copy(f) # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;/usr/local/lib/python2.3/copy.py&quot;, line 84, in copy
+ y = _reconstruct(x, reductor(), 0)
+ File &quot;/usr/local/lib/python2.3/copy_reg.py&quot;, line 57, in _reduce
+ raise TypeError, &quot;can't pickle %s objects&quot; % base.__name__
+TypeError: can't pickle function objects
+</pre>
+</blockquote>
+</div>
+</div>
+<div class="section" id="the-beauty-of-objects">
+<h1><a class="toc-backref" href="#id75" name="the-beauty-of-objects">THE BEAUTY OF OBJECTS</a></h1>
+<p>In this chapter I will show how to define generic objects in Python, and
+how to manipulate them.</p>
+<div class="section" id="user-defined-objects">
+<h2><a class="toc-backref" href="#id76" name="user-defined-objects">User defined objects</a></h2>
+<p>In Python, one cannot directly modify methods and attributes of built-in
+types, since this would be a potentially frightening source of bugs.
+Imagine for instance of changing the sort method of a list and invoking an
+external module expecting the standard sort: all kind of hideous outcome
+could happen.</p>
+<p>Nevertheless, in Python, as in all OOP languages, the user can define
+her own kind of objects, customized to satisfy her needs. In order to
+define a new object, the user must define the class of the objects she
+needs. The simplest possible class is a do-nothing class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Object(object):
+ &quot;A convenient Object class&quot;
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Elements of the <tt class="literal"><span class="pre">Object</span></tt> class can be created (instantiated) quite
+simply:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Object
+&gt;&gt;&gt; obj1=Object()
+&gt;&gt;&gt; obj1
+&lt;oopp.Object object at 0x81580ec&gt;
+&gt;&gt;&gt; obj2=Object()
+obj2
+&lt;object.Object object at 0x8156704&gt;
+</pre>
+</blockquote>
+<p>Notice that the hexadecimal number 0x81580ec is nothing else that the
+unique object reference to <tt class="literal"><span class="pre">obj1</span></tt></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; hex(id(obj1))
+ '0x81580ec'
+</pre>
+</blockquote>
+<p>whereas 0x8156704 is the object reference of <tt class="literal"><span class="pre">obj2</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; hex(id(obj2))
+'0x8156704'
+</pre>
+</blockquote>
+<p>However, at this point <tt class="literal"><span class="pre">obj1</span></tt> and <tt class="literal"><span class="pre">obj2</span></tt> are generic
+doing nothing objects . Nevertheless, they have
+at least an useful attribute, the class docstring:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; obj1.__doc__ #obj1 docstring
+'A convenient Object class'
+&gt;&gt;&gt; obj2.__doc__ # obj2 docstring: it's the same
+'A convenient Object class'
+</pre>
+</blockquote>
+<p>Notice that the docstring is associate to the class and therefore all
+the instances share the same docstring, unless one explicitly assigns
+a different docstring to some instance. <tt class="literal"><span class="pre">__doc__</span></tt>
+is a class attribute (or a static attribute for readers familiar with the
+C++/Java terminology) and the expression is actually syntactic sugar for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Object(object): # with explicit assignement to __doc__
+... __doc__ = &quot;A convenient Object class&quot;
+</pre>
+</blockquote>
+<p>Since instances of 'Object' can be modified, I can transform them in
+anything I want. For instance, I can create a simple clock:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; myclock=Object()
+&gt;&gt;&gt; myclock
+&lt;__main__.Object object at 0x8124614&gt;
+</pre>
+</blockquote>
+<p>A minimal clock should at least print the current time
+on the system. This is given by the <tt class="literal"><span class="pre">get_time</span></tt> function
+we defined in the first chapter. We may &quot;attach&quot; that function
+to our clock as follows:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; myclock.get_time=oopp.get_time
+&gt;&gt;&gt; myclock.get_time # this is a function, not a method
+&lt;function get_time at 0x815c40c&gt;
+</pre>
+</blockquote>
+<p>In other words, we have converted the <tt class="literal"><span class="pre">oopp.get_time</span></tt> function to a
+<tt class="literal"><span class="pre">get_time</span></tt> function of the object <tt class="literal"><span class="pre">myclock</span></tt>. The procedure works</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; myclock.get_time()
+'15:04:57'
+</pre>
+</blockquote>
+<p>but has a disadvantage: if we instantiate another
+clock</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Object
+&gt;&gt;&gt; otherclock=Object()
+</pre>
+</blockquote>
+<p>the other clock will <tt class="literal"><span class="pre">not</span></tt> have a get_time method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; otherclock.get_time() #first attempt; error
+AttributeError: 'Object' object has no attribute 'get_time'
+</pre>
+</blockquote>
+<p>Notice instead that the docstring is a <em>class attribute</em>, i.e. it
+is defined both for the class and <em>all instances</em> of the class,
+therefore even for <tt class="literal"><span class="pre">otherclock</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Object.__doc__
+'A convenient Object class'
+&gt;&gt;&gt; otherclock.__doc__
+'A convenient Object class'
+</pre>
+</blockquote>
+<p>We would like to convert the <tt class="literal"><span class="pre">get_time</span></tt> function to a
+<tt class="literal"><span class="pre">get_time</span></tt> method for the <em>entire</em> class 'Object', i.e. for all its
+instances. Naively, one would be tempted to write the following:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Object.get_time=oopp.get_time
+</pre>
+</blockquote>
+<p>However this would not work:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; otherclock.get_time() #second attempt; still error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: oopp.get_time() takes no arguments (1 given)
+</pre>
+</blockquote>
+<p>This error message is something that all Python beginners encounter
+(and sometimes even non-beginners ;-). The solution is to introduce
+an additional argument:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Object.get_time=lambda self : oopp.get_time()
+&gt;&gt;&gt; otherclock.get_time # this is method now, not a function
+&lt;bound method Object.&lt;lambda&gt; of &lt;__main__.Object object at 0x815881c&gt;&gt;
+&gt;&gt;&gt; otherclock.get_time() #third attempt
+'15:28:41'
+</pre>
+</blockquote>
+<p>Why this works ? The explanation is the following:
+when Python encounters an expression of the form
+<tt class="literal"><span class="pre">objectname.methodname()</span></tt> it looks if there is a already a method
+<em>attached</em> to the object:</p>
+<blockquote>
+<ol class="loweralpha simple">
+<li>if yes it invokes it with no arguments
+(this is why our first example worked);</li>
+<li>if not it looks at the class of the object; if there is a method
+bound to the class it invokes that method <em>by passing the
+object as first argument</em>.</li>
+</ol>
+</blockquote>
+<p>When we invoked <tt class="literal"><span class="pre">otherclock.get_time()</span></tt> in our second attempt, Python
+found that the function <tt class="literal"><span class="pre">get_time</span></tt> was defined at the class level,
+and sent it the <tt class="literal"><span class="pre">otherclock</span></tt> object as first argument: however <tt class="literal"><span class="pre">get_time</span></tt>
+was bind to <tt class="literal"><span class="pre">func_get_time</span></tt>, which is function with <em>no</em> arguments: whence
+the error message. The third attempt worked since, thanks to the
+lambda function trick, the <tt class="literal"><span class="pre">get_time</span></tt> function has been converted to
+a function accepting a first argument.</p>
+<p>Therefore that's the rule: in Python, one can define methods
+at the class level, provided one explitely introduces a first argument
+containing the object on which the method is invoked.</p>
+<p>This first argument is traditionally called <tt class="literal"><span class="pre">self</span></tt>; the name 'self' is not
+enforced, one could use any other valid Python identifier, however the
+convention is so widespread that practically everybody uses it;
+pychecker will even raise a warning in the case you don't follow the
+convention.</p>
+<p>I have just shown one the most interesting features of Python, its
+<em>dynamicity</em>: you can create the class first and add methods to it later.
+That logic cannot be followed in typical compiled language as C++. On the
+other hand, one can also define methods in a static, more traditional way:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;clock1.py&gt;
+
+&quot;Shows how to define methods inside the class (statically)&quot;
+
+import oopp
+
+class Clock(object):
+ 'Clock class; version 0.1'
+ def get_time(self): # method defined inside the class
+ return oopp.get_time()
+
+myclock=Clock() #creates a Clock instance
+print myclock.get_time() # print the current time
+
+#&lt;/clock1.py&gt;
+</pre>
+</blockquote>
+<p>In this case we have defined the <tt class="literal"><span class="pre">get_time</span></tt> method inside the class as a
+normal function with an explicit first argument called self; this is
+entirely equivalent to the use of a lambda function.</p>
+<p>The syntax <tt class="literal"><span class="pre">myclock.get_time()</span></tt> is actually syntactic sugar for
+<tt class="literal"><span class="pre">Clock.get_time(myclock)</span></tt>.</p>
+<p>In this second form, it is clear the <tt class="literal"><span class="pre">get_time</span></tt> is really &quot;attached&quot; to the
+class, not to the instance.</p>
+</div>
+<div class="section" id="objects-have-static-methods-and-classmethods">
+<h2><a class="toc-backref" href="#id77" name="objects-have-static-methods-and-classmethods">Objects have static methods and classmethods</a></h2>
+<blockquote>
+<pre class="line-block">
+<em>There should be one--and preferably only one--obvious way to do it</em>
+-- Tim Peters, <em>The Zen of Python</em>.
+</pre>
+</blockquote>
+<p>For any rule, there is an exception, and despite the Python's motto
+there are many ways to define methods in classes. The way I presented
+before was the obvious one before the Python 2.2 revolution; however,
+nowadays there is another possibility that, even if less obvious, has the
+advantage of some elegance (and it is also slightly more efficient too, even if
+efficiency if never a primary concern for a Python programmer).
+We see that the first argument in the <tt class="literal"><span class="pre">get_time</span></tt> method is useless,
+since the time is computed from the <tt class="literal"><span class="pre">time.asctime()</span></tt> function which
+does not require any information about the object that is calling
+it. This waste is ugly, and since according to the Zen of Python</p>
+<blockquote>
+<em>Beautiful is better than ugly.</em></blockquote>
+<p>we should look for another way. The solution is to use a <em>static method</em>:
+when a static method is invoked, the calling object is <em>not</em> implicitly passed
+as first argument. Therefore we may use a normal function with no additional
+first argument to define the <tt class="literal"><span class="pre">get_time</span></tt> method:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Clock(object):
+ 'Clock with a staticmethod'
+ get_time=staticmethod(get_time)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here is how it works:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Clock
+&gt;&gt;&gt; Clock().get_time() # get_time is bound both to instances
+'10:34:23'
+&gt;&gt;&gt; Clock.get_time() # and to the class
+'10:34:26'
+</pre>
+</blockquote>
+<p>The staticmethod idiom converts the lambda function to a
+static method of the class 'Clock'. Notice that one can avoid the
+lambda expression and use the (arguably more Pythonic) idiom</p>
+<blockquote>
+<pre class="literal-block">
+def get_time()
+ return oopp.get_time()
+get_time=staticmethod(oopp.get_time)
+</pre>
+</blockquote>
+<p>as the documentation suggests:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print staticmethod.__doc__
+staticmethod(function) -&gt; method
+Convert a function to be a static method.
+A static method does not receive an implicit first argument.
+To declare a static method, use this idiom:
+ class C:
+ def f(arg1, arg2, ...): ...
+ f = staticmethod(f)
+It can be called either on the class (e.g. C.f()) or on an instance
+(e.g. C().f()). The instance is ignored except for its class.
+Static methods in Python are similar to those found in Java or C++.
+For a more advanced concept, see the classmethod builtin.
+</pre>
+</blockquote>
+<p>At the present the notation for static methods is still rather ugly,
+but it is expected to improve in future versions of Python (probably
+in Python 2.4). Documentation for static methods can
+be found in Guido's essay and in the PEP.. : however this is intended for
+developers.</p>
+<p>As the docstring says, static methods are also &quot;attached&quot; to the
+class and may be called with the syntax <tt class="literal"><span class="pre">Clock.get_time()</span></tt>.</p>
+<p>A similar remark applies for the so called <em>classmethods</em>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print classmethod.__doc__
+classmethod(function) -&gt; method
+Convert a function to be a class method.
+A class method receives the class as implicit first argument,
+just like an instance method receives the instance.
+To declare a class method, use this idiom:
+class C:
+ def f(cls, arg1, arg2, ...): ...
+ f = classmethod(f)
+It can be called either on the class (e.g. C.f()) or on an instance
+(e.g. C().f()). The instance is ignored except for its class.
+If a class method is called for a derived class, the derived class
+object is passed as the implied first argument.
+Class methods are different than C++ or Java static methods.
+If you want those, see the staticmethod builtin.
+</pre>
+</blockquote>
+<p>#When a regular method is invoked, a reference to the calling object is
+#implicitely passed as first argument; instead, when a static method is
+#invoked, no reference to the calling object is passed.</p>
+<p>As the docstring says, classmethods are convenient when one wants to pass
+to a method the calling <em>class</em>, not the calling object. Here there is an
+example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Clock(object): pass
+&gt;&gt;&gt; Clock.name=classmethod(lambda cls: cls.__name__)
+&gt;&gt;&gt; Clock.name() # called by the class
+'Clock'
+&gt;&gt;&gt; Clock().name() # called by an instance
+'Clock'
+</pre>
+</blockquote>
+<p>Notice that classmethods (and staticmethods too)
+can only be attached to classes, not to objects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Clock(object): pass
+&gt;&gt;&gt; c=Clock()
+&gt;&gt;&gt; c.name=classmethod(lambda cls: cls.__name__)
+&gt;&gt;&gt; c.name() #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: 'classmethod' object is not callable
+</pre>
+</blockquote>
+<p>gives a TypeError. The reason is that classmethods and staticmethods
+are implemented
+trough <em>attribute descriptors</em>. This concept will be discussed in detail in a
+forthcoming in chapter 6.</p>
+<p>Notice that classmethods are not proving any fundamental feature, since
+one could very well use a normal method and retrieve the class with
+<tt class="literal"><span class="pre">self.__class__</span></tt> as we did in the first chapter.
+Therefore, we could live without (actually, I think they are a non-essential
+complication to the language).
+Nevertheless, now that we have them, we can use them, since
+they come handy in various circumstances, as we will see in the following.</p>
+</div>
+<div class="section" id="objects-have-their-privacy">
+<h2><a class="toc-backref" href="#id78" name="objects-have-their-privacy">Objects have their privacy</a></h2>
+<p>In some situations, it is convenient to give to the developer
+some information that should be hided to the final user. To this
+aim Python uses private names (i.e. names starting with a single
+underscore) and private/protected attributes (i.e. attributes starting with
+a double underscore).</p>
+<p>Consider for instance the following script:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;privacy.py&gt;
+
+import time
+
+class Clock(object):
+ __secret=&quot;This Clock is quite stupid.&quot;
+
+myclock=Clock()
+try: print myclock.__secret
+except Exception,e: print &quot;AttributeError:&quot;,e
+
+#&lt;/privacy.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is</p>
+<blockquote>
+<pre class="literal-block">
+AttributeError: 'Clock' object has no attribute '__secret'
+</pre>
+</blockquote>
+<p>Therefore, even if the Clock object <em>does</em> have a <tt class="literal"><span class="pre">__secret</span></tt> attribute,
+the user cannot access it ! In this way she cannot discover that
+actually &quot;This Clock is quite stupid.&quot;</p>
+<p>In other programming languages, attributes like <tt class="literal"><span class="pre">__secret</span></tt> are
+called &quot;private&quot; attributes. However, in Python private attributes
+are not really private and their secrets can be accessed with very
+little effort.</p>
+<p>First of all, we may notice that <tt class="literal"><span class="pre">myclock</span></tt> really contains a secret
+by using the builtin function <tt class="literal"><span class="pre">dir()</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+dir(myclock)
+['_Clock__secret', '__class__', '__delattr__', '__dict__', '__doc__',
+ '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
+ '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__']
+</pre>
+</blockquote>
+<p>We see that the first attribute of myclock is '_Clock__secret``,
+which we may access directly:</p>
+<blockquote>
+<pre class="literal-block">
+print myclock._Clock__secret
+This clock is quite stupid.
+</pre>
+</blockquote>
+<p>We see here the secret of private variables in Python: the <em>name mangling</em>.
+When Python sees a name starting with two underscores (and not ending
+with two underscores, otherwise it would be interpreted as a special
+attribute), internally it manage it as <tt class="literal"><span class="pre">_Classname__privatename</span></tt>.
+Notice that if 'Classname' begins with underscores, the leading underscores
+are stripped in such a way to guarantee that the private name starts with
+only <em>one</em> underscore. For instance, the '__secret' private attribute
+of classes such as 'Clock', '_Clock', '__Clock', '___Clock', etc. is
+mangled to '_Clock__secret'.</p>
+<p>Private names in Python are <em>not</em> intended to keep secrets: they
+have other uses.</p>
+<ol class="arabic simple">
+<li>On one hand, private names are a suggestion to the developer.
+When the Python programmer sees a name starting with one or two
+underscores in a program written by others, she understands
+that name should not be of concern for the final user, but it
+only concerns the internal implementation.</li>
+<li>On the other hand, private names are quite useful in class
+inheritance, since they provides safety with respect to the overriding
+operation. This point we will discussed in the next chapter.</li>
+<li>Names starting with one (or more) underscores are not imported by the
+statement <tt class="literal"><span class="pre">from</span> <span class="pre">module</span> <span class="pre">import</span> <span class="pre">*</span></tt></li>
+</ol>
+<p>Remark: it makes no sense to define names with double underscores
+outside classes, since the name mangling doesn't work in this case.
+Let me show an example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Clock(object): __secret=&quot;This Clock is quite stupid&quot;
+&gt;&gt;&gt; def tellsecret(self): return self.__secret
+&gt;&gt;&gt; Clock.tellsecret=tellsecret
+&gt;&gt;&gt; Clock().tellsecret() #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;&lt;stdin&gt;&quot;, line 2, in tellsecret
+AttributeError: 'Clock' object has no attribute '__secret'
+</pre>
+</blockquote>
+<p>The explanation is that since <tt class="literal"><span class="pre">tellsecret()</span></tt> is defined outside the class,
+<tt class="literal"><span class="pre">__secret</span></tt> is not expanded to <tt class="literal"><span class="pre">_Clock__secret</span></tt> and therefore cannot be
+retrieved, whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Clock(object):
+... __secret=&quot;This Clock is quite stupid&quot;
+... def tellsecret(self): return self.__secret
+&gt;&gt;&gt; Clock().tellsecret()
+This Clock is quite stupid
+</pre>
+</blockquote>
+<p>will work. In other words, private variables are attached to classes,
+not objects.</p>
+</div>
+<div class="section" id="objects-have-properties">
+<h2><a class="toc-backref" href="#id79" name="objects-have-properties">Objects have properties</a></h2>
+<p>In the previous section we have shown that private variables are of
+little use for keeping secrets: if a developer really wants to restrict
+the access to some methods or attributes, she has to resort to
+<em>properties</em>.</p>
+<p>Let me show an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;secret.py&gt;
+
+import oopp
+
+class Clock(object):
+ 'Clock class with a secret'
+
+ you_know_the_pw=False #default
+
+ def give_pw(self,pw):
+ &quot;&quot;&quot;Check if your know the password. For security, one should crypt
+ the password.&quot;&quot;&quot;
+ self.you_know_the_pw=(pw==&quot;xyz&quot;)
+
+ def get_secret(self):
+ if self.you_know_the_pw:
+ return &quot;This clock doesn't work.&quot;
+ else:
+ return &quot;You must give the right password to access 'secret'&quot;
+
+ secret=property(get_secret)
+
+c=Clock()
+print c.secret # =&gt; You must give the right password to access 'secret'
+c.give_pw('xyz') # gives the right password
+print c.secret # =&gt; This clock doesn't work.
+print Clock.secret # =&gt; &lt;property object at 0x814c1b4&gt;
+
+#&lt;/secret.py&gt;
+</pre>
+</blockquote>
+<p>In this script, one wants to restrict the access to the attribute
+'secret', which can be accessed only is the user provide the
+correct password. Obviously, this example is not very secure,
+since I have hard coded the password 'xyz' in the source code,
+which is easily accessible. In reality, one should crypt the
+password a perform a more sophisticated test than the trivial
+check <tt class="literal"><span class="pre">(pw==&quot;xyz&quot;)</span></tt>; anyway, the example is only intended to
+shown the uses of properties, not to be really secure.</p>
+<p>The key action is performed by the descriptor class <tt class="literal"><span class="pre">property</span></tt> that
+converts the function <tt class="literal"><span class="pre">get_secret</span></tt> in a property object. Additional
+informations on the usage of <tt class="literal"><span class="pre">property</span></tt> can be obtained from the
+docstring:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print property.__doc__
+property(fget=None, fset=None, fdel=None, doc=None) -&gt; property attribute
+fget is a function to be used for getting an attribute value, and likewise
+fset is a function for setting, and fdel a function for del'ing, an
+attribute. Typical use is to define a managed attribute x:
+class C(object):
+ def getx(self): return self.__x
+ def setx(self, value): self.__x = value
+ def delx(self): del self.__x
+ x = property(getx, setx, delx, &quot;I'm the 'x' property.&quot;)
+</pre>
+</blockquote>
+<p>Properties are another example of attribute descriptors.</p>
+</div>
+<div class="section" id="objects-have-special-methods">
+<h2><a class="toc-backref" href="#id80" name="objects-have-special-methods">Objects have special methods</a></h2>
+<p>From the beginning, we stressed that objects have special attributes that
+may turn handy, as for instance the docstring <tt class="literal"><span class="pre">__doc__</span></tt> and the class
+name attribute <tt class="literal"><span class="pre">__class__</span></tt>. They have special methods, too.</p>
+<p>With little doubt, the most useful special method is the <tt class="literal"><span class="pre">__init__</span></tt>
+method, that <em>initializes</em> an object right after its creation. <tt class="literal"><span class="pre">__init__</span></tt>
+is typically used to pass parameters to <em>object factories</em>. Let me an
+example with geometric figures:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class GeometricFigure(object): #an example of object factory
+ &quot;&quot;&quot;This class allows to define geometric figures according to their
+ equation in the cartesian plane. It will be extended later.&quot;&quot;&quot;
+ def __init__(self,equation,**parameters):
+ &quot;Specify the cartesian equation of the object and its parameters&quot;
+ self.eq=equation
+ self.par=parameters
+ for k,v in self.par.items(): #replaces the parameters in the equation
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq)
+ # dynamically creates the function 'contains'
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here it is how it works:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; disk=GeometricFigure('(x-x0)**2+(y-y0)**2 &lt;= r**2', x0=0,y0=0,r=5)
+&gt;&gt;&gt; # creates a disk of radius 5 centered in the origing
+&gt;&gt;&gt; disk.contains(1,2) #asks if the point (1,2) is inside the disk
+True
+&gt;&gt;&gt; disk.contains(4,4) #asks if the point (4,4) is inside the disk
+False
+</pre>
+</blockquote>
+<p>Let me continue the section on special methods with some some observations on
+<tt class="literal"><span class="pre">__repr__</span></tt> and <tt class="literal"><span class="pre">__str__</span></tt>.Notice that I
+will not discuss all the subtleties; for a thought discussion, see the
+thread &quot;Using __repr__ or __str__&quot; in c.l.p. (Google is your friend).
+The following discussion applies to new style classes, old style classes
+are subtly different; moreover.</p>
+<p>When one writes</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; disk
+&lt;oopp.GeometricFigure instance at 0x81b496c&gt;
+</pre>
+</blockquote>
+<p>one obtains the <em>string representation</em> of the object. Actually, the previous
+line is syntactic sugar for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print repr(disk)
+&lt;oopp.GeometricFigure instance at 0x81b496c&gt;
+</pre>
+</blockquote>
+<p>or</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print disk.__repr__()
+&lt;oopp.GeometricFigure instance at 0x81b496c&gt;
+</pre>
+</blockquote>
+<p>The <tt class="literal"><span class="pre">repr</span></tt> function extracts the string representation from the
+the special method <tt class="literal"><span class="pre">__repr__</span></tt>, which can be redefined in order to
+have objects pretty printed. Notice that <tt class="literal"><span class="pre">repr</span></tt> is conceptually
+different from the <tt class="literal"><span class="pre">str</span></tt> function that controls the output of the <tt class="literal"><span class="pre">print</span></tt>
+statement. Actually, <tt class="literal"><span class="pre">print</span> <span class="pre">o</span></tt> is syntactic sugar for <tt class="literal"><span class="pre">print</span> <span class="pre">str(o)</span></tt>
+which is sugar for <tt class="literal"><span class="pre">print</span> <span class="pre">o.__str__()</span></tt>.</p>
+<p>If for instance we define</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class PrettyPrinted(object):
+ formatstring='%s' # default
+ def __str__(self):
+ &quot;&quot;&quot;Returns the name of self in quotes, possibly formatted via
+ self.formatstring. If self has no name, returns the name
+ of its class in angular brackets.&quot;&quot;&quot;
+ try: #look if the selfect has a name
+ name=&quot;'%s'&quot; % self.__name__
+ except AttributeError: #if not, use the name of its class
+ name='&lt;%s&gt;' % type(self).__name__
+ if hasattr(self,'formatstring'):
+ return self.formatstring % name
+ else:
+ return name
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>then we have</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import PrettyPrinted
+&gt;&gt;&gt; o=PrettyPrinted() # o is an instance of PrettyPrinted
+&gt;&gt;&gt; print o #invokes o.__str__() which in this case returns o.__class__.name
+&lt;PrettyPrinted&gt;
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; o # i.e. print repr(o)
+&lt;oopp.PrettyPrinted object at 0x400a006c&gt;
+</pre>
+</blockquote>
+<p>However, in most cases <tt class="literal"><span class="pre">__repr__</span></tt> and <tt class="literal"><span class="pre">__str__</span></tt> gives the same
+output, since if <tt class="literal"><span class="pre">__str__</span></tt> is not explicitely defined it defaults
+to <tt class="literal"><span class="pre">__repr__</span></tt>. Therefore, whereas modifying <tt class="literal"><span class="pre">__str__</span></tt>
+does not change <tt class="literal"><span class="pre">__repr__</span></tt>, modifying <tt class="literal"><span class="pre">__repr__</span></tt> changes <tt class="literal"><span class="pre">__str__</span></tt>,
+if <tt class="literal"><span class="pre">__str__</span></tt> is not explicitely given:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;fairytale1.py&gt;
+
+&quot;__repr__ can also be a regular method, not a classmethod&quot;
+
+class Frog(object):
+ attributes=&quot;poor, small, ugly&quot;
+ def __str__(self):
+ return &quot;I am a &quot;+self.attributes+' '+self.__class__.__name__
+
+class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return &quot;I am a &quot;+self.attributes+' '+self.__class__.__name__
+
+jack=Frog(); print repr(jack),jack
+charles=Prince(); print repr(charles),charles
+
+#&lt;/fairytale1.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is:</p>
+<blockquote>
+<pre class="literal-block">
+&lt;Frog object at 0x81866ec&gt; I am a poor, small, ugly Frog
+&lt;Prince object at 0x818670c&gt; I am a rich, tall, beautiful Prince
+</pre>
+</blockquote>
+<p>for jack and charles respectively.</p>
+<p><tt class="literal"><span class="pre">__str__</span></tt> and <tt class="literal"><span class="pre">__repr__</span></tt> are also called by the formatting
+operators &quot;%s&quot; and &quot;%r&quot;.</p>
+<p>Notice that i) <tt class="literal"><span class="pre">__str__</span></tt> can be most naturally
+rewritten as a class method; ii) Python is magic:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;fairytale2.py&gt;
+
+&quot;&quot;&quot;Shows two things:
+ 1) redefining __repr__ automatically changes the output of __str__
+ 2) the class of an object can be dinamically changed! &quot;&quot;&quot;
+
+class Frog(object):
+ attributes=&quot;poor, small, ugly&quot;
+ def __repr__(cls):
+ return &quot;I am a &quot;+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __repr__(cls):
+ return &quot;I am a &quot;+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+def princess_kiss(frog):
+ frog.__class__=Prince
+
+jack=Frog()
+princess_kiss(jack)
+print jack # the same as repr(jack)
+
+#&lt;/fairytale2.py&gt;
+</pre>
+</blockquote>
+<p>Now the output for jack is &quot;I am a rich, tall, beautiful Prince&quot; !
+In Python you may dynamically change the class of an object!!</p>
+<p>Of course, this is a feature to use with care ;-)</p>
+<p>There are many others special methods, such as __new__, __getattr__,
+__setattr__, etc. They will be discussed in the next chapters, in
+conjunction with inheritance.</p>
+</div>
+<div class="section" id="objects-can-be-called-added-subtracted">
+<h2><a class="toc-backref" href="#id81" name="objects-can-be-called-added-subtracted">Objects can be called, added, subtracted, ...</a></h2>
+<p>Python provides a nice generalization of functions, via the concept
+of <em>callable objects</em>. A callable object is an object with a <tt class="literal"><span class="pre">__call__</span></tt>
+special method. They can be used to define &quot;functions&quot; that remember
+how many times they are invoked:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;call.py&gt;
+
+class MultiplyBy(object):
+ def __init__(self,n):
+ self.n=n
+ self.counter=0
+ def __call__(self,x):
+ self.counter+=1
+ return self.n*x
+
+double=MultiplyBy(2)
+res=double(double(3)) # res=12
+print &quot;double is callable: %s&quot; % callable(double)
+print &quot;You have called double %s times.&quot; % double.counter
+
+#&lt;/call.py&gt;
+</pre>
+</blockquote>
+<p>With output</p>
+<blockquote>
+<pre class="literal-block">
+double is callable: True
+You have called double 2 times.
+</pre>
+</blockquote>
+<p>The script also show that callable objects (including functions)
+can be recognized with the <tt class="literal"><span class="pre">callable</span></tt> built-in function.</p>
+<p>Callable object solves elegantly the problem of having &quot;static&quot; variables
+inside functions (cfr. with the 'double' example in chapter 2).
+A class with a <tt class="literal"><span class="pre">__call__</span></tt> method can be used to generate an entire
+set of customized &quot;functions&quot;. For this reason, callable objects are
+especially useful in the conjunction with object factories. Let me show
+an application to my factory of geometric figures:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Makeobj(object):
+ &quot;&quot;&quot;A factory of object factories. Makeobj(cls) returns instances
+ of cls&quot;&quot;&quot;
+ def __init__(self,cls,*args):
+ self.cls=cls
+ self.args=args
+ def __call__(self,**pars):
+ return self.cls(*self.args,**pars)
+
+#&lt;/oopp.py&gt;
+
+#&lt;factory.py&gt;
+
+from oopp import Makeobj,GeometricFigure
+
+makedisk=Makeobj(GeometricFigure,'(x-x0)**2+(y-y0)**2&lt;r**2')
+makesquare=Makeobj(GeometricFigure,'abs(x-x0)&lt;L and abs(y-y0)&lt;L')
+disk=makedisk(x0=0,y0=0,r=10) # make a disk of radius 10
+square=makesquare(x0=0,y0=0,L=20) # make a disk of side 10
+
+print disk.contains(9,9) # =&gt; False
+print square.contains(9,9) # =&gt; True
+#etc.
+
+#&lt;/factory.py&gt;
+</pre>
+</blockquote>
+<p>This factory generates callable objects, such as <tt class="literal"><span class="pre">makedisk</span></tt> and
+<tt class="literal"><span class="pre">makesquare</span></tt> that returns geometric objects. It gives a nicer interface
+to the object factory provided by 'GeometricFigure'.</p>
+<p>Notice that the use of the expression <tt class="literal"><span class="pre">disk.contains(9,9)</span></tt> in order to
+know if the point of coordinates (9,9) is contained in the disk, it is
+rather inelegant: it would be much better to be able to ask if
+<tt class="literal"><span class="pre">(9,9)</span> <span class="pre">in</span> <span class="pre">disk</span></tt>. This is possibile, indeed: and the secrets is to
+define the special method <tt class="literal"><span class="pre">__contains__</span></tt>. This is done in the next
+example, that I think give a good taste of the beauty of objects</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;funnyformatter.py&gt;
+
+from oopp import Makeobj
+
+Nrow=50; Ncol=78
+
+class GeometricFigure(object):
+ &quot;&quot;&quot;This class allows to define geometric figures according to their
+ equation in the cartesian plane. Moreover addition and subtraction
+ of geometric figures are defined as union and subtraction of sets.&quot;&quot;&quot;
+ def __init__(self,equation,**parameters):
+ &quot;Initialize &quot;
+ self.eq=equation
+ self.par=parameters
+ for (k,v) in self.par.items(): #replaces the parameters
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq)
+ def combine(self,fig,operator):
+ &quot;&quot;&quot;Combine self with the geometric figure fig, using the
+ operators &quot;or&quot; (addition) and &quot;and not&quot; (subtraction)&quot;&quot;&quot;
+ comboeq=&quot;(&quot;+self.eq+&quot;)&quot;+operator+&quot;(&quot;+fig.eq+&quot;)&quot;
+ return GeometricFigure(comboeq)
+ def __add__(self,fig):
+ &quot;Union of sets&quot;
+ return self.combine(fig,' or ')
+ def __sub__(self,fig):
+ &quot;Subtraction of sets&quot;
+ return self.combine(fig,' and not')
+ def __contains__(self,point): #point is a tuple (x,y)
+ return self.contains(*point)
+
+makedisk=Makeobj(GeometricFigure,'(x-x0)**2/4+(y-y0)**2 &lt;= r**2')
+upperdisk=makedisk(x0=38,y0=7,r=5)
+smalldisk=makedisk(x0=38,y0=30,r=5)
+bigdisk=makedisk(x0=38,y0=30,r=14)
+
+def format(text,shape):
+ &quot;Format the text in the shape given by figure&quot;
+ text=text.replace('\n',' ')
+ out=[]; i=0; col=0; row=0; L=len(text)
+ while 1:
+ if (col,row) in shape:
+ out.append(text[i]); i+=1
+ if i==L: break
+ else:
+ out.append(&quot; &quot;)
+ if col==Ncol-1:
+ col=0; out.append('\n') # starts new row
+ if row==Nrow-1: row=0 # starts new page
+ else: row+=1
+ else: col+=1
+ return ''.join(out)
+
+composition=bigdisk-smalldisk+upperdisk
+print format(text='Python Rules!'*95,shape=composition)
+
+#&lt;/funnyformatter.py&gt;
+</pre>
+</blockquote>
+<p>I leave as an exercise for the reader to understand how does it work and to
+play with other geometric figures (he can also generate them trough the
+'Makeobj' factory). I think it is nicer to show its output:</p>
+<blockquote>
+<pre class="literal-block">
+ Pyt
+ hon Rules!Pyt
+ hon Rules!Python
+ Rules!Python Rules!
+ Python Rules!Python
+ Rules!Python Rules!P
+ ython Rules!Python
+ Rules!Python Rules!
+ Python Rules!Pyth
+ on Rules!Pyth
+ on
+
+
+
+ Rul
+ es!Python Rules!Pytho
+ n Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Pyt
+ hon Rules!Python Rules!Python Rules!Python Rules!
+ Python Rules!Python Rules!Python Rules!Python Rules
+ !Python Rules!Python Rule s!Python Rules!Python Rul
+ es!Python Rules!Pyth on Rules!Python Rule
+ s!Python Rules!Pyth on Rules!Python Rul
+ es!Python Rules!Py thon Rules!Python
+ Rules!Python Rules !Python Rules!Pyth
+on Rules!Python Ru les!Python Rules!P
+ ython Rules!Python Rules!Python Rule
+ s!Python Rules!Pyt hon Rules!Python R
+ ules!Python Rules!P ython Rules!Python
+ Rules!Python Rules!P ython Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Python
+ Rules!Python Rules!Python Rules!Python Rules!Pytho
+ n Rules!Python Rules!Python Rules!Python Rules!Py
+ thon Rules!Python Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!
+ Python Rules!Python R
+ ule
+
+
+
+
+
+
+
+ s!
+</pre>
+</blockquote>
+<p>Remark.</p>
+<p>Unfortunately, &quot;funnyformatter.py&quot; does not reuse old code: in spite of the
+fact that we already had in our library the 'GeometricFigure' class, with
+an &quot;__init__&quot; method that is exactly the same of the &quot;__init__&quot; method in
+&quot;funnyformatter.py&quot;, we did not reuse that code. We simply did a cut
+and paste. This means that if we later find a bug in the <tt class="literal"><span class="pre">__init__</span></tt> method,
+we will have to fix it twice, both in the script and in the library. Also,
+if we plan to extend the method later, we will have to extend it twice.
+Fortunately, this nasty situation can be avoided: but this requires the
+power of inheritance.</p>
+</div>
+</div>
+<div class="section" id="the-power-of-classes">
+<h1><a class="toc-backref" href="#id82" name="the-power-of-classes">THE POWER OF CLASSES</a></h1>
+<p>This chapter is devoted to the concept of class inheritance. I will discuss
+single inheritance, cooperative methods, multiple inheritance and more.</p>
+<div class="section" id="the-concept-of-inheritance">
+<h2><a class="toc-backref" href="#id83" name="the-concept-of-inheritance">The concept of inheritance</a></h2>
+<p>Inheritance is perhaps the most important basic feature in OOP, since it
+allows the reuse and incremental improvement of old code.
+To show this point, let me come back to one of the
+examples I have introduced in the last chapter, 'fairytale1.py' script,
+where I defined the classes 'Frog' and 'Prince' as</p>
+<blockquote>
+<pre class="literal-block">
+class Frog(object):
+ attributes=&quot;poor, small, ugly&quot;
+ def __str__(self):
+ return &quot;I am a &quot;+self.attributes+' '+self.__class__.__name__
+
+class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return &quot;I am a &quot;+self.attributes+' '+self.__class__.__name__
+</pre>
+</blockquote>
+<p>We see that the way we followed here was very bad since:</p>
+<ol class="arabic simple">
+<li>The <tt class="literal"><span class="pre">__str__</span></tt> method is duplicated both in Frog and in Prince: that
+means that if we find a bug a later, we have to fix it twice!</li>
+<li>The <tt class="literal"><span class="pre">__str__</span></tt> was already defined in the PrettyPrinted class (actually
+more elegantly), therefore we have triplicated the work and worsened the
+situation!</li>
+</ol>
+<p>This is very much against the all philosophy of OOP:</p>
+<blockquote>
+<em>never cut and paste!</em></blockquote>
+<p>We should <em>reuse</em> old code, not paste it!</p>
+<p>The solution is <em>class inheritance</em>. The idea behind inheritance is to
+define new classes as subclasses of a <em>parent</em> classes, in such a way that
+the <em>children</em> classes possess all the features of the parents.
+That means that we do not need to
+redefine the properties of the parents explicitely.
+In this example, we may derive both 'Frog' and 'Prince' from
+the 'PrettyPrinted' class, thus providing to both 'Frog' and 'Prince'
+the <tt class="literal"><span class="pre">PrettyPrinted.__str__</span></tt> method with no effort:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import PrettyPrinted
+&gt;&gt;&gt; class Frog(PrettyPrinted): attributes=&quot;poor, small, ugly&quot;
+...
+&gt;&gt;&gt; class Prince(PrettyPrinted): attributes=&quot;rich, tall, beautiful&quot;
+...
+&gt;&gt;&gt; print repr(Frog()), Frog()
+&lt;__main__.Frog object at 0x401cbeac&gt; &lt;Frog&gt;
+&gt;&gt;&gt; print Prince()
+&gt;&gt;&gt; print repr(Prince()),Prince()
+&lt;__main__.Prince object at 0x401cbaac&gt; &lt;Prince&gt;
+</pre>
+</blockquote>
+<p>Let me show explicitly that both 'Frog' and 'Prince' share the
+'PrettyPrinted.__str__' method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(Frog.__str__) # of course, YMMV
+1074329476
+&gt;&gt;&gt; id(Prince.__str__)
+1074329476
+&gt;&gt;&gt; id(PrettyPrinted.__str__)
+1074329476
+</pre>
+</blockquote>
+<p>The method is always the same, since the object reference is the same
+(the precise value of the reference is not guaranteed to be 1074329476,
+however!).</p>
+<p>This example is good to show the first advantage of inheritance:
+<em>avoiding duplication of code</em>.
+Another advantage of inheritance, is <em>extensibility</em>: one can very easily
+improve existing code. For instance, having written the <tt class="literal"><span class="pre">Clock</span></tt> class once,
+I can reuse it in many different ways. for example I can build a <tt class="literal"><span class="pre">Timer</span></tt>
+to be used for benchmarks. It is enough to reuse the function <tt class="literal"><span class="pre">with_timer</span></tt>
+introduced in the first chapter (functions are good for reuse of code, too ;):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Timer(Clock):
+ &quot;Inherits the get_time staticmethod from Clock&quot;
+ execute=staticmethod(with_timer)
+ loop_overhead=staticmethod(loop_overhead)
+
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of application:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Timer
+&gt;&gt;&gt; Timer.get_time()
+'16:07:06'
+</pre>
+</blockquote>
+<p>Therefore 'Timer' inherits 'Clock.get_time'; moreover it has the additional
+method <tt class="literal"><span class="pre">execute</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def square(x): return x*x
+...
+&gt;&gt;&gt; Timer.execute(square,n=100000)(1)
+executing square ...
+ Real time: 0.01 ms CPU time: 0.008 ms
+</pre>
+</blockquote>
+<p>The advantage of putting the function <tt class="literal"><span class="pre">execute</span></tt> in a class is that
+now we may <em>inherit</em> from that class and improve out timer <em>ad
+libitum</em>.</p>
+</div>
+<div class="section" id="inheritance-versus-run-time-class-modifications">
+<h2><a class="toc-backref" href="#id84" name="inheritance-versus-run-time-class-modifications">Inheritance versus run-time class modifications</a></h2>
+<p>Naively, one could think of substituting inheritance with run-time
+modification of classes, since this is allowed by Python. However,
+this is not such a good idea, in general. Let me give a simple example.
+Suppose we want to improve our previous clock, to show the date, too.
+We could reach that goal with the following script:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;clock2.py&gt;
+
+&quot;Shows how to modify and enhances classes on the fly&quot;
+
+from oopp import *
+
+clock=Clock() #creates a Clock instance
+print clock.get_time() # print the current time
+
+get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+get_data_and_time=lambda : &quot;Today is: %s \nThe time is: %s&quot; % (
+ get_data(),get_time()) # enhances get_time
+
+Clock.get_time=staticmethod(get_data_and_time)
+
+print clock.get_time() # print the current time and data
+
+#&lt;/clock2.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is:</p>
+<blockquote>
+12:51:25
+Today is: Sat Feb 22 2003
+The time is: 12:51:25</blockquote>
+<p>Notice that:</p>
+<ol class="arabic simple">
+<li>I instantiated the <tt class="literal"><span class="pre">clock</span></tt> object <em>before</em> redefining the <tt class="literal"><span class="pre">get_time</span></tt>
+method, when it only could print the time and <em>not</em> the date.</li>
+<li>However, after the redefinition of the class, the behaviour of all its
+instances is changed, <em>including the behaviour of objects instantiated
+before the change!</em>. Then <tt class="literal"><span class="pre">clock</span></tt> <em>can</em> print the date, too.</li>
+</ol>
+<p>This is not so surprising, once you recognize that Guido own a very famous
+time-machine ... ;-)</p>
+<p>Seriously, the reason is that an object does not contains a reserved copy
+of the attributes and methods of its class: it only contains <em>references</em>
+to them. If we change them in the class, the references to them in the
+object will stay the same, but the contents will change.</p>
+<p>In this example, I have solved the problem of enhancing the 'Clock' class
+without inheritance, but dynamically replaceing its <tt class="literal"><span class="pre">get_time</span></tt>
+(static) method with the <cite>get_data_and_time`</cite> (static) method.
+The dynamics modification of methods can be cool, but it should be avoided
+whenever possible, at least for two reasons <a class="footnote-reference" href="#id22" id="id23" name="id23">[11]</a>:</p>
+<ol class="arabic simple">
+<li>having a class and therefore all its instances (including the instances
+created before the modification !) changed during the life-time of the
+program can be very confusing to the programmer, if not to the interpreter.</li>
+<li>the modification is destructive: I cannot have the old <tt class="literal"><span class="pre">get_time</span></tt> method
+and the new one at the same time, unless one explicitly gives to it
+a new name (and giving new names increases the pollution of the namespace).</li>
+</ol>
+<p>Both these disadvantages can be solved by resorting to the mechanism of
+inheritance. For instance, in this example, we can derive a new class
+<tt class="literal"><span class="pre">NewClock</span></tt> from <tt class="literal"><span class="pre">Clock</span></tt> as follows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;newclock.py&gt;
+
+import oopp,time
+
+get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+get_data_and_time=lambda : &quot;Today is: %s \nThe time is: %s&quot; % (
+ get_data(),oopp.get_time()) # enhances get_time
+
+class NewClock(oopp.Clock):
+ &quot;&quot;&quot;NewClock is a class that inherits from Clock, provides get_data
+ and overrides get_time.&quot;&quot;&quot;
+ get_data=staticmethod(get_data)
+ get_time=staticmethod(get_data_and_time)
+
+clock=oopp.Clock(); print 'clock output=',clock.get_time()
+newclock=NewClock(); print 'newclock output=',newclock.get_time()
+
+#&lt;/newclock.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is:</p>
+<blockquote>
+<pre class="literal-block">
+clock output= 16:29:17
+newclock output= Today is: Sat Feb 22 2003
+The time is: 16:29:17
+</pre>
+</blockquote>
+<p>We see that the two problems previously discussed are solved since:</p>
+<ol class="lowerroman simple">
+<li>there is no cut and paste: the old method <tt class="literal"><span class="pre">Clock.get_time()</span></tt> is used
+in the definition of the new method <tt class="literal"><span class="pre">NewClock.get_time()</span></tt>;</li>
+<li>the old method is still accessible as <tt class="literal"><span class="pre">Clock.get_time()</span></tt>; there is
+no need to invent a new name like <tt class="literal"><span class="pre">get_time_old()</span></tt>.</li>
+</ol>
+<p>We say that the method <tt class="literal"><span class="pre">get_time</span></tt> in <tt class="literal"><span class="pre">NewClock</span></tt> <em>overrides</em> the method
+<tt class="literal"><span class="pre">get_time</span></tt> in Clock.</p>
+<p>This simple example shows the power of inheritance in code
+reuse, but there is more than that.</p>
+<p>Inheritance is everywhere in Python, since
+all classes inherit from object. This means that all classes
+inherit the methods and attributes of the object class, such as <tt class="literal"><span class="pre">__doc__</span></tt>,
+<tt class="literal"><span class="pre">__class__</span></tt>, <tt class="literal"><span class="pre">__str__</span></tt>, etc.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id24" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id25" name="id24">[12]</a></td><td>There are cases when run-time modifications of classes is useful
+anyway: particularly when one wants to modify the behavior of
+classes written by others without changing the source code. I
+will show an example in next chapter.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="inheriting-from-built-in-types">
+<h2><a class="toc-backref" href="#id85" name="inheriting-from-built-in-types">Inheriting from built-in types</a></h2>
+<p>However, one can subclass a built-in type, effectively creating an
+user-defined type with all the feature of a built-in type, and modify it.</p>
+<p>Suppose for instance one has a keyword dictionary such as</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; kd={'title': &quot;OOPP&quot;, 'author': &quot;M.S.&quot;, 'year': 2003}
+</pre>
+</blockquote>
+<p>it would be nice to be able to access the attributes without
+excessive quoting, i.e. using <tt class="literal"><span class="pre">kd.author</span></tt> instead of <tt class="literal"><span class="pre">kd[&quot;author&quot;]</span></tt>.
+This can be done by subclassing the built-in class <tt class="literal"><span class="pre">dict</span></tt> and
+by overriding the <tt class="literal"><span class="pre">__getattr__</span></tt> and <tt class="literal"><span class="pre">__setattr__</span></tt> special methods:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp/py&gt;
+
+class kwdict(dict):
+ &quot;Keyword dictionary base class&quot;
+ def __getattr__(self,attr):
+ return self[attr]
+ def __setattr__(self,key,val):
+ self[key]=val
+ __str__ = pretty
+
+#&lt;/oopp/py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import kwdict
+&gt;&gt;&gt; book=kwdict({'title': &quot;OOPP&quot;, 'author': &quot;M.S.&quot;})
+&gt;&gt;&gt; book.author #it works
+'M.S.'
+&gt;&gt;&gt; book[&quot;author&quot;] # this also works
+'M.S.'
+&gt;&gt;&gt; book.year=2003 #you may also add new fields on the fly
+&gt;&gt;&gt; print book
+author = M.S.
+title = OOPP
+year = 2003
+</pre>
+</blockquote>
+<p>The advantage of subclassing the built-in 'dict', it that you have for free
+all the standard dictionary methods, without having to reimplement them.</p>
+<p>However, to subclass built-in it is not always a piece of cake. In
+many cases there are complications, indeed. Suppose for instance
+one wants to create an enhanced string type, with
+the ability of indent and dedent a block of text provided by
+the following functions:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def indent(block,n):
+ &quot;Indent a block of code by n spaces&quot;
+ return '\n'.join([' '*n+line for line in block.splitlines()])
+
+def dedent(block):
+ &quot;Dedent a block of code, if need there is&quot;&quot;&quot;
+ lines=block.splitlines()
+ for line in lines:
+ strippedline=line.lstrip()
+ if strippedline: break
+ spaces=len(line)-len(strippedline)
+ if not spaces: return block
+ return '\n'.join([line[spaces:] for line in lines])
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The solution is to inherit from the built-in string type <tt class="literal"><span class="pre">str</span></tt>, and to
+add to the new class the <tt class="literal"><span class="pre">indent</span></tt> and <tt class="literal"><span class="pre">dedent</span></tt> methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import indent,dedent
+&gt;&gt;&gt; class Str(str):
+... indent=indent
+... dedent=dedent
+&gt;&gt;&gt; s=Str('spam\neggs')
+&gt;&gt;&gt; type(s)
+&lt;class '__main__.Str'&gt;
+&gt;&gt;&gt; print s.indent(4)
+ spam
+ eggs
+</pre>
+</blockquote>
+<p>However, this approach has a disadvantage, since the output of <tt class="literal"><span class="pre">indent</span></tt> is
+not a <tt class="literal"><span class="pre">Str</span></tt>, but a normal <tt class="literal"><span class="pre">str</span></tt>, therefore without the additional
+<tt class="literal"><span class="pre">indent</span></tt> and <tt class="literal"><span class="pre">dedent</span></tt> methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(s.indent(4))
+&lt;type 'str'&gt;
+&gt;&gt;&gt; s.indent(4).indent(4) #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 9, in ?
+AttributeError: 'str' object has no attribute 'indent'
+&gt;&gt;&gt; s.indent(4).dedent(4) #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 9, in ?
+AttributeError: 'str' object has no attribute 'dedent'
+</pre>
+</blockquote>
+<p>We would like <tt class="literal"><span class="pre">indent</span></tt> to return a <tt class="literal"><span class="pre">Str</span></tt> object. To solve this problem
+it is enough to rewrite the class as follows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;Str.py&gt;
+
+from oopp import indent,dedent
+
+class Str(str):
+ def indent(self,n):
+ return Str(indent(self,n))
+ def dedent(self):
+ return Str(dedent(self))
+
+s=Str('spam\neggs').indent(4)
+print type(s)
+print s # indented s
+s=s.dedent()
+print type(s)
+print s # non-indented s
+
+#&lt;/Str.py&gt;
+</pre>
+</blockquote>
+<p>Now, everything works and the output of the previous script is</p>
+<blockquote>
+<pre class="literal-block">
+&lt;class 'Str'&gt;
+ spam
+ eggs
+&lt;class 'Str'&gt;
+spam
+eggs
+</pre>
+</blockquote>
+<p>The solution works because now <tt class="literal"><span class="pre">indent()</span></tt> returns an instance
+of <tt class="literal"><span class="pre">Str</span></tt>, which therefore has an <tt class="literal"><span class="pre">indent</span></tt> method. Unfortunately,
+this is not the end. Suppose we want to add another food to our list:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; s2=s+Str(&quot;\nham&quot;)
+&gt;&gt;&gt; s2.indent(4) #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'str' object has no attribute 'indent'
+</pre>
+</blockquote>
+<p>The problem is the same, again: the type of <tt class="literal"><span class="pre">s2</span></tt> is <tt class="literal"><span class="pre">str</span></tt></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(s2)
+&lt;type 'str'&gt;
+</pre>
+</blockquote>
+<p>and therefore there is no <tt class="literal"><span class="pre">indent</span></tt> method available. There is a
+solution to this problem, i.e. to redefine the addition operator
+for objects of the class <tt class="literal"><span class="pre">Str</span></tt>. This can be done directly by hand,
+but it is <em>ugly</em> for the following reasons:</p>
+<ol class="arabic simple">
+<li>If you derive a new class from <tt class="literal"><span class="pre">Str</span></tt> you have to redefine the
+addition operator (both the left addition and the right addition <a class="footnote-reference" href="#id24" id="id25" name="id25">[12]</a>)
+again (ughh!);</li>
+<li>There are others operators you must redefine, in particular the
+the augumented assignement operator <tt class="literal"><span class="pre">+=</span></tt>, the repetition operator <tt class="literal"><span class="pre">*</span></tt>
+and its augmented version <tt class="literal"><span class="pre">*=</span></tt>;</li>
+<li>In the case of numeric types, one must redefine, <tt class="literal"><span class="pre">+,-,*,/,//,</span> <span class="pre">mod,</span></tt>,
+possibily <tt class="literal"><span class="pre">&lt;&lt;,&gt;&gt;</span></tt> and others, including the corresponding
+augumented assignement operators and the left and the right form of
+the operators.</li>
+</ol>
+<p>This is a mess, especially since due to point 1, one has to redefined
+all the operators each time she defines a new subclass. I short, one has
+to write a lot of boilerplate for a stupid job that the language
+should be able to perform itself automatically. But here are the
+good news: Python <em>can</em> do all that automatically, in an elegant
+and beautiful way, which works for all types, too.</p>
+<p>But this requires the magic of metaclasses.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id26" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id27" name="id26">[13]</a></td><td>The right addition works this way. Python looks at the expression x+y
+and if x has an explicit__add__ method invokes it; on the other hand,
+if x does not define an __add__ method, Python considers y+x.
+If y defines a __radd__ method, it invokes it, otherwise
+raises an exception. The same is done for right multiplication, etc.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="controlling-the-creation-of-objects">
+<h2><a class="toc-backref" href="#id86" name="controlling-the-creation-of-objects">Controlling the creation of objects</a></h2>
+<p>Before introducing multiple inheritance, let me make a short digression on
+the mechanism of object creation in Python 2.2+. The important point is
+that new style classes have a <tt class="literal"><span class="pre">__new__</span></tt> static method that allows
+the user to take complete control of object creation. To understand how
+<tt class="literal"><span class="pre">__new__</span></tt> works, I must explain what happens when an object is instantiated
+with a statement like</p>
+<blockquote>
+<pre class="literal-block">
+s=Str(&quot;spam&quot;) #object creation
+</pre>
+</blockquote>
+<p>What happens under the hood, is that the special static method <tt class="literal"><span class="pre">__new__</span></tt>
+of the class <tt class="literal"><span class="pre">Str</span></tt> (inherited from the built-in <tt class="literal"><span class="pre">str</span></tt> class)
+is invoked <tt class="literal"><span class="pre">before</span></tt> the <tt class="literal"><span class="pre">Str.__init__</span></tt> method. This means that
+the previous line should really be considered syntactic sugar for:</p>
+<blockquote>
+<pre class="literal-block">
+s=Str.__new__(Str,&quot;spam&quot;) # Str.__new__ is actually str.__new__
+assert isinstance(s,Str)
+Str.__init__(s,&quot;spam&quot;) # Str.__init__ is actually str.__init__
+</pre>
+</blockquote>
+<p>Put it more verbosely, what happens during the object creation is the
+following:</p>
+<ol class="arabic simple">
+<li>the static method <tt class="literal"><span class="pre">__new__</span></tt> is invoked with the class of the created
+object as first argument <a class="footnote-reference" href="#id26" id="id27" name="id27">[13]</a>;</li>
+<li><tt class="literal"><span class="pre">__new__</span></tt> returns an instance of that class.</li>
+<li>the instance is then initialized by the <tt class="literal"><span class="pre">__init__</span></tt> method.</li>
+</ol>
+<p>Notice that both <tt class="literal"><span class="pre">__new__</span></tt> and <tt class="literal"><span class="pre">__init__</span></tt> are called with the same
+argument list, therefore one must make sure that they have a compatible
+signature.</p>
+<p>Let me discuss now why <tt class="literal"><span class="pre">__new__</span></tt> must be a static method.
+First of all, it cannot be a normal method with a first argument which is an
+instance of the calling class, since at the time of <tt class="literal"><span class="pre">__new__</span></tt> invocation
+that instance (<tt class="literal"><span class="pre">myclock</span></tt> in the example) has still to be created
+Since <tt class="literal"><span class="pre">__new__</span></tt> needs information about the class calling it, one
+could think of implementing <tt class="literal"><span class="pre">__new__</span></tt> as a class method. However,
+this would implicitly pass the caller class and return an instance
+of it. It is more convenient, to have the ability of creating
+instances of any class directly from C.__new__(B,*args,**kw)</p>
+<p>For this reasons, <tt class="literal"><span class="pre">__new__</span></tt> must be a static method and pass explicitly
+the class which is calling it.</p>
+<p>Let me now show an important application of the <tt class="literal"><span class="pre">__new__</span></tt> static method:
+forbidding object creation. For instance, sometimes it is useful to have
+classes that cannot be instantiated. This kind of classes can be
+obtained by inheriting from a <tt class="literal"><span class="pre">NonInstantiable</span></tt> class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class NonInstantiableError(Exception):
+ pass
+
+class NonInstantiable(object):
+ def __new__(cls,*args,**kw):
+ raise NonInstantiableError(&quot;%s cannot be instantiated&quot; % cls)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import NonInstantiable,get_time
+&gt;&gt;&gt; class Clock(NonInstantiable):
+... get_time=staticmethod(get_time)
+&gt;&gt;&gt; Clock.get_time() # works
+'18:48:08'
+Clock() #error
+Traceback (most recent call last):
+ File &quot;&lt;pyshell#6&gt;&quot;, line 1, in ?
+ Clock()
+ File &quot;oopp.py&quot;, line 257, in __new__
+ raise NonInstantiableError(&quot;%s cannot be instantiated&quot; % cls)
+NonInstantiableError: &lt;class '__main__.Clock'&gt; cannot be instantiated
+</pre>
+</blockquote>
+<p>However, the approach pursued here has a disadvantage:<tt class="literal"><span class="pre">Clock</span></tt> was already
+defined as a subclass of <tt class="literal"><span class="pre">object</span></tt> and I has to change the source code
+to make it a subclass of 'NonInstantiable'. But what happens if
+I cannot change the sources? How can I <em>reuse</em> the old code?</p>
+<p>The solution is provided by multiple inheritance.</p>
+<p>Notice that '__new__' is a staticmethod: <a class="footnote-reference" href="#id29" id="id28" name="id28">[14]</a></p>
+<blockquote>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(NonInstantiable.__dict__['__new__'])
+&lt;type 'staticmethod'&gt;
+</pre>
+</blockquote>
+<table class="footnote" frame="void" id="id29" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id28" name="id29">[14]</a></td><td>This is how <tt class="literal"><span class="pre">type(s)</span></tt> or <tt class="literal"><span class="pre">s.__class__</span></tt> get to know that
+<tt class="literal"><span class="pre">s</span></tt> is an instance of <tt class="literal"><span class="pre">Str</span></tt>, since the class information is
+explicitely passed to the newborn object trough <tt class="literal"><span class="pre">__new__</span></tt>.</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id30" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id31" name="id30">[15]</a></td><td><p>However <tt class="literal"><span class="pre">object.__dict__['__new__']</span></tt> is not a staticmethod</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(object.__dict__['__new__']) # special case
+&lt;type 'builtin_function_or_method'&gt;
+</pre>
+</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="multiple-inheritance">
+<h2><a class="toc-backref" href="#id87" name="multiple-inheritance">Multiple Inheritance</a></h2>
+<p>Multiple Inheritance (often abbreviated as MI) is often
+considered one of the most advanced topic in Object Oriented Programming.
+It is also one of the most difficult features to implement
+in an Object Oriented Programming language. Even, some languages by design
+decided to avoid it. This is for instance the case of Java, that avoided
+MI having seen its implementation in C++ (which is not for the faint of
+heart ;-) and uses a poorest form of it trough interfaces.
+For what concerns the scripting languages, of which
+the most famous are Perl, Python and Ruby (in this order, even if
+the right order would be Python, Ruby and Perl), only Python
+implements Multiple Inheritance well (Ruby has a restricted form
+of it trough mix-ins, whereas Perl implementation is too difficult
+for me to understand what it does ;).</p>
+<p>The fact that Multiple Inheritance can be hairy, does not mean that it
+is <em>always</em> hairy, however. Multiple Inheritance is used with success
+in Lisp derived languages (including Dylan).</p>
+<p>The aims of this chapter is to discuss the
+Python support for MI in the most recent version (2.2 and 2.3), which
+has considerably improved with respect to previous versions.
+The message is the following: if Python 1.5 had a basic support for
+MI inheritance (basic but nevertheless with nice features, dynamic),
+Python 2.2 has <em>greatly</em> improved that support and with the
+change of the Method Resolution Order in Python 2.3, we may say
+that support for MI is now <em>excellent</em>.</p>
+<p>I strongly encourage Python programmers to use MI a lot: this will
+allows even a stronger reuse of code than in single inheritance.</p>
+<p>Often, inheritance is used when one has a complicate class B, and she wants
+to modify (or enhance) its behavior, by deriving a child class C, which is
+only slightly different from B. In this situation, B is already a standalone
+class, providing some non-trivial functionality, independently from
+the existence of C. This kind of design it typical of the so called
+<em>top-down</em> philosophy, where one builds the
+all structure as a monolithic block, leaving room only for minor improvements.
+An alternative approach is the so called <em>bottom-up</em> programming, in
+which one builds complicate things starting from very simple building blocks.
+In this logic, it is very appealing the idea of creating classes with the
+only purpose of being derived. The 'NonInstantiable' just defined is a
+perfect example of this kind of classes, though with multiple inheritance
+in mind and often called <em>mixin</em> classes.
+It can be used to create a new class <tt class="literal"><span class="pre">NonInstantiableClock</span></tt>
+that inherits from <tt class="literal"><span class="pre">Clock</span></tt> and from <tt class="literal"><span class="pre">NonInstantiable</span></tt>.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class NonInstantiableClock(Clock,NonInstantiable):
+ pass
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now <tt class="literal"><span class="pre">NonInstantiableClock</span></tt> is both a clock</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import NonInstantiableClock
+&gt;&gt;&gt; NonInstantiableClock.get_time() # works
+'12:57:00'
+</pre>
+</blockquote>
+<p>and a non-instantiable class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; NonInstantiableClock() # as expected, give an error
+Traceback (most recent call last):
+ File &quot;&lt;pyshell#2&gt;&quot;, line 1, in ?
+ NonInstantiableClock() # error
+ File &quot;oopp.py&quot;, line 245, in __new__
+ raise NonInstantiableError(&quot;%s cannot be instantiated&quot; % cls)
+NonInstantiableError: &lt;class 'oopp.NonInstantiableClock'&gt;
+cannot be instantiated
+</pre>
+</blockquote>
+<p>Let me give a simple example of a situation where the mixin approach
+comes handy. Suppose that the owner of a 'Pizza-shop' needs a program to
+take care of all the pizzas to-go he sell. Pizzas are distinguished
+according to their size (small, medium or large) and their toppings.
+The problem can be solved by inheriting from a generic pizza factory
+like this:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class GenericPizza(object): # to be customized
+ toppinglist=[] # nothing, default
+ baseprice=1 # one dollar, default
+ topping_unit_price=0.5 # half dollar for each topping, default
+ sizefactor={'small':1, 'medium':2, 'large':3}
+ # a medium size pizza costs twice a small pizza,
+ # a large pizza costs three times
+ def __init__(self,size):
+ self.size=size
+ def price(self):
+ return (self.baseprice+
+ self.toppings_price())*self.sizefactor[self.size]
+ def toppings_price(self):
+ return len(self.toppinglist)*self.topping_unit_price
+ def __str__(self):
+ return '%s pizza with %s, cost $ %s' % (self.size,
+ ','.join(self.toppinglist),
+ self.price())
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here the base class 'GenericPizza' is written with inheritance in mind: one
+can derives many pizza classes from it by overriding the <tt class="literal"><span class="pre">toppinglist</span></tt>;
+for instance one could define</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import GenericPizza
+&gt;&gt;&gt; class Margherita(GenericPizza):
+... toppinglist=['tomato']
+</pre>
+</blockquote>
+<p>The problem of this approach is that one must define dozens of
+different pizza subclasses (Marinara, Margherita, Capricciosa, QuattroStagioni,
+Prosciutto, ProsciuttoFunghi, PizzaDellaCasa, etc. etc. <a class="footnote-reference" href="#id30" id="id31" name="id31">[15]</a>). In such a
+situation, it is better to perform the generation of subclasses in a smarter
+way, i.e. via a customizable class factory.
+A simpler approach is to use always the same class and to customize
+its instances just after creation. Both approaches can be implemented via
+the following 'Customizable' mixin class, not meant to be instantiated,
+but rather to be <em>inherited</em>:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Customizable(object):
+ &quot;&quot;&quot;Classes inhering from 'Customizable' have a 'with' method acting as
+ an object modifier and 'With' classmethod acting as a class factory&quot;&quot;&quot;
+ def with(self,**kw):
+ customize(self,**kw)# customize the instance
+ return self # returns the customized instance
+ def With(cls,**kw):
+ class ChildOf(cls): pass # a new class inheriting from cls
+ ChildOf.__name__=cls.__name__ # by default, with the same name
+ customize(ChildOf,**kw) # of the original class
+ return ChildOf
+ With=classmethod(With)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Descendants of 'Customizable' can be customized by using
+'with', that directly acts on the instances, or 'With', that returns
+new classes. Notice that one could make 'With' to customize the
+original class, without returning a new one; however, in practice,
+this would not be safe: I remind that changing a class modifies
+automatically all its instances, even instances created <em>before</em>
+the modification. This could produce bad surprises: it is better to
+returns new classes, that may have the same name of the original one,
+but are actually completely independent from it.</p>
+<p>In order to solve the pizza shop problem we may define a 'CustomizablePizza'
+class</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class CustomizablePizza(GenericPizza,Customizable):
+ pass
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>which can be used in two ways: i) to customize instances just after creation:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import CustomizablePizza
+&gt;&gt;&gt; largepizza=CustomizablePizza('large') # CustomizablePizza instance
+&gt;&gt;&gt; largemarinara=largepizza.with(toppinglist=['tomato'],baseprice=2)
+&gt;&gt;&gt; print largemarinara
+large pizza with tomato mozzarella, cost $ 7.0
+</pre>
+</blockquote>
+<p>and ii) to generated customized new classes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Margherita=CustomizablePizza.With(
+... toppinglist=['tomato','mozzarella'], __name__='Margherita')
+&gt;&gt;&gt; print Margherita('medium')
+medium pizza with tomato,mozzarella, cost $ 4.0
+</pre>
+</blockquote>
+<p>The advantage of the bottom-up approach, is that the 'Customizable' class
+can be reused in completely different problems; for instance, it could
+be used as a class factory. For instance we could use it to generate a
+'CustomizableClock' class as in this example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; CustomizableClock=Customizable.With(get_time=staticmethod(Clock.get_time),
+... __name__='CustomizableClock') #adds get_time
+&gt;&gt;&gt; CustomizableClock.get_time() # now it works
+'09:57:50'
+</pre>
+</blockquote>
+<p>Here 'Customizable' &quot;steal&quot; the 'get_time' method from 'Clock'.
+However that would be a rather perverse usage ;) I wrote it to show
+the advantage of classmethods, more than to suggest to the reader that
+this is an example of good programming.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id32" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id33" name="id32">[16]</a></td><td>In Italy, you can easily find &quot;pizzerie&quot; with more than 50 different
+kinds of pizzas (once I saw a menu with something like one hundred
+different combinations ;)</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="cooperative-hierarchies">
+<h2><a class="toc-backref" href="#id88" name="cooperative-hierarchies">Cooperative hierarchies</a></h2>
+<p>The examples of multiple inheritance hierarchies given until now were pretty
+easy. The reason is that there was no interaction between the methods of the
+children and of the parents. However, things get more complicated (and
+interesting ;) when the methods in the hierarchy call each other.
+Let me consider an example coming from paleoantropology:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;paleoanthropology1.py&gt;
+
+class HomoHabilis(object):
+ def can(self):
+ print self,'can:'
+ print &quot; - make tools&quot;
+
+class HomoSapiens(HomoHabilis):
+ def can(self): #overrides HomoHabilis.can
+ HomoHabilis.can(self)
+ print &quot; - make abstractions&quot;
+
+class HomoSapiensSapiens(HomoSapiens):
+ def can(self): #overrides HomoSapiens.can
+ HomoSapiens.can(self)
+ print &quot; - make art&quot;
+
+modernman=HomoSapiensSapiens()
+modernman.can()
+
+#&lt;/paleoanthropology1.py&gt;
+</pre>
+</blockquote>
+<p>In this example children methods call parent methods:
+'HomoSapiensSapiens.can' calls 'HomoSapiens.can' that in turns calls
+'HomoHabilis.can' and the final output is:</p>
+<blockquote>
+<pre class="literal-block">
+&lt;__main__.HomoSapiensSapiens object at 0x814e1fc&gt; can:
+ - make tools
+ - make abstractions
+ - make art
+</pre>
+</blockquote>
+<p>The script works, but it is far from ideal, if code reuse and refactoring
+are considered important requirements. The point is that (very likely, as the
+research in paleoanthropology progresses) we may want to extend the
+hierarchy, for instance by adding a class on the top or in the middle.
+In the present form, this would require a non-trivial modification of
+the source code (especially
+if one think that the hierarchy could be fleshed out with dozens of others
+methods and attributes). However, the aim of OOP is to avoid as
+much as possible source code modifications. This goal can be attained in
+practice, if the source code is written to be friendly to extensions and
+improvements as much as possible. I think it is worth to spend some time
+in improving this example, since what can be learn here,
+can be lifted to real life cases.</p>
+<p>First of all, let me define a generic <em>Homo</em> class, to be used
+as first ring of the inheritance chain (actually the first ring is
+'object'):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Homo(PrettyPrinted):
+ &quot;&quot;&quot;Defines the method 'can', which is intended to be overriden
+ in the children classes, and inherits '__str__' from PrettyPrinted,
+ ensuring a nice printing representation for all children.&quot;&quot;&quot;
+ def can(self):
+ print self,'can:'
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me point out one of the shortcomings of the previous code: in each
+subclass, we explicitly call its parent class (also called super class)
+by its name. This is inconvenient, both because a change of name in
+later stages of the project would require a lot of search and replace
+(actually not a lot in this toy example, but you can imagine having
+a very big projects with dozens of named method calls) and because it makes
+difficult to insert a new element in the inheritance hierarchy.
+The solution to this problems is the
+<tt class="literal"><span class="pre">super</span></tt> built-in, which provides an easy access to the methods
+of the superclass.
+<tt class="literal"><span class="pre">super</span></tt> objects comes in two flavors: <tt class="literal"><span class="pre">super(cls,obj)</span></tt> objects return
+bound methods whereas <tt class="literal"><span class="pre">super(cls)</span></tt> objects return unbound methods.
+In the next code we will use the first form. The hierarchy can more elegantly
+be rewritten as <a class="footnote-reference" href="#id32" id="id33" name="id33">[16]</a> :</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;paleo2.py&gt;
+
+from oopp import Homo
+
+class HomoHabilis(Homo):
+ def can(self):
+ super(HomoHabilis,self).can()
+ print &quot; - make tools&quot;
+
+class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(HomoSapiens,self).can()
+ print &quot; - make abstractions&quot;
+
+class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(HomoSapiensSapiens,self).can()
+ print &quot; - make art&quot;
+
+
+HomoSapiensSapiens().can()
+
+#&lt;/paleo2.py&gt;
+</pre>
+</blockquote>
+<p>with output</p>
+<blockquote>
+<pre class="literal-block">
+&lt;HomoSapiensSapiens&gt; can:
+ - make tools
+ - make abstractions
+ - make art
+</pre>
+</blockquote>
+<p>This is not yet the most elegant form, since even
+if <tt class="literal"><span class="pre">super</span></tt> avoids naming the base class explicitely, still it
+requires to explicitely name the class where it is defined. This is
+rather annoying.
+Removing that restriction, i.e. implementing really anonymous
+<tt class="literal"><span class="pre">super</span></tt> calls, is possible but requires a good understand of
+private variables in inheritance.</p>
+</div>
+<div class="section" id="inheritance-and-privacy">
+<h2><a class="toc-backref" href="#id89" name="inheritance-and-privacy">Inheritance and privacy</a></h2>
+<p>In order to define anonymous cooperative super calls, we need classes
+that know themselves, i.e. containing a reference to themselves. This
+is not an obvious problem as it could seems, since it cannot be solved
+without incurring in the biggest annoyance in inheritance:
+<em>name clashing</em>. Name clashing happens when names and attributes defined
+in different ancestors overrides each other in a unwanted order.
+Name clashing is especially painful in the case of cooperative
+hierarchies and particularly in in the problem at hand.</p>
+<p>A naive solution would be to attach a plain (i.e. non-private)
+attribute '.this' to the class, containing a reference
+to itself, that can be invoked by the methods of the class.
+Suppose, for instance, that I want to use that attribute in the <tt class="literal"><span class="pre">__init__</span></tt>
+method of that class. A naive attempt would be to write something like:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... def __init__(self):
+... print self.this,'.__init__' # .this defined later
+&gt;&gt;&gt; B.this=B # B.this can be set only after B has been created
+&gt;&gt;&gt; B()
+&lt;class '__main__.B'&gt;
+</pre>
+</blockquote>
+<p>Unfortunately, this approach does not work with cooperative hierarchies.
+Consider, for instance, extending 'B' with a cooperative children
+class 'C' as follows:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(B):
+... def __init__(self):
+... super(self.this,self).__init__() # cooperative call
+... print type(self).this,'.__init__'
+&gt;&gt;&gt; C.this=C
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">C.__init__</span></tt> calls <tt class="literal"><span class="pre">B.__init__</span></tt> by passing a 'C' instance, therefore
+<tt class="literal"><span class="pre">C.this</span></tt> is printed and not <tt class="literal"><span class="pre">B.this</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C()
+&lt;class '__main__.C'&gt; .__init__
+&lt;class '__main__.C'&gt; .__init__
+&lt;__main__.C object at 0x4042ca6c&gt;
+</pre>
+</blockquote>
+<p>The problem is that the <tt class="literal"><span class="pre">C.this</span></tt> overrides <tt class="literal"><span class="pre">B.this</span></tt>. The only
+way of avoiding the name clashing is to use a private attribute
+<tt class="literal"><span class="pre">.__this</span></tt>, as in the following script:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;privateh.py&gt;
+
+class B(object):
+ def __init__(self):
+ print self.__this,'.__init__'
+B._B__this=B
+
+class C(B):
+ def __init__(self):
+ super(self.__this,self).__init__() # cooperative __init__
+ print self.__this,'.__init__'
+C._C__this=C
+
+C()
+
+# output:
+# &lt;class '__main__.B'&gt; .__init__
+# &lt;class '__main__.C'&gt; .__init__
+
+#&lt;/privateh.py&gt;
+</pre>
+</blockquote>
+<p>The script works since, due to the magic of the mangling mechanism,
+in <tt class="literal"><span class="pre">B.__init__</span></tt>, <tt class="literal"><span class="pre">self._B__this</span></tt> i.e. <tt class="literal"><span class="pre">B</span></tt> is retrieved, whereas in
+<tt class="literal"><span class="pre">C.__init__</span></tt> <tt class="literal"><span class="pre">self._C__this</span></tt> i.e. <tt class="literal"><span class="pre">C</span></tt> is retrieved.</p>
+<p>The elegance of the mechanism can be improved with an helper function
+that makes its arguments reflective classes, i.e. classes with a
+<tt class="literal"><span class="pre">__this</span></tt> private attribute:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def reflective(*classes):
+ &quot;&quot;&quot;Reflective classes know themselves, i.e. they possess a private
+ attribute __this containing a reference to themselves. If the class
+ name starts with '_', the underscores are stripped.&quot;&quot;&quot;
+ for c in classes:
+ name=c.__name__ .lstrip('_') # in 2.3
+ setattr(c,'_%s__this' % name,c)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>It is trivial to rewrite the paleonthropological hierarchy in terms of
+anonymous cooperative super calls by using this trick.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class HomoHabilis(Homo):
+ def can(self):
+ super(self.__this,self).can()
+ print &quot; - make tools&quot;
+
+class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(self.__this,self).can()
+ print &quot; - make abstractions&quot;
+
+class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(self.__this,self).can()
+ print &quot; - make art&quot;
+
+reflective(HomoHabilis,HomoSapiens,HomoSapiensSapiens)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; man=HomoSapiensSapiens(); man.can()
+&lt;HomoSapiensSapiens&gt; can:
+ - make tools
+ - make abstractions
+ - make art
+</pre>
+</blockquote>
+<p>We may understand why it works by looking at the attributes of man:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print pretty(attributes(man))
+_HomoHabilis__this = &lt;class 'oopp.HomoHabilis'&gt;
+_HomoSapiensSapiens__this = &lt;class 'oopp.HomoSapiensSapiens'&gt;
+_HomoSapiens__this = &lt;class 'oopp.HomoSapiens'&gt;
+can = &lt;bound method HomoSapiensSapiens.can of
+ &lt;oopp.HomoSapiensSapiens object at 0x404292ec&gt;&gt;
+formatstring = %s
+</pre>
+</blockquote>
+<p>It is also interesting to notice that the hierarchy can be entirely
+rewritten without using cooperative methods, but using private attributes,
+instead. This second approach is simpler, as the following script shows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;privatehierarchy.py&gt;
+
+from oopp import PrettyPrinted,attributes,pretty
+
+class Homo(PrettyPrinted):
+ def can(self):
+ print self,'can:'
+ for attr,value in attributes(self).iteritems():
+ if attr.endswith('__attr'): print value
+class HomoHabilis(Homo):
+ __attr=&quot; - make tools&quot;
+class HomoSapiens(HomoHabilis):
+ __attr=&quot; - make abstractions&quot;
+class HomoSapiensSapiens(HomoSapiens):
+ __attr=&quot; - make art&quot;
+
+modernman=HomoSapiensSapiens()
+modernman.can()
+print '----------------------------------\nAttributes of',modernman
+print pretty(attributes(modernman))
+
+#&lt;/privatehierarchy.py&gt;
+</pre>
+</blockquote>
+<p>Here I have replaced the complicate chain of cooperative methods with
+much simpler private attributes. Only the 'can' method in the 'Homo'
+class survives, and it is modified to print the value of the '__attr'
+attributes. Moreover, all the classes of the hierarchy have been made
+'Customizable', in view of future extensions.</p>
+<p>The second script is much shorter and much more elegant than the original
+one, however its logic can be a little baffling, at first. The solution
+to the mistery is provided by the attribute dictionary of 'moderman',
+given by the second part of the output:</p>
+<blockquote>
+<pre class="literal-block">
+&lt;HomoSapiensSapiens&gt; can:
+ - make abstractions
+ - make art
+ - make tools
+------------------------------------------
+Attributes of &lt;HomoSapiensSapiens&gt;:
+_HomoHabilis__attr = - make tools
+_HomoSapiensSapiens__attr = - make art
+_HomoSapiens__attr = - make abstractions
+can = &lt;bound method HomoSapiensSapiens.can of
+ &lt;__main__.HomoSapiensSapiens object at 0x402d892c&gt;&gt;
+formatstring = %s
+</pre>
+</blockquote>
+<p>We see that, in addition to the 'can' method inherited from 'Homo',
+the 'with' and 'With' method inherited from 'Customizable' and
+the 'formatstring' inherited from 'PrettyPrinted',
+<tt class="literal"><span class="pre">moderman</span></tt> has the attributes</p>
+<blockquote>
+<pre class="literal-block">
+_HomoHabilis__attr:' - make tools' # inherited from HomoHabilis
+_HomoSapiens__attr:' - make abstractions'# inherited from HomoSapiens
+_HomoSapiensSapiens__attr: ' - make art' # inherited from HomoSapiensSapiens
+</pre>
+</blockquote>
+<p>which origin is obvious, once one reminds the mangling mechanism associated
+with private variables. The important point is that the trick would <em>not</em>
+have worked for normal attributes. Had I used as variable name
+'attr' instead of '__attr', the name would have been overridden: the only
+attribute of 'HomoSapiensSapiens' would have been ' - make art'.</p>
+<p>This example explains the advantages of private variables during inheritance:
+they cannot be overridden. Using private name guarantees the absence of
+surprises due to inheritance. If a class B has only private variables,
+deriving a class C from B cannot cause name clashes.</p>
+<p>Private variables have a drawbacks, too. The most obvious disadvantages is
+the fact that in order to customize private variables outside their
+defining class, one needs to pass explicitly the name of the class.</p>
+<p>For instance we could not change an attribute with the syntax
+<tt class="literal"><span class="pre">HomoHabilis.With(__attr='</span> <span class="pre">-</span> <span class="pre">work</span> <span class="pre">the</span> <span class="pre">stone')</span></tt>, we must write the
+more verbose, error prone and redundant
+<tt class="literal"><span class="pre">HomoHabilis.With(_HomoHabilis__attr='</span> <span class="pre">-</span> <span class="pre">work</span> <span class="pre">the</span> <span class="pre">stone')</span></tt></p>
+<p>A subtler drawback will be discussed in chapter 6.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id34" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id35" name="id34">[17]</a></td><td>In single inheritance hierarchies, <tt class="literal"><span class="pre">super</span></tt> can be dismissed
+in favor of <tt class="literal"><span class="pre">__base__</span></tt>: for instance,
+<tt class="literal"><span class="pre">super(HomoSapiens,self).can()</span></tt> is equivalent to
+<tt class="literal"><span class="pre">HomoSapiens.__base__.can(self)</span></tt>. Nevertheless, in view
+of possible extensions to multiple inheritance, using <tt class="literal"><span class="pre">super</span></tt> is a
+much preferable choice.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+</div>
+<div class="section" id="the-sophistication-of-descriptors">
+<h1><a class="toc-backref" href="#id90" name="the-sophistication-of-descriptors">THE SOPHISTICATION OF DESCRIPTORS</a></h1>
+<p>Attribute descriptors are important metaprogramming tools that allows
+the user to customize the behavior of attributes in custom classes.
+For instance, attribute descriptors (or descriptors for short)
+can be used as method wrappers,
+to modify or enhance methods (this is the case for the well
+known staticmethods and classmethods attribute descriptors); they
+can also be used as attribute wrappers, to change or restrict the access to
+attributes (this is the case for properties). Finally, descriptors
+allows the user to play with the resolution order of attributes:
+for instance, the <tt class="literal"><span class="pre">super</span></tt> built-in object used in (multiple) inheritance
+hierarchies, is implemented as an attribute descriptor.</p>
+<p>In this chapter, I will show how the user can define its own attribute
+descriptors and I will give some example of useful things you can do with
+them (in particular to add tracing and timing capabilities).</p>
+<div class="section" id="motivation">
+<h2><a class="toc-backref" href="#id91" name="motivation">Motivation</a></h2>
+<p>Attribute descriptors are a recent idea (they where first introduced in
+Python 2.2) nevertheless, under the hood, are everywhere in Python. It is
+a tribute to Guido's ability of hiding Python complications that
+the average user can easily miss they existence.
+If you need to do simple things, you can very well live without
+the knowledge of descriptors. On the other hand, if you need difficult
+things (such as tracing all the attribute access of your modules)
+attribute descriptors, allow you to perform
+impressive things.
+Let me start by showing why the knowledge of attribute descriptors is
+essential for any user seriously interested in metaprogramming applications.
+Suppose I want to trace the methods of a clock:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; clock=oopp.Clock()
+</pre>
+</blockquote>
+<p>This is easily done with the <tt class="literal"><span class="pre">with_tracer</span></tt> closure of chapter 2:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; oopp.wrapfunctions(clock,oopp.with_tracer)
+&lt;oopp.Clock object at 0x4044c54c&gt;
+&gt;&gt;&gt; clock.get_time()
+[] Calling 'get_time' with arguments
+(){} ...
+-&gt; '.get_time' called with result: 19:55:07
+'19:55:07'
+</pre>
+</blockquote>
+<p>However, this approach fails if I try to trace the entire class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; oopp.wrapfunctions(oopp.Clock,oopp.with_tracer)
+&lt;class 'oopp.Clock'&gt;
+&gt;&gt;&gt; oopp.Clock.get_time() # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 6, in ?
+TypeError: unbound method _() must be called with Clock instance
+as first argument (got nothing instead)
+</pre>
+</blockquote>
+<p>The reason is that <tt class="literal"><span class="pre">wrapfunctions</span></tt> sets the attributes of 'Clock'
+by invoking <tt class="literal"><span class="pre">customize</span></tt>, which uses <tt class="literal"><span class="pre">setattr</span></tt>. This converts
+'_' (i.e. the traced version of <tt class="literal"><span class="pre">get_time</span></tt>) in a regular method, not in
+a staticmethod!
+In order to trace staticmethods, one has to understand the nature
+of attribute descriptors.</p>
+</div>
+<div class="section" id="functions-versus-methods">
+<h2><a class="toc-backref" href="#id92" name="functions-versus-methods">Functions versus methods</a></h2>
+<p>Attribute descriptors are essential for the implementation
+of one of the most basic Python features: the automatic conversion
+of functions in methods. As I already anticipated in chapter 1, there is
+a sort of magic when one writes <tt class="literal"><span class="pre">Clock.get_time=lambda</span> <span class="pre">self:</span> <span class="pre">get_time()</span></tt>
+and Python automagically converts the right hand side, that is a
+function, to a left hand side that is a (unbound) method. In order to
+understand this magic, one needs a better comprehension of the
+relation between functions and methods.
+Actually, this relationship is quite subtle
+and has no analogous in mainstream programming languages.
+For instance, C is not OOP and has only functions, lacking the concept
+of method, whereas Java (as other OOP languages)
+has no functions, only methods.
+C++ has functions and methods, but functions are completely
+different from methods On the other hand, in Python,
+functions and methods can be transformed both ways.</p>
+<p>To show how it works, let me start by defining a simple printing
+function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import __main__ # gives access to the __main__ namespace from the module
+
+def prn(s):
+ &quot;&quot;&quot;Given an evaluable string, print its value and its object reference.
+ Notice that the evaluation is done in the __main__ dictionary.&quot;&quot;&quot;
+ try: obj=eval(s,__main__.__dict__)
+ except: print 'problems in evaluating',s
+ else: print s,'=',obj,'at',hex(id(obj))
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me define a class with a method <tt class="literal"><span class="pre">m</span></tt> equals to the identity
+function <tt class="literal"><span class="pre">f</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): &quot;Identity function&quot;; return x
+...
+&gt;&gt;&gt; class C(object):
+... m=f
+... print m #here m is the function f
+&lt;function f at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>We see that <em>inside</em> its defining class, <tt class="literal"><span class="pre">m</span></tt> coincides with the function
+<tt class="literal"><span class="pre">f</span></tt> (the object reference is the same):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f
+&lt;function f at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>We may retrieve <tt class="literal"><span class="pre">m</span></tt> from <em>outside</em> the class via the class dictionary <a class="footnote-reference" href="#id34" id="id35" name="id35">[17]</a>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__['m']
+&lt;function prn at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>However, if we invoke <tt class="literal"><span class="pre">m</span></tt> with
+the syntax <tt class="literal"><span class="pre">C.m</span></tt>, then it (magically) becomes a (unbound) method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m #here m has become a method!
+&lt;unbound method C.f&gt;
+</pre>
+</blockquote>
+<p>But why it is so? How comes that in the second syntax the function
+<tt class="literal"><span class="pre">f</span></tt> is transformed in a (unbound) method? To answer that question, we have
+to understand how attributes are really invoked in Python, i.e. via
+attribute descriptors.</p>
+</div>
+<div class="section" id="methods-versus-functions">
+<h2><a class="toc-backref" href="#id93" name="methods-versus-functions">Methods versus functions</a></h2>
+<p>First of all, let me point out the differences between methods and
+functions. Here, <tt class="literal"><span class="pre">C.m</span></tt> does <em>not</em> coincides with <tt class="literal"><span class="pre">C.__dict__['m']</span></tt>
+i.e. <tt class="literal"><span class="pre">f</span></tt>, since its object reference is different:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import prn,attributes
+&gt;&gt;&gt; prn('C.m')
+C.m = &lt;unbound method C.prn&gt; at 0x81109b4
+</pre>
+</blockquote>
+<p>The difference is clear since methods and functions have different attributes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; attributes(f).keys()
+['func_closure', 'func_dict', 'func_defaults', 'func_name',
+'func_code', 'func_doc', 'func_globals']
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; attributes(C.m).keys()
+['im_func', 'im_class', 'im_self']
+</pre>
+</blockquote>
+<p>We discussed few of the functions attributes in the chapter
+on functions. The instance method attributes are simpler: <tt class="literal"><span class="pre">im_self</span></tt>
+returns the object to which the method is attached,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C.m.im_self #unbound method, attached to the class
+None
+&gt;&gt;&gt; C().m.im_self #bound method, attached to C()
+&lt;__main__.C object at 0x81bf4ec&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">im_class</span></tt> returns the class to which the
+method is attached</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.im_class #class of the unbound method
+&lt;class '__main__.C'&gt;
+&gt;&gt;&gt; C().m.im_class #class of the bound method,
+&lt;class '__main__.C'&gt;
+</pre>
+</blockquote>
+<p>and <tt class="literal"><span class="pre">im_func</span></tt> returns the function equivalent to
+the method.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.im_func
+&lt;function m at 0x8157f44&gt;
+&gt;&gt;&gt; C().m.im_func # the same
+&lt;function m at 0x8157f44&gt;
+</pre>
+</blockquote>
+<p>As the reference manual states, calling
+<tt class="literal"><span class="pre">m(*args,**kw)</span></tt> is completely equivalent to calling
+<tt class="literal"><span class="pre">m.im_func(m.im_self,</span> <span class="pre">*args,**kw)</span></tt>&quot;.</p>
+<p>As a general rule, an attribute descriptor is an object with a <tt class="literal"><span class="pre">__get__</span></tt>
+special method. The most used descriptors are the good old functions:
+they have a <tt class="literal"><span class="pre">__get__</span></tt> special method returning a <em>method-wrapper object</em></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__
+&lt;method-wrapper object at 0x815cdc4&gt;
+</pre>
+</blockquote>
+<p>method-wrapper objects can be transformed in (both bound and unbound) methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__(None,C)
+&lt;unbound method C.f&gt;
+&gt;&gt;&gt; f.__get__(C(),C)
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>The general calling syntax for method-wrapper objects is
+<tt class="literal"><span class="pre">.__get__(obj,cls=None)</span></tt>, where the first argument is an
+instance object or None and the second (optional) argument is the class (or a
+generic superclass) of the first one.</p>
+<p>Now we see what happens when we use the syntax <tt class="literal"><span class="pre">C.m</span></tt>: Python interprets
+this as a shortcut for <tt class="literal"><span class="pre">C.__dict['m'].__get__(None,C)</span></tt> (if <tt class="literal"><span class="pre">m</span></tt> is
+in the 'C' dictionary, otherwise it looks for ancestor dictionaries).
+We may check that everything is correct by observing that
+<tt class="literal"><span class="pre">f.__get__(None,C)</span></tt> has exactly the same object reference than <tt class="literal"><span class="pre">C.m</span></tt>,
+therefore they are the same object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; hex(id(f.__get__(None,C))) # same as hex(id(C.m))
+'0x811095c'
+</pre>
+</blockquote>
+<p>The process works equally well for the syntax <tt class="literal"><span class="pre">getattr</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print getattr(C,'m'), hex(id(getattr(C,'m')))
+&lt;unbound method C.f&gt; 0x811095c
+</pre>
+</blockquote>
+<p>and for bound methods: if</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>is an instance of the class C, then the syntax</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; getattr(c,'m') #same as c.m
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>is a shortcut for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(c).__dict__['m'].__get__(c,C) # or f.__get__(c,C)
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>(notice that the object reference for <tt class="literal"><span class="pre">c.m</span></tt> and <tt class="literal"><span class="pre">f.__get__(c,C)</span></tt> is
+the same, they are <em>exactly</em> the same object).</p>
+<p>Both the unbound method C.m and the bound method c.m refer to the same
+object at hexadecimal address 0x811095c. This object is common to all other
+instances of C:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c2=C()
+&gt;&gt;&gt; print c2.m,hex(id(c2.m)) #always the same method
+&lt;bound method C.m of &lt;__main__.C object at 0x815768c&gt;&gt; 0x811095c
+</pre>
+</blockquote>
+<p>One can also omit the second argument:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.m.__get__(c)
+&lt;bound method ?.m of &lt;__main__.C object at 0x81597dc&gt;&gt;
+</pre>
+</blockquote>
+<p>Finally, let me point out that methods are attribute descriptors too,
+since they have a <tt class="literal"><span class="pre">__get__</span></tt> attribute returning a method-wrapper
+object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.__get__
+&lt;method-wrapper object at 0x815d51c&gt;
+</pre>
+</blockquote>
+<p>Notice that this method wrapper is <em>not</em> the same than the <tt class="literal"><span class="pre">f.__get__</span></tt>
+method wrapper.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id36" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id37" name="id36">[18]</a></td><td>If <tt class="literal"><span class="pre">C.__dict['m']</span></tt> is not defined, Python looks if <tt class="literal"><span class="pre">m</span></tt> is defined
+in some ancestor of C. For instance if <cite>B</cite> is the base of <cite>C</cite>, it
+looks in <tt class="literal"><span class="pre">B.__dict['m']</span></tt>, etc., by following the MRO.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="static-methods-and-class-methods">
+<h2><a class="toc-backref" href="#id94" name="static-methods-and-class-methods">Static methods and class methods</a></h2>
+<p>Whereas functions and methods are implicit attribute descriptors,
+static methods and class methods are examples of explicit
+descriptors. They allow to convert regular functions to
+specific descriptor objects. Let me show a trivial example.
+Given the identity function</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): return x
+</pre>
+</blockquote>
+<p>we may convert it to a staticmethod object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm=staticmethod(f)
+&gt;&gt;&gt; sm
+&lt;staticmethod object at 0x4018a0a0&gt;
+</pre>
+</blockquote>
+<p>or to a classmethod object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm=classmethod(f)
+&gt;&gt;&gt; cm
+&lt;classmethod object at 0x4018a0b0&gt;
+</pre>
+</blockquote>
+<p>In both cases the <tt class="literal"><span class="pre">__get__</span></tt> special method returns a method-wrapper object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm.__get__
+&lt;method-wrapper object at 0x401751ec&gt;
+&gt;&gt;&gt; cm.__get__
+&lt;method-wrapper object at 0x4017524c&gt;
+</pre>
+</blockquote>
+<p>However the static method wrapper is quite different from the class
+method wrapper. In the first case the wrapper returns a function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm.__get__(C(),C)
+&lt;function f at 0x4027a8b4&gt;
+&gt;&gt;&gt; sm.__get__(C())
+&lt;function f at 0x4027a8b4&gt;
+</pre>
+</blockquote>
+<p>in the second case it returns a method</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__(C(),C)
+&lt;bound method type.f of &lt;class '__main__.C'&gt;&gt;
+</pre>
+</blockquote>
+<p>Let me discuss more in detail the static methods, first.</p>
+<p>It is always possible to extract the function from the static method
+via the syntaxes <tt class="literal"><span class="pre">sm.__get__(a)</span></tt> and <tt class="literal"><span class="pre">sm.__get__(a,b)</span></tt> with <em>ANY</em> valid
+a and b, i.e. the result does not depend on a and b. This is correct,
+since static methods are actually function that have nothing to do
+with the class and the instances to which they are bound.</p>
+<p>This behaviour of the method wrapper makes clear why the relation between
+methods and functions is inversed for static methods with respect to
+regular methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... s=staticmethod(lambda : None)
+... print s
+...
+&lt;staticmethod object at 0x8158ec8&gt;
+</pre>
+</blockquote>
+<p>Static methods are non-trivial objects <em>inside</em> the class, whereas
+they are regular functions <em>outside</em> the class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.s
+&lt;function &lt;lambda&gt; at 0x8158e7c&gt;
+&gt;&gt;&gt; C().s
+&lt;function &lt;lambda&gt; at 0x8158e7c&gt;
+</pre>
+</blockquote>
+<p>The situation is different for classmethods: inside the class they
+are non-trivial objects, just as static methods,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... cm=classmethod(lambda cls: None)
+... print cm
+...
+&lt;classmethod object at 0x8156100&gt;
+</pre>
+</blockquote>
+<p>but outside the class they are methods bound to the class,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; prn('c.cm')
+&lt;bound method type.&lt;lambda&gt; of &lt;class '__main__.C'&gt;&gt;
+0x811095c
+</pre>
+</blockquote>
+<p>and not to the instance 'c'. The reason is that the <tt class="literal"><span class="pre">__get__</span></tt> wrapper method
+can be invoked with the syntax <tt class="literal"><span class="pre">__get__(a,cls)</span></tt> which
+is only sensitive to the second argument or with the syntax
+<tt class="literal"><span class="pre">__get__(obj)</span></tt> which is only sensitive to the type of the first
+argument:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever',C) # the first argument is ignored
+&lt;bound method type.f of &lt;class '__main__.C'&gt;&gt;
+</pre>
+</blockquote>
+<p>sensitive to the type of 'whatever':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever') # in Python 2.2 would give a serious error
+&lt;bound method type.f of &lt;type 'str'&gt;&gt;
+</pre>
+</blockquote>
+<p>Notice that the class method is actually bound to C's class, i.e.
+to 'type'.</p>
+<p>Just as regular methods (and differently
+from static methods) classmethods have attributes <tt class="literal"><span class="pre">im_class</span></tt>, <tt class="literal"><span class="pre">im_func</span></tt>,
+and <tt class="literal"><span class="pre">im_self</span></tt>. In particular one can retrieve the function wrapped inside
+the classmethod with</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever','whatever').im_func
+&lt;function f at 0x402c2534&gt;
+</pre>
+</blockquote>
+<p>The difference with regular methods is that <tt class="literal"><span class="pre">im_class</span></tt> returns the
+class of 'C' whereas <tt class="literal"><span class="pre">im_self</span></tt> returns 'C' itself.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.cm.im_self # a classmethod is attached to the class
+&lt;class '__main__.C'&gt;
+&gt;&gt;&gt; C.cm.im_class #the class of C
+&lt;type 'type'&gt;
+</pre>
+</blockquote>
+<p>Remark: Python 2.2.0 has a bug in classmethods (fixed in newer versions):
+when the first argument of __get__ is None, then one must specify
+the second argument (otherwise segmentation fault :-()</p>
+</div>
+<div class="section" id="properties">
+<h2><a class="toc-backref" href="#id95" name="properties">Properties</a></h2>
+<p>Properties are a more general kind of attribute descriptors than
+staticmethods and classmethods, since their effect can be customized
+trough arbitrary get/set/del functions. Let me give an example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def getp(self): return 'property' # get function
+...
+&gt;&gt;&gt; p=property(getp) # property object
+&gt;&gt;&gt; p
+&lt;property object at 0x815855c&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">p</span></tt> has a <tt class="literal"><span class="pre">__get__</span></tt> special method returning a method-wrapper
+object, just as it happens for other descriptors:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; p.__get__
+&lt;method-wrapper object at 0x8158a7c&gt;
+</pre>
+</blockquote>
+<p>The difference is that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; p.__get__(None,type(p))
+&lt;property object at 0x4017016c&gt;
+&gt;&gt;&gt; p.__get__('whatever')
+'property'
+&gt;&gt;&gt; p.__get__('whatever','whatever')
+'property'
+</pre>
+</blockquote>
+<p>As for static methods, the <tt class="literal"><span class="pre">__get__</span></tt> method wrapper is independent from
+its arguments, unless the first one is None: in such a case it returns
+the property object, in all other circumstances it returns the result
+of <tt class="literal"><span class="pre">getp</span></tt>. This explains the behavior</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): p=p
+&gt;&gt;&gt; C.p
+&lt;property object at 0x815855c&gt;
+&gt;&gt;&gt; C().p
+'property'
+</pre>
+</blockquote>
+<p>Properties are a dangerous feature, since they change the semantics
+of the language. This means that apparently trivial operations can have
+any kind of side effects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def get(self):return 'You gave me the order to destroy your hard disk!!'
+&gt;&gt;&gt; class C(object): x=property(get)
+&gt;&gt;&gt; C().x
+'You gave me the order to destroy your hard disk!!'
+</pre>
+</blockquote>
+<p>Invoking 'C.x' could very well invoke an external program who is going
+to do anything! It is up to the programmer to not abuse properties.
+The same is true for user defined attribute descriptors.</p>
+<p>There are situations in which they are quite handy, however. For
+instance, properties can be used to trace the access data attributes.
+This can be especially useful during debugging, or for logging
+purposes.</p>
+<p>Notice that this approach has the problem that now data attributes cannot
+no more be called trough their class, but only though their instances.
+Moreover properties do not work well with <tt class="literal"><span class="pre">super</span></tt> in cooperative
+methods.</p>
+</div>
+<div class="section" id="user-defined-attribute-descriptors">
+<h2><a class="toc-backref" href="#id96" name="user-defined-attribute-descriptors">User-defined attribute descriptors</a></h2>
+<p>As we have seen, there are plenty of predefined attribute descriptors,
+such as staticmethods, classmethods and properties (the built-in
+<tt class="literal"><span class="pre">super</span></tt> is also an attribute descriptor which, for sake of
+convenience, will be discussed in the next section).
+In addition to them, the user can also define customized attribute
+descriptors, simply trough classes with a <tt class="literal"><span class="pre">__get__</span></tt> special method.
+Let me give an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;simpledescr.py&gt;
+
+class ChattyAttr(object):
+ &quot;&quot;&quot;Chatty descriptor class; descriptor objects are intended to be
+ used as attributes in other classes&quot;&quot;&quot;
+ def __get__(self, obj, cls=None):
+ binding=obj is not None
+ if binding:
+ return 'You are binding %s to %s' % (self,obj)
+ else:
+ return 'Calling %s from %s' % (self,cls)
+
+class C(object):
+ d=ChattyAttr()
+
+c=C()
+
+print c.d # &lt;=&gt; type(c).__dict__['d'].__get__(c,type(c))
+print C.d # &lt;=&gt; C.__dict__['d'].__get__(None,C)
+
+#&lt;/simpledescr.py&gt;
+</pre>
+</blockquote>
+<p>with output:</p>
+<blockquote>
+<pre class="literal-block">
+You are binding &lt;ChattyAttr object at 0x401bc1cc&gt; to
+&lt;C object at 0x401bc2ec&gt;
+Calling &lt;ChattyAttr object at 0x401bc1cc&gt; from &lt;class 'C'&gt;
+</pre>
+</blockquote>
+<p>Invoking a method with the syntax <tt class="literal"><span class="pre">C.d</span></tt> or <tt class="literal"><span class="pre">c.d</span></tt> involves calling
+<tt class="literal"><span class="pre">__get__</span></tt>. The <tt class="literal"><span class="pre">__get__</span></tt> signature is fixed: it is
+`` __get__=__get__(self,obj,cls=None)``, since the notation
+<tt class="literal"><span class="pre">self.descr_attr</span></tt> automatically passes <tt class="literal"><span class="pre">self</span></tt> and <tt class="literal"><span class="pre">self.__class__</span></tt> to
+<tt class="literal"><span class="pre">__get__</span></tt>.</p>
+<p>Custom descriptors can be used to restrict the access to objects in a
+more general way than trough properties. For instance, suppose one
+wants to raise an error if a given attribute 'a' is accessed, both
+from the class and from the instance: a property cannot help here,
+since it works only from the instance. The solution is the following
+custom descriptor:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class AccessError(object):
+ &quot;&quot;&quot;Descriptor raising an AttributeError when the attribute is
+ accessed&quot;&quot;&quot; #could be done with a property
+ def __init__(self,errormessage):
+ self.msg=errormessage
+ def __get__(self,obj,cls=None):
+ raise AttributeError(self.msg)
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import AccessError
+&gt;&gt;&gt; class C(object):
+... a=AccessError(&quot;'a' cannot be accessed&quot;)
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.a #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 313, in __get__
+ raise AttributeError(self.msg)
+AttributeError: 'a' cannot be accessed
+&gt;&gt;&gt; C.a #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 313, in __get__
+ raise AttributeError(self.msg)
+AttributeError: 'a' cannot be accessed
+</pre>
+</blockquote>
+<p>It is always possibile to convert plain attributes (i.e. attributes
+without a &quot;__get__&quot; method) to descriptor objects:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class convert2descriptor(object):
+ &quot;&quot;&quot;To all practical means, this class acts as a function that, given an
+ object, adds to it a __get__ method if it is not already there. The
+ added __get__ method is trivial and simply returns the original object,
+ independently from obj and cls.&quot;&quot;&quot;
+ def __new__(cls,a):
+ if hasattr(a,&quot;__get__&quot;): # do nothing
+ return a # a is already a descriptor
+ else: # creates a trivial attribute descriptor
+ cls.a=a
+ return object.__new__(cls)
+ def __get__(self,obj,cls=None):
+ &quot;Returns self.a independently from obj and cls&quot;
+ return self.a
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This example also shows the magic of <tt class="literal"><span class="pre">__new__</span></tt>, that allows to use a
+class as a function. The output of 'convert2descriptor(a)' can be both
+an instance of 'convert2descriptor' (in this case 'convert2descriptor' acts as
+a normal class, i.e. as an object factory) or 'a' itself
+(if 'a' is already a descriptor): in this case 'convert2descriptor' acts
+as a function.</p>
+<p>For instance, a string is converted to a descriptor</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import convert2descriptor
+&gt;&gt;&gt; a2=convert2descriptor('a')
+&gt;&gt;&gt; a2
+&lt;oopp.convert2descriptor object at 0x4017506c&gt;
+&gt;&gt;&gt; a2.__get__('whatever')
+'a'
+</pre>
+</blockquote>
+<p>whereas a function is untouched:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(): pass
+&gt;&gt;&gt; f2=convert2descriptor(f) # does nothing
+&gt;&gt;&gt; f2
+&lt;function f at 0x4019110c&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="data-descriptors">
+<h2><a class="toc-backref" href="#id97" name="data-descriptors">Data descriptors</a></h2>
+<p>It is also possible to specify a <tt class="literal"><span class="pre">__set__</span></tt> method (descriptors
+with a <tt class="literal"><span class="pre">__set__</span></tt> method are typically data descriptors) with
+the signature <tt class="literal"><span class="pre">__set__(self,obj,value)</span></tt> as in the following
+example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;datadescr.py&gt;
+
+class DataDescriptor(object):
+ value=None
+ def __get__(self, obj, cls=None):
+ if obj is None: obj=cls
+ print &quot;Getting&quot;,obj,&quot;value =&quot;,self.value
+ return self.value
+ def __set__(self, obj, value):
+ self.value=value
+ print &quot;Setting&quot;,obj,&quot;value =&quot;,value
+
+class C(object):
+ d=DataDescriptor()
+
+c=C()
+
+c.d=1 #calls C.__dict__['d'].__set__(c,1)
+c.d #calls C.__dict__['d'].__get__(c,C)
+C.d #calls C.__dict__['d'].__get__(None,C)
+C.d=0 #does *not* call __set__
+print &quot;C.d =&quot;,C.d
+
+#&lt;/datadescr.py&gt;
+</pre>
+</blockquote>
+<p>With output:</p>
+<blockquote>
+<pre class="literal-block">
+Setting &lt;C object at 0x401bc1ec&gt; value = 1
+Getting &lt;C object at 0x401bc42c&gt; value = 1
+Getting &lt;class 'C'&gt; value = 1
+C.d = 0
+</pre>
+</blockquote>
+<p>With this knowledge, we may now reconsider the clock example given
+in chapter 3. #NO!??</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; class Clock(object): pass
+&gt;&gt;&gt; myclock=Clock()
+...
+&gt;&gt;&gt; myclock.get_time=oopp.get_time # this is a function
+&gt;&gt;&gt; Clock.get_time=lambda self : oopp.get_time() # this is a method
+</pre>
+</blockquote>
+<p>In this example, <tt class="literal"><span class="pre">myclock.get_time</span></tt>, which is attached to the <tt class="literal"><span class="pre">myclock</span></tt>
+object, is a function, whereas <tt class="literal"><span class="pre">Clock.get_time</span></tt>, which is attached to
+the <tt class="literal"><span class="pre">Clock</span></tt> class is a method. We may also check this by using the <tt class="literal"><span class="pre">type</span></tt>
+function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(myclock.get_time)
+&lt;type 'function'&gt;
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(Clock.get_time)
+&lt;type 'instance method'&gt;
+</pre>
+</blockquote>
+<p>It must be remarked that user-defined attribute descriptors, just as
+properties, allow to arbitrarily change the semantics of the language
+and should be used with care.</p>
+</div>
+<div class="section" id="the-super-attribute-descriptor">
+<h2><a class="toc-backref" href="#id98" name="the-super-attribute-descriptor">The <tt class="literal"><span class="pre">super</span></tt> attribute descriptor</a></h2>
+<p>super has also a second form, where it is more used as a descriptor.</p>
+<p><tt class="literal"><span class="pre">super</span></tt> objects are attribute descriptors, too, with a <tt class="literal"><span class="pre">__get__</span></tt>
+method returning a method-wrapper object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C()).__get__
+&lt;method-wrapper object at 0x8161074&gt;
+</pre>
+</blockquote>
+<p>Here I give some example of acceptable call:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C()).__get__('whatever')
+&lt;super: &lt;class 'C'&gt;, &lt;C object&gt;&gt;
+&gt;&gt;&gt; super(C,C()).__get__('whatever','whatever')
+&lt;super: &lt;class 'C'&gt;, &lt;C object&gt;&gt;
+</pre>
+</blockquote>
+<p>Unfortunately, for the time being
+(i.e. for Python 2.3), the <tt class="literal"><span class="pre">super</span></tt> mechanism has various limitations.
+To show the issues, let me start by considering the following base class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class ExampleBaseClass(PrettyPrinted):
+ &quot;&quot;&quot;Contains a regular method 'm', a staticmethod 's', a classmethod
+ 'c', a property 'p' and a data attribute 'd'.&quot;&quot;&quot;
+ m=lambda self: 'regular method of %s' % self
+ s=staticmethod(lambda : 'staticmethod')
+ c=classmethod(lambda cls: 'classmethod of %s' % cls)
+ p=property(lambda self: 'property of %s' % self)
+ a=AccessError('Expected error')
+ d='data'
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me derive a new class C from ExampleBaseClass:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import ExampleBaseClass
+&gt;&gt;&gt; class C(ExampleBaseClass): pass
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>Ideally, we would like to retrieve the methods and attributes of
+ExampleBaseClass from C, by using the <tt class="literal"><span class="pre">super</span></tt> mechanism.</p>
+<ol class="arabic simple">
+<li>We see that <tt class="literal"><span class="pre">super</span></tt> works without problems for regular methods,
+staticmethods and classmethods:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).m()
+'regular method of &lt;C&gt;'
+&gt;&gt;&gt; super(C,c).s()
+'staticmethod'
+&gt;&gt;&gt; super(C,c).c()
+&quot;classmethod of &lt;class '__main__.C'&gt;&quot;
+</pre>
+</blockquote>
+<p>It also works for user defined attribute descriptors:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).a # access error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 340, in __get__
+ raise AttributeError(self.msg)
+AttributeError: Expected error
+</pre>
+</blockquote>
+<p>and for properties (only for Python 2.3+):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ExampleBaseClass.p
+&lt;property object at 0x81b30fc&gt;
+</pre>
+</blockquote>
+<p>In Python 2.2 one would get an error, instead</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).p #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute 'p'
+</pre>
+</blockquote>
+<p>3. Moreover, certain attributes of the superclass, such as its
+<tt class="literal"><span class="pre">__name__</span></tt>, cannot be retrieved:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ExampleBaseClass.__name__
+'ExampleBaseClass'
+&gt;&gt;&gt; super(C,c).__name__ #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+</pre>
+</blockquote>
+<ol class="arabic simple" start="4">
+<li>There is no direct way to retrieve the methods of the super-superclass
+(i.e. the grandmother class, if you wish) or in general the furthest
+ancestors, since <tt class="literal"><span class="pre">super</span></tt> does not chain.</li>
+<li>Finally, there are some subtle issues with the <tt class="literal"><span class="pre">super(cls)</span></tt> syntax:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C).m #(2) error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute 'm'
+</pre>
+</blockquote>
+<p>means <tt class="literal"><span class="pre">super(C).__get__(None,C)</span></tt>, but only
+<tt class="literal"><span class="pre">super(C).__get__(c,C).m==super(C,c)</span></tt> works.</p>
+<blockquote>
+<blockquote>
+On the other hand,</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C).__init__ #(1)
+&lt;built-in method __init__ of type object at 0x80e6fc0&gt;
+&gt;&gt;&gt; super(C).__new__ #(1)
+&lt;built-in method __init__ of type object at 0x80e6fc0&gt;
+</pre>
+<blockquote>
+seems to work, whereas in reality does not. The reason is that since
+<tt class="literal"><span class="pre">super</span></tt> objects are instances
+of <tt class="literal"><span class="pre">object</span></tt>, they inherit object's methods, and in particular
+<tt class="literal"><span class="pre">__init__</span></tt> ; therefore the <tt class="literal"><span class="pre">__init__</span></tt> method in (1) is <em>not</em>
+the <tt class="literal"><span class="pre">ExampleBaseClass.__init__</span></tt> method. The point is that <tt class="literal"><span class="pre">super</span></tt>
+objects are attribute descriptors and not references to the superclass.</blockquote>
+</blockquote>
+<p>Probably, in future versions of Python the <tt class="literal"><span class="pre">super</span></tt> mechanism will be
+improved. However, for the time being, one must provide a workaround for
+dealing with these issues. This will be discussed in the next chapter.</p>
+</div>
+<div class="section" id="method-wrappers">
+<h2><a class="toc-backref" href="#id99" name="method-wrappers">Method wrappers</a></h2>
+<p>One of the most typical applications of attribute descriptors is their
+usage as <em>method wrappers</em>.</p>
+<p>Suppose, for instance, one wants to add tracing capabilities to
+the methods of a class for debugging purposes. The problem
+can be solved with a custom descriptor class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import inspect
+
+class wrappedmethod(Customizable):
+ &quot;&quot;&quot;Customizable method factory intended for derivation.
+ The wrapper method is overridden in the children.&quot;&quot;&quot;
+
+ logfile=sys.stdout # default
+ namespace='' # default
+
+ def __new__(cls,meth): # meth is a descriptor
+ if isinstance(meth,FunctionType):
+ kind=0 # regular method
+ func=meth
+ elif isinstance(meth,staticmethod):
+ kind=1 # static method
+ func=meth.__get__('whatever')
+ elif isinstance(meth,classmethod):
+ kind=2 # class method
+ func=meth.__get__('whatever','whatever').im_func
+ elif isinstance(meth,wrappedmethod): # already wrapped
+ return meth # do nothing
+ elif inspect.ismethoddescriptor(meth):
+ kind=0; func=meth # for many builtin methods
+ else:
+ return meth # do nothing
+ self=super(wrappedmethod,cls).__new__(cls)
+ self.kind=kind; self.func=func # pre-initialize
+ return self
+
+ def __init__(self,meth): # meth not used
+ self.logfile=self.logfile # default values
+ self.namespace=self.namespace # copy the current
+
+ def __get__(self,obj,cls): # closure
+ def _(*args,**kw):
+ if obj is None: o=() # unbound method call
+ else: o=(obj,) # bound method call
+ allargs=[o,(),(cls,)][self.kind]+args
+ return self.wrapper()(*allargs,**kw)
+ return _ # the wrapped function
+ # allargs is the only nontrivial line in _; it adds
+ # 0 - obj if meth is a regular method
+ # 1 - nothing if meth is a static method
+ # 2 - cls if meth is a class method
+
+ def wrapper(self): return self.func # do nothing, to be overridden
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This class is intended for derivation: the wrapper method has to be overridden
+in the children in order to introduce the wanted feature. If I want to
+implement the capability of tracing methods, I can reuse the <tt class="literal"><span class="pre">with_tracer</span></tt>
+closure introduced in chapter 2:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class tracedmethod(wrappedmethod):
+ def wrapper(self):
+ return with_tracer(self.func,self.namespace,self.logfile)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Nothing prevents me from introducing timing features by reusing the
+<tt class="literal"><span class="pre">with_timer</span></tt> closure:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class timedmethod(wrappedmethod):
+ iterations=1 # additional default parameter
+
+ def __init__(self,meth):
+ super(timedmethod,self).__init__(self,meth)
+ self.iterations=self.iterations # copy
+
+ def wrapper(self):
+ return with_timer(self.func,self.namespace,
+ self.iterations,self.logfile)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<p>The dictionary of wrapped functions is then built from an utility function</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def wrap(obj,wrapped,condition=lambda k,v: True, err=None):
+ &quot;Retrieves obj's dictionary and wraps it&quot;
+ if isinstance(obj,dict): # obj is a dictionary
+ dic=obj
+ else:
+ dic=getattr(obj,'__dict__',{}).copy() # avoids dictproxy objects
+ if not dic: dic=attributes(obj) # for simple objects
+ wrapped.namespace=getattr(obj,'__name__','')
+ for name,attr in dic.iteritems(): # modify dic
+ if condition(name,attr): dic[name]=wrapped(attr)
+ if not isinstance(obj,dict): # modify obj
+ customize(obj,err,**dic)
+
+#&lt;/oopp.py&gt;
+</pre>
+<pre class="literal-block">
+#&lt;tracingmethods.py&gt;
+
+from oopp import *
+
+class C(object):
+ &quot;Class with traced methods&quot;
+
+ def f(self): return self
+ f=tracedmethod(f)
+
+ g=staticmethod(lambda:None)
+ g=tracedmethod(g)
+
+ h=classmethod(do_nothing)
+ h=tracedmethod(h)
+
+c=C()
+
+#unbound calls
+C.f(c)
+C.g()
+C.h()
+
+#bound calls
+c.f()
+c.g()
+c.h()
+
+#&lt;/tracingmethods.py&gt;
+</pre>
+</blockquote>
+<p>Output:</p>
+<blockquote>
+<pre class="literal-block">
+[C] Calling 'f' with arguments
+(&lt;C object at 0x402042cc&gt;,){} ...
+-&gt; 'C.f' called with result: &lt;C object at 0x402042cc&gt;
+
+[C] Calling '&lt;lambda&gt;' with arguments
+(){} ...
+-&gt; 'C.&lt;lambda&gt;' called with result: None
+
+[C] Calling 'do_nothing' with arguments
+(&lt;class 'C'&gt;,){} ...
+-&gt; 'C.do_nothing' called with result: None
+
+[C] Calling 'f' with arguments
+(&lt;C object at 0x402042cc&gt;,){} ...
+-&gt; 'C.f' called with result: &lt;C object at 0x402042cc&gt;
+
+[C] Calling '&lt;lambda&gt;' with arguments
+(){} ...
+-&gt; 'C.&lt;lambda&gt;' called with result: None
+
+[C] Calling 'do_nothing' with arguments
+(&lt;class 'C'&gt;,){} ...
+-&gt; 'C.do_nothing' called with result: None
+</pre>
+</blockquote>
+<p>The approach in 'tracingmethods.py' works, but it is far from
+being elegant, since I had to explicitly wrap each method in the
+class by hand.</p>
+<p>Both problems can be avoided.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; wrap(Clock,tracedmethod)
+&gt;&gt;&gt; Clock.get_time()
+[Clock] Calling 'get_time' with arguments
+(){} ...
+-&gt; 'Clock.get_time' called with result: 21:56:52
+'21:56:52'
+</pre>
+</blockquote>
+</div>
+</div>
+<div class="section" id="the-subtleties-of-multiple-inheritance">
+<h1><a class="toc-backref" href="#id100" name="the-subtleties-of-multiple-inheritance">THE SUBTLETIES OF MULTIPLE INHERITANCE</a></h1>
+<p>In chapter 4 we introduced the concept of multiple inheritance and discussed
+its simplest applications in absence of name collisions. When with methods
+with different names are derived from different classes multiple inheritance
+is pretty trivial. However, all kind of subtilites comes in presence of name
+clashing, i.e. when we multiply inherits different methods defined in different
+classes but with the <em>same</em> name.
+In order to understand what happens in this situation, it is essential to
+understand the concept of Method Resolution Order (MRO). For reader's
+convenience, I collect in this chapter some of the information
+reported in <a class="reference" href="http://www.python.org/2.3/mro.html">http://www.python.org/2.3/mro.html</a>.</p>
+<div class="section" id="a-little-bit-of-history-why-python-2-3-has-changed-the-mro">
+<h2><a class="toc-backref" href="#id101" name="a-little-bit-of-history-why-python-2-3-has-changed-the-mro">A little bit of history: why Python 2.3 has changed the MRO</a></h2>
+<p>Everything started with a post by Samuele Pedroni to the Python
+development mailing list <a class="footnote-reference" href="#id36" id="id37" name="id37">[18]</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="#id40" id="id38" name="id38">[19]</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: don't be scared!</p>
+<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, 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="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>
+</div>
+<div class="section" id="the-c3-method-resolution-order">
+<h2><a class="toc-backref" href="#id102" name="the-c3-method-resolution-order">The C3 Method Resolution Order</a></h2>
+<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. In order to do that, we need the
+concept of <em>merging</em> lists, since the rule says that</p>
+<blockquote>
+<em>the linearization of C is the sum of C plus the merge of a) the
+linearizations of the parents and b) 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>How is the merge computed? The rule is the following:</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:</p>
+<ol class="arabic">
+<li><p class="first">C is the <tt class="literal"><span class="pre">object</span></tt> class, which has no parents; in this case its
+linearization coincides with itself,</p>
+<blockquote>
+<p>L[object] = object.</p>
+</blockquote>
+</li>
+<li><p class="first">C has only one parent (single inheritance); in this case</p>
+<blockquote>
+<p>L[C(B)] = C + merge(L[B],B) = C + L[B]</p>
+</blockquote>
+</li>
+</ol>
+<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>
+</div>
+<div class="section" id="examples">
+<h2><a class="toc-backref" href="#id103" name="examples">Examples</a></h2>
+<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 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</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>
+</div>
+<div class="section" id="bad-method-resolution-orders">
+<h2><a class="toc-backref" href="#id104" name="bad-method-resolution-orders">Bad Method Resolution Orders</a></h2>
+<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 #under Python 2.3 this is an error
+'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>
+<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="#id41" id="id39" name="id39">[20]</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 object, 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>
+<table class="footnote" frame="void" id="id40" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id38" name="id40">[19]</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="footnote" frame="void" id="id41" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id39" name="id41">[20]</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="footnote" frame="void" id="id42" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id44" name="id42">[21]</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>
+<table class="footnote" frame="void" id="id43" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id47" name="id43">[22]</a></td><td>The (in)famous book on metaclasses, <em>Putting Metaclasses to Work</em>:
+Ira R. Forman, Scott Danforth, Addison-Wesley 1999 (out of print,
+but probably still available on <a class="reference" href="http://www.amazon.com">http://www.amazon.com</a>)</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="understanding-the-method-resolution-order">
+<h2><a class="toc-backref" href="#id105" name="understanding-the-method-resolution-order">Understanding the Method Resolution Order</a></h2>
+<p>The MRO of any given (new style) Python class is given
+by the special attribute <tt class="literal"><span class="pre">__mro__</span></tt>. Notice that since
+Python is an extremely dynamic language it is possible
+to delete and to generate whole classes at run time, therefore the MRO
+is a dynamic concept. For instance, let me show how it is possibile to
+remove a class from my
+paleoanthropological hierarchy: for instance I can
+replace the last class 'HomoSapiensSapiens' with 'HomoSapiensNeardenthalensis'
+(changing a class in the middle of the hierarchy would be more difficult). The
+following lines do the job dynamically:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; del HomoSapiensSapiens
+&gt;&gt;&gt; class HomoSapiensNeardenthalensis(HomoSapiens):
+... def can(self):
+... super(self.__this,self).can()
+... print &quot; - make something&quot;
+&gt;&gt;&gt; reflective(HomoSapiensNeardenthalensis)
+&gt;&gt;&gt; HomoSapiensNeardenthalensis().can()
+HomoSapiensNeardenthalensis can:
+ - make tools
+ - make abstractions
+ - make something
+</pre>
+</blockquote>
+<p>In this case the MRO of 'HomoSapiensNeardenthalensis', i.e. the list of
+all its ancestors, is</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; HomoSapiensNeardenthalensis.__mro__
+[&lt;class '__main__.HomoSapiensNeardenthalensis'&gt;,&lt;class 'oopp.HomoSapiens'&gt;,
+ &lt;class 'oopp.HomoHabilis'&gt;, &lt;class 'oopp.Homo'&gt;,
+ &lt;class 'oopp.PrettyPrinted'&gt;, &lt;class 'oopp.object'&gt;]
+</pre>
+</blockquote>
+<p>The <tt class="literal"><span class="pre">__mro__</span></tt> attribute gives the <em>linearization</em> of the class, i.e. the
+ordered list of its ancestors, starting from the class itself and ending
+with object. The linearization of a class is essential in order to specify
+the resolution order of methods and attributes, i.e. the Method Resolution
+Order (MRO). In the case of single inheritance hierarchies, such the
+paleonthropological example, the MRO is pretty obvious; on the contrary
+it is a quite non-trivial concept in the case of multiple inheritance
+hierarchies.</p>
+<p>For instance, let me reconsider my first example of multiple inheritance,
+the <tt class="literal"><span class="pre">NonInstantiableClock</span></tt> class, inheriting from 'NonInstantiable' and
+'Clock'. I may represent the hierarchy with the following inheritance graph:</p>
+<blockquote>
+<pre class="literal-block">
+ -- object --
+ / (__new__) \
+ / \
+ / \
+ Clock NonInstantiable
+(get_time) (__new__)
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ NonInstantiableClock
+ (get_time,__new__)
+</pre>
+</blockquote>
+<p>The class <tt class="literal"><span class="pre">Clock</span></tt> define a <tt class="literal"><span class="pre">get_time</span></tt> method, whereas the class
+<tt class="literal"><span class="pre">NonInstantiable</span></tt> overrides the <tt class="literal"><span class="pre">__new__</span></tt> method of the <tt class="literal"><span class="pre">object</span></tt> class;
+the class <tt class="literal"><span class="pre">NonInstantiableClock</span></tt> inherits <tt class="literal"><span class="pre">get_time</span></tt> from 'Clock' and
+<tt class="literal"><span class="pre">__new__</span></tt> from 'NonInstantiable'.</p>
+<p>The linearization of 'NonInstantiableClock' is</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; NonInstantiableClock.mro()
+[&lt;class '__main__.NonInstantiableClock'&gt;, &lt;class 'oopp.Clock'&gt;,
+ &lt;class 'oopp.NonInstantiable'&gt;, &lt;type 'object'&gt;]
+</pre>
+</blockquote>
+<p>In particular, since 'NonInstantiable' precedes 'object', its <tt class="literal"><span class="pre">__new__</span></tt>
+method overrides the <tt class="literal"><span class="pre">object</span></tt> new method. However, with the MRO used before
+Python 2.2, the linearization would have been <tt class="literal"><span class="pre">NonInstantiableClock,</span> <span class="pre">Clock,</span>
+<span class="pre">object,</span> <span class="pre">NonInstantiable,</span> <span class="pre">object</span></tt> and the <tt class="literal"><span class="pre">__new__</span></tt> method of object would
+have (hypothetically, of course, since before Python 2.2 there was not
+<tt class="literal"><span class="pre">__new__</span></tt> method! ;-) overridden the <tt class="literal"><span class="pre">__new__</span></tt>
+method of <tt class="literal"><span class="pre">NonInstantiable</span></tt>, therefore <tt class="literal"><span class="pre">NonInstantiableClock</span></tt> would
+have lost the property of being non-instantiable!</p>
+<p>This simple example shows that the choice of a correct Method Resolution
+Order is far from being obvious in general multiple inheritance hierarchies.
+After a false start in Python 2.2, (with a MRO failing in some subtle cases)
+Python 2.3 decided to adopt the so-called C3 MRO, invented by people working
+on Dylan (even if Dylan itself uses the MRO of Common Lisp CLOS). Since this
+is quite a technical matter, I defer the interested reader to appendix 2
+for a full discussion of the C3 algorithm.</p>
+<p>Here, I prefer to point out how the built-in
+<tt class="literal"><span class="pre">super</span></tt> object works in multiple inheritance situations. To this aim, it
+is convenient to define an utility function that retrieves the ancestors
+of a given class with respect to the MRO of one of its subclasses:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def ancestor(C,S=None):
+ &quot;&quot;&quot;Returns the ancestors of the first argument with respect to the
+ MRO of the second argument. If the second argument is None, then
+ returns the MRO of the first argument.&quot;&quot;&quot;
+ if C is object:
+ raise TypeError(&quot;There is no superclass of object&quot;)
+ elif S is None or S is C:
+ return list(C.__mro__)
+ elif issubclass(S,C): # typical case
+ mro=list(S.__mro__)
+ return mro[mro.index(C):] # compute the ancestors from the MRO of S
+ else:
+ raise TypeError(&quot;S must be a subclass of C&quot;)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Let me show how the function <tt class="literal"><span class="pre">ancestor</span></tt> works.
+Consider the class <tt class="literal"><span class="pre">Clock</span></tt> in isolation: then
+its direct superclass, i.e. the first ancestor, is <tt class="literal"><span class="pre">object</span></tt>,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; ancestor(Clock)[1]
+&lt;type 'object'&gt;
+</pre>
+</blockquote>
+<p>therefore <tt class="literal"><span class="pre">super(Clock).__new__</span></tt> retrieves the <tt class="literal"><span class="pre">object.__new__</span></tt> method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(Clock).__new__
+&lt;built-in method __new__ of type object at 0x80e6fc0&gt;
+</pre>
+</blockquote>
+<p>Consider now the <tt class="literal"><span class="pre">Clock</span></tt> class together with its subclass
+<tt class="literal"><span class="pre">NonInstantiableClock</span></tt>:
+in this case the first ancestor of <tt class="literal"><span class="pre">Clock</span></tt>, <em>with respect to the MRO of
+'NonInstantiableClock'</em> is <tt class="literal"><span class="pre">NonInstantiable</span></tt></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ancestor(Clock,NonInstantiableClock)[1]
+&lt;class 'oopp.NonInstantiable'&gt;
+</pre>
+</blockquote>
+<p>Therefore <tt class="literal"><span class="pre">super(Clock,NonInstantiableClock).__new__</span></tt> retrieves the
+<tt class="literal"><span class="pre">NonInstantiable.__new__</span></tt> method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(Clock,NonInstantiableClock).__new__
+&lt;function __new__ at 0x81b293c&gt;
+&gt;&gt;&gt; NonInstantiable.__new__
+&lt;function __new__ at 0x81b293c&gt;
+</pre>
+</blockquote>
+<p>It must be pointed out that <tt class="literal"><span class="pre">super(C,S)</span></tt> is equivalent but not the same
+than <tt class="literal"><span class="pre">ancestor(C,S)[1]</span></tt>, since it does not return the superclass:
+it returns a super object, instead:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(Clock,NonInstantiableClock)
+&lt;super: &lt;class 'Clock'&gt;, &lt;type object&gt;&gt;
+</pre>
+<p>#&lt;oopp.py&gt;</p>
+<p>#class Super(super):
+# def __init__(self,C,S=None):
+# super(Super,self).__init__(C,S)
+# self.__name__=&quot;Super(%s)&quot; % C.__name__</p>
+<p>#&lt;/oopp.py&gt;</p>
+</blockquote>
+<p>Finally, there is little quirk of super:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(PrettyPrinted): pass
+&gt;&gt;&gt; s=super(C,C())
+&gt;&gt;&gt; s.__str__()
+</pre>
+</blockquote>
+<p>but</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; str(s) # idem for print s
+&quot;&lt;super: &lt;class 'C'&gt;, &lt;C object&gt;&gt;&quot;
+</pre>
+</blockquote>
+<p>Idem for non-pre-existing methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(list): pass
+...
+&gt;&gt;&gt; s=super(D,D())
+&gt;&gt;&gt; s.__len__()
+0
+&gt;&gt;&gt; len(s) #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: len() of unsized object
+</pre>
+</blockquote>
+<p>The same problem comes with <tt class="literal"><span class="pre">__getattr__</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(object):
+... def __getattr__(self,name):
+... if name=='__len__': return lambda:0
+...
+&gt;&gt;&gt; e=E()
+&gt;&gt;&gt; e.__len__()
+0
+&gt;&gt;&gt; len(e) # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: len() of unsized object
+</pre>
+</blockquote>
+</div>
+<div class="section" id="counting-instances">
+<h2><a class="toc-backref" href="#id106" name="counting-instances">Counting instances</a></h2>
+<blockquote>
+<pre class="line-block">
+<em>Everything should be built top-down, except the first time.</em>
+-- Alan Perlis
+</pre>
+</blockquote>
+<p>Multiple inheritance adds a step further to the bottom-up philosophy and
+it makes appealing the idea of creating classes with the only
+purpose of being derived. Whereas in the top-down approach one starts
+with full featured standalone classes, to be further refined, in the
+mix-in approach one starts with bare bone classes, providing very simple
+or even trivial features, with the purpose of providing
+basic reusable components in multiple inheritance hierarchies.
+At the very end, the idea is to generate a library of <em>mixin</em> classes, to be
+composed with other classes. We already saw a couple of examples of
+mixin classes: 'NonInstantiable' and 'Customizable'. In this paragraph
+I will show three other examples: 'WithCounter','Singleton' and
+'AvoidDuplication'.</p>
+<p>A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+<tt class="literal"><span class="pre">__init__</span></tt> method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting feature in a separate mix-in class: then the feature can be
+added to the original class via multiple inheritance, without touching
+the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+<tt class="literal"><span class="pre">__new__</span></tt> method of the counter class must me cooperative, and preferably
+via an anonymous super call.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class WithCounter(object):
+ &quot;&quot;&quot;Mixin class counting the total number of its instances and storing
+ it in the class attribute counter.&quot;&quot;&quot;
+
+ counter=0 # class attribute (or static attribute in C++/Java terms)
+
+ def __new__(cls,*args,**kw):
+ cls.counter+=1 # increments the class attribute
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ #anonymous cooperative call to the superclass's method __new__
+
+reflective(WithCounter)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Each time an instance of 'WithCounter' is initialized, the counter 'count' is
+incremented and when 'WithCounter' is composed trough multiple inheritance,
+its '__new__' method cooperatively invokes the <tt class="literal"><span class="pre">__new__</span></tt> method
+of the other components.</p>
+<p>For instance, I can use 'WithCounter' to implement a 'Singleton', i.e.
+a class that can have only one instance. This kind of classes can be
+obtained as follows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Singleton(WithCounter):
+ &quot;If you inherit from me, you can only have one instance&quot;
+ def __new__(cls,*args,**kw):
+ if cls.counter==0: #first call
+ cls.instance=super(cls.__this,cls).__new__(cls,*args,**kw)
+ return cls.instance
+
+reflective(Singleton)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>As an application, I can create a
+class <tt class="literal"><span class="pre">SingleClock</span></tt> that inherits from <tt class="literal"><span class="pre">Clock</span></tt>
+<em>and</em> from <tt class="literal"><span class="pre">Singleton</span></tt>. This means that <tt class="literal"><span class="pre">SingleClock</span></tt> is both a
+'Clock' and a 'Singleton', i.e. there can be only a clock:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Clock,Singleton
+&gt;&gt;&gt; class SingleClock(Clock,Singleton): pass
+...
+&gt;&gt;&gt; clock1=SingleClock()
+&gt;&gt;&gt; clock2=SingleClock()
+&gt;&gt;&gt; clock1 is clock2
+True
+</pre>
+</blockquote>
+<p>Instantiating many clocks is apparently possible (i.e. no error
+message is given) but you always obtain the same instance. This makes
+sense, since there is only one time on the system and a single
+clock is enough.</p>
+<p>A variation of the 'Singleton' is a class that generates a new
+instance only when a certain condition is satisfied. Suppose for instance
+one has a 'Disk' class, to be instantiated with the syntax
+<tt class="literal"><span class="pre">Disk(xpos,ypos,radius)</span></tt>.
+It is clear that two disks with the same radius and the same position in
+the cartesian plane, are essentially the same disk (assuming there are no
+additional attributes such as the color). Therefore it is a vaste of memory
+to instantiate two separate objects to describe the same disk. To solve
+this problem, one possibility is to store in a list the calling arguments.
+When it is time to instanciate a new objects with arguments args = xpos,ypos,
+radius, Python should check if a disk with these arguments has already
+been instanciated: in this case that disk should be returned, not a new
+one. This logic can be elegantly implemented in a mix-in class such as the
+following (compare with the <tt class="literal"><span class="pre">withmemory</span></tt> wrapper in chapter 2):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class AvoidDuplication(object):
+ def __new__(cls,*args,**kw):
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ __new__=withmemory(__new__) # collects the calls in __new__.result
+
+reflective(AvoidDuplication)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice that 'AvoidDuplication' is introduced with the only purpose of
+giving its functionality to 'Disk': in order to reach this goal, it is enough
+to derive 'Disk' from this class and our previously
+introduced 'GeometricFigure' class by writing something like</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class Disk(GeometricFigure,AvoidDuplication):
+... def __init__(self,xpos,ypos,radius):
+... return super(Disk,self).__init__('(x-x0)**2+(y-y0)**2 &lt;= r**2',
+... x0=xpos,y0=ypos,r=radius)
+</pre>
+</blockquote>
+<p>Now, if we create a disk</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c1=Disk(0,0,10) #creates a disk of radius 10
+</pre>
+</blockquote>
+<p>it is easy enough to check that trying to instantiate a new disk with the
+<em>same</em> arguments return the old disk:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c2=Disk(0,0,10) #returns the *same* old disk
+&gt;&gt;&gt; c1 is c2
+True
+</pre>
+</blockquote>
+<p>Here, everything works, because through the
+cooperative <tt class="literal"><span class="pre">super</span></tt> mechanism, <tt class="literal"><span class="pre">Disk.__init__</span></tt> calls
+<tt class="literal"><span class="pre">AvoidDuplication.__init__</span></tt> that calls <tt class="literal"><span class="pre">GeometricFigure.__init__</span></tt>
+that in turns initialize the disk. Inverting the order of
+'AvoidDuplication' and 'GeometricFigure' would case a disaster, since
+<tt class="literal"><span class="pre">GeometricFigure.__init__</span></tt> would override <tt class="literal"><span class="pre">AvoidDuplication.__init__</span></tt>.</p>
+<p>Alternatively, one could use the object factory 'Makeobj' implemented in
+chapter 3:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class NonDuplicatedFigure(GeometricFigure,AvoidDuplication): pass
+&gt;&gt;&gt; makedisk=Makeobj(NonDuplicatedFigure,'(x-x0)**2/4+(y-y0)**2 &lt;= r**2')
+&gt;&gt;&gt; disk1=makedisk(x0=38,y0=7,r=5)
+&gt;&gt;&gt; disk2=makedisk(x0=38,y0=7,r=5)
+&gt;&gt;&gt; disk1 is disk2
+True
+</pre>
+</blockquote>
+<p>Remark: it is interesting to notice that the previous approach would not work
+for keyword arguments, directly, since dictionary are unhashable.</p>
+</div>
+<div class="section" id="the-pizza-shop-example">
+<h2><a class="toc-backref" href="#id107" name="the-pizza-shop-example">The pizza-shop example</a></h2>
+<p>Now it is time to give a non-trivial example of multiple inheritance with
+cooperative and non-cooperative classes. The point is that multiple
+inheritance can easily leads to complicated hierarchies: where the
+resolution order of methods is far from being obvious and actually
+can give bad surprises.</p>
+<p>To explain the issue, let me extend the program for the pizza-shop owner of
+chapter 4, by following the bottom-up approach and using anonymous
+cooperative super calls.
+In this approach, one starts from the simplest thing.
+It is clear that the pizza-shop owner has interest in recording all the
+pizzas he sell.
+To this aim, he needs a class providing logging capabilities:
+each time a new instance is created, its features are stored in a log file. In
+order to count the total number of instances, 'WithLogger' must derive from
+the 'WithCounter' class. In order to have a nicely printed message,
+'WithLogger' must derive from 'PrettyPrinted'. Finally,
+since 'WithLogger' must be a general purpose
+class that I will reuse in other problem as a mixin class, it must be
+cooperative. 'WithLogger' can be implemented as follows:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class WithLogger(WithCounter,PrettyPrinted):
+ &quot;&quot;&quot;WithLogger inherits from WithCounter the 'count' class attribute;
+ moreover it inherits '__str__' from PrettyPrinted&quot;&quot;&quot;
+ logfile=sys.stdout #default
+ verboselog=False #default
+ def __init__(self,*args,**kw):
+ super(self.__this,self).__init__(*args,**kw) # cooperative
+ dic=attributes(self) # non-special attributes dictionary
+ print &gt;&gt; self.logfile,'*'*77
+ print &gt;&gt; self.logfile, time.asctime()
+ print &gt;&gt; self.logfile, &quot;%s. Created %s&quot; % (type(self).counter,self)
+ if self.verboselog:
+ print &gt;&gt; self.logfile,&quot;with accessibile non-special attributes:&quot;
+ if not dic: print &gt;&gt; self.logfile,&quot;&lt;NOTHING&gt;&quot;,
+ else: print &gt;&gt; self.logfile, pretty(dic)
+
+reflective(WithLogger)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here I could well use <tt class="literal"><span class="pre">super(self.__this,self).__init__(*args,**kw)</span></tt>
+instead of <tt class="literal"><span class="pre">super(self.__this,self).__init__(*args,**kw)</span></tt>, nevertheless
+the standard <tt class="literal"><span class="pre">super</span></tt> works in this case and I can use it with better
+performances.
+Thanks to the power of multiple inheritance, we may give logging features
+to the 'CustomizablePizza' class defined in chapter 4
+with just one line of code:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class Pizza(WithLogger,CustomizablePizza):
+... &quot;Notice, WithLogger is before CustomizablePizza&quot;
+&gt;&gt;&gt; Pizza.With(toppinglist=['tomato'])('small')
+****************************************************************************
+Sat Feb 22 14:54:44 2003
+1. Created &lt;Pizza&gt;
+&lt;__main__.Pizza object at 0x816927c&gt;
+</pre>
+</blockquote>
+<p>It is also possible to have a more verbose output:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Pizza.With(verboselog=True)
+&lt;class '__main__.Pizza'&gt;
+&gt;&gt;&gt; Pizza('large')
+****************************************************************************
+Sat Feb 22 14:59:51 2003
+1. Created &lt;Pizza&gt;
+with accessibile non-special attributes:
+With = &lt;bound method type.customized of &lt;class '__main__.Pizza'&gt;&gt;
+baseprice = 1
+count = 2
+formatstring = %s
+logfile = &lt;open file '&lt;stdout&gt;', mode 'w' at 0x402c2058&gt;
+price = &lt;bound method Pizza.price of &lt;__main__.Pizza object at 0x402f6c8c&gt;&gt;
+size = large
+sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+topping_unit_price = 0.5
+toppinglist = ['tomato']
+toppings_price = &lt;bound method Pizza.toppings_price of
+ &lt;__main__.Pizza object at 0x402f6c8c&gt;&gt;
+verboselog = True
+with = &lt;bound method Pizza.customized of
+&lt;__main__.Pizza object at 0x402f6c8c&gt;&gt;
+&lt;__main__.Pizza object at 0x401ce7ac&gt;
+</pre>
+</blockquote>
+<p>However, there is a problem here, since the output is '&lt;Pizza&gt;' and
+not the nice 'large pizza with tomato, cost $ 4.5' that we would
+expect from a child of 'CustomizablePizza'. The solution to the
+puzzle is given by the MRO:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Pizza.mro()
+[&lt;class '__main__.Pizza'&gt;, &lt;class 'oopp.WithLogger'&gt;,
+ &lt;class 'oopp.WithCounter'&gt;, &lt;class 'oopp.PrettyPrinted'&gt;,
+ &lt;class 'oopp.CustomizablePizza'&gt;, &lt;class 'oopp.GenericPizza'&gt;,
+ &lt;class 'oopp.Customizable'&gt;, &lt;type 'object'&gt;]
+</pre>
+</blockquote>
+<p>The inheritance graph is rather complicated:</p>
+<blockquote>
+<pre class="literal-block">
+ object 7
+
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+2 WithCounter PrettyPrinted 3 GenericPizza 5 Customizable 6
+ (__new__) (__str__,__init__) (__str__) /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / CustomizablePizza 4
+ \ / /
+ 1 WithLogger /
+ (__init__) /
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+
+ Pizza O
+</pre>
+</blockquote>
+<p>As we see, the precedence in the resolution of methods is far from being
+trivial. It is denoted in the graph with numbers
+from 0 to 7: first the methods of 'Pizza' (level 0), then the methods of
+'WithLogger' (level 1), then the methods of 'WithCounter' (level 2), then
+the methods of 'PrettyPrinted' (level 3), then the methods of
+'CustomizablePizza' (level 4), then the methods of 'GenericPizza' (level 5),
+then the level of 'Customizable' (level 6), finally the 'object' methods
+(level 7).</p>
+<p>The reason why the MRO is so, can be understood by studying
+appendix 1.</p>
+<p>We see that the <tt class="literal"><span class="pre">__init__</span></tt> methods of 'WithLogger' and
+the <tt class="literal"><span class="pre">__new__</span></tt> method of 'WithCounter' are cooperative.
+<tt class="literal"><span class="pre">WithLogger.__init__</span></tt>
+calls <tt class="literal"><span class="pre">WithCounter.__init__</span></tt> that is
+inherited from <tt class="literal"><span class="pre">CustomizablePizza.__init__</span></tt> which is not cooperative,
+but this is not dangerous since <tt class="literal"><span class="pre">CustomizablePizza.__init__</span></tt> does not need
+to call any other <tt class="literal"><span class="pre">__init__</span></tt>.</p>
+<p>However, <tt class="literal"><span class="pre">PrettyPrinted.__str__</span></tt> and <tt class="literal"><span class="pre">GenericPizza.__str__</span></tt> are not
+cooperative and since 'PrettyPrinted' precedes 'GenericPizza', the
+<tt class="literal"><span class="pre">GenericPizza.__str__</span></tt> method is overridden, which is bad.</p>
+<p>If <tt class="literal"><span class="pre">WithLogger.__init__</span></tt> and <tt class="literal"><span class="pre">WithCounter.__new__</span></tt> were not
+cooperative, they would therefore badly breaking the program.</p>
+<p>The message is: when you inherit from both cooperative and non-cooperative
+classes, put cooperative classes first. The will be fair and will not
+blindly override methods of the non-cooperative classes.</p>
+<p>With multiple inheritance you can reuse old code a lot,
+however the price to pay, is to have a non-trivial hierarchy. If from
+the beginning we knew that 'Pizza' was needing a 'WithLogger',
+a 'WithCounter' and the
+ability to be 'Customizable' we could have put everything in an unique
+class. The problem is that in real life one never knows ;)
+Fortunately, Python dynamism allows to correct design mistakes</p>
+<p>Remark: in all text books about inheritance, the authors always stress
+that inheritance should be used as a &quot;is-a&quot; relation, not
+and &quot;has-a&quot; relation. In spite of this fact, I have decided to implement
+the concept of having a logger (or a counter) via a mixin class. One
+should not blindly believe text books ;)</p>
+</div>
+<div class="section" id="fixing-wrong-hierarchies">
+<h2><a class="toc-backref" href="#id108" name="fixing-wrong-hierarchies">Fixing wrong hierarchies</a></h2>
+<p>A typical metaprogramming technique, is the run-time modification of classes.
+As I said in a previous chapter, this feature can confuse the programmer and
+should not be abused (in particular it should not be used as a replacement
+of inheritance!); nevertheless, there applications where the ability of
+modifying classes at run time is invaluable: for instance,
+it can be used to correct design mistakes.</p>
+<p>In this case we would like the <tt class="literal"><span class="pre">__str__</span> <span class="pre">method</span></tt> of 'PrettyPrinted' to be
+overridden by <tt class="literal"><span class="pre">GenericPizza.__str__</span></tt>. Naively, this can be solved by
+putting 'WithLogger' after 'GenericPizza'. Unfortunately, doing so
+would cause <tt class="literal"><span class="pre">GenericPizza.__init__</span></tt> to override <tt class="literal"><span class="pre">WithLogger.__init__</span></tt>,
+therefore by loosing logging capabilitiesr, unless countermeasures
+are taken.</p>
+<p>A valid countermeasure could be to replace the non-cooperative
+<tt class="literal"><span class="pre">GenericPizza.__init__</span></tt> with a cooperative one. This can miraculously
+done at run time in few lines of code:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def coop_init(self,size): # cooperative __init__ for GenericPizza
+ self.size=size
+ super(self._GenericPizza__this,self).__init__(size)
+
+GenericPizza.__init__=coop_init # replace the old __init__
+
+reflective(GenericPizza) # define GenericPizza.__this
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice the usage of the fully qualified private attribute
+<tt class="literal"><span class="pre">self._GenericPizza__this</span></tt> inside <tt class="literal"><span class="pre">coop_init</span></tt>: since this function
+is defined outside any class, the automatica mangling mechanism cannot
+work and has to be implemented by hand. Notice also that
+<tt class="literal"><span class="pre">super(self._GenericPizza__this,self)</span></tt> could be replaced by
+<tt class="literal"><span class="pre">super(GenericPizza,self)</span></tt>; however the simpler approach is
+less safe against possible future manipulations of the hierarchy.
+Suppose, for example, we want to create a copy of the hierarchy
+with the same name but slightly different features (actually,
+in chapter 8 we will implement a traced copy of the pizza hierarchy,
+useful for debugging purposes): then, using <tt class="literal"><span class="pre">super(GenericPizza,self)</span></tt>
+would raise an error, since self would be an instance of the traced
+hierarchy and <tt class="literal"><span class="pre">GenericPizza</span></tt> the original nontraced class. Using
+the form <tt class="literal"><span class="pre">super(self._GenericPizza__this,self)</span></tt> and making
+<tt class="literal"><span class="pre">self._GenericPizza__this</span></tt> pointing to the traced 'GenericPizza'
+class (actually this will happen automatically) the problems goes
+away.</p>
+<p>Now everything works if 'WithLogger' is put after 'CustomizablePizza'</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class PizzaWithLog(CustomizablePizza,WithLogger): pass
+&gt;&gt;&gt; PizzaWithLog.With(toppinglist=['tomato'])('large')
+****************************************************************************
+Sun Apr 13 16:19:12 2003
+1. Created large pizza with tomato, cost $ 4.5
+&lt;class '__main__.PizzaWithLog'&gt;
+</pre>
+</blockquote>
+<p>The log correctly says <tt class="literal"><span class="pre">Created</span> <span class="pre">large</span> <span class="pre">pizza</span> <span class="pre">with</span> <span class="pre">tomato,</span> <span class="pre">cost</span> <span class="pre">$</span> <span class="pre">4.5</span></tt> and not
+<tt class="literal"><span class="pre">Created</span> <span class="pre">&lt;Pizza&gt;</span></tt> as before since now <tt class="literal"><span class="pre">GenericPizza.__str__</span></tt>
+overrides <tt class="literal"><span class="pre">PrettyPrinted.__str__</span></tt>. Moreover, the hierarchy is logically
+better organized:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; PizzaWithLog.mro()
+[&lt;class '__main__.PizzaWithLog'&gt;, &lt;class 'oopp.CustomizablePizza'&gt;,
+&lt;class 'oopp.GenericPizza'&gt;, &lt;class 'oopp.Customizable'&gt;,
+&lt;class 'oopp.WithLogger'&gt;, &lt;class 'oopp.WithCounter'&gt;,
+&lt;class 'oopp.PrettyPrinted'&gt;, &lt;type 'object'&gt;]
+</pre>
+</blockquote>
+<p>I leave as an exercise for the reader to make the <tt class="literal"><span class="pre">__str__</span></tt> methods
+cooperative ;)</p>
+<p>Obviously, in this example it would have been better to correct the
+original hierarchy, by leaving 'Beautiful' instantiable from the beginning
+(that's why I said the 'Beautiful' is an example of wrong mix-in class):
+nevertheless, sometimes, one has do to with wrong hierarchies written by
+others, and it can be a pain to fix them, both directly by modifying the
+original source code, and indirectly
+by inheritance, since one must change all the names, in order to distinghish
+the original classes from the fixed ones. In those cases Python
+dynamism can save your life. This also allows you enhance original
+classes which are not wrong, but that simply don't do something you want
+to implement.</p>
+<p>Modifying classes at run-time can be trivial, as in the examples I have
+shown here, but can also be rather tricky, as in this example</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import PrettyPrinted
+&gt;&gt;&gt; class PrettyPrintedWouldBe(object): __str__ = PrettyPrinted.__str__
+&gt;&gt;&gt; print PrettyPrintedWouldBe() #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: unbound method __str__() must be called with PrettyPrinted
+instance as first argument (got nothing instead)
+</pre>
+</blockquote>
+<p>As the error message says, the problem here, is that the
+<tt class="literal"><span class="pre">PrettyPrinted.__str__</span></tt> unbound method, has not received any argument.
+This is because in this
+form <tt class="literal"><span class="pre">PrettyPrintedWouldBe.__str__</span></tt> has been defined as an attribute,
+not as a real method. The solution is to write</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class PrettyPrintedWouldBe(object):
+... __str__ = PrettyPrinted.__dict__['__str__']
+...
+&gt;&gt;&gt; print PrettyPrintedWouldBe() # now it works
+&lt;PrettyPrintedWouldBe&gt;
+</pre>
+</blockquote>
+<p>This kind of run-time modifications does not work when private variables
+are involved:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;changewithprivate.py&gt;
+
+class C(object):
+ __x='C.__init__'
+ def __init__(self):
+ print self.__x # okay
+
+class D(object):
+ __x='D.__init__'
+ __init__=C.__dict__['__init__'] # error
+
+class New:
+ class C(object):
+ __x='New.C.__init__'
+ __init__=C.__dict__['__init__'] # okay
+
+C()
+try: D()
+except AttributeError,e: print e
+
+#&lt;/changewithprivate.py&gt;
+</pre>
+</blockquote>
+<p>Gives as result</p>
+<blockquote>
+<pre class="literal-block">
+C.__init__
+'D' object has no attribute '_C__x'
+New.C.__init__
+</pre>
+</blockquote>
+<p>The problem is that when <tt class="literal"><span class="pre">C.__dict__['__init__']</span></tt> is compiled
+(to byte-code) <tt class="literal"><span class="pre">self.__x</span></tt> is expanded to <tt class="literal"><span class="pre">self._C__x</span></tt>. However,
+when one invokes <tt class="literal"><span class="pre">D.__init__</span></tt>, a D-object is passed, which has
+a <tt class="literal"><span class="pre">self._D__x</span></tt> attribute, but not a <tt class="literal"><span class="pre">self._C__x</span></tt> attribute (unless
+'D' is a subclass of 'C'. Fortunately, Python wisdom</p>
+<blockquote>
+<em>Namespaces are one honking great idea -- let's do more of those!</em></blockquote>
+<p>suggests the right solution: to use a new class with the <em>same name</em>
+of the old one, but in a different namespace, in order to avoid
+confusion. The simplest way to generate a new namespace is to
+declare a new class (the class 'New' in this example): then 'New.C'
+becomes an inner class of 'New'. Since it has the same name of the
+original class, private variables are correctly expanded and one
+can freely exchange methods from 'C' to 'New.C' (and viceversa, too).</p>
+</div>
+<div class="section" id="modifying-hierarchies">
+<h2><a class="toc-backref" href="#id109" name="modifying-hierarchies">Modifying hierarchies</a></h2>
+<blockquote>
+<pre class="literal-block">
+def mod(cls): return cls
+
+class New: pass
+
+for c in HomoSapiensSapiens.__mro__:
+ setattr(New,c.__name__,mod(c))
+</pre>
+</blockquote>
+</div>
+<div class="section" id="inspecting-python-code">
+<h2><a class="toc-backref" href="#id110" name="inspecting-python-code">Inspecting Python code</a></h2>
+<p>how to inspect a class, by retrieving useful informations about its
+information.</p>
+<p>A first possibility is to use the standard <tt class="literal"><span class="pre">help</span></tt> function.
+The problem of this approach is that <tt class="literal"><span class="pre">help</span></tt> gives too much
+information.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+#plaindata=
+plainmethod=lambda m:m #identity function
+
+class Get(object):
+ &quot;&quot;&quot;Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod,
+ property, plainmethod, plaindata, returns the corresponding
+ attributes as a keyword dictionary. It works by internally calling
+ the routine inspect.classify_class_attrs. Notice that data
+ attributes with double underscores are not retrieved
+ (this is by design).&quot;&quot;&quot;
+ def __init__(self,cls):
+ self.staticmethods=kwdict()
+ self.classmethods=kwdict()
+ self.properties=kwdict()
+ self.methods=kwdict()
+ self.data=kwdict()
+ for name, kind, klass, attr in inspect.classify_class_attrs(cls):
+ if kind=='static method':
+ self.staticmethods[name]=attr
+ elif kind=='class method':
+ self.classmethods[name]=attr
+ elif kind=='property':
+ self.properties[name]=attr
+ elif kind=='method':
+ self.methods[name]=attr
+ elif kind=='data':
+ if not special(name): self.data[name]=attr
+ def __call__(self,descr): #could be done with a dict
+ if descr==staticmethod: return self.staticmethods
+ elif descr==classmethod: return self.classmethods
+ elif descr==property: return self.properties
+ elif descr==plainmethod: return self.methods
+ elif descr==plaindata: return self.data
+ else: raise SystemExit(&quot;Invalid descriptor&quot;)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>With similar tricks one can automatically recognize cooperative methods:
+#it is different, (better NOT to use descriptors)</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+#class Cooperative(Class):
+# __metaclass__ = WithWrappingCapabilities
+#
+# def cooperative(method):
+# &quot;&quot;&quot;Calls both the superclass method and the class
+# method (if the class has an explicit method).
+# Works for methods returning None.&quot;&quot;&quot;
+# name,cls=Cooperative.parameters # fixed by the meta-metaclass
+# def _(*args,**kw):
+# getattr(super(cls,args[0]),name)(*args[1:],**kw)
+# if method: method(*args,**kw) # call it
+# return _
+#
+# cooperative=staticmethod(cooperative)
+
+
+#&lt;/oopp.py&gt;
+</pre>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def wrapH(cls):
+ for c in cls.__mro__[:-2]:
+ tracer.namespace=c.__name__
+ new=vars(c).get('__new__',None)
+ if new: c.__new__=tracedmethod(new)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+</div>
+</div>
+<div class="section" id="the-magic-of-metaclasses-part-i">
+<h1><a class="toc-backref" href="#id111" name="the-magic-of-metaclasses-part-i">THE MAGIC OF METACLASSES - PART I</a></h1>
+<blockquote>
+<pre class="line-block">
+<em>Metaclasses are deeper magic than 99% of users should ever
+worry about. If you wonder whether you need them, you don't
+(the people who actually need them know with certainty that
+they need them, and don't need an explanation about why).</em>
+--Tim Peters
+</pre>
+</blockquote>
+<p>Python always had metaclasses, since they are inherent to its object
+model. However, before Python 2.2, metaclasses where tricky and their
+study could cause the programmer's brain to explode <a class="footnote-reference" href="#id42" id="id44" name="id44">[21]</a>. Nowadays,
+the situation has changed, and the reader should be able to understand
+this chapter without risk for his/her brain (however I do not give any
+warranty ;)</p>
+<p>Put it shortly, metaclasses give to the Python programmer
+complete control on the creation of classes. This simple statement
+has far reaching consequences, since the ability of interfering with
+the process of class creation, enable the programmer to make miracles.</p>
+<p>In this and in the following chapters, I will show some of these
+miracles.</p>
+<p>This chapter will focus on subtle problems of metaclasses in inheritance
+and multiple inheritance, including multiple inheritance of metaclasses
+with classes and metaclasses with metaclasses.</p>
+<p>The next chapter will focus more on applications.</p>
+<table class="footnote" frame="void" id="id45" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a name="id45">[23]</a></td><td>Metaclasses in Python 1.5 [A.k.a the killer joke]
+<a class="reference" href="http://www.python.org/doc/essays/metaclasses/">http://www.python.org/doc/essays/metaclasses/</a></td></tr>
+</tbody>
+</table>
+<p>There is very little documentation about metaclasses, except Guido's
+essays and the papers by David Mertz and myself published in IBMdeveloperWorks</p>
+<blockquote>
+<a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta.html">http://www-106.ibm.com/developerworks/library/l-pymeta.html</a></blockquote>
+<div class="section" id="metaclasses-as-class-factories">
+<h2><a class="toc-backref" href="#id112" name="metaclasses-as-class-factories">Metaclasses as class factories</a></h2>
+<p>In the Python object model (inspired from the Smalltalk, that had metaclasses
+a quarter of century ago!) classes themselves are objects.
+Now, since objects are instances of classes, that means that classes
+themselves can be seen as instances of special classes called <em>metaclasses</em>.
+Notice that things get hairy soon, since by following this idea, one could
+say the metaclasses themselves are classes and therefore objects; that
+would mean than even metaclasses can be seen as
+instances of special classes called meta-metaclasses. On the other hand,
+meta-meta-classes can be seen as instances of meta-meta-metaclasses,
+etc. Now, it should be obvious why metaclasses have gained such a
+reputation of brain-exploders ;). However, fortunately, the situation
+is not so bad in practice, since the infinite recursion of metaclasses is
+avoided because there is a metaclass that is the &quot;mother of all metaclasses&quot;:
+the built-in metaclass <em>type</em>. 'type' has the property of being its own
+metaclass, therefore the recursion stops. Consider for instance the following
+example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): pass # a generic class
+&gt;&gt;&gt; type(C) #gives the metaclass of C
+&lt;type 'type'&gt;
+&gt;&gt;&gt; type(type(C)) #gives the metaclass of type
+&lt;type 'type'&gt;
+</pre>
+</blockquote>
+<p>The recursion stops, since the metaclass of 'type' is 'type'.
+One cool consequence of classes being instances of 'type',
+is that since <em>type</em> is a subclass of object,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; issubclass(type,object)
+True
+</pre>
+</blockquote>
+<p>any Python class is not only a subclass of <tt class="literal"><span class="pre">object</span></tt>, but also
+an instance of 'object':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(C,type)
+True
+&gt;&gt;&gt; isinstance(C,object)
+True
+&gt;&gt;&gt; issubclass(C,object)
+True
+</pre>
+</blockquote>
+<p>Notice that 'type' is an instance of itself (!) and therefore of 'object':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(type,type) # 'type' is an instance of 'type'
+True
+&gt;&gt;&gt; isinstance(type,object) # therefore 'type' is an instance of 'object'
+True
+</pre>
+</blockquote>
+<p>As it is well known, <tt class="literal"><span class="pre">type(X)</span></tt> returns the type of <tt class="literal"><span class="pre">X</span></tt>; however,
+<tt class="literal"><span class="pre">type</span></tt> has also a second form in which it acts as a class factory.
+The form is <tt class="literal"><span class="pre">type(name,bases,dic)</span></tt> where <tt class="literal"><span class="pre">name</span></tt> is the name of
+the new class to be created, bases is the tuple of its bases and dic
+is the class dictionary. Let me give a few examples:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C=type('C',(),{})
+&gt;&gt;&gt; C
+&lt;class '__main__.C'&gt;
+&gt;&gt;&gt; C.__name__
+'C'
+&gt;&gt;&gt; C.__bases__
+(&lt;type 'object'&gt;,)
+&gt;&gt;&gt; C.__dict__
+&lt;dict-proxy object at 0x8109054&gt;
+</pre>
+</blockquote>
+<p>Notice that since all metaclasses inherits from <tt class="literal"><span class="pre">type</span></tt>, as a consequences
+all metaclasses can be used as class factories.</p>
+<p>A fairy tale example will help in understanding the concept
+and few subtle points on how attributes are transmitted from metaclasses
+to their instances.</p>
+<p>Let me start by defining a 'Nobility' metaclass :</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Nobility(type): attributes=&quot;Power,Richness,Beauty&quot;
+</pre>
+</blockquote>
+<p>instances of 'Nobility' are classes such 'Princes', 'Dukes', 'Barons', etc.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Prince=Nobility(&quot;Prince&quot;,(),{})
+</pre>
+</blockquote>
+<p>Instances of 'Nobility' inherits its attributes, just as instances of normal
+classes inherits the class docstring:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Prince.attributes
+'Power,Richness,Beauty'
+</pre>
+</blockquote>
+<p>Nevertheless, 'attributes' will not be retrieved by the <tt class="literal"><span class="pre">dir</span></tt> function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print dir(Prince)
+['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__weakref__']
+</pre>
+</blockquote>
+<p>However, this is a limitation of <tt class="literal"><span class="pre">dir</span></tt>, in reality <tt class="literal"><span class="pre">Prince.attributes</span></tt>
+is there. On the other hand, the situation is different for a specific
+'Prince' object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; charles=Prince()
+&gt;&gt;&gt; charles.attributes #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'Prince' object has no attribute 'attributes'
+</pre>
+</blockquote>
+<p>The transmission of metaclass attributes is not transitive:
+instances of the metaclass inherits the attributes, but not the instances
+of the instances. This behavior is by design and is needed in order to avoid
+troubles with special methods. This point will be throughly
+explained in the last paragraph. For the moment, I my notice that the
+behaviour is reasonable, since the abstract qualities 'Power,Richness,Beauty'
+are more qualities of the 'Prince' class than of one specific representative.
+They can always be retrieved via the <tt class="literal"><span class="pre">__class__</span></tt> attribute:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; charles.__class__.attributes
+'Power,Richness,Beauty'
+</pre>
+</blockquote>
+<p>Le me now define a metaclass 'Froggyness':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Frogginess(type): attributes=&quot;Powerlessness,Poverty,Uglyness&quot;
+</pre>
+</blockquote>
+<p>Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Frog=Frogginess(&quot;Frog&quot;,(),{})
+&gt;&gt;&gt; Frog.attributes
+'Powerlessness,Poverty,Uglyness'
+</pre>
+</blockquote>
+<p>However, in Python miracles can happen:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def miracle(Frog): Frog.__class__=Nobility
+&gt;&gt;&gt; miracle(Frog); Frog.attributes
+'Powerlessness,Richness,Beauty'
+</pre>
+</blockquote>
+<p>In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.</p>
+<p>However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Frog.attributes=&quot;poor, small, ugly&quot;
+&gt;&gt;&gt; jack=Frog(); jack.attributes
+'poor, small, ugly'
+</pre>
+</blockquote>
+<p>Then the miracle cannot work:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;fairytale2.py&gt;
+
+class Nobility(type): attributes=&quot;Power, Richness, Beauty&quot;
+Prince=Nobility(&quot;Prince&quot;,(),{})
+charles=Prince()
+
+class Frogginess(type): attributes=&quot;Inpuissance, Poverty, Uglyness&quot;
+Frog=Frogginess(&quot;Frog&quot;,(),{})
+Frog.attributes=&quot;poor, small, ugly&quot;
+jack=Frog()
+
+def miracle(Frog): Frog.__class__=Nobility
+
+miracle(Frog)
+
+print &quot;I am&quot;,Frog.attributes,&quot;even if my class is&quot;,Frog.__class__
+
+#&lt;/fairytale2.py&gt;
+</pre>
+</blockquote>
+<p>Output:</p>
+<blockquote>
+<pre class="literal-block">
+I am poor, small, ugly even if my class is &lt;class '__main__.Nobility'&gt;
+</pre>
+</blockquote>
+<p>The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is <tt class="literal"><span class="pre">poor,</span> <span class="pre">small,</span> <span class="pre">ugly</span></tt>. If you think a bit, it makes sense.</p>
+<p>Remark:</p>
+<p>In Python 2.3 there are restrictions when changing the <tt class="literal"><span class="pre">__class__</span></tt>
+attribute for classes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C=type('C',(),{})
+&gt;&gt;&gt; C.__class__ = Nobility #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: __class__ assignment: only for heap types
+</pre>
+</blockquote>
+<p>Here changing <tt class="literal"><span class="pre">C.__class__</span></tt> is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print bool.__doc__ # in Python 2.2 would give an error
+bool(x) -&gt; bool
+Returns True when the argument x is true, False otherwise.
+The builtins True and False are the only two instances of the class bool.
+The class bool is a subclass of the class int, and cannot be subclassed.
+</pre>
+</blockquote>
+<p>In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M1(type): f=lambda cls: 'M1.f' #metaclass1
+&gt;&gt;&gt; class M2(type): f=lambda cls: 'M2.f' #metaclass2
+&gt;&gt;&gt; B=M1('B',(),{}) # B receives M1.f
+&gt;&gt;&gt; class C(B): pass #C receives M1.f
+&gt;&gt;&gt; B.f()
+'M1.f'
+B.__class__=M2 #change the metaclass
+&gt;&gt;&gt; B.f() #B receives M2.f
+'M2.f'
+C.f() #however C does *not* receive M2.f
+&gt;&gt;&gt; C.f()
+'M1.f'
+&gt;&gt;&gt; type(B)
+&lt;class '__main__.M2'&gt;
+&gt;&gt;&gt; type(C)
+&lt;class '__main__.M1'&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="metaclasses-as-class-modifiers">
+<h2><a class="toc-backref" href="#id113" name="metaclasses-as-class-modifiers">Metaclasses as class modifiers</a></h2>
+<p>The interpretation of metaclasses in terms of class factories is quite
+straightforward and I am sure that any Pythonista will be at home
+with the concept. However, metaclasses have such a reputation of black
+magic since their typical usage is <em>not</em> as class factories, but as
+<em>class modifiers</em>. This means that metaclasses are typically
+used to modify <em>in fieri</em> classes. The trouble is that the
+modification can be utterly magical.
+Here there is another fairy tale example showing the syntax
+(via the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook) and the magic of the game:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class UglyDuckling(PrettyPrinted):
+ &quot;A plain, regular class&quot;
+ formatstring=&quot;Not beautiful, I am %s&quot;
+
+class MagicallyTransformed(type):
+ &quot;Metaclass changing the formatstring of its instances&quot;
+ def __init__(cls,*args):
+ cls.formatstring=&quot;Very beautiful, since I am %s&quot;
+
+class TransformedUglyDuckling(PrettyPrinted):
+ &quot;A class metamagically modified&quot;
+ __metaclass__ = MagicallyTransformed
+ formatstring=&quot;Not beautiful, I am %s&quot; # will be changed
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; print UglyDuckling()
+Not beautiful, I am &lt;UglyDuckling&gt;
+</pre>
+</blockquote>
+<p>In this example, even if in 'TransformedUglyDuckling' we explicitely
+set the formatstring to &quot;Not beautiful, I am %s&quot;, the metaclass changes
+it to &quot;Very beautiful, even if I am %s&quot; and thus</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print TransformedUglyDuckling() # gives
+Very beautiful, since I am &lt;TransformedUglyDuckling&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook passes to the metaclass
+<tt class="literal"><span class="pre">MagicallyTransformed</span></tt> the name, bases and dictionary of the class
+being created, i.e. 'TransformedUglyDucking'.</p>
+<p>Metaclasses, when used as class modifiers, act <em>differently</em>
+from functions, when inheritance is
+involved. To clarify this subtle point, consider a subclass 'Swan'
+of 'UglyDuckling':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class Swan(UglyDuckling):
+... formatstring=&quot;Very beautiful, I am %s&quot;
+&gt;&gt;&gt; print Swan()
+Very beautiful, I am &lt;Swan&gt;
+</pre>
+</blockquote>
+<p>Now, let me define a simple function acting as a class modifier:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def magicallyTransform(cls):
+... &quot;Modifies the class formatstring&quot;
+... customize(cls,formatstring=&quot;Very beautiful, even if I am %s&quot;)
+... return cls
+</pre>
+</blockquote>
+<p>The function works:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; magicallyTransform(UglyDuckling)
+&gt;&gt;&gt; print UglyDuckling()
+Very beautiful, even if I am &lt;UglyDuckling&gt;
+</pre>
+</blockquote>
+<p>This approach is destructive, since we cannot have the original
+and the transformed class at the same time, and has potentially bad side
+effects in the derived classes. Nevertheless, in this case it works
+and it is not dangereous for the derived class 'Swan', since 'Swan'
+explicitly overrides the 'formatstring' attribute and doesn't care about
+the change in 'UglyDuckling.formatstring'. Therefore the output
+of</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print Swan()
+Very beautiful, I am &lt;Swan&gt;
+</pre>
+</blockquote>
+<p>is still the same as before the action of the function <tt class="literal"><span class="pre">magicallyTransform</span></tt>.
+The situation is quite different if we use the 'MagicallyTransformed'
+metaclass:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class Swan(TransformedUglyDuckling):
+... formatstring=&quot;Very beautiful, I am %s&quot;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; print TransformedUglyDuckling()
+Very beautiful, since I am &lt;UglyDuckling&gt;
+&gt;&gt;&gt; print Swan() # does *not* print &quot;Very beautiful, I am &lt;Swan&gt;&quot;
+Very beautiful, since I am &lt;Swan&gt;
+</pre>
+</blockquote>
+<p>Therefore, not only the metaclass has magically transformed the
+'TransformedUglyDuckling.formatstring', it has also transformed the
+'Swan.formatstring'! And that, despite the fact that
+'Swan.formatstring' is explicitly set.</p>
+<p>The reason for this behaviour is that since 'UglyDuckling' is a base
+class with metaclass 'MagicallyTransformed', and since 'Swan' inherits from
+'UglyDuckling', then 'Swan' inherits the metaclass 'MagicallyTransformed',
+which is automatically called at 'Swan' creation time.
+That's the reason why metaclasses are much more magical and much
+more dangerous than
+functions: functions do not override attributes in the derived classes,
+metaclasses do, since they are automagically called at the time of
+creation of the subclass. In other words, functions are explicit,
+metaclasses are implicit. Nevertheless, this behavior can be pretty
+useful in many circumstances, and it is a feature, not a bug. In the
+situations where this behavior is not intended, one should use a function,
+not a metaclass. In general, metaclasses are better than functions,
+since metaclasses are classes and as such they can inherit one from each
+other. This means that one can improve a basic metaclass trough
+(multiple) inheritance, with <em>reuse</em> of code.</p>
+</div>
+<div class="section" id="a-few-caveats-about-the-usage-of-metaclasses">
+<h2><a class="toc-backref" href="#id114" name="a-few-caveats-about-the-usage-of-metaclasses">A few caveats about the usage of metaclasses</a></h2>
+<p>Let me start with some caveats about the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook, which
+commonly used and quite powerful, but also quite dangereous.</p>
+<p>Let's imagine a programmer not
+knowing about metaclasses and looking at the 'TransformedUglyDuckling'
+code (assuming there are no comments): she would probably think
+that &quot;__metaclass__&quot; is some special attribute used for introspection
+purposes only, with no other effects, and she would probably expect
+the output of the script to be &quot;Not much, I am the class
+TransformedUglyDucking&quot; whereas it is exacly the contrary! In other
+words, when metaclasses are involved, <em>what you see, is not what you get</em>.
+The situation is even more implicit when the metaclass is inherited
+from some base class, therefore lacking also the visual clue of the hook.</p>
+<p>For these reasons, metaclasses are something to be used with great care;
+they can easily make your code unreadable and confuse inexpert programmers.
+Moreover, it is more difficult to debug programs involving metaclasses, since
+methods are magically transformed by routines defined in the metaclass,
+and the code you see in the class is <em>not</em> what Python sees. I think
+the least confusing way of using metaclasses, is to concentrate all
+the dynamics on them and to write empty classes except for the
+metaclass hook. If you write a class with no methods such as</p>
+<blockquote>
+<pre class="literal-block">
+class TransformedUglyDuckling(object):
+ __metaclass__=MagicallyTransformed
+</pre>
+</blockquote>
+<p>then the only place to look at, is the metaclass. I have found extremely
+confusing to have some of the methods defined in the class and some in
+the metaclass, especially during debugging.</p>
+<p>Another point to make, is that the <tt class="literal"><span class="pre">__metaclass__</span></tt>
+hook should not be used to modify pre-existing classes,
+since it requires modifying the source code (even if it is enough to
+change one line only). Moreover, it is confusing, since adding a
+<tt class="literal"><span class="pre">__metaclass__</span></tt> attribute <em>after</em> the class creation would not do the job:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import UglyDuckling, MagicallyTransformed
+&gt;&gt;&gt; UglyDuckling.__metaclass__=MagicallyTransformed
+&gt;&gt;&gt; print UglyDuckling()
+&quot;Not much, I am the class UglyDuckling&quot;
+</pre>
+</blockquote>
+<p>The reason is that we have to think of UglyDuckling as an instance of
+<tt class="literal"><span class="pre">type</span></tt>, the built-in metaclasses; merely adding a <tt class="literal"><span class="pre">__metaclass__</span></tt>
+attribute does not re-initialize the class.
+The problem is elegantly solved by avoiding the hook and creating
+an enhanced copy of the original class trough <tt class="literal"><span class="pre">MagicallyTransformed</span></tt>
+used as a class factory.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; name=UglyDuckling.__name__
+&gt;&gt;&gt; bases=UglyDuckling.__bases__
+&gt;&gt;&gt; dic=UglyDuckling.__dict__.copy()
+&gt;&gt;&gt; UglyDuckling=MagicallyTransformed(name,bases,dic)
+</pre>
+</blockquote>
+<p>Notice that I have recreated 'UglyDuckling', giving to the new class
+the old identifier.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print UglyDuckling()
+Very beautiful, since I am &lt;UglyDuckling&gt;&gt;
+</pre>
+</blockquote>
+<p>The metaclass of this new 'UglyDuckling' has been specified and will
+accompanies all future children of 'UglyDuckling':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Swan(UglyDuckling): pass
+...
+&gt;&gt;&gt; type(Swan)
+&lt;class '__main__.MagicallyTransformed'&gt;
+</pre>
+</blockquote>
+<p>Another caveat, is in the overridding of `` __init__`` in the metaclass.
+This is quite common in the case of metaclasses called trough the
+<tt class="literal"><span class="pre">__metaclass__</span></tt> hook mechanism, since in this case the class
+has been already defined (if not created) in the class statement,
+and we are interested in initializing it, more than in recreating
+it (which is still possible, by the way).
+The problem is that overriding <tt class="literal"><span class="pre">__init__</span></tt> has severe limitations
+with respect to overriding <tt class="literal"><span class="pre">__new__</span></tt>,
+since the 'name', 'bases' and 'dic' arguments cannot be directly
+changed. Let me show an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;init_in_metaclass.py&gt;
+
+from oopp import *
+
+class M(type):
+ &quot;Shows that dic cannot be modified in __init__, only in __new__&quot;
+ def __init__(cls,name,bases,dic):
+ name='C name cannot be changed in __init__'
+ bases='cannot be changed'
+ dic['changed']=True
+
+class C(object):
+ __metaclass__=M
+ changed=False
+
+print C.__name__ # =&gt; C
+print C.__bases__ # =&gt; (&lt;type 'object'&gt;,)
+print C.changed # =&gt; False
+
+#&lt;/init_in_metaclass.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is <tt class="literal"><span class="pre">False</span></tt>: the dictionary cannot be changed in
+<tt class="literal"><span class="pre">__init__</span></tt> method. However, replacing <tt class="literal"><span class="pre">dic['changed']=True</span></tt> with
+<tt class="literal"><span class="pre">cls.changed=True</span></tt> would work. Analougously, changing <tt class="literal"><span class="pre">cls.__name__</span></tt>
+would work. On the other hand, <tt class="literal"><span class="pre">__bases__</span></tt> is a read-only attribute and
+cannot be changed once the class has been created, therefore there is no
+way it can be touched in <tt class="literal"><span class="pre">__init__</span></tt>. However, <tt class="literal"><span class="pre">__bases__</span></tt> could be
+changed in <tt class="literal"><span class="pre">__new__</span></tt> before the class creation.</p>
+</div>
+<div class="section" id="metaclasses-and-inheritance">
+<h2><a class="toc-backref" href="#id115" name="metaclasses-and-inheritance">Metaclasses and inheritance</a></h2>
+<p>It is easy to get confused about the difference between a metaclass
+and a mix-in class in multiple inheritance, since
+both are denoted by adjectives and both share the same idea of
+enhancing a hierarchy. Moreover, both mix-in classes and metaclasses
+can be inherited in the whole hierarchy.
+Nevertheless, they behaves differently
+and there are various subtle point to emphasize. We have already
+noticed in the first section that attributes of a metaclass
+are transmitted to its instances, but not to the instances of the
+instances, whereas the normal inheritance is transitive: the
+grandfather transmits its attributes to the children and to the grandchild
+too. The difference can be represented with the following picture, where
+'M' is the metaclass, 'B' a base class, 'C' a children of 'B'
+and c an instance of 'C':</p>
+<blockquote>
+<pre class="literal-block">
+M (attr) B (attr)
+: |
+C (attr) C (attr)
+: :
+c () c (attr)
+</pre>
+</blockquote>
+<p>Notice that here the relation of instantiation is denoted by a dotted line.</p>
+<p>This picture is valid when C has metaclass M but not base class, on when C
+has base class but not metaclass. However, what happens whrn the class C has
+both a metaclass M and a base class B ?</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type): a='M.a'
+&gt;&gt;&gt; class B(object): a='B.a'
+&gt;&gt;&gt; class C(B): __metaclass__=M
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>The situation can be represented by in the following graph,</p>
+<blockquote>
+<pre class="literal-block">
+(M.a) M B (B.a)
+ : /
+ : /
+ (?) C
+ :
+ :
+ (?) c
+</pre>
+</blockquote>
+<p>Here the metaclass M and the base class B are fighting one against the other.
+Who wins ? C should inherit the attribute 'B.a' from its base B, however,
+the metaclass would like to induce an attribute 'M.a'.
+The answer is that the inheritance constraint wins on the metaclass contraint:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.a
+'B.a'
+&gt;&gt;&gt; c.a
+'B.a'
+</pre>
+</blockquote>
+<p>The reason is the same we discussed in the fairy tale example: 'M.a' is
+an attribute of the metaclass, if its instance C has already a specified
+attributed C.a (in this case specified trough inheritance from B), then
+the attribute is not modified. However, one could <em>force</em> the modification:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... def __init__(cls,*args): cls.a='M.a'
+&gt;&gt;&gt; class C(B): __metaclass__=M
+&gt;&gt;&gt; C.a
+'M.a'
+</pre>
+</blockquote>
+<p>In this case the metaclass M would win on the base class B. Actually,
+this is not surprising, since it is explicit. What could be surprising,
+had we not explained why inheritance silently wins, is that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.a
+'B.a'
+</pre>
+</blockquote>
+<p>This explain the behaviour for special methods like
+<tt class="literal"><span class="pre">__new__,__init__,__str__</span></tt>,
+etc. which are defined both in the class and the metaclass with the same
+name (in both cases,they are inherited from <tt class="literal"><span class="pre">object</span></tt>).</p>
+<p>In the chapter on objects, we learned that the printed representation of
+an object can be modified by overring the <tt class="literal"><span class="pre">__str__</span></tt> methods of its
+class. In the same sense, the printed representation of a class can be
+modified by overring the <tt class="literal"><span class="pre">__str__</span></tt> methods of its metaclass. Let me show an
+example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Printable(PrettyPrinted,type):
+ &quot;&quot;&quot;Apparently does nothing, but actually makes PrettyPrinted acting as
+ a metaclass.&quot;&quot;&quot;
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Instances of 'Printable' are classes with a nice printable representation:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Printable
+&gt;&gt;&gt; C=Printable('Classname',(),{})
+&gt;&gt;&gt; print C
+Classname
+</pre>
+</blockquote>
+<p>However, the internal string representation stays the same:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C # invokes Printable.__repr__
+&lt;class '__main__.Classname'&gt;
+</pre>
+</blockquote>
+<p>Notice that the name of class 'C' is <tt class="literal"><span class="pre">Classname</span></tt> and not 'C' !</p>
+<p>Consider for instance the following code:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... def __str__(cls):
+... return cls.__name__
+... def method(cls):
+... return cls.__name__
+...
+&gt;&gt;&gt; class C(object):
+... __metaclass__=M
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>In this case the <tt class="literal"><span class="pre">__str__</span></tt> method in <tt class="literal"><span class="pre">M</span></tt> cannot override the
+<tt class="literal"><span class="pre">__str__</span></tt> method in C, which is inherited from <tt class="literal"><span class="pre">object</span></tt>.
+Moreover, if you experiment a little, you will see that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C # is equivalent to print M.__str__(C)
+C
+&gt;&gt;&gt; print c # is equivalent to print C.__str__(c)
+&lt;__main__.C object at 0x8158f54&gt;
+</pre>
+</blockquote>
+<p>The first <tt class="literal"><span class="pre">__str__</span></tt> is &quot;attached&quot; to the metaclass and the
+second to the class.</p>
+<p>Consider now the standard method &quot;method&quot;. It is both attached to the
+metaclass</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print M.method(C)
+C
+</pre>
+</blockquote>
+<p>and to the class</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C.method() #in a sense, this is a class method, i.e. it receives
+C #the class as first argument
+</pre>
+</blockquote>
+<p>Actually it can be seen as a class method of 'C' (cfr. Guido van Rossum
+&quot;Unifying types and classes in Python 2.2&quot;. When he discusses
+classmethods he says: <em>&quot;Python also has real metaclasses, and perhaps
+methods defined in a metaclass have more right to the name &quot;class method&quot;;
+but I expect that most programmers won't be using metaclasses&quot;</em>). Actually,
+this is the SmallTalk terminology, Unfortunately, in Python the word
+<tt class="literal"><span class="pre">classmethod</span></tt> denotes an attribute descriptor, therefore it is better
+to call the methods defined in a metaclass <em>metamethods</em>, in order to avoid
+any possible confusion.</p>
+<p>The difference between <tt class="literal"><span class="pre">method</span></tt> and <tt class="literal"><span class="pre">__str__</span></tt> is that you cannot use the
+syntax</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C.__str__() #error
+TypeError: descriptor '__str__' of 'object' object needs an argument
+</pre>
+</blockquote>
+<p>because of the confusion with the other __str__; you can only use the
+syntax</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print M.__str__(C)
+</pre>
+</blockquote>
+<p>Suppose now I change C's definition by adding a method called &quot;method&quot;:</p>
+<blockquote>
+<pre class="literal-block">
+class C(object):
+ __metaclass__=M
+ def __str__(self):
+ return &quot;instance of %s&quot; % self.__class__
+ def method(self):
+ return &quot;instance of %s&quot; % self.__class__
+</pre>
+</blockquote>
+<p>If I do so, then there is name clashing and the previously working
+statement print C.method() gives now an error:</p>
+<blockquote>
+<pre class="literal-block">
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 24, in ?
+TypeError: unbound method method() must be called with C instance as
+first argument (got nothing instead)
+</pre>
+</blockquote>
+<p>Conclusion: <tt class="literal"><span class="pre">__str__,</span> <span class="pre">__new__,</span> <span class="pre">__init__</span></tt> etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. <tt class="literal"><span class="pre">M.__str__(C)</span></tt>),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. <tt class="literal"><span class="pre">C.method()</span></tt> instead of
+<tt class="literal"><span class="pre">M.method(C)</span></tt>).
+Metaclass methods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for <tt class="literal"><span class="pre">__str__</span></tt>,
+<tt class="literal"><span class="pre">___init__</span></tt>, etc.</p>
+</div>
+<div class="section" id="conflicting-metaclasses">
+<h2><a class="toc-backref" href="#id116" name="conflicting-metaclasses">Conflicting metaclasses</a></h2>
+<p>Consider a class 'A' with metaclass 'M_A' and a class 'B' with
+metaclass 'M_B'; suppose I derive 'C' from 'A' and 'B'. The question is:
+what is the metaclass of 'C' ? Is it 'M_A' or 'M_B' ?</p>
+<p>The correct answer (see &quot;Putting metaclasses to work&quot; for a thought
+discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+'M_A' and 'M_B', as in the following graph:</p>
+<blockquote>
+<div class="figure">
+<p><img alt="fig1.gif" src="fig1.gif" /></p>
+</div>
+</blockquote>
+<p>However, Python is not yet that magic, and it does not automatically create
+'M_C'. Instead, it will raise a <tt class="literal"><span class="pre">TypeError</span></tt>, warning the programmer of
+the possible confusion:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M_A(type): pass
+&gt;&gt;&gt; class M_B(type): pass
+&gt;&gt;&gt; A=M_A('A',(),{})
+&gt;&gt;&gt; B=M_B('B',(),{})
+&gt;&gt;&gt; class C(A,B): pass #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: metatype conflict among bases
+</pre>
+</blockquote>
+<p>This is an example where the metaclasses 'M_A' and 'M_B' fight each other
+to generate 'C' instead of cooperating. The metatype conflict can be avoided
+by assegning the correct metaclass to 'C' by hand:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B): __metaclass__=type(&quot;M_AM_B&quot;,(M_A,M_B),{})
+&gt;&gt;&gt; type(C)
+&lt;class '__main__.M_AM_B'&gt;
+</pre>
+</blockquote>
+<p>In general, a class A(B, C, D , ...) can be generated without conflicts only
+if type(A) is a subclass of each of type(B), type(C), ...</p>
+<p>In order to avoid conflicts, the following function, that generates
+the correct metaclass by looking at the metaclasses of the base
+classes, is handy:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+metadic={}
+
+def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname=&quot;_&quot;+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This function is particularly smart since:</p>
+<blockquote>
+<ol class="arabic simple">
+<li>Avoid duplications ..</li>
+<li>Remember its results.</li>
+</ol>
+</blockquote>
+<p>We may generate the child of a tuple of base classes with a given metaclass
+and avoiding metatype conflicts thanks to the following <tt class="literal"><span class="pre">child</span></tt> function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def makecls(*metas,**options):
+ &quot;&quot;&quot;Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses&quot;&quot;&quot;
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B): __metaclass__=makecls()
+&gt;&gt;&gt; print C,type(C)
+&lt;class 'oopp.AB_'&gt; &lt;class 'oopp._M_AM_B'&gt;
+</pre>
+</blockquote>
+<p>Notice that the automatically generated metaclass does not pollute the
+namespace:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; _M_A_M_B #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+NameError: name '_M_A_M_B' is not defined
+</pre>
+</blockquote>
+<p>It can only be accessed as <tt class="literal"><span class="pre">type(C)</span></tt>.</p>
+<p>Put it shortly, the <tt class="literal"><span class="pre">child</span></tt> function allows to generate a child from bases
+enhanced by different custom metaclasses, by generating under the hood a
+compatibile metaclass via multiple inheritance from the original metaclasses.
+However, this logic can only work if the original metaclasses are
+cooperative, i.e. their methods are written in such a way to avoid
+collisions. This can be done by using the cooperative the <tt class="literal"><span class="pre">super</span></tt> call
+mechanism discussed in chapter 4.</p>
+</div>
+<div class="section" id="cooperative-metaclasses">
+<h2><a class="toc-backref" href="#id117" name="cooperative-metaclasses">Cooperative metaclasses</a></h2>
+<p>In this section I will discuss how metaclasses can be composed with
+classes and with metaclasses, too. Since we will discusss even
+complicated hierarchies, it is convenient to have an utility
+routine printing the MRO of a given class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def MRO(cls):
+ count=0; out=[]
+ print &quot;MRO of %s:&quot; % cls.__name__
+ for c in cls.__mro__:
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=&quot; %s - %s(%s)&quot; % (count,name,bases)
+ if type(c) is not type: s+=&quot;[%s]&quot; % type(c).__name__
+ out.append(s); count+=1
+ return '\n'.join(out)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice that <tt class="literal"><span class="pre">MRO</span></tt> also prints the metaclass' name in square brackets, for
+classes enhanced by a non-trivial metaclass.</p>
+<p>Consider for instance the following hierarchy:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import MRO
+&gt;&gt;&gt; class B(object): pass
+&gt;&gt;&gt; class M(B,type): pass
+&gt;&gt;&gt; class C(B): __metaclass__=M
+</pre>
+</blockquote>
+<p>Here 'M' is a metaclass that inherits from 'type' and the base class 'B'
+and 'C' is both an instance of 'M' and a child of 'B'. The inheritance
+graph can be draw as</p>
+<blockquote>
+<pre class="literal-block">
+ object
+ / \
+B type
+| \ /
+| M
+\ :
+ \ :
+ C
+</pre>
+</blockquote>
+<p>Suppose now we want to retrieve the <tt class="literal"><span class="pre">__new__</span></tt> method of B's superclass
+with respect to the MRO of C: obviously, this is <tt class="literal"><span class="pre">object.__new__</span></tt>, since</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print MRO(C)
+MRO of C:
+ 0 - C(B)[M]
+ 1 - B(object)
+ 2 - object()
+</pre>
+</blockquote>
+<p>This allows to create an instance of 'C' in this way:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(B,C).__new__(C)
+&lt;__main__.C object at 0x4018750c&gt;
+</pre>
+</blockquote>
+<p>It is interesting to notice that this would not work in Python 2.2,
+due to a bug in the implementation of <tt class="literal"><span class="pre">super</span></tt>, therefore do not
+try this trick with older version of Python.</p>
+<p>Notice that everything works
+only because <tt class="literal"><span class="pre">B</span></tt> inherits the <tt class="literal"><span class="pre">object.__new__</span></tt> staticmethod that
+is cooperative and it turns out that it calls <tt class="literal"><span class="pre">type.__new__</span></tt>. However,
+if I give to 'B' a non-cooperative method</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; B.__new__=staticmethod(lambda cls,*args: object.__new__(cls))
+</pre>
+</blockquote>
+<p>things do not work:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; M('D',(),{}) #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;lambda&gt;
+TypeError: object.__new__(M) is not safe, use type.__new__()
+</pre>
+</blockquote>
+<p>A cooperative method would solve the problem:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; B.__new__=staticmethod(lambda m,*args: super(B,m).__new__(m,*args))
+&gt;&gt;&gt; M('D',(),{}) # calls B.__new__(M,'D',(),{})
+&lt;class '__main__.D'&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="metamethods-vs-class-methods">
+<h2><a class="toc-backref" href="#id118" name="metamethods-vs-class-methods">Metamethods vs class methods</a></h2>
+<p>Meta-methods, i.e. methods defined in
+a metaclass.</p>
+<p>Python has already few built-in metamethods: <tt class="literal"><span class="pre">.mro()</span></tt>
+and <tt class="literal"><span class="pre">__subclass__</span></tt>. These are methods of the metaclass 'type' and
+there of any of its sub-metaclasses.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; dir(type)
+['__base__', '__bases__', '__basicsize__', '__call__', '__class__',
+ '__cmp__', '__delattr__', '__dict__', '__dictoffset__', '__doc__',
+ '__flags__', '__getattribute__', '__hash__', '__init__', '__itemsize__',
+ '__module__', '__mro__', '__name__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__subclasses__', '__weakrefoffset__', 'mro']
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; print type.mro.__doc__
+mro() -&gt; list
+return a type's method resolution order
+&gt;&gt;&gt; print type.__subclasses__.__doc__
+__subclasses__() -&gt; list of immediate subclasses
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A(object): pass
+&gt;&gt;&gt; class B(A): pass
+&gt;&gt;&gt; B.mro()
+[&lt;class '__main__.B'&gt;, &lt;class '__main__.A'&gt;, &lt;type 'object'&gt;]
+&gt;&gt;&gt; A.__subclasses__()
+[&lt;class '__main__.B'&gt;]
+</pre>
+</blockquote>
+<p>Notice that <tt class="literal"><span class="pre">mro()</span></tt> and <tt class="literal"><span class="pre">__subclasses__</span></tt> are not retrieved by <tt class="literal"><span class="pre">dir</span></tt>.</p>
+<p>Let me constrast metamethods with the more traditional classmethods.
+In many senses, the to concepts are akin:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... &quot;Metaclass with a (meta)method mm&quot;
+... def mm(cls): return cls
+&gt;&gt;&gt; D=M('D',(),{'cm':classmethod(lambda cls: cls)})
+&gt;&gt;&gt; # instance of M with a classmethod cm
+&gt;&gt;&gt; D.mm # the metamethod
+&lt;bound method M.mm of &lt;class '__main__.C'&gt;&gt;
+&gt;&gt;&gt; D.cm # the classmethod
+&lt;unbound method D.&lt;lambda&gt;&gt;
+</pre>
+</blockquote>
+<p>Notice the similarities between the classmethod and the metamethod:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; D.mm.im_self, D.cm.im_self # the same
+(&lt;class '__main__.D'&gt;, &lt;class '__main__.D'&gt;)
+&gt;&gt;&gt; D.mm.im_class, D.cm.im_class # still the same
+(&lt;class '__main__.M'&gt;, &lt;class '__main__.M'&gt;)
+</pre>
+</blockquote>
+<p>There are no surprises for <tt class="literal"><span class="pre">im_func</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; D.mm.im_func, D.cm.im_func
+(&lt;function mm at 0x402c272c&gt;, &lt;function &lt;lambda&gt; at 0x402c280c&gt;)
+</pre>
+</blockquote>
+<p>Nevertheless, there are differences: metamethods are not bounded to
+instances of the class</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; D().cm() # the classmethod works fine
+&lt;class '__main__.D'&gt;
+&gt;&gt;&gt; D().mm() # the metamethod does not: error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'D' object has no attribute 'mm'
+</pre>
+</blockquote>
+<p>and they are not retrieved by <tt class="literal"><span class="pre">dir</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; attributes(D).keys() # mm is not retrieved, only cm
+['cm']
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever') #under Python 2.2.0 would give a serious error
+Segmentation fault
+&gt;&gt;&gt; cm.__get__(None) #under Python 2.3 there is no error
+&lt;bound method type.&lt;lambda&gt; of &lt;type 'NoneType'&gt;&gt;
+</pre>
+</blockquote>
+<p>Moreover metamethods behaves differently with respect to multiple
+inheritance. If a class A define a classmethod cA and a class B
+defines a classmethod cB, then the class C(A,B) inherits both the
+classmethods cA and cB. In the case of metamethods defined in M_A
+and M_B, the same is true only if one resolves the meta-type
+conflict by hand, by generating the metaclass M_C(M_A,M_B). In this
+sense, classmethods are simpler to use than metamethods.</p>
+</div>
+</div>
+<div class="section" id="the-magic-of-metaclasses-part-2">
+<h1><a class="toc-backref" href="#id119" name="the-magic-of-metaclasses-part-2">THE MAGIC OF METACLASSES - PART 2</a></h1>
+<p>Metaclasses are so powerful that a single chapter is not enough to make
+justice to them ;) In this second chapter on metaclasses I will
+unravel their deepest secrets, covering topics such as meta-metaclasses,
+anonymous inner metaclasses, global metaclasses and advanced class factories.</p>
+<p>Moreover, I will give various magical applications of metaclasses,
+in the realm of enhancing the Python language itself. Actually, this is
+probably the most idiomatic application of metaclasses (Guido's examples
+on the metaclass usage are all in this area). I will show
+how metaclasses can be used to enhance the <tt class="literal"><span class="pre">super</span></tt> cooperatice call
+mechanism.</p>
+<p>This is not a chapter for the faint of heart.</p>
+<div class="section" id="the-secrets-of-the-metaclass-hook">
+<h2><a class="toc-backref" href="#id120" name="the-secrets-of-the-metaclass-hook">The secrets of the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook</a></h2>
+<p>In the previous chapter we have seen how the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook can
+be used as a way of metaclass enhancing pre-existing classes
+with a minimal change of the sourcecode.</p>
+<p>But it has much deeper secrets.</p>
+<p>The first and simplest of them,
+is the fact that the hook can be used it can also be defined
+at the module level, <em>outside</em> the class. This allows a number of neat
+tricks, since in presence of a <tt class="literal"><span class="pre">__metaclass__</span></tt> hook at the module
+level <em>all</em> the old style classes in the module (including nested ones!)
+acquire that hook. A first application is to rejuvenate old style classes
+to new style classes.</p>
+<p>I remind that old style classes are retained with compability with old
+code, but they are a pain in the back, if you want to use features
+intended for new style classes only (for instance properties etc.).
+Naively, one would expect the conversion from old style classes
+to new style to be long and error prone: suppose you have a very large
+application with hundreds of old style classes defined in dozens of modules.
+Suppose you want to update your application to Python 2.2+ classes in order
+to take advantage of the new features I have discussed extensively in this
+book: the naive way to go would be to go trough the source, look for
+all classes definitions and change</p>
+<blockquote>
+<pre class="literal-block">
+Classname: --&gt; Classname(object)
+</pre>
+</blockquote>
+<p>One could solve this problem with a regular expression search and replace
+in all modules, but this would require to change <em>all</em> the source.
+This is againt the spirit of OOP, we must <em>reuse</em> old code.</p>
+<p>Metaclasses are particularly handy to solve this problem: actually it is
+enough to add to your modules the following line as first line:</p>
+<blockquote>
+<pre class="literal-block">
+__metaclass__ = type
+</pre>
+</blockquote>
+<p>Then, all your old style classes will have 'type' as their metaclass: this
+is akin to say that all the old style classes are <em>automagically</em> rejuvenate
+to new style classes! And this also works for <em>nested</em> classes!!</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;rejuvenate.py&gt;
+
+__metaclass__ = type # this rejuvanate all the class in the module
+
+class C:
+ class D: pass
+
+print dir(C) # both C and C.D
+print dir(C.D) # are now new style classes
+
+#&lt;/rejuvenate.py&gt;
+</pre>
+</blockquote>
+<p>This very first example add consistence (if needed) to the
+widespread belief that metaclasses have a well deserved reputation of magic.</p>
+<p>The explanation is that defining a global metaclass called <tt class="literal"><span class="pre">__metaclass__</span></tt>
+automatically makes all old style classes (new style class simply ignore
+the existence of the global <tt class="literal"><span class="pre">__metaclass__</span></tt>) defined in you module
+instances of the given metaclass; this automatically converts them to
+new style classes.</p>
+</div>
+<div class="section" id="anonymous-inner-metaclasses">
+<h2><a class="toc-backref" href="#id121" name="anonymous-inner-metaclasses">Anonymous inner metaclasses</a></h2>
+<p>A second, deeper secret of the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook is that it can be
+used to define anonymous <em>inner metaclasses</em>. The following example
+explain what I mean:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def totuple(arg):
+ &quot;Converts the argument to a tuple, if need there is&quot;
+ if isinstance(arg,tuple): return arg # do nothing
+ else: return (arg,) # convert to tuple
+
+class BracketCallable(object):
+ &quot;&quot;&quot;Any subclass C(BracketCallable) can be called with the syntax C[t],
+ where t is a tuple of arguments stored in bracket_args; returns the
+ class or an instance of it, depending on the flag 'returnclass'.&quot;&quot;&quot;
+
+ returnclass=True
+ class __metaclass__(type): # anonymous inner metaclass
+ def __getitem__(cls,args): # non cooperative metamethod
+ if cls.returnclass:
+ c=type(cls.__name__,(cls,),{'bracket_args':totuple(args)})
+ return c # a customized copy of the original class
+ else:
+ self=cls(); self.bracket_args=totuple(args)
+ return self
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>In this code 'BracketCallable.__metaclass__' is the anonymous (actually
+it has a special name, <tt class="literal"><span class="pre">__metaclass__</span></tt>) inner metaclass of 'BracketCallable'.</p>
+<p>The effect of 'BracketCallable.__metaclass__' is the following: it makes
+'BracketCallable' and its descendants callable with brackets. Since
+the 'returnclass' flag is set, <tt class="literal"><span class="pre">__getitem__</span></tt> returns the class
+with an attribute 'bracket_args' containing the tuple of the passed
+arguments (otherwise it returns an instance of the class).
+This works since when
+Python encounters an expression of kind <tt class="literal"><span class="pre">cls[arg]</span></tt> it interprets it
+as <tt class="literal"><span class="pre">type(cls).__getitem__(cls,arg)</span></tt>. Therefore, if <tt class="literal"><span class="pre">cls</span></tt> is a subclass
+of 'BracketCallable', this means that</p>
+<blockquote>
+<pre class="literal-block">
+cls[arg] &lt;=&gt; BracketCallable.__metaclass__.__getitem__(cls,arg)
+</pre>
+</blockquote>
+<p>Let me give few examples:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import BracketCallable
+&gt;&gt;&gt; type(BracketCallable)
+&lt;class 'oopp.__metaclass__'&gt;
+&gt;&gt;&gt; print type(BracketCallable).__name__ # not really anonymous
+__metaclass__
+&gt;&gt;&gt; print BracketCallable['a1'].bracket_args
+('a1',)
+&gt;&gt;&gt; print BracketCallable['a1','a2'].bracket_args
+('a1', 'a2')
+</pre>
+</blockquote>
+<p>This syntactical feature is an example of a thing that can be done
+<em>trough metaclasses only</em>: it cannot be emulated by functions.</p>
+<p>Anonymous inner metaclasses are the least verbose manner
+of defining metamethods. Moreover, they are a neat trick to define
+mix-in classes that, when inherited, can metamagically enhance
+an entire multiple inheritance hierarchy.</p>
+<p>In the previous example <tt class="literal"><span class="pre">__getitem__</span></tt> is noncooperative, but nothing
+forbids anonymous inner metaclasses from being made cooperative. However,
+there is some subtlety one must be aware of.
+Let me give an example. My 'WithCounter' class counts how many instances
+of 'WithCounter' and its subclasses are generated. However, it does not
+distinguishes bewteen different subclasses.
+This was correct in the pizza shop example, simple only the total
+number of produced pizzas mattered, however, in other situations,
+one may want to reset the counter each time a new subclass is created.
+This can be done automagically by a cooperative inner metaclass:</p>
+<blockquote>
+<pre class="literal-block">
+class WithMultiCounter(WithCounter):
+ &quot;&quot;&quot;Each time a new subclass is derived, the counter is reset&quot;&quot;&quot;
+ class __metaclass__(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.__this,cls).__init__(*args)
+ reflective(__metaclass__)
+</pre>
+</blockquote>
+<p>Notice that the order of execution of this code is subtle:</p>
+<ol class="arabic simple">
+<li>first, the fact that WithMulticounter has a non-trivial metaclass is
+registered, but nothing else is done;</li>
+<li>then, the line <tt class="literal"><span class="pre">reflective(__metaclass__)</span></tt> is executed: this means
+that the inner metaclass (and therefore its instances) get an
+attribute <tt class="literal"><span class="pre">._metaclass__this</span></tt> containing a reference to the
+inner metaclass;</li>
+<li>then, the outer class is passed to its inner metaclass and created
+by the inherited metaclass' <tt class="literal"><span class="pre">__new__</span></tt> method;</li>
+<li>at this point <tt class="literal"><span class="pre">cls</span></tt> exists and <tt class="literal"><span class="pre">cls.__this</span></tt> is inherited from
+<tt class="literal"><span class="pre">__metaclass__._metaclass__this</span></tt>; this means that the expression
+<tt class="literal"><span class="pre">super(cls.__this,cls).__init__(*args)</span></tt> is correctly recognized and
+'WithMultiCounter' can be initialized;</li>
+<li>only after that, the name 'WithMultiCounter' enters in the global namespace
+and can be recognized.</li>
+</ol>
+<p>Notice in particular that inside <tt class="literal"><span class="pre">super</span></tt>, we could also
+use <tt class="literal"><span class="pre">cls.__metaclass__</span></tt> instead of <tt class="literal"><span class="pre">cls.__this</span></tt>, but this
+would not work inside <tt class="literal"><span class="pre">__new__</span></tt>, whereas <tt class="literal"><span class="pre">__this</span></tt> would be
+recognized even in <tt class="literal"><span class="pre">__new__</span></tt>.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; print MRO(WithMultiCounter)
+1 - WithMultiCounter(WithCounter)[__metaclass__]
+2 - WithCounter(object)
+3 - object()
+</pre>
+</blockquote>
+<p>For sake of readability, often it is convenient
+to give a name even to inner classes:</p>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class WithMultiCounter(WithCounter):
+ &quot;&quot;&quot;Each time a new subclass is derived, the counter is reset&quot;&quot;&quot;
+ class ResetsCounter(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.ResetsCounter,cls).__init__(*args)
+ __metaclass__=ResetsCounter
+
+#&lt;/oopp.py&gt;
+</pre>
+<p>Notice that inside super we used the expression <tt class="literal"><span class="pre">cls.ResetsCounter</span></tt> and
+not <tt class="literal"><span class="pre">WithMultiCounter.ResetsCounter</span></tt>: doing that would generate a
+<tt class="literal"><span class="pre">NameError:</span> <span class="pre">global</span> <span class="pre">name</span> <span class="pre">'WithMultiCounter'</span> <span class="pre">is</span> <span class="pre">not</span> <span class="pre">defined</span></tt> since at the
+time when <tt class="literal"><span class="pre">ResetsCounter.__init__</span></tt> is called for the first time,
+the class <tt class="literal"><span class="pre">WithMultiCounter</span></tt> exists but is has not yet entered the global
+namespace: this will happens only after the initialization in the
+<tt class="literal"><span class="pre">ResetsCounter</span></tt> metaclass, as we said before.</p>
+<p>Without the metaclass one can reset the counter by hand each time, or
+can reset the counter on all the classes of the hierarchy with a
+convenient function (akin to the 'traceH' routine defined in chapter 6).</p>
+<p>Example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class GrandFather(WithMultiCounter): pass
+&gt;&gt;&gt; class Father(GrandFather): pass
+&gt;&gt;&gt; class Child(Father): pass
+&gt;&gt;&gt; GrandFather()
+&lt;__main__.GrandFather object at 0x402f7f6c&gt; # first GrandFather instance
+&gt;&gt;&gt; Father()
+&lt;__main__.Father object at 0x402f79ec&gt; # first Father instance
+&gt;&gt;&gt; Father()
+&lt;__main__.Father object at 0x402f7d4c&gt; # second Father instance
+&gt;&gt;&gt; Child.counter # zero instances
+0
+&gt;&gt;&gt; Father.counter # two instances
+2
+&gt;&gt;&gt; GrandFather.counter # one instance
+1
+</pre>
+</blockquote>
+<p>I leave as an exercise for the reader to show that the original 'WithCounter'
+would fail to count correctly the different subclasses and would put the
+total number of instances in 'Child'.</p>
+</div>
+<div class="section" id="passing-parameters-to-meta-classes">
+<h2><a class="toc-backref" href="#id122" name="passing-parameters-to-meta-classes">Passing parameters to (meta) classes</a></h2>
+<p>Calling a class with brackets is a way of passing parameters to it (or
+to its instances, if the 'returnclass' flag is not set).
+There additional ways for of doing that.
+One can control the instantiation syntax of classes by redefining the
+<tt class="literal"><span class="pre">__call__</span></tt> method of the metaclass. The point is that when we instantiate
+an object with the syntax <tt class="literal"><span class="pre">c=C()</span></tt>, Python looks
+at the <tt class="literal"><span class="pre">__call__</span></tt> method of the metaclass of 'C'; the default behaviour
+it is to call <tt class="literal"><span class="pre">C.__new__</span></tt> and <tt class="literal"><span class="pre">C.__init__</span></tt> in succession, however, that
+behavior can be overridden. Let me give an example without using
+anonymous metaclasses (for sake of clarity only).</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;metacall.py&gt;
+
+class M(type): # this is C metaclass
+ def __call__(cls):
+ return &quot;Called M.__call__&quot;
+
+C=M('C',(),{}) # calls type(M).__call__
+c=C() # calls type(C).__call__
+# attention: c is a string!
+print c #=&gt; Called M.__call__
+
+#&lt;/metacall.py&gt;
+</pre>
+</blockquote>
+<p>In this example, <tt class="literal"><span class="pre">M.__call__</span></tt> simply
+returns the string <tt class="literal"><span class="pre">Called</span> <span class="pre">M.__call__</span></tt>, and the class
+'C' is <em>not</em> instantiated. Overriding the metaclass
+<tt class="literal"><span class="pre">__call__</span> <span class="pre">``</span> <span class="pre">method</span> <span class="pre">therefore</span> <span class="pre">provides</span> <span class="pre">another</span> <span class="pre">way</span> <span class="pre">to</span> <span class="pre">implement</span>
+<span class="pre">the</span> <span class="pre">``Singleton</span></tt> pattern. However, savage overridings as the one in
+this example, are not a good idea, since it will confuse everybody.
+This is an example where metaclasses change the semantics: whereas
+usually the notation <tt class="literal"><span class="pre">C()</span></tt> means &quot;creates a C instance&quot;, the
+metaclass can give to the syntax <tt class="literal"><span class="pre">C()</span></tt> any meaning we want.
+Here there is both the power and the danger of metaclasses: they
+allows to make both miracles and disasters. Nevertheless, used with
+a grain of salt, they provide a pretty nice convenience.</p>
+<p>Anyway, overriding the '__call__' method of the metaclass can be
+confusing, since parenthesis are usually reserved to mean instantion,
+therefore I will prefere to pass arguments trough brackets.</p>
+<p>The beauty and the magic of metaclasses stays in the fact that this mechanism
+is completely general: since metaclasses themselves are classes, we can
+'CallableWithBrackets' to pass arguments to a metaclass, i.e.
+'CallableWithBrackets' can also be used as a meta-metaclass!</p>
+<p>I leave as an exercise for the reader to figure out
+how to define meta-meta-metaclasses, meta-meta-meta-metaclasses, etc.
+etc. (there is no limit to the abstraction level you can reach with
+metaclasses;-)</p>
+<p>Let me show an example: a magical way of making methods cooperative.
+This can be done trough a 'Cooperative' metaclass that inherits from
+'BracketCallable' and therefore has 'BracketCallable.__metaclass__'
+as (meta)metaclass:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Cooperative(BracketCallable,type):
+ &quot;&quot;&quot;Bracket-callable metaclass implementing cooperative methods. Works
+ well for plain methods returning None, such as __init__&quot;&quot;&quot;
+ def __init__(cls,*args):
+ methods=cls.bracket_args
+ for meth in methods:
+ setattr(cls,meth,cls.coop_method(meth,vars(cls).get(meth)))
+ def coop_method(cls,name,method): # method can be None
+ &quot;&quot;&quot;Calls both the superclass method and the class method (if the
+ class has an explicit method). Implemented via a closure&quot;&quot;&quot;
+ def _(self,*args,**kw):
+ getattr(super(cls,self),name)(*args,**kw) # call the supermethod
+ if method: method(self,*args,**kw) # call the method
+ return _
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The code above works for methods returing <tt class="literal"><span class="pre">None</span></tt>, such as <tt class="literal"><span class="pre">__init__</span></tt>.
+Here I give a first example of application: a hierarchy where the <tt class="literal"><span class="pre">__init__</span></tt>
+methods are automatically called (similar to automatic initialization
+in Java).</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;cooperative.py&gt;
+
+from oopp import Cooperative
+
+class B(object):
+ &quot;&quot;&quot;Cooperative base class; all its descendants will automagically
+ invoke their ancestors __init__ methods in chain.&quot;&quot;&quot;
+ __metaclass__=Cooperative['__init__']
+ def __init__(self,*args,**kw):
+ print &quot;This is B.__init__&quot;
+
+class C(B):
+ &quot;Has not explicit __init__&quot;
+
+class D(C):
+ &quot;&quot;&quot;The metaclass makes D.__init__ to call C.__init__ and
+ therefore B.__init__&quot;&quot;&quot;
+ def __init__(self,*args,**kw):
+ print &quot;This is D.__init__&quot;
+
+d=D()
+
+print &quot;The metaclass of B is&quot;,type(B)
+print &quot;The meta-metaclass of B is&quot;, type(type(B))
+
+#&lt;/cooperative.py&gt;
+</pre>
+</blockquote>
+<p>Output:</p>
+<blockquote>
+<pre class="literal-block">
+This is B.__init__
+This is D.__init__
+The metaclass of B is &lt;class 'oopp.Cooperative'&gt;
+The meta-metaclass of B is &lt;class 'oopp.__metaclass__'&gt;
+</pre>
+</blockquote>
+<p>A second example, is the following, an alternative way of
+making the paleoanthropological hierarchy of chapter 4 cooperative:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;paleo.py&gt;
+
+from oopp import Cooperative,Homo
+
+class HomoHabilis(Homo):
+ __metaclass__=Cooperative['can']
+ def can(self):
+ print &quot; - make tools&quot;
+
+class HomoSapiens(HomoHabilis):
+ def can(self):
+ print &quot; - make abstractions&quot;
+
+class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ print &quot; - make art&quot;
+
+HomoSapiensSapiens().can()
+
+# Output:
+
+# &lt;HomoSapiensSapiens&gt; can:
+# - make tools
+# - make abstractions
+# - make art
+
+#&lt;/paleo.py&gt;
+</pre>
+</blockquote>
+<p>Metaclasses can be used to violate the old good rule &quot;explicit is
+better than implicit&quot;. Looking at the source code for 'HomoSapiens'
+and 'HomoSapiensSapiens' one would never imagine the <tt class="literal"><span class="pre">can</span></tt> is
+somewhat special. That is why in the following I will prefer to
+use the anonymous super call mechanism, which is explicit, instead
+of the implicit cooperative mechanism.</p>
+</div>
+<div class="section" id="meta-functions">
+<h2><a class="toc-backref" href="#id123" name="meta-functions">Meta-functions</a></h2>
+<p>The third and deepest secret of the <tt class="literal"><span class="pre">__metaclass__</span></tt> hook is that, even if
+it is typically used in conjunction with metaclasses, actually the hook
+can refer to generic class factories callable with the signature
+<tt class="literal"><span class="pre">(name,bases,dic)</span></tt>. Let me show a few examples
+where <tt class="literal"><span class="pre">__metaclass__</span></tt> is a function or a generic callable object
+instead of being a metaclass:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;metafun.py&gt;
+
+from oopp import kwdict
+
+class Callable(object):
+ def __call__(self,name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+callableobj=Callable()
+
+class C: __metaclass__=callableobj
+
+print &quot;type of C:&quot;,C.__class__
+
+def f(name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+class D: __metaclass__=f
+
+print &quot;type of D:&quot;,D.__class__
+
+class B(object):
+ def __metaclass__(name,bases,dic):
+ &quot;&quot;&quot;In this form, the __metaclass__ attribute is a function.
+ In practice, it works as a special static method analogous
+ to __new__&quot;&quot;&quot;
+ print &quot;name: &quot;, name
+ print &quot;bases:&quot;, bases
+ print &quot;dic:\n&quot;,kwdict(dic)
+ return type(name,bases,dic)
+
+class E(B): pass
+
+print &quot;type of E:&quot;,E.__class__
+print &quot;Non-called E.__metaclass__:&quot;, E.__metaclass__
+
+#&lt;/metafun.py&gt;
+</pre>
+</blockquote>
+<p>With output</p>
+<blockquote>
+<pre class="literal-block">
+C ()
+__metaclass__ = &lt;Callable object at 0x401c964c&gt;
+__module__ = __builtin__
+type of C: &lt;type 'type'&gt;
+D ()
+__metaclass__ = &lt;function f at 0x401c4994&gt;
+__module__ = __builtin__
+type of D: &lt;type 'type'&gt;
+name: B
+bases: (&lt;type 'object'&gt;,)
+dic:
+__metaclass__ = &lt;function __metaclass__ at 0x401c4a3c&gt;
+__module__ = __builtin__
+type of E: &lt;type 'type'&gt;
+Non-called E.__metaclass__: &lt;unbound method E.__metaclass__&gt;
+</pre>
+</blockquote>
+<p>The advantage/disadvantage of this solution is that the <tt class="literal"><span class="pre">__metaclass__</span></tt>
+hook is called only once, i.e. it is not called again if a new class
+is derived from the original one. For instance in this example 'E' is
+derived from 'B', but the function <tt class="literal"><span class="pre">B.__metaclass__</span></tt> is <em>not</em> called
+during the creation of 'E'.</p>
+<p>Metafunctions can also be used when one does not want to transmit the
+metaclass contraint. Therefore they usage is convenient in exactly
+the opposite situation of a cooperative metaclass.</p>
+</div>
+<div class="section" id="anonymous-cooperative-super-calls">
+<h2><a class="toc-backref" href="#id124" name="anonymous-cooperative-super-calls">Anonymous cooperative super calls</a></h2>
+<p>As I noticed in the previous chapters, the <tt class="literal"><span class="pre">super</span></tt>
+mechanism has an annoying
+problem: one needs to pass explicitely the name of the base class. Typically,
+this is simply an
+inelegance since it is annoying to be forced to retype the name of the base
+class. However, in particular
+cases, it can be a problem. This happens for instance if we try to
+pass the class's methods to a different class: one cannot do that,
+since the methods contains an explicit reference to the original class
+and would not work with the new one. Moreover, having named super calls
+is annoying in view of refactoring. Consider for
+instance the previous <tt class="literal"><span class="pre">supernew.py</span></tt> script: in the <tt class="literal"><span class="pre">__new__</span></tt> method
+defined inside the class 'B', we called <tt class="literal"><span class="pre">Super</span></tt> with the syntax
+<tt class="literal"><span class="pre">Super(B,cls)</span></tt> by repeating the name of the class 'B'. Now,
+if in the following I decide to give to 'B' a more descriptive
+name, I have to go trough the source, search all the <tt class="literal"><span class="pre">super</span></tt>
+calls, and change them accordingly to the new name. It would be
+nice having Python do the job for me. A first solution is to call
+<tt class="literal"><span class="pre">super</span></tt> (or <tt class="literal"><span class="pre">Super</span></tt>) with the syntax <tt class="literal"><span class="pre">super(self.__this,obj)</span></tt>,
+where the special name <tt class="literal"><span class="pre">__this</span></tt> is explicitly replaced by the name
+of the class where the call is defined by the 'reflective' function
+of last chapter. This approach has the disadvantage that each time we
+derive a new class, we need to invoke <em>explicitely</em> the routine
+<tt class="literal"><span class="pre">reflective</span></tt>. It would be marvelous to instruct Python to invoke
+<tt class="literal"><span class="pre">reflective</span></tt> automatically at each class creation. Actually, this
+seems to be deep magic and indeed it is: fortunately, a custom metaclass
+can perform this deep magic in few lines:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Reflective(type):
+ &quot;&quot;&quot;Cooperative metaclass that defines the private variable __this in
+ its instances. __this contains a reference to the class, therefore
+ it allows anonymous cooperative super calls in the class.&quot;&quot;&quot;
+ def __init__(cls,*args):
+ super(Reflective,cls).__init__(*args)
+ reflective(cls)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me show how 'Reflective' can be used in a practical example.</p>
+<p>By deriving new metaclasses from 'Reflective', one can easily
+create powerful class factories that generate reflective classes.</p>
+<p>Suppose I want to define a handy class
+factory with the abilitity of counting the number of its instances.</p>
+<p>This can be done by noticing that metaclasses are just classes, therefore
+they can be composed with regular classes in multiple inheritance. In
+particular one can derive a 'Logged' metaclass from 'WithLogger': in
+this way we send a message to a log file each time a new class is created.
+This can be done by composing 'WithLogger' with 'WithMultiCounter.__metaclass__'
+and with 'Reflective':</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Logged(WithLogger,Reflective):
+ &quot;&quot;&quot;Metaclass that reuses the features provided by WithLogger. In particular
+ the classes created by Logged are Reflective, PrettyPrinted
+ and Customizable.&quot;&quot;&quot; #WithLogger provides logfile and verboselog
+ def __init__(cls,*args,**kw):
+ super(Logged,cls).__init__(*args,**kw)
+ bases=','.join([c.__name__ for c in cls.__bases__])
+ print &gt;&gt; cls.logfile, &quot;%s is a child of %s&quot; % (cls,bases)
+ print &gt;&gt; cls.logfile,'and an instance of %s' % type(cls).__name__
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The MRO is</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print MRO(Logged)
+MRO of Logged:
+ 0 - Logged(WithLogger,Reflective)
+ 1 - WithLogger(WithCounter,PrettyPrinted)
+ 2 - WithCounter(object)
+ 3 - PrettyPrinted(object)
+ 4 - Reflective(type)
+ 5 - type(object)
+ 6 - object()
+</pre>
+</blockquote>
+<p>and the inheritance graph can be drawn as follows:</p>
+<blockquote>
+<pre class="literal-block">
+ _____________________ object 6 ___
+ / / \
+2 WithCounter 3 PrettyPrinted type 5
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ 1 WithLogger Reflective 4
+ \ /
+ \ /
+ \ /
+ \ /
+ Logged 0
+ :
+ :
+ C1
+</pre>
+</blockquote>
+<p>'WithCounter' acts now as a metaclass, since WithCounter.__new__ invokes
+type.__new__. Since <tt class="literal"><span class="pre">type.__new__</span></tt> is non-cooperative,
+in the composition of a metaclass with a regular class, the metaclass
+should be put first: this guarantees that <tt class="literal"><span class="pre">__new__</span></tt> derives from
+<tt class="literal"><span class="pre">type.__new__</span></tt>, thus avoiding the error message.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Logged.verboselog=True
+&gt;&gt;&gt; C1=Logged('C1',(),{})
+*****************************************************************************
+Tue Apr 22 18:47:05 2003
+1. Created 'C1'
+with accessibile non-special attributes:
+_C1__this = 'C1'
+'C1' is a child of object
+and an instance of Logged
+</pre>
+</blockquote>
+<p>Notice that any instance of 'WithCounterReflective' inherits the 'WithCounter'
+attribute <tt class="literal"><span class="pre">counter</span></tt>, that counts the number of classes that have been
+instantiated (however it is not retrieved by <tt class="literal"><span class="pre">dir</span></tt>; moreover the
+instances of 'WithCounterReflective' instances have no <tt class="literal"><span class="pre">counter</span></tt> attribute).</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C1.counter
+1
+</pre>
+</blockquote>
+</div>
+<div class="section" id="more-on-metaclasses-as-class-factories">
+<h2><a class="toc-backref" href="#id125" name="more-on-metaclasses-as-class-factories">More on metaclasses as class factories</a></h2>
+<p>A slight disadvantage of the approach just described,
+is that 'Logged' cooperatively invokes the <tt class="literal"><span class="pre">type.__new__</span></tt>
+static method, therefore, when we invoke the metaclass, we must explicitly
+provide a name, a tuple of base classes and a dictionary, since the
+<tt class="literal"><span class="pre">type.__new__</span></tt> staticmethod requires that signature. Actually,
+the expression</p>
+<blockquote>
+<pre class="literal-block">
+C=Logged(name,bases,dic)
+</pre>
+</blockquote>
+<p>is roughly syntactic sugar for</p>
+<blockquote>
+<pre class="literal-block">
+C=Logged.__new__(Logged,name,bases,dic)
+assert isinstance(C,Logged)
+Logged.__init__(C,name,bases,dic)
+</pre>
+</blockquote>
+<p>If a different interface is desired, the best way is to use a class
+factory 'ClsFactory' analogous to the object factory 'Makeobj'
+defined in chapter 4. It is convenient to make 'ClsFactory'
+bracket-callable.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class ClsFactory(BracketCallable):
+ &quot;&quot;&quot;Bracket callable non-cooperative class acting as
+ a factory of class factories.
+
+ ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+ . They automatically converts functions to static methods
+ if the input object is not a class. If an explicit name is not passed
+ the name of the created class is obtained by adding an underscore to
+ the name of the original object.&quot;&quot;&quot;
+
+ returnclass=False # ClsFactory[X] returns an *instance* of ClsFactory
+
+ def __call__(self, *args):
+ &quot;&quot;&quot;Generates a new class using self.meta and avoiding conflicts.
+ The first metaobject can be a dictionary, an object with a
+ dictionary (except a class), or a simple name.&quot;&quot;&quot;
+
+ # default attributes
+ self.name=&quot;CreatedWithClsFactory&quot;
+ self.bases=()
+ self.dic={}
+ self.metas=self.bracket_args
+
+ if len(args)==1:
+ arg=args[0]
+ if isinstance(arg,str): # is a name
+ self.name=arg
+ elif hasattr(arg,'__name__'): # has a name
+ self.name=arg.__name__+'_'
+ self.setbasesdic(arg)
+ elif len(args)==2:
+ self.name=args[0]
+ assert isinstance(self.name,str) # must be a name
+ self.setbasesdic(args[1])
+ elif len(args)==3: # must be name,bases,dic
+ self.name=args[0]
+ self.bases+=args[1]
+ self.dic.update(args[2])
+ if len(args)&lt;3 and not self.bases: # creating class from a non-class
+ for k,v in self.dic.iteritems():
+ if isfunction(v): self.dic[k]=staticmethod(v)
+ #return child(*self.bases,**vars(self))
+ return makecls(*self.metas)(self.name,self.bases,self.dic)
+
+ def setbasesdic(self,obj):
+ if isinstance(obj,tuple): # is a tuple
+ self.bases+=obj
+ elif hasattr(obj,'__bases__'): # is a class
+ self.bases+=obj.__bases__
+ if isinstance(obj,dict): # is a dict
+ self.dic.update(obj)
+ elif hasattr(obj,&quot;__dict__&quot;): # has a dict
+ self.dic.update(obj.__dict__)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>'ClsFactory[X]' where 'X' is a metaclass returns callable objects acting as
+class factories. For instance</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+Class=ClsFactory[type] # generates non-conflicting classes
+Mixin=ClsFactory[Reflective] # generates reflective classes
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>can be used as a class factories that automatically provides a default name,
+base classes and dictionary, and avoids meta-type conflicts.
+'Mixin' generates reflective classes that can be used as mixin in multiple
+inheritance hierarchies. Here I give few example of usage of 'Class':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; C1,C2,C3=[Class('C'+str(i+1)) for i in range(3)]
+&gt;&gt;&gt; C1
+&lt;class 'oopp.C1'&gt;
+&gt;&gt;&gt; C2
+&lt;class 'oopp.C2'&gt;
+&gt;&gt;&gt; C3
+&lt;class 'oopp.C3'&gt;]
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; Clock=Class('Clock',{'get_time':get_time})
+&gt;&gt;&gt; Clock
+&lt;class 'oopp.Clock'&gt;
+&gt;&gt;&gt; Clock.get_time()
+16:01:02
+</pre>
+</blockquote>
+<p>Another typical usage of 'Class' is the conversion of a module in a class:
+for instance</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; time_=Class(time)
+&gt;&gt;&gt; time_
+&lt;class 'oopp.time_'&gt;
+</pre>
+</blockquote>
+<p>Notice the convention of adding an underscore to the name of the class
+generated from the 'time' module.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; time_.asctime()
+'Mon Jan 20 16:33:21 2003'
+</pre>
+</blockquote>
+<p>Notice that all the functions in the module <tt class="literal"><span class="pre">time</span></tt> has been magically
+converted in staticmethods of the class <tt class="literal"><span class="pre">time_</span></tt>. An advantage of this
+approach is that now the module is a class and can be enhanced with
+metaclasses: for instance we could add tracing capabilities, debugging
+features, etc.</p>
+<p>By design, 'Class' and 'Reflective' also works when the first argument
+is a class or a tuple of base classes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ClsFactory_=Class(ClsFactory)
+&gt;&gt;&gt; type(ClsFactory_)
+&lt;class 'oopp.__metaclass__'&gt;
+&gt;&gt;&gt; ClsFactory_=Mixin(ClsFactory)
+&gt;&gt;&gt; type(ClsFactory_) # automagically generated metaclass
+&lt;class 'oopp._Reflective__metaclass__'&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="programming-with-metaclasses">
+<h2><a class="toc-backref" href="#id126" name="programming-with-metaclasses">Programming with metaclasses</a></h2>
+<p>In order to how a non-trivial application of metaclasses in real life,
+let me come back to the pizza shop example discussed in chapter 4 and 6.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def Pizza(toppings,**dic):
+ &quot;&quot;&quot;This function produces classes inheriting from GenericPizza and
+ WithLogger, using a metaclass inferred from Logged&quot;&quot;&quot;
+ toppinglist=toppings.split()
+ name='Pizza'+''.join([n.capitalize() for n in toppinglist])
+ dic['toppinglist']=toppinglist
+ return ClsFactory[Logged](name,
+ (GenericPizza,WithLogger,WithMultiCounter),dic)
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; Margherita=Pizza('tomato mozzarella',verboselog=True)
+*****************************************************************************
+Tue May 13 14:42:17 2003
+1. Created 'PizzaTomatoMozzarella'
+with accessibile non-special attributes:
+ResetsCounter = &lt;class 'oopp.ResetsCounter'&gt;
+_GenericPizza__this = &lt;class 'oopp.GenericPizza'&gt;
+_WithCounter__this = &lt;class 'oopp.WithCounter'&gt;
+_WithLogger__this = &lt;class 'oopp.WithLogger'&gt;
+baseprice = 1
+counter = 0
+formatstring = %s
+logfile = &lt;open file '&lt;stdout&gt;', mode 'w' at 0x402c2058&gt;
+price = &lt;unbound method PizzaTomatoMozzarella.price&gt;
+sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+topping_unit_price = 0.5
+toppinglist = ['tomato', 'mozzarella']
+toppings_price = &lt;unbound method PizzaTomatoMozzarella.toppings_price&gt;
+verboselog = True
+'PizzaTomatoMozzarella' is a child of GenericPizza,WithLogger,
+WithMultiCounter and an instance of _LoggedResetsCounter
+</pre>
+</blockquote>
+<p>Notice the <em>deep</em> magic: <tt class="literal"><span class="pre">Pizza</span></tt> invokes <tt class="literal"><span class="pre">ClsFactory[Logged]</span></tt> which in
+turns calls the class factory <tt class="literal"><span class="pre">child</span></tt> that creates 'Margherita' from
+'GenericPizza', 'WithLogger' and 'WithMultiCounter' by using the
+metaclass 'Logged': however, since 'WithMultiCounter', has the internal
+metaclass 'ResetsCounter' , there is a metatype conflict:
+<tt class="literal"><span class="pre">child</span></tt> <em>automagically</em> solves the conflict by creating the metaclass
+'_LoggedResetsCounter' that inherits both from 'Logged' and 'ResetsCounter'.
+At this point, 'Margherita' can be safely created
+by '_LoggedResetsCounter'. As such, the creation of 'Margherita'
+will be registered in the log file and 'Margherita' (with all its
+children) will continue to be able to recognize the special identifier
+<tt class="literal"><span class="pre">this</span></tt>.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print Margherita('large')
+*****************************************************************************
+Tue May 13 14:47:03 2003
+1. Created large pizza with tomato,mozzarella, cost $ 6.0
+with accessibile non-special attributes:
+ResetsCounter = &lt;class 'oopp.ResetsCounter'&gt;
+_GenericPizza__this = &lt;class 'oopp.GenericPizza'&gt;
+_WithCounter__this = &lt;class 'oopp.WithCounter'&gt;
+_WithLogger__this = &lt;class 'oopp.WithLogger'&gt;
+baseprice = 1
+counter = 1
+formatstring = %s
+logfile = &lt;open file '&lt;stdout&gt;', mode 'w' at 0x402c2058&gt;
+price = &lt;bound method PizzaTomatoMozzarella.price of
+&lt;oopp.PizzaTomatoMozzarella object at 0x4032764c&gt;&gt;
+size = large
+sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+topping_unit_price = 0.5
+toppinglist = ['tomato', 'mozzarella']
+toppings_price = &lt;bound method PizzaTomatoMozzarella.toppings_price of
+&lt;oopp.PizzaTomatoMozzarella object at 0x4032764c&gt;&gt;
+verboselog = True
+large pizza with tomato,mozzarella, cost $ 6.0
+&gt;&gt;&gt; print MRO(Margherita)
+MRO of PizzaTomatoMozzarella:
+ 0 - PizzaTomatoMozzarella(GenericPizza,WithLogger)[_LoggedResetsCounter]
+ 1 - GenericPizza(object)
+ 2 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 3 - WithMultiCounter(WithCounter)[ResetsCounter]
+ 4 - WithCounter(object)
+ 5 - PrettyPrinted(object)
+ 6 - object()
+</pre>
+</blockquote>
+<p>Notice that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print Margherita
+'PizzaTomatoMozzarella'
+</pre>
+</blockquote>
+<p>The power of inheritance in this example is quite impressive, since
+I have reused the same class 'WithLogger' (and its children) both in the
+metaclass hierarchy and in the regular hierarchy: this means that I have added
+logging capabilities both to classes and their instances in a
+strike! And there is no confusion between the two. For instance,
+there is a <tt class="literal"><span class="pre">counter</span></tt> attribute for the metaclass 'Logged'
+and many independent <tt class="literal"><span class="pre">counter</span></tt> attributes for any generated class,
+i.e. for any kind of pizza.</p>
+<blockquote>
+It is interesting to notice that '' itself is an instance of
+its inner metaclass, as <tt class="literal"><span class="pre">type()</span></tt> would show. This technique
+avoids the need for inventing a new name for the metaclass. The inner
+metaclass is automatically inherited by classes inheriting from the outer
+class.</blockquote>
+</div>
+<div class="section" id="metaclass-aided-operator-overloading">
+<h2><a class="toc-backref" href="#id127" name="metaclass-aided-operator-overloading">Metaclass-aided operator overloading</a></h2>
+<p>As we discussed in chapter 4, inheriting from built-in types is generally
+painful. The problem is that if P is a primitive class, i.e. a
+Python built-in type, and D=D(P) is a derived class, then the
+primitive methods returning P-objects have to be modified (wrapped) in
+such a way to return D-objects.</p>
+<p>The problem is expecially clear in the context of operator overloading.</p>
+<p>Consider for instance the problem of defining a 'Vector' class in the
+mathematical sense. Mathematically-speaking, vectors are defined as objects
+that can be summed each other and multiplied by numbers; they can be represented
+by (finite or infinite) sequences. In the case of finite sequences, vectors
+can be represented with lists and a vector class can be naturally
+implemented by subclassing <tt class="literal"><span class="pre">list</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;vector.py&gt;
+
+class Vector(list):
+ &quot;&quot;&quot;Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)&quot;&quot;&quot;
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [el*scalar for el in self]
+ def __rmul__(self,scalar):
+ return [scalar*el for el in self]
+
+v=Vector([1,0])
+w=Vector([0,1])
+
+print v+w, type(v+w)
+print 2*v, type(2*v)
+print v*2, type(v*2)
+
+#&lt;/vector.py&gt;
+</pre>
+</blockquote>
+<p>With output</p>
+<blockquote>
+<pre class="literal-block">
+[1, 1] &lt;type 'list'&gt;
+[2, 0] &lt;type 'list'&gt;
+[2, 0] &lt;type 'list'&gt;
+</pre>
+</blockquote>
+<p>The problem is that the overloaded methods must be wrapped in such a way
+to return <tt class="literal"><span class="pre">Vector</span></tt> object and not <tt class="literal"><span class="pre">list</span></tt> object; moreover, if
+<tt class="literal"><span class="pre">Vector</span></tt> is subclassed (for instance by defining a <tt class="literal"><span class="pre">NumericVector</span></tt>),
+the overloaded methods must return instances of the subclass. There is
+only one way of doing that automatically: trough the magic of metaclasses.</p>
+<p>Here is the solution, involving an <tt class="literal"><span class="pre">autowrappedmethod</span></tt> descriptor class,
+that wraps the overloaded operators and is automatically invoked by
+the metaclass <tt class="literal"><span class="pre">AutoWrapped</span></tt>.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class autowrappedmethod(wrappedmethod):
+ &quot;&quot;&quot;Makes the method returning cls instances, by wrapping its
+ output with cls&quot;&quot;&quot;
+ klass=None # has to be fixed dynamically from outside
+ def __init__(self,meth):
+ super(autowrappedmethod,self).__init__(meth) # cooperative
+ self.klass=self.klass # class variable -&gt; instance variable
+ def wrapper(self): # closure
+ return lambda *args,**kw: self.klass(self.func(*args,**kw))
+
+class AutoWrapped(type):
+ &quot;&quot;&quot;Metaclass that looks at the methods declared in the attributes
+ builtinlist and wraplist of its instances and wraps them with
+ autowrappedmethod.&quot;&quot;&quot;
+ def __init__(cls,name,bases,dic):
+ super(AutoWrapped,cls).__init__(name,bases,dic) # cooperative
+ cls.builtinlist=getattr(cls,'builtinlist',[])
+ if not hasattr(cls,'diclist') : # true only at the first call
+ cls.diclist=[(a,vars(bases[0])[a]) for a in cls.builtinlist]
+ if dic.has_key('wraplist'): # can be true at any call
+ cls.diclist+=[(a,dic[a]) for a in cls.wraplist]
+ wrapper=autowrappedmethod.With(klass=cls)
+ d=dict([(a,wrapper(v)) for a,v in cls.diclist])
+ customize(cls,**d)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now the <tt class="literal"><span class="pre">Vector</span></tt> class can be written as</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Vector(list):
+ &quot;&quot;&quot;Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)&quot;&quot;&quot;
+ __metaclass__=AutoWrapped
+ wraplist='__add__ __radd__ __mul__ __rmul__'.split()
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [scalar*el for el in self]
+ def __rmul__(self,scalar):
+ return [el*scalar for el in self]
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here the <tt class="literal"><span class="pre">AutoWrapped</span></tt> metaclass wraps the output of <tt class="literal"><span class="pre">__add__,</span>
+<span class="pre">__radd__,</span> <span class="pre">__mul__,</span> <span class="pre">__rmul__</span></tt>, guaranteeing that they returns <tt class="literal"><span class="pre">Vector</span></tt>
+instances or instances of some subclass of <tt class="literal"><span class="pre">Vector</span></tt>, if <tt class="literal"><span class="pre">Vector</span></tt> is
+subclassed. This is an example of usage:</p>
+<blockquote>
+<!-- doctest -->
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Vector
+&gt;&gt;&gt; v=Vector([1,0])
+&gt;&gt;&gt; v
+&lt;oopp.Vector object at 0x4032858c&gt;
+&gt;&gt;&gt; w=Vector([0,1])
+&gt;&gt;&gt; v+2*w
+&lt;oopp.Vector object at 0x403190ac&gt;
+&gt;&gt;&gt; print v+2*w
+[1, 2]
+</pre>
+</blockquote>
+<p>It should be clear by now that metaclasses are the natural framework where
+to discuss operator overloading
+(at least in languages that have metaclasses ;-). After all, operator
+overloading is another kind of (very nice) syntactic sugar and we know
+already that metaclasses are very good when we need syntactic sugar.</p>
+</div>
+</div>
+<div class="section" id="advanced-metaprogramming-techniques">
+<h1><a class="toc-backref" href="#id128" name="advanced-metaprogramming-techniques">ADVANCED METAPROGRAMMING TECHNIQUES</a></h1>
+<p>In elementary OOP, the programmer works with objects; in advanced OOP,
+the programmer works with classes, taking full advantage of
+(multiple) inheritance and metaclasses. Metaprograming is the activity of
+building, composing and modifying classes.</p>
+<p>I will give various examples of metaprogramming techniques
+using run-time class modifications
+multiple inheritance, metaclasses, attribute descriptors and
+even simple functions.</p>
+<p>Moreover, I will show show metaclasses can change the
+semantics of Python programs: hence theire reputation of <em>black</em> magic.
+That is to say that the techniques explained here are dangerous!</p>
+<div class="section" id="on-code-processing">
+<h2><a class="toc-backref" href="#id129" name="on-code-processing">On code processing</a></h2>
+<p>It is a good programming practice to avoid the direct modification
+of source code. Nevertheless, there are situations where the ability of
+modifying the source code <em>dynamically</em> is invaluable. Python has the
+capability of</p>
+<ol class="arabic simple">
+<li>generating new code from scratch;</li>
+<li>modifying pre-existing source code;</li>
+<li>executing the newly created/modified code at run-time.</li>
+</ol>
+<p>The capability of creating source code and executing it <em>immediately</em> has
+no equivalent in static languages such as C/C++/Java and it is maybe the
+most poweful feature of dynamics languages such as Java/Python/Perl.
+This feature has been exploited to its ultimate consequences in the languages
+of the Lisp family, in which one can use incredibly poweful macros, which
+in a broad sense, are programs that write themselves</p>
+<p>In this chapter I will discuss how to implement macros in Python and I will
+present some of the miracles you may perform with this technique. To this
+aim, I will discuss various ways of manipulating Python source code, by
+using regular expressions and state machines.</p>
+</div>
+<div class="section" id="regular-expressions">
+<h2><a class="toc-backref" href="#id130" name="regular-expressions">Regular expressions</a></h2>
+<blockquote>
+<pre class="line-block">
+<em>Some people, when confronted with a problem,
+think &quot;I know, I'll use regular expressions.&quot;
+Now they have two problems.</em>
+ -- Jamie Zawinski
+</pre>
+</blockquote>
+<p>Python source code is a kind of text and can manipulated with the same
+techniques that are used to manipulate text:</p>
+<ol class="arabic simple">
+<li>the trivial search and replace;</li>
+<li>regular expressions;</li>
+<li>state machines;</li>
+<li>parsers</li>
+</ol>
+<p>There is not very much to say about the search and replace methods: it
+is fast, efficient and it works. It should always be used whenever
+possible. However, in this chapter I will only be interested in cases
+where something more sophisticated than a plain search and replace is
+needed. Cases that can be managed with regular expressions
+or with something even more sophisticated than them: a state machine or
+even a full featured parser.</p>
+<p>I will <em>not</em> give a primer on regular expression here, since they are
+already well documented in the standard documentation (see Andrew's
+Kuchling 'Howto') as well in many books (for instance 'Mastering Regular
+Expression' first edition and 'Python in a Nutshell').
+Instead, I will give various practical examples of usage.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import re
+&gt;&gt;&gt; reobj=re.compile(r'x')
+</pre>
+</blockquote>
+</div>
+<div class="section" id="more-on-metaclasses-and-subclassing-built-in-types">
+<h2><a class="toc-backref" href="#id131" name="more-on-metaclasses-and-subclassing-built-in-types">More on metaclasses and subclassing built-in types</a></h2>
+<p>Subclassing <tt class="literal"><span class="pre">list</span></tt> is easy since there are no methods returning lists
+except the methods correspondings to the '+' and '*' operators.
+Subclassing <tt class="literal"><span class="pre">str</span></tt> is more complicated, since one has many methods
+that return strings. Nevertheless, it can be done with the <tt class="literal"><span class="pre">AutoWrapped</span></tt>
+metaclass, simply by specifying the list of the builtins to be wrapped.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Str(str):
+ __metaclass__=AutoWrapped
+ builtinlist=&quot;&quot;&quot;__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
+ center expandtabs join ljust lower lstrip replace rjust rstrip strip
+ swapcase title translate upper zfill&quot;&quot;&quot;.split()
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here I show various tests.</p>
+<blockquote>
+<!-- doctest -->
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Str
+&gt;&gt;&gt; sum=Str('a')+Str('b') # check the sum
+&gt;&gt;&gt; print sum, type(sum)
+ab &lt;class 'oopp.Str'&gt;
+&gt;&gt;&gt; rprod=Str('a')*2 # check the right product
+&gt;&gt;&gt; print rprod,type(rprod)
+aa &lt;class 'oopp.Str'&gt;
+&gt;&gt;&gt; lprod=2*Str('a') # check the left product
+&gt;&gt;&gt; print lprod,type(lprod)
+aa &lt;class 'oopp.Str'&gt;
+&gt;&gt;&gt; r=Str('a').replace('a','b') # check replace
+&gt;&gt;&gt; print r,type(r)
+b &lt;class 'oopp.Str'&gt;
+&gt;&gt;&gt; r=Str('a').capitalize() # check capitalize
+&gt;&gt;&gt; print r,type(r)
+A &lt;class 'oopp.Str'&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">Str</span></tt> acts as a nice base class to built abstractions based on strings.
+In particular, regular expressions can be built on top of strings describing
+their representation (I remind that if <tt class="literal"><span class="pre">x</span></tt> is a regular expression object,
+<tt class="literal"><span class="pre">x.pattern</span></tt> is its string representation). Then, the sum of two regular
+expressions <tt class="literal"><span class="pre">x</span></tt> and <tt class="literal"><span class="pre">y</span></tt> can be defined as the sum of their string
+representation, <tt class="literal"><span class="pre">(x+y).pattern=x.pattern+y.pattern</span></tt>. Moreover, it is
+convenient to define the <tt class="literal"><span class="pre">__or__</span></tt> method of two regular expression in
+such a way that <tt class="literal"><span class="pre">(x</span> <span class="pre">|</span> <span class="pre">y).pattern=x.pattern+'|'+y.pattern</span></tt>.
+All this can be achieved trough the following class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class BaseRegexp(Str):
+
+ builtinlist=['__radd__', '__ror__']
+ wraplist=['__add__','__or__']
+
+ __add__ = lambda self,other: self.pattern + other
+ __or__ = lambda self,other: self.pattern+'|'+other
+
+ def __init__ (self,regexp):
+ &quot;Adds to str methods the regexp methods&quot;
+ reobj=re.compile(regexp)
+ for attr in dir(reobj)+['pattern']:
+ setattr(self,attr,getattr(reobj,attr))
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; aob=BaseRegexp('a')|BaseRegexp('b'); print aob
+a|b
+&gt;&gt;&gt; print pretty(attributes(aob))
+encode = &lt;built-in method encode of BaseRegexp object at 0x401b25cc&gt;
+endswith = &lt;built-in method endswith of BaseRegexp object at 0x401b25cc&gt;
+expandtabs = &lt;function _ at 0x401b69cc&gt;
+find = &lt;built-in method find of BaseRegexp object at 0x401b25cc&gt;
+findall = &lt;built-in method findall of _sre.SRE_Pattern object at 0x4019b890&gt;
+finditer = &lt;built-in method finditer of _sre.SRE_Pattern object at
+0x4019b890&gt;
+index = &lt;built-in method index of BaseRegexp object at 0x401b25cc&gt;
+isalnum = &lt;built-in method isalnum of BaseRegexp object at 0x401b25cc&gt;
+isalpha = &lt;built-in method isalpha of BaseRegexp object at 0x401b25cc&gt;
+isdigit = &lt;built-in method isdigit of BaseRegexp object at 0x401b25cc&gt;
+islower = &lt;built-in method islower of BaseRegexp object at 0x401b25cc&gt;
+isspace = &lt;built-in method isspace of BaseRegexp object at 0x401b25cc&gt;
+istitle = &lt;built-in method istitle of BaseRegexp object at 0x401b25cc&gt;
+isupper = &lt;built-in method isupper of BaseRegexp object at 0x401b25cc&gt;
+join = &lt;function _ at 0x401b6a74&gt;
+ljust = &lt;function _ at 0x401b6b1c&gt;
+lower = &lt;function _ at 0x401b6cdc&gt;
+lstrip = &lt;function _ at 0x401b6d84&gt;
+match = &lt;built-in method match of _sre.SRE_Pattern object at 0x4019b890&gt;
+pattern = ba
+replace = &lt;function _ at 0x401b6ed4&gt;
+rfind = &lt;built-in method rfind of BaseRegexp object at 0x401b25cc&gt;
+rindex = &lt;built-in method rindex of BaseRegexp object at 0x401b25cc&gt;
+rjust = &lt;function _ at 0x401ba0d4&gt;
+rstrip = &lt;function _ at 0x401ba10c&gt;
+scanner = &lt;built-in method scanner of _sre.SRE_Pattern object at 0x4019b890&gt;
+search = &lt;built-in method search of _sre.SRE_Pattern object at 0x4019b890&gt;
+split = &lt;built-in method split of _sre.SRE_Pattern object at 0x4019b890&gt;
+splitlines = &lt;built-in method splitlines of BaseRegexp object at 0x401b25cc&gt;
+startswith = &lt;built-in method startswith of BaseRegexp object at 0x401b25cc&gt;
+strip = &lt;function _ at 0x401ba25c&gt;
+sub = &lt;built-in method sub of _sre.SRE_Pattern object at 0x4019b890&gt;
+subn = &lt;built-in method subn of _sre.SRE_Pattern object at 0x4019b890&gt;
+swapcase = &lt;function _ at 0x401ba294&gt;
+title = &lt;function _ at 0x401ba4fc&gt;
+translate = &lt;function _ at 0x401ba534&gt;
+upper = &lt;function _ at 0x401ba5dc&gt;
+wraplist = ['__add__', '__radd__', '__or__', '__ror__']
+zfill = &lt;function _ at 0x401ba614&gt;
+
+#&lt;oopp.py&gt;
+
+class Regexp(BaseRegexp):
+ class __metaclass__(BaseRegexp.__metaclass__):
+ def __setattr__(cls,name,value):
+ if name==name.upper(): # all caps means regexp constant
+ if not isinstance(value,cls): value=cls(value)
+ value.name=name # set regexp name
+ BaseRegexp.__metaclass__.__setattr__(cls,name,value)
+ # basic setattr
+
+ def named(self,name=None):
+ name=getattr(self,'name',name)
+ if name is None: raise 'Unnamed regular expression'
+ return self.__class__('(?P&lt;%s&gt;%s)' % (name,self.pattern))
+
+ generateblocks=generateblocks
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The magic of <tt class="literal"><span class="pre">Regexp.__metaclass__</span></tt> allows to generate a library of
+regular expressions in an elegant way:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+r=Regexp
+
+customize(r,
+ DOTALL =r'(?s)' , # starts the DOTALL mode; must be at the beginning
+ NAME =r'\b[a-zA-Z_]\w*', # plain Python name
+ EXTNAME=r'\b[a-zA-Z_][\w\.]*', # Python name with or without dots
+ DOTNAME=r'\b[a-zA-Z_]\w*\.[\w\.]*',# Python name with (at least one) dots
+ COMMENT=r&quot;#.*?(?=\n)&quot;, # Python comment
+ QUOTED1=&quot;'.+?'&quot;, # single quoted string '
+ QUOTED2='&quot;.+?&quot;', # single quoted string &quot;
+ TRIPLEQ1=&quot;'''.+?'''&quot;, # triple quoted string '
+ TRIPLEQ2='&quot;&quot;&quot;.+?&quot;&quot;&quot;' # triple quoted string &quot;
+ )
+
+r.STRING=r.TRIPLEQ1|r.TRIPLEQ2|r.QUOTED1|r.QUOTED2
+r.CODESEP=r.DOTALL+r.COMMENT.named()|r.STRING.named()
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>The trick is in the redefinition of <tt class="literal"><span class="pre">__setattr__</span></tt>, which magically converts
+all caps attributes in <tt class="literal"><span class="pre">Regexp</span></tt> objects.</p>
+<p>The features of <tt class="literal"><span class="pre">Regexp</span></tt> can be tested with the following code:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;test_re.py&gt;
+
+&quot;&quot;&quot;This script looks at its own source code and extracts dotted names,
+i.e. names containing at least one dot, such as object.attribute or
+more general one, such as obj.attr.subattr.&quot;&quot;&quot;
+
+# Notice that dotted.names in comments and literal strings are ignored
+
+from oopp import *
+import __main__
+
+text=inspect.getsource(__main__)
+
+regexp=Regexp.CODESEP| Regexp.DOTNAME.named()
+
+print 'Using the regular expression',regexp
+
+print &quot;I have found the following dotted names:\n%s&quot; % [
+ MO.group() for MO in regexp.finditer(text) if MO.lastgroup=='DOTNAME']
+
+#&lt;/test_re.py&gt;
+</pre>
+</blockquote>
+<p>with output:</p>
+<blockquote>
+<pre class="literal-block">
+Using the regular expression (?s)(?P&lt;COMMENT&gt;#.*?(?=\n))|(?P&lt;STRING&gt;
+'''.+?'''|&quot;&quot;&quot;.+?&quot;&quot;&quot;|'.+?'|&quot;.+?&quot;)|(?P&lt;DOTNAME&gt;[a-zA-Z_]\w*\.[\w\.]*)
+I have found the following dotted names:
+['inspect.getsource', 'Regexp.CODESEP', 'Regexp.DOTNAME.named', 'MO.group',
+ 'dotname.finditer', 'MO.lastgroup']
+</pre>
+</blockquote>
+<p>Now one can define a good <tt class="literal"><span class="pre">CodeStr</span></tt> class with replacing features</p>
+<p>Let me consider for instance the solution to the problem discussed in chapter
+4, i.e. the definition of a <tt class="literal"><span class="pre">TextStr</span></tt> class able to indent and dedent
+blocks of text.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def codeprocess(code,TPO): # TPO=text processing operator
+ code=code.replace(&quot;\\'&quot;,&quot;\x01&quot;).replace('\\&quot;','\x02')
+ genblock,out = Regexp.CODESEP.generateblocks(code),[]
+ for block in genblock:
+ out.append(TPO(block))
+ out.append(genblock.next())
+ return ''.join(out).replace(&quot;\x01&quot;,&quot;\\'&quot;).replace('\x02','\\&quot;')
+
+def quotencode(text):
+ return text.replace(&quot;\\'&quot;,&quot;\x01&quot;).replace('\\&quot;','\x02')
+
+def quotdecode(text):
+ return text.replace(&quot;\x01&quot;,&quot;\\'&quot;).replace('\x02','\\&quot;')
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage: replacing 'Print' with 'print' except in
+comments and literal strings.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;codeproc.py&gt;
+
+from oopp import codeprocess
+
+wrongcode=r'''
+&quot;&quot;&quot;Code processing example: replaces 'Print' with 'print' except in
+comments and literal strings&quot;&quot;&quot;
+Print &quot;This program prints \&quot;Hello World!\&quot;&quot; # look at this line!
+'''
+
+fixPrint=lambda s: s.replace('Print','print')
+validcode=codeprocess(wrongcode,fixPrint)
+
+print 'Source code:\n',validcode
+print 'Output:\n'; exec validcode
+
+#&lt;/codeproc.py&gt;
+</pre>
+</blockquote>
+<p>with output</p>
+<blockquote>
+<pre class="literal-block">
+Source code:
+
+&quot;&quot;&quot;Code processing example: replaces 'Print' with 'print' except in
+comments and literal strings&quot;&quot;&quot;
+print &quot;Prints \&quot;Hello World!\&quot;&quot; # look at this line!
+
+Output:
+
+This program prints &quot;Hello World!&quot;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="a-simple-state-machine">
+<h2><a class="toc-backref" href="#id132" name="a-simple-state-machine">A simple state machine</a></h2>
+<p>Regular expression, however powerful, are limited in scope since they
+cannot recognize recursive structures. For instance, they cannot parse
+parenthesized expression.</p>
+<p>The simplest way to parse a parenthesized expression is to use a state
+machine.</p>
+<blockquote>
+<pre class="literal-block">
+(?:...) non-grouping
+(?P&lt;name&gt;...)
+
+(?=...) look-ahead
+(?!...) negative
+(?&lt;=...) look-behind
+(?&lt;!...) negative
+
+dec=\
+&quot;&quot;&quot;
+paren = ( .. )
+bracket = [ .. ]
+brace = { .. }
+comment = # .. \n
+&quot;&quot;&quot;
+
+actions=\
+&quot;&quot;&quot;
+reobj1 : R' .. (?&lt;!\\)' -&gt; Regexp(r''' .. ''')
+reobj2 : R&quot; .. (?&lt;!\\)&quot; -&gt; Regexp(r&quot;&quot;&quot; .. &quot;&quot;&quot;)
+string1 : (?&lt;!')'(?!') .. (?&lt;!\\)'(?!') -&gt; ''' .. '''
+string2 : (?&lt;!&quot;)&quot;(?!&quot;) .. (?&lt;!\\)&quot;(?!&quot;) -&gt; &quot;&quot;&quot; .. &quot;&quot;&quot;
+&quot;&quot;&quot;
+
+beg=0; end=1
+
+string1[beg]=r&quot;(?&lt;!')'(?!')&quot; # an isolated single quote
+string2[beg]=r'(?&lt;!&quot;)&quot;(?!&quot;)' # an isolated double quote
+string1[end]=r&quot;(?&lt;!\\)'(?!')&quot; # ending single quote
+string2[end]=r'(?&lt;!\\)&quot;(?!&quot;)' # ending double quote
+
+reobj1[beg]=r&quot;R'&quot;
+reobj2[beg]=r'R&quot;'
+reobj1[end]=string1[end] # ending single quote
+reobj2[end]=string2[end] # ending double quote
+
+actions=\
+&quot;&quot;&quot;
+reobj1 : R' .. (?&lt;!\\)' -&gt; Regexp(r''' .. ''')
+reobj2 : R&quot; .. (?&lt;!\\)&quot; -&gt; Regexp(r&quot;&quot;&quot; .. &quot;&quot;&quot;)
+string1 : (?&lt;!')'(?!') .. (?&lt;!\\)'(?!') -&gt; ''' .. '''
+string2 : (?&lt;!&quot;)&quot;(?!&quot;) .. (?&lt;!\\)&quot;(?!&quot;) -&gt; &quot;&quot;&quot; .. &quot;&quot;&quot;
+&quot;&quot;&quot;
+
+beg={}; end={}; ls=[]
+for line in decl.splitlines():
+ mode,rest=line.split(' : ')
+ s,r=rest.split(' -&gt; ')
+
+ beg[mode],end[mode]=s.split(' .. ')
+ ls.append('(?P&lt;beg_%s&gt;%s)' % (mode,beg[mode]))
+ ls.append('(?P&lt;end_%s&gt;%s)' % (mode,end[mode]))
+
+ beg2[mode],end2[mode]=r.split(' .. ')
+ ls.append(beg2[mode])
+ ls.append(end2[mode])
+
+delimiters='(%s)' % re.compile('|'.join(ls))
+splitlist=['']+delimiters.split(source)
+for delim,text in splitlist:
+ delimiters.match(delim).lastgroup
+</pre>
+</blockquote>
+</div>
+<div class="section" id="creating-classes">
+<h2><a class="toc-backref" href="#id133" name="creating-classes">Creating classes</a></h2>
+<p>TODO</p>
+</div>
+<div class="section" id="modifying-modules">
+<h2><a class="toc-backref" href="#id134" name="modifying-modules">Modifying modules</a></h2>
+<p>Metaclasses are extremely
+useful since they allows to change the behaviour of the code without
+changing the sources. For instance, suppose you have a large library written
+by others that you want to enhance in some way.</p>
+<p>Typically, it is always a bad idea to modify the sources, for many reasons:</p>
+<ul class="simple">
+<li>touching code written by others, you may introduce new bugs;</li>
+<li>you may have many scripts that requires the original version
+of the library, not the modified one;</li>
+<li>if you change the sources and then you buy the new version of the
+library, you have to change the sources again!</li>
+</ul>
+<p>The solution is to enhance the proprierties of the library at run
+time, when the module is imported, by using metaclasses.</p>
+<p>To show a concrete example, let me consider the case of the module
+<em>commands</em> in the Standard Library. This module is Unix-specific,
+and cannot be used under Windows. It would be nice to have a
+metaclass able to enhance the module in such a way that
+when it is invoked on a Windows platform, Windows specific replacement
+of the Unix functions provided in the module are used. However,
+for sake of brevity, I will only give a metaclasses that display
+a nice message in the case we are in a Window platform, without
+raising an error (one could easily implement such a behaviour,
+however).</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;recognizewindows.py&gt;
+
+import oopp,sys,commands
+
+class WindowsAware(type):
+ def __init__(cls,*args):
+ if sys.platform=='win32':
+ for key,val in vars(cls).iteritems():
+ if isinstance(val,staticmethod):
+ setattr(cls,key,staticmethod(lambda *args:
+ &quot;Sorry, you are (or I pretend you are) on Windows,&quot;
+ &quot; you cannot use the %s.module&quot; % cls.__name__))
+
+sys.platform=&quot;win32&quot; #just in case you are not on Windows
+
+commands=oopp.ClsFactory[WindowsAware](commands)
+
+print commands.getoutput('date') #cannot be executed on Windows
+
+#&lt;/recognizewindows.py&gt;
+</pre>
+</blockquote>
+<p>The output of this script is</p>
+<blockquote>
+<pre class="literal-block">
+Sorry, you are on Windows, you cannot use the commands.module
+</pre>
+</blockquote>
+<p>However, if you are on Linux and you comment out the line</p>
+<blockquote>
+<pre class="literal-block">
+sys.platform=&quot;win32&quot;
+</pre>
+</blockquote>
+<p>you will see that the script works.</p>
+<p>Notice that the line <tt class="literal"><span class="pre">commands=WindowsAware(commands)</span></tt> actually
+converts the 'commands' module in a 'commands' class, but since
+the usage is the same, this will fool all programs using the
+commands module. In this case the class factory 'WindowsAware'
+can also be thought as a module modifier. In this sense, it is
+very useful to denote the metaclass with an <em>adjective</em>.</p>
+</div>
+<div class="section" id="metaclasses-and-attribute-descriptors">
+<h2><a class="toc-backref" href="#id135" name="metaclasses-and-attribute-descriptors">Metaclasses and attribute descriptors</a></h2>
+<p>Descriptors are especially useful in conjunction with metaclasses, since
+a custom metaclass can use them as low level tools to modify the methods
+and the attributes of its instances. This allows to implement very
+sophisticated features with few lines of code.</p>
+<p>Notice, anyway, that
+even plain old function can be thought of as of descriptors.</p>
+<p>Descriptors share at least two features with metaclasses:</p>
+<ol class="arabic simple">
+<li>as metaclasses, descriptors are best used as adjectives, since they
+are intended to modify and enhance standard methods and attributes, in the
+same sense metaclasses modify and enhance standard classes;</li>
+<li>as metaclasses, descriptors can change the <em>semantics</em> of Python, i.e.
+what you see is not necessarely what you get. As such, they are a
+dangerous feature. Use them with judgement!</li>
+</ol>
+<p>Now I will show a possible application of properties.
+Suppose one has a given class with various kind of
+attributes (plain methods, regular methods, static methods,
+class methods, properties and data attributes) and she wants
+to trace to access to the data attributes (notice that the motivation
+for the following problem come from a real question asked in
+comp.lang.python). Then one needs to retrieve data
+attributes from the class and convert them in properties
+controlling their access syntax. The first problem is solved
+by a simple function</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def isplaindata(a):
+ &quot;&quot;&quot;A data attribute has no __get__ or __set__ attributes, is not
+ a built-in function, nor a built-in method.&quot;&quot;&quot;
+ return not(hasattr(a,'__get__') or hasattr(a,'__set__')
+ or isinstance(a,BuiltinMethodType) or
+ isinstance(a,BuiltinFunctionType))
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>whereas the second problem is elegantly solved by a custom metaclass:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracedaccess.py&gt;
+
+from oopp import isplaindata,inspect
+
+class TracedAccess(type):
+ &quot;Metaclass converting data attributes to properties&quot;
+ def __init__(cls,name,bases,dic):
+ cls.datadic={}
+ for a in dic:
+ if isplaindata(a):
+ cls.datadic[a]=dic[a]
+ def get(self,a=a):
+ v=cls.datadic[a]
+ print &quot;Accessing %s, value=%s&quot; % (a,v)
+ return v
+ def set(self,v,a=a):
+ print &quot;Setting %s, value=%s&quot; % (a,v)
+ cls.datadic[a]=v
+ setattr(cls,a,property(get,set))
+
+class C(object):
+ __metaclass__ = TracedAccess
+ a1='x'
+
+class D(C): # shows that the approach works well with inheritance
+ a2='y'
+
+i=D()
+i.a1 # =&gt; Accessing a1, value=x
+i.a2 # =&gt; Accessing a2, value=y
+i.a1='z' # =&gt; Setting a1, value=z
+i.a1 # =&gt; Accessing a1, value=z
+
+#&lt;/tracedaccess.py&gt;
+</pre>
+</blockquote>
+<p>In this example the metaclass looks at the plain data attributes (recognized
+thanks ot the <tt class="literal"><span class="pre">isplaindata</span></tt> function) of its instances and put them
+in the dictionary <tt class="literal"><span class="pre">cls.datadic</span></tt>. Then the original attributes are replaced
+with property objects tracing the access to them. The solution is a 4-line
+custom metaclass doing the boring job for me:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class Wrapped(Customizable,type):
+ &quot;&quot;&quot;A customizable metaclass to wrap methods with a given wrapper and
+ a given condition&quot;&quot;&quot;
+ __metaclass__=Reflective
+ wrapper=wrappedmethod
+ condition=lambda k,v: True # wrap all
+ def __init__(cls,*args):
+ super(cls.__this,cls).__init__(*args)
+ wrap(cls,cls.wrapper,cls.condition.im_func)
+
+Traced=Wrapped.With(wrapper=tracedmethod,__name__='Traced')
+Timed=Wrapped.With(wrapper=timedmethod,__name__='Timed')
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; time_=ClsFactory[Traced](time)
+&gt;&gt;&gt; print time_.asctime()
+[time_] Calling 'asctime' with arguments
+(){} ...
+-&gt; 'time_.asctime' called with result: Sun May 4 07:30:51 2003
+Sun May 4 07:30:51 2003
+</pre>
+</blockquote>
+<p>Another is</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracemain.py&gt;
+
+from oopp import ClsFactory,Traced,Reflective
+
+def f1(x): return x # nested functions
+def f2(x): return f1(x) # we want to trace
+
+f1orf2=lambda k,v : v is f1 or v is f2
+make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]
+traced=make('traced',globals())
+
+traced.f2('hello!') # call traced.f2
+
+#&lt;/tracemain.py&gt;
+</pre>
+</blockquote>
+<p>with output</p>
+<blockquote>
+<pre class="literal-block">
+[__main__] Calling 'f2' with arguments
+('hello!',){} ...
+[__main__] Calling 'f1' with arguments
+('hello!',){} ...
+-&gt; '__main__.f1' called with result: hello!
+-&gt; '__main__.f2' called with result: hello!
+</pre>
+</blockquote>
+</div>
+<div class="section" id="id46">
+<h2><a class="toc-backref" href="#id136" name="id46">Modifying hierarchies</a></h2>
+<p>Suppose one wants to enhance a pre-existing class, for instance
+by adding tracing capabilities to it. The problem is non-trivial
+since it is not enough to derive a new class from the original
+class using the 'Traced' metaclass. For instance, we could imagine of
+tracing the 'Pizza' class introduced in chapter 4 by defining</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; class TracedTomatoPizza(GenericPizza,WithLogger):
+... __metaclass__=ClsFactory[Traced]
+... toppinglist=['tomato']
+</pre>
+</blockquote>
+<p>However, this would only trace the methods of the newly defined class,
+not of the original one. Since the new class does not introduce any
+non-trivial method, the addition of 'Traced' is practically without
+any effect:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; marinara=TracedTomatoPizza('small') # nothing happens
+*****************************************************************************
+Tue Apr 15 11:00:17 2003
+1. Created small pizza with tomato, cost $ 1.5
+</pre>
+</blockquote>
+</div>
+<div class="section" id="tracing-hierarchies">
+<h2><a class="toc-backref" href="#id137" name="tracing-hierarchies">Tracing hierarchies</a></h2>
+<blockquote>
+<pre class="literal-block">
+#&lt;traceH.py&gt;
+
+from oopp import *
+
+def wrapMRO(cls,wrapped):
+ for c in cls.__mro__[:-1]:
+ wrap(c,wrapped)
+
+tracing=tracedmethod.With(logfile=file('trace.txt','w'))
+wrapMRO(HomoSapiensSapiens,tracing)
+HomoSapiensSapiens().can()
+
+#&lt;/traceH.py&gt;
+</pre>
+</blockquote>
+<p>with output in trace.txt</p>
+<blockquote>
+<pre class="literal-block">
+[HomoSapiensSapiens] Calling 'can' with arguments
+ (&lt;oopp.HomoSapiensSapiens object at 0x4020364c&gt;,){} ...
+ [HomoSapiens] Calling 'can' with arguments
+ (&lt;oopp.HomoSapiensSapiens object at 0x4020364c&gt;,){} ...
+ [HomoHabilis] Calling 'can' with arguments
+ (&lt;oopp.HomoSapiensSapiens object at 0x4020364c&gt;,){} ...
+ [Homo] Calling 'can' with arguments
+ (&lt;oopp.HomoSapiensSapiens object at 0x4020364c&gt;,){} ...
+ [PrettyPrinted] Calling '__str__' with arguments
+ (&lt;oopp.HomoSapiensSapiens object at 0x4020364c&gt;,){} ...
+ [PrettyPrinted.__str__] called with result:
+ &lt;HomoSapiensSapiens&gt;
+ [Homo.can] called with result: None
+ [HomoHabilis.can] called with result: None
+ [HomoSapiens.can] called with result: None
+[HomoSapiensSapiens.can] called with result: None
+</pre>
+</blockquote>
+</div>
+<div class="section" id="modifying-source-code">
+<h2><a class="toc-backref" href="#id138" name="modifying-source-code">Modifying source code</a></h2>
+<p>The real solution would be to derive the original class 'GenericPizza'
+from 'Traced' and not from 'object'. One could imagine of creating
+a new class inhering from 'Traced' and with all the methods of the
+original 'GenericPizza' class; then one should create copies of
+all the classes in the whole multiple inheritance hierarchy.
+This would be a little annoying, but feasable; the real problem is
+that this approach would not work with cooperative methods, since
+cooperative calls in the derived classes would invoked methods in
+the original classes, which are not traced.</p>
+<p>This is a case where the modification of the original source code is
+much more appealing and simpler that any other method: it is enough
+to perform a search and replace in the original source code, by adding
+the metaclass 'Traced', to enhance the whole multiple inheritance hierarchy.
+Let me assume that the hierarchy is contained in a module (which is
+typical case). The idea, is to generate <em>dynamically</em> a new module from the
+modified source code, with a suitable name to avoid conflicts with the
+original module. Incredibily enough, this can be done in few lines:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def modulesub(s,r,module):
+ &quot;Requires 2.3&quot;
+ name=module.__name__
+ source=inspect.getsource(module).replace(s,r)
+ dic={name: module}; exec source in dic # exec the modified module
+ module2=ModuleType(name+'2') # creates an an empty module
+ customize(module2,**dic) # populates it with dic
+ return module2
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">sub</span></tt> function, that modifies the source code of
+a given module and returns a modified module, requires Python 2.3
+to work. This is a due to a subtle bug in <tt class="literal"><span class="pre">exec</span></tt> in Python 2.2.
+Anyway, the restriction to Python 2.3 allows me to take advantage
+of one of the most elegant convenience of Python 2.3: the name in
+the <tt class="literal"><span class="pre">types</span></tt> module acts are type factories and in particular
+<tt class="literal"><span class="pre">ModuleType(s)</span></tt> returns an (empty) module named <tt class="literal"><span class="pre">s</span></tt>.
+Here is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; s='GenericPizza(object):'
+&gt;&gt;&gt; oopp2=oopp.modulesub(s,s+'\n __metaclass__=oopp.Traced',oopp)
+</pre>
+</blockquote>
+<p>Name clashes are avoided, being 'oopp2' a different module from
+'oopp'; we have simultaneously access to both the original hierarchy
+in 'oopp' (non-traced) and the modified one in 'oopp2' (traced).
+In particular 'oopp2.CustomizablePizza' is traced and therefore</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
+... __metaclass__=makecls()
+&gt;&gt;&gt; marinara=PizzaLog.With(toppinglist=['tomato'])('small')
+</pre>
+</blockquote>
+<p>gives the output</p>
+<blockquote>
+<pre class="literal-block">
+[PizzaLog] Calling '__init__' with arguments
+(&lt;oopp.PizzaLog object at 0x40470dac&gt;, 'small'){} ...
+-&gt; 'PizzaLog.__init__' called with result: None
+
+*****************************************************************************
+Thu Mar 27 09:18:28 2003
+[PizzaLog] Calling '__str__' with arguments
+(&lt;oopp.PizzaLog object at 0x40470dac&gt;,){} ...
+[PizzaLog] Calling 'price' with arguments
+(&lt;oopp.PizzaLog object at 0x40470dac&gt;,){} ...
+[PizzaLog] Calling 'toppings_price' with arguments
+(&lt;oopp.PizzaLog object at 0x40470dac&gt;,){} ...
+-&gt; 'PizzaLog.toppings_price' called with result: 0.5
+
+-&gt; 'PizzaLog.price' called with result: 1.5
+
+-&gt; 'PizzaLog.__str__' called with result: small pizza with tomato, cost $ 1.5
+
+1. Created small pizza with tomato, cost $ 1.5
+</pre>
+</blockquote>
+<p>From that we understand what is happening:</p>
+<ul class="simple">
+<li><tt class="literal"><span class="pre">PizzaLog.__init__</span></tt> calls <tt class="literal"><span class="pre">GenericPizza.__init__</span></tt> that defines size and
+cooperatively calls <tt class="literal"><span class="pre">WithLogger.__init__</span></tt></li>
+<li>WithLogger.__init__ cooperatively calls <tt class="literal"><span class="pre">WithCounter.__init__</span></tt>
+that increments the count attribute;</li>
+<li>at this point, the instruction 'print self' in <tt class="literal"><span class="pre">WithLogger.__init__</span></tt> calls
+<tt class="literal"><span class="pre">PizzaLog.__str__</span></tt> (inherited from <tt class="literal"><span class="pre">GenericPizza.__str__</span></tt>);</li>
+<li><tt class="literal"><span class="pre">GenericPizza.__str__</span></tt> calls 'price' that in turns calls
+'toppings_price'.</li>
+</ul>
+<p>On top of that, notice that the metaclass of 'PizzaLog' is
+<tt class="literal"><span class="pre">_TracedReflective</span></tt> that has been automagically generated by
+<tt class="literal"><span class="pre">makecls</span></tt> from the metaclasses of 'CustomizablePizza' (i.e. 'Traced')
+and of 'WithLogger' (i.e. 'Reflective'); the leading underscore helps
+to understand the dynamical origin of '_TracedReflective'.
+It turns out that '_TracedReflective' has a dynamically
+generated (meta-meta)class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print type(type(PizzaLog)) #meta-metaclass
+&lt;class 'oopp._WithWrappingCapabilitiesReflective'&gt;
+</pre>
+</blockquote>
+<p>Therefore this example has a non-trivial class hierarchy</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print oopp.MRO(PizzaLog)
+MRO of PizzaLog:
+ 0 - PizzaLog(CustomizablePizza,WithLogger)[Traced]
+ 1 - CustomizablePizza(GenericPizza,Customizable)[Traced]
+ 2 - GenericPizza(object)[Traced]
+ 3 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 4 - WithCounter(object)
+ 5 - Customizable(object)
+ 6 - PrettyPrinted(object)
+ 7 - object()
+</pre>
+</blockquote>
+<p>a non-trivial metaclass hierarchy,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
+MRO of Traced:
+ 0 - Traced(Reflective)[WithWrappingCapabilities]
+ 1 - Reflective(type)
+ 2 - type(object)
+ 3 - object()
+</pre>
+</blockquote>
+<p>and a non-trivial meta-metaclass hierarchy:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
+MRO of WithWrappingCapabilities:
+ 0 - WithWrappingCapabilities(BracketCallable)
+ 1 - CallableWithBrackets(type)
+ 2 - type(object)
+ 3 - object()
+</pre>
+</blockquote>
+<p>Pretty much complicated, isn't it ? ;)</p>
+<p>This example is there to show what kind of maintenance one can have
+with programs doing a large use of metaclasses, particularly, when
+they should be understood by somebody else than the autor ...</p>
+</div>
+<div class="section" id="metaclass-regenerated-hierarchies">
+<h2><a class="toc-backref" href="#id139" name="metaclass-regenerated-hierarchies">Metaclass regenerated hierarchies</a></h2>
+<blockquote>
+<pre class="literal-block">
+import types
+
+def hierarchy(self,cls):
+ d=dict([(t.__name__,t) for t in vars(types).itervalues()
+ if isinstance(t,type)])
+ def new(c):
+ bases=tuple([d[b.__name__] for b in c.__bases__])
+ return self(c.__name__, bases, c.__dict__.copy())
+ mro=list(cls.__mro__[:-1])
+ mro.reverse()
+ for c in mro:
+ if not c.__name__ in d:
+ d[c.__name__]=new(c)
+ customize(self,**d)
+
+ClsFactory.hierarchy=hierarchy
+traced=ClsFactory[Traced,Reflective]
+</pre>
+</blockquote>
+<p>Unfortunately, this approach does not work if the original hierarchy makes
+named cooperative super calls.</p>
+<p>Therefore the source-code run-time modification has its advantages.</p>
+</div>
+</div>
+<div class="section" id="the-programmable-programming-language">
+<h1><a class="toc-backref" href="#id140" name="the-programmable-programming-language">THE PROGRAMMABLE PROGRAMMING LANGUAGE</a></h1>
+<blockquote>
+<em>I think that lisp is a better applications language than Python.
+However, Python is close enough, or at least so much better than the
+alternatives, that Python's social and glue language advantages are
+often decisive.</em> -- Andy Freeman on c.l.p.</blockquote>
+<p>I go in <em>really</em> DEEP BLACK MAGIC here.</p>
+<p>Lisp has been called the <em>programmable programming language</em> <a class="footnote-reference" href="#id43" id="id47" name="id47">[22]</a>
+since its macros allow the programmer to change the <em>syntax</em> of
+the language. Python has no macros and the syntax of the language
+cannot be changed. Nevertheless, Python metaclasses allows
+to change the <em>semantics</em> of the language. In this sense, they
+are even more powerful and more dangerous than Lisp macros.
+Python metaclass allow the user to customize the language (if not
+its syntax). This is cool enough, however it can make your programs
+unreadable by others. The techniques explained in this
+chapter should be used with care. Nevertheless, I trust the judgement
+of the programmer who has been able to reach this chapter, and I don't
+mind providing him further rope to shoot in his/her foot ;)</p>
+<table class="footnote" frame="void" id="id48" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a name="id48">[24]</a></td><td>Paul Graham, 'OnLisp'
+citing</td></tr>
+</tbody>
+</table>
+<div class="section" id="enhancing-the-python-language">
+<h2><a class="toc-backref" href="#id141" name="enhancing-the-python-language">Enhancing the Python language</a></h2>
+<p>Let me start with some minor usage of metaclasses. In this section I
+will show how the user can implement in few lines features that are
+built-in in other languages, through a minimal usage of metaclasses.</p>
+<p>For instance, suppose one wants to define a class which cannot be
+derived: in Java this can be done with the &quot;final&quot; keyword.
+In Python there is no need to add a new keyword to the language:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class NonDerivableError(Exception): pass
+
+class Final(type): # better derived from WithCounter,type
+ &quot;Instances of Final cannot be derived&quot;
+ def __new__(meta,name,bases,dic):
+ try:
+ meta.already_called is True
+ except AttributeError: # not already called
+ meta.already_called=True
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else: #if already called
+ raise NonDerivableError(&quot;I cannot derive from %s&quot; % bases)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import Final
+&gt;&gt;&gt; class C:
+... __metaclass__=Final
+...
+&gt;&gt;&gt; class D(C): pass #error
+...
+NonDerivableError: D not created from (&lt;class 'oopp.C'&gt;,)
+</pre>
+</blockquote>
+<p>It is interesting to notice that a similar effect can be reached
+with a <tt class="literal"><span class="pre">singletonClass</span></tt> class factory: a 'MetaSingleton' inherits
+from <tt class="literal"><span class="pre">Singleton</span></tt> and from 'type' (therefore it is a metaclass):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class S(Singleton,type): pass
+singletonClass=ClsFactory[S]
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>If we write</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import singletonClass
+&gt;&gt;&gt; C=singletonClass()
+&gt;&gt;&gt; class D(C):
+... pass
+</pre>
+</blockquote>
+<p>we see that actually 'D' is not a new instance of 'Singleton', but
+it coincides with 'C', instead:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; id(C),id(D)
+(135622140, 135622140)
+&gt;&gt;&gt; C is D
+True
+&gt;&gt;&gt; type(C)
+&lt;class '__main__._Singleton'&gt;
+&gt;&gt;&gt; type(C).__bases__
+(&lt;class 'oopp.Singleton'&gt;, &lt;type 'type'&gt;)
+&gt;&gt;&gt; c=C(); d=D()
+&gt;&gt;&gt; id(c),id(d)
+(1075378028, 1075378924)
+</pre>
+</blockquote>
+<p>Notice the order: 'SingletonClass' must inherit from 'Singleton'
+first and from <tt class="literal"><span class="pre">Class</span></tt> second, otherwise the <tt class="literal"><span class="pre">Class.__new__</span></tt> method would
+override the <tt class="literal"><span class="pre">Singleton.__new__</span></tt>, therefore losing the 'Singleton'
+basic property of having only one instance. On the other hand, in
+the correct order, 'Singleton' first and 'Class' second, the inheritance
+diagram is</p>
+<blockquote>
+<pre class="literal-block">
+ object 5
+ (__new__)
+ / \
+ / \
+2 WithCounter type 4
+ (__new__) (__new__)
+ | |
+ | |
+1 Singleton Class 3
+ (__new__) (__new__)
+ \ /
+ \ /
+ SingletonClass 0
+ (Singleton.__new__)
+</pre>
+<pre class="literal-block">
+ object
+ / \
+ / |
+WithCounter |
+ | |
+Singleton type
+ \ /
+ \ /
+ MetaSingleton
+ :
+ :
+ : instantiation
+ :
+ :
+ C = D
+</pre>
+</blockquote>
+<p>whereas 'SingletonClass' inherits <tt class="literal"><span class="pre">Singleton.__new__</span></tt> which, trough
+the <tt class="literal"><span class="pre">super</span></tt> mechanism, calls 'type.__new__' and therefore creates
+the class 'C'. Notice that class 'D' is never created, it is simply
+an alias for 'C'.</p>
+<p>I think it is simpler to write down the class 'Final' explicitely
+(explicit is better than implicit) as I did; however a fanatic of code
+reuse could derive it from 'SingletonClass':</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;final.py&gt;
+
+from oopp import *
+
+class Final(Singleton,type):
+ &quot;Inherits the 'instance' attribute from Singleton (default None)&quot;
+ def __new__(meta,name,bases,dic):
+ if meta.counter==0: # first call
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else:
+ raise NonDerivableError(&quot;I cannot derive from %s&quot; % bases)
+
+class C: __metaclass__=Final
+
+try:
+ class D(C): pass
+except NonDerivableError,e:
+ print e
+
+#&lt;/final.py&gt;
+</pre>
+</blockquote>
+<p>The reader can check that this script has the correct output
+&quot;I cannot derive from &lt;class 'oopp.C'&gt;&quot;. I leave to the reader
+to understand the issues with trying to implement 'NonDerivable'
+from 'NonInstantiable'. #And why an inner metaclass would not work.</p>
+</div>
+<div class="section" id="restricting-python-dynamism">
+<h2><a class="toc-backref" href="#id142" name="restricting-python-dynamism">Restricting Python dynamism</a></h2>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def frozen(self,name,value):
+ if hasattr(self,name):
+ type(self).__bases__[0].__setattr__(self,name,value)
+ else:
+ raise AttributeError(&quot;You cannot add attributes to %s&quot; % self)
+
+class Frozen(object):
+ &quot;&quot;&quot;Subclasses of Frozen are frozen, i.e. it is impossibile to add
+ new attributes to them and their instances&quot;&quot;&quot;
+ __setattr__ = frozen
+ class __metaclass__(type):
+ __setattr__ = frozen
+
+#&lt;/oopp.py&gt;
+
+
+#&lt;frozen.py&gt;
+
+from oopp import *
+
+class C(Frozen):
+ c=1
+ def __init__(self):
+ #self.x=5 # won't work anymore, __new__ will be okay
+ pass
+
+class D(C):
+ d=2
+
+C.c=2
+
+print D().d
+
+#&lt;/frozen.py&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="changing-the-language-without-changing-the-language">
+<h2><a class="toc-backref" href="#id143" name="changing-the-language-without-changing-the-language">Changing the language without changing the language</a></h2>
+<p>In Lisp the user has the possibility of changing the syntax of the
+language to suit her purposes (or simply to fit her taste).
+In Python, the user cannot change the basic grammar of the language,
+nevertheless, to a great extent, metaclasses allows to emulate this effect.
+Notice that using metaclasses to this aim is not necessarely
+a good idea, since once you start
+changing the Python standard behaviour, it will become impossible for
+others to understand your programs (which is what happened to Lisp ;).</p>
+<p>Let me show how metaclasses can be used to provide notational convenience
+(i.e. syntactic sugar) for Python.</p>
+<p>As first example, I will show how we may use metaclasses to provide some
+convenient notation for staticmethods and classmethods:</p>
+<blockquote>
+<pre class="literal-block">
+class MetaSugar(type):
+ def __init__(cls,name,bases,clsdict):
+ for key,value in clsdict.iteritems():
+ if key.startswith(&quot;static_&quot;):
+ setattr(cls,key[7:],staticmethod(value))
+ elif key.startwith(&quot;class_&quot;):
+ setattr(cls,key[6:],classmethod(value))
+</pre>
+</blockquote>
+<p>The same effect can be obtained trough normal inheritance</p>
+<blockquote>
+<pre class="literal-block">
+class SyntacticSugar(object):
+ def __init__(self):
+ for k,v in self.__class__.__dict__.iteritems():
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+</pre>
+</blockquote>
+<p>Let me now implement some syntactic sugar for the __metaclass__ hook.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import re
+squarednames=re.compile('\[([A-Za-z_][\w\., ]*)\]')
+
+def inferredfromdocstring(name,bases,dic):
+ docstring=dic['__doc__']
+ match=squarednames.match(docstring)
+ if not match: return ClsFactory[Reflective](name,bases,dic)
+ metanames=[name.strip() for name in match.group(1).split(',')]
+ metaname=''.join(metanames)
+ if len(metanames)&gt;1: # creates a new metaclass
+ metaclass=type(metaname,tuple(map(eval,metanames)),{})
+ else:
+ metaclass=eval(metaname)
+ return ClsFactory[metaclass](name,bases,dic)
+
+#&lt;/oopp.py&gt;
+
+#&lt;sugar.py&gt;
+
+from oopp import *
+__metaclass__ = inferredfromdocstring
+class B:
+ &quot;Do nothing class&quot;
+
+class C:
+ &quot;[Reflective]&quot;
+ &quot; Do nothing class&quot;
+
+class D:
+ &quot;[WithLogger,Final]&quot;
+ &quot;Do nothing class&quot;
+
+class E(C):
+ pass
+
+#&lt;/sugar.py&gt;
+</pre>
+</blockquote>
+<p>With output:</p>
+<blockquote>
+<pre class="literal-block">
+*****************************************************************************
+Fri Feb 21 09:35:58 2003
+Creating class Logged_C descending from (),
+instance of &lt;class 'oopp.Logged'&gt;
+
+Logged_C dictionary:
+ __doc__ = Do nothing class
+*****************************************************************************
+Fri Feb 21 09:35:58 2003
+Creating class Logged_Final_D descending from (),
+instance of &lt;class 'oopp.LoggedFinal'&gt;
+
+Logged_Final_D dictionary:
+__doc__ = Do nothing class
+*****************************************************************************
+Fri Feb 21 09:35:58 2003
+Creating class E descending from (&lt;class 'oopp.Logged_C'&gt;,),
+instance of &lt;class 'oopp.Logged'&gt;
+
+E dictionary:
+&lt;EMPTY&gt;
+</pre>
+</blockquote>
+<p>At the end, let me point out few observations:
+Metaclasses can be used to provide syntactic sugar, as I have shown
+in the previous example. However, I have given the previous
+routines as a proof of concept: I do <em>not</em> use these routines in
+my actual code for many good reasons:</p>
+<ol class="arabic simple">
+<li>At the end a convenient notation will be provided in Python 2.4</li>
+<li>I don't want to use magic tricks on my code, I want others to
+be able to understand what the code is doing;</li>
+<li>I want to be able myself to understand my own code in six months
+from today ;)</li>
+</ol>
+<p>Anyway, I think it is a good thing to know about this potentiality
+of metaclasses, that can turn out to be very convenient in certain
+applications: but this does not mean that should be blindly used
+and/or abused. In other words: with great powers come
+great responsabilities ;)</p>
+</div>
+<div class="section" id="recognizing-magic-comments">
+<h2><a class="toc-backref" href="#id144" name="recognizing-magic-comments">Recognizing magic comments</a></h2>
+<p>In this section, I will begin to unravel the secrets of the black magic art
+of changing Python semantics and I will show that with few lines
+involving metaclasses
+and the standard library 'inspect' module, even comments can be made
+significant! (let me continue with my series &quot;how to do what should not
+be done&quot;).</p>
+<p>To this aim, I need a brief digression on regular expressions.</p>
+<blockquote>
+<pre class="literal-block">
+class RecognizesMagicComments(object):
+ form=r'def %s(NAME)(args):#!\s?staticmethod'
+ class __metaclass__(type):
+ def __new__(meta,name,bases,dic):
+ code=[]
+ for attr in dic:
+ source=inspect.getsource(dic[attr]).splitlines()
+ for line in source:
+ split=line.split('#!')
+ if len(split)==2:
+ descriptor=split[1]; code.append(split[0])
+ else: code.append(line)
+
+class C(RecognizesMagicComments):
+ #!staticmethod
+ def f(x): #!staticmethod
+ return x
+</pre>
+</blockquote>
+</div>
+<div class="section" id="interpreting-python-source-code-on-the-fly">
+<h2><a class="toc-backref" href="#id145" name="interpreting-python-source-code-on-the-fly">Interpreting Python source code on the fly</a></h2>
+<p>At this point, I can really go <em>DEEP</em> in black magic.</p>
+<blockquote>
+<pre class="literal-block">
+import sys, inspect, linecache, re
+
+def cls_source(name,module):
+ lines = linecache.getlines(inspect.getsourcefile(module))
+ if not lines: raise IOError, 'could not get source code'
+ pat = re.compile(r'^\s*class\s*' + name + r'\b')
+ for i in range(len(lines)):
+ if pat.match(lines[i]): break
+ else: raise IOError, 'could not find class definition'
+ lines, lnum = inspect.getblock(lines[i:]), i + 1
+ return ''.join(lines)
+
+class Interpreter(object):
+ def __init__(self,CPO): # possible composition of code processing opers
+ self.repl=CPO
+ def __call__(self,name,bases,dic):
+ try:
+ modulename=dic['__module__'] # module where the class is defined
+ except KeyError: # no __module__ attribute
+ raise IOError(&quot;Class %s cannot be defined dynamically or in the\n&quot;
+ &quot;interpreter and the source code cannot came from a pipe&quot;% name)
+ module=sys.modules[modulename]
+ source=self.repl(cls_source(name,module))
+ source=re.sub('__metaclass__=.*','__metaclass__=type',source)
+ #print source
+ loc={}; exec source in vars(module),loc
+ return loc[name]
+
+regexp_expand=Interpreter(regexp)
+</pre>
+</blockquote>
+</div>
+<div class="section" id="implementing-lazy-evaluation">
+<h2><a class="toc-backref" href="#id146" name="implementing-lazy-evaluation">Implementing lazy evaluation</a></h2>
+<p>At this point of our knowledge, it becomes trivial to implement lazy
+evaluation and then a ternary operator. (My original, simpler, implementation
+is posted on c.l.p.; see the thread 'PEP 312 (and thus 308) implemented
+with a black magic trick')</p>
+</div>
+<div class="section" id="implementing-a-ternary-operator">
+<h2><a class="toc-backref" href="#id147" name="implementing-a-ternary-operator">Implementing a ternary operator</a></h2>
+<blockquote>
+<pre class="literal-block">
+# module ternary.py
+
+&quot;PEP 308 and 312 implemented via a metaclass-powered dirty trick&quot;
+
+import inspect,__main__
+
+# the ternary operator:
+
+def if_(cond,f,g):
+ &quot;Short circuiting ternary operator implemented via lambdas&quot;
+ if cond: return f()
+ else: return g()
+
+# the metaclass black magic:
+
+class DirtyTrick(type):
+ &quot;&quot;&quot;Cooperative metaclass that looks at the source code of its instances
+ and replaces the string '~' with 'lambda :' before the class creation&quot;&quot;&quot;
+ def __new__(meta,name,bases,dic):
+ for attr in dic.values():
+ if inspect.isfunction(attr):
+ code=inspect.getsource(attr)
+ if code.find('~')==-1: continue # no '~' found, skip
+ code=code.replace('~','lambda :')
+ code=dedent(code)+'\n'
+ exec code in __main__.__dict__,dic # modifies dic
+ return super(DirtyTrick,meta).__new__(meta,name,bases,dic)
+
+# a convenient base class:
+
+class RecognizesImplicitLambdas:
+ &quot;Children of this class do recognize implicit lambdas&quot;
+ __metaclass__=DirtyTrick
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<blockquote>
+<pre class="literal-block">
+from ternary import if_, RecognizesImplicitLambdas
+from math import sqrt
+
+class C(RecognizesImplicitLambdas):
+ def safesqrt(self,x):
+ return if_( x&gt;0, ~sqrt(x), ~0) #short-circuiting ternary operator
+
+c=C()
+print c.safesqrt(4), c.safesqrt(-4)
+</pre>
+</blockquote>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/all.tex b/pypers/all.tex
new file mode 100755
index 0000000..e9672c4
--- /dev/null
+++ b/pypers/all.tex
@@ -0,0 +1,12166 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper]{geometry}
+%% 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}}
+% 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{OBJECT ORIENTED PROGRAMMING IN PYTHON}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={OBJECT ORIENTED PROGRAMMING IN PYTHON},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Version}: &
+ 0.5 \\
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{E-mail}: &
+ mis6@pitt.edu \\
+\textbf{Home-page}: &
+ http://www.phyast.pitt.edu/~micheles/ \\
+\textbf{Disclaimer}: &
+ I release this book to the general public.
+It can be freely distributed if unchanged.
+As usual, I don't give any warranty: while I have tried hard to ensure the
+correctness of what follows, I disclaim any responsability in case of
+errors . Use it at your own risk and peril ! \\
+\end{tabularx}
+\end{center}
+
+\setlength{\locallinewidth}{\linewidth}
+
+
+
+\hypertarget{contents}{}
+\pdfbookmark[0]{Contents}{contents}
+\subsection*{~\hfill Contents\hfill ~}
+\begin{list}{}{}
+\item {} \href{\#preface}{Preface}
+\begin{list}{}{}
+\item {} \href{\#the-philosophy-of-this-book}{The philosophy of this book}
+
+\item {} \href{\#for-who-this-book-in-intended}{For who this book in intended}
+
+\item {} \href{\#about-the-scripts-in-this-book}{About the scripts in this book}
+
+\item {} \href{\#conventions-used-in-this-book}{Conventions used in this book}
+
+\end{list}
+
+\item {} \href{\#introduction}{Introduction}
+\begin{list}{}{}
+\item {} \href{\#why-oop}{Why OOP ?}
+
+\item {} \href{\#why-python}{Why Python ?}
+
+\item {} \href{\#further-thoughts}{Further thoughts}
+
+\end{list}
+
+\item {} \href{\#first-things-first}{FIRST THINGS, FIRST}
+\begin{list}{}{}
+\item {} \href{\#what-s-an-object}{What's an object?}
+
+\item {} \href{\#objects-and-classes}{Objects and classes}
+
+\item {} \href{\#objects-have-attributes}{Objects have attributes}
+
+\item {} \href{\#objects-have-methods}{Objects have methods}
+
+\item {} \href{\#summing-objects}{Summing objects}
+
+\item {} \href{\#inspecting-objects}{Inspecting objects}
+
+\item {} \href{\#built-in-objects-iterators-and-generators}{Built-in objects: iterators and generators}
+
+\end{list}
+
+\item {} \href{\#the-convenience-of-functions}{THE CONVENIENCE OF FUNCTIONS}
+\begin{list}{}{}
+\item {} \href{\#id16}{Introduction}
+
+\item {} \href{\#a-few-useful-functions}{A few useful functions}
+
+\item {} \href{\#functions-are-objects}{Functions are objects}
+
+\item {} \href{\#profiling-functions}{Profiling functions}
+
+\item {} \href{\#about-python-speed}{About Python speed}
+
+\item {} \href{\#tracing-functions}{Tracing functions}
+
+\item {} \href{\#tracing-objects}{Tracing objects}
+
+\item {} \href{\#inspecting-functions}{Inspecting functions}
+
+\end{list}
+
+\item {} \href{\#the-beauty-of-objects}{THE BEAUTY OF OBJECTS}
+\begin{list}{}{}
+\item {} \href{\#user-defined-objects}{User defined objects}
+
+\item {} \href{\#objects-have-static-methods-and-classmethods}{Objects have static methods and classmethods}
+
+\item {} \href{\#objects-have-their-privacy}{Objects have their privacy}
+
+\item {} \href{\#objects-have-properties}{Objects have properties}
+
+\item {} \href{\#objects-have-special-methods}{Objects have special methods}
+
+\item {} \href{\#objects-can-be-called-added-subtracted}{Objects can be called, added, subtracted, ...}
+
+\end{list}
+
+\item {} \href{\#the-power-of-classes}{THE POWER OF CLASSES}
+\begin{list}{}{}
+\item {} \href{\#the-concept-of-inheritance}{The concept of inheritance}
+
+\item {} \href{\#inheritance-versus-run-time-class-modifications}{Inheritance versus run-time class modifications}
+
+\item {} \href{\#inheriting-from-built-in-types}{Inheriting from built-in types}
+
+\item {} \href{\#controlling-the-creation-of-objects}{Controlling the creation of objects}
+
+\item {} \href{\#multiple-inheritance}{Multiple Inheritance}
+
+\item {} \href{\#cooperative-hierarchies}{Cooperative hierarchies}
+
+\item {} \href{\#inheritance-and-privacy}{Inheritance and privacy}
+
+\end{list}
+
+\item {} \href{\#the-sophistication-of-descriptors}{THE SOPHISTICATION OF DESCRIPTORS}
+\begin{list}{}{}
+\item {} \href{\#motivation}{Motivation}
+
+\item {} \href{\#functions-versus-methods}{Functions versus methods}
+
+\item {} \href{\#methods-versus-functions}{Methods versus functions}
+
+\item {} \href{\#static-methods-and-class-methods}{Static methods and class methods}
+
+\item {} \href{\#properties}{Properties}
+
+\item {} \href{\#user-defined-attribute-descriptors}{User-defined attribute descriptors}
+
+\item {} \href{\#data-descriptors}{Data descriptors}
+
+\item {} \href{\#the-super-attribute-descriptor}{The \texttt{super} attribute descriptor}
+
+\item {} \href{\#method-wrappers}{Method wrappers}
+
+\end{list}
+
+\item {} \href{\#the-subtleties-of-multiple-inheritance}{THE SUBTLETIES OF MULTIPLE INHERITANCE}
+\begin{list}{}{}
+\item {} \href{\#a-little-bit-of-history-why-python-2-3-has-changed-the-mro}{A little bit of history: why Python 2.3 has changed the MRO}
+
+\item {} \href{\#the-c3-method-resolution-order}{The C3 Method Resolution Order}
+
+\item {} \href{\#examples}{Examples}
+
+\item {} \href{\#bad-method-resolution-orders}{Bad Method Resolution Orders}
+
+\item {} \href{\#understanding-the-method-resolution-order}{Understanding the Method Resolution Order}
+
+\item {} \href{\#counting-instances}{Counting instances}
+
+\item {} \href{\#the-pizza-shop-example}{The pizza-shop example}
+
+\item {} \href{\#fixing-wrong-hierarchies}{Fixing wrong hierarchies}
+
+\item {} \href{\#modifying-hierarchies}{Modifying hierarchies}
+
+\item {} \href{\#inspecting-python-code}{Inspecting Python code}
+
+\end{list}
+
+\item {} \href{\#the-magic-of-metaclasses-part-i}{THE MAGIC OF METACLASSES - PART I}
+\begin{list}{}{}
+\item {} \href{\#metaclasses-as-class-factories}{Metaclasses as class factories}
+
+\item {} \href{\#metaclasses-as-class-modifiers}{Metaclasses as class modifiers}
+
+\item {} \href{\#a-few-caveats-about-the-usage-of-metaclasses}{A few caveats about the usage of metaclasses}
+
+\item {} \href{\#metaclasses-and-inheritance}{Metaclasses and inheritance}
+
+\item {} \href{\#conflicting-metaclasses}{Conflicting metaclasses}
+
+\item {} \href{\#cooperative-metaclasses}{Cooperative metaclasses}
+
+\item {} \href{\#metamethods-vs-class-methods}{Metamethods vs class methods}
+
+\end{list}
+
+\item {} \href{\#the-magic-of-metaclasses-part-2}{THE MAGIC OF METACLASSES - PART 2}
+\begin{list}{}{}
+\item {} \href{\#the-secrets-of-the-metaclass-hook}{The secrets of the \texttt{{\_}{\_}metaclass{\_}{\_}} hook}
+
+\item {} \href{\#anonymous-inner-metaclasses}{Anonymous inner metaclasses}
+
+\item {} \href{\#passing-parameters-to-meta-classes}{Passing parameters to (meta) classes}
+
+\item {} \href{\#meta-functions}{Meta-functions}
+
+\item {} \href{\#anonymous-cooperative-super-calls}{Anonymous cooperative super calls}
+
+\item {} \href{\#more-on-metaclasses-as-class-factories}{More on metaclasses as class factories}
+
+\item {} \href{\#programming-with-metaclasses}{Programming with metaclasses}
+
+\item {} \href{\#metaclass-aided-operator-overloading}{Metaclass-aided operator overloading}
+
+\end{list}
+
+\item {} \href{\#advanced-metaprogramming-techniques}{ADVANCED METAPROGRAMMING TECHNIQUES}
+\begin{list}{}{}
+\item {} \href{\#on-code-processing}{On code processing}
+
+\item {} \href{\#regular-expressions}{Regular expressions}
+
+\item {} \href{\#more-on-metaclasses-and-subclassing-built-in-types}{More on metaclasses and subclassing built-in types}
+
+\item {} \href{\#a-simple-state-machine}{A simple state machine}
+
+\item {} \href{\#creating-classes}{Creating classes}
+
+\item {} \href{\#modifying-modules}{Modifying modules}
+
+\item {} \href{\#metaclasses-and-attribute-descriptors}{Metaclasses and attribute descriptors}
+
+\item {} \href{\#id46}{Modifying hierarchies}
+
+\item {} \href{\#tracing-hierarchies}{Tracing hierarchies}
+
+\item {} \href{\#modifying-source-code}{Modifying source code}
+
+\item {} \href{\#metaclass-regenerated-hierarchies}{Metaclass regenerated hierarchies}
+
+\end{list}
+
+\item {} \href{\#the-programmable-programming-language}{THE PROGRAMMABLE PROGRAMMING LANGUAGE}
+\begin{list}{}{}
+\item {} \href{\#enhancing-the-python-language}{Enhancing the Python language}
+
+\item {} \href{\#restricting-python-dynamism}{Restricting Python dynamism}
+
+\item {} \href{\#changing-the-language-without-changing-the-language}{Changing the language without changing the language}
+
+\item {} \href{\#recognizing-magic-comments}{Recognizing magic comments}
+
+\item {} \href{\#interpreting-python-source-code-on-the-fly}{Interpreting Python source code on the fly}
+
+\item {} \href{\#implementing-lazy-evaluation}{Implementing lazy evaluation}
+
+\item {} \href{\#implementing-a-ternary-operator}{Implementing a ternary operator}
+
+\end{list}
+
+\end{list}
+
+\setcounter{chapter}{-1}
+
+%___________________________________________________________________________
+
+\hypertarget{preface}{}
+\pdfbookmark[0]{Preface}{preface}
+\section*{Preface}
+\begin{quote}
+\begin{flushleft}
+\emph{There~is~only~one~way~to~learn:~trough~examples}
+\end{flushleft}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-philosophy-of-this-book}{}
+\pdfbookmark[1]{The philosophy of this book}{the-philosophy-of-this-book}
+\subsection*{The philosophy of this book}
+
+This book is written with the intent to help the programmer going trough
+the fascinating concepts of Object Oriented Programming (OOP), in their
+Python incarnation. Notice that I say to help, not to teach. Actually,
+I do not think that a book can teach OOP or any other non-trivial matter
+in Computer Science or other disciplines. Only the
+practice can teach: practice, then practice, and practice again.
+You must learn yourself from your experiments, not from the books.
+Nevertheless, books are useful. They cannot teach, but they can help.
+They should give you new ideas that you was not thinking about, they should
+show tricks you do not find in the manual, and in general they should be of
+some guidance in the uphill road to knowledge. That is the philosophy
+of this book. For this reason
+
+1. It is not comprehensive, not systematic;
+it is intended to give ideas and basis: from
+that the reader is expected to cover the missing part on his own,
+browsing the documentation, other sources and other books, and finally
+the definite autority, the source itself.
+
+2. It will not even try to teach the \emph{best} practices. I will show what you can
+do with Python, not what you ``should'' do. Often I will show solutions that are
+not recommended. I am not a mammy saying this is
+good, this is bad, do this do that.
+
+3. You can only learn from your failures. If you think ``it should work, if I do
+X and Y'' and it works, then you have learned nothing new.
+You have merely verified
+that your previous knowledge was correct, but you haven't create a new
+knowledge. On the other hand, when you think ``it should work, if I do
+X and Y'' and it doesn't, then you have learned that your previous knowlegde
+was wrong or incomplete, and you are forced to learn something new to
+overcome the difficulty. For this reason, I think it is useful to report
+not only how to do something, but also to report how not to do something,
+showing the pitfalls of wrong approaches.
+
+That's in my opinion is the goal of a good book. I don't know if have
+reached this goal or not (the decision is up to the reader), but at least
+I have tried to follow these guidelines.
+
+Moreover, this is not a book on OOP,
+it is a book on OOP \emph{in Python}.
+
+In other words, the point of view of this book is not
+to emphasize general topics of OOP that are exportable to other languages,
+but exactly the opposite: I want to emphasize specific techniques that one
+can only use in Python, or that are difficult to translate to other
+languages. Moreover, I will not provide comparisons with other
+languages (except for the section ``Why Python?'' in this introduction and
+in few selected other places),
+in order to keep the discussion focused.
+
+This choice comes from the initial motivation for this book, which was
+to fulfill a gap in the (otherwise excellent) Python documentation.
+The problem is that the available documentation still lacks an accessible
+reference of the new Python 2.2+ object-oriented features.
+Since myself I have learned Python and OOP from scratch,
+I have decided to write this book in order to fill that gap and
+help others.
+
+The emphasis in this book is not in giving
+solutions to specific problems (even if most of the recipes of this book
+can easily be tailored to solve real life concrete problems), it is in
+teaching how does it work, why it does work in some cases and why does
+not work in some other cases. Avoiding too specific problems has an
+additional bonus, since it allows me to use \emph{short} examples (the majority
+of the scripts presented here is under 20-30 lines) which I think are
+best suited to teach a new matter [\hyperlink{id2}{1}] . Notice, however, that whereas
+the majority of the scripts in this book are short, it is also true
+that they are pretty \emph{dense}. The density is due to various reasons:
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1}.}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+I am defining a lot of helper functions and classes, that are
+reused and enhanced during all the book.
+
+\item {}
+I am doing a strong use of inheritance, therefore a script at the
+end of the book can inherits from the classes defined through all
+the book;
+
+\item {}
+A ten line script involving metaclasses can easily perform the equivalent
+of generating hundreds of lines of code in a language without metaclasses
+such as Java or C++.
+
+\end{list}
+
+To my knowledge, there are no other books covering the same topics with
+the same focus (be warned, however, that I haven't read so many Python
+books ;-). The two references that come closest to the present book are
+the \texttt{Python Cookbook} by Alex Martelli and David Ascher, and
+Alex Martelli's \texttt{Python in a Nutshell}. They are quite recent books and
+therefore it covers (in much less detail) some of the 2.2 features that are
+the central topics to this book.
+However, the Cookbook reserves to OOP only one chapter and has a quite
+different philosophy from the present book, therefore there is
+practically no overlapping. Also \texttt{Python in a Nutshell} covers
+metaclasses in few pages, whereas half of this book is essentially
+dedied to them. This means that you can read both ;-)
+\begin{figure}[b]\hypertarget{id2}[1]
+Readers that prefer the opposite philosophy of using longer,
+real life-like, examples, have already the excellent ``Dive into
+Python'' book \href{http://diveintopython.org/}{http://diveintopython.org/} at their disposal. This is
+a very good book that I certainly recommend to any (experienced)
+Python programmer; it is also freely available (just like this ;-).
+However, the choice of arguments is quite different and there is
+essentially no overlap between my book and ``Dive into Python''
+(therefore you can read both ;-).
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{for-who-this-book-in-intended}{}
+\pdfbookmark[1]{For who this book in intended}{for-who-this-book-in-intended}
+\subsection*{For who this book in intended}
+
+I have tried to make this tutorial useful to a large public of Pythonistas,
+i.e. both people with no previous experience of Object Oriented Programming
+and people with experience on OOP, but unfamiliar with the most
+recent Python 2.2-2.3 features (such as attribute descriptors,
+metaclasses, change of the MRO in multiple inheritance, etc).
+However, this is not a book for beginners: the non-experienced reader should
+check (at least) the Internet sites www.python.org/newbies.com and
+www.awaretek.com, that provide a nice collection of resources for Python
+newbies.
+
+These are my recommendations for the reader, according to her/his level:
+\newcounter{listcnt2}
+\begin{list}{\arabic{listcnt2}.}
+{
+\usecounter{listcnt2}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+If you are an absolute beginner, with no experience on programming,
+this book is \emph{not} for you (yet ;-). Go to
+\href{http://www.python.org/doc/Newbies.html}{http://www.python.org/doc/Newbies.html} and read one of the introductive
+texts listed there, then come back here. I recommend ``How to Think Like
+a Computer Scientist'', available for free on the net (see
+\href{http://www.ibiblio.org/obp/thinkCSpy/}{http://www.ibiblio.org/obp/thinkCSpy/}); I found it useful myself when
+I started learning Python; be warned, however, that it refers to the rather
+old Python version 1.5.2. There are also excellent books
+on the market (see \href{http://www.awaretek.com/plf.html}{http://www.awaretek.com/plf.html}).
+\href{http://www.uselesspython.com/}{http://www.uselesspython.com/} is a good resource to find recensions
+about available Python books. For free books, look at
+\href{http://www.tcfb.com/freetechbooks/bookphyton.html}{http://www.tcfb.com/freetechbooks/bookphyton.html} .
+This is \emph{not} another Python tutorial.
+
+\item {}
+If you know already (at least) another programming language, but you don't
+know Python, then this book is \emph{not} for you (again ;-). Read the FAQ, the
+Python Tutorial and play a little with the Standard Library (all this
+material can be downloaded for free from \href{http://www.python.org}{http://www.python.org}), then
+come back here.
+
+\item {}
+If you have passed steps 1 and 2, and you are confortable with Python
+at the level of simple procedural programming, but have no clue about
+objects and classes, \emph{then} this book is for you. Read this book till
+the end and your knowledge of OOP will pass from zero to a quite advanced
+level (hopefully). Of course, you will have to play with the code in
+this book and write a lot of code on your own, first ;-)
+
+\item {}
+If you are confortable with Python and you also known OOP from other
+languages or from earlier version of Python, then this book is for
+you, too: you are ready to read the more advanced chapters.
+
+\item {}
+If you are a Python guru, then you should read the book, too. I expect
+you will find the errors and send me feedback, helping me to improve
+this tutorial.
+
+\end{list}
+
+
+%___________________________________________________________________________
+
+\hypertarget{about-the-scripts-in-this-book}{}
+\pdfbookmark[1]{About the scripts in this book}{about-the-scripts-in-this-book}
+\subsection*{About the scripts in this book}
+
+All the scripts in this book are free. You are expected to play
+with them, to modify them and to improve them.
+
+In order to facilitate the extraction of the scripts from the main text, both
+visually for the reader and automatically for Python, I use the
+convention of sandwiching the body of the example scripts in blocks like this
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<myfirstscript.py>}\\
+\mbox{}\\
+\mbox{print~"Here~Starts~the~Python~Way~to~Object~Oriented~Programming~!"}\\
+\mbox{}\\
+\mbox{{\#}</myfirstscript.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+You may extract the source of this script with the a Python program
+called ``test.py'' and provided in the distribution. Simply give the
+following command:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python~test.py~myfirstscript.py}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This will create a file called ``myfirstscript.py'', containing the
+source of \texttt{myfirstscript.py}; moreover it will execute the script
+and write its output in a file called ``output.txt''. I have tested
+all the scripts in this tutorial under Red Hat Linux 7.x and
+Windows 98SE. You should not have any problem in running them,
+but if a problem is there, ``test.py'' will probably discover it,
+even if, unfortunately, it will not provide the solution :-(.
+Notice that test.py requires Python 2.3+ to work, since most of
+the examples in this book heavily depends on the new features
+introduced in Python 2.2-2.3. Since the installation of Python
+2.3 is simple, quick and free, I think I am requiring to my readers
+who haven't upgraded yet a very little effort. This is well worth
+the pain since Python 2.3 fixes few bugs of 2.2 (notably in the subject of
+attribute descriptors and the \texttt{super} built-in) that makes
+
+You may give more arguments to test.py, as in this example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python~test.py~myfirstscript.py~mysecondscript.py}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of both scripts will still be placed in the file ``output.txt''.
+Notice that if you give an argument which is not the name of a script in the
+book, it will be simply ignored. Morever, if you will not give any argument,
+``test.py'' will automatically executes all the tutorial scripts, writing their
+output in ``output.txt'' [\hyperlink{id4}{2}] . You may want to give a look at this file, once
+you have finished the tutorial. It also contains the source code of
+the scripts, for better readability.
+
+Many examples of this tutorial depend on utility functions defined
+in a external module called \texttt{oopp} (\texttt{oopp} is an obvious abbreviation
+for the title of the tutorial). The module \texttt{oopp} is automatically generated
+by ``test.py'', which works by extracting from the tutorial
+text blocks of code of the form \texttt{{\#}<oopp.py> something {\#}</oopp.py>}
+and saving them in a file called ``oopp.py''.
+Let me give an example. A very recent enhancement to Python (in
+Python 2.3) has been the addition of a built-in boolean type with
+values True and False:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python}\\
+\mbox{Python~2.3a1~({\#}1,~Jan~~6~2003,~10:31:14)}\\
+\mbox{[GCC~2.96~20000731~(Red~Hat~Linux~7.2~2.96-108.7.2)]~on~linux2}\\
+\mbox{Type~"help",~"copyright",~"credits"~or~"license"~for~more~information.}\\
+\mbox{>>>~1+1==2}\\
+\mbox{True}\\
+\mbox{>>>~1+1==3}\\
+\mbox{False}\\
+\mbox{>>>~type(True)}\\
+\mbox{<type~'bool'>}\\
+\mbox{>>>~type(False)}\\
+\mbox{<type~'bool'>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+However, previous version of Python use the integers 1 and 0 for
+True and False respectively.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python}\\
+\mbox{Python~2.2~({\#}1,~Apr~12~2002,~15:29:57)}\\
+\mbox{[GCC~2.96~20000731~(Red~Hat~Linux~7.2~2.96-109)]~on~linux2}\\
+\mbox{Type~"help",~"copyright",~"credits"~or~"license"~for~more~information.}\\
+\mbox{>>>~1+1==2}\\
+\mbox{1}\\
+\mbox{>>>~1+1==3~}\\
+\mbox{0}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Following the 2.3 convension, in this tutorial I will use the names
+\texttt{True} and \texttt{False} to denotes the numbers 1 and 0 respectively.
+This is automatic in Python 2.2.1+, but not in Python 2.2. Therefore,
+for sake of compatibility, it is convenient to set the values \texttt{True}
+and \texttt{False} in our utility module:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~{\_}{\_}builtin{\_}{\_}}\\
+\mbox{try:~}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.True~~~{\#}look~if~True~is~already~defined}\\
+\mbox{except~AttributeError:~{\#}~if~not~add~True~and~False~to~the~builtins}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.True~=~1}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.False~=~0}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<mysecondscript.py>}\\
+\mbox{}\\
+\mbox{import~oopp}\\
+\mbox{print~"True~=",True,}\\
+\mbox{print~"False~=",False}\\
+\mbox{}\\
+\mbox{{\#}</mysecondscript.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output is ``True = 1 False = 0'' under Python 2.2 and
+``True = True False = False'' under Python 2.3+.
+\begin{figure}[b]\hypertarget{id4}[2]
+``test.py'', invoked without arguments, does not create '.py' files,
+since I don't want to kludge the distribution with dozens of ten-line
+scripts. I expect you may want to save only few scripts as standalone
+programs, and cut and paste the others.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{conventions-used-in-this-book}{}
+\pdfbookmark[1]{Conventions used in this book}{conventions-used-in-this-book}
+\subsection*{Conventions used in this book}
+
+Python expressions are denoted with monospaced fonts when in the text.
+Sections marked with an asterisk can be skipped in a first reading.
+Typically they have the purpose of clarifying some subtle point and
+are not needed for the rest of the book. These sections are intended
+for the advanced reader, but could confuse the beginner.
+An example is the section about the difference between methods and
+functions, or the difference between the inheritance constraint and
+the metaclass constraint.
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\pdfbookmark[0]{Introduction}{introduction}
+\section*{Introduction}
+\begin{quote}
+\begin{flushleft}
+\emph{A~language~that~doesn't~affect~the~way~you~think~about~programming,~\\
+is~not~worth~knowing.}~--~Alan~Perlis
+\end{flushleft}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-oop}{}
+\pdfbookmark[1]{Why OOP ?}{why-oop}
+\subsection*{Why OOP ?}
+
+I guess some of my readers, like me, have started programming in the mid-80's,
+when traditional (i.e. non object-oriented) Basic and Pascal where popular as
+first languages. At the time OOP was not as pervasive in software development
+how it is now, most of the mainstream languages were non-object-oriented and
+C++ was just being released. That was a time when the transition from
+spaghetti-code to structured code was already well accomplished, but
+the transition from structured programming to (the first phase of)
+OOP was at the beginning.
+
+Nowaydays, we live in a similar time of transition . Today, the transition
+to (the first phase of) OOP is well accomplished and essentially all
+mainstream
+languages support some elementary form of OOP. To be clear, when I say
+mainstream langauges, I have in mind Java and C++: C is a remarkable
+exception to the rule, since it is mainstream but not object-oriented.
+
+However, both Java an C++ (I mean standard Java and C++, not special
+extension like DTS C++, that have quite powerful object oriented features)
+are quite poor object-oriented languages: they provides only the most
+elementary aspects of OOP, the features of the \emph{first phase} of OOP.
+
+Hence, today the transition to the \emph{second phase} of OOP is only at the
+beginning, i.e mainstream language are not yet really OO, but they will
+become OOP in the near future.
+
+By second phase of OOP I mean the phase in which the primary
+objects of concern for the programmer are no more the objects, but the
+metaobjects. In elementary OOP one works on objects, which have attributes
+and methods (the evolution of old-fashioned data and functions) defined
+by their classes; in the second phase of OOP one works on classes
+which behavior is described by metaclasses. We no more modify objects
+trough classes: nowadays we modify classes and class hierarchies
+through metaclasses and multiple inheritance.
+
+It would be tempting to represent the history of programming in the last
+quarter of century with an evolutionary table like that:
+
+\begin{longtable}[c]{|p{0.30\locallinewidth}|p{0.25\locallinewidth}|p{0.27\locallinewidth}|p{0.10\locallinewidth}|}
+\hline
+\textbf{
+{\textasciitilde}1975
+} & \textbf{
+{\textasciitilde}1985
+} & \textbf{
+{\textasciitilde}1995
+} & \textbf{
+{\textasciitilde}2005
+} \\ \hline
+\endhead
+%[visit_tbody]
+
+procedural programming
+ &
+OOP1
+ &
+OOP2
+ &
+?
+ \\ \hline
+
+data,functions
+ &
+objects,classes
+ &
+classes,metaclasses
+ &
+?
+ \\ \hline
+%[depart_tbody]
+\end{longtable}
+
+The problem is that table would be simply wrong, since in truth
+Smalltalk had metaclasses already 25 years ago! And also Lisp
+had \emph{in nuce} everything a long \emph{long} time ago.
+The truth is that certains languages where too much ahead of their
+time ;-)
+
+Therefore, today we already have all the ideas
+and the conceptual tools to go beyond the first phase of OOP
+(they where invented 20-30 years ago), nevertheless those ideas are
+not yet universally known, nor implemented in mainstream languages.
+
+Fortunately, there are good languages
+where you can access the bonus of the second phase of OOP (Smalltalk, CLOS,
+Dylan, ...): unfortunately
+most of them are academic and/or little known in the real world
+(often for purely commercial reasons, since typically languages are not
+chosen accordingly to their merits, helas!). Python is an exception to this
+rule, in the sense that it is an eminently practical language (it started
+as a scripting language to do Operating System administrative jobs),
+which is relatively known and used in that application niche (even if some
+people \emph{wrongly} think that should not be used for 'serious' things).
+
+There are various reasons why most mainstream languages are rather
+poor languages, i.e. underfeatured languages (as Java) or powerful, but too
+tricky to use, as C++. Some are good reasons (for instance \emph{efficiency}: if
+efficiency is the first concern, then poor languages can be much
+better suited to the goal: for instance Fortran for number crunching
+and C for system programming), some are less good (economical
+monopoly). There is nothing to do against these reasons: if you
+need efficiency, or if you are forced to use a proprietary language
+because it is the language used by your employer. However, if you
+are free from these restrictions, there is another reason why you
+could not choose to use a poweful language. The reason is that,
+till now, programmers working in the industrial world mostly had simple
+problems (I mean conceptually simple problems). In order to solve
+simple problems one does not need a powerful language, and the effort
+spent in learning it is not worth.
+
+However, nowadays the situations has changed. Now, with Internet and graphics
+programming everywhere, and object-oriented languages so widespread,
+now it is the time when actually people \emph{needs} metaprogramming, the
+ability to changing classes and programs. Now everybody is programming
+in the large.
+
+In this situation, it is justified to spend some time to learn better
+way of programming. And of course, it is convenient to start from
+the language with the flattest learning curve of all.
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-python}{}
+\pdfbookmark[1]{Why Python ?}{why-python}
+\subsection*{Why Python ?}
+\begin{quote}
+\begin{flushleft}
+\emph{In~many~ways,~it's~a~dull~language,~borrowing~solid~old~concepts~from~~\\
+many~other~languages~{\&}~styles:~~boring~syntax,~unsurprising~semantics,~\\
+few~automatic~coercions,~etc~etc.~~But~that's~one~of~the~things~I~like~\\
+about~it.}~~--Tim~Peters~on~Python,~16~Sep~93
+\end{flushleft}
+\end{quote}
+
+If you are reading this book, I assume you already have some experience
+with Python. If this is the case, you already know the obvious advantages
+of Python such as readability, easy of use and short development time.
+Nevertheless, you could only have used Python as a fast and simple
+scripting language. If you are in this situation, then your risk to
+have an incorrect opinion on the language like ``it is a nice little
+language, but too simple to be useful in 'real' applications''. The
+truth is that Python is designed to be \emph{simple}, and actually it
+is; but by no means it is a ``shallow'' language. Actually, it goes
+quite \emph{deep}, but it takes some time to appreciate this fact.
+
+Let me contrast Python with Lisp, for instance. From the beginning,
+Lisp was intended to be a language for experts, for people with difficult
+problems to solve. The first
+users of Lisp were academicians, professors of CS and scientists.
+On the contrary, from the beginning Python
+was intended to be language for everybody (Python predecessor was ABC,
+a language invented to teach CS to children). Python makes great a first
+language for everybody, whereas Lisp would require especially
+clever and motivated students (and we all know that there is lack
+of them ;-)
+
+From this difference of origins, Python inherits an easy to learn syntax,
+whereas Lisp syntax is horrible for the beginner (even if not as
+horrible as C++ syntax ;-)
+\begin{quote}
+\begin{flushleft}
+\emph{Macros~are~a~powerful~extension~to~weak~languages.~\\
+Powerful~languages~don't~need~macros~by~definition.}~~\\
+--~Christian~Tismer~on~c.l.p.~(referring~to~C)
+\end{flushleft}
+\end{quote}
+
+Despite the differences, Python borrows quite a lot from Lisp and it
+is nearly as expressive as it (I say nearly since Python is
+not as powerful as Lisp: by tradition, Lisp has always been on the top of
+hierarchy of programming language with respect to power of abstraction).
+It is true that Python lacks some powerful Lisp features: for instance
+Python object model lacks multiple dispatching (for the time being ;-)
+and the language lacks Lisp macros (but this unlikely to change in the
+near future since Pythonistas see the lack of macro as a Good Thing [\hyperlink{id6}{3}]):
+nevertheless, the point is that Python is much \emph{much} easier to learn.
+You have (nearly) all the power, but without the complexity.
+
+One of the reasons, is that Python
+try to be as \emph{less} innovative as
+possible: it takes the proven good things from others, more innovative
+languages, and avoids their pitfalls. If you are an experienced
+programmer , it will be even easier to you to learn Python, since
+there is more or less nothing which is really original to Python.
+For instance:
+\newcounter{listcnt3}
+\begin{list}{\arabic{listcnt3}.}
+{
+\usecounter{listcnt3}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+the object model is took from languages that are good at it, such
+as Smalltalk;
+
+\item {}
+multiple inheritance has been modeled from languages good in it. such
+as CLOS and Dylan;
+
+\item {}
+regular expression follows the road opened by Perl;
+
+\item {}
+functional features are borrowed from functional languages;
+
+\item {}
+the idea of documentation strings come from Lisp;
+
+\item {}
+list comprehension come from Haskell;
+
+\item {}
+iterators and generators come from Icon;
+
+\item {}
+etc. etc. (many other points here)
+
+\end{list}
+
+I thinks the really distinctive feature of Python with respect to
+any other serious language I know, is that Python is \emph{easy}. You have the
+power (I mean power in conceptual sense, not computational power: in
+the sense of computational power the best languages are
+non-object-oriented ones)
+of the most powerful languages with a very little investement.
+In addition to that, Python has a relatively large user base
+(as compared to Smalltalk or Ruby, or the various fragmented Lisp
+communities). Of course,
+there is quite a difference between the user base of Python with
+respect to the user base of, let say, VisualBasic or Perl. But
+I would never take in consideration VisualBasic for anything serious,
+whereas Perl is too ugly for my taste ;-).
+Finally, Python is \emph{practical}. With this I mean the fact that
+Python has libraries that
+allow the user to do nearly everything, since you can access all the C/C++
+libraries with little or no effort, and all the Java libraries, though the
+Python implementation known as Jython. In particular, one has the choice
+between many excellent GUI's trough PyQt, wxPython, Tkinter, etc.
+
+Python started as an Object Oriented Programming
+Languages from the beginning, nevertheless is was never intended to be
+a \emph{pure} OOPL as SmallTalk or, more recently, Ruby. Python is a
+\emph{multiparadigm}
+language such a Lisp, that you choose your programming style according
+to your problem: spaghetti-code, structured programming, functional
+programming, object-oriented programming are all supported. You can
+even write bad code in Python, even if it is less simple than in other
+languages ;-). Python is a language which has quite evolved in its twelve
+years of life (the first public release was released in February 1991)
+and many new features have been integrated in the language with time.
+In particular, Python 2.2 (released in 2002) was a major breakthrough
+in the history of the language
+for what concerns support to Object Oriented Programming (OOP).
+Before the 2.2 revolution, Python Object
+Orientation was good; now it is \emph{excellent}. All the fundamental features
+of OOP, including pretty sophisticated ones, as metaclasses and multiple
+inheritance, have now a very good support (the only missing thing is
+multiple dispatching).
+\begin{figure}[b]\hypertarget{id6}[3]
+Python lacks macros for an intentional design choice: many people
+in the community (including Guido itself) feel that macros are
+``too powerful''. If you give the user the freedom to create her
+own language, you must face at least three problems: i) the risk
+to split the original language in dozens of different dialects;
+ii) in collaborative projects, the individual programmer must
+spend an huge amount of time and effort would be spent in learning
+macro systems written by others; iii) not all users are good
+language designers: the programmer will have to fight with badly
+designed macro systems. Due to these problems, it seems unlikely
+that macros will be added to Python in the future.
+\end{figure}
+\begin{figure}[b]\hypertarget{id7}[4]
+For a good comparison between Python and Lisp I remind the reader to
+the excellent Peter Norvig's article in
+\href{http://www.norvig.com/python-lisp.html}{http://www.norvig.com/python-lisp.html}
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{further-thoughts}{}
+\pdfbookmark[1]{Further thoughts}{further-thoughts}
+\subsection*{Further thoughts}
+
+Actually, the principal reasons why I begun studying
+Python was the documentation and the newsgroup: Python has an outstanding
+freely available documentation and an incredibly helpful newsgroup that
+make extremely easy to learn the language. If I had found a comparable
+free documentation/newsgroup for C++ or Lisp, I would have studied that
+languages instead.
+
+Unfortunately, the enormous development at the software level, had no
+correspondence with with an appropriate development of documentation.
+As a consequence, the many beatiful, powerful and extremely \emph{useful}
+new features of Python 2.2+ object orientation are mostly remained
+confined to developers and power users: the average Python programmer
+has remained a little a part from the rapid development and she
+\emph{wrongly} thinks she has no use for the new features. There have
+also been \emph{protestations} of the users against developers of the
+kind ``please, stop adding thousands of complicated new extensions
+to the language for which we have no use'' !
+
+Extending a language is always a delicate thing to do, for a whole
+bunch of reasons:
+\newcounter{listcnt4}
+\begin{list}{\arabic{listcnt4}.}
+{
+\usecounter{listcnt4}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+once one extension is done, it is there \emph{forever}.
+
+\end{list}
+
+My experience has been the following.
+
+When I first read about metaclasses, in Guido's essay
+``Unifying types and classes in Python 2.2'', I thought ``Wow,
+classes of classes, cool concept, but how useful is it?
+Are metaclasses really providing some new functionality?
+What can I do with metaclasses that I cannot do without?''
+
+Clearly, in these terms, the question is rather retorical, since in principle
+any Turing-complete programming languages contains all the features provided
+by metaclasses. Python metaclasses themselves are implemented in C, that has
+no metaclasses. Therefore, my real question was not ``What can I do
+with metaclasses that I cannot do without?'' but ``How big is the convenience
+provided by metaclasses, with respect to my typical applications?''.
+
+The answer depends on the kind of problem you are considering. For certain
+classes of problems it can be \emph{very} large, as I will show in this and in
+the next chapters.
+
+I think the biggest advantage of metaclasses is \emph{elegance}. Altough it
+is true that most of what you can do with metaclasses, can be done without
+metaclasses, not using metaclasses can result in a much \emph{uglier} solution.
+
+One needs difficult problems in order to appreciate the advantage
+of powerful methods.
+
+If all you need is to write few scripts for copying two or three files,
+there is no point in learning OOP.On the other hand, if you only
+write simple programs where you define only one of two classes, there
+is no point in using metaclasses. Metaclasses becomes relevant only
+when you have many classes, whole classes of classes with similar
+features that you want to modify.
+
+In this sense, metaprogramming is for experts only, i.e. with people
+with difficult problems. The point however, is that nowaydays,
+many persons have difficult problems.
+
+Finally, let me conclude this preface by recalling the
+gist of Python wisdom.
+\begin{quote}
+\begin{verbatim}>>> import this
+The Zen of Python, by Tim Peters
+.
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it.
+Although that way may not be obvious at first unless you're Dutch.
+Now is better than never.
+Although never is often better than *right* now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{first-things-first}{}
+\pdfbookmark[0]{FIRST THINGS, FIRST}{first-things-first}
+\section*{FIRST THINGS, FIRST}
+
+This is an introductory chapter, with the main purpose of fixing the
+terminology used in the sequel. In particular, I give the definitions
+of objects, classes, attributes and methods. I discuss a few examples
+and I show some of the most elementary Python introspection features.
+
+
+%___________________________________________________________________________
+
+\hypertarget{what-s-an-object}{}
+\pdfbookmark[1]{What's an object?}{what-s-an-object}
+\subsection*{What's an object?}
+\begin{quote}
+\begin{flushleft}
+\emph{So~Everything~Is~An~object.~~~\\
+I'm~sure~the~Smalltalkers~are~very~happy~:)}~\\
+~\\
+--~Michael~Hudson~on~comp.lang.python
+\end{flushleft}
+\end{quote}
+
+``What's an object'' is the obvious question raised by anybody starting
+to learn Object Oriented Programming. The answer is simple: in Python,
+everything in an object!
+
+An operative definition is the following: an \emph{object}
+is everything that can be labelled with an \emph{object reference}.
+
+In practical terms, the object reference is implemented as
+the object memory address, that is an integer number which uniquely
+specify the object. There is a simple way to retrieve the object reference:
+to use the builtin \texttt{id} function. Informations on \texttt{id} can be retrieved
+via the \texttt{help} function [\hyperlink{id7}{4}]:
+\begin{quote}
+\begin{verbatim}>>> help(id)
+Help on built-in function id:
+id(...)
+id(object) -> integer
+Return the identity of an object. This is guaranteed to be unique among
+simultaneously existing objects. (Hint: it's the object's memory address.)\end{verbatim}
+\end{quote}
+
+The reader is strongly encouraged to try the help function on everything
+(including help(help) ;-). This is the best way to learn how Python works,
+even \emph{better} than reading the standard documentation, since the on-line
+help is often more update.
+
+Suppose for instance we wonder if the number \texttt{1} is an object:
+it is easy enough to ask Python for the answer:
+\begin{quote}
+\begin{verbatim}>>> id(1)
+135383880\end{verbatim}
+\end{quote}
+
+Therefore the number 1 is a Python object and it is stored at the memory
+address 135383880, at least in my computer and during the current session.
+Notice that the object reference is a dynamic thing; nevertheless it
+is guaranteed to be unique and constant for a given object during its
+lifetime (two objects whose lifetimes are disjunct may have the same id()
+value, though).
+
+Here there are other examples of built-in objects:
+\begin{quote}
+\begin{verbatim}>>> id(1L) # long
+1074483312
+>>> id(1.0) #float
+135682468
+>>> id(1j) # complex
+135623440
+>>> id('1') #string
+1074398272
+>>> id([1]) #list
+1074376588
+>>> id((1,)) #tuple
+1074348844
+>>> id({1:1}) # dict
+1074338100\end{verbatim}
+\end{quote}
+
+Even functions are objects:
+\begin{quote}
+\begin{verbatim}>>> def f(x): return x #user-defined function
+>>> id(f)
+1074292020
+>>> g=lambda x: x #another way to define functions
+>>> id(g)
+1074292468
+>>> id(id) #id itself is a built-in function
+1074278668\end{verbatim}
+\end{quote}
+
+Modules are objects, too:
+\begin{quote}
+\begin{verbatim}>>> import math
+>>> id(math) #module of the standard library
+1074239068
+>>> id(math.sqrt) #function of the standard library
+1074469420\end{verbatim}
+\end{quote}
+
+\texttt{help} itself is an object:
+\begin{quote}
+\begin{verbatim}>>> id(help)
+1074373452\end{verbatim}
+\end{quote}
+
+Finally, we may notice that the reserved keywords are not objects:
+\begin{quote}
+\begin{verbatim}>>> id(print) #error
+File "<string>", line 1
+ id(print) ^
+SyntaxError: invalid syntax\end{verbatim}
+\end{quote}
+
+The operative definition is convenient since it gives a practical way
+to check if something is an object and, more importantly, if two
+objects are the same or not:
+\begin{quote}
+% doctest
+\begin{verbatim}>>> s1='spam'
+>>> s2='spam'
+>>> s1==s2
+True
+>>> id(s1)==id(s2)
+True\end{verbatim}
+\end{quote}
+
+A more elegant way of spelling \texttt{id(obj1)==id(obj2)} is to use the
+keyword \texttt{is}:
+\begin{quote}
+\begin{verbatim}>>> s1 is s2
+True\end{verbatim}
+\end{quote}
+
+However, I should warn the reader that sometimes \texttt{is} can be surprising:
+\begin{quote}
+\begin{verbatim}>>> id([]) == id([])
+True
+>>> [] is []
+False\end{verbatim}
+\end{quote}
+
+This is happening because writing \texttt{id([])} dynamically creates an unique
+object (a list) which goes away when you're finished with it. So when an
+expression needs both at the same time (\texttt{[] is []}), two unique objects
+are created, but when an expression doesn't need both at the same time
+(\texttt{id([]) == id([])}), an object gets created with an ID, is destroyed,
+and then a second object is created with the same ID (since the last one
+just got reclaimed) and their IDs compare equal. In other words, ``the
+ID is guaranteed to be unique \emph{only} among simultaneously existing objects''.
+
+Another surprise is the following:
+\begin{quote}
+\begin{verbatim}>>> a=1
+>>> b=1
+>>> a is b
+True
+>>> a=556
+>>> b=556
+>>> a is b
+False\end{verbatim}
+\end{quote}
+
+The reason is that integers between 0 and 99 are pre-instantiated by the
+interpreter, whereas larger integers are recreated each time.
+
+Notice the difference between '==' and 'is':
+\begin{quote}
+\begin{verbatim}>>> 1L==1
+True\end{verbatim}
+\end{quote}
+
+but
+\begin{quote}
+\begin{verbatim}>>> 1L is 1
+False \end{verbatim}
+\end{quote}
+
+since they are different objects:
+\begin{quote}
+\begin{verbatim}>>> id(1L) # long 1
+135625536
+>>> id(1) # int 1
+135286080\end{verbatim}
+\end{quote}
+
+The disadvantage of the operative definition is that it gives little
+understanding of what an object can be used for. To this aim, I must
+introduce the concept of \emph{class}.
+\begin{figure}[b]\hypertarget{id9}[5]
+Actually \texttt{help} is not a function but a callable object. The
+difference will be discussed in a following chapter.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-and-classes}{}
+\pdfbookmark[1]{Objects and classes}{objects-and-classes}
+\subsection*{Objects and classes}
+
+It is convenient to think of an object as an element of a set.
+
+It you think a bit, this is the most general definition that actually
+grasps what we mean by object in the common language.
+For instance, consider this book, ``Object Oriented Programming in Python'':
+this book is an object, in the sense that it is a specific representative
+of the \emph{class} of all possible books.
+According to this definition, objects are strictly related to classes, and
+actually we say that objects are \emph{instances} of classes.
+
+Classes are nested: for
+instance this book belongs to the class of books about programming
+language, which is a subset of the class of all possible books;
+moreover we may further specify this book as a Python book; moreover
+we may specify this book as a Python 2.2+ book. There is no limit
+to the restrictions we may impose to our classes.
+On the other hand. it is convenient to have a ``mother'' class,
+such that any object belongs to it. All strongly Object Oriented
+Language have such a class [\hyperlink{id9}{5}]; in Python it is called \emph{object}.
+
+The relation between objects and classes in Python can be investigated
+trough the built-in function \texttt{type} [\hyperlink{id12}{6}] that gives the class of any
+Python object.
+
+Let me give some example:
+\newcounter{listcnt5}
+\begin{list}{\arabic{listcnt5}.}
+{
+\usecounter{listcnt5}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Integers numbers are instances of the class \texttt{int} or \texttt{long}:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type(1)
+<type 'int'>
+>>> type(1L)
+<type 'long'>\end{verbatim}
+\end{quote}
+\newcounter{listcnt6}
+\begin{list}{\arabic{listcnt6}.}
+{
+\usecounter{listcnt6}
+\addtocounter{listcnt6}{1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Floating point numbers are instances of the class \texttt{float}:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type(1.0)
+<type 'float'>\end{verbatim}
+\end{quote}
+\newcounter{listcnt7}
+\begin{list}{\arabic{listcnt7}.}
+{
+\usecounter{listcnt7}
+\addtocounter{listcnt7}{2}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Complex numbers are instances of the class \texttt{complex}:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type(1.0+1.0j)
+<type 'complex'>\end{verbatim}
+\end{quote}
+\newcounter{listcnt8}
+\begin{list}{\arabic{listcnt8}.}
+{
+\usecounter{listcnt8}
+\addtocounter{listcnt8}{3}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Strings are instances of the class \texttt{str}:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type('1')
+<type 'str'>\end{verbatim}
+\end{quote}
+\newcounter{listcnt9}
+\begin{list}{\arabic{listcnt9}.}
+{
+\usecounter{listcnt9}
+\addtocounter{listcnt9}{4}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+List, tuples and dictionaries are instances of \texttt{list}, \texttt{tuple} and
+\texttt{dict} respectively:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type('1')
+<type 'str'>
+>>> type([1])
+<type 'list'>
+>>> type((1,))
+<type 'tuple'>
+>>> type({1:1})
+<type 'dict'>\end{verbatim}
+\end{quote}
+\newcounter{listcnt10}
+\begin{list}{\arabic{listcnt10}.}
+{
+\usecounter{listcnt10}
+\addtocounter{listcnt10}{5}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+User defined functions are instances of the \texttt{function} built-in type
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> type(f)
+<type 'function'>
+>>> type(g)
+<type 'function'>\end{verbatim}
+\end{quote}
+
+All the previous types are subclasses of object:
+\begin{quote}
+\begin{verbatim}>>> for cl in int,long,float,str,list,tuple,dict: issubclass(cl,object)
+True
+True
+True
+True
+True
+True
+True\end{verbatim}
+\end{quote}
+
+However, Python is not a 100{\%} pure Object
+Oriented Programming language and its object model has still some minor
+warts, due to historical accidents.
+
+Paraphrasing George Orwell, we may say that in Python 2.2-2.3,
+all objects are equal, but some objects are more equal than others.
+Actually, we may distinguish Python objects in new style objects,
+or rich man objects, and old style objects, or poor man objects.
+New style objects are instances of new style classes whereas old
+style objects are instances of old style classes.
+The difference is that new style classes are subclasses of object whereas
+old style classes are not.
+
+Old style classes are there for sake of compatibility with previous
+releases of Python, but starting from Python 2.2 practically all built-in
+classes are new style classes.
+
+Instance of old style classes are called old style objects. I will give
+few examples of old style objects in the future.
+
+In this tutorial with the term
+object \emph{tout court} we will mean new style objects, unless the contrary
+is explicitely stated.
+\begin{figure}[b]\hypertarget{id12}[6]
+one may notice that C++ does not have such a class, but C++
+is \emph{not} a strongly object oriented language ;-)
+\end{figure}
+\begin{figure}[b]\hypertarget{id13}[7]
+Actually \texttt{type} is not a function, but a metaclass; nevertheless,
+since this is an advanced concept, discussed in the fourth chapter;
+for the time being it is better to think of \texttt{type} as a built-in
+function analogous to \texttt{id}.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-attributes}{}
+\pdfbookmark[1]{Objects have attributes}{objects-have-attributes}
+\subsection*{Objects have attributes}
+
+All objects have attributes describing their characteristics, that may
+be accessed via the dot notation
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{objectname.objectattribute}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The dot notation is common to most Object Oriented programming languages,
+therefore the reader with a little of experience should find it not surprising
+at all (Python strongly believes in the Principle of Least Surprise). However,
+Python objects also have special attributes denoted by the double-double
+underscore notation
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{objectname.{\_}{\_}specialattribute{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with the aim of helping the wonderful Python introspection features, that
+does not have correspondence in all OOP language.
+
+Consider for example the string literal ``spam''. We may discover its
+class by looking at its special attribute \emph{{\_}{\_}class{\_}{\_}}:
+\begin{quote}
+\begin{verbatim}>>> 'spam'.__class__
+<type 'str'>\end{verbatim}
+\end{quote}
+
+Using the \texttt{{\_}{\_}class{\_}{\_}} attribute is not always equivalent to using the
+\texttt{type} function, but it works for all built-in types. Consider for instance
+the number \emph{1}: we may extract its class as follows:
+\begin{quote}
+\begin{verbatim}>>> (1).__class__
+<type 'int'>\end{verbatim}
+\end{quote}
+
+Notice that the parenthesis are needed to avoid confusion between the integer
+1 and the float (1.).
+
+The non-equivalence type/class is the key to distinguish new style objects from
+old style, since for old style objects \texttt{type(obj)<>obj.{\_}{\_}class{\_}{\_}}.
+We may use this knowledge to make and utility function that discovers
+if an object is a ``real'' object (i.e. new style) or a poor man object:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~isnewstyle(obj):}\\
+\mbox{~~~~try:~{\#}some~objects~may~lack~a~{\_}{\_}class{\_}{\_}~attribute~}\\
+\mbox{~~~~~~~~obj.{\_}{\_}class{\_}{\_}}\\
+\mbox{~~~~except~AttributeError:}\\
+\mbox{~~~~~~~~return~False}\\
+\mbox{~~~~else:~{\#}look~if~there~is~unification~type/class}\\
+\mbox{~~~~~~~~return~type(obj)~is~obj.{\_}{\_}class{\_}{\_}}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Let us check this with various examples:
+\begin{quote}
+\begin{verbatim}>>> from oopp import isnewstyle
+>>> isnewstyle(1)
+True
+>>> isnewstyle(lambda x:x)
+True
+>>> isnewstyle(id)
+True
+>>> isnewstyle(type)
+True
+>>> isnewstyle(isnewstyle)
+True
+>>> import math
+>>> isnewstyle(math)
+True
+>>> isnewstyle(math.sqrt)
+True
+>>> isnewstyle('hello')
+True\end{verbatim}
+\end{quote}
+
+It is not obvious to find something which is not a real object,
+between the built-in objects, however it is possible. For instance,
+the \texttt{help} ``function'' is an old style object:
+\begin{quote}
+\begin{verbatim}>>> isnewstyle(help)
+False\end{verbatim}
+\end{quote}
+
+since
+\begin{quote}
+\begin{verbatim}>>> help.__class__
+<class site._Helper at 0x8127c94>\end{verbatim}
+\end{quote}
+
+is different from
+\begin{quote}
+\begin{verbatim}>>> type(help)
+<type 'instance'>\end{verbatim}
+\end{quote}
+
+Regular expression objects are even poorer objects with no \texttt{{\_}{\_}class{\_}{\_}}
+attribute:
+\begin{quote}
+\begin{verbatim}>>> import re
+>>> reobj=re.compile('somestring')
+>>> isnewstyle(reobj)
+False
+>>> type(reobj)
+<type '_sre.SRE_Pattern'>
+>>> reobj.__class__ #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: __class__\end{verbatim}
+\end{quote}
+
+There other special attributes other than \texttt{{\_}{\_}class{\_}{\_}}; a particularly useful
+one is \texttt{{\_}{\_}doc{\_}{\_}}, that contains informations on the class it
+refers to. Consider for instance the \texttt{str} class: by looking at its
+\texttt{{\_}{\_}doc{\_}{\_}} attribute we can get information on the usage of this class:
+\begin{quote}
+\begin{verbatim}>>> str.__doc__
+str(object) -> string
+Return a nice string representation of the object.
+If the argument is a string, the return value is the same object.\end{verbatim}
+\end{quote}
+
+From that docstring we learn how to convert generic objects in strings;
+for instance we may convert numbers, lists, tuples and dictionaries:
+\begin{quote}
+\begin{verbatim}>>> str(1)
+'1'
+>>> str([1])
+'[1]'
+>>> str((1,))
+(1,)'
+>>> str({1:1})
+'{1: 1}'\end{verbatim}
+\end{quote}
+
+\texttt{str} is implicitely called each time we use the \texttt{print} statement, since
+\texttt{print obj} is actually syntactic sugar for \texttt{print str(obj)}.
+
+Classes and modules have another interesting special attribute, the
+\texttt{{\_}{\_}dict{\_}{\_}} attribute that gives the content of the class/module.
+For instance, the contents of the standard \texttt{math} module can be retrieved
+as follows:
+\begin{quote}
+\begin{verbatim}>>> import math
+>>> for key in math.__dict__: print key,
+...
+fmod atan pow __file__ cosh ldexp hypot sinh __name__ tan ceil asin cos
+e log fabs floor tanh sqrt __doc__ frexp atan2 modf exp acos pi log10 sin\end{verbatim}
+\end{quote}
+
+Alternatively, one can use the built-in function \texttt{vars}:
+\begin{quote}
+\begin{verbatim}>>> vars(math) is math.__dict__
+True\end{verbatim}
+\end{quote}
+
+This identity is true for any object with a \texttt{{\_}{\_}dict{\_}{\_}} attribute.
+Two others interesting special attributes are \texttt{{\_}{\_}doc{\_}{\_}}
+\begin{quote}
+\begin{verbatim}>>> print math.__doc__
+This module is always available. It provides access to the
+mathematical functions defined by the C standard. \end{verbatim}
+\end{quote}
+
+and \texttt{{\_}{\_}file{\_}{\_}}:
+\begin{quote}
+\begin{verbatim}>>> math.__file__ #gives the file associated with the module
+'/usr/lib/python2.2/lib-dynload/mathmodule.so'\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-methods}{}
+\pdfbookmark[1]{Objects have methods}{objects-have-methods}
+\subsection*{Objects have methods}
+
+In addition to attributes, objects also have \emph{methods}, i.e.
+functions attached to their classes [\hyperlink{id13}{7}].
+Methods are also invoked with the dot notation, but
+they can be distinguished by attributes because they are typically
+called with parenthesis (this is a little simplistic, but it is enough for
+an introductory chapter). As a simple example, let me show the
+invocation of the \texttt{split} method for a string object:
+\begin{quote}
+\begin{verbatim}>>> s='hello world!'
+>>> s.split()
+['hello', 'world!']\end{verbatim}
+\end{quote}
+
+In this example \texttt{s.split} is called a \emph{bount method}, since it is
+applied to the string object \texttt{s}:
+\begin{quote}
+\begin{verbatim}>>> s.split
+<built-in method split of str object at 0x81572b8>\end{verbatim}
+\end{quote}
+
+An \emph{unbound method}, instead, is applied to the class: in this case the
+unbound version of \texttt{split} is applied to the \texttt{str} class:
+\begin{quote}
+\begin{verbatim}>>> str.split
+<method 'split' of 'str' objects>\end{verbatim}
+\end{quote}
+
+A bound method is obtained from its corresponding unbound
+method by providing the object to the unbound method: for instance
+by providing \texttt{s} to \texttt{str.split} we obtain the same effect of \titlereference{s.split()}:
+\begin{quote}
+\begin{verbatim}>>> str.split(s)
+['hello', 'world!']\end{verbatim}
+\end{quote}
+
+This operation is called \emph{binding} in the Python literature: when write
+\texttt{str.split(s)} we bind the unbound method \texttt{str.split} to the object \texttt{s}.
+It is interesting to recognize that the bound and unbound methods are
+\emph{different} objects:
+\begin{quote}
+\begin{verbatim}>>> id(str.split) # unbound method reference
+135414364
+>>> id(s.split) # this is a different object!
+135611408\end{verbatim}
+\end{quote}
+
+The unbound method (and therefore the bound method) has a \texttt{{\_}{\_}doc{\_}{\_}}
+attribute explaining how it works:
+\begin{quote}
+\begin{verbatim}>>> print str.split.__doc__
+S.split([sep [,maxsplit]]) -> list of strings
+Return a list of the words in the string S, using sep as the
+delimiter string. If maxsplit is given, at most maxsplit
+splits are done. If sep is not specified or is None, any
+whitespace string is a separator.\end{verbatim}
+\end{quote}
+\begin{figure}[b]\hypertarget{id15}[8]
+A precise definition will be given in chapter 5 that introduces the
+concept of attribute descriptors. There are subtle
+differences between functions and methods.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{summing-objects}{}
+\pdfbookmark[1]{Summing objects}{summing-objects}
+\subsection*{Summing objects}
+
+In a pure object-oriented world, there are no functions and everything is
+done trough methods. Python is not a pure OOP language, however quite a
+lot is done trough methods. For instance, it is quite interesting to analyze
+what happens when an apparently trivial statement such as
+\begin{quote}
+\begin{verbatim}>>> 1+1
+2\end{verbatim}
+\end{quote}
+
+is executed in an object-oriented world.
+
+The key to understand, is to notice that the number 1 is an object, specifically
+an instance of class \texttt{int}: this means that that 1 inherits all the methods
+of the \texttt{int} class. In particular it inherits a special method called
+\texttt{{\_}{\_}add{\_}{\_}}: this means 1+1 is actually syntactic sugar for
+\begin{quote}
+\begin{verbatim}>>> (1).__add__(1)
+2\end{verbatim}
+\end{quote}
+
+which in turns is syntactic sugar for
+\begin{quote}
+\begin{verbatim}>>> int.__add__(1,1)
+2\end{verbatim}
+\end{quote}
+
+The same is true for subtraction, multiplication, division and other
+binary operations.
+\begin{quote}
+\begin{verbatim}>>> 'hello'*2
+'hellohello'
+>>> (2).__mul__('hello')
+'hellohello'
+>>> str.__mul__('hello',2)
+'hellohello'\end{verbatim}
+\end{quote}
+
+However, notice that
+\begin{quote}
+\begin{verbatim}>>> str.__mul__(2,'hello') #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: descriptor '__mul__' requires a 'str' object but received a 'int'\end{verbatim}
+\end{quote}
+
+The fact that operators are implemented as methods, is the key to
+\emph{operator overloading}: in Python (as well as in other OOP languages)
+the user can redefine the operators. This is already done by default
+for some operators: for instance the operator \texttt{+} is overloaded
+and works both for integers, floats, complex numbers and for strings.
+
+
+%___________________________________________________________________________
+
+\hypertarget{inspecting-objects}{}
+\pdfbookmark[1]{Inspecting objects}{inspecting-objects}
+\subsection*{Inspecting objects}
+
+In Python it is possible to retrieve most of the attributes and methods
+of an object by using the built-in function \texttt{dir()}
+(try \texttt{help(dir)} for more information).
+
+Let me consider the simplest case of a generic object:
+\begin{quote}
+\begin{verbatim}>>> obj=object()
+>>> dir(obj)
+['__class__', '__delattr__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__']\end{verbatim}
+\end{quote}
+
+As we see, there are plenty of attributes available
+even to a do nothing object; many of them are special attributes
+providing introspection capabilities which are not
+common to all programming languages. We have already discussed the
+meaning of some of the more obvious special attributes.
+The meaning of some of the others is quite non-obvious, however.
+The docstring is invaluable in providing some clue.
+
+Notice that there are special \emph{hidden} attributes that cannot be retrieved
+with \texttt{dir()}. For instance the \texttt{{\_}{\_}name{\_}{\_}} attribute, returning the
+name of the object (defined for classes, modules and functions)
+and the \texttt{{\_}{\_}subclasses{\_}{\_}} method, defined for classes and returning the
+list of immediate subclasses of a class:
+\begin{quote}
+\begin{verbatim}>>> str.__name__
+'str'
+>>> str.__subclasses__.__doc__
+'__subclasses__() -> list of immediate subclasses'
+>>> str.__subclasses__() # no subclasses of 'str' are currently defined
+[]\end{verbatim}
+\end{quote}
+
+For instance by doing
+\begin{quote}
+\begin{verbatim}>>> obj.__getattribute__.__doc__
+"x.__getattribute__('name') <==> x.name"\end{verbatim}
+\end{quote}
+
+we discover that the expression \texttt{x.name} is syntactic sugar for
+\begin{quote}
+
+\texttt{x.{\_}{\_}getattribute{\_}{\_}('name')}
+\end{quote}
+
+Another equivalent form which is more often used is
+\begin{quote}
+
+\texttt{getattr(x,'name')}
+\end{quote}
+
+We may use this trick to make a function that retrieves all the
+attributes of an object except the special ones:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~special(name):~return~name.startswith('{\_}{\_}')~and~name.endswith('{\_}{\_}')}\\
+\mbox{}\\
+\mbox{def~attributes(obj,condition=lambda~n,v:~not~special(n)):}\\
+\mbox{~~~~"""Returns~a~dictionary~containing~the~accessible~attributes~of~}\\
+\mbox{~~~~an~object.~By~default,~returns~the~non-special~attributes~only."""}\\
+\mbox{~~~~dic={\{}{\}}}\\
+\mbox{~~~~for~attr~in~dir(obj):}\\
+\mbox{~~~~~~~~try:~v=getattr(obj,attr)}\\
+\mbox{~~~~~~~~except:~continue~{\#}attr~is~not~accessible}\\
+\mbox{~~~~~~~~if~condition(attr,v):~dic[attr]=v}\\
+\mbox{~~~~return~dic}\\
+\mbox{}\\
+\mbox{getall~=~lambda~n,v:~True}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that certain attributes may be unaccessible (we will see how
+to make attributes unaccessible in a following chapter)
+and in this case they are simply ignored.
+For instance you may retrieve the regular (i.e. non special)
+attributes of the built-in functions:
+\begin{quote}
+\begin{verbatim}>>> from oopp import attributes
+>>> attributes(f).keys()
+['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']\end{verbatim}
+\end{quote}
+
+In the same vein of the \texttt{getattr} function, there is a built-in
+\texttt{setattr} function (that actually calls the \texttt{{\_}{\_}setattr{\_}{\_}} built-in
+method), that allows the user to change the attributes and methods of
+and object. Informations on \texttt{setattr} can be retrieved from the help
+function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{>>>~help(setattr)}\\
+\mbox{Help~on~built-in~function~setattr:}\\
+\mbox{setattr(...)}\\
+\mbox{setattr(object,~name,~value)}\\
+\mbox{Set~a~named~attribute~on~an~object;~setattr(x,~'y',~v)~is~equivalent~to}\\
+\mbox{``x.y~=~v''.}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+\texttt{setattr} can be used to add attributes to an object:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~sys}\\
+\mbox{}\\
+\mbox{def~customize(obj,errfile=None,**kw):}\\
+\mbox{~~~~"""Adds~attributes~to~an~object,~if~possible.~If~not,~writes~an~error}\\
+\mbox{~~~~message~on~'errfile'.~If~errfile~is~None,~skips~the~exception."""}\\
+\mbox{~~~~for~k~in~kw:}\\
+\mbox{~~~~~~~~try:~}\\
+\mbox{~~~~~~~~~~~~setattr(obj,k,kw[k])}\\
+\mbox{~~~~~~~~except:~{\#}~setting~error}\\
+\mbox{~~~~~~~~~~~~if~errfile:}\\
+\mbox{~~~~~~~~~~~~~~~~print~>>~errfile,"Error:~{\%}s~cannot~be~set"~{\%}~k}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The attributes of built-in objects cannot be set, however:
+\begin{quote}
+\begin{verbatim}>>> from oopp import customize,sys
+>>> customize(object(),errfile=sys.stdout,newattr='hello!') #error
+AttributeError: newattr cannot be set\end{verbatim}
+\end{quote}
+
+On the other hand, the attributes of modules can be set:
+\begin{quote}
+\begin{verbatim}>>> import time
+>>> customize(time,newattr='hello!')
+>>> time.newattr
+'hello!'\end{verbatim}
+\end{quote}
+
+Notice that this means we may enhances modules at run-time, but adding
+new routines, not only new data attributes.
+
+The \texttt{attributes} and \texttt{customize} functions work for any kind of objects;
+in particular, since classes are a special kind of objects, they work
+for classes, too. Here are the attributes of the \texttt{str}, \texttt{list} and
+\texttt{dict} built-in types:
+\begin{quote}
+\begin{verbatim}>>> from oopp import attributes
+>>> attributes(str).keys()
+['startswith', 'rjust', 'lstrip', 'swapcase', 'replace','encode',
+ 'endswith', 'splitlines', 'rfind', 'strip', 'isdigit', 'ljust',
+ 'capitalize', 'find', 'count', 'index', 'lower', 'translate','join',
+ 'center', 'isalnum','title', 'rindex', 'expandtabs', 'isspace',
+ 'decode', 'isalpha', 'split', 'rstrip', 'islower', 'isupper',
+ 'istitle', 'upper']
+>>> attributes(list).keys()
+['append', 'count', 'extend', 'index', 'insert', 'pop',
+ 'remove', 'reverse', 'sort']
+>>> attributes(dict).keys()
+['clear','copy','fromkeys', 'get', 'has_key', 'items','iteritems',
+ 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault',
+ 'update', 'values']\end{verbatim}
+\end{quote}
+
+Classes and modules have a special attribute \texttt{{\_}{\_}dict{\_}{\_}} giving the
+dictionary of their attributes. Since it is often a quite large dictionary,
+it is convenient to define an utility function printing this dictionary in a
+nice form:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~pretty(dic):}\\
+\mbox{~~~~"Returns~a~nice~string~representation~for~the~dictionary"}\\
+\mbox{~~~~keys=dic.keys();~keys.sort()~{\#}~sorts~the~keys}\\
+\mbox{~~~~return~'{\textbackslash}n'.join(['{\%}s~=~{\%}s'~{\%}~(k,dic[k])~for~k~in~keys])}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+I encourage the use of this function in order to retrieve more
+information about the modules of the standard library:
+\begin{quote}
+\begin{verbatim}>>> from oopp import pretty
+>>> import time #look at the 'time' standard library module
+>>> print pretty(vars(time))
+__doc__ = This module provides various functions to manipulate time values.
+There are two standard representations of time. One is the number
+of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer
+or a floating point number (to represent fractions of seconds).
+The Epoch is system-defined; on Unix, it is generally January 1st, 1970.
+The actual value can be retrieved by calling gmtime(0).
+The other representation is a tuple of 9 integers giving local time.
+The tuple items are:
+ year (four digits, e.g. 1998)
+ month (1-12)
+ day (1-31)
+ hours (0-23)
+ minutes (0-59)
+ seconds (0-59)
+ weekday (0-6, Monday is 0)
+ Julian day (day in the year, 1-366)
+ DST (Daylight Savings Time) flag (-1, 0 or 1)
+If the DST flag is 0, the time is given in the regular time zone;
+if it is 1, the time is given in the DST time zone;
+if it is -1, mktime() should guess based on the date and time.
+Variables:
+timezone -- difference in seconds between UTC and local standard time
+altzone -- difference in seconds between UTC and local DST time
+daylight -- whether local time should reflect DST
+tzname -- tuple of (standard time zone name, DST time zone name)
+Functions:
+time() -- return current time in seconds since the Epoch as a float
+clock() -- return CPU time since process start as a float
+sleep() -- delay for a number of seconds given as a float
+gmtime() -- convert seconds since Epoch to UTC tuple
+localtime() -- convert seconds since Epoch to local time tuple
+asctime() -- convert time tuple to string
+ctime() -- convert time in seconds to string
+mktime() -- convert local time tuple to seconds since Epoch
+strftime() -- convert time tuple to string according to format specification
+strptime() -- parse string to time tuple according to format specification
+__file__ = /usr/local/lib/python2.3/lib-dynload/time.so
+__name__ = time
+accept2dyear = 1
+altzone = 14400
+asctime = <built-in function asctime>
+clock = <built-in function clock>
+ctime = <built-in function ctime>
+daylight = 1
+gmtime = <built-in function gmtime>
+localtime = <built-in function localtime>
+mktime = <built-in function mktime>
+newattr = hello!
+sleep = <built-in function sleep>
+strftime = <built-in function strftime>
+strptime = <built-in function strptime>
+struct_time = <type 'time.struct_time'>
+time = <built-in function time>
+timezone = 18000
+tzname = ('EST', 'EDT')\end{verbatim}
+\end{quote}
+
+The list of the built-in Python types can be found in the \texttt{types} module:
+\begin{quote}
+\begin{verbatim}>>> import types
+>>> t_dict=dict([(k,v) for (k,v) in vars(types).iteritems()
+... if k.endswith('Type')])
+>>> for t in t_dict: print t,
+...
+DictType IntType TypeType FileType CodeType XRangeType EllipsisType
+SliceType BooleanType ListType MethodType TupleType ModuleType FrameType
+StringType LongType BuiltinMethodType BufferType FloatType ClassType
+DictionaryType BuiltinFunctionType UnboundMethodType UnicodeType
+LambdaType DictProxyType ComplexType GeneratorType ObjectType
+FunctionType InstanceType NoneType TracebackType\end{verbatim}
+\end{quote}
+
+For a pedagogical account of the most elementary
+Python introspection features,
+Patrick O' Brien:
+\href{http://www-106.ibm.com/developerworks/linux/library/l-pyint.html}{http://www-106.ibm.com/developerworks/linux/library/l-pyint.html}
+
+
+%___________________________________________________________________________
+
+\hypertarget{built-in-objects-iterators-and-generators}{}
+\pdfbookmark[1]{Built-in objects: iterators and generators}{built-in-objects-iterators-and-generators}
+\subsection*{Built-in objects: iterators and generators}
+
+At the end of the last section , I have used the \texttt{iteritems} method
+of the dictionary, which returns an iterator:
+\begin{quote}
+\begin{verbatim}>>> dict.iteritems.__doc__
+'D.iteritems() -> an iterator over the (key, value) items of D'\end{verbatim}
+\end{quote}
+
+Iterators (and generators) are new features of Python 2.2 and could not be
+familiar to all readers. However, since they are unrelated to OOP, they
+are outside the scope of this book and will not be discussed here in detail.
+Nevertheless, I will give a typical example of use of a generator, since
+this construct will be used in future chapters.
+
+At the syntactical level, a generator is a ``function'' with (at least one)
+\texttt{yield} statement (notice that in Python 2.2 the \texttt{yield} statement is
+enabled trough the \texttt{from {\_}{\_}future{\_}{\_} import generators} syntax):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~re}\\
+\mbox{}\\
+\mbox{def~generateblocks(regexp,text):}\\
+\mbox{~~~~"Generator~splitting~text~in~blocks~according~to~regexp"}\\
+\mbox{~~~~start=0}\\
+\mbox{~~~~for~MO~in~regexp.finditer(text):}\\
+\mbox{~~~~~~~~beg,end=MO.span()}\\
+\mbox{~~~~~~~~yield~text[start:beg]~{\#}~actual~text}\\
+\mbox{~~~~~~~~yield~text[beg:end]~{\#}~separator}\\
+\mbox{~~~~~~~~start=end}\\
+\mbox{~~~~lastblock=text[start:]~}\\
+\mbox{~~~~if~lastblock:~yield~lastblock;~yield~''}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In order to understand this example, the reader my want to refresh his/her
+understanding of regular expressions; since this is not a subject for
+this book, I simply remind the meaning of \texttt{finditer}:
+\begin{quote}
+\begin{verbatim}>>> import re
+>>> help(re.finditer)
+finditer(pattern, string)
+ Return an iterator over all non-overlapping matches in the
+ string. For each match, the iterator returns a match object.
+ Empty matches are included in the result.\end{verbatim}
+\end{quote}
+
+Generators can be thought of as resumable functions that stop at the
+\texttt{yield} statement and resume from the point where they left.
+\begin{quote}
+\begin{verbatim}>>> from oopp import generateblocks
+>>> text='Python_Rules!'
+>>> g=generateblocks(re.compile('_'),text)
+>>> g
+<generator object at 0x401b140c>
+>>> dir(g)
+['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
+ '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__',
+ '__repr__', '__setattr__', '__str__', 'gi_frame', 'gi_running', 'next']\end{verbatim}
+\end{quote}
+
+Generator objects can be used as iterators in a \texttt{for} loop.
+In this example the generator takes a text and a regular expression
+describing a fixed delimiter; then it splits the text in blocks
+according to the delimiter. For instance, if the delimiter is
+'{\_}', the text 'Python Rules!' is splitted as 'Python', '{\_}' and 'Rules!':
+\begin{quote}
+\begin{verbatim}>>> for n, block in enumerate(g): print n, block
+...
+0 Python
+1
+2 Rules!
+3\end{verbatim}
+\end{quote}
+
+This example also show the usage of the new Python 2.3 built-in \texttt{enumerate}.
+
+Under the hood the \texttt{for} loop is calling the generator via its
+\texttt{next} method, until the \texttt{StopIteration} exception is raised.
+For this reason a new call to the \texttt{for} loop will have no effect:
+\begin{quote}
+\begin{verbatim}>>> for n, block in enumerate(g): print n, block
+...\end{verbatim}
+\end{quote}
+
+The point is that the generator has already yield its last element:
+\begin{quote}
+\begin{verbatim}>>> g.next() # error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+StopIteration\end{verbatim}
+\end{quote}
+
+\texttt{generateblocks} always returns an even number of blocks; odd blocks
+are delimiters whereas even blocks are the intertwining text; there may be
+empty blocks, corresponding to the null string ''.
+
+It must be remarked the difference with the 'str.split' method
+\begin{quote}
+\begin{verbatim}>>> 'Python_Rules!'.split('_')
+['Python', 'Rules!']\end{verbatim}
+\end{quote}
+
+and the regular expression split method:
+\begin{quote}
+\begin{verbatim}>>> re.compile('_').split('Python_Rules!')
+['Python', 'Rules!']\end{verbatim}
+\end{quote}
+
+both returns lists with an odd number of elements and both miss the separator.
+The regular expression split method can catch the separator, if wanted,
+\begin{quote}
+\begin{verbatim}>>> re.compile('(_)').split('Python_Rules!')
+['Python', '_', 'Rules!']\end{verbatim}
+\end{quote}
+
+but still is different from the generator, since it returns a list. The
+difference is relevant if we want to split a very large text, since
+the generator avoids to build a very large list and thus it is much more
+memory efficient (it is faster, too). Moreover, \texttt{generateblocks}
+works differently in the case of multiple groups:
+\begin{quote}
+\begin{verbatim}>>> delim=re.compile('(_)|(!)') #delimiter is space or exclamation mark
+>>> for n, block in enumerate(generateblocks(delim,text)):
+... print n, block
+0 Python
+1 _
+2 Rules
+3 !\end{verbatim}
+\end{quote}
+
+whereas
+\begin{quote}
+\begin{verbatim}>>> delim.split(text)
+['Python', '_', None, 'Rules', None, '!', '']\end{verbatim}
+\end{quote}
+
+gives various unwanted \texttt{None} (which could be skipped with
+\texttt{[x for x in delim.split(text) if x is not None]}); notice, that
+there are no differences (apart from the fact that \texttt{delim.split(text)}
+has an odd number of elements) when one uses a single group regular expression:
+\begin{quote}
+\begin{verbatim}>>> delim=re.compile('(_|!)')
+>>> delim.split(text)
+['Python', '_', 'Rules', '!', '']\end{verbatim}
+\end{quote}
+
+The reader unfamiliar with iterators and generators is encouraged
+to look at the standard documentation and other
+references. For instance, there are Alex Martelli's notes on iterators at
+\href{http://www.strakt.com/dev_talks.html}{http://www.strakt.com/dev{\_}talks.html}
+and there is a good article on generators by David Mertz
+\href{http://www-106.ibm.com/developerworks/linux/library/l-pycon.html}{http://www-106.ibm.com/developerworks/linux/library/l-pycon.html}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-convenience-of-functions}{}
+\pdfbookmark[0]{THE CONVENIENCE OF FUNCTIONS}{the-convenience-of-functions}
+\section*{THE CONVENIENCE OF FUNCTIONS}
+
+Functions are the most basic Python objects. They are also the simplest
+objects where one can apply the metaprogramming techniques that are
+the subject of this book. The tricks used in this chapter and the utility
+functions defined here will be used over all the book. Therefore this
+is an \emph{essential} chapter.
+
+Since it is intended to be a gentle introduction, the tone will be
+informal.
+
+
+%___________________________________________________________________________
+
+\hypertarget{id16}{}
+\pdfbookmark[1]{Introduction}{id16}
+\subsection*{Introduction}
+
+One could be surprised that a text on OOP begins with a chapter on the
+well known old-fashioned functions. In some sense, this is also
+against the spirit of an important trend in OOP, which tries to
+shift the focus from functions to data. In pure OOP languages,
+there are no more functions, only methods. [\hyperlink{id15}{8}]
+
+However, there are good reasons for that:
+\newcounter{listcnt11}
+\begin{list}{\arabic{listcnt11}.}
+{
+\usecounter{listcnt11}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+In Python, functions \emph{are} objects. And particularly useful ones.
+
+\item {}
+Python functions are pretty powerful and all their secrets are probably
+\emph{not} well known to the average Python programmer.
+
+\item {}
+In the solutions of many problems, you don't need the full apparatus
+of OOP: good old functions can be enough.
+
+\end{list}
+
+Moreover, I am a believer in the multiparadigm approach to programming,
+in which you choose your tools according to your problem.
+With a bazooka you can kill a mosquito, yes, but this does not mean
+that you must use the bazooka \emph{always}.
+In certain languages, you have no choice, and you must define
+a class (involving a lot of boiler plate code) even for the most trivial
+application. Python's philosophy is to keep simple things simple, but
+having the capability of doing even difficult things with a reasonable
+amount of effort. The message of this chapter will be: ``use functions when
+you don't need classes''. Functions are good because:
+\newcounter{listcnt12}
+\begin{list}{\arabic{listcnt12}.}
+{
+\usecounter{listcnt12}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+They are easy to write (no boiler plate);
+
+\item {}
+They are easy to understand;
+
+\item {}
+They can be reused in your code;
+
+\item {}
+Functions are an essential building block in the construction of objects.
+
+\end{list}
+
+Even if I think that OOP is an extremely effective strategy, with
+enormous advantages on design, maintanibility and reusability of code,
+nevertheless this book is \emph{not} intended to be a panegyric of OOP. There
+are cases in which you don't need OOP. I think the critical parameter is
+the size of the program. These are the rules I follows usually (to be
+taken as indicative):
+\newcounter{listcnt13}
+\begin{list}{\arabic{listcnt13}.}
+{
+\usecounter{listcnt13}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+If I have to write a short script of 20-30 lines, that copies two or
+three files and prints some message, I use fast and dirty spaghetti-code;
+there is no use for OOP.
+
+\item {}
+If your script grows to one-hundred lines or more, I structure
+it write a few routines and a main program: but still I can live
+without OOP.
+
+\item {}
+If the script goes beyond the two hundred lines, I start
+collecting my routines in few classes.
+
+\item {}
+If the script goes beyond the five hundred lines, I split the program
+in various files and modules and convert it to a package.
+
+\item {}
+I never write a function longer than 50 lines, since 50 lines is more
+or less the size of a page in my editor, and I need to be able to
+see the entire function in a page.
+
+\end{list}
+
+Of course your taste could be different and you could prefer to write a
+monolitic program of five thousand lines; however the average size of
+the modules in the Python standard library is of 111 lines.
+I think this is a \emph{strong} suggestion towards
+a modular style of programming, which
+is \emph{very} well supported in Python.
+
+The point is that OOP is especially useful for \emph{large} programs: if you
+only use Python for short system administration scripts you may well
+live without OOP. Unfortunaly, as everybody knows, short scripts have
+an evil tendency to become medium size scripts, and medium size scripts
+have the even more evil tendency to become large scripts and possible
+even full featured applications ! For this reason it is very probable
+that at a certain moment you will feel the need for OOP.
+
+I remember my first big program, a long time ago: I wrote a program
+to draw mathematical functions in AmigaBasic. It was good and nice
+until it had size of few hundred lines; but when it passed a thousand
+of lines, it became rapidly unmanageable and unmaintenable. There where
+three problems:
+\newcounter{listcnt14}
+\begin{list}{\arabic{listcnt14}.}
+{
+\usecounter{listcnt14}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+I could not split the program in modules, as I wanted, due to the
+limitations of AmigaBasic;
+
+\item {}
+I was missing OOP to keep the logic of the program all together, but
+at the time I didn't know that;
+
+\item {}
+I was missing effective debugging techniques.
+
+\item {}
+I was missing effective refactoring tools.
+
+\end{list}
+
+I am sure anybody who has ever written a large program has run in these
+limitations: and the biggest help of OOP is in overcoming these limitations.
+Obviously, miracles are impossible, and even object oriented programs can
+grow to a size where they become unmaintanable: the point is that the
+critical limit is much higher than the thousand lines of structured programs.
+I haven't yet reached the limit of unmanageability with Python. The fact
+that the standard library is 66492 lines long (as result from the total
+number of lines in \texttt{/usr/local/lib/python2.2/}), but it is still manageable,
+give me an hope ;-)
+\begin{quote}
+\begin{figure}[b]\hypertarget{id18}[9]
+However, one could argue that having functions distinguished from
+methods is the best thing to do, even in a strongly object-oriented
+world. For instance, generic functions can be used to implement
+multimethods. See for instance Lisp, Dylan and MultiJava. This latter
+is forced to introduce the concept of function outside a class,
+foreign to traditional Java, just to implement multimethods.
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-few-useful-functions}{}
+\pdfbookmark[1]{A few useful functions}{a-few-useful-functions}
+\subsection*{A few useful functions}
+
+It is always a good idea to have a set of useful function collected in
+a user defined module. The first function we want to have in our module
+is the \texttt{do{\_}nothing} function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~do{\_}nothing(*args,**kw):~pass}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This function accept a variable number of arguments and keywords (I
+defer the reader to the standard documentation if she is unfamiliar
+with these concept; this is \emph{not} another Python tutorial ;-) and
+return \texttt{None}. It is very useful for debugging purposes, when in a
+complex program you may want concentrate your attention to few crucial
+functions and set the non-relevant functions to \texttt{do{\_}nothing} functions.
+
+A second function which is useful in developing programs is a timer
+function. Very ofter indeed, we may want to determine the bottleneck
+parts of a program, we are interested in profiling them and in seeing
+if we can improve the speed by improving the algorithm, or by using
+a Python ``compiler'' such as Psyco, or if really we need to write a C
+extension. In my experience, I never needed to write a C extension,
+since Python is fast enough. Nevertheless, to profile a program is
+always a good idea and Python provides a profiler module in the
+stardard library with this aim. Still, it is convenient to have
+a set of user defined functions to test the execution speed of
+few selected routines (whereas the standard profiler profiles everything).
+
+We see from the standard library documentation that
+the current time can be retrieved from the \texttt{time} module: [\hyperlink{id18}{9}]
+\begin{quote}
+\begin{verbatim}>>> import time
+>>> time.asctime()
+'Wed Jan 15 12:46:03 2003'\end{verbatim}
+\end{quote}
+
+Since we are not interested in the date but only in the time, we need
+a function to extract it. This is easily implemented:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~time}\\
+\mbox{}\\
+\mbox{def~get{\_}time():}\\
+\mbox{~~~~"Return~the~time~of~the~system~in~the~format~HH:MM:SS"}\\
+\mbox{~~~~return~time.asctime().split()[3]}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~from~oopp~import~get{\_}time}\\
+\mbox{>>>~get{\_}time()}\\
+\mbox{'13:03:49'}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Suppose, for instance, we want to know how much it takes to Python
+to write a Gigabyte of data. This can be a quite useful benchmark
+to have an idea of the I/O bottlenecks in our system. Since to take in memory
+a file of a Gigabyte can be quite problematic, let me compute the
+time spent in writing 1024 files of one Megabyte each. To this
+aim we need a \texttt{writefile} function
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~writefile(fname,data):}\\
+\mbox{~~~~f=file(fname,'w')}\\
+\mbox{~~~~f.write(data)}\\
+\mbox{~~~~f.close()}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+and timing function. The idea is to wrap the \texttt{writefile} function in
+a \texttt{with{\_}clock} function as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~with{\_}clock(func,n=1):}\\
+\mbox{~~~~def~{\_}(*args,**kw):~{\#}~this~is~a~closure}\\
+\mbox{~~~~~~~~print~"Process~started~on",get{\_}time()}\\
+\mbox{~~~~~~~~print~'~..~please~wait~..'}\\
+\mbox{~~~~~~~~for~i~in~range(n):~func(*args,**kw)}\\
+\mbox{~~~~~~~~print~"Process~ended~on",get{\_}time()}\\
+\mbox{~~~~return~{\_}}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The wrapper function \texttt{with{\_}clock} has converted the function \texttt{writefile}
+in a function \texttt{with{\_}clock(writefile)} which has the same arguments
+of \texttt{writefile}, but contains additional features: in this case
+timing capabilities. Technically speaking, the internal function \texttt{{\_}}
+is called a \emph{closure}. Closures are very common in functional languages
+and can be used in Python too, with very little effort [\hyperlink{id21}{10}].
+
+I will use closures very often in the following, and I will use
+the convention of denoting with ``{\_}'' the inner
+function in the closure, since there is no reason of giving to it a
+descriptive name (the name 'with{\_}clock' in the outer function
+is descriptive enough). For the same, reason I do not use a
+docstring for ``{\_}''. If Python would allow multistatement lambda
+functions, ``{\_}'' would be a good candidate for an anonymous function.
+
+Here is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> data='*'*1024*1024 #one megabyte
+>>> with_clock(writefile,n=1024)('datafile',data) #.
+Process started on 21:20:01
+ .. please wait ..
+Process ended on 21:20:57\end{verbatim}
+\end{quote}
+
+This example shows that Python has written one Gigabyte of data (splitted in
+1024 chunks of one Megabyte each) in less than a minute. However,the
+result depends very much on the filesystem. I always suggest people
+to profile their programs, since one \emph{always} find surprises.
+For instance, I have checked the performance of my laptop,
+a dual machine Windows 98 SE/ Red Hat Linux 7.3.
+The results are collected in the following table:
+\begin{quote}
+
+\begin{longtable}[c]{|p{0.21\locallinewidth}|p{0.26\locallinewidth}|p{0.30\locallinewidth}|}
+\hline
+\textbf{
+Linux ext-3
+} & \textbf{
+FAT under Linux
+} & \textbf{
+FAT under Windows 98
+} \\ \hline
+\endhead
+%[visit_tbody]
+
+24-25 s
+ &
+56-58 s
+ &
+86-88 s
+ \\ \hline
+%[depart_tbody]
+\end{longtable}
+\end{quote}
+
+We see that Linux is \emph{much} faster: more than three times faster than
+Windows, using the same machine! Notice that the FAT filesystem under
+Linux (where it is \emph{not} native) is remarkably faster than the FAT
+under Windows 98, where it is native !! I think that now my readers
+can begin to understand why this book has been written under Linux
+and why I \emph{never} use Windows for programming (actually I use it only
+to see the DVD's ;-).
+
+I leave as an exercise for the reader to check the results on this
+script on their machine. Since my laptop is quite old, you will probably
+have much better performances (for instance on my linux desktop I can
+write a Gigabyte in less than 12 seconds!). However, there are \emph{always}
+surprises: my desktop is a dual Windows 2000 machine with three different
+filesystems, Linux ext-2, FAT and NTFS. Surprisingly enough, the NT
+filesystem is the more inefficient for writing, \emph{ten times slower}
+than Linux!
+\begin{quote}
+
+\begin{longtable}[c]{|p{0.21\locallinewidth}|p{0.26\locallinewidth}|p{0.30\locallinewidth}|}
+\hline
+\textbf{
+Linux ext-2
+} & \textbf{
+FAT under Win2000
+} & \textbf{
+NTFS under Win2000
+} \\ \hline
+\endhead
+%[visit_tbody]
+
+11-12 s
+ &
+95-97 s
+ &
+117-120 s
+ \\ \hline
+%[depart_tbody]
+\end{longtable}
+\end{quote}
+\begin{figure}[b]\hypertarget{id21}[10]
+Users of Python 2.3 can give a look to the new \texttt{datetime} module,
+if they are looking for a sophisticated clock/calendar.
+\end{figure}
+\begin{figure}[b]\hypertarget{id22}[11]
+There are good references on functional programming in Python;
+I suggest the Python Cookbook and the articles by David Mertz
+www.IBM.dW.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{functions-are-objects}{}
+\pdfbookmark[1]{Functions are objects}{functions-are-objects}
+\subsection*{Functions are objects}
+
+As we said in the first chapter, objects have attributes accessible with the
+dot notation. This is not surprising at all. However, it could be
+surprising to realize that since Python functions are objects, they
+can have attributes, too. This could be surprising since this feature is quite
+uncommon: typically or i) the language is
+not object-oriented, and therefore functions are not objects, or ii)
+the language is strongly object-oriented and does not have functions, only
+methods. Python is a multiparadigm language (which I prefer to the
+term ``hybrid'' language), therefore it has functions that are objects,
+as in Lisp and other functional languages.
+Consider for instance the \texttt{get{\_}time} function.
+That function has at least an useful attribute, its doctring:
+\begin{quote}
+\begin{verbatim}>>> from oopp import get_time
+>>> print get_time.func_doc
+Return the time of the system in the format HH:MM:SS\end{verbatim}
+\end{quote}
+
+The docstring can also be obtained with the \texttt{help} function:
+\begin{quote}
+\begin{verbatim}>>> help(get_time)
+Help on function get_time in module oopp:
+get_time()
+ Return the time of the system in the format HH:MM:SS\end{verbatim}
+\end{quote}
+
+Therefore \texttt{help} works on user-defined functions, too, not only on
+built-in functions. Notice that \texttt{help} also returns the argument list of
+the function. For instance, this is
+the help message on the \texttt{round} function that we will use in the
+following:
+\begin{quote}
+\begin{verbatim}>>> help(round)
+Help on built-in function round:
+round(...)
+ round(number[, ndigits]) -> floating point number
+ Round a number to a given precision in decimal digits (default 0
+ digits).This always returns a floating point number. Precision may
+ be negative.\end{verbatim}
+\end{quote}
+
+I strongly recommend Python programmers to use docstrings, not
+only for clarity sake during the development, but especially because
+it is possible to automatically generate nice HTML documentation from
+the docstrings, by using the standard tool ``pydoc''.
+
+One can easily add attributes to a function. For instance:
+\begin{quote}
+\begin{verbatim}>>> get_time.more_doc='get_time invokes the function time.asctime'
+>>> print get_time.more_doc
+get_time invokes the function time.asctime\end{verbatim}
+\end{quote}
+
+Attributes can be functions, too:
+\begin{quote}
+\begin{verbatim}>>> def IamAfunction(): print "I am a function attached to a function"
+>>> get_time.f=IamAfunction
+>>> get_time.f()
+I am a function attached to a function\end{verbatim}
+\end{quote}
+
+This is a quite impressive potentiality of Python functions, which has
+no direct equivalent in most other languages.
+
+One possible application is to fake C ``static'' variables. Suppose
+for instance we need a function remembering how may times it is
+called: we can simply use
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<double.py>}\\
+\mbox{}\\
+\mbox{def~double(x):}\\
+\mbox{~~~~try:~{\#}look~if~double.counter~is~defined}\\
+\mbox{~~~~~~~~double.counter}\\
+\mbox{~~~~except~AttributeError:}\\
+\mbox{~~~~~~~~double.counter=0~{\#}first~call}\\
+\mbox{~~~~double.counter+=1}\\
+\mbox{~~~~return~2*x}\\
+\mbox{}\\
+\mbox{double(double(2))}\\
+\mbox{print~"double~has~been~called~{\%}s~times"~{\%}~double.counter}\\
+\mbox{}\\
+\mbox{{\#}</double.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output \texttt{double has been called 2 times}.
+A more elegant approach involves closures. A closure can enhance an
+ordinary function, providing to it the capability of remembering
+the results of its previous calls and avoiding the duplication of
+computations:
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~withmemory(f):}\\
+\mbox{~~~~"""This~closure~invokes~the~callable~object~f~only~if~need~there~is"""}\\
+\mbox{~~~~argskw=[];~result=[]}\\
+\mbox{~~~~def~{\_}(*args,**kw):~}\\
+\mbox{~~~~~~~~akw=args,kw}\\
+\mbox{~~~~~~~~try:~{\#}~returns~a~previously~stored~result}\\
+\mbox{~~~~~~~~~~~~i=argskw.index(akw)}\\
+\mbox{~~~~~~~~except~ValueError:~{\#}~there~is~no~previously~stored~result}\\
+\mbox{~~~~~~~~~~~~res=f(*args,**kw)~~{\#}~returns~the~new~result}\\
+\mbox{~~~~~~~~~~~~argskw.append(akw)~{\#}~update~argskw}\\
+\mbox{~~~~~~~~~~~~result.append(res)~{\#}~update~result}\\
+\mbox{~~~~~~~~~~~~return~res}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~return~result[i]~~}\\
+\mbox{~~~~{\_}.argskw=argskw~{\#}makes~the~argskw~list~accessible~outside}\\
+\mbox{~~~~{\_}.result=result~{\#}makes~the~result~list~accessible~outside}\\
+\mbox{~~~~return~{\_}}\\
+\mbox{}\\
+\mbox{def~memoize(f):}\\
+\mbox{~~~~"""This~closure~remembers~all~f~invocations"""}\\
+\mbox{~~~~argskw,result~=~[],[]}\\
+\mbox{~~~~def~{\_}(*args,**kw):~}\\
+\mbox{~~~~~~~~akw=args,kw}\\
+\mbox{~~~~~~~~try:~{\#}~returns~a~previously~stored~result}\\
+\mbox{~~~~~~~~~~~~return~result[argskw.index(akw)]}\\
+\mbox{~~~~~~~~except~ValueError:~{\#}~there~is~no~previously~stored~result}\\
+\mbox{~~~~~~~~~~~~argskw.append(akw)~{\#}~update~argskw}\\
+\mbox{~~~~~~~~~~~~result.append(f(*args,**kw))~{\#}~update~result}\\
+\mbox{~~~~~~~~~~~~return~result[-1]~{\#}~return~the~new~result}\\
+\mbox{~~~~{\_}.argskw=argskw~{\#}makes~the~argskw~list~accessible~outside}\\
+\mbox{~~~~{\_}.result=result~{\#}makes~the~result~list~accessible~outside}\\
+\mbox{~~~~return~{\_}}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+
+Now, if we call the wrapped function \texttt{f} twice with the same arguments,
+Python can give the result without repeating the (possibly very long)
+computation.
+\begin{quote}
+\begin{verbatim}>>> def f(x):
+... print 'called f'
+... return x*x
+>>> wrapped_f=withmemory(f)
+>>> wrapped_f(2) #first call with the argument 2; executes the computation
+called f
+4
+>>> wrapped_f(2) #does not repeat the computation
+4
+>>> wrapped_f.result
+[4]
+>>> wrapped_f.argskw
+[((2,), {})]\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{profiling-functions}{}
+\pdfbookmark[1]{Profiling functions}{profiling-functions}
+\subsection*{Profiling functions}
+
+The \texttt{with{\_}clock} function provided before was intended to be
+pedagogical; as such it is a quite poor solution to the
+problem of profiling a Python routine. A better solution involves
+using two others functions in the time library, \texttt{time.time()}
+that gives that time in seconds elapsed from a given date, and
+\texttt{time.clock()} that gives the time spent by the CPU in a given
+computation. Notice that \texttt{time.clock()} has not an infinite
+precision (the precision depends on the system) and one
+should expect relatively big errors if the function runs in
+a very short time. That's the reason why it is convenient
+to execute multiple times short functions and divide the total
+time by the number of repetitions. Moreover, one should subtract the
+overhead do to the looping. This can be computed with the following
+routine:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~loop{\_}overhead(N):}\\
+\mbox{~~~~"Computes~the~time~spent~in~empty~loop~of~N~iterations"}\\
+\mbox{~~~~t0=time.clock()}\\
+\mbox{~~~~for~i~in~xrange(N):~pass}\\
+\mbox{~~~~return~time.clock()-t0}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+For instance, on my laptop an empty loop of one million of iterations
+is performed in 1.3 seconds. Typically the loop overhead is negligible,
+whereas the real problem is the function overhead.
+
+Using the attribute trick discussed above, we may
+define a \texttt{with{\_}timer} function that enhances quite a bit
+\texttt{with{\_}clock}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~with{\_}timer(func,~modulename='{\_}{\_}main{\_}{\_}',~n=1,~logfile=sys.stdout):}\\
+\mbox{~~~~"""Wraps~the~function~func~and~executes~it~n~times~(default~n=1).~}\\
+\mbox{~~~~The~average~time~spent~in~one~iteration,~express~in~milliseconds,~}\\
+\mbox{~~~~is~stored~in~the~attributes~func.time~and~func.CPUtime,~and~saved~}\\
+\mbox{~~~~in~a~log~file~which~defaults~to~the~standard~output.}\\
+\mbox{~~~~"""}\\
+\mbox{~~~~def~{\_}(*args,**kw):~{\#}~anonymous~function}\\
+\mbox{~~~~~~~~time1=time.time()}\\
+\mbox{~~~~~~~~CPUtime1=time.clock()}\\
+\mbox{~~~~~~~~print~'Executing~{\%}s.{\%}s~...'~{\%}~(modulename,func.{\_}{\_}name{\_}{\_}),}\\
+\mbox{~~~~~~~~for~i~in~xrange(n):~res=func(*args,**kw)~{\#}~executes~func~n~times}\\
+\mbox{~~~~~~~~time2=time.time()}\\
+\mbox{~~~~~~~~CPUtime2=time.clock()}\\
+\mbox{~~~~~~~~func.time=1000*(time2-time1)/n}\\
+\mbox{~~~~~~~~func.CPUtime=1000*(CPUtime2-CPUtime1-loop{\_}overhead(n))/n}\\
+\mbox{~~~~~~~~if~func.CPUtime<10:~r=3~{\#}better~rounding}\\
+\mbox{~~~~~~~~else:~r=1~{\#}default~rounding}\\
+\mbox{~~~~~~~~print~>>~logfile,~'Real~time:~{\%}s~ms'~{\%}~round(func.time,r),}\\
+\mbox{~~~~~~~~print~>>~logfile,~'~CPU~time:~{\%}s~ms'~{\%}~round(func.CPUtime,r)}\\
+\mbox{~~~~~~~~return~res}\\
+\mbox{~~~~return~{\_}}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here it is an example of application:
+\begin{quote}
+\begin{verbatim}>>> from oopp import with_timer,writefile
+>>> data='*'*1024*1024 #one megabyte
+>>> with_timer(writefile,n=1024)('datafile',data) #.
+Executing writefile ... Real time: 60.0 ms CPU time: 42.2 ms\end{verbatim}
+\end{quote}
+
+The CPU time can be quite different from the real time,
+as you can see in the following example:
+\begin{quote}
+\begin{verbatim}>>> import time
+>>> def sleep(): time.sleep(1)
+...
+>>> with_timer(sleep)() #.
+Executing sleep ... Real time: 999.7 ms CPU time: 0.0 ms\end{verbatim}
+\end{quote}
+
+We see that Python has run for 999.7 ms (i.e. 1 second, up to
+approximation errors in the system clock) during which the CPU has
+worked for 0.0 ms (i.e. the CPU took a rest ;-).
+The CPU time is the relevant time to use with the purpose of
+benchmarking Python speed.
+
+I should notice that the approach pursued in \texttt{with{\_}timer} is still
+quite simple. A better approach would be to
+plot the time versus the number of iteration, do a linear interpolation
+and extract the typical time for iteration from that. This allows
+to check visually that the machine is not doing something strange
+during the execution time and it is what
+I do in my personal benchmark routine; doing something similar is
+left as an exercise for the reader ;-).
+
+Another approach is to use the \texttt{timeit.py} module (new in Python 2.3,
+but works also with Python 2.2):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~timeit,{\_}{\_}main{\_}{\_},warnings}\\
+\mbox{}\\
+\mbox{warnings.filterwarnings('ignore',}\\
+\mbox{'import~{\textbackslash}*~only~allowed~at~module~level',SyntaxWarning)}\\
+\mbox{}\\
+\mbox{def~timeit{\_}(stmt,setup='from~{\_}{\_}main{\_}{\_}~import~*',n=1000):}\\
+\mbox{~~~~t=timeit.Timer(stmt,setup)}\\
+\mbox{~~~~try:~print~t.repeat(number=n)~{\#}~class~timeit~3~times}\\
+\mbox{~~~~except:~t.print{\_}exc()}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+It is often stated that Python is slow and quite ineffective
+in application involving hard computations. This is generally speaking
+true, but how bad is the situation ? To test the (in)efficiency of
+Python on number crunching, let me give a function to compute the
+Mandelbrot set, which I have found in the Python Frequently Asked
+Question (FAQ 4.15. \emph{Is it possible to write obfuscated one-liners
+in Python?}).
+This function is due to Ulf Bartelt and you should ask him to know how
+does it work ;-)
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~mandelbrot(row,col):}\\
+\mbox{~~~~"Computes~the~Mandelbrot~set~in~one~line"}\\
+\mbox{~~~~return~(lambda~Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(}\\
+\mbox{~~~~~~~~lambda~x,y:x+y,map(lambda~y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=}\\
+\mbox{~~~~~~~~lambda~yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,~Sx=Sx,Sy=Sy:reduce(}\\
+\mbox{~~~~~~~~lambda~x,y:x+y,map(lambda~x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,~i=i,}\\
+\mbox{~~~~~~~~Sx=Sx,F=lambda~xc,yc,x,y,k,f=lambda~xc,yc,x,y,k,f:(k<=0)}\\
+\mbox{~~~~~~~~or~(x*x+y*y>=4.0)~or~1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):}\\
+\mbox{~~~~~~~~f(xc,yc,x,y,k,f):chr(64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),}\\
+\mbox{~~~~~~~~range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(}\\
+\mbox{~~~~~~~~-2.1,~0.7,~-1.2,~1.2,~30,~col,~row)}\\
+\mbox{~~~~{\#}~~~~{\textbackslash}{\_}{\_}{\_}~{\_}{\_}{\_}/~~{\textbackslash}{\_}{\_}{\_}~{\_}{\_}{\_}/~~|~~~|~~~~|{\_}~lines~on~screen}\\
+\mbox{~~~~{\#}~~~~~~~~V~~~~~~~~~~V~~~~~~|~~~|{\_}{\_}{\_}{\_}{\_}{\_}~columns~on~screen}\\
+\mbox{~~~~{\#}~~~~~~~~|~~~~~~~~~~|~~~~~~|{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~maximum~of~"iterations"}\\
+\mbox{~~~~{\#}~~~~~~~~|~~~~~~~~~~|{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~range~on~y~axis}\\
+\mbox{~~~~{\#}~~~~~~~~|{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~range~on~x~axis}\\
+\mbox{~~~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is the benchmark on my laptop:
+\begin{quote}
+\begin{verbatim}>>> from oopp import mandelbrot,with_timer
+>>> row,col=24,75
+>>> output=with_timer(mandelbrot,n=1)(row,col)
+Executing __main__.mandelbrot ... Real time: 427.9 ms CPU time: 410.0 ms
+>>> for r in range(row): print output[r*col:(r+1)*col]
+...
+BBBBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC
+BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+BBB_____________________________________________________________OKIHGFFEEEE
+BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC\end{verbatim}
+\end{quote}
+
+I am willing to concede that this code is not typical Python code and
+actually it could be an example of \emph{bad} code, but I wanted a nice ASCII
+picture on my book ... :) Also, this prove that Python is not necessarily
+readable and easy to understand ;-)
+I leave for the courageous reader to convert the previous algorithm to C and
+measure the difference in speed ;-)
+
+
+%___________________________________________________________________________
+
+\hypertarget{about-python-speed}{}
+\pdfbookmark[1]{About Python speed}{about-python-speed}
+\subsection*{About Python speed}
+
+The best way to improved the speed is to improve the algorithm; in
+this sense Python is an ideal language since it allows you to test
+many algorithms in an incredibly short time: in other words, the time you
+would spend fighting with the compiler in other languages, in Python
+can be used to improve the algorithm.
+However in some cases, there is little to do: for instance, in many
+problems one has to run lots of loops, and Python loops are horribly
+inefficients as compared to C loops. In this case the simplest possibility
+is to use Psyco. Psyco is a specialing Python compiler written by Armin
+Rigo. It works for 386 based processors and allows Python to run loops at
+C speed. Installing Psyco requires {\$}0.00 and ten minutes of your time:
+nine minutes to find the program, download it, and install it; one
+minute to understand how to use it.
+
+The following script explains both the usage and the advantages of Psyco:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<psyco1.py>}\\
+\mbox{}\\
+\mbox{import~oopp,sys}\\
+\mbox{try:~}\\
+\mbox{~~~~import~psyco}\\
+\mbox{except~ImportError:~}\\
+\mbox{~~~~print~"Psyco~is~not~installed,~sorry."}\\
+\mbox{else:}\\
+\mbox{~~~~n=1000000~{\#}~1,000,000~loops}\\
+\mbox{}\\
+\mbox{~~~~without=oopp.loop{\_}overhead(n)~}\\
+\mbox{~~~~print~"Without~Psyco:",without}\\
+\mbox{}\\
+\mbox{~~~~psyco.bind(oopp.loop{\_}overhead)~{\#}compile~the~empty{\_}loop}\\
+\mbox{}\\
+\mbox{~~~~with=oopp.loop{\_}overhead(n)~}\\
+\mbox{~~~~print~"With~Psyco:",with}\\
+\mbox{}\\
+\mbox{~~~~print~'Speedup~=~{\%}sx'~{\%}~round(without/with,1)}\\
+\mbox{}\\
+\mbox{{\#}</psyco1.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output is impressive:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Without~Psyco:~1.3}\\
+\mbox{With~Psyco:~0.02}\\
+\mbox{Speedup~=~65.0x}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that repeating the test, you will obtain different speedups.
+On my laptop, the speedup for an empty loop of 10,000,000 of
+iteration is of the order of 70x, which is the same speed of a C loop,
+actually (I checked it). On my desktop, I have even found a speedup of
+94x !
+
+However, I must say that Psyco has some limitations. The problem is
+the function call overhead. Psyco enhances the overhead and in some
+programs it can even \emph{worsen} the performance (this is way you should
+\emph{never} use the \texttt{psyco.jit()} function that wraps all the functions of
+your program: you should only wrap the bottleneck loops). Generally speaking,
+you should expect a much more modest improvement, a factor of 2 or 3
+is what I obtain usually in my programs.
+
+Look at this second example, which essentially measure the function
+call overhead by invoking the \texttt{do{\_}nothing} function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<psyco2.py>}\\
+\mbox{}\\
+\mbox{import~oopp}\\
+\mbox{try:~}\\
+\mbox{~~~~import~psyco}\\
+\mbox{except~ImportError:}\\
+\mbox{~~~~print~"Psyco~is~not~installed,~sorry."}\\
+\mbox{else:}\\
+\mbox{~~~~n=10000~{\#}~10,000~loops}\\
+\mbox{~}\\
+\mbox{~~~~def~do{\_}nothing{\_}loop():}\\
+\mbox{~~~~~~~~for~i~in~xrange(n):~oopp.do{\_}nothing()}\\
+\mbox{}\\
+\mbox{~~~~print~"Without~Psyco:{\textbackslash}n"}\\
+\mbox{~~~~oopp.with{\_}timer(do{\_}nothing{\_}loop,n=5)()~{\#}50,000~times}\\
+\mbox{}\\
+\mbox{~~~~without=do{\_}nothing{\_}loop.CPUtime}\\
+\mbox{}\\
+\mbox{~~~~psyco.bind(do{\_}nothing{\_}loop)~}\\
+\mbox{~~~~print~"With~Psyco:{\textbackslash}n"}\\
+\mbox{~~~~oopp.with{\_}timer(do{\_}nothing{\_}loop,n=5)()~{\#}50,000~times}\\
+\mbox{}\\
+\mbox{~~~~with=do{\_}nothing{\_}loop.CPUtime}\\
+\mbox{}\\
+\mbox{~~~~print~'Speedup~=~{\%}sx'~{\%}~round(without/with,1)}\\
+\mbox{}\\
+\mbox{{\#}</psyco2.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output is less incredible:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Without~Psyco:}\\
+\mbox{Executing~do{\_}nothing{\_}loop~...~Real~time:~138.2~ms~~CPU~time:~130.0~ms}\\
+\mbox{With~Psyco:}\\
+\mbox{Executing~do{\_}nothing{\_}loop~...~Real~time:~70.0~ms~~CPU~time:~68.0~ms}\\
+\mbox{Speedup~=~1.9x}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+However, this is still impressive, if you think that you can double
+the speed of your program by adding \emph{a line} of code! Moreover this
+example is not fair since Psyco cannot improve very much the performance
+for loops invoking functions with a variable number of arguments. On the
+other hand, it can do quite a lot for loops invoking functions with
+a fixed number of arguments. I have checked that you can easily reach
+speedups of 20x (!). The only disadvantage is that a program invoking
+Psyco takes much more memory, than a normal Python program, but this
+is not a problem for most applications in nowadays computers.
+Therefore, often Psyco
+can save you the effort of going trough a C extension. In some cases,
+however, there is no hope: I leave as an exercise for the reader
+to check (at least the version 0.4.1 I am using now) is unable to
+improve the performance on the Mandelbrot set example. This proves
+that in the case bad code, there is no point in using a compiler:
+you have to improve the algorithm first !
+
+By the way, if you really want to go trough a C extension with a minimal
+departure from Python, you can use Pyrex by Greg Ewing. A Pyrex program
+is essentially a Python program with variable declarations that is
+automatically converted to C code. Alternatively, you can inline
+C functions is Python with \texttt{weave} of ...
+Finally, if you want to access C/C++ libraries, there tools
+like Swig, Booster and others.
+
+
+%___________________________________________________________________________
+
+\hypertarget{tracing-functions}{}
+\pdfbookmark[1]{Tracing functions}{tracing-functions}
+\subsection*{Tracing functions}
+
+Typically, a script contains many functions that call themselves each
+other when some conditions are satisfied. Also, typically during
+debugging things do not work the way we would like and it is not
+clear which functions are called, in which order they are called,
+and which parameters are passed. The best way to know all these
+informations, is to trace the functions in our script, and to write
+all the relevant informations in a log file. In order to keep the
+distinction between the traced functions and the original one, it
+is convenient to collect all the wrapped functions in a separate dictionary.
+The tracing of a single function can be done with a closure
+like this:
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~with{\_}tracer(function,namespace='{\_}{\_}main{\_}{\_}',output=sys.stdout,~indent=[0]):}\\
+\mbox{~~~~"""Closure~returning~traced~functions.~It~is~typically~invoked}\\
+\mbox{~~~~trough~an~auxiliary~function~fixing~the~parameters~of~with{\_}tracer."""}\\
+\mbox{~~~~def~{\_}(*args,**kw):}\\
+\mbox{~~~~~~~~name=function.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~i='~'*indent[0];~indent[0]+=4~{\#}~increases~indentation}\\
+\mbox{~~~~~~~~output.write("{\%}s[{\%}s]~Calling~'{\%}s'~with~arguments{\textbackslash}n"~{\%}~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(i,namespace,name))}\\
+\mbox{~~~~~~~~output.write("{\%}s~{\%}s~...{\textbackslash}n"~{\%}~(i,str(args)+str(kw)))}\\
+\mbox{~~~~~~~~res=function(*args,**kw)}\\
+\mbox{~~~~~~~~output.write("{\%}s[{\%}s.{\%}s]~called~with~result:~{\%}s{\textbackslash}n"}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~{\%}~(i,namespace,name,str(res)))}\\
+\mbox{~~~~~~~~indent[0]-=4~{\#}~restores~indentation}\\
+\mbox{~~~~~~~~return~res}\\
+\mbox{~~~~return~{\_}~{\#}~the~traced~function}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+
+Here is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import with_tracer
+>>> def fact(n): # factorial function
+... if n==1: return 1
+... else: return n*fact(n-1)
+>>> fact=with_tracer(fact)
+>>> fact(3)
+[__main__] Calling 'fact' with arguments
+ (3,){} ...
+ [__main__] Calling 'fact' with arguments
+ (2,){} ...
+ [__main__] Calling 'fact' with arguments
+ (1,){} ...
+ [__main__.fact] called with result: 1
+ [__main__.fact] called with result: 2
+[__main__.fact] called with result: 6
+6\end{verbatim}
+\end{quote}
+
+The logic behind \texttt{with{\_}tracer} should be clear; the only trick is the
+usage of a default list as a way to store a global indentation parameter.
+Since \texttt{indent} is mutable, the value of \texttt{indent[0]} changes at any
+recursive call of the traced function, resulting in a nested display.
+
+Typically, one wants to trace all the functions in a given module;
+this can be done trough the following function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{from~types~import~*}\\
+\mbox{}\\
+\mbox{isfunction=lambda~f:~isinstance(f,(FunctionType,BuiltinFunctionType))}\\
+\mbox{}\\
+\mbox{def~wrapfunctions(obj,wrapper,err=None,**options):}\\
+\mbox{~~~~"Traces~the~callable~objects~in~an~object~with~a~dictionary"}\\
+\mbox{~~~~namespace=options.get('namespace',getattr(obj,'{\_}{\_}name{\_}{\_}',''))}\\
+\mbox{~~~~output=options.get('output',sys.stdout)}\\
+\mbox{~~~~dic=dict([(k,wrapper(v,namespace,output))~}\\
+\mbox{~~~~~~~~~~~~~~for~k,v~in~attributes(obj).items()~if~isfunction(v)])}\\
+\mbox{~~~~customize(obj,err,**dic)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that 'wrapfunctions' accepts as first argument an object with
+a \texttt{{\_}{\_}dict{\_}{\_}} attribute (such as a module or a class) or with some
+explicit attributes (such as a simple object) and modifies it. One can
+trace a module as in this example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<tracemodule.py>}\\
+\mbox{}\\
+\mbox{import~oopp,random}\\
+\mbox{}\\
+\mbox{oopp.wrapfunctions(random,oopp.with{\_}tracer)~}\\
+\mbox{}\\
+\mbox{random.random()}\\
+\mbox{}\\
+\mbox{{\#}</tracemodule.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[random]~Calling~'random'~with~arguments}\\
+\mbox{(){\{}{\}}~...}\\
+\mbox{->~'random.random'~called~with~result:~0.175450439202}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The beauty of the present approach is its generality: 'wrap' can be
+used to add any kind of capabilities to a pre-existing module.
+For instance, we could time the functions in a module, with the
+purpose of looking at the bottlenecks. To this aim, it is enough
+to use a 'timer' nested closure:
+
+An example of calling is \texttt{wrapfunction(obj,timer,iterations=1)}.
+
+We may also compose our closures; for instance one could define a
+\texttt{with{\_}timer{\_}and{\_}tracer} closure:
+\begin{quote}
+\begin{verbatim}>>> with_timer_and_tracer=lambda f: with_timer(with_tracer(f))\end{verbatim}
+\end{quote}
+
+It should be noticed that Python comes with a standard profiler
+(in my system it is located in \texttt{/usr/local/lib/python2.2/profile.py})
+that allows to profile a script or a module (try
+python /usr/local/lib/python2.2/profile.py oopp.py)
+
+or
+\begin{quote}
+\begin{verbatim}>>> import profile; help(profile)\end{verbatim}
+\end{quote}
+
+and see the on-line documentation.
+
+
+%___________________________________________________________________________
+
+\hypertarget{tracing-objects}{}
+\pdfbookmark[1]{Tracing objects}{tracing-objects}
+\subsection*{Tracing objects}
+
+In this section, I will give a more sophisticated example, in which
+one can easily understand why the Python ability of changing methods and
+attributes during run-time, is so useful.
+As a preparation to the real example, let me
+first introduce an utility routine that allows the user
+to add tracing capabilities to a given object.
+Needless to say, this feature can be invaluable during debugging, or in trying
+to understand the behaviour of a program written by others.
+
+This routine is a little complex and needs some explanation.
+\newcounter{listcnt15}
+\begin{list}{\arabic{listcnt15}.}
+{
+\usecounter{listcnt15}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+The routine looks in the attributes of the object and try to access them.
+
+\item {}
+If the access is possible, the routines looks for methods (methods
+are recognized trough the \texttt{inspect.isroutine} function in the
+standard library) and ignores regular attributes;
+
+\item {}
+The routine try to override the original methods with improved ones,
+that possess tracing capabilities;
+
+\item {}
+the traced method is obtained with the wrapping trick discussed before.
+
+\end{list}
+
+I give now the real life example that I have anticipated before.
+Improvements and elaborations of this example can be useful to the
+professional programmer, too. Suppose you have an XML text you want
+to parse. Python provides excellent support for this kind of operation
+and various standard modules. One of the most common is the \texttt{expat}
+module (see the standard library documentation for more).
+
+If you are just starting using the module, it is certainly useful
+to have a way of tracing its behaviour; this is especially true if
+you you find some unexpected error during the parsing of a document
+(and this may happens even if you are an experience programmer ;-).
+
+The tracing routine just defined can be used to trace the parser, as
+it is exemplified in the following short script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<expat.py>}\\
+\mbox{}\\
+\mbox{import~oopp,~xml.parsers.expat,~sys}\\
+\mbox{}\\
+\mbox{{\#}~text~to~be~parsed}\\
+\mbox{text{\_}xml="""{\textbackslash}}\\
+\mbox{<?xml~version="1.0"?>}\\
+\mbox{<parent~id="dad">}\\
+\mbox{<child~name="kid">Text~goes~here</child>}\\
+\mbox{</parent>"""}\\
+\mbox{}\\
+\mbox{{\#}~a~few~do~nothing~functions}\\
+\mbox{def~start(*args):~pass}\\
+\mbox{def~end(*args):~pass}\\
+\mbox{def~handler(*args):~pass}\\
+\mbox{}\\
+\mbox{{\#}~a~parser~object}\\
+\mbox{p~=~xml.parsers.expat.ParserCreate()}\\
+\mbox{}\\
+\mbox{p.StartElementHandler~=~start}\\
+\mbox{p.EndElementHandler~=~end}\\
+\mbox{p.CharacterDataHandler~=~handler}\\
+\mbox{}\\
+\mbox{{\#}adds~tracing~capabilities~to~p}\\
+\mbox{oopp.wrapfunctions(p,oopp.with{\_}tracer,~err=sys.stdout)}\\
+\mbox{}\\
+\mbox{p.Parse(text{\_}xml)}\\
+\mbox{}\\
+\mbox{{\#}</expat.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output is:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Error:~SetBase~cannot~be~set}\\
+\mbox{Error:~Parse~cannot~be~set}\\
+\mbox{Error:~ParseFile~cannot~be~set}\\
+\mbox{Error:~GetBase~cannot~be~set}\\
+\mbox{Error:~SetParamEntityParsing~cannot~be~set}\\
+\mbox{Error:~ExternalEntityParserCreate~cannot~be~set}\\
+\mbox{Error:~GetInputContext~cannot~be~set}\\
+\mbox{[]~Calling~'start'~with~arguments}\\
+\mbox{~(u'parent',~{\{}u'id':~u'dad'{\}}){\{}{\}}~...}\\
+\mbox{[.start]~called~with~result:~None}\\
+\mbox{[]~Calling~'handler'~with~arguments}\\
+\mbox{~(u'{\textbackslash}n',){\{}{\}}~...}\\
+\mbox{[.handler]~called~with~result:~None}\\
+\mbox{[]~Calling~'start'~with~arguments}\\
+\mbox{~(u'child',~{\{}u'name':~u'kid'{\}}){\{}{\}}~...}\\
+\mbox{[.start]~called~with~result:~None}\\
+\mbox{[]~Calling~'handler'~with~arguments}\\
+\mbox{~(u'Text~goes~here',){\{}{\}}~...}\\
+\mbox{[.handler]~called~with~result:~None}\\
+\mbox{[]~Calling~'end'~with~arguments}\\
+\mbox{~(u'child',){\{}{\}}~...}\\
+\mbox{[.end]~called~with~result:~None}\\
+\mbox{[]~Calling~'handler'~with~arguments}\\
+\mbox{~(u'{\textbackslash}n',){\{}{\}}~...}\\
+\mbox{[.handler]~called~with~result:~None}\\
+\mbox{[]~Calling~'end'~with~arguments}\\
+\mbox{~(u'parent',){\{}{\}}~...}\\
+\mbox{[.end]~called~with~result:~None}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This is a case where certain methods cannot be managed with
+\texttt{getattr/setattr}, because they are internally coded in C: this
+explain the error messages at the beginning. I leave as an exercise
+for the reader to understand the rest ;-)
+
+
+%___________________________________________________________________________
+
+\hypertarget{inspecting-functions}{}
+\pdfbookmark[1]{Inspecting functions}{inspecting-functions}
+\subsection*{Inspecting functions}
+
+Python wonderful introspection features are really impressive when applied
+to functions. It is possible to extract a big deal of informations
+from a Python function, by looking at its associated \emph{code object}.
+For instance, let me consider my, \texttt{do{\_}nothing} function: its associated
+code object can be extracted from the \texttt{func{\_}code} attribute:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> co=do_nothing.func_code # extracts the code object
+>>> co
+<code object do_nothing at 0x402c5d20, file "oopp.py", line 48>
+>>> type(co)
+<type 'code'>\end{verbatim}
+\end{quote}
+
+The code object is far being trivial: the docstring says it all:
+\begin{quote}
+\begin{verbatim}>>> print type(co).__doc__
+code(argcount, nlocals, stacksize, flags, codestring, constants, names,
+ varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])
+Create a code object. Not for the faint of heart.\end{verbatim}
+\end{quote}
+
+In the case of my \texttt{do{\_}nothing} function, the code object
+possesses the following attributes:
+\begin{quote}
+\begin{verbatim}>>> print pretty(attributes(co))
+co_argcount = 0
+co_cellvars = ()
+co_code = dS
+co_consts = (None,)
+co_filename = oopp.py
+co_firstlineno = 48
+co_flags = 15
+co_freevars = ()
+co_lnotab =
+co_name = do_nothing
+co_names = ()
+co_nlocals = 2
+co_stacksize = 1
+co_varnames = ('args', 'kw')\end{verbatim}
+\end{quote}
+
+Some of these arguments are pretty technical and implementation dependent;
+however, some of these are pretty clear and useful:
+\begin{quote}
+\begin{itemize}
+\item {}
+co{\_}argcount is the total number of arguments
+
+\item {}
+co{\_}filename is the name of the file where the function is defined
+
+\item {}
+co{\_}firstlineno is the line number where the function is defined
+
+\item {}
+co{\_}name is the name of the function
+
+\item {}
+co{\_}varnames are the names
+
+\end{itemize}
+\end{quote}
+
+The programmer that it is not a ``faint of heart'' can study
+the built-in documentation on code objects; s/he should try
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{for~k,v~in~attributes(co).iteritems():~print~k,':',v.{\_}{\_}doc{\_}{\_},'{\textbackslash}n'}
+\end{flushleft}\end{ttfamily}
+
+{\#} does not work now !!
+\begin{ttfamily}\begin{flushleft}
+\mbox{add=[lambda~x,i=i:~x+i~for~i~in~range(10)]}\\
+\mbox{}\\
+\mbox{>>>~def~f(y):}\\
+\mbox{...~~~~return~lambda~x:~x+y}\\
+\mbox{...}\\
+\mbox{>>>~f(1).func{\_}closure~{\#}closure~cell~object}\\
+\mbox{(<cell~at~0x402b56bc:~int~object~at~0x811d6c8>,)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+func.defaults, closure, etc.
+
+{\#}how to extract (non-default) arguments as help does.
+
+print (lambda:None).func{\_}code.co{\_}filename
+
+One cannot change the name of a function:
+\begin{quote}
+\begin{verbatim}>>> def f(): pass
+...
+>>> f.__name__='ciao' # error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: readonly attribute\end{verbatim}
+\end{quote}
+
+However, one can create a copy with a different name:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~copyfunc(f,newname=None):~{\#}~works~under~Python~2.3}\\
+\mbox{~~~~if~newname~is~None:~newname=f.func{\_}name~{\#}~same~name}\\
+\mbox{~~~~return~FunctionType(f.func{\_}code,~globals(),~newname,~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~f.func{\_}defaults,~f.func{\_}closure)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~copyfunc(f,newname='f2')}\\
+\mbox{<function~f2~at~0x403e233c>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that the \texttt{copy} module would not do the job:
+\begin{quote}
+\begin{verbatim}>>> import copy
+>>> copy.copy(f) # error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "/usr/local/lib/python2.3/copy.py", line 84, in copy
+ y = _reconstruct(x, reductor(), 0)
+ File "/usr/local/lib/python2.3/copy_reg.py", line 57, in _reduce
+ raise TypeError, "can't pickle %s objects" % base.__name__
+TypeError: can't pickle function objects\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-beauty-of-objects}{}
+\pdfbookmark[0]{THE BEAUTY OF OBJECTS}{the-beauty-of-objects}
+\section*{THE BEAUTY OF OBJECTS}
+
+In this chapter I will show how to define generic objects in Python, and
+how to manipulate them.
+
+
+%___________________________________________________________________________
+
+\hypertarget{user-defined-objects}{}
+\pdfbookmark[1]{User defined objects}{user-defined-objects}
+\subsection*{User defined objects}
+
+In Python, one cannot directly modify methods and attributes of built-in
+types, since this would be a potentially frightening source of bugs.
+Imagine for instance of changing the sort method of a list and invoking an
+external module expecting the standard sort: all kind of hideous outcome
+could happen.
+
+Nevertheless, in Python, as in all OOP languages, the user can define
+her own kind of objects, customized to satisfy her needs. In order to
+define a new object, the user must define the class of the objects she
+needs. The simplest possible class is a do-nothing class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Object(object):}\\
+\mbox{~~~~"A~convenient~Object~class"}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Elements of the \texttt{Object} class can be created (instantiated) quite
+simply:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Object
+>>> obj1=Object()
+>>> obj1
+<oopp.Object object at 0x81580ec>
+>>> obj2=Object()
+obj2
+<object.Object object at 0x8156704>\end{verbatim}
+\end{quote}
+
+Notice that the hexadecimal number 0x81580ec is nothing else that the
+unique object reference to \texttt{obj1}
+\begin{quote}
+\begin{verbatim}>>> hex(id(obj1))
+ '0x81580ec'\end{verbatim}
+\end{quote}
+
+whereas 0x8156704 is the object reference of \texttt{obj2}:
+\begin{quote}
+\begin{verbatim}>>> hex(id(obj2))
+'0x8156704'\end{verbatim}
+\end{quote}
+
+However, at this point \texttt{obj1} and \texttt{obj2} are generic
+doing nothing objects . Nevertheless, they have
+at least an useful attribute, the class docstring:
+\begin{quote}
+\begin{verbatim}>>> obj1.__doc__ #obj1 docstring
+'A convenient Object class'
+>>> obj2.__doc__ # obj2 docstring: it's the same
+'A convenient Object class'\end{verbatim}
+\end{quote}
+
+Notice that the docstring is associate to the class and therefore all
+the instances share the same docstring, unless one explicitly assigns
+a different docstring to some instance. \texttt{{\_}{\_}doc{\_}{\_}}
+is a class attribute (or a static attribute for readers familiar with the
+C++/Java terminology) and the expression is actually syntactic sugar for
+\begin{quote}
+\begin{verbatim}>>> class Object(object): # with explicit assignement to __doc__
+... __doc__ = "A convenient Object class"\end{verbatim}
+\end{quote}
+
+Since instances of 'Object' can be modified, I can transform them in
+anything I want. For instance, I can create a simple clock:
+\begin{quote}
+\begin{verbatim}>>> myclock=Object()
+>>> myclock
+<__main__.Object object at 0x8124614>\end{verbatim}
+\end{quote}
+
+A minimal clock should at least print the current time
+on the system. This is given by the \texttt{get{\_}time} function
+we defined in the first chapter. We may ``attach'' that function
+to our clock as follows:
+\begin{quote}
+\begin{verbatim}>>> import oopp
+>>> myclock.get_time=oopp.get_time
+>>> myclock.get_time # this is a function, not a method
+<function get_time at 0x815c40c>\end{verbatim}
+\end{quote}
+
+In other words, we have converted the \texttt{oopp.get{\_}time} function to a
+\texttt{get{\_}time} function of the object \texttt{myclock}. The procedure works
+\begin{quote}
+\begin{verbatim}>>> myclock.get_time()
+'15:04:57'\end{verbatim}
+\end{quote}
+
+but has a disadvantage: if we instantiate another
+clock
+\begin{quote}
+\begin{verbatim}>>> from oopp import Object
+>>> otherclock=Object()\end{verbatim}
+\end{quote}
+
+the other clock will \texttt{not} have a get{\_}time method:
+\begin{quote}
+\begin{verbatim}>>> otherclock.get_time() #first attempt; error
+AttributeError: 'Object' object has no attribute 'get_time'\end{verbatim}
+\end{quote}
+
+Notice instead that the docstring is a \emph{class attribute}, i.e. it
+is defined both for the class and \emph{all instances} of the class,
+therefore even for \texttt{otherclock}:
+\begin{quote}
+\begin{verbatim}>>> Object.__doc__
+'A convenient Object class'
+>>> otherclock.__doc__
+'A convenient Object class'\end{verbatim}
+\end{quote}
+
+We would like to convert the \texttt{get{\_}time} function to a
+\texttt{get{\_}time} method for the \emph{entire} class 'Object', i.e. for all its
+instances. Naively, one would be tempted to write the following:
+\begin{quote}
+\begin{verbatim}>>> Object.get_time=oopp.get_time\end{verbatim}
+\end{quote}
+
+However this would not work:
+\begin{quote}
+\begin{verbatim}>>> otherclock.get_time() #second attempt; still error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: oopp.get_time() takes no arguments (1 given)\end{verbatim}
+\end{quote}
+
+This error message is something that all Python beginners encounter
+(and sometimes even non-beginners ;-). The solution is to introduce
+an additional argument:
+\begin{quote}
+\begin{verbatim}>>> Object.get_time=lambda self : oopp.get_time()
+>>> otherclock.get_time # this is method now, not a function
+<bound method Object.<lambda> of <__main__.Object object at 0x815881c>>
+>>> otherclock.get_time() #third attempt
+'15:28:41'\end{verbatim}
+\end{quote}
+
+Why this works ? The explanation is the following:
+when Python encounters an expression of the form
+\texttt{objectname.methodname()} it looks if there is a already a method
+\emph{attached} to the object:
+\begin{quote}
+\newcounter{listcnt16}
+\begin{list}{\alph{listcnt16}.}
+{
+\usecounter{listcnt16}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+if yes it invokes it with no arguments
+(this is why our first example worked);
+
+\item {}
+if not it looks at the class of the object; if there is a method
+bound to the class it invokes that method \emph{by passing the
+object as first argument}.
+
+\end{list}
+\end{quote}
+
+When we invoked \texttt{otherclock.get{\_}time()} in our second attempt, Python
+found that the function \texttt{get{\_}time} was defined at the class level,
+and sent it the \texttt{otherclock} object as first argument: however \texttt{get{\_}time}
+was bind to \texttt{func{\_}get{\_}time}, which is function with \emph{no} arguments: whence
+the error message. The third attempt worked since, thanks to the
+lambda function trick, the \texttt{get{\_}time} function has been converted to
+a function accepting a first argument.
+
+Therefore that's the rule: in Python, one can define methods
+at the class level, provided one explitely introduces a first argument
+containing the object on which the method is invoked.
+
+This first argument is traditionally called \texttt{self}; the name 'self' is not
+enforced, one could use any other valid Python identifier, however the
+convention is so widespread that practically everybody uses it;
+pychecker will even raise a warning in the case you don't follow the
+convention.
+
+I have just shown one the most interesting features of Python, its
+\emph{dynamicity}: you can create the class first and add methods to it later.
+That logic cannot be followed in typical compiled language as C++. On the
+other hand, one can also define methods in a static, more traditional way:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<clock1.py>}\\
+\mbox{}\\
+\mbox{"Shows~how~to~define~methods~inside~the~class~(statically)"}\\
+\mbox{}\\
+\mbox{import~oopp}\\
+\mbox{}\\
+\mbox{class~Clock(object):}\\
+\mbox{~~~~'Clock~class;~version~0.1'}\\
+\mbox{~~~~def~get{\_}time(self):~{\#}~method~defined~inside~the~class}\\
+\mbox{~~~~~~~~return~oopp.get{\_}time()}\\
+\mbox{}\\
+\mbox{myclock=Clock()~{\#}creates~a~Clock~instance}\\
+\mbox{print~myclock.get{\_}time()~{\#}~print~the~current~time}\\
+\mbox{}\\
+\mbox{{\#}</clock1.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this case we have defined the \texttt{get{\_}time} method inside the class as a
+normal function with an explicit first argument called self; this is
+entirely equivalent to the use of a lambda function.
+
+The syntax \texttt{myclock.get{\_}time()} is actually syntactic sugar for
+\texttt{Clock.get{\_}time(myclock)}.
+
+In this second form, it is clear the \texttt{get{\_}time} is really ``attached'' to the
+class, not to the instance.
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-static-methods-and-classmethods}{}
+\pdfbookmark[1]{Objects have static methods and classmethods}{objects-have-static-methods-and-classmethods}
+\subsection*{Objects have static methods and classmethods}
+\begin{quote}
+\begin{flushleft}
+\emph{There~should~be~one--and~preferably~only~one--obvious~way~to~do~it}~\\
+--~Tim~Peters,~\emph{The~Zen~of~Python}.
+\end{flushleft}
+\end{quote}
+
+For any rule, there is an exception, and despite the Python's motto
+there are many ways to define methods in classes. The way I presented
+before was the obvious one before the Python 2.2 revolution; however,
+nowadays there is another possibility that, even if less obvious, has the
+advantage of some elegance (and it is also slightly more efficient too, even if
+efficiency if never a primary concern for a Python programmer).
+We see that the first argument in the \texttt{get{\_}time} method is useless,
+since the time is computed from the \texttt{time.asctime()} function which
+does not require any information about the object that is calling
+it. This waste is ugly, and since according to the Zen of Python
+\begin{quote}
+
+\emph{Beautiful is better than ugly.}
+\end{quote}
+
+we should look for another way. The solution is to use a \emph{static method}:
+when a static method is invoked, the calling object is \emph{not} implicitly passed
+as first argument. Therefore we may use a normal function with no additional
+first argument to define the \texttt{get{\_}time} method:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Clock(object):}\\
+\mbox{~~~~'Clock~with~a~staticmethod'}\\
+\mbox{~~~~get{\_}time=staticmethod(get{\_}time)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is how it works:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Clock
+>>> Clock().get_time() # get_time is bound both to instances
+'10:34:23'
+>>> Clock.get_time() # and to the class
+'10:34:26'\end{verbatim}
+\end{quote}
+
+The staticmethod idiom converts the lambda function to a
+static method of the class 'Clock'. Notice that one can avoid the
+lambda expression and use the (arguably more Pythonic) idiom
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~get{\_}time()}\\
+\mbox{~~~~return~oopp.get{\_}time()}\\
+\mbox{get{\_}time=staticmethod(oopp.get{\_}time)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+as the documentation suggests:
+\begin{quote}
+\begin{verbatim}>>> print staticmethod.__doc__
+staticmethod(function) -> method
+Convert a function to be a static method.
+A static method does not receive an implicit first argument.
+To declare a static method, use this idiom:
+ class C:
+ def f(arg1, arg2, ...): ...
+ f = staticmethod(f)
+It can be called either on the class (e.g. C.f()) or on an instance
+(e.g. C().f()). The instance is ignored except for its class.
+Static methods in Python are similar to those found in Java or C++.
+For a more advanced concept, see the classmethod builtin.\end{verbatim}
+\end{quote}
+
+At the present the notation for static methods is still rather ugly,
+but it is expected to improve in future versions of Python (probably
+in Python 2.4). Documentation for static methods can
+be found in Guido's essay and in the PEP.. : however this is intended for
+developers.
+
+As the docstring says, static methods are also ``attached'' to the
+class and may be called with the syntax \texttt{Clock.get{\_}time()}.
+
+A similar remark applies for the so called \emph{classmethods}:
+\begin{quote}
+\begin{verbatim}>>> print classmethod.__doc__
+classmethod(function) -> method
+Convert a function to be a class method.
+A class method receives the class as implicit first argument,
+just like an instance method receives the instance.
+To declare a class method, use this idiom:
+class C:
+ def f(cls, arg1, arg2, ...): ...
+ f = classmethod(f)
+It can be called either on the class (e.g. C.f()) or on an instance
+(e.g. C().f()). The instance is ignored except for its class.
+If a class method is called for a derived class, the derived class
+object is passed as the implied first argument.
+Class methods are different than C++ or Java static methods.
+If you want those, see the staticmethod builtin.\end{verbatim}
+\end{quote}
+
+{\#}When a regular method is invoked, a reference to the calling object is
+{\#}implicitely passed as first argument; instead, when a static method is
+{\#}invoked, no reference to the calling object is passed.
+
+As the docstring says, classmethods are convenient when one wants to pass
+to a method the calling \emph{class}, not the calling object. Here there is an
+example:
+\begin{quote}
+\begin{verbatim}>>> class Clock(object): pass
+>>> Clock.name=classmethod(lambda cls: cls.__name__)
+>>> Clock.name() # called by the class
+'Clock'
+>>> Clock().name() # called by an instance
+'Clock'\end{verbatim}
+\end{quote}
+
+Notice that classmethods (and staticmethods too)
+can only be attached to classes, not to objects:
+\begin{quote}
+\begin{verbatim}>>> class Clock(object): pass
+>>> c=Clock()
+>>> c.name=classmethod(lambda cls: cls.__name__)
+>>> c.name() #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: 'classmethod' object is not callable\end{verbatim}
+\end{quote}
+
+gives a TypeError. The reason is that classmethods and staticmethods
+are implemented
+trough \emph{attribute descriptors}. This concept will be discussed in detail in a
+forthcoming in chapter 6.
+
+Notice that classmethods are not proving any fundamental feature, since
+one could very well use a normal method and retrieve the class with
+\texttt{self.{\_}{\_}class{\_}{\_}} as we did in the first chapter.
+Therefore, we could live without (actually, I think they are a non-essential
+complication to the language).
+Nevertheless, now that we have them, we can use them, since
+they come handy in various circumstances, as we will see in the following.
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-their-privacy}{}
+\pdfbookmark[1]{Objects have their privacy}{objects-have-their-privacy}
+\subsection*{Objects have their privacy}
+
+In some situations, it is convenient to give to the developer
+some information that should be hided to the final user. To this
+aim Python uses private names (i.e. names starting with a single
+underscore) and private/protected attributes (i.e. attributes starting with
+a double underscore).
+
+Consider for instance the following script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<privacy.py>}\\
+\mbox{}\\
+\mbox{import~time}\\
+\mbox{}\\
+\mbox{class~Clock(object):}\\
+\mbox{~~~~{\_}{\_}secret="This~Clock~is~quite~stupid."}\\
+\mbox{}\\
+\mbox{myclock=Clock()}\\
+\mbox{try:~print~myclock.{\_}{\_}secret}\\
+\mbox{except~Exception,e:~print~"AttributeError:",e}\\
+\mbox{}\\
+\mbox{{\#}</privacy.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{AttributeError:~'Clock'~object~has~no~attribute~'{\_}{\_}secret'}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Therefore, even if the Clock object \emph{does} have a \texttt{{\_}{\_}secret} attribute,
+the user cannot access it ! In this way she cannot discover that
+actually ``This Clock is quite stupid.''
+
+In other programming languages, attributes like \texttt{{\_}{\_}secret} are
+called ``private'' attributes. However, in Python private attributes
+are not really private and their secrets can be accessed with very
+little effort.
+
+First of all, we may notice that \texttt{myclock} really contains a secret
+by using the builtin function \texttt{dir()}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{dir(myclock)}\\
+\mbox{['{\_}Clock{\_}{\_}secret',~'{\_}{\_}class{\_}{\_}',~'{\_}{\_}delattr{\_}{\_}',~'{\_}{\_}dict{\_}{\_}',~'{\_}{\_}doc{\_}{\_}',~}\\
+\mbox{~'{\_}{\_}getattribute{\_}{\_}',~'{\_}{\_}hash{\_}{\_}',~'{\_}{\_}init{\_}{\_}',~'{\_}{\_}module{\_}{\_}',~'{\_}{\_}new{\_}{\_}',~}\\
+\mbox{~'{\_}{\_}reduce{\_}{\_}',~'{\_}{\_}repr{\_}{\_}',~'{\_}{\_}setattr{\_}{\_}',~'{\_}{\_}str{\_}{\_}',~'{\_}{\_}weakref{\_}{\_}']}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+We see that the first attribute of myclock is '{\_}Clock{\_}{\_}secret``,
+which we may access directly:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{print~myclock.{\_}Clock{\_}{\_}secret}\\
+\mbox{This~clock~is~quite~stupid.}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+We see here the secret of private variables in Python: the \emph{name mangling}.
+When Python sees a name starting with two underscores (and not ending
+with two underscores, otherwise it would be interpreted as a special
+attribute), internally it manage it as \texttt{{\_}Classname{\_}{\_}privatename}.
+Notice that if 'Classname' begins with underscores, the leading underscores
+are stripped in such a way to guarantee that the private name starts with
+only \emph{one} underscore. For instance, the '{\_}{\_}secret' private attribute
+of classes such as 'Clock', '{\_}Clock', '{\_}{\_}Clock', '{\_}{\_}{\_}Clock', etc. is
+mangled to '{\_}Clock{\_}{\_}secret'.
+
+Private names in Python are \emph{not} intended to keep secrets: they
+have other uses.
+\newcounter{listcnt17}
+\begin{list}{\arabic{listcnt17}.}
+{
+\usecounter{listcnt17}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+On one hand, private names are a suggestion to the developer.
+When the Python programmer sees a name starting with one or two
+underscores in a program written by others, she understands
+that name should not be of concern for the final user, but it
+only concerns the internal implementation.
+
+\item {}
+On the other hand, private names are quite useful in class
+inheritance, since they provides safety with respect to the overriding
+operation. This point we will discussed in the next chapter.
+
+\item {}
+Names starting with one (or more) underscores are not imported by the
+statement \texttt{from module import *}
+
+\end{list}
+
+Remark: it makes no sense to define names with double underscores
+outside classes, since the name mangling doesn't work in this case.
+Let me show an example:
+\begin{quote}
+\begin{verbatim}>>> class Clock(object): __secret="This Clock is quite stupid"
+>>> def tellsecret(self): return self.__secret
+>>> Clock.tellsecret=tellsecret
+>>> Clock().tellsecret() #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 2, in tellsecret
+AttributeError: 'Clock' object has no attribute '__secret'\end{verbatim}
+\end{quote}
+
+The explanation is that since \texttt{tellsecret()} is defined outside the class,
+\texttt{{\_}{\_}secret} is not expanded to \texttt{{\_}Clock{\_}{\_}secret} and therefore cannot be
+retrieved, whereas
+\begin{quote}
+\begin{verbatim}>>> class Clock(object):
+... __secret="This Clock is quite stupid"
+... def tellsecret(self): return self.__secret
+>>> Clock().tellsecret()
+This Clock is quite stupid\end{verbatim}
+\end{quote}
+
+will work. In other words, private variables are attached to classes,
+not objects.
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-properties}{}
+\pdfbookmark[1]{Objects have properties}{objects-have-properties}
+\subsection*{Objects have properties}
+
+In the previous section we have shown that private variables are of
+little use for keeping secrets: if a developer really wants to restrict
+the access to some methods or attributes, she has to resort to
+\emph{properties}.
+
+Let me show an example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<secret.py>}\\
+\mbox{}\\
+\mbox{import~oopp}\\
+\mbox{}\\
+\mbox{class~Clock(object):~}\\
+\mbox{~~~~'Clock~class~with~a~secret'}\\
+\mbox{~~}\\
+\mbox{~~~~you{\_}know{\_}the{\_}pw=False~{\#}default}\\
+\mbox{~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~def~give{\_}pw(self,pw):}\\
+\mbox{~~~~~~~~"""Check~if~your~know~the~password.~For~security,~one~should~crypt}\\
+\mbox{~~~~~~~~the~password."""}\\
+\mbox{~~~~~~~~self.you{\_}know{\_}the{\_}pw=(pw=="xyz")}\\
+\mbox{~~~~~~}\\
+\mbox{~~~~def~get{\_}secret(self):}\\
+\mbox{~~~~~~~~if~self.you{\_}know{\_}the{\_}pw:}\\
+\mbox{~~~~~~~~~~~~return~"This~clock~doesn't~work."}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~return~"You~must~give~the~right~password~to~access~'secret'"}\\
+\mbox{~~~~~~}\\
+\mbox{~~~~secret=property(get{\_}secret)}\\
+\mbox{}\\
+\mbox{c=Clock()}\\
+\mbox{print~c.secret~{\#}~=>~You~must~give~the~right~password~to~access~'secret'}\\
+\mbox{c.give{\_}pw('xyz')~{\#}~gives~the~right~password}\\
+\mbox{print~c.secret~{\#}~=>~This~clock~doesn't~work.}\\
+\mbox{print~Clock.secret~{\#}~=>~<property~object~at~0x814c1b4>}\\
+\mbox{}\\
+\mbox{{\#}</secret.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this script, one wants to restrict the access to the attribute
+'secret', which can be accessed only is the user provide the
+correct password. Obviously, this example is not very secure,
+since I have hard coded the password 'xyz' in the source code,
+which is easily accessible. In reality, one should crypt the
+password a perform a more sophisticated test than the trivial
+check \texttt{(pw=="xyz")}; anyway, the example is only intended to
+shown the uses of properties, not to be really secure.
+
+The key action is performed by the descriptor class \texttt{property} that
+converts the function \texttt{get{\_}secret} in a property object. Additional
+informations on the usage of \texttt{property} can be obtained from the
+docstring:
+\begin{quote}
+\begin{verbatim}>>> print property.__doc__
+property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
+fget is a function to be used for getting an attribute value, and likewise
+fset is a function for setting, and fdel a function for del'ing, an
+attribute. Typical use is to define a managed attribute x:
+class C(object):
+ def getx(self): return self.__x
+ def setx(self, value): self.__x = value
+ def delx(self): del self.__x
+ x = property(getx, setx, delx, "I'm the 'x' property.")\end{verbatim}
+\end{quote}
+
+Properties are another example of attribute descriptors.
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-have-special-methods}{}
+\pdfbookmark[1]{Objects have special methods}{objects-have-special-methods}
+\subsection*{Objects have special methods}
+
+From the beginning, we stressed that objects have special attributes that
+may turn handy, as for instance the docstring \texttt{{\_}{\_}doc{\_}{\_}} and the class
+name attribute \texttt{{\_}{\_}class{\_}{\_}}. They have special methods, too.
+
+With little doubt, the most useful special method is the \texttt{{\_}{\_}init{\_}{\_}}
+method, that \emph{initializes} an object right after its creation. \texttt{{\_}{\_}init{\_}{\_}}
+is typically used to pass parameters to \emph{object factories}. Let me an
+example with geometric figures:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{~~}\\
+\mbox{class~GeometricFigure(object):~{\#}an~example~of~object~factory}\\
+\mbox{~~~~"""This~class~allows~to~define~geometric~figures~according~to~their}\\
+\mbox{~~~~equation~in~the~cartesian~plane.~It~will~be~extended~later."""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,equation,**parameters):}\\
+\mbox{~~~~~~~~"Specify~the~cartesian~equation~of~the~object~and~its~parameters"}\\
+\mbox{~~~~~~~~self.eq=equation}\\
+\mbox{~~~~~~~~self.par=parameters}\\
+\mbox{~~~~~~~~for~k,v~in~self.par.items():~{\#}replaces~the~parameters~in~the~equation}\\
+\mbox{~~~~~~~~~~~~self.eq=self.eq.replace(k,str(v))}\\
+\mbox{~~~~~~~~self.contains=eval('lambda~x,y~:~'+self.eq)~}\\
+\mbox{~~~~~~~~{\#}~dynamically~creates~the~function~'contains'}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here it is how it works:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> disk=GeometricFigure('(x-x0)**2+(y-y0)**2 <= r**2', x0=0,y0=0,r=5)
+>>> # creates a disk of radius 5 centered in the origing
+>>> disk.contains(1,2) #asks if the point (1,2) is inside the disk
+True
+>>> disk.contains(4,4) #asks if the point (4,4) is inside the disk
+False\end{verbatim}
+\end{quote}
+
+Let me continue the section on special methods with some some observations on
+\texttt{{\_}{\_}repr{\_}{\_}} and \texttt{{\_}{\_}str{\_}{\_}}.Notice that I
+will not discuss all the subtleties; for a thought discussion, see the
+thread ``Using {\_}{\_}repr{\_}{\_} or {\_}{\_}str{\_}{\_}'' in c.l.p. (Google is your friend).
+The following discussion applies to new style classes, old style classes
+are subtly different; moreover.
+
+When one writes
+\begin{quote}
+\begin{verbatim}>>> disk
+<oopp.GeometricFigure instance at 0x81b496c>\end{verbatim}
+\end{quote}
+
+one obtains the \emph{string representation} of the object. Actually, the previous
+line is syntactic sugar for
+\begin{quote}
+\begin{verbatim}>>> print repr(disk)
+<oopp.GeometricFigure instance at 0x81b496c>\end{verbatim}
+\end{quote}
+
+or
+\begin{quote}
+\begin{verbatim}>>> print disk.__repr__()
+<oopp.GeometricFigure instance at 0x81b496c>\end{verbatim}
+\end{quote}
+
+The \texttt{repr} function extracts the string representation from the
+the special method \texttt{{\_}{\_}repr{\_}{\_}}, which can be redefined in order to
+have objects pretty printed. Notice that \texttt{repr} is conceptually
+different from the \texttt{str} function that controls the output of the \texttt{print}
+statement. Actually, \texttt{print o} is syntactic sugar for \texttt{print str(o)}
+which is sugar for \texttt{print o.{\_}{\_}str{\_}{\_}()}.
+
+If for instance we define
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~PrettyPrinted(object):}\\
+\mbox{~~~~formatstring='{\%}s'~{\#}~default}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~"""Returns~the~name~of~self~in~quotes,~possibly~formatted~via~}\\
+\mbox{~~~~~~~~self.formatstring.~If~self~has~no~name,~returns~the~name~}\\
+\mbox{~~~~~~~~of~its~class~in~angular~brackets."""~}\\
+\mbox{~~~~~~~~try:~{\#}look~if~the~selfect~has~a~name~}\\
+\mbox{~~~~~~~~~~~name="'{\%}s'"~{\%}~self.{\_}{\_}name{\_}{\_}~}\\
+\mbox{~~~~~~~~except~AttributeError:~{\#}if~not,~use~the~name~of~its~class}\\
+\mbox{~~~~~~~~~~~~name='<{\%}s>'~{\%}~type(self).{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~if~hasattr(self,'formatstring'):}\\
+\mbox{~~~~~~~~~~~~return~self.formatstring~{\%}~name}\\
+\mbox{~~~~~~~~else:~}\\
+\mbox{~~~~~~~~~~~~return~name}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+then we have
+\begin{quote}
+\begin{verbatim}>>> from oopp import PrettyPrinted
+>>> o=PrettyPrinted() # o is an instance of PrettyPrinted
+>>> print o #invokes o.__str__() which in this case returns o.__class__.name
+<PrettyPrinted>\end{verbatim}
+\end{quote}
+
+whereas
+\begin{quote}
+\begin{verbatim}>>> o # i.e. print repr(o)
+<oopp.PrettyPrinted object at 0x400a006c>\end{verbatim}
+\end{quote}
+
+However, in most cases \texttt{{\_}{\_}repr{\_}{\_}} and \texttt{{\_}{\_}str{\_}{\_}} gives the same
+output, since if \texttt{{\_}{\_}str{\_}{\_}} is not explicitely defined it defaults
+to \texttt{{\_}{\_}repr{\_}{\_}}. Therefore, whereas modifying \texttt{{\_}{\_}str{\_}{\_}}
+does not change \texttt{{\_}{\_}repr{\_}{\_}}, modifying \texttt{{\_}{\_}repr{\_}{\_}} changes \texttt{{\_}{\_}str{\_}{\_}},
+if \texttt{{\_}{\_}str{\_}{\_}} is not explicitely given:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<fairytale1.py>}\\
+\mbox{}\\
+\mbox{"{\_}{\_}repr{\_}{\_}~can~also~be~a~regular~method,~not~a~classmethod"}\\
+\mbox{}\\
+\mbox{class~Frog(object):}\\
+\mbox{~~~~attributes="poor,~small,~ugly"}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+self.attributes+'~'+self.{\_}{\_}class{\_}{\_}.{\_}{\_}name{\_}{\_}}\\
+\mbox{}\\
+\mbox{class~Prince(object):}\\
+\mbox{~~~~attributes='rich,~tall,~beautiful'}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+self.attributes+'~'+self.{\_}{\_}class{\_}{\_}.{\_}{\_}name{\_}{\_}}\\
+\mbox{}\\
+\mbox{jack=Frog();~print~repr(jack),jack}\\
+\mbox{charles=Prince();~print~repr(charles),charles~~}\\
+\mbox{}\\
+\mbox{{\#}</fairytale1.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{<Frog~object~at~0x81866ec>~I~am~a~poor,~small,~ugly~Frog}\\
+\mbox{<Prince~object~at~0x818670c>~I~am~a~rich,~tall,~beautiful~Prince}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+for jack and charles respectively.
+
+\texttt{{\_}{\_}str{\_}{\_}} and \texttt{{\_}{\_}repr{\_}{\_}} are also called by the formatting
+operators ``{\%}s'' and ``{\%}r''.
+
+Notice that i) \texttt{{\_}{\_}str{\_}{\_}} can be most naturally
+rewritten as a class method; ii) Python is magic:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<fairytale2.py>}\\
+\mbox{~~}\\
+\mbox{"""Shows~two~things:~}\\
+\mbox{~~~~1)~redefining~{\_}{\_}repr{\_}{\_}~automatically~changes~the~output~of~{\_}{\_}str{\_}{\_}}\\
+\mbox{~~~~2)~the~class~of~an~object~can~be~dinamically~changed!~"""}\\
+\mbox{}\\
+\mbox{class~Frog(object):}\\
+\mbox{~~~~attributes="poor,~small,~ugly"}\\
+\mbox{~~~~def~{\_}{\_}repr{\_}{\_}(cls):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+cls.attributes+'~'+cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~{\_}{\_}repr{\_}{\_}=classmethod({\_}{\_}repr{\_}{\_})}\\
+\mbox{}\\
+\mbox{class~Prince(object):}\\
+\mbox{~~~~attributes='rich,~tall,~beautiful'}\\
+\mbox{~~~~def~{\_}{\_}repr{\_}{\_}(cls):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+cls.attributes+'~'+cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~{\_}{\_}repr{\_}{\_}=classmethod({\_}{\_}repr{\_}{\_})}\\
+\mbox{}\\
+\mbox{def~princess{\_}kiss(frog):}\\
+\mbox{~~~~~~frog.{\_}{\_}class{\_}{\_}=Prince}\\
+\mbox{}\\
+\mbox{jack=Frog()}\\
+\mbox{princess{\_}kiss(jack)}\\
+\mbox{print~jack~{\#}~the~same~as~repr(jack)}\\
+\mbox{}\\
+\mbox{{\#}</fairytale2.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now the output for jack is ``I am a rich, tall, beautiful Prince'' !
+In Python you may dynamically change the class of an object!!
+
+Of course, this is a feature to use with care ;-)
+
+There are many others special methods, such as {\_}{\_}new{\_}{\_}, {\_}{\_}getattr{\_}{\_},
+{\_}{\_}setattr{\_}{\_}, etc. They will be discussed in the next chapters, in
+conjunction with inheritance.
+
+
+%___________________________________________________________________________
+
+\hypertarget{objects-can-be-called-added-subtracted}{}
+\pdfbookmark[1]{Objects can be called, added, subtracted, ...}{objects-can-be-called-added-subtracted}
+\subsection*{Objects can be called, added, subtracted, ...}
+
+Python provides a nice generalization of functions, via the concept
+of \emph{callable objects}. A callable object is an object with a \texttt{{\_}{\_}call{\_}{\_}}
+special method. They can be used to define ``functions'' that remember
+how many times they are invoked:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<call.py>}\\
+\mbox{}\\
+\mbox{class~MultiplyBy(object):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,n):}\\
+\mbox{~~~~~~~~self.n=n}\\
+\mbox{~~~~~~~~self.counter=0}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,x):}\\
+\mbox{~~~~~~~~self.counter+=1}\\
+\mbox{~~~~~~~~return~self.n*x}\\
+\mbox{}\\
+\mbox{double=MultiplyBy(2)}\\
+\mbox{res=double(double(3))~{\#}~res=12}\\
+\mbox{print~"double~is~callable:~{\%}s"~{\%}~callable(double)}\\
+\mbox{print~"You~have~called~double~{\%}s~times."~{\%}~double.counter}\\
+\mbox{}\\
+\mbox{{\#}</call.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{double~is~callable:~~True}\\
+\mbox{You~have~called~double~2~times.}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The script also show that callable objects (including functions)
+can be recognized with the \texttt{callable} built-in function.
+
+Callable object solves elegantly the problem of having ``static'' variables
+inside functions (cfr. with the 'double' example in chapter 2).
+A class with a \texttt{{\_}{\_}call{\_}{\_}} method can be used to generate an entire
+set of customized ``functions''. For this reason, callable objects are
+especially useful in the conjunction with object factories. Let me show
+an application to my factory of geometric figures:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Makeobj(object):}\\
+\mbox{~~~~"""A~factory~of~object~factories.~Makeobj(cls)~returns~instances}\\
+\mbox{~~~~~of~cls"""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,cls,*args):}\\
+\mbox{~~~~~~~~self.cls=cls}\\
+\mbox{~~~~~~~~self.args=args}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,**pars):}\\
+\mbox{~~~~~~~~return~self.cls(*self.args,**pars)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{{\#}<factory.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~Makeobj,GeometricFigure}\\
+\mbox{}\\
+\mbox{makedisk=Makeobj(GeometricFigure,'(x-x0)**2+(y-y0)**2<r**2')}\\
+\mbox{makesquare=Makeobj(GeometricFigure,'abs(x-x0)<L~and~abs(y-y0)<L')}\\
+\mbox{disk=makedisk(x0=0,y0=0,r=10)~{\#}~make~a~disk~of~radius~10}\\
+\mbox{square=makesquare(x0=0,y0=0,L=20)~{\#}~make~a~disk~of~side~10}\\
+\mbox{}\\
+\mbox{print~disk.contains(9,9)~{\#}~=>~False}\\
+\mbox{print~square.contains(9,9)~{\#}~=>~True}\\
+\mbox{{\#}etc.}\\
+\mbox{}\\
+\mbox{{\#}</factory.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This factory generates callable objects, such as \texttt{makedisk} and
+\texttt{makesquare} that returns geometric objects. It gives a nicer interface
+to the object factory provided by 'GeometricFigure'.
+
+Notice that the use of the expression \texttt{disk.contains(9,9)} in order to
+know if the point of coordinates (9,9) is contained in the disk, it is
+rather inelegant: it would be much better to be able to ask if
+\texttt{(9,9) in disk}. This is possibile, indeed: and the secrets is to
+define the special method \texttt{{\_}{\_}contains{\_}{\_}}. This is done in the next
+example, that I think give a good taste of the beauty of objects
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<funnyformatter.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~Makeobj}\\
+\mbox{}\\
+\mbox{Nrow=50;~Ncol=78}\\
+\mbox{~~}\\
+\mbox{class~GeometricFigure(object):}\\
+\mbox{~~~~"""This~class~allows~to~define~geometric~figures~according~to~their}\\
+\mbox{~~~~equation~in~the~cartesian~plane.~Moreover~addition~and~subtraction}\\
+\mbox{~~~~of~geometric~figures~are~defined~as~union~and~subtraction~of~sets."""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,equation,**parameters):}\\
+\mbox{~~~~~~~~"Initialize~"}\\
+\mbox{~~~~~~~~self.eq=equation}\\
+\mbox{~~~~~~~~self.par=parameters}\\
+\mbox{~~~~~~~~for~(k,v)~in~self.par.items():~{\#}replaces~the~parameters}\\
+\mbox{~~~~~~~~~~~~self.eq=self.eq.replace(k,str(v))}\\
+\mbox{~~~~~~~~self.contains=eval('lambda~x,y~:~'+self.eq)}\\
+\mbox{~~~~def~combine(self,fig,operator):}\\
+\mbox{~~~~~~~~"""Combine~self~with~the~geometric~figure~fig,~using~the}\\
+\mbox{~~~~~~~~operators~"or"~(addition)~and~"and~not"~(subtraction)"""}\\
+\mbox{~~~~~~~~comboeq="("+self.eq+")"+operator+"("+fig.eq+")"}\\
+\mbox{~~~~~~~~return~GeometricFigure(comboeq)}\\
+\mbox{~~~~def~{\_}{\_}add{\_}{\_}(self,fig):}\\
+\mbox{~~~~~~~~"Union~of~sets"}\\
+\mbox{~~~~~~~~return~self.combine(fig,'~or~')}\\
+\mbox{~~~~def~{\_}{\_}sub{\_}{\_}(self,fig):}\\
+\mbox{~~~~~~~~"Subtraction~of~sets"}\\
+\mbox{~~~~~~~~return~self.combine(fig,'~and~not')}\\
+\mbox{~~~~def~{\_}{\_}contains{\_}{\_}(self,point):~{\#}point~is~a~tuple~(x,y)}\\
+\mbox{~~~~~~~~return~self.contains(*point)}\\
+\mbox{}\\
+\mbox{makedisk=Makeobj(GeometricFigure,'(x-x0)**2/4+(y-y0)**2~<=~r**2')}\\
+\mbox{upperdisk=makedisk(x0=38,y0=7,r=5)}\\
+\mbox{smalldisk=makedisk(x0=38,y0=30,r=5)}\\
+\mbox{bigdisk=makedisk(x0=38,y0=30,r=14)}\\
+\mbox{}\\
+\mbox{def~format(text,shape):}\\
+\mbox{~~~~"Format~the~text~in~the~shape~given~by~figure"}\\
+\mbox{~~~~text=text.replace('{\textbackslash}n','~')}\\
+\mbox{~~~~out=[];~i=0;~col=0;~row=0;~L=len(text)}\\
+\mbox{~~~~while~1:}\\
+\mbox{~~~~~~~~if~(col,row)~in~shape:}\\
+\mbox{~~~~~~~~~~~~out.append(text[i]);~i+=1}\\
+\mbox{~~~~~~~~~~~~if~i==L:~break}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~out.append("~")}\\
+\mbox{~~~~~~~~if~col==Ncol-1:}\\
+\mbox{~~~~~~~~~~~~col=0;~out.append('{\textbackslash}n')~{\#}~starts~new~row}\\
+\mbox{~~~~~~~~~~~~if~row==Nrow-1:~row=0~~~{\#}~starts~new~page}\\
+\mbox{~~~~~~~~~~~~else:~row+=1}\\
+\mbox{~~~~~~~~else:~col+=1~}\\
+\mbox{~~~~return~''.join(out)}\\
+\mbox{}\\
+\mbox{composition=bigdisk-smalldisk+upperdisk}\\
+\mbox{print~format(text='Python~Rules!'*95,shape=composition)}\\
+\mbox{}\\
+\mbox{{\#}</funnyformatter.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+I leave as an exercise for the reader to understand how does it work and to
+play with other geometric figures (he can also generate them trough the
+'Makeobj' factory). I think it is nicer to show its output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~Pyt~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~hon~Rules!Pyt~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~hon~Rules!Python~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~Rules!Python~Rules!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~Python~Rules!Python~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~Rules!Python~Rules!P~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~ython~Rules!Python~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~Rules!Python~Rules!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~Python~Rules!Pyth~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~on~Rules!Pyth~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~on~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~Rul~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~es!Python~Rules!Pytho~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~n~Rules!Python~Rules!Python~R~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~ules!Python~Rules!Python~Rules!Pyth~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~on~Rules!Python~Rules!Python~Rules!Pyth~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~on~Rules!Python~Rules!Python~Rules!Python~R~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~ules!Python~Rules!Python~Rules!Python~Rules!Pyt~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~hon~Rules!Python~Rules!Python~Rules!Python~Rules!~~~~~~~~~~~~~~~}\\
+\mbox{~~~Python~Rules!Python~Rules!Python~Rules!Python~Rules~~~~~~~~~~~~~~}\\
+\mbox{~~!Python~Rules!Python~Rule~~~s!Python~Rules!Python~Rul~~~~~~~~~~~~~}\\
+\mbox{~~es!Python~Rules!Pyth~~~~~~~~~~~~~on~Rules!Python~Rule~~~~~~~~~~~~~}\\
+\mbox{~s!Python~Rules!Pyth~~~~~~~~~~~~~~~~~on~Rules!Python~Rul~~~~~~~~~~~~}\\
+\mbox{~es!Python~Rules!Py~~~~~~~~~~~~~~~~~~~thon~Rules!Python~~~~~~~~~~~~~}\\
+\mbox{~Rules!Python~Rules~~~~~~~~~~~~~~~~~~~!Python~Rules!Pyth~~~~~~~~~~~~}\\
+\mbox{on~Rules!Python~Ru~~~~~~~~~~~~~~~~~~~~~les!Python~Rules!P~~~~~~~~~~~}\\
+\mbox{~ython~Rules!Python~~~~~~~~~~~~~~~~~~~~Rules!Python~Rule~~~~~~~~~~~~}\\
+\mbox{~s!Python~Rules!Pyt~~~~~~~~~~~~~~~~~~~hon~Rules!Python~R~~~~~~~~~~~~}\\
+\mbox{~ules!Python~Rules!P~~~~~~~~~~~~~~~~~ython~Rules!Python~~~~~~~~~~~~~}\\
+\mbox{~~Rules!Python~Rules!P~~~~~~~~~~~~~ython~Rules!Python~R~~~~~~~~~~~~~}\\
+\mbox{~~ules!Python~Rules!Python~~~~Rules!Python~Rules!Python~~~~~~~~~~~~~}\\
+\mbox{~~~~Rules!Python~Rules!Python~Rules!Python~Rules!Pytho~~~~~~~~~~~~~~}\\
+\mbox{~~~~n~Rules!Python~Rules!Python~Rules!Python~Rules!Py~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~thon~Rules!Python~Rules!Python~Rules!Python~Rul~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~es!Python~Rules!Python~Rules!Python~Rules!P~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~ython~Rules!Python~Rules!Python~Rules!P~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~ython~Rules!Python~Rules!Python~Rul~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~es!Python~Rules!Python~Rules!~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~Python~Rules!Python~R~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~ule~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~s!}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Remark.
+
+Unfortunately, ``funnyformatter.py'' does not reuse old code: in spite of the
+fact that we already had in our library the 'GeometricFigure' class, with
+an ``{\_}{\_}init{\_}{\_}'' method that is exactly the same of the ``{\_}{\_}init{\_}{\_}'' method in
+``funnyformatter.py'', we did not reuse that code. We simply did a cut
+and paste. This means that if we later find a bug in the \texttt{{\_}{\_}init{\_}{\_}} method,
+we will have to fix it twice, both in the script and in the library. Also,
+if we plan to extend the method later, we will have to extend it twice.
+Fortunately, this nasty situation can be avoided: but this requires the
+power of inheritance.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-power-of-classes}{}
+\pdfbookmark[0]{THE POWER OF CLASSES}{the-power-of-classes}
+\section*{THE POWER OF CLASSES}
+
+This chapter is devoted to the concept of class inheritance. I will discuss
+single inheritance, cooperative methods, multiple inheritance and more.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-concept-of-inheritance}{}
+\pdfbookmark[1]{The concept of inheritance}{the-concept-of-inheritance}
+\subsection*{The concept of inheritance}
+
+Inheritance is perhaps the most important basic feature in OOP, since it
+allows the reuse and incremental improvement of old code.
+To show this point, let me come back to one of the
+examples I have introduced in the last chapter, 'fairytale1.py' script,
+where I defined the classes 'Frog' and 'Prince' as
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~Frog(object):}\\
+\mbox{~~~~attributes="poor,~small,~ugly"}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+self.attributes+'~'+self.{\_}{\_}class{\_}{\_}.{\_}{\_}name{\_}{\_}}\\
+\mbox{}\\
+\mbox{class~Prince(object):}\\
+\mbox{~~~~attributes='rich,~tall,~beautiful'}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~"I~am~a~"+self.attributes+'~'+self.{\_}{\_}class{\_}{\_}.{\_}{\_}name{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+We see that the way we followed here was very bad since:
+\newcounter{listcnt18}
+\begin{list}{\arabic{listcnt18}.}
+{
+\usecounter{listcnt18}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+The \texttt{{\_}{\_}str{\_}{\_}} method is duplicated both in Frog and in Prince: that
+means that if we find a bug a later, we have to fix it twice!
+
+\item {}
+The \texttt{{\_}{\_}str{\_}{\_}} was already defined in the PrettyPrinted class (actually
+more elegantly), therefore we have triplicated the work and worsened the
+situation!
+
+\end{list}
+
+This is very much against the all philosophy of OOP:
+\begin{quote}
+
+\emph{never cut and paste!}
+\end{quote}
+
+We should \emph{reuse} old code, not paste it!
+
+The solution is \emph{class inheritance}. The idea behind inheritance is to
+define new classes as subclasses of a \emph{parent} classes, in such a way that
+the \emph{children} classes possess all the features of the parents.
+That means that we do not need to
+redefine the properties of the parents explicitely.
+In this example, we may derive both 'Frog' and 'Prince' from
+the 'PrettyPrinted' class, thus providing to both 'Frog' and 'Prince'
+the \texttt{PrettyPrinted.{\_}{\_}str{\_}{\_}} method with no effort:
+\begin{quote}
+\begin{verbatim}>>> from oopp import PrettyPrinted
+>>> class Frog(PrettyPrinted): attributes="poor, small, ugly"
+...
+>>> class Prince(PrettyPrinted): attributes="rich, tall, beautiful"
+...
+>>> print repr(Frog()), Frog()
+<__main__.Frog object at 0x401cbeac> <Frog>
+>>> print Prince()
+>>> print repr(Prince()),Prince()
+<__main__.Prince object at 0x401cbaac> <Prince>\end{verbatim}
+\end{quote}
+
+Let me show explicitly that both 'Frog' and 'Prince' share the
+'PrettyPrinted.{\_}{\_}str{\_}{\_}' method:
+\begin{quote}
+\begin{verbatim}>>> id(Frog.__str__) # of course, YMMV
+1074329476
+>>> id(Prince.__str__)
+1074329476
+>>> id(PrettyPrinted.__str__)
+1074329476\end{verbatim}
+\end{quote}
+
+The method is always the same, since the object reference is the same
+(the precise value of the reference is not guaranteed to be 1074329476,
+however!).
+
+This example is good to show the first advantage of inheritance:
+\emph{avoiding duplication of code}.
+Another advantage of inheritance, is \emph{extensibility}: one can very easily
+improve existing code. For instance, having written the \texttt{Clock} class once,
+I can reuse it in many different ways. for example I can build a \texttt{Timer}
+to be used for benchmarks. It is enough to reuse the function \texttt{with{\_}timer}
+introduced in the first chapter (functions are good for reuse of code, too ;):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Timer(Clock):}\\
+\mbox{~~~~"Inherits~the~get{\_}time~staticmethod~from~Clock"}\\
+\mbox{~~~~execute=staticmethod(with{\_}timer)}\\
+\mbox{~~~~loop{\_}overhead=staticmethod(loop{\_}overhead)}\\
+\mbox{}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of application:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Timer
+>>> Timer.get_time()
+'16:07:06'\end{verbatim}
+\end{quote}
+
+Therefore 'Timer' inherits 'Clock.get{\_}time'; moreover it has the additional
+method \texttt{execute}:
+\begin{quote}
+\begin{verbatim}>>> def square(x): return x*x
+...
+>>> Timer.execute(square,n=100000)(1)
+executing square ...
+ Real time: 0.01 ms CPU time: 0.008 ms\end{verbatim}
+\end{quote}
+
+The advantage of putting the function \texttt{execute} in a class is that
+now we may \emph{inherit} from that class and improve out timer \emph{ad
+libitum}.
+
+
+%___________________________________________________________________________
+
+\hypertarget{inheritance-versus-run-time-class-modifications}{}
+\pdfbookmark[1]{Inheritance versus run-time class modifications}{inheritance-versus-run-time-class-modifications}
+\subsection*{Inheritance versus run-time class modifications}
+
+Naively, one could think of substituting inheritance with run-time
+modification of classes, since this is allowed by Python. However,
+this is not such a good idea, in general. Let me give a simple example.
+Suppose we want to improve our previous clock, to show the date, too.
+We could reach that goal with the following script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<clock2.py>}\\
+\mbox{}\\
+\mbox{"Shows~how~to~modify~and~enhances~classes~on~the~fly"}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{clock=Clock()~{\#}creates~a~Clock~instance}\\
+\mbox{print~clock.get{\_}time()~{\#}~print~the~current~time}\\
+\mbox{}\\
+\mbox{get{\_}data=lambda~:~'~'.join(time.asctime().split()[0:3])+~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~'~'+time.asctime().split()[-1]}\\
+\mbox{}\\
+\mbox{get{\_}data{\_}and{\_}time=lambda~:~"Today~is:~{\%}s~{\textbackslash}nThe~time~is:~{\%}s"~{\%}~(}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~get{\_}data(),get{\_}time())~{\#}~enhances~get{\_}time}\\
+\mbox{}\\
+\mbox{Clock.get{\_}time=staticmethod(get{\_}data{\_}and{\_}time)}\\
+\mbox{}\\
+\mbox{print~clock.get{\_}time()~{\#}~print~the~current~time~and~data}\\
+\mbox{}\\
+\mbox{{\#}</clock2.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is:
+\begin{quote}
+
+12:51:25
+Today is: Sat Feb 22 2003
+The time is: 12:51:25
+\end{quote}
+
+Notice that:
+\newcounter{listcnt19}
+\begin{list}{\arabic{listcnt19}.}
+{
+\usecounter{listcnt19}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+I instantiated the \texttt{clock} object \emph{before} redefining the \texttt{get{\_}time}
+method, when it only could print the time and \emph{not} the date.
+
+\item {}
+However, after the redefinition of the class, the behaviour of all its
+instances is changed, \emph{including the behaviour of objects instantiated
+before the change!}. Then \texttt{clock} \emph{can} print the date, too.
+
+\end{list}
+
+This is not so surprising, once you recognize that Guido own a very famous
+time-machine ... ;-)
+
+Seriously, the reason is that an object does not contains a reserved copy
+of the attributes and methods of its class: it only contains \emph{references}
+to them. If we change them in the class, the references to them in the
+object will stay the same, but the contents will change.
+
+In this example, I have solved the problem of enhancing the 'Clock' class
+without inheritance, but dynamically replaceing its \texttt{get{\_}time}
+(static) method with the \titlereference{get{\_}data{\_}and{\_}time`} (static) method.
+The dynamics modification of methods can be cool, but it should be avoided
+whenever possible, at least for two reasons [\hyperlink{id22}{11}]:
+\newcounter{listcnt20}
+\begin{list}{\arabic{listcnt20}.}
+{
+\usecounter{listcnt20}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+having a class and therefore all its instances (including the instances
+created before the modification !) changed during the life-time of the
+program can be very confusing to the programmer, if not to the interpreter.
+
+\item {}
+the modification is destructive: I cannot have the old \texttt{get{\_}time} method
+and the new one at the same time, unless one explicitly gives to it
+a new name (and giving new names increases the pollution of the namespace).
+
+\end{list}
+
+Both these disadvantages can be solved by resorting to the mechanism of
+inheritance. For instance, in this example, we can derive a new class
+\texttt{NewClock} from \texttt{Clock} as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<newclock.py>}\\
+\mbox{}\\
+\mbox{import~oopp,time}\\
+\mbox{}\\
+\mbox{get{\_}data=lambda~:~'~'.join(time.asctime().split()[0:3])+~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~'~'+time.asctime().split()[-1]}\\
+\mbox{}\\
+\mbox{get{\_}data{\_}and{\_}time=lambda~:~"Today~is:~{\%}s~{\textbackslash}nThe~time~is:~{\%}s"~{\%}~(}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~get{\_}data(),oopp.get{\_}time())~{\#}~enhances~get{\_}time}\\
+\mbox{}\\
+\mbox{class~NewClock(oopp.Clock):}\\
+\mbox{~~~~~~~"""NewClock~is~a~class~that~inherits~from~Clock,~provides~get{\_}data}\\
+\mbox{~~~~~~~~and~overrides~get{\_}time."""}\\
+\mbox{~~~~~~~get{\_}data=staticmethod(get{\_}data)}\\
+\mbox{~~~~~~~get{\_}time=staticmethod(get{\_}data{\_}and{\_}time)}\\
+\mbox{}\\
+\mbox{clock=oopp.Clock();~print~'clock~output=',clock.get{\_}time()~}\\
+\mbox{newclock=NewClock();~print~'newclock~output=',newclock.get{\_}time()}\\
+\mbox{}\\
+\mbox{{\#}</newclock.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{clock~output=~16:29:17}\\
+\mbox{newclock~output=~Today~is:~Sat~Feb~22~2003~}\\
+\mbox{The~time~is:~16:29:17}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+We see that the two problems previously discussed are solved since:
+\newcounter{listcnt21}
+\begin{list}{\roman{listcnt21})}
+{
+\usecounter{listcnt21}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+there is no cut and paste: the old method \texttt{Clock.get{\_}time()} is used
+in the definition of the new method \texttt{NewClock.get{\_}time()};
+
+\item {}
+the old method is still accessible as \texttt{Clock.get{\_}time()}; there is
+no need to invent a new name like \texttt{get{\_}time{\_}old()}.
+
+\end{list}
+
+We say that the method \texttt{get{\_}time} in \texttt{NewClock} \emph{overrides} the method
+\texttt{get{\_}time} in Clock.
+
+This simple example shows the power of inheritance in code
+reuse, but there is more than that.
+
+Inheritance is everywhere in Python, since
+all classes inherit from object. This means that all classes
+inherit the methods and attributes of the object class, such as \texttt{{\_}{\_}doc{\_}{\_}},
+\texttt{{\_}{\_}class{\_}{\_}}, \texttt{{\_}{\_}str{\_}{\_}}, etc.
+\begin{quote}
+\begin{figure}[b]\hypertarget{id24}[12]
+There are cases when run-time modifications of classes is useful
+anyway: particularly when one wants to modify the behavior of
+classes written by others without changing the source code. I
+will show an example in next chapter.
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{inheriting-from-built-in-types}{}
+\pdfbookmark[1]{Inheriting from built-in types}{inheriting-from-built-in-types}
+\subsection*{Inheriting from built-in types}
+
+However, one can subclass a built-in type, effectively creating an
+user-defined type with all the feature of a built-in type, and modify it.
+
+Suppose for instance one has a keyword dictionary such as
+\begin{quote}
+\begin{verbatim}>>> kd={'title': "OOPP", 'author': "M.S.", 'year': 2003}\end{verbatim}
+\end{quote}
+
+it would be nice to be able to access the attributes without
+excessive quoting, i.e. using \texttt{kd.author} instead of \texttt{kd["author"]}.
+This can be done by subclassing the built-in class \texttt{dict} and
+by overriding the \texttt{{\_}{\_}getattr{\_}{\_}} and \texttt{{\_}{\_}setattr{\_}{\_}} special methods:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp/py>}\\
+\mbox{}\\
+\mbox{class~kwdict(dict):}\\
+\mbox{~~~~"Keyword~dictionary~base~class"}\\
+\mbox{~~~~def~{\_}{\_}getattr{\_}{\_}(self,attr):~}\\
+\mbox{~~~~~~~~return~self[attr]}\\
+\mbox{~~~~def~{\_}{\_}setattr{\_}{\_}(self,key,val):~}\\
+\mbox{~~~~~~~~self[key]=val}\\
+\mbox{~~~~{\_}{\_}str{\_}{\_}~=~pretty~}\\
+\mbox{}\\
+\mbox{{\#}</oopp/py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import kwdict
+>>> book=kwdict({'title': "OOPP", 'author': "M.S."})
+>>> book.author #it works
+'M.S.'
+>>> book["author"] # this also works
+'M.S.'
+>>> book.year=2003 #you may also add new fields on the fly
+>>> print book
+author = M.S.
+title = OOPP
+year = 2003\end{verbatim}
+\end{quote}
+
+The advantage of subclassing the built-in 'dict', it that you have for free
+all the standard dictionary methods, without having to reimplement them.
+
+However, to subclass built-in it is not always a piece of cake. In
+many cases there are complications, indeed. Suppose for instance
+one wants to create an enhanced string type, with
+the ability of indent and dedent a block of text provided by
+the following functions:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~indent(block,n):}\\
+\mbox{~~~~~"Indent~a~block~of~code~by~n~spaces"}\\
+\mbox{~~~~~return~'{\textbackslash}n'.join(['~'*n+line~for~line~in~block.splitlines()])}\\
+\mbox{}\\
+\mbox{def~dedent(block):}\\
+\mbox{~~~~"Dedent~a~block~of~code,~if~need~there~is"""}\\
+\mbox{~~~~lines=block.splitlines()}\\
+\mbox{~~~~for~line~in~lines:}\\
+\mbox{~~~~~~~~strippedline=line.lstrip()}\\
+\mbox{~~~~~~~~if~strippedline:~break}\\
+\mbox{~~~~spaces=len(line)-len(strippedline)}\\
+\mbox{~~~~if~not~spaces:~return~block}\\
+\mbox{~~~~return~'{\textbackslash}n'.join([line[spaces:]~for~line~in~lines])}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The solution is to inherit from the built-in string type \texttt{str}, and to
+add to the new class the \texttt{indent} and \texttt{dedent} methods:
+\begin{quote}
+\begin{verbatim}>>> from oopp import indent,dedent
+>>> class Str(str):
+... indent=indent
+... dedent=dedent
+>>> s=Str('spam\neggs')
+>>> type(s)
+<class '__main__.Str'>
+>>> print s.indent(4)
+ spam
+ eggs\end{verbatim}
+\end{quote}
+
+However, this approach has a disadvantage, since the output of \texttt{indent} is
+not a \texttt{Str}, but a normal \texttt{str}, therefore without the additional
+\texttt{indent} and \texttt{dedent} methods:
+\begin{quote}
+\begin{verbatim}>>> type(s.indent(4))
+<type 'str'>
+>>> s.indent(4).indent(4) #error
+Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+AttributeError: 'str' object has no attribute 'indent'
+>>> s.indent(4).dedent(4) #error
+Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+AttributeError: 'str' object has no attribute 'dedent'\end{verbatim}
+\end{quote}
+
+We would like \texttt{indent} to return a \texttt{Str} object. To solve this problem
+it is enough to rewrite the class as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<Str.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~indent,dedent}\\
+\mbox{}\\
+\mbox{class~Str(str):}\\
+\mbox{~~~def~indent(self,n):}\\
+\mbox{~~~~~~~return~Str(indent(self,n))}\\
+\mbox{~~~def~dedent(self):}\\
+\mbox{~~~~~~~return~Str(dedent(self))}\\
+\mbox{}\\
+\mbox{s=Str('spam{\textbackslash}neggs').indent(4)}\\
+\mbox{print~type(s)}\\
+\mbox{print~s~{\#}~indented~s}\\
+\mbox{s=s.dedent()}\\
+\mbox{print~type(s)}\\
+\mbox{print~s~{\#}~non-indented~s}\\
+\mbox{}\\
+\mbox{{\#}</Str.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now, everything works and the output of the previous script is
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{<class~'Str'>}\\
+\mbox{~~~~spam}\\
+\mbox{~~~~eggs}\\
+\mbox{<class~'Str'>}\\
+\mbox{spam}\\
+\mbox{eggs}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The solution works because now \texttt{indent()} returns an instance
+of \texttt{Str}, which therefore has an \texttt{indent} method. Unfortunately,
+this is not the end. Suppose we want to add another food to our list:
+\begin{quote}
+\begin{verbatim}>>> s2=s+Str("\nham")
+>>> s2.indent(4) #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'str' object has no attribute 'indent'\end{verbatim}
+\end{quote}
+
+The problem is the same, again: the type of \texttt{s2} is \texttt{str}
+\begin{quote}
+\begin{verbatim}>>> type(s2)
+<type 'str'>\end{verbatim}
+\end{quote}
+
+and therefore there is no \texttt{indent} method available. There is a
+solution to this problem, i.e. to redefine the addition operator
+for objects of the class \texttt{Str}. This can be done directly by hand,
+but it is \emph{ugly} for the following reasons:
+\newcounter{listcnt22}
+\begin{list}{\arabic{listcnt22}.}
+{
+\usecounter{listcnt22}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+If you derive a new class from \texttt{Str} you have to redefine the
+addition operator (both the left addition and the right addition [\hyperlink{id24}{12}])
+again (ughh!);
+
+\item {}
+There are others operators you must redefine, in particular the
+the augumented assignement operator \texttt{+=}, the repetition operator \texttt{*}
+and its augmented version \texttt{*=};
+
+\item {}
+In the case of numeric types, one must redefine, \texttt{+,-,*,/,//, mod,},
+possibily \texttt{<<,>>} and others, including the corresponding
+augumented assignement operators and the left and the right form of
+the operators.
+
+\end{list}
+
+This is a mess, especially since due to point 1, one has to redefined
+all the operators each time she defines a new subclass. I short, one has
+to write a lot of boilerplate for a stupid job that the language
+should be able to perform itself automatically. But here are the
+good news: Python \emph{can} do all that automatically, in an elegant
+and beautiful way, which works for all types, too.
+
+But this requires the magic of metaclasses.
+\begin{quote}
+\begin{figure}[b]\hypertarget{id26}[13]
+The right addition works this way. Python looks at the expression x+y
+and if x has an explicit{\_}{\_}add{\_}{\_} method invokes it; on the other hand,
+if x does not define an {\_}{\_}add{\_}{\_} method, Python considers y+x.
+If y defines a {\_}{\_}radd{\_}{\_} method, it invokes it, otherwise
+raises an exception. The same is done for right multiplication, etc.
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{controlling-the-creation-of-objects}{}
+\pdfbookmark[1]{Controlling the creation of objects}{controlling-the-creation-of-objects}
+\subsection*{Controlling the creation of objects}
+
+Before introducing multiple inheritance, let me make a short digression on
+the mechanism of object creation in Python 2.2+. The important point is
+that new style classes have a \texttt{{\_}{\_}new{\_}{\_}} static method that allows
+the user to take complete control of object creation. To understand how
+\texttt{{\_}{\_}new{\_}{\_}} works, I must explain what happens when an object is instantiated
+with a statement like
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{s=Str("spam")~{\#}object~creation}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+What happens under the hood, is that the special static method \texttt{{\_}{\_}new{\_}{\_}}
+of the class \texttt{Str} (inherited from the built-in \texttt{str} class)
+is invoked \texttt{before} the \texttt{Str.{\_}{\_}init{\_}{\_}} method. This means that
+the previous line should really be considered syntactic sugar for:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{s=Str.{\_}{\_}new{\_}{\_}(Str,"spam")~{\#}~Str.{\_}{\_}new{\_}{\_}~is~actually~str.{\_}{\_}new{\_}{\_}}\\
+\mbox{assert~isinstance(s,Str)}\\
+\mbox{Str.{\_}{\_}init{\_}{\_}(s,"spam")~~{\#}~Str.{\_}{\_}init{\_}{\_}~is~actually~str.{\_}{\_}init{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Put it more verbosely, what happens during the object creation is the
+following:
+\newcounter{listcnt23}
+\begin{list}{\arabic{listcnt23}.}
+{
+\usecounter{listcnt23}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+the static method \texttt{{\_}{\_}new{\_}{\_}} is invoked with the class of the created
+object as first argument [\hyperlink{id26}{13}];
+
+\item {}
+\texttt{{\_}{\_}new{\_}{\_}} returns an instance of that class.
+
+\item {}
+the instance is then initialized by the \texttt{{\_}{\_}init{\_}{\_}} method.
+
+\end{list}
+
+Notice that both \texttt{{\_}{\_}new{\_}{\_}} and \texttt{{\_}{\_}init{\_}{\_}} are called with the same
+argument list, therefore one must make sure that they have a compatible
+signature.
+
+Let me discuss now why \texttt{{\_}{\_}new{\_}{\_}} must be a static method.
+First of all, it cannot be a normal method with a first argument which is an
+instance of the calling class, since at the time of \texttt{{\_}{\_}new{\_}{\_}} invocation
+that instance (\texttt{myclock} in the example) has still to be created
+Since \texttt{{\_}{\_}new{\_}{\_}} needs information about the class calling it, one
+could think of implementing \texttt{{\_}{\_}new{\_}{\_}} as a class method. However,
+this would implicitly pass the caller class and return an instance
+of it. It is more convenient, to have the ability of creating
+instances of any class directly from C.{\_}{\_}new{\_}{\_}(B,*args,**kw)
+
+For this reasons, \texttt{{\_}{\_}new{\_}{\_}} must be a static method and pass explicitly
+the class which is calling it.
+
+Let me now show an important application of the \texttt{{\_}{\_}new{\_}{\_}} static method:
+forbidding object creation. For instance, sometimes it is useful to have
+classes that cannot be instantiated. This kind of classes can be
+obtained by inheriting from a \texttt{NonInstantiable} class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~NonInstantiableError(Exception):~}\\
+\mbox{~~~~pass}\\
+\mbox{}\\
+\mbox{class~NonInstantiable(object):~}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,*args,**kw):}\\
+\mbox{~~~~~~~~raise~NonInstantiableError("{\%}s~cannot~be~instantiated"~{\%}~cls)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import NonInstantiable,get_time
+>>> class Clock(NonInstantiable):
+... get_time=staticmethod(get_time)
+>>> Clock.get_time() # works
+'18:48:08'
+Clock() #error
+Traceback (most recent call last):
+ File "<pyshell#6>", line 1, in ?
+ Clock()
+ File "oopp.py", line 257, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+NonInstantiableError: <class '__main__.Clock'> cannot be instantiated\end{verbatim}
+\end{quote}
+
+However, the approach pursued here has a disadvantage:\texttt{Clock} was already
+defined as a subclass of \texttt{object} and I has to change the source code
+to make it a subclass of 'NonInstantiable'. But what happens if
+I cannot change the sources? How can I \emph{reuse} the old code?
+
+The solution is provided by multiple inheritance.
+
+Notice that '{\_}{\_}new{\_}{\_}' is a staticmethod: [\hyperlink{id29}{14}]
+\begin{quote}
+\begin{quote}
+\begin{verbatim}>>> type(NonInstantiable.__dict__['__new__'])
+<type 'staticmethod'>\end{verbatim}
+\end{quote}
+\begin{figure}[b]\hypertarget{id29}[14]
+This is how \texttt{type(s)} or \texttt{s.{\_}{\_}class{\_}{\_}} get to know that
+\texttt{s} is an instance of \texttt{Str}, since the class information is
+explicitely passed to the newborn object trough \texttt{{\_}{\_}new{\_}{\_}}.
+\end{figure}
+\begin{figure}[b]\hypertarget{id30}[15]
+However \texttt{object.{\_}{\_}dict{\_}{\_}['{\_}{\_}new{\_}{\_}']} is not a staticmethod
+\begin{verbatim}>>> type(object.__dict__['__new__']) # special case
+<type 'builtin_function_or_method'>\end{verbatim}
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{multiple-inheritance}{}
+\pdfbookmark[1]{Multiple Inheritance}{multiple-inheritance}
+\subsection*{Multiple Inheritance}
+
+Multiple Inheritance (often abbreviated as MI) is often
+considered one of the most advanced topic in Object Oriented Programming.
+It is also one of the most difficult features to implement
+in an Object Oriented Programming language. Even, some languages by design
+decided to avoid it. This is for instance the case of Java, that avoided
+MI having seen its implementation in C++ (which is not for the faint of
+heart ;-) and uses a poorest form of it trough interfaces.
+For what concerns the scripting languages, of which
+the most famous are Perl, Python and Ruby (in this order, even if
+the right order would be Python, Ruby and Perl), only Python
+implements Multiple Inheritance well (Ruby has a restricted form
+of it trough mix-ins, whereas Perl implementation is too difficult
+for me to understand what it does ;).
+
+The fact that Multiple Inheritance can be hairy, does not mean that it
+is \emph{always} hairy, however. Multiple Inheritance is used with success
+in Lisp derived languages (including Dylan).
+
+The aims of this chapter is to discuss the
+Python support for MI in the most recent version (2.2 and 2.3), which
+has considerably improved with respect to previous versions.
+The message is the following: if Python 1.5 had a basic support for
+MI inheritance (basic but nevertheless with nice features, dynamic),
+Python 2.2 has \emph{greatly} improved that support and with the
+change of the Method Resolution Order in Python 2.3, we may say
+that support for MI is now \emph{excellent}.
+
+I strongly encourage Python programmers to use MI a lot: this will
+allows even a stronger reuse of code than in single inheritance.
+
+Often, inheritance is used when one has a complicate class B, and she wants
+to modify (or enhance) its behavior, by deriving a child class C, which is
+only slightly different from B. In this situation, B is already a standalone
+class, providing some non-trivial functionality, independently from
+the existence of C. This kind of design it typical of the so called
+\emph{top-down} philosophy, where one builds the
+all structure as a monolithic block, leaving room only for minor improvements.
+An alternative approach is the so called \emph{bottom-up} programming, in
+which one builds complicate things starting from very simple building blocks.
+In this logic, it is very appealing the idea of creating classes with the
+only purpose of being derived. The 'NonInstantiable' just defined is a
+perfect example of this kind of classes, though with multiple inheritance
+in mind and often called \emph{mixin} classes.
+It can be used to create a new class \texttt{NonInstantiableClock}
+that inherits from \texttt{Clock} and from \texttt{NonInstantiable}.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~NonInstantiableClock(Clock,NonInstantiable):~}\\
+\mbox{~~~~pass}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now \texttt{NonInstantiableClock} is both a clock
+\begin{quote}
+\begin{verbatim}>>> from oopp import NonInstantiableClock
+>>> NonInstantiableClock.get_time() # works
+'12:57:00' \end{verbatim}
+\end{quote}
+
+and a non-instantiable class:
+\begin{quote}
+\begin{verbatim}>>> NonInstantiableClock() # as expected, give an error
+Traceback (most recent call last):
+ File "<pyshell#2>", line 1, in ?
+ NonInstantiableClock() # error
+ File "oopp.py", line 245, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+NonInstantiableError: <class 'oopp.NonInstantiableClock'>
+cannot be instantiated\end{verbatim}
+\end{quote}
+
+Let me give a simple example of a situation where the mixin approach
+comes handy. Suppose that the owner of a 'Pizza-shop' needs a program to
+take care of all the pizzas to-go he sell. Pizzas are distinguished
+according to their size (small, medium or large) and their toppings.
+The problem can be solved by inheriting from a generic pizza factory
+like this:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~GenericPizza(object):~{\#}~to~be~customized}\\
+\mbox{~~~~toppinglist=[]~{\#}~nothing,~default~}\\
+\mbox{~~~~baseprice=1~{\#}~one~dollar,~default}\\
+\mbox{~~~~topping{\_}unit{\_}price=0.5~{\#}~half~dollar~for~each~topping,~default}\\
+\mbox{~~~~sizefactor={\{}'small':1,~'medium':2,~'large':3{\}}~}\\
+\mbox{~~~~{\#}~a~medium~size~pizza~costs~twice~a~small~pizza,~}\\
+\mbox{~~~~{\#}~a~large~pizza~costs~three~times}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,size):}\\
+\mbox{~~~~~~~~self.size=size}\\
+\mbox{~~~~def~price(self):}\\
+\mbox{~~~~~~~~return~(self.baseprice+}\\
+\mbox{~~~~~~~~~~~~~~~self.toppings{\_}price())*self.sizefactor[self.size]}\\
+\mbox{~~~~def~toppings{\_}price(self):}\\
+\mbox{~~~~~~~~return~len(self.toppinglist)*self.topping{\_}unit{\_}price}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~'{\%}s~pizza~with~{\%}s,~cost~{\$}~{\%}s'~{\%}~(self.size,}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~','.join(self.toppinglist),}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~self.price())}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here the base class 'GenericPizza' is written with inheritance in mind: one
+can derives many pizza classes from it by overriding the \texttt{toppinglist};
+for instance one could define
+\begin{quote}
+\begin{verbatim}>>> from oopp import GenericPizza
+>>> class Margherita(GenericPizza):
+... toppinglist=['tomato']\end{verbatim}
+\end{quote}
+
+The problem of this approach is that one must define dozens of
+different pizza subclasses (Marinara, Margherita, Capricciosa, QuattroStagioni,
+Prosciutto, ProsciuttoFunghi, PizzaDellaCasa, etc. etc. [\hyperlink{id30}{15}]). In such a
+situation, it is better to perform the generation of subclasses in a smarter
+way, i.e. via a customizable class factory.
+A simpler approach is to use always the same class and to customize
+its instances just after creation. Both approaches can be implemented via
+the following 'Customizable' mixin class, not meant to be instantiated,
+but rather to be \emph{inherited}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Customizable(object):}\\
+\mbox{~~~~"""Classes~inhering~from~'Customizable'~have~a~'with'~method~acting~as}\\
+\mbox{~~~~an~object~modifier~and~'With'~classmethod~acting~as~a~class~factory"""}\\
+\mbox{~~~~def~with(self,**kw):}\\
+\mbox{~~~~~~~~customize(self,**kw){\#}~customize~the~instance}\\
+\mbox{~~~~~~~~return~self~{\#}~returns~the~customized~instance}\\
+\mbox{~~~~def~With(cls,**kw):}\\
+\mbox{~~~~~~~~class~ChildOf(cls):~pass~{\#}~a~new~class~inheriting~from~cls}\\
+\mbox{~~~~~~~~ChildOf.{\_}{\_}name{\_}{\_}=cls.{\_}{\_}name{\_}{\_}~{\#}~by~default,~with~the~same~name}\\
+\mbox{~~~~~~~~customize(ChildOf,**kw)~~~~~~~{\#}~of~the~original~class}\\
+\mbox{~~~~~~~~return~ChildOf}\\
+\mbox{~~~~With=classmethod(With)~}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Descendants of 'Customizable' can be customized by using
+'with', that directly acts on the instances, or 'With', that returns
+new classes. Notice that one could make 'With' to customize the
+original class, without returning a new one; however, in practice,
+this would not be safe: I remind that changing a class modifies
+automatically all its instances, even instances created \emph{before}
+the modification. This could produce bad surprises: it is better to
+returns new classes, that may have the same name of the original one,
+but are actually completely independent from it.
+
+In order to solve the pizza shop problem we may define a 'CustomizablePizza'
+class
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~CustomizablePizza(GenericPizza,Customizable):}\\
+\mbox{~~~~pass}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+which can be used in two ways: i) to customize instances just after creation:
+\begin{quote}
+\begin{verbatim}>>> from oopp import CustomizablePizza
+>>> largepizza=CustomizablePizza('large') # CustomizablePizza instance
+>>> largemarinara=largepizza.with(toppinglist=['tomato'],baseprice=2)
+>>> print largemarinara
+large pizza with tomato mozzarella, cost $ 7.0\end{verbatim}
+\end{quote}
+
+and ii) to generated customized new classes:
+\begin{quote}
+\begin{verbatim}>>> Margherita=CustomizablePizza.With(
+... toppinglist=['tomato','mozzarella'], __name__='Margherita')
+>>> print Margherita('medium')
+medium pizza with tomato,mozzarella, cost $ 4.0\end{verbatim}
+\end{quote}
+
+The advantage of the bottom-up approach, is that the 'Customizable' class
+can be reused in completely different problems; for instance, it could
+be used as a class factory. For instance we could use it to generate a
+'CustomizableClock' class as in this example:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> CustomizableClock=Customizable.With(get_time=staticmethod(Clock.get_time),
+... __name__='CustomizableClock') #adds get_time
+>>> CustomizableClock.get_time() # now it works
+'09:57:50'\end{verbatim}
+\end{quote}
+
+Here 'Customizable' ``steal'' the 'get{\_}time' method from 'Clock'.
+However that would be a rather perverse usage ;) I wrote it to show
+the advantage of classmethods, more than to suggest to the reader that
+this is an example of good programming.
+\begin{quote}
+\begin{figure}[b]\hypertarget{id32}[16]
+In Italy, you can easily find ``pizzerie'' with more than 50 different
+kinds of pizzas (once I saw a menu with something like one hundred
+different combinations ;)
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{cooperative-hierarchies}{}
+\pdfbookmark[1]{Cooperative hierarchies}{cooperative-hierarchies}
+\subsection*{Cooperative hierarchies}
+
+The examples of multiple inheritance hierarchies given until now were pretty
+easy. The reason is that there was no interaction between the methods of the
+children and of the parents. However, things get more complicated (and
+interesting ;) when the methods in the hierarchy call each other.
+Let me consider an example coming from paleoantropology:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<paleoanthropology1.py>}\\
+\mbox{}\\
+\mbox{class~HomoHabilis(object):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~print~self,'can:'}\\
+\mbox{~~~~~~~~print~"~-~make~tools"}\\
+\mbox{}\\
+\mbox{class~HomoSapiens(HomoHabilis):}\\
+\mbox{~~~~def~can(self):~{\#}overrides~HomoHabilis.can}\\
+\mbox{~~~~~~~~HomoHabilis.can(self)}\\
+\mbox{~~~~~~~~print~"~-~make~abstractions"}\\
+\mbox{~~~~~~}\\
+\mbox{class~HomoSapiensSapiens(HomoSapiens):}\\
+\mbox{~~~~def~can(self):~{\#}overrides~HomoSapiens.can}\\
+\mbox{~~~~~~~~HomoSapiens.can(self)}\\
+\mbox{~~~~~~~~print~"~-~make~art"}\\
+\mbox{}\\
+\mbox{modernman=HomoSapiensSapiens()}\\
+\mbox{modernman.can()}\\
+\mbox{}\\
+\mbox{{\#}</paleoanthropology1.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this example children methods call parent methods:
+'HomoSapiensSapiens.can' calls 'HomoSapiens.can' that in turns calls
+'HomoHabilis.can' and the final output is:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{<{\_}{\_}main{\_}{\_}.HomoSapiensSapiens~object~at~0x814e1fc>~can:}\\
+\mbox{~-~make~tools}\\
+\mbox{~-~make~abstractions}\\
+\mbox{~-~make~art}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The script works, but it is far from ideal, if code reuse and refactoring
+are considered important requirements. The point is that (very likely, as the
+research in paleoanthropology progresses) we may want to extend the
+hierarchy, for instance by adding a class on the top or in the middle.
+In the present form, this would require a non-trivial modification of
+the source code (especially
+if one think that the hierarchy could be fleshed out with dozens of others
+methods and attributes). However, the aim of OOP is to avoid as
+much as possible source code modifications. This goal can be attained in
+practice, if the source code is written to be friendly to extensions and
+improvements as much as possible. I think it is worth to spend some time
+in improving this example, since what can be learn here,
+can be lifted to real life cases.
+
+First of all, let me define a generic \emph{Homo} class, to be used
+as first ring of the inheritance chain (actually the first ring is
+'object'):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Homo(PrettyPrinted):~}\\
+\mbox{~~~~"""Defines~the~method~'can',~which~is~intended~to~be~overriden~}\\
+\mbox{~~~~in~the~children~classes,~and~inherits~'{\_}{\_}str{\_}{\_}'~from~PrettyPrinted,}\\
+\mbox{~~~~ensuring~a~nice~printing~representation~for~all~children."""}\\
+\mbox{~~~~def~can(self):~}\\
+\mbox{~~~~~~~~print~self,'can:'}\\
+\mbox{~~~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now, let me point out one of the shortcomings of the previous code: in each
+subclass, we explicitly call its parent class (also called super class)
+by its name. This is inconvenient, both because a change of name in
+later stages of the project would require a lot of search and replace
+(actually not a lot in this toy example, but you can imagine having
+a very big projects with dozens of named method calls) and because it makes
+difficult to insert a new element in the inheritance hierarchy.
+The solution to this problems is the
+\texttt{super} built-in, which provides an easy access to the methods
+of the superclass.
+\texttt{super} objects comes in two flavors: \texttt{super(cls,obj)} objects return
+bound methods whereas \texttt{super(cls)} objects return unbound methods.
+In the next code we will use the first form. The hierarchy can more elegantly
+be rewritten as [\hyperlink{id32}{16}] :
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<paleo2.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~Homo}\\
+\mbox{}\\
+\mbox{class~HomoHabilis(Homo):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(HomoHabilis,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~tools"}\\
+\mbox{}\\
+\mbox{class~HomoSapiens(HomoHabilis):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(HomoSapiens,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~abstractions"}\\
+\mbox{~~~~~~}\\
+\mbox{class~HomoSapiensSapiens(HomoSapiens):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(HomoSapiensSapiens,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~art"}\\
+\mbox{}\\
+\mbox{}\\
+\mbox{HomoSapiensSapiens().can()}\\
+\mbox{}\\
+\mbox{{\#}</paleo2.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{<HomoSapiensSapiens>~can:}\\
+\mbox{~-~make~tools}\\
+\mbox{~-~make~abstractions}\\
+\mbox{~-~make~art}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This is not yet the most elegant form, since even
+if \texttt{super} avoids naming the base class explicitely, still it
+requires to explicitely name the class where it is defined. This is
+rather annoying.
+Removing that restriction, i.e. implementing really anonymous
+\texttt{super} calls, is possible but requires a good understand of
+private variables in inheritance.
+
+
+%___________________________________________________________________________
+
+\hypertarget{inheritance-and-privacy}{}
+\pdfbookmark[1]{Inheritance and privacy}{inheritance-and-privacy}
+\subsection*{Inheritance and privacy}
+
+In order to define anonymous cooperative super calls, we need classes
+that know themselves, i.e. containing a reference to themselves. This
+is not an obvious problem as it could seems, since it cannot be solved
+without incurring in the biggest annoyance in inheritance:
+\emph{name clashing}. Name clashing happens when names and attributes defined
+in different ancestors overrides each other in a unwanted order.
+Name clashing is especially painful in the case of cooperative
+hierarchies and particularly in in the problem at hand.
+
+A naive solution would be to attach a plain (i.e. non-private)
+attribute '.this' to the class, containing a reference
+to itself, that can be invoked by the methods of the class.
+Suppose, for instance, that I want to use that attribute in the \texttt{{\_}{\_}init{\_}{\_}}
+method of that class. A naive attempt would be to write something like:
+\begin{quote}
+\begin{verbatim}>>> class B(object):
+... def __init__(self):
+... print self.this,'.__init__' # .this defined later
+>>> B.this=B # B.this can be set only after B has been created
+>>> B()
+<class '__main__.B'>\end{verbatim}
+\end{quote}
+
+Unfortunately, this approach does not work with cooperative hierarchies.
+Consider, for instance, extending 'B' with a cooperative children
+class 'C' as follows:
+\begin{quote}
+\begin{verbatim}>>> class C(B):
+... def __init__(self):
+... super(self.this,self).__init__() # cooperative call
+... print type(self).this,'.__init__'
+>>> C.this=C\end{verbatim}
+\end{quote}
+
+\texttt{C.{\_}{\_}init{\_}{\_}} calls \texttt{B.{\_}{\_}init{\_}{\_}} by passing a 'C' instance, therefore
+\texttt{C.this} is printed and not \texttt{B.this}:
+\begin{quote}
+\begin{verbatim}>>> C()
+<class '__main__.C'> .__init__
+<class '__main__.C'> .__init__
+<__main__.C object at 0x4042ca6c>\end{verbatim}
+\end{quote}
+
+The problem is that the \texttt{C.this} overrides \texttt{B.this}. The only
+way of avoiding the name clashing is to use a private attribute
+\texttt{.{\_}{\_}this}, as in the following script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<privateh.py>}\\
+\mbox{}\\
+\mbox{class~B(object):}\\
+\mbox{~~~~~def~{\_}{\_}init{\_}{\_}(self):~}\\
+\mbox{~~~~~~~~print~self.{\_}{\_}this,'.{\_}{\_}init{\_}{\_}'}\\
+\mbox{B.{\_}B{\_}{\_}this=B}\\
+\mbox{}\\
+\mbox{class~C(B):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\
+\mbox{~~~~~~~super(self.{\_}{\_}this,self).{\_}{\_}init{\_}{\_}()~{\#}~cooperative~{\_}{\_}init{\_}{\_}~}\\
+\mbox{~~~~~~~print~self.{\_}{\_}this,'.{\_}{\_}init{\_}{\_}'}\\
+\mbox{C.{\_}C{\_}{\_}this=C}\\
+\mbox{}\\
+\mbox{C()}\\
+\mbox{}\\
+\mbox{{\#}~output:}\\
+\mbox{{\#}~<class~'{\_}{\_}main{\_}{\_}.B'>~.{\_}{\_}init{\_}{\_}}\\
+\mbox{{\#}~<class~'{\_}{\_}main{\_}{\_}.C'>~.{\_}{\_}init{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}</privateh.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The script works since, due to the magic of the mangling mechanism,
+in \texttt{B.{\_}{\_}init{\_}{\_}}, \texttt{self.{\_}B{\_}{\_}this} i.e. \texttt{B} is retrieved, whereas in
+\texttt{C.{\_}{\_}init{\_}{\_}} \texttt{self.{\_}C{\_}{\_}this} i.e. \texttt{C} is retrieved.
+
+The elegance of the mechanism can be improved with an helper function
+that makes its arguments reflective classes, i.e. classes with a
+\texttt{{\_}{\_}this} private attribute:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~reflective(*classes):}\\
+\mbox{~~~~"""Reflective~classes~know~themselves,~i.e.~they~possess~a~private}\\
+\mbox{~~~~attribute~{\_}{\_}this~containing~a~reference~to~themselves.~If~the~class}\\
+\mbox{~~~~name~starts~with~'{\_}',~the~underscores~are~stripped."""}\\
+\mbox{~~~~for~c~in~classes:}\\
+\mbox{~~~~~~~~name=c.{\_}{\_}name{\_}{\_}~.lstrip('{\_}')~~{\#}~in~2.3}\\
+\mbox{~~~~~~~~setattr(c,'{\_}{\%}s{\_}{\_}this'~{\%}~name,c)~}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+It is trivial to rewrite the paleonthropological hierarchy in terms of
+anonymous cooperative super calls by using this trick.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~HomoHabilis(Homo):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(self.{\_}{\_}this,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~tools"}\\
+\mbox{}\\
+\mbox{class~HomoSapiens(HomoHabilis):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(self.{\_}{\_}this,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~abstractions"}\\
+\mbox{~~~~~~}\\
+\mbox{class~HomoSapiensSapiens(HomoSapiens):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~super(self.{\_}{\_}this,self).can()}\\
+\mbox{~~~~~~~~print~"~-~make~art"}\\
+\mbox{}\\
+\mbox{reflective(HomoHabilis,HomoSapiens,HomoSapiensSapiens)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> man=HomoSapiensSapiens(); man.can()
+<HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art\end{verbatim}
+\end{quote}
+
+We may understand why it works by looking at the attributes of man:
+\begin{quote}
+\begin{verbatim}>>> print pretty(attributes(man))
+_HomoHabilis__this = <class 'oopp.HomoHabilis'>
+_HomoSapiensSapiens__this = <class 'oopp.HomoSapiensSapiens'>
+_HomoSapiens__this = <class 'oopp.HomoSapiens'>
+can = <bound method HomoSapiensSapiens.can of
+ <oopp.HomoSapiensSapiens object at 0x404292ec>>
+formatstring = %s\end{verbatim}
+\end{quote}
+
+It is also interesting to notice that the hierarchy can be entirely
+rewritten without using cooperative methods, but using private attributes,
+instead. This second approach is simpler, as the following script shows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<privatehierarchy.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~PrettyPrinted,attributes,pretty}\\
+\mbox{}\\
+\mbox{class~Homo(PrettyPrinted):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~print~self,'can:'}\\
+\mbox{~~~~~~~~for~attr,value~in~attributes(self).iteritems():~}\\
+\mbox{~~~~~~~~~~~~if~attr.endswith('{\_}{\_}attr'):~print~value}\\
+\mbox{class~HomoHabilis(Homo):~}\\
+\mbox{~~~~{\_}{\_}attr="~-~make~tools"}\\
+\mbox{class~HomoSapiens(HomoHabilis):~}\\
+\mbox{~~~~{\_}{\_}attr="~-~make~abstractions"}\\
+\mbox{class~HomoSapiensSapiens(HomoSapiens):~}\\
+\mbox{~~~~{\_}{\_}attr="~-~make~art"}\\
+\mbox{}\\
+\mbox{modernman=HomoSapiensSapiens()}\\
+\mbox{modernman.can()}\\
+\mbox{print~'----------------------------------{\textbackslash}nAttributes~of',modernman}\\
+\mbox{print~pretty(attributes(modernman))}\\
+\mbox{}\\
+\mbox{{\#}</privatehierarchy.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here I have replaced the complicate chain of cooperative methods with
+much simpler private attributes. Only the 'can' method in the 'Homo'
+class survives, and it is modified to print the value of the '{\_}{\_}attr'
+attributes. Moreover, all the classes of the hierarchy have been made
+'Customizable', in view of future extensions.
+
+The second script is much shorter and much more elegant than the original
+one, however its logic can be a little baffling, at first. The solution
+to the mistery is provided by the attribute dictionary of 'moderman',
+given by the second part of the output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{<HomoSapiensSapiens>~can:}\\
+\mbox{~-~make~abstractions~~}\\
+\mbox{~-~make~art~~}\\
+\mbox{~-~make~tools~}\\
+\mbox{------------------------------------------}\\
+\mbox{Attributes~of~<HomoSapiensSapiens>:}\\
+\mbox{{\_}HomoHabilis{\_}{\_}attr~=~~-~make~tools}\\
+\mbox{{\_}HomoSapiensSapiens{\_}{\_}attr~=~~-~make~art}\\
+\mbox{{\_}HomoSapiens{\_}{\_}attr~=~~-~make~abstractions}\\
+\mbox{can~=~<bound~method~HomoSapiensSapiens.can~of~}\\
+\mbox{~~~~~~<{\_}{\_}main{\_}{\_}.HomoSapiensSapiens~object~at~0x402d892c>>}\\
+\mbox{formatstring~=~{\%}s}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+We see that, in addition to the 'can' method inherited from 'Homo',
+the 'with' and 'With' method inherited from 'Customizable' and
+the 'formatstring' inherited from 'PrettyPrinted',
+\texttt{moderman} has the attributes
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\_}HomoHabilis{\_}{\_}attr:'~-~make~tools'~{\#}~inherited~from~HomoHabilis}\\
+\mbox{{\_}HomoSapiens{\_}{\_}attr:'~-~make~abstractions'{\#}~inherited~from~HomoSapiens}\\
+\mbox{{\_}HomoSapiensSapiens{\_}{\_}attr:~'~-~make~art'~{\#}~inherited~from~HomoSapiensSapiens}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+which origin is obvious, once one reminds the mangling mechanism associated
+with private variables. The important point is that the trick would \emph{not}
+have worked for normal attributes. Had I used as variable name
+'attr' instead of '{\_}{\_}attr', the name would have been overridden: the only
+attribute of 'HomoSapiensSapiens' would have been ' - make art'.
+
+This example explains the advantages of private variables during inheritance:
+they cannot be overridden. Using private name guarantees the absence of
+surprises due to inheritance. If a class B has only private variables,
+deriving a class C from B cannot cause name clashes.
+
+Private variables have a drawbacks, too. The most obvious disadvantages is
+the fact that in order to customize private variables outside their
+defining class, one needs to pass explicitly the name of the class.
+
+For instance we could not change an attribute with the syntax
+\texttt{HomoHabilis.With({\_}{\_}attr=' - work the stone')}, we must write the
+more verbose, error prone and redundant
+\texttt{HomoHabilis.With({\_}HomoHabilis{\_}{\_}attr=' - work the stone')}
+
+A subtler drawback will be discussed in chapter 6.
+\begin{quote}
+\begin{figure}[b]\hypertarget{id34}[17]
+In single inheritance hierarchies, \texttt{super} can be dismissed
+in favor of \texttt{{\_}{\_}base{\_}{\_}}: for instance,
+\texttt{super(HomoSapiens,self).can()} is equivalent to
+\texttt{HomoSapiens.{\_}{\_}base{\_}{\_}.can(self)}. Nevertheless, in view
+of possible extensions to multiple inheritance, using \texttt{super} is a
+much preferable choice.
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-sophistication-of-descriptors}{}
+\pdfbookmark[0]{THE SOPHISTICATION OF DESCRIPTORS}{the-sophistication-of-descriptors}
+\section*{THE SOPHISTICATION OF DESCRIPTORS}
+
+Attribute descriptors are important metaprogramming tools that allows
+the user to customize the behavior of attributes in custom classes.
+For instance, attribute descriptors (or descriptors for short)
+can be used as method wrappers,
+to modify or enhance methods (this is the case for the well
+known staticmethods and classmethods attribute descriptors); they
+can also be used as attribute wrappers, to change or restrict the access to
+attributes (this is the case for properties). Finally, descriptors
+allows the user to play with the resolution order of attributes:
+for instance, the \texttt{super} built-in object used in (multiple) inheritance
+hierarchies, is implemented as an attribute descriptor.
+
+In this chapter, I will show how the user can define its own attribute
+descriptors and I will give some example of useful things you can do with
+them (in particular to add tracing and timing capabilities).
+
+
+%___________________________________________________________________________
+
+\hypertarget{motivation}{}
+\pdfbookmark[1]{Motivation}{motivation}
+\subsection*{Motivation}
+
+Attribute descriptors are a recent idea (they where first introduced in
+Python 2.2) nevertheless, under the hood, are everywhere in Python. It is
+a tribute to Guido's ability of hiding Python complications that
+the average user can easily miss they existence.
+If you need to do simple things, you can very well live without
+the knowledge of descriptors. On the other hand, if you need difficult
+things (such as tracing all the attribute access of your modules)
+attribute descriptors, allow you to perform
+impressive things.
+Let me start by showing why the knowledge of attribute descriptors is
+essential for any user seriously interested in metaprogramming applications.
+Suppose I want to trace the methods of a clock:
+\begin{quote}
+\begin{verbatim}>>> import oopp
+>>> clock=oopp.Clock()\end{verbatim}
+\end{quote}
+
+This is easily done with the \texttt{with{\_}tracer} closure of chapter 2:
+\begin{quote}
+\begin{verbatim}>>> oopp.wrapfunctions(clock,oopp.with_tracer)
+<oopp.Clock object at 0x4044c54c>
+>>> clock.get_time()
+[] Calling 'get_time' with arguments
+(){} ...
+-> '.get_time' called with result: 19:55:07
+'19:55:07'\end{verbatim}
+\end{quote}
+
+However, this approach fails if I try to trace the entire class:
+\begin{quote}
+\begin{verbatim}>>> oopp.wrapfunctions(oopp.Clock,oopp.with_tracer)
+<class 'oopp.Clock'>
+>>> oopp.Clock.get_time() # error
+Traceback (most recent call last):
+ File "<stdin>", line 6, in ?
+TypeError: unbound method _() must be called with Clock instance
+as first argument (got nothing instead)\end{verbatim}
+\end{quote}
+
+The reason is that \texttt{wrapfunctions} sets the attributes of 'Clock'
+by invoking \texttt{customize}, which uses \texttt{setattr}. This converts
+'{\_}' (i.e. the traced version of \texttt{get{\_}time}) in a regular method, not in
+a staticmethod!
+In order to trace staticmethods, one has to understand the nature
+of attribute descriptors.
+
+
+%___________________________________________________________________________
+
+\hypertarget{functions-versus-methods}{}
+\pdfbookmark[1]{Functions versus methods}{functions-versus-methods}
+\subsection*{Functions versus methods}
+
+Attribute descriptors are essential for the implementation
+of one of the most basic Python features: the automatic conversion
+of functions in methods. As I already anticipated in chapter 1, there is
+a sort of magic when one writes \texttt{Clock.get{\_}time=lambda self: get{\_}time()}
+and Python automagically converts the right hand side, that is a
+function, to a left hand side that is a (unbound) method. In order to
+understand this magic, one needs a better comprehension of the
+relation between functions and methods.
+Actually, this relationship is quite subtle
+and has no analogous in mainstream programming languages.
+For instance, C is not OOP and has only functions, lacking the concept
+of method, whereas Java (as other OOP languages)
+has no functions, only methods.
+C++ has functions and methods, but functions are completely
+different from methods On the other hand, in Python,
+functions and methods can be transformed both ways.
+
+To show how it works, let me start by defining a simple printing
+function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~{\_}{\_}main{\_}{\_}~{\#}~gives~access~to~the~{\_}{\_}main{\_}{\_}~namespace~from~the~module}\\
+\mbox{}\\
+\mbox{def~prn(s):}\\
+\mbox{~~~~"""Given~an~evaluable~string,~print~its~value~and~its~object~reference.}\\
+\mbox{~~~~Notice~that~the~evaluation~is~done~in~the~{\_}{\_}main{\_}{\_}~dictionary."""}\\
+\mbox{~~~~try:~obj=eval(s,{\_}{\_}main{\_}{\_}.{\_}{\_}dict{\_}{\_})}\\
+\mbox{~~~~except:~print~'problems~in~evaluating',s}\\
+\mbox{~~~~else:~print~s,'=',obj,'at',hex(id(obj))}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now, let me define a class with a method \texttt{m} equals to the identity
+function \texttt{f}:
+\begin{quote}
+\begin{verbatim}>>> def f(x): "Identity function"; return x
+...
+>>> class C(object):
+... m=f
+... print m #here m is the function f
+<function f at 0x401c2b1c>\end{verbatim}
+\end{quote}
+
+We see that \emph{inside} its defining class, \texttt{m} coincides with the function
+\texttt{f} (the object reference is the same):
+\begin{quote}
+\begin{verbatim}>>> f
+<function f at 0x401c2b1c>\end{verbatim}
+\end{quote}
+
+We may retrieve \texttt{m} from \emph{outside} the class via the class dictionary [\hyperlink{id34}{17}]:
+\begin{quote}
+\begin{verbatim}>>> C.__dict__['m']
+<function prn at 0x401c2b1c>\end{verbatim}
+\end{quote}
+
+However, if we invoke \texttt{m} with
+the syntax \texttt{C.m}, then it (magically) becomes a (unbound) method:
+\begin{quote}
+\begin{verbatim}>>> C.m #here m has become a method!
+<unbound method C.f>\end{verbatim}
+\end{quote}
+
+But why it is so? How comes that in the second syntax the function
+\texttt{f} is transformed in a (unbound) method? To answer that question, we have
+to understand how attributes are really invoked in Python, i.e. via
+attribute descriptors.
+
+
+%___________________________________________________________________________
+
+\hypertarget{methods-versus-functions}{}
+\pdfbookmark[1]{Methods versus functions}{methods-versus-functions}
+\subsection*{Methods versus functions}
+
+First of all, let me point out the differences between methods and
+functions. Here, \texttt{C.m} does \emph{not} coincides with \texttt{C.{\_}{\_}dict{\_}{\_}['m']}
+i.e. \texttt{f}, since its object reference is different:
+\begin{quote}
+\begin{verbatim}>>> from oopp import prn,attributes
+>>> prn('C.m')
+C.m = <unbound method C.prn> at 0x81109b4\end{verbatim}
+\end{quote}
+
+The difference is clear since methods and functions have different attributes:
+\begin{quote}
+\begin{verbatim}>>> attributes(f).keys()
+['func_closure', 'func_dict', 'func_defaults', 'func_name',
+'func_code', 'func_doc', 'func_globals']\end{verbatim}
+\end{quote}
+
+whereas
+\begin{quote}
+\begin{verbatim}>>> attributes(C.m).keys()
+['im_func', 'im_class', 'im_self']\end{verbatim}
+\end{quote}
+
+We discussed few of the functions attributes in the chapter
+on functions. The instance method attributes are simpler: \texttt{im{\_}self}
+returns the object to which the method is attached,
+\begin{quote}
+\begin{verbatim}>>> print C.m.im_self #unbound method, attached to the class
+None
+>>> C().m.im_self #bound method, attached to C()
+<__main__.C object at 0x81bf4ec> \end{verbatim}
+\end{quote}
+
+\texttt{im{\_}class} returns the class to which the
+method is attached
+\begin{quote}
+\begin{verbatim}>>> C.m.im_class #class of the unbound method
+<class '__main__.C'>
+>>> C().m.im_class #class of the bound method,
+<class '__main__.C'>\end{verbatim}
+\end{quote}
+
+and \texttt{im{\_}func} returns the function equivalent to
+the method.
+\begin{quote}
+\begin{verbatim}>>> C.m.im_func
+<function m at 0x8157f44>
+>>> C().m.im_func # the same
+<function m at 0x8157f44>\end{verbatim}
+\end{quote}
+
+As the reference manual states, calling
+\texttt{m(*args,**kw)} is completely equivalent to calling
+\texttt{m.im{\_}func(m.im{\_}self, *args,**kw)}``.
+
+As a general rule, an attribute descriptor is an object with a \texttt{{\_}{\_}get{\_}{\_}}
+special method. The most used descriptors are the good old functions:
+they have a \texttt{{\_}{\_}get{\_}{\_}} special method returning a \emph{method-wrapper object}
+\begin{quote}
+\begin{verbatim}>>> f.__get__
+<method-wrapper object at 0x815cdc4>\end{verbatim}
+\end{quote}
+
+method-wrapper objects can be transformed in (both bound and unbound) methods:
+\begin{quote}
+\begin{verbatim}>>> f.__get__(None,C)
+<unbound method C.f>
+>>> f.__get__(C(),C)
+<bound method C.f of <__main__.C object at 0x815cdc4>>\end{verbatim}
+\end{quote}
+
+The general calling syntax for method-wrapper objects is
+\texttt{.{\_}{\_}get{\_}{\_}(obj,cls=None)}, where the first argument is an
+instance object or None and the second (optional) argument is the class (or a
+generic superclass) of the first one.
+
+Now we see what happens when we use the syntax \texttt{C.m}: Python interprets
+this as a shortcut for \texttt{C.{\_}{\_}dict['m'].{\_}{\_}get{\_}{\_}(None,C)} (if \texttt{m} is
+in the 'C' dictionary, otherwise it looks for ancestor dictionaries).
+We may check that everything is correct by observing that
+\texttt{f.{\_}{\_}get{\_}{\_}(None,C)} has exactly the same object reference than \texttt{C.m},
+therefore they are the same object:
+\begin{quote}
+\begin{verbatim}>>> hex(id(f.__get__(None,C))) # same as hex(id(C.m))
+'0x811095c'\end{verbatim}
+\end{quote}
+
+The process works equally well for the syntax \texttt{getattr}:
+\begin{quote}
+\begin{verbatim}>>> print getattr(C,'m'), hex(id(getattr(C,'m')))
+<unbound method C.f> 0x811095c\end{verbatim}
+\end{quote}
+
+and for bound methods: if
+\begin{quote}
+\begin{verbatim}>>> c=C()\end{verbatim}
+\end{quote}
+
+is an instance of the class C, then the syntax
+\begin{quote}
+\begin{verbatim}>>> getattr(c,'m') #same as c.m
+<bound method C.f of <__main__.C object at 0x815cdc4>>\end{verbatim}
+\end{quote}
+
+is a shortcut for
+\begin{quote}
+\begin{verbatim}>>> type(c).__dict__['m'].__get__(c,C) # or f.__get__(c,C)
+<bound method C.f of <__main__.C object at 0x815cdc4>>\end{verbatim}
+\end{quote}
+
+(notice that the object reference for \texttt{c.m} and \texttt{f.{\_}{\_}get{\_}{\_}(c,C)} is
+the same, they are \emph{exactly} the same object).
+
+Both the unbound method C.m and the bound method c.m refer to the same
+object at hexadecimal address 0x811095c. This object is common to all other
+instances of C:
+\begin{quote}
+\begin{verbatim}>>> c2=C()
+>>> print c2.m,hex(id(c2.m)) #always the same method
+<bound method C.m of <__main__.C object at 0x815768c>> 0x811095c\end{verbatim}
+\end{quote}
+
+One can also omit the second argument:
+\begin{quote}
+\begin{verbatim}>>> c.m.__get__(c)
+<bound method ?.m of <__main__.C object at 0x81597dc>>\end{verbatim}
+\end{quote}
+
+Finally, let me point out that methods are attribute descriptors too,
+since they have a \texttt{{\_}{\_}get{\_}{\_}} attribute returning a method-wrapper
+object:
+\begin{quote}
+\begin{verbatim}>>> C.m.__get__
+<method-wrapper object at 0x815d51c>\end{verbatim}
+\end{quote}
+
+Notice that this method wrapper is \emph{not} the same than the \texttt{f.{\_}{\_}get{\_}{\_}}
+method wrapper.
+\begin{quote}
+\begin{figure}[b]\hypertarget{id36}[18]
+If \texttt{C.{\_}{\_}dict['m']} is not defined, Python looks if \texttt{m} is defined
+in some ancestor of C. For instance if \titlereference{B} is the base of \titlereference{C}, it
+looks in \texttt{B.{\_}{\_}dict['m']}, etc., by following the MRO.
+\end{figure}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{static-methods-and-class-methods}{}
+\pdfbookmark[1]{Static methods and class methods}{static-methods-and-class-methods}
+\subsection*{Static methods and class methods}
+
+Whereas functions and methods are implicit attribute descriptors,
+static methods and class methods are examples of explicit
+descriptors. They allow to convert regular functions to
+specific descriptor objects. Let me show a trivial example.
+Given the identity function
+\begin{quote}
+\begin{verbatim}>>> def f(x): return x\end{verbatim}
+\end{quote}
+
+we may convert it to a staticmethod object
+\begin{quote}
+\begin{verbatim}>>> sm=staticmethod(f)
+>>> sm
+<staticmethod object at 0x4018a0a0>\end{verbatim}
+\end{quote}
+
+or to a classmethod object
+\begin{quote}
+\begin{verbatim}>>> cm=classmethod(f)
+>>> cm
+<classmethod object at 0x4018a0b0>\end{verbatim}
+\end{quote}
+
+In both cases the \texttt{{\_}{\_}get{\_}{\_}} special method returns a method-wrapper object
+\begin{quote}
+\begin{verbatim}>>> sm.__get__
+<method-wrapper object at 0x401751ec>
+>>> cm.__get__
+<method-wrapper object at 0x4017524c>\end{verbatim}
+\end{quote}
+
+However the static method wrapper is quite different from the class
+method wrapper. In the first case the wrapper returns a function:
+\begin{quote}
+\begin{verbatim}>>> sm.__get__(C(),C)
+<function f at 0x4027a8b4>
+>>> sm.__get__(C())
+<function f at 0x4027a8b4>\end{verbatim}
+\end{quote}
+
+in the second case it returns a method
+\begin{quote}
+\begin{verbatim}>>> cm.__get__(C(),C)
+<bound method type.f of <class '__main__.C'>>\end{verbatim}
+\end{quote}
+
+Let me discuss more in detail the static methods, first.
+
+It is always possible to extract the function from the static method
+via the syntaxes \texttt{sm.{\_}{\_}get{\_}{\_}(a)} and \texttt{sm.{\_}{\_}get{\_}{\_}(a,b)} with \emph{ANY} valid
+a and b, i.e. the result does not depend on a and b. This is correct,
+since static methods are actually function that have nothing to do
+with the class and the instances to which they are bound.
+
+This behaviour of the method wrapper makes clear why the relation between
+methods and functions is inversed for static methods with respect to
+regular methods:
+\begin{quote}
+\begin{verbatim}>>> class C(object):
+... s=staticmethod(lambda : None)
+... print s
+...
+<staticmethod object at 0x8158ec8>\end{verbatim}
+\end{quote}
+
+Static methods are non-trivial objects \emph{inside} the class, whereas
+they are regular functions \emph{outside} the class:
+\begin{quote}
+\begin{verbatim}>>> C.s
+<function <lambda> at 0x8158e7c>
+>>> C().s
+<function <lambda> at 0x8158e7c>\end{verbatim}
+\end{quote}
+
+The situation is different for classmethods: inside the class they
+are non-trivial objects, just as static methods,
+\begin{quote}
+\begin{verbatim}>>> class C(object):
+... cm=classmethod(lambda cls: None)
+... print cm
+...
+<classmethod object at 0x8156100>\end{verbatim}
+\end{quote}
+
+but outside the class they are methods bound to the class,
+\begin{quote}
+\begin{verbatim}>>> c=C()
+>>> prn('c.cm')
+<bound method type.<lambda> of <class '__main__.C'>>
+0x811095c\end{verbatim}
+\end{quote}
+
+and not to the instance 'c'. The reason is that the \texttt{{\_}{\_}get{\_}{\_}} wrapper method
+can be invoked with the syntax \texttt{{\_}{\_}get{\_}{\_}(a,cls)} which
+is only sensitive to the second argument or with the syntax
+\texttt{{\_}{\_}get{\_}{\_}(obj)} which is only sensitive to the type of the first
+argument:
+\begin{quote}
+\begin{verbatim}>>> cm.__get__('whatever',C) # the first argument is ignored
+<bound method type.f of <class '__main__.C'>>\end{verbatim}
+\end{quote}
+
+sensitive to the type of 'whatever':
+\begin{quote}
+\begin{verbatim}>>> cm.__get__('whatever') # in Python 2.2 would give a serious error
+<bound method type.f of <type 'str'>>\end{verbatim}
+\end{quote}
+
+Notice that the class method is actually bound to C's class, i.e.
+to 'type'.
+
+Just as regular methods (and differently
+from static methods) classmethods have attributes \texttt{im{\_}class}, \texttt{im{\_}func},
+and \texttt{im{\_}self}. In particular one can retrieve the function wrapped inside
+the classmethod with
+\begin{quote}
+\begin{verbatim}>>> cm.__get__('whatever','whatever').im_func
+<function f at 0x402c2534>\end{verbatim}
+\end{quote}
+
+The difference with regular methods is that \texttt{im{\_}class} returns the
+class of 'C' whereas \texttt{im{\_}self} returns 'C' itself.
+\begin{quote}
+\begin{verbatim}>>> C.cm.im_self # a classmethod is attached to the class
+<class '__main__.C'>
+>>> C.cm.im_class #the class of C
+<type 'type'>\end{verbatim}
+\end{quote}
+
+Remark: Python 2.2.0 has a bug in classmethods (fixed in newer versions):
+when the first argument of {\_}{\_}get{\_}{\_} is None, then one must specify
+the second argument (otherwise segmentation fault :-()
+
+
+%___________________________________________________________________________
+
+\hypertarget{properties}{}
+\pdfbookmark[1]{Properties}{properties}
+\subsection*{Properties}
+
+Properties are a more general kind of attribute descriptors than
+staticmethods and classmethods, since their effect can be customized
+trough arbitrary get/set/del functions. Let me give an example:
+\begin{quote}
+\begin{verbatim}>>> def getp(self): return 'property' # get function
+...
+>>> p=property(getp) # property object
+>>> p
+<property object at 0x815855c>\end{verbatim}
+\end{quote}
+
+\texttt{p} has a \texttt{{\_}{\_}get{\_}{\_}} special method returning a method-wrapper
+object, just as it happens for other descriptors:
+\begin{quote}
+\begin{verbatim}>>> p.__get__
+<method-wrapper object at 0x8158a7c>\end{verbatim}
+\end{quote}
+
+The difference is that
+\begin{quote}
+\begin{verbatim}>>> p.__get__(None,type(p))
+<property object at 0x4017016c>
+>>> p.__get__('whatever')
+'property'
+>>> p.__get__('whatever','whatever')
+'property'\end{verbatim}
+\end{quote}
+
+As for static methods, the \texttt{{\_}{\_}get{\_}{\_}} method wrapper is independent from
+its arguments, unless the first one is None: in such a case it returns
+the property object, in all other circumstances it returns the result
+of \texttt{getp}. This explains the behavior
+\begin{quote}
+\begin{verbatim}>>> class C(object): p=p
+>>> C.p
+<property object at 0x815855c>
+>>> C().p
+'property'\end{verbatim}
+\end{quote}
+
+Properties are a dangerous feature, since they change the semantics
+of the language. This means that apparently trivial operations can have
+any kind of side effects:
+\begin{quote}
+\begin{verbatim}>>> def get(self):return 'You gave me the order to destroy your hard disk!!'
+>>> class C(object): x=property(get)
+>>> C().x
+'You gave me the order to destroy your hard disk!!'\end{verbatim}
+\end{quote}
+
+Invoking 'C.x' could very well invoke an external program who is going
+to do anything! It is up to the programmer to not abuse properties.
+The same is true for user defined attribute descriptors.
+
+There are situations in which they are quite handy, however. For
+instance, properties can be used to trace the access data attributes.
+This can be especially useful during debugging, or for logging
+purposes.
+
+Notice that this approach has the problem that now data attributes cannot
+no more be called trough their class, but only though their instances.
+Moreover properties do not work well with \texttt{super} in cooperative
+methods.
+
+
+%___________________________________________________________________________
+
+\hypertarget{user-defined-attribute-descriptors}{}
+\pdfbookmark[1]{User-defined attribute descriptors}{user-defined-attribute-descriptors}
+\subsection*{User-defined attribute descriptors}
+
+As we have seen, there are plenty of predefined attribute descriptors,
+such as staticmethods, classmethods and properties (the built-in
+\texttt{super} is also an attribute descriptor which, for sake of
+convenience, will be discussed in the next section).
+In addition to them, the user can also define customized attribute
+descriptors, simply trough classes with a \texttt{{\_}{\_}get{\_}{\_}} special method.
+Let me give an example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<simpledescr.py>}\\
+\mbox{}\\
+\mbox{class~ChattyAttr(object):}\\
+\mbox{~~~~"""Chatty~descriptor~class;~descriptor~objects~are~intended~to~be~}\\
+\mbox{~~~~used~as~attributes~in~other~classes"""}\\
+\mbox{~~~~def~{\_}{\_}get{\_}{\_}(self,~obj,~cls=None):}\\
+\mbox{~~~~~~~~binding=obj~is~not~None}\\
+\mbox{~~~~~~~~if~~binding:}\\
+\mbox{~~~~~~~~~~~~return~'You~are~binding~{\%}s~to~{\%}s'~{\%}~(self,obj)}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~return~'Calling~{\%}s~from~{\%}s'~{\%}~(self,cls)}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~d=ChattyAttr()}\\
+\mbox{}\\
+\mbox{c=C()}\\
+\mbox{}\\
+\mbox{print~c.d~{\#}~<=>~type(c).{\_}{\_}dict{\_}{\_}['d'].{\_}{\_}get{\_}{\_}(c,type(c))}\\
+\mbox{print~C.d~{\#}~<=>~C.{\_}{\_}dict{\_}{\_}['d'].{\_}{\_}get{\_}{\_}(None,C)}\\
+\mbox{}\\
+\mbox{{\#}</simpledescr.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{You~are~binding~<ChattyAttr~object~at~0x401bc1cc>~to~}\\
+\mbox{<C~object~at~0x401bc2ec>}\\
+\mbox{Calling~<ChattyAttr~object~at~0x401bc1cc>~from~<class~'C'>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Invoking a method with the syntax \texttt{C.d} or \texttt{c.d} involves calling
+\texttt{{\_}{\_}get{\_}{\_}}. The \texttt{{\_}{\_}get{\_}{\_}} signature is fixed: it is
+`` {\_}{\_}get{\_}{\_}={\_}{\_}get{\_}{\_}(self,obj,cls=None)``, since the notation
+\texttt{self.descr{\_}attr} automatically passes \texttt{self} and \texttt{self.{\_}{\_}class{\_}{\_}} to
+\texttt{{\_}{\_}get{\_}{\_}}.
+
+Custom descriptors can be used to restrict the access to objects in a
+more general way than trough properties. For instance, suppose one
+wants to raise an error if a given attribute 'a' is accessed, both
+from the class and from the instance: a property cannot help here,
+since it works only from the instance. The solution is the following
+custom descriptor:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~AccessError(object):}\\
+\mbox{~~~~"""Descriptor~raising~an~AttributeError~when~the~attribute~is~}\\
+\mbox{~~~~accessed"""~{\#}could~be~done~with~a~property}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,errormessage):}\\
+\mbox{~~~~~~~~self.msg=errormessage}\\
+\mbox{~~~~def~{\_}{\_}get{\_}{\_}(self,obj,cls=None):}\\
+\mbox{~~~~~~~~raise~AttributeError(self.msg)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~from~oopp~import~AccessError}\\
+\mbox{>>>~class~C(object):}\\
+\mbox{...~~~~a=AccessError("'a'~cannot~be~accessed")}\\
+\mbox{>>>~c=C()}\\
+\mbox{>>>~c.a~{\#}error}\\
+\mbox{Traceback~(most~recent~call~last):}\\
+\mbox{~~File~"<stdin>",~line~1,~in~?}\\
+\mbox{~~File~"oopp.py",~line~313,~in~{\_}{\_}get{\_}{\_}}\\
+\mbox{~~~~raise~AttributeError(self.msg)}\\
+\mbox{AttributeError:~'a'~cannot~be~accessed}\\
+\mbox{>>>~C.a~{\#}error}\\
+\mbox{Traceback~(most~recent~call~last):}\\
+\mbox{~~File~"<stdin>",~line~1,~in~?}\\
+\mbox{~~File~"oopp.py",~line~313,~in~{\_}{\_}get{\_}{\_}}\\
+\mbox{~~~~raise~AttributeError(self.msg)}\\
+\mbox{AttributeError:~'a'~cannot~be~accessed}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+It is always possibile to convert plain attributes (i.e. attributes
+without a ''{\_}{\_}get{\_}{\_}`` method) to descriptor objects:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~convert2descriptor(object):}\\
+\mbox{~~~~"""To~all~practical~means,~this~class~acts~as~a~function~that,~given~an}\\
+\mbox{~~~~object,~adds~to~it~a~{\_}{\_}get{\_}{\_}~method~if~it~is~not~already~there.~The~}\\
+\mbox{~~~~added~{\_}{\_}get{\_}{\_}~method~is~trivial~and~simply~returns~the~original~object,~}\\
+\mbox{~~~~independently~from~obj~and~cls."""}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,a):}\\
+\mbox{~~~~~~~~if~hasattr(a,"{\_}{\_}get{\_}{\_}"):~{\#}~do~nothing}\\
+\mbox{~~~~~~~~~~~~return~a~{\#}~a~is~already~a~descriptor}\\
+\mbox{~~~~~~~~else:~{\#}~creates~a~trivial~attribute~descriptor}\\
+\mbox{~~~~~~~~~~~~cls.a=a}\\
+\mbox{~~~~~~~~~~~~return~object.{\_}{\_}new{\_}{\_}(cls)}\\
+\mbox{~~~~def~{\_}{\_}get{\_}{\_}(self,obj,cls=None):}\\
+\mbox{~~~~~~~~"Returns~self.a~independently~from~obj~and~cls"}\\
+\mbox{~~~~~~~~return~self.a}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This example also shows the magic of \texttt{{\_}{\_}new{\_}{\_}}, that allows to use a
+class as a function. The output of 'convert2descriptor(a)' can be both
+an instance of 'convert2descriptor' (in this case 'convert2descriptor' acts as
+a normal class, i.e. as an object factory) or 'a' itself
+(if 'a' is already a descriptor): in this case 'convert2descriptor' acts
+as a function.
+
+For instance, a string is converted to a descriptor
+\begin{quote}
+\begin{verbatim}>>> from oopp import convert2descriptor
+>>> a2=convert2descriptor('a')
+>>> a2
+<oopp.convert2descriptor object at 0x4017506c>
+>>> a2.__get__('whatever')
+'a'\end{verbatim}
+\end{quote}
+
+whereas a function is untouched:
+\begin{quote}
+\begin{verbatim}>>> def f(): pass
+>>> f2=convert2descriptor(f) # does nothing
+>>> f2
+<function f at 0x4019110c>\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{data-descriptors}{}
+\pdfbookmark[1]{Data descriptors}{data-descriptors}
+\subsection*{Data descriptors}
+
+It is also possible to specify a \texttt{{\_}{\_}set{\_}{\_}} method (descriptors
+with a \texttt{{\_}{\_}set{\_}{\_}} method are typically data descriptors) with
+the signature \texttt{{\_}{\_}set{\_}{\_}(self,obj,value)} as in the following
+example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<datadescr.py>}\\
+\mbox{}\\
+\mbox{class~DataDescriptor(object):}\\
+\mbox{~~~~value=None}\\
+\mbox{~~~~def~{\_}{\_}get{\_}{\_}(self,~obj,~cls=None):}\\
+\mbox{~~~~~~~~if~obj~is~None:~obj=cls}\\
+\mbox{~~~~~~~~print~"Getting",obj,"value~=",self.value}\\
+\mbox{~~~~~~~~return~self.value}\\
+\mbox{~~~~def~{\_}{\_}set{\_}{\_}(self,~obj,~value):}\\
+\mbox{~~~~~~~~self.value=value}\\
+\mbox{~~~~~~~~print~"Setting",obj,"value~=",value}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~d=DataDescriptor()}\\
+\mbox{}\\
+\mbox{c=C()}\\
+\mbox{}\\
+\mbox{c.d=1~{\#}calls~C.{\_}{\_}dict{\_}{\_}['d'].{\_}{\_}set{\_}{\_}(c,1)}\\
+\mbox{c.d~~~{\#}calls~C.{\_}{\_}dict{\_}{\_}['d'].{\_}{\_}get{\_}{\_}(c,C)}\\
+\mbox{C.d~~~{\#}calls~C.{\_}{\_}dict{\_}{\_}['d'].{\_}{\_}get{\_}{\_}(None,C)}\\
+\mbox{C.d=0~{\#}does~*not*~call~{\_}{\_}set{\_}{\_}}\\
+\mbox{print~"C.d~=",C.d}\\
+\mbox{}\\
+\mbox{{\#}</datadescr.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Setting~<C~object~at~0x401bc1ec>~value~=~1}\\
+\mbox{Getting~<C~object~at~0x401bc42c>~value~=~1}\\
+\mbox{Getting~<class~'C'>~value~=~1~~~~~~}\\
+\mbox{C.d~=~0}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With this knowledge, we may now reconsider the clock example given
+in chapter 3. {\#}NO!??
+\begin{quote}
+\begin{verbatim}>>> import oopp
+>>> class Clock(object): pass
+>>> myclock=Clock()
+...
+>>> myclock.get_time=oopp.get_time # this is a function
+>>> Clock.get_time=lambda self : oopp.get_time() # this is a method \end{verbatim}
+\end{quote}
+
+In this example, \texttt{myclock.get{\_}time}, which is attached to the \texttt{myclock}
+object, is a function, whereas \texttt{Clock.get{\_}time}, which is attached to
+the \texttt{Clock} class is a method. We may also check this by using the \texttt{type}
+function:
+\begin{quote}
+\begin{verbatim}>>> type(myclock.get_time)
+<type 'function'>\end{verbatim}
+\end{quote}
+
+whereas
+\begin{quote}
+\begin{verbatim}>>> type(Clock.get_time)
+<type 'instance method'>\end{verbatim}
+\end{quote}
+
+It must be remarked that user-defined attribute descriptors, just as
+properties, allow to arbitrarily change the semantics of the language
+and should be used with care.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-super-attribute-descriptor}{}
+\pdfbookmark[1]{The super attribute descriptor}{the-super-attribute-descriptor}
+\subsection*{The \texttt{super} attribute descriptor}
+
+super has also a second form, where it is more used as a descriptor.
+
+\texttt{super} objects are attribute descriptors, too, with a \texttt{{\_}{\_}get{\_}{\_}}
+method returning a method-wrapper object:
+\begin{quote}
+\begin{verbatim}>>> super(C,C()).__get__
+<method-wrapper object at 0x8161074>\end{verbatim}
+\end{quote}
+
+Here I give some example of acceptable call:
+\begin{quote}
+\begin{verbatim}>>> super(C,C()).__get__('whatever')
+<super: <class 'C'>, <C object>>
+>>> super(C,C()).__get__('whatever','whatever')
+<super: <class 'C'>, <C object>>\end{verbatim}
+\end{quote}
+
+Unfortunately, for the time being
+(i.e. for Python 2.3), the \texttt{super} mechanism has various limitations.
+To show the issues, let me start by considering the following base class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~ExampleBaseClass(PrettyPrinted):}\\
+\mbox{~~~~"""Contains~a~regular~method~'m',~a~staticmethod~'s',~a~classmethod~}\\
+\mbox{~~~~'c',~a~property~'p'~and~a~data~attribute~'d'."""}\\
+\mbox{~~~~m=lambda~self:~'regular~method~of~{\%}s'~{\%}~self}\\
+\mbox{~~~~s=staticmethod(lambda~:~'staticmethod')}\\
+\mbox{~~~~c=classmethod(lambda~cls:~'classmethod~of~{\%}s'~{\%}~cls)}\\
+\mbox{~~~~p=property(lambda~self:~'property~of~{\%}s'~{\%}~self)}\\
+\mbox{~~~~a=AccessError('Expected~error')}\\
+\mbox{~~~~d='data'}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now, let me derive a new class C from ExampleBaseClass:
+\begin{quote}
+\begin{verbatim}>>> from oopp import ExampleBaseClass
+>>> class C(ExampleBaseClass): pass
+>>> c=C()\end{verbatim}
+\end{quote}
+
+Ideally, we would like to retrieve the methods and attributes of
+ExampleBaseClass from C, by using the \texttt{super} mechanism.
+\newcounter{listcnt24}
+\begin{list}{\arabic{listcnt24}.}
+{
+\usecounter{listcnt24}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+We see that \texttt{super} works without problems for regular methods,
+staticmethods and classmethods:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> super(C,c).m()
+'regular method of <C>'
+>>> super(C,c).s()
+'staticmethod'
+>>> super(C,c).c()
+"classmethod of <class '__main__.C'>"\end{verbatim}
+\end{quote}
+
+It also works for user defined attribute descriptors:
+\begin{quote}
+\begin{verbatim}>>> super(C,c).a # access error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 340, in __get__
+ raise AttributeError(self.msg)
+AttributeError: Expected error\end{verbatim}
+\end{quote}
+
+and for properties (only for Python 2.3+):
+\begin{quote}
+\begin{verbatim}>>> ExampleBaseClass.p
+<property object at 0x81b30fc>\end{verbatim}
+\end{quote}
+
+In Python 2.2 one would get an error, instead
+\begin{quote}
+\begin{verbatim}>>> super(C,c).p #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute 'p'\end{verbatim}
+\end{quote}
+
+3. Moreover, certain attributes of the superclass, such as its
+\texttt{{\_}{\_}name{\_}{\_}}, cannot be retrieved:
+\begin{quote}
+\begin{verbatim}>>> ExampleBaseClass.__name__
+'ExampleBaseClass'
+>>> super(C,c).__name__ #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'\end{verbatim}
+\end{quote}
+\newcounter{listcnt25}
+\begin{list}{\arabic{listcnt25}.}
+{
+\usecounter{listcnt25}
+\addtocounter{listcnt25}{3}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+There is no direct way to retrieve the methods of the super-superclass
+(i.e. the grandmother class, if you wish) or in general the furthest
+ancestors, since \texttt{super} does not chain.
+
+\item {}
+Finally, there are some subtle issues with the \texttt{super(cls)} syntax:
+
+\end{list}
+\begin{quote}
+\begin{verbatim}>>> super(C).m #(2) error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute 'm'\end{verbatim}
+\end{quote}
+
+means \texttt{super(C).{\_}{\_}get{\_}{\_}(None,C)}, but only
+\texttt{super(C).{\_}{\_}get{\_}{\_}(c,C).m==super(C,c)} works.
+\begin{quote}
+\begin{quote}
+
+On the other hand,
+\end{quote}
+\begin{verbatim}>>> super(C).__init__ #(1)
+<built-in method __init__ of type object at 0x80e6fc0>
+>>> super(C).__new__ #(1)
+<built-in method __init__ of type object at 0x80e6fc0>\end{verbatim}
+\begin{quote}
+
+seems to work, whereas in reality does not. The reason is that since
+\texttt{super} objects are instances
+of \texttt{object}, they inherit object's methods, and in particular
+\texttt{{\_}{\_}init{\_}{\_}} ; therefore the \texttt{{\_}{\_}init{\_}{\_}} method in (1) is \emph{not}
+the \texttt{ExampleBaseClass.{\_}{\_}init{\_}{\_}} method. The point is that \texttt{super}
+objects are attribute descriptors and not references to the superclass.
+\end{quote}
+\end{quote}
+
+Probably, in future versions of Python the \texttt{super} mechanism will be
+improved. However, for the time being, one must provide a workaround for
+dealing with these issues. This will be discussed in the next chapter.
+
+
+%___________________________________________________________________________
+
+\hypertarget{method-wrappers}{}
+\pdfbookmark[1]{Method wrappers}{method-wrappers}
+\subsection*{Method wrappers}
+
+One of the most typical applications of attribute descriptors is their
+usage as \emph{method wrappers}.
+
+Suppose, for instance, one wants to add tracing capabilities to
+the methods of a class for debugging purposes. The problem
+can be solved with a custom descriptor class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~inspect}\\
+\mbox{}\\
+\mbox{class~wrappedmethod(Customizable):}\\
+\mbox{~~~~"""Customizable~method~factory~intended~for~derivation.}\\
+\mbox{~~~~The~wrapper~method~is~overridden~in~the~children."""}\\
+\mbox{}\\
+\mbox{~~~~logfile=sys.stdout~{\#}~default}\\
+\mbox{~~~~namespace=''~{\#}~default}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,meth):~{\#}~meth~is~a~descriptor}\\
+\mbox{~~~~~~~~if~isinstance(meth,FunctionType):}\\
+\mbox{~~~~~~~~~~~~kind=0~{\#}~regular~method}\\
+\mbox{~~~~~~~~~~~~func=meth}\\
+\mbox{~~~~~~~~elif~isinstance(meth,staticmethod):}\\
+\mbox{~~~~~~~~~~~~kind=1~{\#}~static~method}\\
+\mbox{~~~~~~~~~~~~func=meth.{\_}{\_}get{\_}{\_}('whatever')}\\
+\mbox{~~~~~~~~elif~isinstance(meth,classmethod):}\\
+\mbox{~~~~~~~~~~~~kind=2~{\#}~class~method}\\
+\mbox{~~~~~~~~~~~~func=meth.{\_}{\_}get{\_}{\_}('whatever','whatever').im{\_}func~}\\
+\mbox{~~~~~~~~elif~isinstance(meth,wrappedmethod):~{\#}~already~wrapped}\\
+\mbox{~~~~~~~~~~~~return~meth~{\#}~do~nothing}\\
+\mbox{~~~~~~~~elif~inspect.ismethoddescriptor(meth):}\\
+\mbox{~~~~~~~~~~~~kind=0;~func=meth~{\#}~for~many~builtin~methods~}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~return~meth~{\#}~do~nothing}\\
+\mbox{~~~~~~~~self=super(wrappedmethod,cls).{\_}{\_}new{\_}{\_}(cls)}\\
+\mbox{~~~~~~~~self.kind=kind;~self.func=func~{\#}~pre-initialize}\\
+\mbox{~~~~~~~~return~self}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,meth):~{\#}~meth~not~used}\\
+\mbox{~~~~~~~~self.logfile=self.logfile~{\#}~default~values}\\
+\mbox{~~~~~~~~self.namespace=self.namespace~{\#}~copy~the~current}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}get{\_}{\_}(self,obj,cls):~{\#}~closure~}\\
+\mbox{~~~~~~~~def~{\_}(*args,**kw):}\\
+\mbox{~~~~~~~~~~~~if~obj~is~None:~o=()~{\#}~unbound~method~call}\\
+\mbox{~~~~~~~~~~~~else:~o=(obj,)~{\#}~bound~method~call}\\
+\mbox{~~~~~~~~~~~~allargs=[o,(),(cls,)][self.kind]+args~}\\
+\mbox{~~~~~~~~~~~~return~self.wrapper()(*allargs,**kw)}\\
+\mbox{~~~~~~~~return~{\_}~{\#}~the~wrapped~function}\\
+\mbox{~~~~~~~~{\#}~allargs~is~the~only~nontrivial~line~in~{\_};~it~adds}\\
+\mbox{~~~~~~~~{\#}~0~-~obj~if~meth~is~a~regular~method}\\
+\mbox{~~~~~~~~{\#}~1~-~nothing~if~meth~is~a~static~method}\\
+\mbox{~~~~~~~~{\#}~2~-~cls~if~meth~is~a~class~method}\\
+\mbox{}\\
+\mbox{~~~~def~wrapper(self):~return~self.func~{\#}~do~nothing,~to~be~overridden}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This class is intended for derivation: the wrapper method has to be overridden
+in the children in order to introduce the wanted feature. If I want to
+implement the capability of tracing methods, I can reuse the \texttt{with{\_}tracer}
+closure introduced in chapter 2:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~tracedmethod(wrappedmethod):}\\
+\mbox{~~~~def~wrapper(self):}\\
+\mbox{~~~~~~~~return~with{\_}tracer(self.func,self.namespace,self.logfile)}\\
+\mbox{~~~~~~~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Nothing prevents me from introducing timing features by reusing the
+\texttt{with{\_}timer} closure:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~timedmethod(wrappedmethod):}\\
+\mbox{~~~~iterations=1~{\#}~additional~default~parameter}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,meth):}\\
+\mbox{~~~~~~~~super(timedmethod,self).{\_}{\_}init{\_}{\_}(self,meth)}\\
+\mbox{~~~~~~~~self.iterations=self.iterations~{\#}~copy}\\
+\mbox{}\\
+\mbox{~~~~def~wrapper(self):}\\
+\mbox{~~~~~~~~return~with{\_}timer(self.func,self.namespace,}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~self.iterations,self.logfile)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+
+The dictionary of wrapped functions is then built from an utility function
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~wrap(obj,wrapped,condition=lambda~k,v:~True,~err=None):}\\
+\mbox{~~~~"Retrieves~obj's~dictionary~and~wraps~it"}\\
+\mbox{~~~~if~isinstance(obj,dict):~{\#}~obj~is~a~dictionary~}\\
+\mbox{~~~~~~~~dic=obj}\\
+\mbox{~~~~else:~}\\
+\mbox{~~~~~~~~dic=getattr(obj,'{\_}{\_}dict{\_}{\_}',{\{}{\}}).copy()~{\#}~avoids~dictproxy~objects}\\
+\mbox{~~~~~~~~if~not~dic:~dic=attributes(obj)~{\#}~for~simple~objects}\\
+\mbox{~~~~wrapped.namespace=getattr(obj,'{\_}{\_}name{\_}{\_}','')}\\
+\mbox{~~~~for~name,attr~in~dic.iteritems():~{\#}~modify~dic}\\
+\mbox{~~~~~~~~if~condition(name,attr):~dic[name]=wrapped(attr)}\\
+\mbox{~~~~if~not~isinstance(obj,dict):~{\#}~modify~obj}\\
+\mbox{~~~~~~~~customize(obj,err,**dic)~}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<tracingmethods.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{class~C(object):~}\\
+\mbox{~~~~"Class~with~traced~methods"}\\
+\mbox{}\\
+\mbox{~~~~def~f(self):~return~self~}\\
+\mbox{~~~~f=tracedmethod(f)}\\
+\mbox{}\\
+\mbox{~~~~g=staticmethod(lambda:None)}\\
+\mbox{~~~~g=tracedmethod(g)}\\
+\mbox{}\\
+\mbox{~~~~h=classmethod(do{\_}nothing)}\\
+\mbox{~~~~h=tracedmethod(h)}\\
+\mbox{}\\
+\mbox{c=C()}\\
+\mbox{}\\
+\mbox{{\#}unbound~calls}\\
+\mbox{C.f(c)~}\\
+\mbox{C.g()}\\
+\mbox{C.h()}\\
+\mbox{}\\
+\mbox{{\#}bound~calls}\\
+\mbox{c.f()~~}\\
+\mbox{c.g()}\\
+\mbox{c.h()}\\
+\mbox{}\\
+\mbox{{\#}</tracingmethods.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[C]~Calling~'f'~with~arguments}\\
+\mbox{(<C~object~at~0x402042cc>,){\{}{\}}~...}\\
+\mbox{->~'C.f'~called~with~result:~<C~object~at~0x402042cc>}\\
+\mbox{}\\
+\mbox{[C]~Calling~'<lambda>'~with~arguments}\\
+\mbox{(){\{}{\}}~...}\\
+\mbox{->~'C.<lambda>'~called~with~result:~None}\\
+\mbox{}\\
+\mbox{[C]~Calling~'do{\_}nothing'~with~arguments}\\
+\mbox{(<class~'C'>,){\{}{\}}~...}\\
+\mbox{->~'C.do{\_}nothing'~called~with~result:~None}\\
+\mbox{}\\
+\mbox{[C]~Calling~'f'~with~arguments}\\
+\mbox{(<C~object~at~0x402042cc>,){\{}{\}}~...}\\
+\mbox{->~'C.f'~called~with~result:~<C~object~at~0x402042cc>}\\
+\mbox{}\\
+\mbox{[C]~Calling~'<lambda>'~with~arguments}\\
+\mbox{(){\{}{\}}~...}\\
+\mbox{->~'C.<lambda>'~called~with~result:~None}\\
+\mbox{}\\
+\mbox{[C]~Calling~'do{\_}nothing'~with~arguments}\\
+\mbox{(<class~'C'>,){\{}{\}}~...}\\
+\mbox{->~'C.do{\_}nothing'~called~with~result:~None}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The approach in 'tracingmethods.py' works, but it is far from
+being elegant, since I had to explicitly wrap each method in the
+class by hand.
+
+Both problems can be avoided.
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> wrap(Clock,tracedmethod)
+>>> Clock.get_time()
+[Clock] Calling 'get_time' with arguments
+(){} ...
+-> 'Clock.get_time' called with result: 21:56:52
+'21:56:52'\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-subtleties-of-multiple-inheritance}{}
+\pdfbookmark[0]{THE SUBTLETIES OF MULTIPLE INHERITANCE}{the-subtleties-of-multiple-inheritance}
+\section*{THE SUBTLETIES OF MULTIPLE INHERITANCE}
+
+In chapter 4 we introduced the concept of multiple inheritance and discussed
+its simplest applications in absence of name collisions. When with methods
+with different names are derived from different classes multiple inheritance
+is pretty trivial. However, all kind of subtilites comes in presence of name
+clashing, i.e. when we multiply inherits different methods defined in different
+classes but with the \emph{same} name.
+In order to understand what happens in this situation, it is essential to
+understand the concept of Method Resolution Order (MRO). For reader's
+convenience, I collect in this chapter some of the information
+reported in \href{http://www.python.org/2.3/mro.html}{http://www.python.org/2.3/mro.html}.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-little-bit-of-history-why-python-2-3-has-changed-the-mro}{}
+\pdfbookmark[1]{A little bit of history: why Python 2.3 has changed the MRO}{a-little-bit-of-history-why-python-2-3-has-changed-the-mro}
+\subsection*{A little bit of history: why Python 2.3 has changed the MRO}
+
+Everything started with a post by Samuele Pedroni to the Python
+development mailing list [\hyperlink{id36}{18}]. 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{id40}{19}]. 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: 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.
+\newcounter{listcnt26}
+\begin{list}{\arabic{listcnt26})}
+{
+\usecounter{listcnt26}
+\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, 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{ttfamily}\begin{flushleft}
+\mbox{~-----------}\\
+\mbox{|~~~~~~~~~~~|}\\
+\mbox{|~~~~O~~~~~~|}\\
+\mbox{|~~/~~~{\textbackslash}~~~~|}\\
+\mbox{~-~X~~~~Y~~/}\\
+\mbox{~~~|~~/~|~/}\\
+\mbox{~~~|~/~~|/}\\
+\mbox{~~~A~~~~B}\\
+\mbox{~~~{\textbackslash}~~~/}\\
+\mbox{~~~~~?}
+\end{flushleft}\end{ttfamily}
+\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).
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-c3-method-resolution-order}{}
+\pdfbookmark[1]{The C3 Method Resolution Order}{the-c3-method-resolution-order}
+\subsection*{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. In order to do that, we need the
+concept of \emph{merging} lists, since the rule says that
+\begin{quote}
+
+\emph{the linearization of C is the sum of C plus the merge of a) the
+linearizations of the parents and b) 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}
+
+How is the merge computed? The rule is the following:
+\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:
+\newcounter{listcnt27}
+\begin{list}{\arabic{listcnt27}.}
+{
+\usecounter{listcnt27}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+C is the \texttt{object} class, which has no parents; in this case its
+linearization coincides with itself,
+\begin{quote}
+
+L[object] = object.
+\end{quote}
+
+\item {}
+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}
+
+\end{list}
+
+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 ;-)
+
+
+%___________________________________________________________________________
+
+\hypertarget{examples}{}
+\pdfbookmark[1]{Examples}{examples}
+\subsection*{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{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~6}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~---}\\
+\mbox{Level~3~~~~~~~~~~~~~~~~~|~O~|~~~~~~~~~~~~~~~~~~(more~general)}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~/~~---~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~/~~~~|~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~/~~~~~|~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~/~~~~~~|~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~---~~~~---~~~~---~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{Level~2~~~~~~~~3~|~D~|~4|~E~|~~|~F~|~5~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~---~~~~---~~~~---~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~{\textbackslash}~~{\textbackslash}~{\_}~/~~~~~~~|~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~{\textbackslash}~{\_}~~~~|~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~/~~~~~~{\textbackslash}~~|~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~---~~~~~~---~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{Level~1~~~~~~~~~~~~1~|~B~|~~~~|~C~|~2~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~---~~~~~~---~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~/~~~~~~~~~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~---}\\
+\mbox{Level~0~~~~~~~~~~~~~~~~~0~|~A~|~~~~~~~~~~~~~~~~(more~specialized)}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~---}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The linearizations of O,D,E and F are trivial:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[O]~=~O}\\
+\mbox{L[D]~=~D~O}\\
+\mbox{L[E]~=~E~O}\\
+\mbox{L[F]~=~F~O}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The linearization of B can be computed as
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[B]~=~B~+~merge(DO,~EO,~DE)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+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
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[B]~=~~B~D~E~O}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Using the same procedure one finds:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[C]~=~C~+~merge(DO,FO,DF)}\\
+\mbox{~~~~~=~C~+~D~+~merge(O,FO,F)}\\
+\mbox{~~~~~=~C~+~D~+~F~+~merge(O,O)}\\
+\mbox{~~~~~=~C~D~F~O}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now we can compute:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[A]~=~A~+~merge(BDEO,CDFO,BC)}\\
+\mbox{~~~~~=~A~+~B~+~merge(DEO,CDFO,C)}\\
+\mbox{~~~~~=~A~+~B~+~C~+~merge(DEO,DFO)}\\
+\mbox{~~~~~=~A~+~B~+~C~+~D~+~merge(EO,FO)}\\
+\mbox{~~~~~=~A~+~B~+~C~+~D~+~E~+~merge(O,FO)}\\
+\mbox{~~~~~=~A~+~B~+~C~+~D~+~E~+~F~+~merge(O,O)}\\
+\mbox{~~~~~=~A~B~C~D~E~F~O}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~6}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~---}\\
+\mbox{Level~3~~~~~~~~~~~~~~~~~~|~O~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~/~~---~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~/~~~~|~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~/~~~~~|~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~/~~~~~~|~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~---~~~~~---~~~~---}\\
+\mbox{Level~2~~~~~~~~2~|~E~|~4~|~D~|~~|~F~|~5}\\
+\mbox{~~~~~~~~~~~~~~~~~~---~~~~~---~~~~---}\\
+\mbox{~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~/~{\textbackslash}~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/~~~{\textbackslash}~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~/~~~~~{\textbackslash}~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~---~~~~~---}\\
+\mbox{Level~1~~~~~~~~~~~~1~|~B~|~~~|~C~|~3}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~---~~~~~---}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~---}\\
+\mbox{Level~0~~~~~~~~~~~~~~~~0~|~A~|}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~---}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{L[O]~=~0}\\
+\mbox{L[X]~=~X~O}\\
+\mbox{L[Y]~=~Y~O}\\
+\mbox{L[A]~=~A~X~Y~O}\\
+\mbox{L[B]~=~B~Y~X~O}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+However, it is impossible to compute the linearization for a class C
+that inherits from A and B:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[C]~=~C~+~merge(AXYO,~BYXO,~AB)}\\
+\mbox{~~~~~=~C~+~A~+~merge(XYO,~BYXO,~B)}\\
+\mbox{~~~~~=~C~+~A~+~B~+~merge(XYO,~YXO)}
+\end{flushleft}\end{ttfamily}
+\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.
+
+
+%___________________________________________________________________________
+
+\hypertarget{bad-method-resolution-orders}{}
+\pdfbookmark[1]{Bad Method Resolution Orders}{bad-method-resolution-orders}
+\subsection*{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{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~O}\\
+\mbox{~~~~~~~~~~~~~|}\\
+\mbox{(buy~spam)~~~F}\\
+\mbox{~~~~~~~~~~~~~|~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~|~E~~~(buy~eggs)}\\
+\mbox{~~~~~~~~~~~~~|~/}\\
+\mbox{~~~~~~~~~~~~~G}\\
+\mbox{}\\
+\mbox{~~~~~~(buy~eggs~or~spam~?)}
+\end{flushleft}\end{ttfamily}
+\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 #under Python 2.3 this is an error
+'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{ttfamily}\begin{flushleft}
+\mbox{L[G,P22]=~G~E~F~object~~~{\#}~F~*follows*~E}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{merge(FO,EFO,FE)}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~O}\\
+\mbox{~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~~F~(spam)}\\
+\mbox{~~~~~~~~~/~|}\\
+\mbox{(eggs)~~~E~|}\\
+\mbox{~~~~~~~~~{\textbackslash}~|}\\
+\mbox{~~~~~~~~~~~G}\\
+\mbox{~~~~~~~~~~~~~(eggs,~no~doubt)}
+\end{flushleft}\end{ttfamily}
+\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{listcnt28}
+\begin{list}{\arabic{listcnt28}.}
+{
+\usecounter{listcnt28}
+\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}
+
+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{ttfamily}\begin{flushleft}
+\mbox{~~~C}\\
+\mbox{~~/~{\textbackslash}}\\
+\mbox{~/~~~{\textbackslash}}\\
+\mbox{A~~~~~B}\\
+\mbox{~{\textbackslash}~~~/}\\
+\mbox{~~{\textbackslash}~/}\\
+\mbox{~~~D}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+One easily discerns the inconsistency:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{L[B,P21]~=~B~C~~~~~~~~{\#}~B~precedes~C~:~B's~methods~win}\\
+\mbox{L[D,P21]~=~D~A~C~B~C~~{\#}~B~follows~C~~:~C's~methods~win!}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{L[D]~=~D~A~B~C}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Guido points out in his essay [\hyperlink{id41}{20}] 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:
+\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{ttfamily}\begin{flushleft}
+\mbox{L[A]~=~A~O}\\
+\mbox{L[B]~=~B~O}\\
+\mbox{L[C]~=~C~O}\\
+\mbox{L[D]~=~D~O}\\
+\mbox{L[E]~=~E~O}\\
+\mbox{L[K1]=~K1~A~B~C~O}\\
+\mbox{L[K2]=~K2~D~B~E~O}\\
+\mbox{L[K3]=~K3~D~A~O}\\
+\mbox{L[Z]~=~Z~K1~K2~K3~D~A~B~C~E~O}
+\end{flushleft}\end{ttfamily}
+\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{ttfamily}\begin{flushleft}
+\mbox{L[Z,P22]~=~Z~K1~K3~A~K2~D~B~C~E~O}
+\end{flushleft}\end{ttfamily}
+\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.
+\begin{figure}[b]\hypertarget{id40}[19]
+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{id41}[20]
+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{id42}[21]
+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}
+\begin{figure}[b]\hypertarget{id43}[22]
+The (in)famous book on metaclasses, \emph{Putting Metaclasses to Work}:
+Ira R. Forman, Scott Danforth, Addison-Wesley 1999 (out of print,
+but probably still available on \href{http://www.amazon.com}{http://www.amazon.com})
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{understanding-the-method-resolution-order}{}
+\pdfbookmark[1]{Understanding the Method Resolution Order}{understanding-the-method-resolution-order}
+\subsection*{Understanding the Method Resolution Order}
+
+The MRO of any given (new style) Python class is given
+by the special attribute \texttt{{\_}{\_}mro{\_}{\_}}. Notice that since
+Python is an extremely dynamic language it is possible
+to delete and to generate whole classes at run time, therefore the MRO
+is a dynamic concept. For instance, let me show how it is possibile to
+remove a class from my
+paleoanthropological hierarchy: for instance I can
+replace the last class 'HomoSapiensSapiens' with 'HomoSapiensNeardenthalensis'
+(changing a class in the middle of the hierarchy would be more difficult). The
+following lines do the job dynamically:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> del HomoSapiensSapiens
+>>> class HomoSapiensNeardenthalensis(HomoSapiens):
+... def can(self):
+... super(self.__this,self).can()
+... print " - make something"
+>>> reflective(HomoSapiensNeardenthalensis)
+>>> HomoSapiensNeardenthalensis().can()
+HomoSapiensNeardenthalensis can:
+ - make tools
+ - make abstractions
+ - make something\end{verbatim}
+\end{quote}
+
+In this case the MRO of 'HomoSapiensNeardenthalensis', i.e. the list of
+all its ancestors, is
+\begin{quote}
+\begin{verbatim}>>> HomoSapiensNeardenthalensis.__mro__
+[<class '__main__.HomoSapiensNeardenthalensis'>,<class 'oopp.HomoSapiens'>,
+ <class 'oopp.HomoHabilis'>, <class 'oopp.Homo'>,
+ <class 'oopp.PrettyPrinted'>, <class 'oopp.object'>]\end{verbatim}
+\end{quote}
+
+The \texttt{{\_}{\_}mro{\_}{\_}} attribute gives the \emph{linearization} of the class, i.e. the
+ordered list of its ancestors, starting from the class itself and ending
+with object. The linearization of a class is essential in order to specify
+the resolution order of methods and attributes, i.e. the Method Resolution
+Order (MRO). In the case of single inheritance hierarchies, such the
+paleonthropological example, the MRO is pretty obvious; on the contrary
+it is a quite non-trivial concept in the case of multiple inheritance
+hierarchies.
+
+For instance, let me reconsider my first example of multiple inheritance,
+the \texttt{NonInstantiableClock} class, inheriting from 'NonInstantiable' and
+'Clock'. I may represent the hierarchy with the following inheritance graph:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~--~~~object~~~--~}\\
+\mbox{~~~~~~~~/~~~~~({\_}{\_}new{\_}{\_})~~~~{\textbackslash}}\\
+\mbox{~~~~~~~/~~~~~~~~~~~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~/~~~~~~~~~~~~~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~Clock~~~~~~~~~~~~~~~~NonInstantiable}\\
+\mbox{(get{\_}time)~~~~~~~~~~~~~~~~~({\_}{\_}new{\_}{\_})}\\
+\mbox{~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~NonInstantiableClock~~~}\\
+\mbox{~~~~~~~~~~(get{\_}time,{\_}{\_}new{\_}{\_})}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The class \texttt{Clock} define a \texttt{get{\_}time} method, whereas the class
+\texttt{NonInstantiable} overrides the \texttt{{\_}{\_}new{\_}{\_}} method of the \texttt{object} class;
+the class \texttt{NonInstantiableClock} inherits \texttt{get{\_}time} from 'Clock' and
+\texttt{{\_}{\_}new{\_}{\_}} from 'NonInstantiable'.
+
+The linearization of 'NonInstantiableClock' is
+\begin{quote}
+\begin{verbatim}>>> NonInstantiableClock.mro()
+[<class '__main__.NonInstantiableClock'>, <class 'oopp.Clock'>,
+ <class 'oopp.NonInstantiable'>, <type 'object'>]\end{verbatim}
+\end{quote}
+
+In particular, since 'NonInstantiable' precedes 'object', its \texttt{{\_}{\_}new{\_}{\_}}
+method overrides the \texttt{object} new method. However, with the MRO used before
+Python 2.2, the linearization would have been \texttt{NonInstantiableClock, Clock,
+object, NonInstantiable, object} and the \texttt{{\_}{\_}new{\_}{\_}} method of object would
+have (hypothetically, of course, since before Python 2.2 there was not
+\texttt{{\_}{\_}new{\_}{\_}} method! ;-) overridden the \texttt{{\_}{\_}new{\_}{\_}}
+method of \texttt{NonInstantiable}, therefore \texttt{NonInstantiableClock} would
+have lost the property of being non-instantiable!
+
+This simple example shows that the choice of a correct Method Resolution
+Order is far from being obvious in general multiple inheritance hierarchies.
+After a false start in Python 2.2, (with a MRO failing in some subtle cases)
+Python 2.3 decided to adopt the so-called C3 MRO, invented by people working
+on Dylan (even if Dylan itself uses the MRO of Common Lisp CLOS). Since this
+is quite a technical matter, I defer the interested reader to appendix 2
+for a full discussion of the C3 algorithm.
+
+Here, I prefer to point out how the built-in
+\texttt{super} object works in multiple inheritance situations. To this aim, it
+is convenient to define an utility function that retrieves the ancestors
+of a given class with respect to the MRO of one of its subclasses:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~ancestor(C,S=None):}\\
+\mbox{~~~~"""Returns~the~ancestors~of~the~first~argument~with~respect~to~the~}\\
+\mbox{~~~~MRO~of~the~second~argument.~If~the~second~argument~is~None,~then~}\\
+\mbox{~~~~returns~the~MRO~of~the~first~argument."""}\\
+\mbox{~~~~if~C~is~object:}\\
+\mbox{~~~~~~~~raise~TypeError("There~is~no~superclass~of~object")}\\
+\mbox{~~~~elif~S~is~None~or~S~is~C:}\\
+\mbox{~~~~~~~~return~list(C.{\_}{\_}mro{\_}{\_})}\\
+\mbox{~~~~elif~issubclass(S,C):~{\#}~typical~case}\\
+\mbox{~~~~~~~~mro=list(S.{\_}{\_}mro{\_}{\_})}\\
+\mbox{~~~~~~~~return~mro[mro.index(C):]~{\#}~compute~the~ancestors~from~the~MRO~of~S}\\
+\mbox{~~~~else:}\\
+\mbox{~~~~~~~~raise~TypeError("S~must~be~a~subclass~of~C")}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Let me show how the function \texttt{ancestor} works.
+Consider the class \texttt{Clock} in isolation: then
+its direct superclass, i.e. the first ancestor, is \texttt{object},
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> ancestor(Clock)[1]
+<type 'object'>\end{verbatim}
+\end{quote}
+
+therefore \texttt{super(Clock).{\_}{\_}new{\_}{\_}} retrieves the \texttt{object.{\_}{\_}new{\_}{\_}} method:
+\begin{quote}
+\begin{verbatim}>>> super(Clock).__new__
+<built-in method __new__ of type object at 0x80e6fc0>\end{verbatim}
+\end{quote}
+
+Consider now the \texttt{Clock} class together with its subclass
+\texttt{NonInstantiableClock}:
+in this case the first ancestor of \texttt{Clock}, \emph{with respect to the MRO of
+'NonInstantiableClock'} is \texttt{NonInstantiable}
+\begin{quote}
+\begin{verbatim}>>> ancestor(Clock,NonInstantiableClock)[1]
+<class 'oopp.NonInstantiable'>\end{verbatim}
+\end{quote}
+
+Therefore \texttt{super(Clock,NonInstantiableClock).{\_}{\_}new{\_}{\_}} retrieves the
+\texttt{NonInstantiable.{\_}{\_}new{\_}{\_}} method:
+\begin{quote}
+\begin{verbatim}>>> super(Clock,NonInstantiableClock).__new__
+<function __new__ at 0x81b293c>
+>>> NonInstantiable.__new__
+<function __new__ at 0x81b293c>\end{verbatim}
+\end{quote}
+
+It must be pointed out that \texttt{super(C,S)} is equivalent but not the same
+than \texttt{ancestor(C,S)[1]}, since it does not return the superclass:
+it returns a super object, instead:
+\begin{quote}
+\begin{verbatim}>>> super(Clock,NonInstantiableClock)
+<super: <class 'Clock'>, <type object>>\end{verbatim}
+
+{\#}{\textless}oopp.py{\textgreater}
+
+{\#}class Super(super):
+{\#} def {\_}{\_}init{\_}{\_}(self,C,S=None):
+{\#} super(Super,self).{\_}{\_}init{\_}{\_}(C,S)
+{\#} self.{\_}{\_}name{\_}{\_}=''Super({\%}s)`` {\%} C.{\_}{\_}name{\_}{\_}
+
+{\#}{\textless}/oopp.py{\textgreater}
+\end{quote}
+
+Finally, there is little quirk of super:
+\begin{quote}
+\begin{verbatim}>>> class C(PrettyPrinted): pass
+>>> s=super(C,C())
+>>> s.__str__()\end{verbatim}
+\end{quote}
+
+but
+\begin{quote}
+\begin{verbatim}>>> str(s) # idem for print s
+"<super: <class 'C'>, <C object>>"\end{verbatim}
+\end{quote}
+
+Idem for non-pre-existing methods:
+\begin{quote}
+\begin{verbatim}>>> class D(list): pass
+...
+>>> s=super(D,D())
+>>> s.__len__()
+0
+>>> len(s) #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: len() of unsized object\end{verbatim}
+\end{quote}
+
+The same problem comes with \texttt{{\_}{\_}getattr{\_}{\_}}:
+\begin{quote}
+\begin{verbatim}>>> class E(object):
+... def __getattr__(self,name):
+... if name=='__len__': return lambda:0
+...
+>>> e=E()
+>>> e.__len__()
+0
+>>> len(e) # error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: len() of unsized object\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{counting-instances}{}
+\pdfbookmark[1]{Counting instances}{counting-instances}
+\subsection*{Counting instances}
+\begin{quote}
+\begin{flushleft}
+\emph{Everything~should~be~built~top-down,~except~the~first~time.}~\\
+--~Alan~Perlis
+\end{flushleft}
+\end{quote}
+
+Multiple inheritance adds a step further to the bottom-up philosophy and
+it makes appealing the idea of creating classes with the only
+purpose of being derived. Whereas in the top-down approach one starts
+with full featured standalone classes, to be further refined, in the
+mix-in approach one starts with bare bone classes, providing very simple
+or even trivial features, with the purpose of providing
+basic reusable components in multiple inheritance hierarchies.
+At the very end, the idea is to generate a library of \emph{mixin} classes, to be
+composed with other classes. We already saw a couple of examples of
+mixin classes: 'NonInstantiable' and 'Customizable'. In this paragraph
+I will show three other examples: 'WithCounter','Singleton' and
+'AvoidDuplication'.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+\texttt{{\_}{\_}init{\_}{\_}} method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting feature in a separate mix-in class: then the feature can be
+added to the original class via multiple inheritance, without touching
+the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+\texttt{{\_}{\_}new{\_}{\_}} method of the counter class must me cooperative, and preferably
+via an anonymous super call.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~WithCounter(object):~}\\
+\mbox{~~~~"""Mixin~class~counting~the~total~number~of~its~instances~and~storing~}\\
+\mbox{~~~~~it~in~the~class~attribute~counter."""}\\
+\mbox{}\\
+\mbox{~~~~counter=0~{\#}~class~attribute~(or~static~attribute~in~C++/Java~terms)}\\
+\mbox{~}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,*args,**kw):}\\
+\mbox{~~~~~~~~cls.counter+=1~{\#}~increments~the~class~attribute}\\
+\mbox{~~~~~~~~return~super(cls.{\_}{\_}this,cls).{\_}{\_}new{\_}{\_}(cls,*args,**kw)~~}\\
+\mbox{~~~~~~~~{\#}anonymous~cooperative~call~to~the~superclass's~method~{\_}{\_}new{\_}{\_}}\\
+\mbox{}\\
+\mbox{reflective(WithCounter)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Each time an instance of 'WithCounter' is initialized, the counter 'count' is
+incremented and when 'WithCounter' is composed trough multiple inheritance,
+its '{\_}{\_}new{\_}{\_}' method cooperatively invokes the \texttt{{\_}{\_}new{\_}{\_}} method
+of the other components.
+
+For instance, I can use 'WithCounter' to implement a 'Singleton', i.e.
+a class that can have only one instance. This kind of classes can be
+obtained as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Singleton(WithCounter):}\\
+\mbox{~~~~"If~you~inherit~from~me,~you~can~only~have~one~instance"}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,*args,**kw):}\\
+\mbox{~~~~~~~~if~cls.counter==0:~{\#}first~call}\\
+\mbox{~~~~~~~~~~~~cls.instance=super(cls.{\_}{\_}this,cls).{\_}{\_}new{\_}{\_}(cls,*args,**kw)}\\
+\mbox{~~~~~~~~return~cls.instance}\\
+\mbox{}\\
+\mbox{reflective(Singleton)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+As an application, I can create a
+class \texttt{SingleClock} that inherits from \texttt{Clock}
+\emph{and} from \texttt{Singleton}. This means that \texttt{SingleClock} is both a
+'Clock' and a 'Singleton', i.e. there can be only a clock:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Clock,Singleton
+>>> class SingleClock(Clock,Singleton): pass
+...
+>>> clock1=SingleClock()
+>>> clock2=SingleClock()
+>>> clock1 is clock2
+True\end{verbatim}
+\end{quote}
+
+Instantiating many clocks is apparently possible (i.e. no error
+message is given) but you always obtain the same instance. This makes
+sense, since there is only one time on the system and a single
+clock is enough.
+
+A variation of the 'Singleton' is a class that generates a new
+instance only when a certain condition is satisfied. Suppose for instance
+one has a 'Disk' class, to be instantiated with the syntax
+\texttt{Disk(xpos,ypos,radius)}.
+It is clear that two disks with the same radius and the same position in
+the cartesian plane, are essentially the same disk (assuming there are no
+additional attributes such as the color). Therefore it is a vaste of memory
+to instantiate two separate objects to describe the same disk. To solve
+this problem, one possibility is to store in a list the calling arguments.
+When it is time to instanciate a new objects with arguments args = xpos,ypos,
+radius, Python should check if a disk with these arguments has already
+been instanciated: in this case that disk should be returned, not a new
+one. This logic can be elegantly implemented in a mix-in class such as the
+following (compare with the \texttt{withmemory} wrapper in chapter 2):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~AvoidDuplication(object):}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(cls,*args,**kw):}\\
+\mbox{~~~~~~~~return~super(cls.{\_}{\_}this,cls).{\_}{\_}new{\_}{\_}(cls,*args,**kw)~}\\
+\mbox{~~~~{\_}{\_}new{\_}{\_}=withmemory({\_}{\_}new{\_}{\_})~{\#}~collects~the~calls~in~{\_}{\_}new{\_}{\_}.result}\\
+\mbox{}\\
+\mbox{reflective(AvoidDuplication)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that 'AvoidDuplication' is introduced with the only purpose of
+giving its functionality to 'Disk': in order to reach this goal, it is enough
+to derive 'Disk' from this class and our previously
+introduced 'GeometricFigure' class by writing something like
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class Disk(GeometricFigure,AvoidDuplication):
+... def __init__(self,xpos,ypos,radius):
+... return super(Disk,self).__init__('(x-x0)**2+(y-y0)**2 <= r**2',
+... x0=xpos,y0=ypos,r=radius)\end{verbatim}
+\end{quote}
+
+Now, if we create a disk
+\begin{quote}
+\begin{verbatim}>>> c1=Disk(0,0,10) #creates a disk of radius 10\end{verbatim}
+\end{quote}
+
+it is easy enough to check that trying to instantiate a new disk with the
+\emph{same} arguments return the old disk:
+\begin{quote}
+\begin{verbatim}>>> c2=Disk(0,0,10) #returns the *same* old disk
+>>> c1 is c2
+True\end{verbatim}
+\end{quote}
+
+Here, everything works, because through the
+cooperative \texttt{super} mechanism, \texttt{Disk.{\_}{\_}init{\_}{\_}} calls
+\texttt{AvoidDuplication.{\_}{\_}init{\_}{\_}} that calls \texttt{GeometricFigure.{\_}{\_}init{\_}{\_}}
+that in turns initialize the disk. Inverting the order of
+'AvoidDuplication' and 'GeometricFigure' would case a disaster, since
+\texttt{GeometricFigure.{\_}{\_}init{\_}{\_}} would override \texttt{AvoidDuplication.{\_}{\_}init{\_}{\_}}.
+
+Alternatively, one could use the object factory 'Makeobj' implemented in
+chapter 3:
+\begin{quote}
+\begin{verbatim}>>> class NonDuplicatedFigure(GeometricFigure,AvoidDuplication): pass
+>>> makedisk=Makeobj(NonDuplicatedFigure,'(x-x0)**2/4+(y-y0)**2 <= r**2')
+>>> disk1=makedisk(x0=38,y0=7,r=5)
+>>> disk2=makedisk(x0=38,y0=7,r=5)
+>>> disk1 is disk2
+True\end{verbatim}
+\end{quote}
+
+Remark: it is interesting to notice that the previous approach would not work
+for keyword arguments, directly, since dictionary are unhashable.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-pizza-shop-example}{}
+\pdfbookmark[1]{The pizza-shop example}{the-pizza-shop-example}
+\subsection*{The pizza-shop example}
+
+Now it is time to give a non-trivial example of multiple inheritance with
+cooperative and non-cooperative classes. The point is that multiple
+inheritance can easily leads to complicated hierarchies: where the
+resolution order of methods is far from being obvious and actually
+can give bad surprises.
+
+To explain the issue, let me extend the program for the pizza-shop owner of
+chapter 4, by following the bottom-up approach and using anonymous
+cooperative super calls.
+In this approach, one starts from the simplest thing.
+It is clear that the pizza-shop owner has interest in recording all the
+pizzas he sell.
+To this aim, he needs a class providing logging capabilities:
+each time a new instance is created, its features are stored in a log file. In
+order to count the total number of instances, 'WithLogger' must derive from
+the 'WithCounter' class. In order to have a nicely printed message,
+'WithLogger' must derive from 'PrettyPrinted'. Finally,
+since 'WithLogger' must be a general purpose
+class that I will reuse in other problem as a mixin class, it must be
+cooperative. 'WithLogger' can be implemented as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~WithLogger(WithCounter,PrettyPrinted):}\\
+\mbox{~~~~"""WithLogger~inherits~from~WithCounter~the~'count'~class~attribute;~}\\
+\mbox{~~~~moreover~it~inherits~'{\_}{\_}str{\_}{\_}'~from~PrettyPrinted"""}\\
+\mbox{~~~~logfile=sys.stdout~{\#}default}\\
+\mbox{~~~~verboselog=False~{\#}default}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,*args,**kw):~}\\
+\mbox{~~~~~~~~super(self.{\_}{\_}this,self).{\_}{\_}init{\_}{\_}(*args,**kw)~{\#}~cooperative}\\
+\mbox{~~~~~~~~dic=attributes(self)~{\#}~non-special~attributes~dictionary}\\
+\mbox{~~~~~~~~print~>>~self.logfile,'*'*77}\\
+\mbox{~~~~~~~~print~>>~self.logfile,~time.asctime()}\\
+\mbox{~~~~~~~~print~>>~self.logfile,~"{\%}s.~Created~{\%}s"~{\%}~(type(self).counter,self)}\\
+\mbox{~~~~~~~~if~self.verboselog:}\\
+\mbox{~~~~~~~~~~~~print~>>~self.logfile,"with~accessibile~non-special~attributes:"}\\
+\mbox{~~~~~~~~~~~~if~not~dic:~print~>>~self.logfile,"<NOTHING>",}\\
+\mbox{~~~~~~~~~~~~else:~print~>>~self.logfile,~pretty(dic)}\\
+\mbox{}\\
+\mbox{reflective(WithLogger)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here I could well use \texttt{super(self.{\_}{\_}this,self).{\_}{\_}init{\_}{\_}(*args,**kw)}
+instead of \texttt{super(self.{\_}{\_}this,self).{\_}{\_}init{\_}{\_}(*args,**kw)}, nevertheless
+the standard \texttt{super} works in this case and I can use it with better
+performances.
+Thanks to the power of multiple inheritance, we may give logging features
+to the 'CustomizablePizza' class defined in chapter 4
+with just one line of code:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class Pizza(WithLogger,CustomizablePizza):
+... "Notice, WithLogger is before CustomizablePizza"
+>>> Pizza.With(toppinglist=['tomato'])('small')
+****************************************************************************
+Sat Feb 22 14:54:44 2003
+1. Created <Pizza>
+<__main__.Pizza object at 0x816927c>\end{verbatim}
+\end{quote}
+
+It is also possible to have a more verbose output:
+\begin{quote}
+\begin{verbatim}>>> Pizza.With(verboselog=True)
+<class '__main__.Pizza'>
+>>> Pizza('large')
+****************************************************************************
+Sat Feb 22 14:59:51 2003
+1. Created <Pizza>
+with accessibile non-special attributes:
+With = <bound method type.customized of <class '__main__.Pizza'>>
+baseprice = 1
+count = 2
+formatstring = %s
+logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+price = <bound method Pizza.price of <__main__.Pizza object at 0x402f6c8c>>
+size = large
+sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+topping_unit_price = 0.5
+toppinglist = ['tomato']
+toppings_price = <bound method Pizza.toppings_price of
+ <__main__.Pizza object at 0x402f6c8c>>
+verboselog = True
+with = <bound method Pizza.customized of
+<__main__.Pizza object at 0x402f6c8c>>
+<__main__.Pizza object at 0x401ce7ac>\end{verbatim}
+\end{quote}
+
+However, there is a problem here, since the output is '{\textless}Pizza{\textgreater}' and
+not the nice 'large pizza with tomato, cost {\$} 4.5' that we would
+expect from a child of 'CustomizablePizza'. The solution to the
+puzzle is given by the MRO:
+\begin{quote}
+\begin{verbatim}>>> Pizza.mro()
+[<class '__main__.Pizza'>, <class 'oopp.WithLogger'>,
+ <class 'oopp.WithCounter'>, <class 'oopp.PrettyPrinted'>,
+ <class 'oopp.CustomizablePizza'>, <class 'oopp.GenericPizza'>,
+ <class 'oopp.Customizable'>, <type 'object'>]\end{verbatim}
+\end{quote}
+
+The inheritance graph is rather complicated:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~object~~7}\\
+\mbox{}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~/~~~~~/~~~{\textbackslash}~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~/~~~~~~/~~~~~{\textbackslash}~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~/~~~~~~~/~~~~~~~{\textbackslash}~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~/~~~~~~~~/~~~~~~~~~{\textbackslash}~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~/~~~~~~~~~/~~~~~~~~~~~{\textbackslash}~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~/~~~~~~~~~~/~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~/~~~~~~~~~~~/~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~/~~~~~~~~~~~~/~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~/~~~~~~~~~~~~~/~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~{\textbackslash}}\\
+\mbox{2~~WithCounter~~~PrettyPrinted~3~~~~GenericPizza~5~~Customizable~6}\\
+\mbox{~~({\_}{\_}new{\_}{\_})~~~~({\_}{\_}str{\_}{\_},{\_}{\_}init{\_}{\_})~~~~~~({\_}{\_}str{\_}{\_})~~~~~~~/~~~~~~~~~~~~~~}\\
+\mbox{~~~~~~{\textbackslash}~~~~~~~~~~~~/~~~~~~~~~~~~~~~~~~~~~~~/~~~~~~~~/~~~~}\\
+\mbox{~~~~~~~{\textbackslash}~~~~~~~~~~/~~~~~~~~~~~~~~~~~~~~~~~/~~~~~~/~~~~~~~~~}\\
+\mbox{~~~~~~~~{\textbackslash}~~~~~~~~/~~~~~~~~~~~~~~~~~~~~~~~/~~~~/}\\
+\mbox{~~~~~~~~~{\textbackslash}~~~~~~/~~~~~~~~~~~~~~~~~~~~~~~/~~/}\\
+\mbox{~~~~~~~~~~{\textbackslash}~~~~/~~~~~~~~~~~~CustomizablePizza~~4}\\
+\mbox{~~~~~~~~~~~{\textbackslash}~~/~~~~~~~~~~~~~~~~~~~~~~/~~~~~~~}\\
+\mbox{~~~1~~~~~WithLogger~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~({\_}{\_}init{\_}{\_})~~~~~~~~~~~~~~/~~~~~~~~}\\
+\mbox{~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~/}\\
+\mbox{}\\
+\mbox{~~~~~~~~~~~~~~~~~~Pizza~~O}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+As we see, the precedence in the resolution of methods is far from being
+trivial. It is denoted in the graph with numbers
+from 0 to 7: first the methods of 'Pizza' (level 0), then the methods of
+'WithLogger' (level 1), then the methods of 'WithCounter' (level 2), then
+the methods of 'PrettyPrinted' (level 3), then the methods of
+'CustomizablePizza' (level 4), then the methods of 'GenericPizza' (level 5),
+then the level of 'Customizable' (level 6), finally the 'object' methods
+(level 7).
+
+The reason why the MRO is so, can be understood by studying
+appendix 1.
+
+We see that the \texttt{{\_}{\_}init{\_}{\_}} methods of 'WithLogger' and
+the \texttt{{\_}{\_}new{\_}{\_}} method of 'WithCounter' are cooperative.
+\texttt{WithLogger.{\_}{\_}init{\_}{\_}}
+calls \texttt{WithCounter.{\_}{\_}init{\_}{\_}} that is
+inherited from \texttt{CustomizablePizza.{\_}{\_}init{\_}{\_}} which is not cooperative,
+but this is not dangerous since \texttt{CustomizablePizza.{\_}{\_}init{\_}{\_}} does not need
+to call any other \texttt{{\_}{\_}init{\_}{\_}}.
+
+However, \texttt{PrettyPrinted.{\_}{\_}str{\_}{\_}} and \texttt{GenericPizza.{\_}{\_}str{\_}{\_}} are not
+cooperative and since 'PrettyPrinted' precedes 'GenericPizza', the
+\texttt{GenericPizza.{\_}{\_}str{\_}{\_}} method is overridden, which is bad.
+
+If \texttt{WithLogger.{\_}{\_}init{\_}{\_}} and \texttt{WithCounter.{\_}{\_}new{\_}{\_}} were not
+cooperative, they would therefore badly breaking the program.
+
+The message is: when you inherit from both cooperative and non-cooperative
+classes, put cooperative classes first. The will be fair and will not
+blindly override methods of the non-cooperative classes.
+
+With multiple inheritance you can reuse old code a lot,
+however the price to pay, is to have a non-trivial hierarchy. If from
+the beginning we knew that 'Pizza' was needing a 'WithLogger',
+a 'WithCounter' and the
+ability to be 'Customizable' we could have put everything in an unique
+class. The problem is that in real life one never knows ;)
+Fortunately, Python dynamism allows to correct design mistakes
+
+Remark: in all text books about inheritance, the authors always stress
+that inheritance should be used as a ''is-a`` relation, not
+and ''has-a`` relation. In spite of this fact, I have decided to implement
+the concept of having a logger (or a counter) via a mixin class. One
+should not blindly believe text books ;)
+
+
+%___________________________________________________________________________
+
+\hypertarget{fixing-wrong-hierarchies}{}
+\pdfbookmark[1]{Fixing wrong hierarchies}{fixing-wrong-hierarchies}
+\subsection*{Fixing wrong hierarchies}
+
+A typical metaprogramming technique, is the run-time modification of classes.
+As I said in a previous chapter, this feature can confuse the programmer and
+should not be abused (in particular it should not be used as a replacement
+of inheritance!); nevertheless, there applications where the ability of
+modifying classes at run time is invaluable: for instance,
+it can be used to correct design mistakes.
+
+In this case we would like the \texttt{{\_}{\_}str{\_}{\_} method} of 'PrettyPrinted' to be
+overridden by \texttt{GenericPizza.{\_}{\_}str{\_}{\_}}. Naively, this can be solved by
+putting 'WithLogger' after 'GenericPizza'. Unfortunately, doing so
+would cause \texttt{GenericPizza.{\_}{\_}init{\_}{\_}} to override \texttt{WithLogger.{\_}{\_}init{\_}{\_}},
+therefore by loosing logging capabilitiesr, unless countermeasures
+are taken.
+
+A valid countermeasure could be to replace the non-cooperative
+\texttt{GenericPizza.{\_}{\_}init{\_}{\_}} with a cooperative one. This can miraculously
+done at run time in few lines of code:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~coop{\_}init(self,size):~{\#}~cooperative~{\_}{\_}init{\_}{\_}~for~GenericPizza}\\
+\mbox{~~~~self.size=size}\\
+\mbox{~~~~super(self.{\_}GenericPizza{\_}{\_}this,self).{\_}{\_}init{\_}{\_}(size)}\\
+\mbox{}\\
+\mbox{GenericPizza.{\_}{\_}init{\_}{\_}=coop{\_}init~{\#}~replace~the~old~{\_}{\_}init{\_}{\_}}\\
+\mbox{}\\
+\mbox{reflective(GenericPizza)~{\#}~define~GenericPizza.{\_}{\_}this}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice the usage of the fully qualified private attribute
+\texttt{self.{\_}GenericPizza{\_}{\_}this} inside \texttt{coop{\_}init}: since this function
+is defined outside any class, the automatica mangling mechanism cannot
+work and has to be implemented by hand. Notice also that
+\texttt{super(self.{\_}GenericPizza{\_}{\_}this,self)} could be replaced by
+\texttt{super(GenericPizza,self)}; however the simpler approach is
+less safe against possible future manipulations of the hierarchy.
+Suppose, for example, we want to create a copy of the hierarchy
+with the same name but slightly different features (actually,
+in chapter 8 we will implement a traced copy of the pizza hierarchy,
+useful for debugging purposes): then, using \texttt{super(GenericPizza,self)}
+would raise an error, since self would be an instance of the traced
+hierarchy and \texttt{GenericPizza} the original nontraced class. Using
+the form \texttt{super(self.{\_}GenericPizza{\_}{\_}this,self)} and making
+\texttt{self.{\_}GenericPizza{\_}{\_}this} pointing to the traced 'GenericPizza'
+class (actually this will happen automatically) the problems goes
+away.
+
+Now everything works if 'WithLogger' is put after 'CustomizablePizza'
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class PizzaWithLog(CustomizablePizza,WithLogger): pass
+>>> PizzaWithLog.With(toppinglist=['tomato'])('large')
+****************************************************************************
+Sun Apr 13 16:19:12 2003
+1. Created large pizza with tomato, cost $ 4.5
+<class '__main__.PizzaWithLog'>\end{verbatim}
+\end{quote}
+
+The log correctly says \texttt{Created large pizza with tomato, cost {\$} 4.5} and not
+\texttt{Created <Pizza>} as before since now \texttt{GenericPizza.{\_}{\_}str{\_}{\_}}
+overrides \texttt{PrettyPrinted.{\_}{\_}str{\_}{\_}}. Moreover, the hierarchy is logically
+better organized:
+\begin{quote}
+\begin{verbatim}>>> PizzaWithLog.mro()
+[<class '__main__.PizzaWithLog'>, <class 'oopp.CustomizablePizza'>,
+<class 'oopp.GenericPizza'>, <class 'oopp.Customizable'>,
+<class 'oopp.WithLogger'>, <class 'oopp.WithCounter'>,
+<class 'oopp.PrettyPrinted'>, <type 'object'>]\end{verbatim}
+\end{quote}
+
+I leave as an exercise for the reader to make the \texttt{{\_}{\_}str{\_}{\_}} methods
+cooperative ;)
+
+Obviously, in this example it would have been better to correct the
+original hierarchy, by leaving 'Beautiful' instantiable from the beginning
+(that's why I said the 'Beautiful' is an example of wrong mix-in class):
+nevertheless, sometimes, one has do to with wrong hierarchies written by
+others, and it can be a pain to fix them, both directly by modifying the
+original source code, and indirectly
+by inheritance, since one must change all the names, in order to distinghish
+the original classes from the fixed ones. In those cases Python
+dynamism can save your life. This also allows you enhance original
+classes which are not wrong, but that simply don't do something you want
+to implement.
+
+Modifying classes at run-time can be trivial, as in the examples I have
+shown here, but can also be rather tricky, as in this example
+\begin{quote}
+\begin{verbatim}>>> from oopp import PrettyPrinted
+>>> class PrettyPrintedWouldBe(object): __str__ = PrettyPrinted.__str__
+>>> print PrettyPrintedWouldBe() #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: unbound method __str__() must be called with PrettyPrinted
+instance as first argument (got nothing instead)\end{verbatim}
+\end{quote}
+
+As the error message says, the problem here, is that the
+\texttt{PrettyPrinted.{\_}{\_}str{\_}{\_}} unbound method, has not received any argument.
+This is because in this
+form \texttt{PrettyPrintedWouldBe.{\_}{\_}str{\_}{\_}} has been defined as an attribute,
+not as a real method. The solution is to write
+\begin{quote}
+\begin{verbatim}>>> class PrettyPrintedWouldBe(object):
+... __str__ = PrettyPrinted.__dict__['__str__']
+...
+>>> print PrettyPrintedWouldBe() # now it works
+<PrettyPrintedWouldBe>\end{verbatim}
+\end{quote}
+
+This kind of run-time modifications does not work when private variables
+are involved:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<changewithprivate.py>}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~{\_}{\_}x='C.{\_}{\_}init{\_}{\_}'}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):~}\\
+\mbox{~~~~~~~~print~self.{\_}{\_}x~{\#}~okay}\\
+\mbox{}\\
+\mbox{class~D(object):}\\
+\mbox{~~~~{\_}{\_}x='D.{\_}{\_}init{\_}{\_}'}\\
+\mbox{~~~~{\_}{\_}init{\_}{\_}=C.{\_}{\_}dict{\_}{\_}['{\_}{\_}init{\_}{\_}']~{\#}~error}\\
+\mbox{}\\
+\mbox{class~New:}\\
+\mbox{~~~~class~C(object):}\\
+\mbox{~~~~~~~~{\_}{\_}x='New.C.{\_}{\_}init{\_}{\_}'}\\
+\mbox{~~~~~~~~{\_}{\_}init{\_}{\_}=C.{\_}{\_}dict{\_}{\_}['{\_}{\_}init{\_}{\_}']~{\#}~okay}\\
+\mbox{}\\
+\mbox{C()}\\
+\mbox{try:~D()}\\
+\mbox{except~AttributeError,e:~print~e}\\
+\mbox{}\\
+\mbox{{\#}</changewithprivate.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Gives as result
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{C.{\_}{\_}init{\_}{\_}}\\
+\mbox{'D'~object~has~no~attribute~'{\_}C{\_}{\_}x'}\\
+\mbox{New.C.{\_}{\_}init{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The problem is that when \texttt{C.{\_}{\_}dict{\_}{\_}['{\_}{\_}init{\_}{\_}']} is compiled
+(to byte-code) \texttt{self.{\_}{\_}x} is expanded to \texttt{self.{\_}C{\_}{\_}x}. However,
+when one invokes \texttt{D.{\_}{\_}init{\_}{\_}}, a D-object is passed, which has
+a \texttt{self.{\_}D{\_}{\_}x} attribute, but not a \texttt{self.{\_}C{\_}{\_}x} attribute (unless
+'D' is a subclass of 'C'. Fortunately, Python wisdom
+\begin{quote}
+
+\emph{Namespaces are one honking great idea -- let's do more of those!}
+\end{quote}
+
+suggests the right solution: to use a new class with the \emph{same name}
+of the old one, but in a different namespace, in order to avoid
+confusion. The simplest way to generate a new namespace is to
+declare a new class (the class 'New' in this example): then 'New.C'
+becomes an inner class of 'New'. Since it has the same name of the
+original class, private variables are correctly expanded and one
+can freely exchange methods from 'C' to 'New.C' (and viceversa, too).
+
+
+%___________________________________________________________________________
+
+\hypertarget{modifying-hierarchies}{}
+\pdfbookmark[1]{Modifying hierarchies}{modifying-hierarchies}
+\subsection*{Modifying hierarchies}
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~mod(cls):~return~cls}\\
+\mbox{}\\
+\mbox{class~New:~pass}\\
+\mbox{}\\
+\mbox{for~c~in~HomoSapiensSapiens.{\_}{\_}mro{\_}{\_}:}\\
+\mbox{~~~~setattr(New,c.{\_}{\_}name{\_}{\_},mod(c))}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{inspecting-python-code}{}
+\pdfbookmark[1]{Inspecting Python code}{inspecting-python-code}
+\subsection*{Inspecting Python code}
+
+how to inspect a class, by retrieving useful informations about its
+information.
+
+A first possibility is to use the standard \texttt{help} function.
+The problem of this approach is that \texttt{help} gives too much
+information.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{{\#}plaindata=}\\
+\mbox{plainmethod=lambda~m:m~{\#}identity~function}\\
+\mbox{}\\
+\mbox{class~Get(object):}\\
+\mbox{~~~~"""Invoked~as~Get(cls)(xxx)~where~xxx~=~staticmethod,~classmethod,}\\
+\mbox{~~~~property,~plainmethod,~plaindata,~returns~the~corresponding~}\\
+\mbox{~~~~attributes~as~a~keyword~dictionary.~It~works~by~internally~calling~}\\
+\mbox{~~~~the~routine~inspect.classify{\_}class{\_}attrs.~Notice~that~data}\\
+\mbox{~~~~attributes~with~double~underscores~are~not~retrieved~}\\
+\mbox{~~~~(this~is~by~design)."""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,cls):}\\
+\mbox{~~~~~~~~self.staticmethods=kwdict()}\\
+\mbox{~~~~~~~~self.classmethods=kwdict()}\\
+\mbox{~~~~~~~~self.properties=kwdict()}\\
+\mbox{~~~~~~~~self.methods=kwdict()}\\
+\mbox{~~~~~~~~self.data=kwdict()}\\
+\mbox{~~~~~~~~for~name,~kind,~klass,~attr~in~inspect.classify{\_}class{\_}attrs(cls):}\\
+\mbox{~~~~~~~~~~~~if~kind=='static~method':}\\
+\mbox{~~~~~~~~~~~~~~~~self.staticmethods[name]=attr}\\
+\mbox{~~~~~~~~~~~~elif~kind=='class~method':}\\
+\mbox{~~~~~~~~~~~~~~~~self.classmethods[name]=attr}\\
+\mbox{~~~~~~~~~~~~elif~kind=='property':}\\
+\mbox{~~~~~~~~~~~~~~~~self.properties[name]=attr}\\
+\mbox{~~~~~~~~~~~~elif~kind=='method':}\\
+\mbox{~~~~~~~~~~~~~~~~self.methods[name]=attr}\\
+\mbox{~~~~~~~~~~~~elif~kind=='data':}\\
+\mbox{~~~~~~~~~~~~~~~if~not~special(name):~self.data[name]=attr}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,descr):~{\#}could~be~done~with~a~dict}\\
+\mbox{~~~~~~~~if~descr==staticmethod:~return~self.staticmethods~}\\
+\mbox{~~~~~~~~elif~descr==classmethod:~return~self.classmethods}\\
+\mbox{~~~~~~~~elif~descr==property:~return~self.properties~}\\
+\mbox{~~~~~~~~elif~descr==plainmethod:~return~self.methods}\\
+\mbox{~~~~~~~~elif~descr==plaindata:~return~self.data}\\
+\mbox{~~~~~~~~else:~raise~SystemExit("Invalid~descriptor")}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With similar tricks one can automatically recognize cooperative methods:
+{\#}it is different, (better NOT to use descriptors)
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{{\#}class~Cooperative(Class):}\\
+\mbox{{\#}~~~~{\_}{\_}metaclass{\_}{\_}~=~WithWrappingCapabilities}\\
+\mbox{{\#}}\\
+\mbox{{\#}~~~~def~cooperative(method):}\\
+\mbox{{\#}~~~~~~~~~"""Calls~both~the~superclass~method~and~the~class}\\
+\mbox{{\#}~~~~~~~~~method~(if~the~class~has~an~explicit~method).~}\\
+\mbox{{\#}~~~~~~~~~Works~for~methods~returning~None."""}\\
+\mbox{{\#}~~~~~~~~~name,cls=Cooperative.parameters~{\#}~fixed~by~the~meta-metaclass}\\
+\mbox{{\#}~~~~~~~~~def~{\_}(*args,**kw):}\\
+\mbox{{\#}~~~~~~~~~~~~getattr(super(cls,args[0]),name)(*args[1:],**kw)~}\\
+\mbox{{\#}~~~~~~~~~~~~if~method:~method(*args,**kw)~{\#}~call~it}\\
+\mbox{{\#}~~~~~~~~~return~{\_}}\\
+\mbox{{\#}~~~~}\\
+\mbox{{\#}~~~~cooperative=staticmethod(cooperative)}\\
+\mbox{}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~wrapH(cls):}\\
+\mbox{~~~~for~c~in~cls.{\_}{\_}mro{\_}{\_}[:-2]:}\\
+\mbox{~~~~~~~~tracer.namespace=c.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~new=vars(c).get('{\_}{\_}new{\_}{\_}',None)}\\
+\mbox{~~~~~~~~if~new:~c.{\_}{\_}new{\_}{\_}=tracedmethod(new)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-magic-of-metaclasses-part-i}{}
+\pdfbookmark[0]{THE MAGIC OF METACLASSES - PART I}{the-magic-of-metaclasses-part-i}
+\section*{THE MAGIC OF METACLASSES - PART I}
+\begin{quote}
+\begin{flushleft}
+\emph{Metaclasses~are~deeper~magic~than~99{\%}~of~users~should~ever~\\
+worry~about.~~If~you~wonder~whether~you~need~them,~you~don't~\\
+(the~people~who~actually~need~them~know~with~certainty~that~\\
+they~need~them,~and~don't~need~an~explanation~about~why).}~\\
+--Tim~Peters
+\end{flushleft}
+\end{quote}
+
+Python always had metaclasses, since they are inherent to its object
+model. However, before Python 2.2, metaclasses where tricky and their
+study could cause the programmer's brain to explode [\hyperlink{id42}{21}]. Nowadays,
+the situation has changed, and the reader should be able to understand
+this chapter without risk for his/her brain (however I do not give any
+warranty ;)
+
+Put it shortly, metaclasses give to the Python programmer
+complete control on the creation of classes. This simple statement
+has far reaching consequences, since the ability of interfering with
+the process of class creation, enable the programmer to make miracles.
+
+In this and in the following chapters, I will show some of these
+miracles.
+
+This chapter will focus on subtle problems of metaclasses in inheritance
+and multiple inheritance, including multiple inheritance of metaclasses
+with classes and metaclasses with metaclasses.
+
+The next chapter will focus more on applications.
+\begin{figure}[b]\hypertarget{id45}[23]
+Metaclasses in Python 1.5 [A.k.a the killer joke]
+\href{http://www.python.org/doc/essays/metaclasses/}{http://www.python.org/doc/essays/metaclasses/}
+\end{figure}
+
+There is very little documentation about metaclasses, except Guido's
+essays and the papers by David Mertz and myself published in IBMdeveloperWorks
+\begin{quote}
+
+\href{http://www-106.ibm.com/developerworks/library/l-pymeta.html}{http://www-106.ibm.com/developerworks/library/l-pymeta.html}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclasses-as-class-factories}{}
+\pdfbookmark[1]{Metaclasses as class factories}{metaclasses-as-class-factories}
+\subsection*{Metaclasses as class factories}
+
+In the Python object model (inspired from the Smalltalk, that had metaclasses
+a quarter of century ago!) classes themselves are objects.
+Now, since objects are instances of classes, that means that classes
+themselves can be seen as instances of special classes called \emph{metaclasses}.
+Notice that things get hairy soon, since by following this idea, one could
+say the metaclasses themselves are classes and therefore objects; that
+would mean than even metaclasses can be seen as
+instances of special classes called meta-metaclasses. On the other hand,
+meta-meta-classes can be seen as instances of meta-meta-metaclasses,
+etc. Now, it should be obvious why metaclasses have gained such a
+reputation of brain-exploders ;). However, fortunately, the situation
+is not so bad in practice, since the infinite recursion of metaclasses is
+avoided because there is a metaclass that is the ''mother of all metaclasses``:
+the built-in metaclass \emph{type}. 'type' has the property of being its own
+metaclass, therefore the recursion stops. Consider for instance the following
+example:
+\begin{quote}
+\begin{verbatim}>>> class C(object): pass # a generic class
+>>> type(C) #gives the metaclass of C
+<type 'type'>
+>>> type(type(C)) #gives the metaclass of type
+<type 'type'>\end{verbatim}
+\end{quote}
+
+The recursion stops, since the metaclass of 'type' is 'type'.
+One cool consequence of classes being instances of 'type',
+is that since \emph{type} is a subclass of object,
+\begin{quote}
+\begin{verbatim}>>> issubclass(type,object)
+True \end{verbatim}
+\end{quote}
+
+any Python class is not only a subclass of \texttt{object}, but also
+an instance of 'object':
+\begin{quote}
+\begin{verbatim}>>> isinstance(C,type)
+True
+>>> isinstance(C,object)
+True
+>>> issubclass(C,object)
+True\end{verbatim}
+\end{quote}
+
+Notice that 'type' is an instance of itself (!) and therefore of 'object':
+\begin{quote}
+\begin{verbatim}>>> isinstance(type,type) # 'type' is an instance of 'type'
+True
+>>> isinstance(type,object) # therefore 'type' is an instance of 'object'
+True\end{verbatim}
+\end{quote}
+
+As it is well known, \texttt{type(X)} returns the type of \texttt{X}; however,
+\texttt{type} has also a second form in which it acts as a class factory.
+The form is \texttt{type(name,bases,dic)} where \texttt{name} is the name of
+the new class to be created, bases is the tuple of its bases and dic
+is the class dictionary. Let me give a few examples:
+\begin{quote}
+\begin{verbatim}>>> C=type('C',(),{})
+>>> C
+<class '__main__.C'>
+>>> C.__name__
+'C'
+>>> C.__bases__
+(<type 'object'>,)
+>>> C.__dict__
+<dict-proxy object at 0x8109054>\end{verbatim}
+\end{quote}
+
+Notice that since all metaclasses inherits from \texttt{type}, as a consequences
+all metaclasses can be used as class factories.
+
+A fairy tale example will help in understanding the concept
+and few subtle points on how attributes are transmitted from metaclasses
+to their instances.
+
+Let me start by defining a 'Nobility' metaclass :
+\begin{quote}
+\begin{verbatim}>>> class Nobility(type): attributes="Power,Richness,Beauty"\end{verbatim}
+\end{quote}
+
+instances of 'Nobility' are classes such 'Princes', 'Dukes', 'Barons', etc.
+\begin{quote}
+\begin{verbatim}>>> Prince=Nobility("Prince",(),{})\end{verbatim}
+\end{quote}
+
+Instances of 'Nobility' inherits its attributes, just as instances of normal
+classes inherits the class docstring:
+\begin{quote}
+\begin{verbatim}>>> Prince.attributes
+'Power,Richness,Beauty'\end{verbatim}
+\end{quote}
+
+Nevertheless, 'attributes' will not be retrieved by the \texttt{dir} function:
+\begin{quote}
+\begin{verbatim}>>> print dir(Prince)
+['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__weakref__']\end{verbatim}
+\end{quote}
+
+However, this is a limitation of \texttt{dir}, in reality \texttt{Prince.attributes}
+is there. On the other hand, the situation is different for a specific
+'Prince' object
+\begin{quote}
+\begin{verbatim}>>> charles=Prince()
+>>> charles.attributes #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'Prince' object has no attribute 'attributes'\end{verbatim}
+\end{quote}
+
+The transmission of metaclass attributes is not transitive:
+instances of the metaclass inherits the attributes, but not the instances
+of the instances. This behavior is by design and is needed in order to avoid
+troubles with special methods. This point will be throughly
+explained in the last paragraph. For the moment, I my notice that the
+behaviour is reasonable, since the abstract qualities 'Power,Richness,Beauty'
+are more qualities of the 'Prince' class than of one specific representative.
+They can always be retrieved via the \texttt{{\_}{\_}class{\_}{\_}} attribute:
+\begin{quote}
+\begin{verbatim}>>> charles.__class__.attributes
+'Power,Richness,Beauty'\end{verbatim}
+\end{quote}
+
+Le me now define a metaclass 'Froggyness':
+\begin{quote}
+\begin{verbatim}>>> class Frogginess(type): attributes="Powerlessness,Poverty,Uglyness"\end{verbatim}
+\end{quote}
+
+Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.
+\begin{quote}
+\begin{verbatim}>>> Frog=Frogginess("Frog",(),{})
+>>> Frog.attributes
+'Powerlessness,Poverty,Uglyness'\end{verbatim}
+\end{quote}
+
+However, in Python miracles can happen:
+\begin{quote}
+\begin{verbatim}>>> def miracle(Frog): Frog.__class__=Nobility
+>>> miracle(Frog); Frog.attributes
+'Powerlessness,Richness,Beauty'\end{verbatim}
+\end{quote}
+
+In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.
+
+However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:
+\begin{quote}
+\begin{verbatim}>>> Frog.attributes="poor, small, ugly"
+>>> jack=Frog(); jack.attributes
+'poor, small, ugly'\end{verbatim}
+\end{quote}
+
+Then the miracle cannot work:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<fairytale2.py>}\\
+\mbox{}\\
+\mbox{class~Nobility(type):~attributes="Power,~Richness,~Beauty"}\\
+\mbox{Prince=Nobility("Prince",(),{\{}{\}})}\\
+\mbox{charles=Prince()}\\
+\mbox{}\\
+\mbox{class~Frogginess(type):~attributes="Inpuissance,~Poverty,~Uglyness"}\\
+\mbox{Frog=Frogginess("Frog",(),{\{}{\}})}\\
+\mbox{Frog.attributes="poor,~small,~ugly"}\\
+\mbox{jack=Frog()}\\
+\mbox{}\\
+\mbox{def~miracle(Frog):~Frog.{\_}{\_}class{\_}{\_}=Nobility}\\
+\mbox{}\\
+\mbox{miracle(Frog)}\\
+\mbox{}\\
+\mbox{print~"I~am",Frog.attributes,"even~if~my~class~is",Frog.{\_}{\_}class{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}</fairytale2.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{I~am~poor,~small,~ugly~even~if~my~class~is~<class~'{\_}{\_}main{\_}{\_}.Nobility'>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is \texttt{poor, small, ugly}. If you think a bit, it makes sense.
+
+Remark:
+
+In Python 2.3 there are restrictions when changing the \texttt{{\_}{\_}class{\_}{\_}}
+attribute for classes:
+\begin{quote}
+\begin{verbatim}>>> C=type('C',(),{})
+>>> C.__class__ = Nobility #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: __class__ assignment: only for heap types\end{verbatim}
+\end{quote}
+
+Here changing \texttt{C.{\_}{\_}class{\_}{\_}} is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:
+\begin{quote}
+\begin{verbatim}>>> print bool.__doc__ # in Python 2.2 would give an error
+bool(x) -> bool
+Returns True when the argument x is true, False otherwise.
+The builtins True and False are the only two instances of the class bool.
+The class bool is a subclass of the class int, and cannot be subclassed.\end{verbatim}
+\end{quote}
+
+In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:
+\begin{quote}
+\begin{verbatim}>>> class M1(type): f=lambda cls: 'M1.f' #metaclass1
+>>> class M2(type): f=lambda cls: 'M2.f' #metaclass2
+>>> B=M1('B',(),{}) # B receives M1.f
+>>> class C(B): pass #C receives M1.f
+>>> B.f()
+'M1.f'
+B.__class__=M2 #change the metaclass
+>>> B.f() #B receives M2.f
+'M2.f'
+C.f() #however C does *not* receive M2.f
+>>> C.f()
+'M1.f'
+>>> type(B)
+<class '__main__.M2'>
+>>> type(C)
+<class '__main__.M1'>\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclasses-as-class-modifiers}{}
+\pdfbookmark[1]{Metaclasses as class modifiers}{metaclasses-as-class-modifiers}
+\subsection*{Metaclasses as class modifiers}
+
+The interpretation of metaclasses in terms of class factories is quite
+straightforward and I am sure that any Pythonista will be at home
+with the concept. However, metaclasses have such a reputation of black
+magic since their typical usage is \emph{not} as class factories, but as
+\emph{class modifiers}. This means that metaclasses are typically
+used to modify \emph{in fieri} classes. The trouble is that the
+modification can be utterly magical.
+Here there is another fairy tale example showing the syntax
+(via the \texttt{{\_}{\_}metaclass{\_}{\_}} hook) and the magic of the game:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~UglyDuckling(PrettyPrinted):}\\
+\mbox{~~~~"A~plain,~regular~class"}\\
+\mbox{~~~~formatstring="Not~beautiful,~I~am~{\%}s"}\\
+\mbox{}\\
+\mbox{class~MagicallyTransformed(type):}\\
+\mbox{~~~~"Metaclass~changing~the~formatstring~of~its~instances"}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~cls.formatstring="Very~beautiful,~since~I~am~{\%}s"}\\
+\mbox{~~~~~~~~}\\
+\mbox{class~TransformedUglyDuckling(PrettyPrinted):}\\
+\mbox{~~~~"A~class~metamagically~modified"}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}~=~MagicallyTransformed}\\
+\mbox{~~~~formatstring="Not~beautiful,~I~am~{\%}s"~{\#}~will~be~changed}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~from~oopp~import~*}\\
+\mbox{>>>~print~UglyDuckling()}\\
+\mbox{Not~beautiful,~I~am~<UglyDuckling>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this example, even if in 'TransformedUglyDuckling' we explicitely
+set the formatstring to ''Not beautiful, I am {\%}s``, the metaclass changes
+it to ''Very beautiful, even if I am {\%}s`` and thus
+\begin{quote}
+\begin{verbatim}>>> print TransformedUglyDuckling() # gives
+Very beautiful, since I am <TransformedUglyDuckling>\end{verbatim}
+\end{quote}
+
+Notice that the \texttt{{\_}{\_}metaclass{\_}{\_}} hook passes to the metaclass
+\texttt{MagicallyTransformed} the name, bases and dictionary of the class
+being created, i.e. 'TransformedUglyDucking'.
+
+Metaclasses, when used as class modifiers, act \emph{differently}
+from functions, when inheritance is
+involved. To clarify this subtle point, consider a subclass 'Swan'
+of 'UglyDuckling':
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class Swan(UglyDuckling):
+... formatstring="Very beautiful, I am %s"
+>>> print Swan()
+Very beautiful, I am <Swan>\end{verbatim}
+\end{quote}
+
+Now, let me define a simple function acting as a class modifier:
+\begin{quote}
+\begin{verbatim}>>> def magicallyTransform(cls):
+... "Modifies the class formatstring"
+... customize(cls,formatstring="Very beautiful, even if I am %s")
+... return cls\end{verbatim}
+\end{quote}
+
+The function works:
+\begin{quote}
+\begin{verbatim}>>> magicallyTransform(UglyDuckling)
+>>> print UglyDuckling()
+Very beautiful, even if I am <UglyDuckling>\end{verbatim}
+\end{quote}
+
+This approach is destructive, since we cannot have the original
+and the transformed class at the same time, and has potentially bad side
+effects in the derived classes. Nevertheless, in this case it works
+and it is not dangereous for the derived class 'Swan', since 'Swan'
+explicitly overrides the 'formatstring' attribute and doesn't care about
+the change in 'UglyDuckling.formatstring'. Therefore the output
+of
+\begin{quote}
+\begin{verbatim}>>> print Swan()
+Very beautiful, I am <Swan>\end{verbatim}
+\end{quote}
+
+is still the same as before the action of the function \texttt{magicallyTransform}.
+The situation is quite different if we use the 'MagicallyTransformed'
+metaclass:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class Swan(TransformedUglyDuckling):
+... formatstring="Very beautiful, I am %s"\end{verbatim}
+\begin{verbatim}>>> print TransformedUglyDuckling()
+Very beautiful, since I am <UglyDuckling>
+>>> print Swan() # does *not* print "Very beautiful, I am <Swan>"
+Very beautiful, since I am <Swan> \end{verbatim}
+\end{quote}
+
+Therefore, not only the metaclass has magically transformed the
+'TransformedUglyDuckling.formatstring', it has also transformed the
+'Swan.formatstring'! And that, despite the fact that
+'Swan.formatstring' is explicitly set.
+
+The reason for this behaviour is that since 'UglyDuckling' is a base
+class with metaclass 'MagicallyTransformed', and since 'Swan' inherits from
+'UglyDuckling', then 'Swan' inherits the metaclass 'MagicallyTransformed',
+which is automatically called at 'Swan' creation time.
+That's the reason why metaclasses are much more magical and much
+more dangerous than
+functions: functions do not override attributes in the derived classes,
+metaclasses do, since they are automagically called at the time of
+creation of the subclass. In other words, functions are explicit,
+metaclasses are implicit. Nevertheless, this behavior can be pretty
+useful in many circumstances, and it is a feature, not a bug. In the
+situations where this behavior is not intended, one should use a function,
+not a metaclass. In general, metaclasses are better than functions,
+since metaclasses are classes and as such they can inherit one from each
+other. This means that one can improve a basic metaclass trough
+(multiple) inheritance, with \emph{reuse} of code.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-few-caveats-about-the-usage-of-metaclasses}{}
+\pdfbookmark[1]{A few caveats about the usage of metaclasses}{a-few-caveats-about-the-usage-of-metaclasses}
+\subsection*{A few caveats about the usage of metaclasses}
+
+Let me start with some caveats about the \texttt{{\_}{\_}metaclass{\_}{\_}} hook, which
+commonly used and quite powerful, but also quite dangereous.
+
+Let's imagine a programmer not
+knowing about metaclasses and looking at the 'TransformedUglyDuckling'
+code (assuming there are no comments): she would probably think
+that ''{\_}{\_}metaclass{\_}{\_}`` is some special attribute used for introspection
+purposes only, with no other effects, and she would probably expect
+the output of the script to be ''Not much, I am the class
+TransformedUglyDucking`` whereas it is exacly the contrary! In other
+words, when metaclasses are involved, \emph{what you see, is not what you get}.
+The situation is even more implicit when the metaclass is inherited
+from some base class, therefore lacking also the visual clue of the hook.
+
+For these reasons, metaclasses are something to be used with great care;
+they can easily make your code unreadable and confuse inexpert programmers.
+Moreover, it is more difficult to debug programs involving metaclasses, since
+methods are magically transformed by routines defined in the metaclass,
+and the code you see in the class is \emph{not} what Python sees. I think
+the least confusing way of using metaclasses, is to concentrate all
+the dynamics on them and to write empty classes except for the
+metaclass hook. If you write a class with no methods such as
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~TransformedUglyDuckling(object):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=MagicallyTransformed}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+then the only place to look at, is the metaclass. I have found extremely
+confusing to have some of the methods defined in the class and some in
+the metaclass, especially during debugging.
+
+Another point to make, is that the \texttt{{\_}{\_}metaclass{\_}{\_}}
+hook should not be used to modify pre-existing classes,
+since it requires modifying the source code (even if it is enough to
+change one line only). Moreover, it is confusing, since adding a
+\texttt{{\_}{\_}metaclass{\_}{\_}} attribute \emph{after} the class creation would not do the job:
+\begin{quote}
+\begin{verbatim}>>> from oopp import UglyDuckling, MagicallyTransformed
+>>> UglyDuckling.__metaclass__=MagicallyTransformed
+>>> print UglyDuckling()
+"Not much, I am the class UglyDuckling"\end{verbatim}
+\end{quote}
+
+The reason is that we have to think of UglyDuckling as an instance of
+\texttt{type}, the built-in metaclasses; merely adding a \texttt{{\_}{\_}metaclass{\_}{\_}}
+attribute does not re-initialize the class.
+The problem is elegantly solved by avoiding the hook and creating
+an enhanced copy of the original class trough \texttt{MagicallyTransformed}
+used as a class factory.
+\begin{quote}
+\begin{verbatim}>>> name=UglyDuckling.__name__
+>>> bases=UglyDuckling.__bases__
+>>> dic=UglyDuckling.__dict__.copy()
+>>> UglyDuckling=MagicallyTransformed(name,bases,dic)\end{verbatim}
+\end{quote}
+
+Notice that I have recreated 'UglyDuckling', giving to the new class
+the old identifier.
+\begin{quote}
+\begin{verbatim}>>> print UglyDuckling()
+Very beautiful, since I am <UglyDuckling>>\end{verbatim}
+\end{quote}
+
+The metaclass of this new 'UglyDuckling' has been specified and will
+accompanies all future children of 'UglyDuckling':
+\begin{quote}
+\begin{verbatim}>>> class Swan(UglyDuckling): pass
+...
+>>> type(Swan)
+<class '__main__.MagicallyTransformed'>\end{verbatim}
+\end{quote}
+
+Another caveat, is in the overridding of `` {\_}{\_}init{\_}{\_}`` in the metaclass.
+This is quite common in the case of metaclasses called trough the
+\texttt{{\_}{\_}metaclass{\_}{\_}} hook mechanism, since in this case the class
+has been already defined (if not created) in the class statement,
+and we are interested in initializing it, more than in recreating
+it (which is still possible, by the way).
+The problem is that overriding \texttt{{\_}{\_}init{\_}{\_}} has severe limitations
+with respect to overriding \texttt{{\_}{\_}new{\_}{\_}},
+since the 'name', 'bases' and 'dic' arguments cannot be directly
+changed. Let me show an example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<init{\_}in{\_}metaclass.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{class~M(type):}\\
+\mbox{~~~~"Shows~that~dic~cannot~be~modified~in~{\_}{\_}init{\_}{\_},~only~in~{\_}{\_}new{\_}{\_}"}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,dic):}\\
+\mbox{~~~~~~~~name='C~name~cannot~be~changed~in~{\_}{\_}init{\_}{\_}'}\\
+\mbox{~~~~~~~~bases='cannot~be~changed'}\\
+\mbox{~~~~~~~~dic['changed']=True}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=M}\\
+\mbox{~~~~changed=False}\\
+\mbox{}\\
+\mbox{print~C.{\_}{\_}name{\_}{\_}~~{\#}~=>~C}\\
+\mbox{print~C.{\_}{\_}bases{\_}{\_}~{\#}~=>~(<type~'object'>,)}\\
+\mbox{print~C.changed~~~{\#}~=>~False}\\
+\mbox{}\\
+\mbox{{\#}</init{\_}in{\_}metaclass.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is \texttt{False}: the dictionary cannot be changed in
+\texttt{{\_}{\_}init{\_}{\_}} method. However, replacing \texttt{dic['changed']=True} with
+\texttt{cls.changed=True} would work. Analougously, changing \texttt{cls.{\_}{\_}name{\_}{\_}}
+would work. On the other hand, \texttt{{\_}{\_}bases{\_}{\_}} is a read-only attribute and
+cannot be changed once the class has been created, therefore there is no
+way it can be touched in \texttt{{\_}{\_}init{\_}{\_}}. However, \texttt{{\_}{\_}bases{\_}{\_}} could be
+changed in \texttt{{\_}{\_}new{\_}{\_}} before the class creation.
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclasses-and-inheritance}{}
+\pdfbookmark[1]{Metaclasses and inheritance}{metaclasses-and-inheritance}
+\subsection*{Metaclasses and inheritance}
+
+It is easy to get confused about the difference between a metaclass
+and a mix-in class in multiple inheritance, since
+both are denoted by adjectives and both share the same idea of
+enhancing a hierarchy. Moreover, both mix-in classes and metaclasses
+can be inherited in the whole hierarchy.
+Nevertheless, they behaves differently
+and there are various subtle point to emphasize. We have already
+noticed in the first section that attributes of a metaclass
+are transmitted to its instances, but not to the instances of the
+instances, whereas the normal inheritance is transitive: the
+grandfather transmits its attributes to the children and to the grandchild
+too. The difference can be represented with the following picture, where
+'M' is the metaclass, 'B' a base class, 'C' a children of 'B'
+and c an instance of 'C':
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{M~(attr)~~~~~~~~~B~(attr)~~~}\\
+\mbox{:~~~~~~~~~~~~~~~~|}\\
+\mbox{C~(attr)~~~~~~~~~C~(attr)~~~~}\\
+\mbox{:~~~~~~~~~~~~~~~~:}\\
+\mbox{c~()~~~~~~~~~~~~~c~(attr)~~~~}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that here the relation of instantiation is denoted by a dotted line.
+
+This picture is valid when C has metaclass M but not base class, on when C
+has base class but not metaclass. However, what happens whrn the class C has
+both a metaclass M and a base class B ?
+\begin{quote}
+\begin{verbatim}>>> class M(type): a='M.a'
+>>> class B(object): a='B.a'
+>>> class C(B): __metaclass__=M
+>>> c=C()\end{verbatim}
+\end{quote}
+
+The situation can be represented by in the following graph,
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{(M.a)~~~M~~~B~~(B.a)}\\
+\mbox{~~~~~~~~:~~/}\\
+\mbox{~~~~~~~~:~/}\\
+\mbox{~~~(?)~~C}\\
+\mbox{~~~~~~~~:}\\
+\mbox{~~~~~~~~:}\\
+\mbox{~~~(?)~~c}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here the metaclass M and the base class B are fighting one against the other.
+Who wins ? C should inherit the attribute 'B.a' from its base B, however,
+the metaclass would like to induce an attribute 'M.a'.
+The answer is that the inheritance constraint wins on the metaclass contraint:
+\begin{quote}
+\begin{verbatim}>>> C.a
+'B.a'
+>>> c.a
+'B.a'\end{verbatim}
+\end{quote}
+
+The reason is the same we discussed in the fairy tale example: 'M.a' is
+an attribute of the metaclass, if its instance C has already a specified
+attributed C.a (in this case specified trough inheritance from B), then
+the attribute is not modified. However, one could \emph{force} the modification:
+\begin{quote}
+\begin{verbatim}>>> class M(type):
+... def __init__(cls,*args): cls.a='M.a'
+>>> class C(B): __metaclass__=M
+>>> C.a
+'M.a'\end{verbatim}
+\end{quote}
+
+In this case the metaclass M would win on the base class B. Actually,
+this is not surprising, since it is explicit. What could be surprising,
+had we not explained why inheritance silently wins, is that
+\begin{quote}
+\begin{verbatim}>>> c.a
+'B.a'\end{verbatim}
+\end{quote}
+
+This explain the behaviour for special methods like
+\texttt{{\_}{\_}new{\_}{\_},{\_}{\_}init{\_}{\_},{\_}{\_}str{\_}{\_}},
+etc. which are defined both in the class and the metaclass with the same
+name (in both cases,they are inherited from \texttt{object}).
+
+In the chapter on objects, we learned that the printed representation of
+an object can be modified by overring the \texttt{{\_}{\_}str{\_}{\_}} methods of its
+class. In the same sense, the printed representation of a class can be
+modified by overring the \texttt{{\_}{\_}str{\_}{\_}} methods of its metaclass. Let me show an
+example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Printable(PrettyPrinted,type):}\\
+\mbox{~~~"""Apparently~does~nothing,~but~actually~makes~PrettyPrinted~acting~as}\\
+\mbox{~~~~~~a~metaclass."""}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Instances of 'Printable' are classes with a nice printable representation:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Printable
+>>> C=Printable('Classname',(),{})
+>>> print C
+Classname\end{verbatim}
+\end{quote}
+
+However, the internal string representation stays the same:
+\begin{quote}
+\begin{verbatim}>>> C # invokes Printable.__repr__
+<class '__main__.Classname'>\end{verbatim}
+\end{quote}
+
+Notice that the name of class 'C' is \texttt{Classname} and not 'C' !
+
+Consider for instance the following code:
+\begin{quote}
+\begin{verbatim}>>> class M(type):
+... def __str__(cls):
+... return cls.__name__
+... def method(cls):
+... return cls.__name__
+...
+>>> class C(object):
+... __metaclass__=M
+>>> c=C()\end{verbatim}
+\end{quote}
+
+In this case the \texttt{{\_}{\_}str{\_}{\_}} method in \texttt{M} cannot override the
+\texttt{{\_}{\_}str{\_}{\_}} method in C, which is inherited from \texttt{object}.
+Moreover, if you experiment a little, you will see that
+\begin{quote}
+\begin{verbatim}>>> print C # is equivalent to print M.__str__(C)
+C
+>>> print c # is equivalent to print C.__str__(c)
+<__main__.C object at 0x8158f54>\end{verbatim}
+\end{quote}
+
+The first \texttt{{\_}{\_}str{\_}{\_}} is ''attached`` to the metaclass and the
+second to the class.
+
+Consider now the standard method ''method``. It is both attached to the
+metaclass
+\begin{quote}
+\begin{verbatim}>>> print M.method(C)
+C\end{verbatim}
+\end{quote}
+
+and to the class
+\begin{quote}
+\begin{verbatim}>>> print C.method() #in a sense, this is a class method, i.e. it receives
+C #the class as first argument\end{verbatim}
+\end{quote}
+
+Actually it can be seen as a class method of 'C' (cfr. Guido van Rossum
+''Unifying types and classes in Python 2.2``. When he discusses
+classmethods he says: \emph{''Python also has real metaclasses, and perhaps
+methods defined in a metaclass have more right to the name ``class method'';
+but I expect that most programmers won't be using metaclasses``}). Actually,
+this is the SmallTalk terminology, Unfortunately, in Python the word
+\texttt{classmethod} denotes an attribute descriptor, therefore it is better
+to call the methods defined in a metaclass \emph{metamethods}, in order to avoid
+any possible confusion.
+
+The difference between \texttt{method} and \texttt{{\_}{\_}str{\_}{\_}} is that you cannot use the
+syntax
+\begin{quote}
+\begin{verbatim}>>> print C.__str__() #error
+TypeError: descriptor '__str__' of 'object' object needs an argument\end{verbatim}
+\end{quote}
+
+because of the confusion with the other {\_}{\_}str{\_}{\_}; you can only use the
+syntax
+\begin{quote}
+\begin{verbatim}>>> print M.__str__(C)\end{verbatim}
+\end{quote}
+
+Suppose now I change C's definition by adding a method called ''method``:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~C(object):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=M}\\
+\mbox{~~~~def~{\_}{\_}str{\_}{\_}(self):}\\
+\mbox{~~~~~~~~return~"instance~of~{\%}s"~{\%}~self.{\_}{\_}class{\_}{\_}}\\
+\mbox{~~~~def~method(self):}\\
+\mbox{~~~~~~~~return~"instance~of~{\%}s"~{\%}~self.{\_}{\_}class{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+If I do so, then there is name clashing and the previously working
+statement print C.method() gives now an error:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Traceback~(most~recent~call~last):}\\
+\mbox{~~File~"<stdin>",~line~24,~in~?}\\
+\mbox{TypeError:~unbound~method~method()~must~be~called~with~C~instance~as}\\
+\mbox{first~argument~(got~nothing~instead)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Conclusion: \texttt{{\_}{\_}str{\_}{\_}, {\_}{\_}new{\_}{\_}, {\_}{\_}init{\_}{\_}} etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. \texttt{M.{\_}{\_}str{\_}{\_}(C)}),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. \texttt{C.method()} instead of
+\texttt{M.method(C)}).
+Metaclass methods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for \texttt{{\_}{\_}str{\_}{\_}},
+\texttt{{\_}{\_}{\_}init{\_}{\_}}, etc.
+
+
+%___________________________________________________________________________
+
+\hypertarget{conflicting-metaclasses}{}
+\pdfbookmark[1]{Conflicting metaclasses}{conflicting-metaclasses}
+\subsection*{Conflicting metaclasses}
+
+Consider a class 'A' with metaclass 'M{\_}A' and a class 'B' with
+metaclass 'M{\_}B'; suppose I derive 'C' from 'A' and 'B'. The question is:
+what is the metaclass of 'C' ? Is it 'M{\_}A' or 'M{\_}B' ?
+
+The correct answer (see ''Putting metaclasses to work`` for a thought
+discussion) is 'M{\_}C', where 'M{\_}C' is a metaclass that inherits from
+'M{\_}A' and 'M{\_}B', as in the following graph:
+\begin{quote}
+\begin{figure}
+
+\includegraphics{fig1.ps}
+\end{figure}
+\end{quote}
+
+However, Python is not yet that magic, and it does not automatically create
+'M{\_}C'. Instead, it will raise a \texttt{TypeError}, warning the programmer of
+the possible confusion:
+\begin{quote}
+\begin{verbatim}>>> class M_A(type): pass
+>>> class M_B(type): pass
+>>> A=M_A('A',(),{})
+>>> B=M_B('B',(),{})
+>>> class C(A,B): pass #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: metatype conflict among bases\end{verbatim}
+\end{quote}
+
+This is an example where the metaclasses 'M{\_}A' and 'M{\_}B' fight each other
+to generate 'C' instead of cooperating. The metatype conflict can be avoided
+by assegning the correct metaclass to 'C' by hand:
+\begin{quote}
+\begin{verbatim}>>> class C(A,B): __metaclass__=type("M_AM_B",(M_A,M_B),{})
+>>> type(C)
+<class '__main__.M_AM_B'>\end{verbatim}
+\end{quote}
+
+In general, a class A(B, C, D , ...) can be generated without conflicts only
+if type(A) is a subclass of each of type(B), type(C), ...
+
+In order to avoid conflicts, the following function, that generates
+the correct metaclass by looking at the metaclasses of the base
+classes, is handy:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{metadic={\{}{\}}}\\
+\mbox{}\\
+\mbox{def~{\_}generatemetaclass(bases,metas,priority):}\\
+\mbox{~~~~trivial=lambda~m:~sum([issubclass(M,m)~for~M~in~metas],m~is~type)}\\
+\mbox{~~~~{\#}~hackish!!~m~is~trivial~if~it~is~'type'~or,~in~the~case~explicit}\\
+\mbox{~~~~{\#}~metaclasses~are~given,~if~it~is~a~superclass~of~at~least~one~of~them}\\
+\mbox{~~~~metabs=tuple([mb~for~mb~in~map(type,bases)~if~not~trivial(mb)])}\\
+\mbox{~~~~metabases=(metabs+metas,~metas+metabs)[priority]}\\
+\mbox{~~~~if~metabases~in~metadic:~{\#}~already~generated~metaclass}\\
+\mbox{~~~~~~~~return~metadic[metabases]}\\
+\mbox{~~~~elif~not~metabases:~{\#}~trivial~metabase}\\
+\mbox{~~~~~~~~meta=type~}\\
+\mbox{~~~~elif~len(metabases)==1:~{\#}~single~metabase}\\
+\mbox{~~~~~~~~meta=metabases[0]}\\
+\mbox{~~~~else:~{\#}~multiple~metabases}\\
+\mbox{~~~~~~~~metaname="{\_}"+''.join([m.{\_}{\_}name{\_}{\_}~for~m~in~metabases])}\\
+\mbox{~~~~~~~~meta=makecls()(metaname,metabases,{\{}{\}})}\\
+\mbox{~~~~return~metadic.setdefault(metabases,meta)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This function is particularly smart since:
+\begin{quote}
+\newcounter{listcnt29}
+\begin{list}{\arabic{listcnt29}.}
+{
+\usecounter{listcnt29}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Avoid duplications ..
+
+\item {}
+Remember its results.
+
+\end{list}
+\end{quote}
+
+We may generate the child of a tuple of base classes with a given metaclass
+and avoiding metatype conflicts thanks to the following \texttt{child} function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~makecls(*metas,**options):}\\
+\mbox{~~~~"""Class~factory~avoiding~metatype~conflicts.~The~invocation~syntax~is}\\
+\mbox{~~~~makecls(M1,M2,..,priority=1)(name,bases,dic).~If~the~base~classes~have~}\\
+\mbox{~~~~metaclasses~conflicting~within~themselves~or~with~the~given~metaclasses,}\\
+\mbox{~~~~it~automatically~generates~a~compatible~metaclass~and~instantiate~it.~}\\
+\mbox{~~~~If~priority~is~True,~the~given~metaclasses~have~priority~over~the~}\\
+\mbox{~~~~bases'~metaclasses"""}\\
+\mbox{}\\
+\mbox{~~~~priority=options.get('priority',False)~{\#}~default,~no~priority}\\
+\mbox{~~~~return~lambda~n,b,d:~{\_}generatemetaclass(b,metas,priority)(n,b,d)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> class C(A,B): __metaclass__=makecls()
+>>> print C,type(C)
+<class 'oopp.AB_'> <class 'oopp._M_AM_B'>\end{verbatim}
+\end{quote}
+
+Notice that the automatically generated metaclass does not pollute the
+namespace:
+\begin{quote}
+\begin{verbatim}>>> _M_A_M_B #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+NameError: name '_M_A_M_B' is not defined\end{verbatim}
+\end{quote}
+
+It can only be accessed as \texttt{type(C)}.
+
+Put it shortly, the \texttt{child} function allows to generate a child from bases
+enhanced by different custom metaclasses, by generating under the hood a
+compatibile metaclass via multiple inheritance from the original metaclasses.
+However, this logic can only work if the original metaclasses are
+cooperative, i.e. their methods are written in such a way to avoid
+collisions. This can be done by using the cooperative the \texttt{super} call
+mechanism discussed in chapter 4.
+
+
+%___________________________________________________________________________
+
+\hypertarget{cooperative-metaclasses}{}
+\pdfbookmark[1]{Cooperative metaclasses}{cooperative-metaclasses}
+\subsection*{Cooperative metaclasses}
+
+In this section I will discuss how metaclasses can be composed with
+classes and with metaclasses, too. Since we will discusss even
+complicated hierarchies, it is convenient to have an utility
+routine printing the MRO of a given class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~MRO(cls):}\\
+\mbox{~~~~count=0;~out=[]}\\
+\mbox{~~~~print~"MRO~of~{\%}s:"~{\%}~cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~for~c~in~cls.{\_}{\_}mro{\_}{\_}:}\\
+\mbox{~~~~~~~~name=c.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~bases=','.join([b.{\_}{\_}name{\_}{\_}~for~b~in~c.{\_}{\_}bases{\_}{\_}])}\\
+\mbox{~~~~~~~~s="~~{\%}s~-~{\%}s({\%}s)"~{\%}~(count,name,bases)}\\
+\mbox{~~~~~~~~if~type(c)~is~not~type:~s+="[{\%}s]"~{\%}~type(c).{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~out.append(s);~count+=1}\\
+\mbox{~~~~return~'{\textbackslash}n'.join(out)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that \texttt{MRO} also prints the metaclass' name in square brackets, for
+classes enhanced by a non-trivial metaclass.
+
+Consider for instance the following hierarchy:
+\begin{quote}
+\begin{verbatim}>>> from oopp import MRO
+>>> class B(object): pass
+>>> class M(B,type): pass
+>>> class C(B): __metaclass__=M\end{verbatim}
+\end{quote}
+
+Here 'M' is a metaclass that inherits from 'type' and the base class 'B'
+and 'C' is both an instance of 'M' and a child of 'B'. The inheritance
+graph can be draw as
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~object}\\
+\mbox{~/~~~{\textbackslash}}\\
+\mbox{B~~~~type}\\
+\mbox{|~{\textbackslash}~~/}\\
+\mbox{|~~M}\\
+\mbox{{\textbackslash}~~:}\\
+\mbox{~{\textbackslash}~:~~~~~}\\
+\mbox{~~~C}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Suppose now we want to retrieve the \texttt{{\_}{\_}new{\_}{\_}} method of B's superclass
+with respect to the MRO of C: obviously, this is \texttt{object.{\_}{\_}new{\_}{\_}}, since
+\begin{quote}
+\begin{verbatim}>>> print MRO(C)
+MRO of C:
+ 0 - C(B)[M]
+ 1 - B(object)
+ 2 - object()\end{verbatim}
+\end{quote}
+
+This allows to create an instance of 'C' in this way:
+\begin{quote}
+\begin{verbatim}>>> super(B,C).__new__(C)
+<__main__.C object at 0x4018750c>\end{verbatim}
+\end{quote}
+
+It is interesting to notice that this would not work in Python 2.2,
+due to a bug in the implementation of \texttt{super}, therefore do not
+try this trick with older version of Python.
+
+Notice that everything works
+only because \texttt{B} inherits the \texttt{object.{\_}{\_}new{\_}{\_}} staticmethod that
+is cooperative and it turns out that it calls \texttt{type.{\_}{\_}new{\_}{\_}}. However,
+if I give to 'B' a non-cooperative method
+\begin{quote}
+\begin{verbatim}>>> B.__new__=staticmethod(lambda cls,*args: object.__new__(cls))\end{verbatim}
+\end{quote}
+
+things do not work:
+\begin{quote}
+\begin{verbatim}>>> M('D',(),{}) #error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 1, in <lambda>
+TypeError: object.__new__(M) is not safe, use type.__new__()\end{verbatim}
+\end{quote}
+
+A cooperative method would solve the problem:
+\begin{quote}
+\begin{verbatim}>>> B.__new__=staticmethod(lambda m,*args: super(B,m).__new__(m,*args))
+>>> M('D',(),{}) # calls B.__new__(M,'D',(),{})
+<class '__main__.D'>\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{metamethods-vs-class-methods}{}
+\pdfbookmark[1]{Metamethods vs class methods}{metamethods-vs-class-methods}
+\subsection*{Metamethods vs class methods}
+
+Meta-methods, i.e. methods defined in
+a metaclass.
+
+Python has already few built-in metamethods: \texttt{.mro()}
+and \texttt{{\_}{\_}subclass{\_}{\_}}. These are methods of the metaclass 'type' and
+there of any of its sub-metaclasses.
+\begin{quote}
+\begin{verbatim}>>> dir(type)
+['__base__', '__bases__', '__basicsize__', '__call__', '__class__',
+ '__cmp__', '__delattr__', '__dict__', '__dictoffset__', '__doc__',
+ '__flags__', '__getattribute__', '__hash__', '__init__', '__itemsize__',
+ '__module__', '__mro__', '__name__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__subclasses__', '__weakrefoffset__', 'mro']\end{verbatim}
+\begin{verbatim}>>> print type.mro.__doc__
+mro() -> list
+return a type's method resolution order
+>>> print type.__subclasses__.__doc__
+__subclasses__() -> list of immediate subclasses\end{verbatim}
+\begin{verbatim}>>> class A(object): pass
+>>> class B(A): pass
+>>> B.mro()
+[<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
+>>> A.__subclasses__()
+[<class '__main__.B'>]\end{verbatim}
+\end{quote}
+
+Notice that \texttt{mro()} and \texttt{{\_}{\_}subclasses{\_}{\_}} are not retrieved by \texttt{dir}.
+
+Let me constrast metamethods with the more traditional classmethods.
+In many senses, the to concepts are akin:
+\begin{quote}
+\begin{verbatim}>>> class M(type):
+... "Metaclass with a (meta)method mm"
+... def mm(cls): return cls
+>>> D=M('D',(),{'cm':classmethod(lambda cls: cls)})
+>>> # instance of M with a classmethod cm
+>>> D.mm # the metamethod
+<bound method M.mm of <class '__main__.C'>>
+>>> D.cm # the classmethod
+<unbound method D.<lambda>>\end{verbatim}
+\end{quote}
+
+Notice the similarities between the classmethod and the metamethod:
+\begin{quote}
+\begin{verbatim}>>> D.mm.im_self, D.cm.im_self # the same
+(<class '__main__.D'>, <class '__main__.D'>)
+>>> D.mm.im_class, D.cm.im_class # still the same
+(<class '__main__.M'>, <class '__main__.M'>)\end{verbatim}
+\end{quote}
+
+There are no surprises for \texttt{im{\_}func}:
+\begin{quote}
+\begin{verbatim}>>> D.mm.im_func, D.cm.im_func
+(<function mm at 0x402c272c>, <function <lambda> at 0x402c280c>)\end{verbatim}
+\end{quote}
+
+Nevertheless, there are differences: metamethods are not bounded to
+instances of the class
+\begin{quote}
+\begin{verbatim}>>> D().cm() # the classmethod works fine
+<class '__main__.D'>
+>>> D().mm() # the metamethod does not: error
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'D' object has no attribute 'mm'\end{verbatim}
+\end{quote}
+
+and they are not retrieved by \texttt{dir}:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> attributes(D).keys() # mm is not retrieved, only cm
+['cm']\end{verbatim}
+\begin{verbatim}>>> cm.__get__('whatever') #under Python 2.2.0 would give a serious error
+Segmentation fault
+>>> cm.__get__(None) #under Python 2.3 there is no error
+<bound method type.<lambda> of <type 'NoneType'>>\end{verbatim}
+\end{quote}
+
+Moreover metamethods behaves differently with respect to multiple
+inheritance. If a class A define a classmethod cA and a class B
+defines a classmethod cB, then the class C(A,B) inherits both the
+classmethods cA and cB. In the case of metamethods defined in M{\_}A
+and M{\_}B, the same is true only if one resolves the meta-type
+conflict by hand, by generating the metaclass M{\_}C(M{\_}A,M{\_}B). In this
+sense, classmethods are simpler to use than metamethods.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-magic-of-metaclasses-part-2}{}
+\pdfbookmark[0]{THE MAGIC OF METACLASSES - PART 2}{the-magic-of-metaclasses-part-2}
+\section*{THE MAGIC OF METACLASSES - PART 2}
+
+Metaclasses are so powerful that a single chapter is not enough to make
+justice to them ;) In this second chapter on metaclasses I will
+unravel their deepest secrets, covering topics such as meta-metaclasses,
+anonymous inner metaclasses, global metaclasses and advanced class factories.
+
+Moreover, I will give various magical applications of metaclasses,
+in the realm of enhancing the Python language itself. Actually, this is
+probably the most idiomatic application of metaclasses (Guido's examples
+on the metaclass usage are all in this area). I will show
+how metaclasses can be used to enhance the \texttt{super} cooperatice call
+mechanism.
+
+This is not a chapter for the faint of heart.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-secrets-of-the-metaclass-hook}{}
+\pdfbookmark[1]{The secrets of the \_\_metaclass\_\_ hook}{the-secrets-of-the-metaclass-hook}
+\subsection*{The secrets of the \texttt{{\_}{\_}metaclass{\_}{\_}} hook}
+
+In the previous chapter we have seen how the \texttt{{\_}{\_}metaclass{\_}{\_}} hook can
+be used as a way of metaclass enhancing pre-existing classes
+with a minimal change of the sourcecode.
+
+But it has much deeper secrets.
+
+The first and simplest of them,
+is the fact that the hook can be used it can also be defined
+at the module level, \emph{outside} the class. This allows a number of neat
+tricks, since in presence of a \texttt{{\_}{\_}metaclass{\_}{\_}} hook at the module
+level \emph{all} the old style classes in the module (including nested ones!)
+acquire that hook. A first application is to rejuvenate old style classes
+to new style classes.
+
+I remind that old style classes are retained with compability with old
+code, but they are a pain in the back, if you want to use features
+intended for new style classes only (for instance properties etc.).
+Naively, one would expect the conversion from old style classes
+to new style to be long and error prone: suppose you have a very large
+application with hundreds of old style classes defined in dozens of modules.
+Suppose you want to update your application to Python 2.2+ classes in order
+to take advantage of the new features I have discussed extensively in this
+book: the naive way to go would be to go trough the source, look for
+all classes definitions and change
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Classname:~-->~Classname(object)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+One could solve this problem with a regular expression search and replace
+in all modules, but this would require to change \emph{all} the source.
+This is againt the spirit of OOP, we must \emph{reuse} old code.
+
+Metaclasses are particularly handy to solve this problem: actually it is
+enough to add to your modules the following line as first line:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~type}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Then, all your old style classes will have 'type' as their metaclass: this
+is akin to say that all the old style classes are \emph{automagically} rejuvenate
+to new style classes! And this also works for \emph{nested} classes!!
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<rejuvenate.py>}\\
+\mbox{}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~type~{\#}~this~rejuvanate~all~the~class~in~the~module}\\
+\mbox{}\\
+\mbox{class~C:}\\
+\mbox{~~~class~D:~pass}\\
+\mbox{}\\
+\mbox{print~dir(C)~~~{\#}~both~C~and~C.D}\\
+\mbox{print~dir(C.D)~{\#}~are~now~new~style~classes}\\
+\mbox{}\\
+\mbox{{\#}</rejuvenate.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This very first example add consistence (if needed) to the
+widespread belief that metaclasses have a well deserved reputation of magic.
+
+The explanation is that defining a global metaclass called \texttt{{\_}{\_}metaclass{\_}{\_}}
+automatically makes all old style classes (new style class simply ignore
+the existence of the global \texttt{{\_}{\_}metaclass{\_}{\_}}) defined in you module
+instances of the given metaclass; this automatically converts them to
+new style classes.
+
+
+%___________________________________________________________________________
+
+\hypertarget{anonymous-inner-metaclasses}{}
+\pdfbookmark[1]{Anonymous inner metaclasses}{anonymous-inner-metaclasses}
+\subsection*{Anonymous inner metaclasses}
+
+A second, deeper secret of the \texttt{{\_}{\_}metaclass{\_}{\_}} hook is that it can be
+used to define anonymous \emph{inner metaclasses}. The following example
+explain what I mean:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~totuple(arg):}\\
+\mbox{~~~~"Converts~the~argument~to~a~tuple,~if~need~there~is"}\\
+\mbox{~~~~if~isinstance(arg,tuple):~return~arg~{\#}~do~nothing}\\
+\mbox{~~~~else:~return~(arg,)~{\#}~convert~to~tuple}\\
+\mbox{}\\
+\mbox{class~BracketCallable(object):}\\
+\mbox{~~~~"""Any~subclass~C(BracketCallable)~can~be~called~with~the~syntax~C[t],~}\\
+\mbox{~~~~where~t~is~a~tuple~of~arguments~stored~in~bracket{\_}args;~~returns~the~}\\
+\mbox{~~~~class~or~an~instance~of~it,~depending~on~the~flag~'returnclass'."""}\\
+\mbox{}\\
+\mbox{~~~~returnclass=True}\\
+\mbox{~~~~class~{\_}{\_}metaclass{\_}{\_}(type):~{\#}~anonymous~inner~metaclass}\\
+\mbox{~~~~~~~~def~{\_}{\_}getitem{\_}{\_}(cls,args):~{\#}~non~cooperative~metamethod}\\
+\mbox{~~~~~~~~~~~~if~cls.returnclass:~}\\
+\mbox{~~~~~~~~~~~~~~~~c=type(cls.{\_}{\_}name{\_}{\_},(cls,),{\{}'bracket{\_}args':totuple(args){\}})}\\
+\mbox{~~~~~~~~~~~~~~~~return~c~{\#}~a~customized~copy~of~the~original~class}\\
+\mbox{~~~~~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~~~~~self=cls();~self.bracket{\_}args=totuple(args)}\\
+\mbox{~~~~~~~~~~~~~~~~return~self}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this code 'BracketCallable.{\_}{\_}metaclass{\_}{\_}' is the anonymous (actually
+it has a special name, \texttt{{\_}{\_}metaclass{\_}{\_}}) inner metaclass of 'BracketCallable'.
+
+The effect of 'BracketCallable.{\_}{\_}metaclass{\_}{\_}' is the following: it makes
+'BracketCallable' and its descendants callable with brackets. Since
+the 'returnclass' flag is set, \texttt{{\_}{\_}getitem{\_}{\_}} returns the class
+with an attribute 'bracket{\_}args' containing the tuple of the passed
+arguments (otherwise it returns an instance of the class).
+This works since when
+Python encounters an expression of kind \texttt{cls[arg]} it interprets it
+as \texttt{type(cls).{\_}{\_}getitem{\_}{\_}(cls,arg)}. Therefore, if \texttt{cls} is a subclass
+of 'BracketCallable', this means that
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{cls[arg]~<=>~BracketCallable.{\_}{\_}metaclass{\_}{\_}.{\_}{\_}getitem{\_}{\_}(cls,arg)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Let me give few examples:
+\begin{quote}
+\begin{verbatim}>>> from oopp import BracketCallable
+>>> type(BracketCallable)
+<class 'oopp.__metaclass__'>
+>>> print type(BracketCallable).__name__ # not really anonymous
+__metaclass__
+>>> print BracketCallable['a1'].bracket_args
+('a1',)
+>>> print BracketCallable['a1','a2'].bracket_args
+('a1', 'a2')\end{verbatim}
+\end{quote}
+
+This syntactical feature is an example of a thing that can be done
+\emph{trough metaclasses only}: it cannot be emulated by functions.
+
+Anonymous inner metaclasses are the least verbose manner
+of defining metamethods. Moreover, they are a neat trick to define
+mix-in classes that, when inherited, can metamagically enhance
+an entire multiple inheritance hierarchy.
+
+In the previous example \texttt{{\_}{\_}getitem{\_}{\_}} is noncooperative, but nothing
+forbids anonymous inner metaclasses from being made cooperative. However,
+there is some subtlety one must be aware of.
+Let me give an example. My 'WithCounter' class counts how many instances
+of 'WithCounter' and its subclasses are generated. However, it does not
+distinguishes bewteen different subclasses.
+This was correct in the pizza shop example, simple only the total
+number of produced pizzas mattered, however, in other situations,
+one may want to reset the counter each time a new subclass is created.
+This can be done automagically by a cooperative inner metaclass:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~WithMultiCounter(WithCounter):}\\
+\mbox{~~~~"""Each~time~a~new~subclass~is~derived,~the~counter~is~reset"""}\\
+\mbox{~~~~class~{\_}{\_}metaclass{\_}{\_}(type):}\\
+\mbox{~~~~~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~~~~~cls.counter=0}\\
+\mbox{~~~~~~~~~~~~super(cls.{\_}{\_}this,cls).{\_}{\_}init{\_}{\_}(*args)}\\
+\mbox{~~~~reflective({\_}{\_}metaclass{\_}{\_})}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that the order of execution of this code is subtle:
+\newcounter{listcnt30}
+\begin{list}{\arabic{listcnt30})}
+{
+\usecounter{listcnt30}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+first, the fact that WithMulticounter has a non-trivial metaclass is
+registered, but nothing else is done;
+
+\item {}
+then, the line \texttt{reflective({\_}{\_}metaclass{\_}{\_})} is executed: this means
+that the inner metaclass (and therefore its instances) get an
+attribute \texttt{.{\_}metaclass{\_}{\_}this} containing a reference to the
+inner metaclass;
+
+\item {}
+then, the outer class is passed to its inner metaclass and created
+by the inherited metaclass' \texttt{{\_}{\_}new{\_}{\_}} method;
+
+\item {}
+at this point \texttt{cls} exists and \texttt{cls.{\_}{\_}this} is inherited from
+\texttt{{\_}{\_}metaclass{\_}{\_}.{\_}metaclass{\_}{\_}this}; this means that the expression
+\texttt{super(cls.{\_}{\_}this,cls).{\_}{\_}init{\_}{\_}(*args)} is correctly recognized and
+'WithMultiCounter' can be initialized;
+
+\item {}
+only after that, the name 'WithMultiCounter' enters in the global namespace
+and can be recognized.
+
+\end{list}
+
+Notice in particular that inside \texttt{super}, we could also
+use \texttt{cls.{\_}{\_}metaclass{\_}{\_}} instead of \texttt{cls.{\_}{\_}this}, but this
+would not work inside \texttt{{\_}{\_}new{\_}{\_}}, whereas \texttt{{\_}{\_}this} would be
+recognized even in \texttt{{\_}{\_}new{\_}{\_}}.
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> print MRO(WithMultiCounter)
+1 - WithMultiCounter(WithCounter)[__metaclass__]
+2 - WithCounter(object)
+3 - object()\end{verbatim}
+\end{quote}
+
+For sake of readability, often it is convenient
+to give a name even to inner classes:
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~WithMultiCounter(WithCounter):}\\
+\mbox{~~~~"""Each~time~a~new~subclass~is~derived,~the~counter~is~reset"""}\\
+\mbox{~~~~class~ResetsCounter(type):}\\
+\mbox{~~~~~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~~~~~cls.counter=0}\\
+\mbox{~~~~~~~~~~~~super(cls.ResetsCounter,cls).{\_}{\_}init{\_}{\_}(*args)}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=ResetsCounter}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+
+Notice that inside super we used the expression \texttt{cls.ResetsCounter} and
+not \texttt{WithMultiCounter.ResetsCounter}: doing that would generate a
+\texttt{NameError: global name 'WithMultiCounter' is not defined} since at the
+time when \texttt{ResetsCounter.{\_}{\_}init{\_}{\_}} is called for the first time,
+the class \texttt{WithMultiCounter} exists but is has not yet entered the global
+namespace: this will happens only after the initialization in the
+\texttt{ResetsCounter} metaclass, as we said before.
+
+Without the metaclass one can reset the counter by hand each time, or
+can reset the counter on all the classes of the hierarchy with a
+convenient function (akin to the 'traceH' routine defined in chapter 6).
+
+Example:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class GrandFather(WithMultiCounter): pass
+>>> class Father(GrandFather): pass
+>>> class Child(Father): pass
+>>> GrandFather()
+<__main__.GrandFather object at 0x402f7f6c> # first GrandFather instance
+>>> Father()
+<__main__.Father object at 0x402f79ec> # first Father instance
+>>> Father()
+<__main__.Father object at 0x402f7d4c> # second Father instance
+>>> Child.counter # zero instances
+0
+>>> Father.counter # two instances
+2
+>>> GrandFather.counter # one instance
+1\end{verbatim}
+\end{quote}
+
+I leave as an exercise for the reader to show that the original 'WithCounter'
+would fail to count correctly the different subclasses and would put the
+total number of instances in 'Child'.
+
+
+%___________________________________________________________________________
+
+\hypertarget{passing-parameters-to-meta-classes}{}
+\pdfbookmark[1]{Passing parameters to (meta) classes}{passing-parameters-to-meta-classes}
+\subsection*{Passing parameters to (meta) classes}
+
+Calling a class with brackets is a way of passing parameters to it (or
+to its instances, if the 'returnclass' flag is not set).
+There additional ways for of doing that.
+One can control the instantiation syntax of classes by redefining the
+\texttt{{\_}{\_}call{\_}{\_}} method of the metaclass. The point is that when we instantiate
+an object with the syntax \texttt{c=C()}, Python looks
+at the \texttt{{\_}{\_}call{\_}{\_}} method of the metaclass of 'C'; the default behaviour
+it is to call \texttt{C.{\_}{\_}new{\_}{\_}} and \texttt{C.{\_}{\_}init{\_}{\_}} in succession, however, that
+behavior can be overridden. Let me give an example without using
+anonymous metaclasses (for sake of clarity only).
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<metacall.py>}\\
+\mbox{}\\
+\mbox{class~M(type):~{\#}~this~is~C~metaclass}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(cls):}\\
+\mbox{~~~~~~~~return~"Called~M.{\_}{\_}call{\_}{\_}"~}\\
+\mbox{}\\
+\mbox{C=M('C',(),{\{}{\}})~{\#}~calls~type(M).{\_}{\_}call{\_}{\_}}\\
+\mbox{c=C()~{\#}~calls~type(C).{\_}{\_}call{\_}{\_}}\\
+\mbox{{\#}~attention:~c~is~a~string!}\\
+\mbox{print~c~{\#}=>~Called~M.{\_}{\_}call{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}</metacall.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this example, \texttt{M.{\_}{\_}call{\_}{\_}} simply
+returns the string \texttt{Called M.{\_}{\_}call{\_}{\_}}, and the class
+'C' is \emph{not} instantiated. Overriding the metaclass
+\texttt{{\_}{\_}call{\_}{\_} `` method therefore provides another way to implement
+the ``Singleton} pattern. However, savage overridings as the one in
+this example, are not a good idea, since it will confuse everybody.
+This is an example where metaclasses change the semantics: whereas
+usually the notation \texttt{C()} means ''creates a C instance``, the
+metaclass can give to the syntax \texttt{C()} any meaning we want.
+Here there is both the power and the danger of metaclasses: they
+allows to make both miracles and disasters. Nevertheless, used with
+a grain of salt, they provide a pretty nice convenience.
+
+Anyway, overriding the '{\_}{\_}call{\_}{\_}' method of the metaclass can be
+confusing, since parenthesis are usually reserved to mean instantion,
+therefore I will prefere to pass arguments trough brackets.
+
+The beauty and the magic of metaclasses stays in the fact that this mechanism
+is completely general: since metaclasses themselves are classes, we can
+'CallableWithBrackets' to pass arguments to a metaclass, i.e.
+'CallableWithBrackets' can also be used as a meta-metaclass!
+
+I leave as an exercise for the reader to figure out
+how to define meta-meta-metaclasses, meta-meta-meta-metaclasses, etc.
+etc. (there is no limit to the abstraction level you can reach with
+metaclasses;-)
+
+Let me show an example: a magical way of making methods cooperative.
+This can be done trough a 'Cooperative' metaclass that inherits from
+'BracketCallable' and therefore has 'BracketCallable.{\_}{\_}metaclass{\_}{\_}'
+as (meta)metaclass:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Cooperative(BracketCallable,type):}\\
+\mbox{~~~~"""Bracket-callable~metaclass~implementing~cooperative~methods.~Works}\\
+\mbox{~~~~well~for~plain~methods~returning~None,~such~as~{\_}{\_}init{\_}{\_}"""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~methods=cls.bracket{\_}args}\\
+\mbox{~~~~~~~~for~meth~in~methods:~}\\
+\mbox{~~~~~~~~~~~~setattr(cls,meth,cls.coop{\_}method(meth,vars(cls).get(meth)))}\\
+\mbox{~~~~def~coop{\_}method(cls,name,method):~{\#}~method~can~be~None}\\
+\mbox{~~~~~~~~"""Calls~both~the~superclass~method~and~the~class~method~(if~the~}\\
+\mbox{~~~~~~~~class~has~an~explicit~method).~Implemented~via~a~closure"""}\\
+\mbox{~~~~~~~~def~{\_}(self,*args,**kw):}\\
+\mbox{~~~~~~~~~~~~getattr(super(cls,self),name)(*args,**kw)~{\#}~call~the~supermethod}\\
+\mbox{~~~~~~~~~~~~if~method:~method(self,*args,**kw)~{\#}~call~the~method}\\
+\mbox{~~~~~~~~return~{\_}}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The code above works for methods returing \texttt{None}, such as \texttt{{\_}{\_}init{\_}{\_}}.
+Here I give a first example of application: a hierarchy where the \texttt{{\_}{\_}init{\_}{\_}}
+methods are automatically called (similar to automatic initialization
+in Java).
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<cooperative.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~Cooperative}\\
+\mbox{}\\
+\mbox{class~B(object):}\\
+\mbox{~~~~"""Cooperative~base~class;~all~its~descendants~will~automagically~}\\
+\mbox{~~~~invoke~their~ancestors~{\_}{\_}init{\_}{\_}~methods~in~chain."""}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=Cooperative['{\_}{\_}init{\_}{\_}']}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,*args,**kw):}\\
+\mbox{~~~~~~~~print~"This~is~B.{\_}{\_}init{\_}{\_}"}\\
+\mbox{}\\
+\mbox{class~C(B):}\\
+\mbox{~~~~"Has~not~explicit~{\_}{\_}init{\_}{\_}"}\\
+\mbox{}\\
+\mbox{class~D(C):}\\
+\mbox{~~~~"""The~metaclass~makes~D.{\_}{\_}init{\_}{\_}~to~call~C.{\_}{\_}init{\_}{\_}~and~}\\
+\mbox{~~~~therefore~B.{\_}{\_}init{\_}{\_}"""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,*args,**kw):}\\
+\mbox{~~~~~~~~print~"This~is~D.{\_}{\_}init{\_}{\_}"}\\
+\mbox{}\\
+\mbox{d=D()}\\
+\mbox{}\\
+\mbox{print~"The~metaclass~of~B~is",type(B)}\\
+\mbox{print~"The~meta-metaclass~of~B~is",~type(type(B))}\\
+\mbox{}\\
+\mbox{{\#}</cooperative.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{This~is~B.{\_}{\_}init{\_}{\_}}\\
+\mbox{This~is~D.{\_}{\_}init{\_}{\_}}\\
+\mbox{The~metaclass~of~B~is~<class~'oopp.Cooperative'>}\\
+\mbox{The~meta-metaclass~of~B~~is~<class~'oopp.{\_}{\_}metaclass{\_}{\_}'>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+A second example, is the following, an alternative way of
+making the paleoanthropological hierarchy of chapter 4 cooperative:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<paleo.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~Cooperative,Homo}\\
+\mbox{}\\
+\mbox{class~HomoHabilis(Homo):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=Cooperative['can']}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~print~"~-~make~tools"}\\
+\mbox{}\\
+\mbox{class~HomoSapiens(HomoHabilis):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~print~"~-~make~abstractions"}\\
+\mbox{~~~~}\\
+\mbox{class~HomoSapiensSapiens(HomoSapiens):}\\
+\mbox{~~~~def~can(self):}\\
+\mbox{~~~~~~~~print~"~-~make~art"}\\
+\mbox{}\\
+\mbox{HomoSapiensSapiens().can()}\\
+\mbox{}\\
+\mbox{{\#}~Output:}\\
+\mbox{}\\
+\mbox{{\#}~<HomoSapiensSapiens>~can:}\\
+\mbox{{\#}~~-~make~tools}\\
+\mbox{{\#}~~-~make~abstractions}\\
+\mbox{{\#}~~-~make~art}\\
+\mbox{}\\
+\mbox{{\#}</paleo.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Metaclasses can be used to violate the old good rule ''explicit is
+better than implicit``. Looking at the source code for 'HomoSapiens'
+and 'HomoSapiensSapiens' one would never imagine the \texttt{can} is
+somewhat special. That is why in the following I will prefer to
+use the anonymous super call mechanism, which is explicit, instead
+of the implicit cooperative mechanism.
+
+
+%___________________________________________________________________________
+
+\hypertarget{meta-functions}{}
+\pdfbookmark[1]{Meta-functions}{meta-functions}
+\subsection*{Meta-functions}
+
+The third and deepest secret of the \texttt{{\_}{\_}metaclass{\_}{\_}} hook is that, even if
+it is typically used in conjunction with metaclasses, actually the hook
+can refer to generic class factories callable with the signature
+\texttt{(name,bases,dic)}. Let me show a few examples
+where \texttt{{\_}{\_}metaclass{\_}{\_}} is a function or a generic callable object
+instead of being a metaclass:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<metafun.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~kwdict}\\
+\mbox{}\\
+\mbox{class~Callable(object):}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,name,bases,dic):}\\
+\mbox{~~~~~~~~print~name,bases,'{\textbackslash}n',kwdict(dic)}\\
+\mbox{~~~~~~~~return~type(name,bases,dic)}\\
+\mbox{}\\
+\mbox{callableobj=Callable()}\\
+\mbox{}\\
+\mbox{class~C:~{\_}{\_}metaclass{\_}{\_}=callableobj}\\
+\mbox{}\\
+\mbox{print~"type~of~C:",C.{\_}{\_}class{\_}{\_}}\\
+\mbox{}\\
+\mbox{def~f(name,bases,dic):}\\
+\mbox{~~~~print~name,bases,'{\textbackslash}n',kwdict(dic)}\\
+\mbox{~~~~return~type(name,bases,dic)}\\
+\mbox{}\\
+\mbox{class~D:~{\_}{\_}metaclass{\_}{\_}=f}\\
+\mbox{}\\
+\mbox{print~"type~of~D:",D.{\_}{\_}class{\_}{\_}}\\
+\mbox{}\\
+\mbox{class~B(object):}\\
+\mbox{~~~~def~{\_}{\_}metaclass{\_}{\_}(name,bases,dic):}\\
+\mbox{~~~~~~~~"""In~this~form,~the~{\_}{\_}metaclass{\_}{\_}~attribute~is~a~function.~}\\
+\mbox{~~~~~~~~In~practice,~it~works~as~a~special~static~method~analogous~}\\
+\mbox{~~~~~~~~to~{\_}{\_}new{\_}{\_}"""}\\
+\mbox{~~~~~~~~print~"name:~",~name}\\
+\mbox{~~~~~~~~print~"bases:",~bases}\\
+\mbox{~~~~~~~~print~"dic:{\textbackslash}n",kwdict(dic)}\\
+\mbox{~~~~~~~~return~type(name,bases,dic)}\\
+\mbox{}\\
+\mbox{class~E(B):~pass}\\
+\mbox{}\\
+\mbox{print~"type~of~E:",E.{\_}{\_}class{\_}{\_}}\\
+\mbox{print~"Non-called~E.{\_}{\_}metaclass{\_}{\_}:",~E.{\_}{\_}metaclass{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}</metafun.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{C~()~}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~<Callable~object~at~0x401c964c>}\\
+\mbox{{\_}{\_}module{\_}{\_}~=~{\_}{\_}builtin{\_}{\_}}\\
+\mbox{type~of~C:~<type~'type'>}\\
+\mbox{D~()~}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~<function~f~at~0x401c4994>}\\
+\mbox{{\_}{\_}module{\_}{\_}~=~{\_}{\_}builtin{\_}{\_}}\\
+\mbox{type~of~D:~<type~'type'>}\\
+\mbox{name:~~B}\\
+\mbox{bases:~(<type~'object'>,)}\\
+\mbox{dic:~}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~<function~{\_}{\_}metaclass{\_}{\_}~at~0x401c4a3c>}\\
+\mbox{{\_}{\_}module{\_}{\_}~=~{\_}{\_}builtin{\_}{\_}}\\
+\mbox{type~of~E:~<type~'type'>}\\
+\mbox{Non-called~E.{\_}{\_}metaclass{\_}{\_}:~<unbound~method~E.{\_}{\_}metaclass{\_}{\_}>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The advantage/disadvantage of this solution is that the \texttt{{\_}{\_}metaclass{\_}{\_}}
+hook is called only once, i.e. it is not called again if a new class
+is derived from the original one. For instance in this example 'E' is
+derived from 'B', but the function \texttt{B.{\_}{\_}metaclass{\_}{\_}} is \emph{not} called
+during the creation of 'E'.
+
+Metafunctions can also be used when one does not want to transmit the
+metaclass contraint. Therefore they usage is convenient in exactly
+the opposite situation of a cooperative metaclass.
+
+
+%___________________________________________________________________________
+
+\hypertarget{anonymous-cooperative-super-calls}{}
+\pdfbookmark[1]{Anonymous cooperative super calls}{anonymous-cooperative-super-calls}
+\subsection*{Anonymous cooperative super calls}
+
+As I noticed in the previous chapters, the \texttt{super}
+mechanism has an annoying
+problem: one needs to pass explicitely the name of the base class. Typically,
+this is simply an
+inelegance since it is annoying to be forced to retype the name of the base
+class. However, in particular
+cases, it can be a problem. This happens for instance if we try to
+pass the class's methods to a different class: one cannot do that,
+since the methods contains an explicit reference to the original class
+and would not work with the new one. Moreover, having named super calls
+is annoying in view of refactoring. Consider for
+instance the previous \texttt{supernew.py} script: in the \texttt{{\_}{\_}new{\_}{\_}} method
+defined inside the class 'B', we called \texttt{Super} with the syntax
+\texttt{Super(B,cls)} by repeating the name of the class 'B'. Now,
+if in the following I decide to give to 'B' a more descriptive
+name, I have to go trough the source, search all the \texttt{super}
+calls, and change them accordingly to the new name. It would be
+nice having Python do the job for me. A first solution is to call
+\texttt{super} (or \texttt{Super}) with the syntax \texttt{super(self.{\_}{\_}this,obj)},
+where the special name \texttt{{\_}{\_}this} is explicitly replaced by the name
+of the class where the call is defined by the 'reflective' function
+of last chapter. This approach has the disadvantage that each time we
+derive a new class, we need to invoke \emph{explicitely} the routine
+\texttt{reflective}. It would be marvelous to instruct Python to invoke
+\texttt{reflective} automatically at each class creation. Actually, this
+seems to be deep magic and indeed it is: fortunately, a custom metaclass
+can perform this deep magic in few lines:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{~~}\\
+\mbox{class~Reflective(type):}\\
+\mbox{~~~~"""Cooperative~metaclass~that~defines~the~private~variable~{\_}{\_}this~in}\\
+\mbox{~~~~its~instances.~{\_}{\_}this~contains~a~reference~to~the~class,~therefore}\\
+\mbox{~~~~it~allows~anonymous~cooperative~super~calls~in~the~class."""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~super(Reflective,cls).{\_}{\_}init{\_}{\_}(*args)}\\
+\mbox{~~~~~~~~reflective(cls)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now, let me show how 'Reflective' can be used in a practical example.
+
+By deriving new metaclasses from 'Reflective', one can easily
+create powerful class factories that generate reflective classes.
+
+Suppose I want to define a handy class
+factory with the abilitity of counting the number of its instances.
+
+This can be done by noticing that metaclasses are just classes, therefore
+they can be composed with regular classes in multiple inheritance. In
+particular one can derive a 'Logged' metaclass from 'WithLogger': in
+this way we send a message to a log file each time a new class is created.
+This can be done by composing 'WithLogger' with 'WithMultiCounter.{\_}{\_}metaclass{\_}{\_}'
+and with 'Reflective':
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Logged(WithLogger,Reflective):~}\\
+\mbox{~~~~"""Metaclass~that~reuses~the~features~provided~by~WithLogger.~In~particular}\\
+\mbox{~~~~the~classes~created~by~Logged~are~Reflective,~PrettyPrinted~}\\
+\mbox{~~~~and~Customizable."""~{\#}WithLogger~provides~logfile~and~verboselog}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args,**kw):}\\
+\mbox{~~~~~~~~super(Logged,cls).{\_}{\_}init{\_}{\_}(*args,**kw)~}\\
+\mbox{~~~~~~~~bases=','.join([c.{\_}{\_}name{\_}{\_}~for~c~in~cls.{\_}{\_}bases{\_}{\_}])}\\
+\mbox{~~~~~~~~print~>>~cls.logfile,~"{\%}s~is~a~child~of~{\%}s"~{\%}~(cls,bases)}\\
+\mbox{~~~~~~~~print~>>~cls.logfile,'and~an~instance~of~{\%}s'~{\%}~type(cls).{\_}{\_}name{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The MRO is
+\begin{quote}
+\begin{verbatim}>>> print MRO(Logged)
+MRO of Logged:
+ 0 - Logged(WithLogger,Reflective)
+ 1 - WithLogger(WithCounter,PrettyPrinted)
+ 2 - WithCounter(object)
+ 3 - PrettyPrinted(object)
+ 4 - Reflective(type)
+ 5 - type(object)
+ 6 - object()\end{verbatim}
+\end{quote}
+
+and the inheritance graph can be drawn as follows:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~object~6~{\_}{\_}{\_}}\\
+\mbox{~~~~~~/~~~~~~~~~~~~~~~~~~~~~~~~/~~~~~~~~~{\textbackslash}}\\
+\mbox{2~WithCounter~~~~~~~~3~PrettyPrinted~~~~~~~~type~5}\\
+\mbox{~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~{\textbackslash}~~~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~{\textbackslash}~~~~~~/~~~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~1~WithLogger~~~~~~~Reflective~4}\\
+\mbox{~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~{\textbackslash}~~~~~/}\\
+\mbox{~~~~~~~~~~~~~~~~Logged~0}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~:}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~:}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~C1}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+'WithCounter' acts now as a metaclass, since WithCounter.{\_}{\_}new{\_}{\_} invokes
+type.{\_}{\_}new{\_}{\_}. Since \texttt{type.{\_}{\_}new{\_}{\_}} is non-cooperative,
+in the composition of a metaclass with a regular class, the metaclass
+should be put first: this guarantees that \texttt{{\_}{\_}new{\_}{\_}} derives from
+\texttt{type.{\_}{\_}new{\_}{\_}}, thus avoiding the error message.
+\begin{quote}
+\begin{verbatim}>>> Logged.verboselog=True
+>>> C1=Logged('C1',(),{})
+*****************************************************************************
+Tue Apr 22 18:47:05 2003
+1. Created 'C1'
+with accessibile non-special attributes:
+_C1__this = 'C1'
+'C1' is a child of object
+and an instance of Logged\end{verbatim}
+\end{quote}
+
+Notice that any instance of 'WithCounterReflective' inherits the 'WithCounter'
+attribute \texttt{counter}, that counts the number of classes that have been
+instantiated (however it is not retrieved by \texttt{dir}; moreover the
+instances of 'WithCounterReflective' instances have no \texttt{counter} attribute).
+\begin{quote}
+\begin{verbatim}>>> C1.counter
+1\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{more-on-metaclasses-as-class-factories}{}
+\pdfbookmark[1]{More on metaclasses as class factories}{more-on-metaclasses-as-class-factories}
+\subsection*{More on metaclasses as class factories}
+
+A slight disadvantage of the approach just described,
+is that 'Logged' cooperatively invokes the \texttt{type.{\_}{\_}new{\_}{\_}}
+static method, therefore, when we invoke the metaclass, we must explicitly
+provide a name, a tuple of base classes and a dictionary, since the
+\texttt{type.{\_}{\_}new{\_}{\_}} staticmethod requires that signature. Actually,
+the expression
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{C=Logged(name,bases,dic)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+is roughly syntactic sugar for
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{C=Logged.{\_}{\_}new{\_}{\_}(Logged,name,bases,dic)~}\\
+\mbox{assert~isinstance(C,Logged)}\\
+\mbox{Logged.{\_}{\_}init{\_}{\_}(C,name,bases,dic)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+If a different interface is desired, the best way is to use a class
+factory 'ClsFactory' analogous to the object factory 'Makeobj'
+defined in chapter 4. It is convenient to make 'ClsFactory'
+bracket-callable.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~ClsFactory(BracketCallable):}\\
+\mbox{~~~~"""Bracket~callable~non-cooperative~class~acting~as~}\\
+\mbox{~~~~a~factory~of~class~factories.}\\
+\mbox{}\\
+\mbox{~~~~ClsFactory~instances~are~class~factories~accepting~0,1,2~or~3~arguments.~}\\
+\mbox{~~.~They~automatically~converts~functions~to~static~methods~}\\
+\mbox{~~~~if~the~input~object~is~not~a~class.~If~an~explicit~name~is~not~passed}\\
+\mbox{~~~~the~name~of~the~created~class~is~obtained~by~adding~an~underscore~to~}\\
+\mbox{~~~~the~name~of~the~original~object."""}\\
+\mbox{~~~~}\\
+\mbox{~~~~returnclass=False~{\#}~ClsFactory[X]~returns~an~*instance*~of~ClsFactory}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,~*args):}\\
+\mbox{~~~~~~~~"""Generates~a~new~class~using~self.meta~and~avoiding~conflicts.}\\
+\mbox{~~~~~~~~The~first~metaobject~can~be~a~dictionary,~an~object~with~a}\\
+\mbox{~~~~~~~~dictionary~(except~a~class),~or~a~simple~name."""}\\
+\mbox{~~~~~~~~}\\
+\mbox{~~~~~~~~{\#}~default~attributes}\\
+\mbox{~~~~~~~~self.name="CreatedWithClsFactory"~~~~}\\
+\mbox{~~~~~~~~self.bases=()}\\
+\mbox{~~~~~~~~self.dic={\{}{\}}}\\
+\mbox{~~~~~~~~self.metas=self.bracket{\_}args}\\
+\mbox{}\\
+\mbox{~~~~~~~~if~len(args)==1:}\\
+\mbox{~~~~~~~~~~~~arg=args[0]}\\
+\mbox{~~~~~~~~~~~~if~isinstance(arg,str):~{\#}~is~a~name~}\\
+\mbox{~~~~~~~~~~~~~~~~self.name=arg}\\
+\mbox{~~~~~~~~~~~~elif~hasattr(arg,'{\_}{\_}name{\_}{\_}'):~{\#}~has~a~name}\\
+\mbox{~~~~~~~~~~~~~~~~self.name=arg.{\_}{\_}name{\_}{\_}+'{\_}'}\\
+\mbox{~~~~~~~~~~~~self.setbasesdic(arg)}\\
+\mbox{~~~~~~~~elif~len(args)==2:~}\\
+\mbox{~~~~~~~~~~~~self.name=args[0]~}\\
+\mbox{~~~~~~~~~~~~assert~isinstance(self.name,str)~{\#}~must~be~a~name}\\
+\mbox{~~~~~~~~~~~~self.setbasesdic(args[1])}\\
+\mbox{~~~~~~~~elif~len(args)==3:~{\#}~must~be~name,bases,dic}\\
+\mbox{~~~~~~~~~~~~self.name=args[0]}\\
+\mbox{~~~~~~~~~~~~self.bases+=args[1]}\\
+\mbox{~~~~~~~~~~~~self.dic.update(args[2])}\\
+\mbox{~~~~~~~~if~len(args)<3~and~not~self.bases:~{\#}~creating~class~from~a~non-class}\\
+\mbox{~~~~~~~~~~~~for~k,v~in~self.dic.iteritems():}\\
+\mbox{~~~~~~~~~~~~~~~~if~isfunction(v):~self.dic[k]=staticmethod(v)}\\
+\mbox{~~~~~~~~{\#}return~child(*self.bases,**vars(self))}\\
+\mbox{~~~~~~~~return~makecls(*self.metas)(self.name,self.bases,self.dic)}\\
+\mbox{}\\
+\mbox{~~~~def~setbasesdic(self,obj):}\\
+\mbox{~~~~~~~~if~isinstance(obj,tuple):~{\#}~is~a~tuple}\\
+\mbox{~~~~~~~~~~~~self.bases+=obj}\\
+\mbox{~~~~~~~~elif~hasattr(obj,'{\_}{\_}bases{\_}{\_}'):~{\#}~is~a~class}\\
+\mbox{~~~~~~~~~~~~self.bases+=obj.{\_}{\_}bases{\_}{\_}}\\
+\mbox{~~~~~~~~if~isinstance(obj,dict):~{\#}~is~a~dict}\\
+\mbox{~~~~~~~~~~~~self.dic.update(obj)}\\
+\mbox{~~~~~~~~elif~hasattr(obj,"{\_}{\_}dict{\_}{\_}"):~{\#}~has~a~dict}\\
+\mbox{~~~~~~~~~~~~self.dic.update(obj.{\_}{\_}dict{\_}{\_})}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+'ClsFactory[X]' where 'X' is a metaclass returns callable objects acting as
+class factories. For instance
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{Class=ClsFactory[type]~{\#}~generates~non-conflicting~classes}\\
+\mbox{Mixin=ClsFactory[Reflective]~{\#}~generates~reflective~classes}\\
+\mbox{~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+can be used as a class factories that automatically provides a default name,
+base classes and dictionary, and avoids meta-type conflicts.
+'Mixin' generates reflective classes that can be used as mixin in multiple
+inheritance hierarchies. Here I give few example of usage of 'Class':
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> C1,C2,C3=[Class('C'+str(i+1)) for i in range(3)]
+>>> C1
+<class 'oopp.C1'>
+>>> C2
+<class 'oopp.C2'>
+>>> C3
+<class 'oopp.C3'>]\end{verbatim}
+\begin{verbatim}>>> Clock=Class('Clock',{'get_time':get_time})
+>>> Clock
+<class 'oopp.Clock'>
+>>> Clock.get_time()
+16:01:02\end{verbatim}
+\end{quote}
+
+Another typical usage of 'Class' is the conversion of a module in a class:
+for instance
+\begin{quote}
+\begin{verbatim}>>> time_=Class(time)
+>>> time_
+<class 'oopp.time_'>\end{verbatim}
+\end{quote}
+
+Notice the convention of adding an underscore to the name of the class
+generated from the 'time' module.
+\begin{quote}
+\begin{verbatim}>>> time_.asctime()
+'Mon Jan 20 16:33:21 2003'\end{verbatim}
+\end{quote}
+
+Notice that all the functions in the module \texttt{time} has been magically
+converted in staticmethods of the class \texttt{time{\_}}. An advantage of this
+approach is that now the module is a class and can be enhanced with
+metaclasses: for instance we could add tracing capabilities, debugging
+features, etc.
+
+By design, 'Class' and 'Reflective' also works when the first argument
+is a class or a tuple of base classes:
+\begin{quote}
+\begin{verbatim}>>> ClsFactory_=Class(ClsFactory)
+>>> type(ClsFactory_)
+<class 'oopp.__metaclass__'>
+>>> ClsFactory_=Mixin(ClsFactory)
+>>> type(ClsFactory_) # automagically generated metaclass
+<class 'oopp._Reflective__metaclass__'>\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{programming-with-metaclasses}{}
+\pdfbookmark[1]{Programming with metaclasses}{programming-with-metaclasses}
+\subsection*{Programming with metaclasses}
+
+In order to how a non-trivial application of metaclasses in real life,
+let me come back to the pizza shop example discussed in chapter 4 and 6.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~Pizza(toppings,**dic):~}\\
+\mbox{~~~~~"""This~function~produces~classes~inheriting~from~GenericPizza~and~}\\
+\mbox{~~~~~WithLogger,~using~a~metaclass~inferred~from~Logged"""}\\
+\mbox{~~~~~toppinglist=toppings.split()}\\
+\mbox{~~~~~name='Pizza'+''.join([n.capitalize()~for~n~in~toppinglist])}\\
+\mbox{~~~~~dic['toppinglist']=toppinglist}\\
+\mbox{~~~~~return~ClsFactory[Logged](name,}\\
+\mbox{~~~~~~~~~~~~(GenericPizza,WithLogger,WithMultiCounter),dic)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~from~oopp~import~*}\\
+\mbox{>>>~Margherita=Pizza('tomato~mozzarella',verboselog=True)}\\
+\mbox{*****************************************************************************}\\
+\mbox{Tue~May~13~14:42:17~2003}\\
+\mbox{1.~Created~'PizzaTomatoMozzarella'}\\
+\mbox{with~accessibile~non-special~attributes:}\\
+\mbox{ResetsCounter~=~<class~'oopp.ResetsCounter'>}\\
+\mbox{{\_}GenericPizza{\_}{\_}this~=~<class~'oopp.GenericPizza'>}\\
+\mbox{{\_}WithCounter{\_}{\_}this~=~<class~'oopp.WithCounter'>}\\
+\mbox{{\_}WithLogger{\_}{\_}this~=~<class~'oopp.WithLogger'>}\\
+\mbox{baseprice~=~1}\\
+\mbox{counter~=~0}\\
+\mbox{formatstring~=~{\%}s}\\
+\mbox{logfile~=~<open~file~'<stdout>',~mode~'w'~at~0x402c2058>}\\
+\mbox{price~=~<unbound~method~PizzaTomatoMozzarella.price>}\\
+\mbox{sizefactor~=~{\{}'small':~1,~'large':~3,~'medium':~2{\}}}\\
+\mbox{topping{\_}unit{\_}price~=~0.5}\\
+\mbox{toppinglist~=~['tomato',~'mozzarella']}\\
+\mbox{toppings{\_}price~=~<unbound~method~PizzaTomatoMozzarella.toppings{\_}price>}\\
+\mbox{verboselog~=~True}\\
+\mbox{'PizzaTomatoMozzarella'~is~a~child~of~GenericPizza,WithLogger,}\\
+\mbox{WithMultiCounter~and~an~instance~of~{\_}LoggedResetsCounter}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice the \emph{deep} magic: \texttt{Pizza} invokes \texttt{ClsFactory[Logged]} which in
+turns calls the class factory \texttt{child} that creates 'Margherita' from
+'GenericPizza', 'WithLogger' and 'WithMultiCounter' by using the
+metaclass 'Logged': however, since 'WithMultiCounter', has the internal
+metaclass 'ResetsCounter' , there is a metatype conflict:
+\texttt{child} \emph{automagically} solves the conflict by creating the metaclass
+'{\_}LoggedResetsCounter' that inherits both from 'Logged' and 'ResetsCounter'.
+At this point, 'Margherita' can be safely created
+by '{\_}LoggedResetsCounter'. As such, the creation of 'Margherita'
+will be registered in the log file and 'Margherita' (with all its
+children) will continue to be able to recognize the special identifier
+\texttt{this}.
+\begin{quote}
+\begin{verbatim}>>> print Margherita('large')
+*****************************************************************************
+Tue May 13 14:47:03 2003
+1. Created large pizza with tomato,mozzarella, cost $ 6.0
+with accessibile non-special attributes:
+ResetsCounter = <class 'oopp.ResetsCounter'>
+_GenericPizza__this = <class 'oopp.GenericPizza'>
+_WithCounter__this = <class 'oopp.WithCounter'>
+_WithLogger__this = <class 'oopp.WithLogger'>
+baseprice = 1
+counter = 1
+formatstring = %s
+logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+price = <bound method PizzaTomatoMozzarella.price of
+<oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+size = large
+sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+topping_unit_price = 0.5
+toppinglist = ['tomato', 'mozzarella']
+toppings_price = <bound method PizzaTomatoMozzarella.toppings_price of
+<oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+verboselog = True
+large pizza with tomato,mozzarella, cost $ 6.0
+>>> print MRO(Margherita)
+MRO of PizzaTomatoMozzarella:
+ 0 - PizzaTomatoMozzarella(GenericPizza,WithLogger)[_LoggedResetsCounter]
+ 1 - GenericPizza(object)
+ 2 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 3 - WithMultiCounter(WithCounter)[ResetsCounter]
+ 4 - WithCounter(object)
+ 5 - PrettyPrinted(object)
+ 6 - object()\end{verbatim}
+\end{quote}
+
+Notice that
+\begin{quote}
+\begin{verbatim}>>> print Margherita
+'PizzaTomatoMozzarella'\end{verbatim}
+\end{quote}
+
+The power of inheritance in this example is quite impressive, since
+I have reused the same class 'WithLogger' (and its children) both in the
+metaclass hierarchy and in the regular hierarchy: this means that I have added
+logging capabilities both to classes and their instances in a
+strike! And there is no confusion between the two. For instance,
+there is a \texttt{counter} attribute for the metaclass 'Logged'
+and many independent \texttt{counter} attributes for any generated class,
+i.e. for any kind of pizza.
+\begin{quote}
+
+It is interesting to notice that '' itself is an instance of
+its inner metaclass, as \texttt{type()} would show. This technique
+avoids the need for inventing a new name for the metaclass. The inner
+metaclass is automatically inherited by classes inheriting from the outer
+class.
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclass-aided-operator-overloading}{}
+\pdfbookmark[1]{Metaclass-aided operator overloading}{metaclass-aided-operator-overloading}
+\subsection*{Metaclass-aided operator overloading}
+
+As we discussed in chapter 4, inheriting from built-in types is generally
+painful. The problem is that if P is a primitive class, i.e. a
+Python built-in type, and D=D(P) is a derived class, then the
+primitive methods returning P-objects have to be modified (wrapped) in
+such a way to return D-objects.
+
+The problem is expecially clear in the context of operator overloading.
+
+Consider for instance the problem of defining a 'Vector' class in the
+mathematical sense. Mathematically-speaking, vectors are defined as objects
+that can be summed each other and multiplied by numbers; they can be represented
+by (finite or infinite) sequences. In the case of finite sequences, vectors
+can be represented with lists and a vector class can be naturally
+implemented by subclassing \texttt{list}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<vector.py>}\\
+\mbox{}\\
+\mbox{class~Vector(list):}\\
+\mbox{~~~~"""Implements~finite~dimensional~vectors~as~lists.~Can~be~instantiated}\\
+\mbox{~~~~as~Vector([a,b,c,..])~or~as~Vector(a,b,c~..)"""}\\
+\mbox{~~~~def~{\_}{\_}add{\_}{\_}(self,other):}\\
+\mbox{~~~~~~~~return~[el+other[i]~for~i,el~in~enumerate(self)]}\\
+\mbox{~~~~{\_}{\_}radd{\_}{\_}={\_}{\_}add{\_}{\_}}\\
+\mbox{~~~~def~{\_}{\_}mul{\_}{\_}(self,scalar):}\\
+\mbox{~~~~~~~~return~[el*scalar~for~el~in~self]}\\
+\mbox{~~~~def~{\_}{\_}rmul{\_}{\_}(self,scalar):}\\
+\mbox{~~~~~~~~return~[scalar*el~for~el~in~self]}\\
+\mbox{}\\
+\mbox{v=Vector([1,0])}\\
+\mbox{w=Vector([0,1])}\\
+\mbox{}\\
+\mbox{print~v+w,~type(v+w)~}\\
+\mbox{print~2*v,~type(2*v)~}\\
+\mbox{print~v*2,~type(v*2)~}\\
+\mbox{}\\
+\mbox{{\#}</vector.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[1,~1]~<type~'list'>}\\
+\mbox{[2,~0]~<type~'list'>}\\
+\mbox{[2,~0]~<type~'list'>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The problem is that the overloaded methods must be wrapped in such a way
+to return \texttt{Vector} object and not \texttt{list} object; moreover, if
+\texttt{Vector} is subclassed (for instance by defining a \texttt{NumericVector}),
+the overloaded methods must return instances of the subclass. There is
+only one way of doing that automatically: trough the magic of metaclasses.
+
+Here is the solution, involving an \texttt{autowrappedmethod} descriptor class,
+that wraps the overloaded operators and is automatically invoked by
+the metaclass \texttt{AutoWrapped}.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~autowrappedmethod(wrappedmethod):}\\
+\mbox{~~~~"""Makes~the~method~returning~cls~instances,~by~wrapping~its}\\
+\mbox{~~~~output~with~cls"""}\\
+\mbox{~~~~klass=None~{\#}~has~to~be~fixed~dynamically~from~outside}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,meth):}\\
+\mbox{~~~~~~~~super(autowrappedmethod,self).{\_}{\_}init{\_}{\_}(meth)~{\#}~cooperative}\\
+\mbox{~~~~~~~~self.klass=self.klass~{\#}~class~variable~->~instance~variable}\\
+\mbox{~~~~def~wrapper(self):~{\#}~closure}\\
+\mbox{~~~~~~~~return~lambda~*args,**kw:~self.klass(self.func(*args,**kw))}\\
+\mbox{}\\
+\mbox{class~AutoWrapped(type):}\\
+\mbox{~~~~"""Metaclass~that~looks~at~the~methods~declared~in~the~attributes~}\\
+\mbox{~~~~builtinlist~and~wraplist~of~its~instances~and~wraps~them~with}\\
+\mbox{~~~~autowrappedmethod."""}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,dic):}\\
+\mbox{~~~~~~~~super(AutoWrapped,cls).{\_}{\_}init{\_}{\_}(name,bases,dic)~{\#}~cooperative}\\
+\mbox{~~~~~~~~cls.builtinlist=getattr(cls,'builtinlist',[])}\\
+\mbox{~~~~~~~~if~not~hasattr(cls,'diclist')~:~{\#}~true~only~at~the~first~call}\\
+\mbox{~~~~~~~~~~~~cls.diclist=[(a,vars(bases[0])[a])~for~a~in~cls.builtinlist]}\\
+\mbox{~~~~~~~~if~dic.has{\_}key('wraplist'):~{\#}~can~be~true~at~any~call}\\
+\mbox{~~~~~~~~~~~~cls.diclist+=[(a,dic[a])~for~a~in~cls.wraplist]~}\\
+\mbox{~~~~~~~~wrapper=autowrappedmethod.With(klass=cls)}\\
+\mbox{~~~~~~~~d=dict([(a,wrapper(v))~for~a,v~in~cls.diclist])}\\
+\mbox{~~~~~~~~customize(cls,**d)}\\
+\mbox{~~~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now the \texttt{Vector} class can be written as
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Vector(list):}\\
+\mbox{~~~~"""Implements~finite~dimensional~vectors~as~lists.~Can~be~instantiated}\\
+\mbox{~~~~as~Vector([a,b,c,..])~or~as~Vector(a,b,c~..)"""}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=AutoWrapped}\\
+\mbox{~~~~wraplist='{\_}{\_}add{\_}{\_}~{\_}{\_}radd{\_}{\_}~{\_}{\_}mul{\_}{\_}~{\_}{\_}rmul{\_}{\_}'.split()}\\
+\mbox{~~~~def~{\_}{\_}add{\_}{\_}(self,other):}\\
+\mbox{~~~~~~~~return~[el+other[i]~for~i,el~in~enumerate(self)]}\\
+\mbox{~~~~{\_}{\_}radd{\_}{\_}={\_}{\_}add{\_}{\_}}\\
+\mbox{~~~~def~{\_}{\_}mul{\_}{\_}(self,scalar):}\\
+\mbox{~~~~~~~~return~[scalar*el~for~el~in~self]}\\
+\mbox{~~~~def~{\_}{\_}rmul{\_}{\_}(self,scalar):}\\
+\mbox{~~~~~~~~return~[el*scalar~for~el~in~self]}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here the \texttt{AutoWrapped} metaclass wraps the output of \texttt{{\_}{\_}add{\_}{\_},
+{\_}{\_}radd{\_}{\_}, {\_}{\_}mul{\_}{\_}, {\_}{\_}rmul{\_}{\_}}, guaranteeing that they returns \texttt{Vector}
+instances or instances of some subclass of \texttt{Vector}, if \texttt{Vector} is
+subclassed. This is an example of usage:
+\begin{quote}
+% doctest
+\begin{verbatim}>>> from oopp import Vector
+>>> v=Vector([1,0])
+>>> v
+<oopp.Vector object at 0x4032858c>
+>>> w=Vector([0,1])
+>>> v+2*w
+<oopp.Vector object at 0x403190ac>
+>>> print v+2*w
+[1, 2]\end{verbatim}
+\end{quote}
+
+It should be clear by now that metaclasses are the natural framework where
+to discuss operator overloading
+(at least in languages that have metaclasses ;-). After all, operator
+overloading is another kind of (very nice) syntactic sugar and we know
+already that metaclasses are very good when we need syntactic sugar.
+
+
+%___________________________________________________________________________
+
+\hypertarget{advanced-metaprogramming-techniques}{}
+\pdfbookmark[0]{ADVANCED METAPROGRAMMING TECHNIQUES}{advanced-metaprogramming-techniques}
+\section*{ADVANCED METAPROGRAMMING TECHNIQUES}
+
+In elementary OOP, the programmer works with objects; in advanced OOP,
+the programmer works with classes, taking full advantage of
+(multiple) inheritance and metaclasses. Metaprograming is the activity of
+building, composing and modifying classes.
+
+I will give various examples of metaprogramming techniques
+using run-time class modifications
+multiple inheritance, metaclasses, attribute descriptors and
+even simple functions.
+
+Moreover, I will show show metaclasses can change the
+semantics of Python programs: hence theire reputation of \emph{black} magic.
+That is to say that the techniques explained here are dangerous!
+
+
+%___________________________________________________________________________
+
+\hypertarget{on-code-processing}{}
+\pdfbookmark[1]{On code processing}{on-code-processing}
+\subsection*{On code processing}
+
+It is a good programming practice to avoid the direct modification
+of source code. Nevertheless, there are situations where the ability of
+modifying the source code \emph{dynamically} is invaluable. Python has the
+capability of
+\newcounter{listcnt31}
+\begin{list}{\arabic{listcnt31})}
+{
+\usecounter{listcnt31}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+generating new code from scratch;
+
+\item {}
+modifying pre-existing source code;
+
+\item {}
+executing the newly created/modified code at run-time.
+
+\end{list}
+
+The capability of creating source code and executing it \emph{immediately} has
+no equivalent in static languages such as C/C++/Java and it is maybe the
+most poweful feature of dynamics languages such as Java/Python/Perl.
+This feature has been exploited to its ultimate consequences in the languages
+of the Lisp family, in which one can use incredibly poweful macros, which
+in a broad sense, are programs that write themselves
+
+In this chapter I will discuss how to implement macros in Python and I will
+present some of the miracles you may perform with this technique. To this
+aim, I will discuss various ways of manipulating Python source code, by
+using regular expressions and state machines.
+
+
+%___________________________________________________________________________
+
+\hypertarget{regular-expressions}{}
+\pdfbookmark[1]{Regular expressions}{regular-expressions}
+\subsection*{Regular expressions}
+\begin{quote}
+\begin{flushleft}
+\emph{Some~people,~when~confronted~with~a~problem,~~\\
+think~''I~know,~I'll~use~regular~expressions.``~~\\
+Now~they~have~two~problems.}~\\
+~~~--~Jamie~Zawinski
+\end{flushleft}
+\end{quote}
+
+Python source code is a kind of text and can manipulated with the same
+techniques that are used to manipulate text:
+\newcounter{listcnt32}
+\begin{list}{\arabic{listcnt32}.}
+{
+\usecounter{listcnt32}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+the trivial search and replace;
+
+\item {}
+regular expressions;
+
+\item {}
+state machines;
+
+\item {}
+parsers
+
+\end{list}
+
+There is not very much to say about the search and replace methods: it
+is fast, efficient and it works. It should always be used whenever
+possible. However, in this chapter I will only be interested in cases
+where something more sophisticated than a plain search and replace is
+needed. Cases that can be managed with regular expressions
+or with something even more sophisticated than them: a state machine or
+even a full featured parser.
+
+I will \emph{not} give a primer on regular expression here, since they are
+already well documented in the standard documentation (see Andrew's
+Kuchling 'Howto') as well in many books (for instance 'Mastering Regular
+Expression' first edition and 'Python in a Nutshell').
+Instead, I will give various practical examples of usage.
+\begin{quote}
+\begin{verbatim}>>> import re
+>>> reobj=re.compile(r'x')\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{more-on-metaclasses-and-subclassing-built-in-types}{}
+\pdfbookmark[1]{More on metaclasses and subclassing built-in types}{more-on-metaclasses-and-subclassing-built-in-types}
+\subsection*{More on metaclasses and subclassing built-in types}
+
+Subclassing \texttt{list} is easy since there are no methods returning lists
+except the methods correspondings to the '+' and '*' operators.
+Subclassing \texttt{str} is more complicated, since one has many methods
+that return strings. Nevertheless, it can be done with the \texttt{AutoWrapped}
+metaclass, simply by specifying the list of the builtins to be wrapped.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Str(str):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=AutoWrapped}\\
+\mbox{~~~~builtinlist="""{\_}{\_}add{\_}{\_}~{\_}{\_}mod{\_}{\_}~{\_}{\_}mul{\_}{\_}~{\_}{\_}rmod{\_}{\_}~{\_}{\_}rmul{\_}{\_}~capitalize}\\
+\mbox{~~~~~~~~center~expandtabs~join~ljust~lower~lstrip~replace~rjust~rstrip~strip}\\
+\mbox{~~~~~~~~swapcase~title~translate~upper~zfill""".split()}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here I show various tests.
+\begin{quote}
+% doctest
+\begin{verbatim}>>> from oopp import Str
+>>> sum=Str('a')+Str('b') # check the sum
+>>> print sum, type(sum)
+ab <class 'oopp.Str'>
+>>> rprod=Str('a')*2 # check the right product
+>>> print rprod,type(rprod)
+aa <class 'oopp.Str'>
+>>> lprod=2*Str('a') # check the left product
+>>> print lprod,type(lprod)
+aa <class 'oopp.Str'>
+>>> r=Str('a').replace('a','b') # check replace
+>>> print r,type(r)
+b <class 'oopp.Str'>
+>>> r=Str('a').capitalize() # check capitalize
+>>> print r,type(r)
+A <class 'oopp.Str'>\end{verbatim}
+\end{quote}
+
+\texttt{Str} acts as a nice base class to built abstractions based on strings.
+In particular, regular expressions can be built on top of strings describing
+their representation (I remind that if \texttt{x} is a regular expression object,
+\texttt{x.pattern} is its string representation). Then, the sum of two regular
+expressions \texttt{x} and \texttt{y} can be defined as the sum of their string
+representation, \texttt{(x+y).pattern=x.pattern+y.pattern}. Moreover, it is
+convenient to define the \texttt{{\_}{\_}or{\_}{\_}} method of two regular expression in
+such a way that \texttt{(x | y).pattern=x.pattern+'|'+y.pattern}.
+All this can be achieved trough the following class:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~BaseRegexp(Str):}\\
+\mbox{}\\
+\mbox{~~~~builtinlist=['{\_}{\_}radd{\_}{\_}',~'{\_}{\_}ror{\_}{\_}']}\\
+\mbox{~~~~wraplist=['{\_}{\_}add{\_}{\_}','{\_}{\_}or{\_}{\_}']}\\
+\mbox{}\\
+\mbox{~~~~{\_}{\_}add{\_}{\_}~=~lambda~self,other:~self.pattern~+~other~}\\
+\mbox{~~~~{\_}{\_}or{\_}{\_}~~=~lambda~self,other:~self.pattern+'|'+other}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}~(self,regexp):}\\
+\mbox{~~~~~~~~"Adds~to~str~methods~the~regexp~methods"}\\
+\mbox{~~~~~~~~reobj=re.compile(regexp)}\\
+\mbox{~~~~~~~~for~attr~in~dir(reobj)+['pattern']:}\\
+\mbox{~~~~~~~~~~~~setattr(self,attr,getattr(reobj,attr))}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{>>>~from~oopp~import~*}\\
+\mbox{>>>~aob=BaseRegexp('a')|BaseRegexp('b');~print~aob}\\
+\mbox{a|b}\\
+\mbox{>>>~print~pretty(attributes(aob))}\\
+\mbox{encode~=~<built-in~method~encode~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{endswith~=~<built-in~method~endswith~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{expandtabs~=~<function~{\_}~at~0x401b69cc>}\\
+\mbox{find~=~<built-in~method~find~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{findall~=~<built-in~method~findall~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{finditer~=~<built-in~method~finditer~of~{\_}sre.SRE{\_}Pattern~object~at~}\\
+\mbox{0x4019b890>}\\
+\mbox{index~=~<built-in~method~index~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{isalnum~=~<built-in~method~isalnum~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{isalpha~=~<built-in~method~isalpha~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{isdigit~=~<built-in~method~isdigit~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{islower~=~<built-in~method~islower~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{isspace~=~<built-in~method~isspace~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{istitle~=~<built-in~method~istitle~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{isupper~=~<built-in~method~isupper~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{join~=~<function~{\_}~at~0x401b6a74>}\\
+\mbox{ljust~=~<function~{\_}~at~0x401b6b1c>}\\
+\mbox{lower~=~<function~{\_}~at~0x401b6cdc>}\\
+\mbox{lstrip~=~<function~{\_}~at~0x401b6d84>}\\
+\mbox{match~=~<built-in~method~match~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{pattern~=~ba}\\
+\mbox{replace~=~<function~{\_}~at~0x401b6ed4>}\\
+\mbox{rfind~=~<built-in~method~rfind~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{rindex~=~<built-in~method~rindex~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{rjust~=~<function~{\_}~at~0x401ba0d4>}\\
+\mbox{rstrip~=~<function~{\_}~at~0x401ba10c>}\\
+\mbox{scanner~=~<built-in~method~scanner~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{search~=~<built-in~method~search~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{split~=~<built-in~method~split~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{splitlines~=~<built-in~method~splitlines~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{startswith~=~<built-in~method~startswith~of~BaseRegexp~object~at~0x401b25cc>}\\
+\mbox{strip~=~<function~{\_}~at~0x401ba25c>}\\
+\mbox{sub~=~<built-in~method~sub~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{subn~=~<built-in~method~subn~of~{\_}sre.SRE{\_}Pattern~object~at~0x4019b890>}\\
+\mbox{swapcase~=~<function~{\_}~at~0x401ba294>}\\
+\mbox{title~=~<function~{\_}~at~0x401ba4fc>}\\
+\mbox{translate~=~<function~{\_}~at~0x401ba534>}\\
+\mbox{upper~=~<function~{\_}~at~0x401ba5dc>}\\
+\mbox{wraplist~=~['{\_}{\_}add{\_}{\_}',~'{\_}{\_}radd{\_}{\_}',~'{\_}{\_}or{\_}{\_}',~'{\_}{\_}ror{\_}{\_}']}\\
+\mbox{zfill~=~<function~{\_}~at~0x401ba614>}\\
+\mbox{}\\
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Regexp(BaseRegexp):}\\
+\mbox{~~~~class~{\_}{\_}metaclass{\_}{\_}(BaseRegexp.{\_}{\_}metaclass{\_}{\_}):}\\
+\mbox{~~~~~~~~def~{\_}{\_}setattr{\_}{\_}(cls,name,value):}\\
+\mbox{~~~~~~~~~~~~if~name==name.upper():~{\#}~all~caps~means~regexp~constant}\\
+\mbox{~~~~~~~~~~~~~~~~if~not~isinstance(value,cls):~value=cls(value)}\\
+\mbox{~~~~~~~~~~~~~~~~value.name=name~{\#}~set~regexp~name}\\
+\mbox{~~~~~~~~~~~~BaseRegexp.{\_}{\_}metaclass{\_}{\_}.{\_}{\_}setattr{\_}{\_}(cls,name,value)}\\
+\mbox{~~~~~~~~~~~~{\#}~basic~setattr}\\
+\mbox{}\\
+\mbox{~~~~def~named(self,name=None):}\\
+\mbox{~~~~~~~~name=getattr(self,'name',name)}\\
+\mbox{~~~~~~~~if~name~is~None:~raise~'Unnamed~regular~expression'}\\
+\mbox{~~~~~~~~return~self.{\_}{\_}class{\_}{\_}('(?P<{\%}s>{\%}s)'~{\%}~(name,self.pattern))}\\
+\mbox{}\\
+\mbox{~~~~generateblocks=generateblocks}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The magic of \texttt{Regexp.{\_}{\_}metaclass{\_}{\_}} allows to generate a library of
+regular expressions in an elegant way:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{r=Regexp}\\
+\mbox{}\\
+\mbox{customize(r,}\\
+\mbox{~~~~DOTALL~=r'(?s)'~,~~~~~~{\#}~starts~the~DOTALL~mode;~must~be~at~the~beginning}\\
+\mbox{~~~~NAME~~~=r'{\textbackslash}b[a-zA-Z{\_}]{\textbackslash}w*',~{\#}~plain~Python~name}\\
+\mbox{~~~~EXTNAME=r'{\textbackslash}b[a-zA-Z{\_}][{\textbackslash}w{\textbackslash}.]*',~{\#}~Python~name~with~or~without~dots}\\
+\mbox{~~~~DOTNAME=r'{\textbackslash}b[a-zA-Z{\_}]{\textbackslash}w*{\textbackslash}.[{\textbackslash}w{\textbackslash}.]*',{\#}~Python~name~with~(at~least~one)~dots}\\
+\mbox{~~~~COMMENT=r"{\#}.*?(?={\textbackslash}n)",~{\#}~Python~comment}\\
+\mbox{~~~~QUOTED1="'.+?'",~~~~~~~{\#}~single~quoted~string~'}\\
+\mbox{~~~~QUOTED2='".+?"',~~~~~~~{\#}~single~quoted~string~"}\\
+\mbox{~~~~TRIPLEQ1="'''.+?'''",~~{\#}~triple~quoted~string~'}\\
+\mbox{~~~~TRIPLEQ2='""".+?"""'~~~{\#}~triple~quoted~string~"~}\\
+\mbox{~~)}\\
+\mbox{}\\
+\mbox{r.STRING=r.TRIPLEQ1|r.TRIPLEQ2|r.QUOTED1|r.QUOTED2}\\
+\mbox{r.CODESEP=r.DOTALL+r.COMMENT.named()|r.STRING.named()}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The trick is in the redefinition of \texttt{{\_}{\_}setattr{\_}{\_}}, which magically converts
+all caps attributes in \texttt{Regexp} objects.
+
+The features of \texttt{Regexp} can be tested with the following code:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<test{\_}re.py>}\\
+\mbox{}\\
+\mbox{"""This~script~looks~at~its~own~source~code~and~extracts~dotted~names,}\\
+\mbox{i.e.~names~containing~at~least~one~dot,~such~as~object.attribute~or}\\
+\mbox{more~general~one,~such~as~obj.attr.subattr."""}\\
+\mbox{}\\
+\mbox{{\#}~Notice~that~dotted.names~in~comments~and~literal~strings~are~ignored}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{import~{\_}{\_}main{\_}{\_}}\\
+\mbox{}\\
+\mbox{text=inspect.getsource({\_}{\_}main{\_}{\_})}\\
+\mbox{}\\
+\mbox{regexp=Regexp.CODESEP|~Regexp.DOTNAME.named()}\\
+\mbox{}\\
+\mbox{print~'Using~the~regular~expression',regexp}\\
+\mbox{}\\
+\mbox{print~"I~have~found~the~following~dotted~names:{\textbackslash}n{\%}s"~{\%}~[}\\
+\mbox{~~~~MO.group()~for~MO~in~regexp.finditer(text)~if~MO.lastgroup=='DOTNAME']}\\
+\mbox{}\\
+\mbox{{\#}</test{\_}re.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Using~the~regular~expression~(?s)(?P<COMMENT>{\#}.*?(?={\textbackslash}n))|(?P<STRING>}\\
+\mbox{'''.+?'''|""".+?"""|'.+?'|".+?")|(?P<DOTNAME>[a-zA-Z{\_}]{\textbackslash}w*{\textbackslash}.[{\textbackslash}w{\textbackslash}.]*)}\\
+\mbox{I~have~found~the~following~dotted~names:}\\
+\mbox{['inspect.getsource',~'Regexp.CODESEP',~'Regexp.DOTNAME.named',~'MO.group',~}\\
+\mbox{~'dotname.finditer',~'MO.lastgroup']}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now one can define a good \texttt{CodeStr} class with replacing features
+
+Let me consider for instance the solution to the problem discussed in chapter
+4, i.e. the definition of a \texttt{TextStr} class able to indent and dedent
+blocks of text.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~codeprocess(code,TPO):~{\#}~TPO=text~processing~operator}\\
+\mbox{~~~~code=code.replace("{\textbackslash}{\textbackslash}'","{\textbackslash}x01").replace('{\textbackslash}{\textbackslash}"','{\textbackslash}x02')}\\
+\mbox{~~~~genblock,out~=~Regexp.CODESEP.generateblocks(code),[]}\\
+\mbox{~~~~for~block~in~genblock:}\\
+\mbox{~~~~~~~~out.append(TPO(block))}\\
+\mbox{~~~~~~~~out.append(genblock.next())}\\
+\mbox{~~~~return~''.join(out).replace("{\textbackslash}x01","{\textbackslash}{\textbackslash}'").replace('{\textbackslash}x02','{\textbackslash}{\textbackslash}"')}\\
+\mbox{}\\
+\mbox{def~quotencode(text):}\\
+\mbox{~~~~return~text.replace("{\textbackslash}{\textbackslash}'","{\textbackslash}x01").replace('{\textbackslash}{\textbackslash}"','{\textbackslash}x02')}\\
+\mbox{}\\
+\mbox{def~quotdecode(text):}\\
+\mbox{~~~~return~text.replace("{\textbackslash}x01","{\textbackslash}{\textbackslash}'").replace('{\textbackslash}x02','{\textbackslash}{\textbackslash}"')}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is an example of usage: replacing 'Print' with 'print' except in
+comments and literal strings.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<codeproc.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~codeprocess}\\
+\mbox{}\\
+\mbox{wrongcode=r'''}\\
+\mbox{"""Code~processing~example:~replaces~'Print'~with~'print'~except~in}\\
+\mbox{comments~and~literal~strings"""}\\
+\mbox{Print~"This~program~prints~{\textbackslash}"Hello~World!{\textbackslash}""~{\#}~look~at~this~line!}\\
+\mbox{'''}\\
+\mbox{}\\
+\mbox{fixPrint=lambda~s:~s.replace('Print','print')}\\
+\mbox{validcode=codeprocess(wrongcode,fixPrint)}\\
+\mbox{}\\
+\mbox{print~'Source~code:{\textbackslash}n',validcode}\\
+\mbox{print~'Output:{\textbackslash}n';~exec~validcode}\\
+\mbox{}\\
+\mbox{{\#}</codeproc.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Source~code:}\\
+\mbox{}\\
+\mbox{"""Code~processing~example:~replaces~'Print'~with~'print'~except~in}\\
+\mbox{comments~and~literal~strings"""}\\
+\mbox{print~"Prints~{\textbackslash}"Hello~World!{\textbackslash}""~{\#}~look~at~this~line!}\\
+\mbox{}\\
+\mbox{Output:}\\
+\mbox{}\\
+\mbox{This~program~prints~"Hello~World!"}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-simple-state-machine}{}
+\pdfbookmark[1]{A simple state machine}{a-simple-state-machine}
+\subsection*{A simple state machine}
+
+Regular expression, however powerful, are limited in scope since they
+cannot recognize recursive structures. For instance, they cannot parse
+parenthesized expression.
+
+The simplest way to parse a parenthesized expression is to use a state
+machine.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{(?:...)~non-grouping}\\
+\mbox{(?P<name>...)~}\\
+\mbox{}\\
+\mbox{(?=...)~look-ahead~}\\
+\mbox{(?!...)~negative}\\
+\mbox{(?<=...)~look-behind~}\\
+\mbox{(?<!...)~negative}\\
+\mbox{}\\
+\mbox{dec={\textbackslash}}\\
+\mbox{"""}\\
+\mbox{paren~~~=~(~..~)}\\
+\mbox{bracket~=~[~..~]}\\
+\mbox{brace~~~=~{\{}~..~{\}}}\\
+\mbox{comment~=~{\#}~..~{\textbackslash}n}\\
+\mbox{"""}\\
+\mbox{}\\
+\mbox{actions={\textbackslash}}\\
+\mbox{"""}\\
+\mbox{reobj1~~:~R'~..~(?<!{\textbackslash}{\textbackslash})'~->~Regexp(r'''~..~''')}\\
+\mbox{reobj2~~:~R"~..~(?<!{\textbackslash}{\textbackslash})"~->~Regexp(r"""~..~""")}\\
+\mbox{string1~:~(?<!')'(?!')~..~(?<!{\textbackslash}{\textbackslash})'(?!')~->~'''~..~'''}\\
+\mbox{string2~:~(?<!")"(?!")~..~(?<!{\textbackslash}{\textbackslash})"(?!")~->~"""~..~"""}\\
+\mbox{"""}\\
+\mbox{}\\
+\mbox{beg=0;~end=1}\\
+\mbox{}\\
+\mbox{string1[beg]=r"(?<!')'(?!')"~{\#}~an~isolated~single~quote}\\
+\mbox{string2[beg]=r'(?<!")"(?!")'~{\#}~an~isolated~double~quote}\\
+\mbox{string1[end]=r"(?<!{\textbackslash}{\textbackslash})'(?!')"~{\#}~ending~single~quote}\\
+\mbox{string2[end]=r'(?<!{\textbackslash}{\textbackslash})"(?!")'~{\#}~ending~double~quote}\\
+\mbox{}\\
+\mbox{reobj1[beg]=r"R'"~~~~~~~}\\
+\mbox{reobj2[beg]=r'R"'~}\\
+\mbox{reobj1[end]=string1[end]~{\#}~ending~single~quote}\\
+\mbox{reobj2[end]=string2[end]~{\#}~ending~double~quote}\\
+\mbox{}\\
+\mbox{actions={\textbackslash}}\\
+\mbox{"""}\\
+\mbox{reobj1~~:~R'~..~(?<!{\textbackslash}{\textbackslash})'~->~Regexp(r'''~..~''')}\\
+\mbox{reobj2~~:~R"~..~(?<!{\textbackslash}{\textbackslash})"~->~Regexp(r"""~..~""")}\\
+\mbox{string1~:~(?<!')'(?!')~..~(?<!{\textbackslash}{\textbackslash})'(?!')~->~'''~..~'''}\\
+\mbox{string2~:~(?<!")"(?!")~..~(?<!{\textbackslash}{\textbackslash})"(?!")~->~"""~..~"""}\\
+\mbox{"""}\\
+\mbox{}\\
+\mbox{beg={\{}{\}};~end={\{}{\}};~ls=[]}\\
+\mbox{for~line~in~decl.splitlines():}\\
+\mbox{~~~mode,rest=line.split('~:~')}\\
+\mbox{~~~s,r=rest.split('~->~')}\\
+\mbox{}\\
+\mbox{~beg[mode],end[mode]=s.split('~..~')}\\
+\mbox{~ls.append('(?P<beg{\_}{\%}s>{\%}s)'~{\%}~(mode,beg[mode]))}\\
+\mbox{~ls.append('(?P<end{\_}{\%}s>{\%}s)'~{\%}~(mode,end[mode]))}\\
+\mbox{}\\
+\mbox{~beg2[mode],end2[mode]=r.split('~..~')}\\
+\mbox{~ls.append(beg2[mode])}\\
+\mbox{~ls.append(end2[mode])}\\
+\mbox{}\\
+\mbox{delimiters='({\%}s)'~{\%}~re.compile('|'.join(ls))}\\
+\mbox{splitlist=['']+delimiters.split(source)}\\
+\mbox{for~delim,text~in~splitlist:}\\
+\mbox{~~~~delimiters.match(delim).lastgroup}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{creating-classes}{}
+\pdfbookmark[1]{Creating classes}{creating-classes}
+\subsection*{Creating classes}
+
+TODO
+
+
+%___________________________________________________________________________
+
+\hypertarget{modifying-modules}{}
+\pdfbookmark[1]{Modifying modules}{modifying-modules}
+\subsection*{Modifying modules}
+
+Metaclasses are extremely
+useful since they allows to change the behaviour of the code without
+changing the sources. For instance, suppose you have a large library written
+by others that you want to enhance in some way.
+
+Typically, it is always a bad idea to modify the sources, for many reasons:
+\begin{itemize}
+\item {}
+touching code written by others, you may introduce new bugs;
+
+\item {}
+you may have many scripts that requires the original version
+of the library, not the modified one;
+
+\item {}
+if you change the sources and then you buy the new version of the
+library, you have to change the sources again!
+
+\end{itemize}
+
+The solution is to enhance the proprierties of the library at run
+time, when the module is imported, by using metaclasses.
+
+To show a concrete example, let me consider the case of the module
+\emph{commands} in the Standard Library. This module is Unix-specific,
+and cannot be used under Windows. It would be nice to have a
+metaclass able to enhance the module in such a way that
+when it is invoked on a Windows platform, Windows specific replacement
+of the Unix functions provided in the module are used. However,
+for sake of brevity, I will only give a metaclasses that display
+a nice message in the case we are in a Window platform, without
+raising an error (one could easily implement such a behaviour,
+however).
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<recognizewindows.py>}\\
+\mbox{}\\
+\mbox{import~oopp,sys,commands}\\
+\mbox{}\\
+\mbox{class~WindowsAware(type):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):~}\\
+\mbox{~~~~~~~~if~sys.platform=='win32':~}\\
+\mbox{~~~~~~~~~~~~for~key,val~in~vars(cls).iteritems():}\\
+\mbox{~~~~~~~~~~~~~~~~if~isinstance(val,staticmethod):}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~setattr(cls,key,staticmethod(lambda~*args:~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~"Sorry,~you~are~(or~I~pretend~you~are)~on~Windows,"}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~"~you~cannot~use~the~{\%}s.module"~{\%}~cls.{\_}{\_}name{\_}{\_}))}\\
+\mbox{~~~~~~~~~~}\\
+\mbox{sys.platform="win32"~{\#}just~in~case~you~are~not~on~Windows}\\
+\mbox{}\\
+\mbox{commands=oopp.ClsFactory[WindowsAware](commands)}\\
+\mbox{}\\
+\mbox{print~commands.getoutput('date')~{\#}cannot~be~executed~on~Windows}\\
+\mbox{}\\
+\mbox{{\#}</recognizewindows.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of this script is
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{Sorry,~you~are~on~Windows,~you~cannot~use~the~commands.module}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+However, if you are on Linux and you comment out the line
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{sys.platform="win32"~}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+you will see that the script works.
+
+Notice that the line \texttt{commands=WindowsAware(commands)} actually
+converts the 'commands' module in a 'commands' class, but since
+the usage is the same, this will fool all programs using the
+commands module. In this case the class factory 'WindowsAware'
+can also be thought as a module modifier. In this sense, it is
+very useful to denote the metaclass with an \emph{adjective}.
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclasses-and-attribute-descriptors}{}
+\pdfbookmark[1]{Metaclasses and attribute descriptors}{metaclasses-and-attribute-descriptors}
+\subsection*{Metaclasses and attribute descriptors}
+
+Descriptors are especially useful in conjunction with metaclasses, since
+a custom metaclass can use them as low level tools to modify the methods
+and the attributes of its instances. This allows to implement very
+sophisticated features with few lines of code.
+
+Notice, anyway, that
+even plain old function can be thought of as of descriptors.
+
+Descriptors share at least two features with metaclasses:
+\newcounter{listcnt33}
+\begin{list}{\arabic{listcnt33}.}
+{
+\usecounter{listcnt33}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+as metaclasses, descriptors are best used as adjectives, since they
+are intended to modify and enhance standard methods and attributes, in the
+same sense metaclasses modify and enhance standard classes;
+
+\item {}
+as metaclasses, descriptors can change the \emph{semantics} of Python, i.e.
+what you see is not necessarely what you get. As such, they are a
+dangerous feature. Use them with judgement!
+
+\end{list}
+
+Now I will show a possible application of properties.
+Suppose one has a given class with various kind of
+attributes (plain methods, regular methods, static methods,
+class methods, properties and data attributes) and she wants
+to trace to access to the data attributes (notice that the motivation
+for the following problem come from a real question asked in
+comp.lang.python). Then one needs to retrieve data
+attributes from the class and convert them in properties
+controlling their access syntax. The first problem is solved
+by a simple function
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~isplaindata(a):}\\
+\mbox{~~~~"""A~data~attribute~has~no~{\_}{\_}get{\_}{\_}~or~{\_}{\_}set{\_}{\_}~attributes,~is~not}\\
+\mbox{~~~~a~built-in~function,~nor~a~built-in~method."""~}\\
+\mbox{~~~~return~not(hasattr(a,'{\_}{\_}get{\_}{\_}')~or~hasattr(a,'{\_}{\_}set{\_}{\_}')}\\
+\mbox{~~~~~~~~~~~~~~~or~isinstance(a,BuiltinMethodType)~or}\\
+\mbox{~~~~~~~~~~~~~~~isinstance(a,BuiltinFunctionType))}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+whereas the second problem is elegantly solved by a custom metaclass:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<tracedaccess.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~isplaindata,inspect}\\
+\mbox{}\\
+\mbox{class~TracedAccess(type):}\\
+\mbox{~~~~"Metaclass~converting~data~attributes~to~properties"}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,dic):}\\
+\mbox{~~~~~~~~cls.datadic={\{}{\}}}\\
+\mbox{~~~~~~~~for~a~in~dic:}\\
+\mbox{~~~~~~~~~~~~if~isplaindata(a):}\\
+\mbox{~~~~~~~~~~~~~~~~cls.datadic[a]=dic[a]}\\
+\mbox{~~~~~~~~~~~~~~~~def~get(self,a=a):}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~v=cls.datadic[a]}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~print~"Accessing~{\%}s,~value={\%}s"~{\%}~(a,v)}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~return~v}\\
+\mbox{~~~~~~~~~~~~~~~~def~set(self,v,a=a):}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~print~"Setting~{\%}s,~value={\%}s"~{\%}~(a,v)}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~cls.datadic[a]=v}\\
+\mbox{~~~~~~~~~~~~~~~~setattr(cls,a,property(get,set))}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}~=~TracedAccess}\\
+\mbox{~~~~a1='x'}\\
+\mbox{}\\
+\mbox{class~D(C):~{\#}~shows~that~the~approach~works~well~with~inheritance}\\
+\mbox{~~~~a2='y'}\\
+\mbox{}\\
+\mbox{i=D()}\\
+\mbox{i.a1~{\#}~=>~Accessing~a1,~value=x}\\
+\mbox{i.a2~{\#}~=>~Accessing~a2,~value=y}\\
+\mbox{i.a1='z'~{\#}~=>~Setting~a1,~value=z}\\
+\mbox{i.a1~{\#}~=>~Accessing~a1,~value=z}\\
+\mbox{}\\
+\mbox{{\#}</tracedaccess.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this example the metaclass looks at the plain data attributes (recognized
+thanks ot the \texttt{isplaindata} function) of its instances and put them
+in the dictionary \texttt{cls.datadic}. Then the original attributes are replaced
+with property objects tracing the access to them. The solution is a 4-line
+custom metaclass doing the boring job for me:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~Wrapped(Customizable,type):}\\
+\mbox{~~~~"""A~customizable~metaclass~to~wrap~methods~with~a~given~wrapper~and}\\
+\mbox{~~~~a~given~condition"""}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=Reflective}\\
+\mbox{~~~~wrapper=wrappedmethod}\\
+\mbox{~~~~condition=lambda~k,v:~True~{\#}~wrap~all}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,*args):}\\
+\mbox{~~~~~~~~super(cls.{\_}{\_}this,cls).{\_}{\_}init{\_}{\_}(*args)}\\
+\mbox{~~~~~~~~wrap(cls,cls.wrapper,cls.condition.im{\_}func)}\\
+\mbox{}\\
+\mbox{Traced=Wrapped.With(wrapper=tracedmethod,{\_}{\_}name{\_}{\_}='Traced')}\\
+\mbox{Timed=Wrapped.With(wrapper=timedmethod,{\_}{\_}name{\_}{\_}='Timed')}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> time_=ClsFactory[Traced](time)
+>>> print time_.asctime()
+[time_] Calling 'asctime' with arguments
+(){} ...
+-> 'time_.asctime' called with result: Sun May 4 07:30:51 2003
+Sun May 4 07:30:51 2003\end{verbatim}
+\end{quote}
+
+Another is
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<tracemain.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~ClsFactory,Traced,Reflective}\\
+\mbox{}\\
+\mbox{def~f1(x):~return~x~~~~~{\#}~nested~functions~}\\
+\mbox{def~f2(x):~return~f1(x)~{\#}~we~want~to~trace}\\
+\mbox{}\\
+\mbox{f1orf2=lambda~k,v~:~v~is~f1~or~v~is~f2}\\
+\mbox{make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]}\\
+\mbox{traced=make('traced',globals())}\\
+\mbox{}\\
+\mbox{traced.f2('hello!')~{\#}~call~traced.f2}\\
+\mbox{}\\
+\mbox{{\#}</tracemain.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[{\_}{\_}main{\_}{\_}]~Calling~'f2'~with~arguments}\\
+\mbox{('hello!',){\{}{\}}~...}\\
+\mbox{[{\_}{\_}main{\_}{\_}]~Calling~'f1'~with~arguments}\\
+\mbox{('hello!',){\{}{\}}~...}\\
+\mbox{->~'{\_}{\_}main{\_}{\_}.f1'~called~with~result:~hello!}\\
+\mbox{->~'{\_}{\_}main{\_}{\_}.f2'~called~with~result:~hello!}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{id46}{}
+\pdfbookmark[1]{Modifying hierarchies}{id46}
+\subsection*{Modifying hierarchies}
+
+Suppose one wants to enhance a pre-existing class, for instance
+by adding tracing capabilities to it. The problem is non-trivial
+since it is not enough to derive a new class from the original
+class using the 'Traced' metaclass. For instance, we could imagine of
+tracing the 'Pizza' class introduced in chapter 4 by defining
+\begin{quote}
+\begin{verbatim}>>> from oopp import *
+>>> class TracedTomatoPizza(GenericPizza,WithLogger):
+... __metaclass__=ClsFactory[Traced]
+... toppinglist=['tomato']\end{verbatim}
+\end{quote}
+
+However, this would only trace the methods of the newly defined class,
+not of the original one. Since the new class does not introduce any
+non-trivial method, the addition of 'Traced' is practically without
+any effect:
+\begin{quote}
+\begin{verbatim}>>> marinara=TracedTomatoPizza('small') # nothing happens
+*****************************************************************************
+Tue Apr 15 11:00:17 2003
+1. Created small pizza with tomato, cost $ 1.5\end{verbatim}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{tracing-hierarchies}{}
+\pdfbookmark[1]{Tracing hierarchies}{tracing-hierarchies}
+\subsection*{Tracing hierarchies}
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<traceH.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{def~wrapMRO(cls,wrapped):}\\
+\mbox{~~~~for~c~in~cls.{\_}{\_}mro{\_}{\_}[:-1]:}\\
+\mbox{~~~~~~~~wrap(c,wrapped)}\\
+\mbox{}\\
+\mbox{tracing=tracedmethod.With(logfile=file('trace.txt','w'))}\\
+\mbox{wrapMRO(HomoSapiensSapiens,tracing)}\\
+\mbox{HomoSapiensSapiens().can()}\\
+\mbox{}\\
+\mbox{{\#}</traceH.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+with output in trace.txt
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[HomoSapiensSapiens]~Calling~'can'~with~arguments}\\
+\mbox{~(<oopp.HomoSapiensSapiens~object~at~0x4020364c>,){\{}{\}}~...}\\
+\mbox{~~~~[HomoSapiens]~Calling~'can'~with~arguments}\\
+\mbox{~~~~~(<oopp.HomoSapiensSapiens~object~at~0x4020364c>,){\{}{\}}~...}\\
+\mbox{~~~~~~~~[HomoHabilis]~Calling~'can'~with~arguments}\\
+\mbox{~~~~~~~~~(<oopp.HomoSapiensSapiens~object~at~0x4020364c>,){\{}{\}}~...}\\
+\mbox{~~~~~~~~~~~~[Homo]~Calling~'can'~with~arguments}\\
+\mbox{~~~~~~~~~~~~~(<oopp.HomoSapiensSapiens~object~at~0x4020364c>,){\{}{\}}~...}\\
+\mbox{~~~~~~~~~~~~~~~~[PrettyPrinted]~Calling~'{\_}{\_}str{\_}{\_}'~with~arguments}\\
+\mbox{~~~~~~~~~~~~~~~~~(<oopp.HomoSapiensSapiens~object~at~0x4020364c>,){\{}{\}}~...}\\
+\mbox{~~~~~~~~~~~~~~~~[PrettyPrinted.{\_}{\_}str{\_}{\_}]~called~with~result:~}\\
+\mbox{~~~~~~~~~~~~~~~~~<HomoSapiensSapiens>}\\
+\mbox{~~~~~~~~~~~~[Homo.can]~called~with~result:~None}\\
+\mbox{~~~~~~~~[HomoHabilis.can]~called~with~result:~None}\\
+\mbox{~~~~[HomoSapiens.can]~called~with~result:~None}\\
+\mbox{[HomoSapiensSapiens.can]~called~with~result:~None}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{modifying-source-code}{}
+\pdfbookmark[1]{Modifying source code}{modifying-source-code}
+\subsection*{Modifying source code}
+
+The real solution would be to derive the original class 'GenericPizza'
+from 'Traced' and not from 'object'. One could imagine of creating
+a new class inhering from 'Traced' and with all the methods of the
+original 'GenericPizza' class; then one should create copies of
+all the classes in the whole multiple inheritance hierarchy.
+This would be a little annoying, but feasable; the real problem is
+that this approach would not work with cooperative methods, since
+cooperative calls in the derived classes would invoked methods in
+the original classes, which are not traced.
+
+This is a case where the modification of the original source code is
+much more appealing and simpler that any other method: it is enough
+to perform a search and replace in the original source code, by adding
+the metaclass 'Traced', to enhance the whole multiple inheritance hierarchy.
+Let me assume that the hierarchy is contained in a module (which is
+typical case). The idea, is to generate \emph{dynamically} a new module from the
+modified source code, with a suitable name to avoid conflicts with the
+original module. Incredibily enough, this can be done in few lines:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~modulesub(s,r,module):}\\
+\mbox{~~~~"Requires~2.3"}\\
+\mbox{~~~~name=module.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~source=inspect.getsource(module).replace(s,r)}\\
+\mbox{~~~~dic={\{}name:~module{\}};~exec~source~in~dic~{\#}~exec~the~modified~module}\\
+\mbox{~~~~module2=ModuleType(name+'2')~{\#}~creates~an~an~empty~module~}\\
+\mbox{~~~~customize(module2,**dic)~{\#}~populates~it~with~dic}\\
+\mbox{~~~~return~module2}\\
+\mbox{~~}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that the \texttt{sub} function, that modifies the source code of
+a given module and returns a modified module, requires Python 2.3
+to work. This is a due to a subtle bug in \texttt{exec} in Python 2.2.
+Anyway, the restriction to Python 2.3 allows me to take advantage
+of one of the most elegant convenience of Python 2.3: the name in
+the \texttt{types} module acts are type factories and in particular
+\texttt{ModuleType(s)} returns an (empty) module named \texttt{s}.
+Here is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> import oopp
+>>> s='GenericPizza(object):'
+>>> oopp2=oopp.modulesub(s,s+'\n __metaclass__=oopp.Traced',oopp) \end{verbatim}
+\end{quote}
+
+Name clashes are avoided, being 'oopp2' a different module from
+'oopp'; we have simultaneously access to both the original hierarchy
+in 'oopp' (non-traced) and the modified one in 'oopp2' (traced).
+In particular 'oopp2.CustomizablePizza' is traced and therefore
+\begin{quote}
+\begin{verbatim}>>> class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
+... __metaclass__=makecls()
+>>> marinara=PizzaLog.With(toppinglist=['tomato'])('small')\end{verbatim}
+\end{quote}
+
+gives the output
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[PizzaLog]~Calling~'{\_}{\_}init{\_}{\_}'~with~arguments}\\
+\mbox{(<oopp.PizzaLog~object~at~0x40470dac>,~'small'){\{}{\}}~...}\\
+\mbox{->~'PizzaLog.{\_}{\_}init{\_}{\_}'~called~with~result:~None}\\
+\mbox{}\\
+\mbox{*****************************************************************************}\\
+\mbox{Thu~Mar~27~09:18:28~2003}\\
+\mbox{[PizzaLog]~Calling~'{\_}{\_}str{\_}{\_}'~with~arguments}\\
+\mbox{(<oopp.PizzaLog~object~at~0x40470dac>,){\{}{\}}~...}\\
+\mbox{[PizzaLog]~Calling~'price'~with~arguments}\\
+\mbox{(<oopp.PizzaLog~object~at~0x40470dac>,){\{}{\}}~...}\\
+\mbox{[PizzaLog]~Calling~'toppings{\_}price'~with~arguments}\\
+\mbox{(<oopp.PizzaLog~object~at~0x40470dac>,){\{}{\}}~...}\\
+\mbox{->~'PizzaLog.toppings{\_}price'~called~with~result:~0.5}\\
+\mbox{}\\
+\mbox{->~'PizzaLog.price'~called~with~result:~1.5}\\
+\mbox{}\\
+\mbox{->~'PizzaLog.{\_}{\_}str{\_}{\_}'~called~with~result:~small~pizza~with~tomato,~cost~{\$}~1.5}\\
+\mbox{}\\
+\mbox{1.~Created~small~pizza~with~tomato,~cost~{\$}~1.5}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+From that we understand what is happening:
+\begin{itemize}
+\item {}
+\texttt{PizzaLog.{\_}{\_}init{\_}{\_}} calls \texttt{GenericPizza.{\_}{\_}init{\_}{\_}} that defines size and
+cooperatively calls \texttt{WithLogger.{\_}{\_}init{\_}{\_}}
+
+\item {}
+WithLogger.{\_}{\_}init{\_}{\_} cooperatively calls \texttt{WithCounter.{\_}{\_}init{\_}{\_}}
+that increments the count attribute;
+
+\item {}
+at this point, the instruction 'print self' in \texttt{WithLogger.{\_}{\_}init{\_}{\_}} calls
+\texttt{PizzaLog.{\_}{\_}str{\_}{\_}} (inherited from \texttt{GenericPizza.{\_}{\_}str{\_}{\_}});
+
+\item {}
+\texttt{GenericPizza.{\_}{\_}str{\_}{\_}} calls 'price' that in turns calls
+'toppings{\_}price'.
+
+\end{itemize}
+
+On top of that, notice that the metaclass of 'PizzaLog' is
+\texttt{{\_}TracedReflective} that has been automagically generated by
+\texttt{makecls} from the metaclasses of 'CustomizablePizza' (i.e. 'Traced')
+and of 'WithLogger' (i.e. 'Reflective'); the leading underscore helps
+to understand the dynamical origin of '{\_}TracedReflective'.
+It turns out that '{\_}TracedReflective' has a dynamically
+generated (meta-meta)class:
+\begin{quote}
+\begin{verbatim}>>> print type(type(PizzaLog)) #meta-metaclass
+<class 'oopp._WithWrappingCapabilitiesReflective'>\end{verbatim}
+\end{quote}
+
+Therefore this example has a non-trivial class hierarchy
+\begin{quote}
+\begin{verbatim}>>> print oopp.MRO(PizzaLog)
+MRO of PizzaLog:
+ 0 - PizzaLog(CustomizablePizza,WithLogger)[Traced]
+ 1 - CustomizablePizza(GenericPizza,Customizable)[Traced]
+ 2 - GenericPizza(object)[Traced]
+ 3 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 4 - WithCounter(object)
+ 5 - Customizable(object)
+ 6 - PrettyPrinted(object)
+ 7 - object()\end{verbatim}
+\end{quote}
+
+a non-trivial metaclass hierarchy,
+\begin{quote}
+\begin{verbatim}>>> print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
+MRO of Traced:
+ 0 - Traced(Reflective)[WithWrappingCapabilities]
+ 1 - Reflective(type)
+ 2 - type(object)
+ 3 - object()\end{verbatim}
+\end{quote}
+
+and a non-trivial meta-metaclass hierarchy:
+\begin{quote}
+\begin{verbatim}>>> print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
+MRO of WithWrappingCapabilities:
+ 0 - WithWrappingCapabilities(BracketCallable)
+ 1 - CallableWithBrackets(type)
+ 2 - type(object)
+ 3 - object()\end{verbatim}
+\end{quote}
+
+Pretty much complicated, isn't it ? ;)
+
+This example is there to show what kind of maintenance one can have
+with programs doing a large use of metaclasses, particularly, when
+they should be understood by somebody else than the autor ...
+
+
+%___________________________________________________________________________
+
+\hypertarget{metaclass-regenerated-hierarchies}{}
+\pdfbookmark[1]{Metaclass regenerated hierarchies}{metaclass-regenerated-hierarchies}
+\subsection*{Metaclass regenerated hierarchies}
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{import~types}\\
+\mbox{~~}\\
+\mbox{def~hierarchy(self,cls):}\\
+\mbox{~~~~d=dict([(t.{\_}{\_}name{\_}{\_},t)~for~t~in~vars(types).itervalues()}\\
+\mbox{~~~~~~~~~~~~if~isinstance(t,type)])}\\
+\mbox{~~~~def~new(c):}\\
+\mbox{~~~~~~~~bases=tuple([d[b.{\_}{\_}name{\_}{\_}]~for~b~in~c.{\_}{\_}bases{\_}{\_}])}\\
+\mbox{~~~~~~~~return~self(c.{\_}{\_}name{\_}{\_},~bases,~c.{\_}{\_}dict{\_}{\_}.copy())}\\
+\mbox{~~~~mro=list(cls.{\_}{\_}mro{\_}{\_}[:-1])}\\
+\mbox{~~~~mro.reverse()}\\
+\mbox{~~~~for~c~in~mro:}\\
+\mbox{~~~~~~~~if~not~c.{\_}{\_}name{\_}{\_}~in~d:}\\
+\mbox{~~~~~~~~~~~~d[c.{\_}{\_}name{\_}{\_}]=new(c)}\\
+\mbox{~~~~customize(self,**d)}\\
+\mbox{}\\
+\mbox{ClsFactory.hierarchy=hierarchy}\\
+\mbox{traced=ClsFactory[Traced,Reflective]}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Unfortunately, this approach does not work if the original hierarchy makes
+named cooperative super calls.
+
+Therefore the source-code run-time modification has its advantages.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-programmable-programming-language}{}
+\pdfbookmark[0]{THE PROGRAMMABLE PROGRAMMING LANGUAGE}{the-programmable-programming-language}
+\section*{THE PROGRAMMABLE PROGRAMMING LANGUAGE}
+\begin{quote}
+
+\emph{I think that lisp is a better applications language than Python.
+However, Python is close enough, or at least so much better than the
+alternatives, that Python's social and glue language advantages are
+often decisive.} -- Andy Freeman on c.l.p.
+\end{quote}
+
+I go in \emph{really} DEEP BLACK MAGIC here.
+
+Lisp has been called the \emph{programmable programming language} [\hyperlink{id43}{22}]
+since its macros allow the programmer to change the \emph{syntax} of
+the language. Python has no macros and the syntax of the language
+cannot be changed. Nevertheless, Python metaclasses allows
+to change the \emph{semantics} of the language. In this sense, they
+are even more powerful and more dangerous than Lisp macros.
+Python metaclass allow the user to customize the language (if not
+its syntax). This is cool enough, however it can make your programs
+unreadable by others. The techniques explained in this
+chapter should be used with care. Nevertheless, I trust the judgement
+of the programmer who has been able to reach this chapter, and I don't
+mind providing him further rope to shoot in his/her foot ;)
+\begin{figure}[b]\hypertarget{id48}[24]
+Paul Graham, 'OnLisp'
+citing
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{enhancing-the-python-language}{}
+\pdfbookmark[1]{Enhancing the Python language}{enhancing-the-python-language}
+\subsection*{Enhancing the Python language}
+
+Let me start with some minor usage of metaclasses. In this section I
+will show how the user can implement in few lines features that are
+built-in in other languages, through a minimal usage of metaclasses.
+
+For instance, suppose one wants to define a class which cannot be
+derived: in Java this can be done with the ''final`` keyword.
+In Python there is no need to add a new keyword to the language:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~NonDerivableError(Exception):~pass}\\
+\mbox{}\\
+\mbox{class~Final(type):~{\#}~better~derived~from~WithCounter,type}\\
+\mbox{~~~~"Instances~of~Final~cannot~be~derived"}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(meta,name,bases,dic):}\\
+\mbox{~~~~~~~~try:}\\
+\mbox{~~~~~~~~~~~~meta.already{\_}called~is~True}\\
+\mbox{~~~~~~~~except~AttributeError:~{\#}~not~already~called}\\
+\mbox{~~~~~~~~~~~~meta.already{\_}called=True}\\
+\mbox{~~~~~~~~~~~~return~super(Final,meta).{\_}{\_}new{\_}{\_}(meta,name,bases,dic)}\\
+\mbox{~~~~~~~~else:~{\#}if~already~called}\\
+\mbox{~~~~~~~~~~~~raise~NonDerivableError("I~cannot~derive~from~{\%}s"~{\%}~bases)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{verbatim}>>> from oopp import Final
+>>> class C:
+... __metaclass__=Final
+...
+>>> class D(C): pass #error
+...
+NonDerivableError: D not created from (<class 'oopp.C'>,)\end{verbatim}
+\end{quote}
+
+It is interesting to notice that a similar effect can be reached
+with a \texttt{singletonClass} class factory: a 'MetaSingleton' inherits
+from \texttt{Singleton} and from 'type' (therefore it is a metaclass):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{class~S(Singleton,type):~pass}\\
+\mbox{singletonClass=ClsFactory[S]}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+If we write
+\begin{quote}
+\begin{verbatim}>>> from oopp import singletonClass
+>>> C=singletonClass()
+>>> class D(C):
+... pass\end{verbatim}
+\end{quote}
+
+we see that actually 'D' is not a new instance of 'Singleton', but
+it coincides with 'C', instead:
+\begin{quote}
+\begin{verbatim}>>> id(C),id(D)
+(135622140, 135622140)
+>>> C is D
+True
+>>> type(C)
+<class '__main__._Singleton'>
+>>> type(C).__bases__
+(<class 'oopp.Singleton'>, <type 'type'>)
+>>> c=C(); d=D()
+>>> id(c),id(d)
+(1075378028, 1075378924)\end{verbatim}
+\end{quote}
+
+Notice the order: 'SingletonClass' must inherit from 'Singleton'
+first and from \texttt{Class} second, otherwise the \texttt{Class.{\_}{\_}new{\_}{\_}} method would
+override the \texttt{Singleton.{\_}{\_}new{\_}{\_}}, therefore losing the 'Singleton'
+basic property of having only one instance. On the other hand, in
+the correct order, 'Singleton' first and 'Class' second, the inheritance
+diagram is
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~~~~~~~~~~object~~~5}\\
+\mbox{~~~~~~~~~~~~~({\_}{\_}new{\_}{\_})}\\
+\mbox{~~~~~~~~~~~~/~~~~~~~~~~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~/~~~~~~~~~~~~{\textbackslash}}\\
+\mbox{2~~~~~~WithCounter~~~~~~~~~~type~~~~4}\\
+\mbox{~~~~~~({\_}{\_}new{\_}{\_})~~~~~~~({\_}{\_}new{\_}{\_})}\\
+\mbox{~~~~~~~~~~|~~~~~~~~~~~~~~|}\\
+\mbox{~~~~~~~~~~|~~~~~~~~~~~~~~|}\\
+\mbox{1~~~~~Singleton~~~~~~~~~Class~~~~3}\\
+\mbox{~~~~~~({\_}{\_}new{\_}{\_})~~~~~~~({\_}{\_}new{\_}{\_})}\\
+\mbox{~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~{\textbackslash}~~~~~~~~~~~/}\\
+\mbox{~~~~~~~~~~~~SingletonClass~~~~0}\\
+\mbox{~~~~~~~~~(Singleton.{\_}{\_}new{\_}{\_})}
+\end{flushleft}\end{ttfamily}
+\begin{ttfamily}\begin{flushleft}
+\mbox{~~~~~~object}\\
+\mbox{~~~~~/~~~~~{\textbackslash}}\\
+\mbox{~~~~/~~~~~~~|}\\
+\mbox{WithCounter~~~~~|~}\\
+\mbox{~~~~|~~~~~~~|}\\
+\mbox{Singleton~~type}\\
+\mbox{~~~~~{\textbackslash}~~~~~/}\\
+\mbox{~~~~~~{\textbackslash}~~~/}\\
+\mbox{~~~MetaSingleton}\\
+\mbox{~~~~~~~~:}\\
+\mbox{~~~~~~~~:~~~~~~~}\\
+\mbox{~~~~~~~~:~~~instantiation}\\
+\mbox{~~~~~~~~:}\\
+\mbox{~~~~~~~~:}\\
+\mbox{~~~~~~C~=~D}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+whereas 'SingletonClass' inherits \texttt{Singleton.{\_}{\_}new{\_}{\_}} which, trough
+the \texttt{super} mechanism, calls 'type.{\_}{\_}new{\_}{\_}' and therefore creates
+the class 'C'. Notice that class 'D' is never created, it is simply
+an alias for 'C'.
+
+I think it is simpler to write down the class 'Final' explicitely
+(explicit is better than implicit) as I did; however a fanatic of code
+reuse could derive it from 'SingletonClass':
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<final.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{class~Final(Singleton,type):}\\
+\mbox{~~~~"Inherits~the~'instance'~attribute~from~Singleton~(default~None)"}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(meta,name,bases,dic):}\\
+\mbox{~~~~~~~~if~meta.counter==0:~{\#}~first~call}\\
+\mbox{~~~~~~~~~~~~return~super(Final,meta).{\_}{\_}new{\_}{\_}(meta,name,bases,dic)}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~raise~NonDerivableError("I~cannot~derive~from~{\%}s"~{\%}~bases)}\\
+\mbox{~~}\\
+\mbox{class~C:~~{\_}{\_}metaclass{\_}{\_}=Final}\\
+\mbox{}\\
+\mbox{try:}\\
+\mbox{~~~~class~D(C):~pass}\\
+\mbox{except~NonDerivableError,e:}\\
+\mbox{~~~~print~e}\\
+\mbox{}\\
+\mbox{{\#}</final.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The reader can check that this script has the correct output
+''I cannot derive from {\textless}class 'oopp.C'{\textgreater}``. I leave to the reader
+to understand the issues with trying to implement 'NonDerivable'
+from 'NonInstantiable'. {\#}And why an inner metaclass would not work.
+
+
+%___________________________________________________________________________
+
+\hypertarget{restricting-python-dynamism}{}
+\pdfbookmark[1]{Restricting Python dynamism}{restricting-python-dynamism}
+\subsection*{Restricting Python dynamism}
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{def~frozen(self,name,value):}\\
+\mbox{~~~~if~hasattr(self,name):}\\
+\mbox{~~~~~~~~type(self).{\_}{\_}bases{\_}{\_}[0].{\_}{\_}setattr{\_}{\_}(self,name,value)~}\\
+\mbox{~~~~else:}\\
+\mbox{~~~~~~~~raise~AttributeError("You~cannot~add~attributes~to~{\%}s"~{\%}~self)}\\
+\mbox{}\\
+\mbox{class~Frozen(object):}\\
+\mbox{~~~~"""Subclasses~of~Frozen~are~frozen,~i.e.~it~is~impossibile~to~add}\\
+\mbox{~~~~~new~attributes~to~them~and~their~instances"""}\\
+\mbox{~~~~{\_}{\_}setattr{\_}{\_}~=~frozen}\\
+\mbox{~~~~class~{\_}{\_}metaclass{\_}{\_}(type):}\\
+\mbox{~~~~~~~~{\_}{\_}setattr{\_}{\_}~=~frozen}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{}\\
+\mbox{{\#}<frozen.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{}\\
+\mbox{class~C(Frozen):}\\
+\mbox{~~~~c=1}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):~}\\
+\mbox{~~~~~~~~{\#}self.x=5~{\#}~won't~work~anymore,~{\_}{\_}new{\_}{\_}~will~be~okay}\\
+\mbox{~~~~~~~~pass}\\
+\mbox{}\\
+\mbox{class~D(C):}\\
+\mbox{~~~~d=2}\\
+\mbox{~~}\\
+\mbox{C.c=2}\\
+\mbox{}\\
+\mbox{print~D().d}\\
+\mbox{}\\
+\mbox{{\#}</frozen.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{changing-the-language-without-changing-the-language}{}
+\pdfbookmark[1]{Changing the language without changing the language}{changing-the-language-without-changing-the-language}
+\subsection*{Changing the language without changing the language}
+
+In Lisp the user has the possibility of changing the syntax of the
+language to suit her purposes (or simply to fit her taste).
+In Python, the user cannot change the basic grammar of the language,
+nevertheless, to a great extent, metaclasses allows to emulate this effect.
+Notice that using metaclasses to this aim is not necessarely
+a good idea, since once you start
+changing the Python standard behaviour, it will become impossible for
+others to understand your programs (which is what happened to Lisp ;).
+
+Let me show how metaclasses can be used to provide notational convenience
+(i.e. syntactic sugar) for Python.
+
+As first example, I will show how we may use metaclasses to provide some
+convenient notation for staticmethods and classmethods:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~MetaSugar(type):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,clsdict):}\\
+\mbox{~~~~~~~~for~key,value~in~clsdict.iteritems():}\\
+\mbox{~~~~~~~~~~~~if~key.startswith("static{\_}"):}\\
+\mbox{~~~~~~~~~~~~~~~~setattr(cls,key[7:],staticmethod(value))}\\
+\mbox{~~~~~~~~~~~~elif~key.startwith("class{\_}"):}\\
+\mbox{~~~~~~~~~~~~~~~~setattr(cls,key[6:],classmethod(value))}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The same effect can be obtained trough normal inheritance
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~SyntacticSugar(object):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\
+\mbox{~~~~~~~~for~k,v~in~self.{\_}{\_}class{\_}{\_}.{\_}{\_}dict{\_}{\_}.iteritems():}\\
+\mbox{~~~~~~~~~~~~if~k.startswith('static{\_}'):}\\
+\mbox{~~~~~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.{\_}{\_}dict{\_}{\_}[k[7:]]~=~staticmethod(v)}\\
+\mbox{~~~~~~~~~~~~if~k.startswith('static{\_}'):}\\
+\mbox{~~~~~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.{\_}{\_}dict{\_}{\_}[k[7:]]~=~staticmethod(v)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Let me now implement some syntactic sugar for the {\_}{\_}metaclass{\_}{\_} hook.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}<oopp.py>}\\
+\mbox{}\\
+\mbox{import~re}\\
+\mbox{squarednames=re.compile('{\textbackslash}[([A-Za-z{\_}][{\textbackslash}w{\textbackslash}.,~]*){\textbackslash}]')}\\
+\mbox{}\\
+\mbox{def~inferredfromdocstring(name,bases,dic):}\\
+\mbox{~~~~docstring=dic['{\_}{\_}doc{\_}{\_}']}\\
+\mbox{~~~~match=squarednames.match(docstring)}\\
+\mbox{~~~~if~not~match:~return~ClsFactory[Reflective](name,bases,dic)}\\
+\mbox{~~~~metanames=[name.strip()~for~name~in~match.group(1).split(',')]}\\
+\mbox{~~~~metaname=''.join(metanames)~~}\\
+\mbox{~~~~if~len(metanames)>1:~{\#}~creates~a~new~metaclass}\\
+\mbox{~~~~~~~~metaclass=type(metaname,tuple(map(eval,metanames)),{\{}{\}})}\\
+\mbox{~~~~else:}\\
+\mbox{~~~~~~~~metaclass=eval(metaname)}\\
+\mbox{~~~~return~ClsFactory[metaclass](name,bases,dic)}\\
+\mbox{}\\
+\mbox{{\#}</oopp.py>}\\
+\mbox{}\\
+\mbox{{\#}<sugar.py>}\\
+\mbox{}\\
+\mbox{from~oopp~import~*}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}~=~inferredfromdocstring}\\
+\mbox{class~B:}\\
+\mbox{~~~~"Do~nothing~class"}\\
+\mbox{}\\
+\mbox{class~C:~}\\
+\mbox{~~~~"[Reflective]"}\\
+\mbox{~~~~"~Do~nothing~class"}\\
+\mbox{}\\
+\mbox{class~D:}\\
+\mbox{~~~~"[WithLogger,Final]"}\\
+\mbox{~~~~"Do~nothing~class"}\\
+\mbox{}\\
+\mbox{class~E(C):}\\
+\mbox{~~~~pass}\\
+\mbox{}\\
+\mbox{{\#}</sugar.py>}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+With output:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{*****************************************************************************}\\
+\mbox{Fri~Feb~21~09:35:58~2003}\\
+\mbox{Creating~class~Logged{\_}C~descending~from~(),}\\
+\mbox{instance~of~<class~'oopp.Logged'>}\\
+\mbox{}\\
+\mbox{Logged{\_}C~dictionary:}\\
+\mbox{~{\_}{\_}doc{\_}{\_}~=~Do~nothing~class}\\
+\mbox{*****************************************************************************}\\
+\mbox{Fri~Feb~21~09:35:58~2003}\\
+\mbox{Creating~class~Logged{\_}Final{\_}D~descending~from~(),}\\
+\mbox{instance~of~<class~'oopp.LoggedFinal'>}\\
+\mbox{}\\
+\mbox{Logged{\_}Final{\_}D~dictionary:}\\
+\mbox{{\_}{\_}doc{\_}{\_}~=~Do~nothing~class}\\
+\mbox{*****************************************************************************}\\
+\mbox{Fri~Feb~21~09:35:58~2003}\\
+\mbox{Creating~class~E~descending~from~(<class~'oopp.Logged{\_}C'>,),}\\
+\mbox{instance~of~<class~'oopp.Logged'>}\\
+\mbox{}\\
+\mbox{E~dictionary:}\\
+\mbox{<EMPTY>~}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+At the end, let me point out few observations:
+Metaclasses can be used to provide syntactic sugar, as I have shown
+in the previous example. However, I have given the previous
+routines as a proof of concept: I do \emph{not} use these routines in
+my actual code for many good reasons:
+\newcounter{listcnt34}
+\begin{list}{\arabic{listcnt34}.}
+{
+\usecounter{listcnt34}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+At the end a convenient notation will be provided in Python 2.4
+
+\item {}
+I don't want to use magic tricks on my code, I want others to
+be able to understand what the code is doing;
+
+\item {}
+I want to be able myself to understand my own code in six months
+from today ;)
+
+\end{list}
+
+Anyway, I think it is a good thing to know about this potentiality
+of metaclasses, that can turn out to be very convenient in certain
+applications: but this does not mean that should be blindly used
+and/or abused. In other words: with great powers come
+great responsabilities ;)
+
+
+%___________________________________________________________________________
+
+\hypertarget{recognizing-magic-comments}{}
+\pdfbookmark[1]{Recognizing magic comments}{recognizing-magic-comments}
+\subsection*{Recognizing magic comments}
+
+In this section, I will begin to unravel the secrets of the black magic art
+of changing Python semantics and I will show that with few lines
+involving metaclasses
+and the standard library 'inspect' module, even comments can be made
+significant! (let me continue with my series ''how to do what should not
+be done``).
+
+To this aim, I need a brief digression on regular expressions.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{class~RecognizesMagicComments(object):}\\
+\mbox{~~~form=r'def~{\%}s(NAME)(args):{\#}!{\textbackslash}s?staticmethod'}\\
+\mbox{~~~class~{\_}{\_}metaclass{\_}{\_}(type):}\\
+\mbox{~~~~~~~def~{\_}{\_}new{\_}{\_}(meta,name,bases,dic):}\\
+\mbox{~~~~~~~~~~~code=[]}\\
+\mbox{~~~~~~~~~~~for~attr~in~dic:}\\
+\mbox{~~~~~~~~~~~~~~~source=inspect.getsource(dic[attr]).splitlines()}\\
+\mbox{~~~~~~~~~~~~~~~for~line~in~source:}\\
+\mbox{~~~~~~~~~~~~~~~~~~~split=line.split('{\#}!')}\\
+\mbox{~~~~~~~~~~~~~~~~~~~if~len(split)==2:}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~descriptor=split[1];~code.append(split[0])}\\
+\mbox{~~~~~~~~~~~~~~~~~~~else:~code.append(line)}\\
+\mbox{~~~~~~~~~~~~~}\\
+\mbox{class~C(RecognizesMagicComments):}\\
+\mbox{~~~~{\#}!staticmethod}\\
+\mbox{~~~~def~f(x):~{\#}!staticmethod}\\
+\mbox{~~~~~~~~return~x}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{interpreting-python-source-code-on-the-fly}{}
+\pdfbookmark[1]{Interpreting Python source code on the fly}{interpreting-python-source-code-on-the-fly}
+\subsection*{Interpreting Python source code on the fly}
+
+At this point, I can really go \emph{DEEP} in black magic.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{import~sys,~inspect,~linecache,~re}\\
+\mbox{}\\
+\mbox{def~cls{\_}source(name,module):}\\
+\mbox{~~~~lines~=~linecache.getlines(inspect.getsourcefile(module))}\\
+\mbox{~~~~if~not~lines:~raise~IOError,~'could~not~get~source~code'}\\
+\mbox{~~~~pat~=~re.compile(r'{\textasciicircum}{\textbackslash}s*class{\textbackslash}s*'~+~name~+~r'{\textbackslash}b')}\\
+\mbox{~~~~for~i~in~range(len(lines)):}\\
+\mbox{~~~~~~~~if~pat.match(lines[i]):~break}\\
+\mbox{~~~~else:~raise~IOError,~'could~not~find~class~definition'}\\
+\mbox{~~~~lines,~lnum~=~inspect.getblock(lines[i:]),~i~+~1}\\
+\mbox{~~~~return~''.join(lines)}\\
+\mbox{}\\
+\mbox{class~Interpreter(object):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,CPO):~{\#}~possible~composition~of~code~processing~opers}\\
+\mbox{~~~~~~~~self.repl=CPO}\\
+\mbox{~~~~def~{\_}{\_}call{\_}{\_}(self,name,bases,dic):}\\
+\mbox{~~~~~~~~try:}\\
+\mbox{~~~~~~~~~~~modulename=dic['{\_}{\_}module{\_}{\_}']~{\#}~module~where~the~class~is~defined}\\
+\mbox{~~~~~~~~except~KeyError:~{\#}~no~{\_}{\_}module{\_}{\_}~attribute}\\
+\mbox{~~~~~~~~~~~raise~IOError("Class~{\%}s~cannot~be~defined~dynamically~or~in~the{\textbackslash}n"}\\
+\mbox{~~~~~~~~~~~"interpreter~and~the~source~code~cannot~came~from~a~pipe"{\%}~name)}\\
+\mbox{~~~~~~~~module=sys.modules[modulename]~}\\
+\mbox{~~~~~~~~source=self.repl(cls{\_}source(name,module))}\\
+\mbox{~~~~~~~~source=re.sub('{\_}{\_}metaclass{\_}{\_}=.*','{\_}{\_}metaclass{\_}{\_}=type',source)}\\
+\mbox{~~~~~~~~{\#}print~source}\\
+\mbox{~~~~~~~~loc={\{}{\}};~exec~source~in~vars(module),loc}\\
+\mbox{~~~~~~~~return~loc[name]}\\
+\mbox{}\\
+\mbox{regexp{\_}expand=Interpreter(regexp)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{implementing-lazy-evaluation}{}
+\pdfbookmark[1]{Implementing lazy evaluation}{implementing-lazy-evaluation}
+\subsection*{Implementing lazy evaluation}
+
+At this point of our knowledge, it becomes trivial to implement lazy
+evaluation and then a ternary operator. (My original, simpler, implementation
+is posted on c.l.p.; see the thread 'PEP 312 (and thus 308) implemented
+with a black magic trick')
+
+
+%___________________________________________________________________________
+
+\hypertarget{implementing-a-ternary-operator}{}
+\pdfbookmark[1]{Implementing a ternary operator}{implementing-a-ternary-operator}
+\subsection*{Implementing a ternary operator}
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}~module~ternary.py}\\
+\mbox{}\\
+\mbox{"PEP~308~and~312~implemented~via~a~metaclass-powered~dirty~trick"}\\
+\mbox{}\\
+\mbox{import~inspect,{\_}{\_}main{\_}{\_}}\\
+\mbox{}\\
+\mbox{{\#}~the~ternary~operator:}\\
+\mbox{}\\
+\mbox{def~if{\_}(cond,f,g):}\\
+\mbox{~~~~"Short~circuiting~ternary~operator~implemented~via~lambdas"}\\
+\mbox{~~~~if~cond:~return~f()}\\
+\mbox{~~~~else:~return~g()}\\
+\mbox{}\\
+\mbox{{\#}~the~metaclass~black~magic:}\\
+\mbox{}\\
+\mbox{class~DirtyTrick(type):}\\
+\mbox{~~~~"""Cooperative~metaclass~that~looks~at~the~source~code~of~its~instances~}\\
+\mbox{~~~~and~replaces~the~string~'{\textasciitilde}'~with~'lambda~:'~before~the~class~creation"""}\\
+\mbox{~~~~def~{\_}{\_}new{\_}{\_}(meta,name,bases,dic):}\\
+\mbox{~~~~~~~~for~attr~in~dic.values():}\\
+\mbox{~~~~~~~~~~~~if~inspect.isfunction(attr):~}\\
+\mbox{~~~~~~~~~~~~~~~~code=inspect.getsource(attr)}\\
+\mbox{~~~~~~~~~~~~~~~~if~code.find('{\textasciitilde}')==-1:~continue~{\#}~no~'{\textasciitilde}'~found,~skip}\\
+\mbox{~~~~~~~~~~~~~~~~code=code.replace('{\textasciitilde}','lambda~:')}\\
+\mbox{~~~~~~~~~~~~~~~~code=dedent(code)+'{\textbackslash}n'}\\
+\mbox{~~~~~~~~~~~~~~~~exec~code~in~{\_}{\_}main{\_}{\_}.{\_}{\_}dict{\_}{\_},dic~{\#}~modifies~dic}\\
+\mbox{~~~~~~~~return~super(DirtyTrick,meta).{\_}{\_}new{\_}{\_}(meta,name,bases,dic)}\\
+\mbox{}\\
+\mbox{{\#}~a~convenient~base~class:}\\
+\mbox{}\\
+\mbox{class~RecognizesImplicitLambdas:}\\
+\mbox{~~~~"Children~of~this~class~do~recognize~implicit~lambdas"}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=DirtyTrick}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{from~ternary~import~if{\_},~RecognizesImplicitLambdas}\\
+\mbox{from~math~import~sqrt}\\
+\mbox{}\\
+\mbox{class~C(RecognizesImplicitLambdas):}\\
+\mbox{~~~def~safesqrt(self,x):}\\
+\mbox{~~~~~~~~return~if{\_}(~x>0,~{\textasciitilde}sqrt(x),~{\textasciitilde}0)~{\#}short-circuiting~ternary~operator}\\
+\mbox{}\\
+\mbox{c=C()}\\
+\mbox{print~c.safesqrt(4),~c.safesqrt(-4)~}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+\end{document}
+
diff --git a/pypers/all.txt b/pypers/all.txt
new file mode 100755
index 0000000..9c5f8a9
--- /dev/null
+++ b/pypers/all.txt
@@ -0,0 +1,10528 @@
+.. -*- mode: rst -*-
+
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+OBJECT ORIENTED PROGRAMMING IN PYTHON
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:Version: 0.5
+:Author: Michele Simionato
+:E-mail: mis6@pitt.edu
+:Home-page: http://www.phyast.pitt.edu/~micheles/
+
+:Disclaimer: I release this book to the general public.
+ It can be freely distributed if unchanged.
+ As usual, I don't give any warranty: while I have tried hard to ensure the
+ correctness of what follows, I disclaim any responsability in case of
+ errors . Use it at your own risk and peril !
+
+.. contents::
+
+.. raw:: latex
+
+ \setcounter{chapter}{-1}
+
+Preface
+============
+
+ .. line-block::
+
+ *There is only one way to learn: trough examples*
+
+The philosophy of this book
+---------------------------
+
+This book is written with the intent to help the programmer going trough
+the fascinating concepts of Object Oriented Programming (OOP), in their
+Python incarnation. Notice that I say to help, not to teach. Actually,
+I do not think that a book can teach OOP or any other non-trivial matter
+in Computer Science or other disciplines. Only the
+practice can teach: practice, then practice, and practice again.
+You must learn yourself from your experiments, not from the books.
+Nevertheless, books are useful. They cannot teach, but they can help.
+They should give you new ideas that you was not thinking about, they should
+show tricks you do not find in the manual, and in general they should be of
+some guidance in the uphill road to knowledge. That is the philosophy
+of this book. For this reason
+
+1. It is not comprehensive, not systematic;
+it is intended to give ideas and basis: from
+that the reader is expected to cover the missing part on his own,
+browsing the documentation, other sources and other books, and finally
+the definite autority, the source itself.
+
+2. It will not even try to teach the *best* practices. I will show what you can
+do with Python, not what you "should" do. Often I will show solutions that are
+not recommended. I am not a mammy saying this is
+good, this is bad, do this do that.
+
+
+3. You can only learn from your failures. If you think "it should work, if I do
+X and Y" and it works, then you have learned nothing new.
+You have merely verified
+that your previous knowledge was correct, but you haven't create a new
+knowledge. On the other hand, when you think "it should work, if I do
+X and Y" and it doesn't, then you have learned that your previous knowlegde
+was wrong or incomplete, and you are forced to learn something new to
+overcome the difficulty. For this reason, I think it is useful to report
+not only how to do something, but also to report how not to do something,
+showing the pitfalls of wrong approaches.
+
+That's in my opinion is the goal of a good book. I don't know if have
+reached this goal or not (the decision is up to the reader), but at least
+I have tried to follow these guidelines.
+
+Moreover, this is not a book on OOP,
+it is a book on OOP *in Python*.
+
+In other words, the point of view of this book is not
+to emphasize general topics of OOP that are exportable to other languages,
+but exactly the opposite: I want to emphasize specific techniques that one
+can only use in Python, or that are difficult to translate to other
+languages. Moreover, I will not provide comparisons with other
+languages (except for the section "Why Python?" in this introduction and
+in few selected other places),
+in order to keep the discussion focused.
+
+This choice comes from the initial motivation for this book, which was
+to fulfill a gap in the (otherwise excellent) Python documentation.
+The problem is that the available documentation still lacks an accessible
+reference of the new Python 2.2+ object-oriented features.
+Since myself I have learned Python and OOP from scratch,
+I have decided to write this book in order to fill that gap and
+help others.
+
+The emphasis in this book is not in giving
+solutions to specific problems (even if most of the recipes of this book
+can easily be tailored to solve real life concrete problems), it is in
+teaching how does it work, why it does work in some cases and why does
+not work in some other cases. Avoiding too specific problems has an
+additional bonus, since it allows me to use *short* examples (the majority
+of the scripts presented here is under 20-30 lines) which I think are
+best suited to teach a new matter [#]_ . Notice, however, that whereas
+the majority of the scripts in this book are short, it is also true
+that they are pretty *dense*. The density is due to various reasons:
+
+1. I am defining a lot of helper functions and classes, that are
+ reused and enhanced during all the book.
+
+2. I am doing a strong use of inheritance, therefore a script at the
+ end of the book can inherits from the classes defined through all
+ the book;
+
+3. A ten line script involving metaclasses can easily perform the equivalent
+ of generating hundreds of lines of code in a language without metaclasses
+ such as Java or C++.
+
+To my knowledge, there are no other books covering the same topics with
+the same focus (be warned, however, that I haven't read so many Python
+books ;-). The two references that come closest to the present book are
+the ``Python Cookbook`` by Alex Martelli and David Ascher, and
+Alex Martelli's ``Python in a Nutshell``. They are quite recent books and
+therefore it covers (in much less detail) some of the 2.2 features that are
+the central topics to this book.
+However, the Cookbook reserves to OOP only one chapter and has a quite
+different philosophy from the present book, therefore there is
+practically no overlapping. Also ``Python in a Nutshell`` covers
+metaclasses in few pages, whereas half of this book is essentially
+dedied to them. This means that you can read both ;-)
+
+
+.. [#] Readers that prefer the opposite philosophy of using longer,
+ real life-like, examples, have already the excellent "Dive into
+ Python" book http://diveintopython.org/ at their disposal. This is
+ a very good book that I certainly recommend to any (experienced)
+ Python programmer; it is also freely available (just like this ;-).
+ However, the choice of arguments is quite different and there is
+ essentially no overlap between my book and "Dive into Python"
+ (therefore you can read both ;-).
+
+For who this book in intended
+-----------------------------
+
+I have tried to make this tutorial useful to a large public of Pythonistas,
+i.e. both people with no previous experience of Object Oriented Programming
+and people with experience on OOP, but unfamiliar with the most
+recent Python 2.2-2.3 features (such as attribute descriptors,
+metaclasses, change of the MRO in multiple inheritance, etc).
+However, this is not a book for beginners: the non-experienced reader should
+check (at least) the Internet sites www.python.org/newbies.com and
+www.awaretek.com, that provide a nice collection of resources for Python
+newbies.
+
+These are my recommendations for the reader, according to her/his level:
+
+1. If you are an absolute beginner, with no experience on programming,
+ this book is *not* for you (yet ;-). Go to
+ http://www.python.org/doc/Newbies.html and read one of the introductive
+ texts listed there, then come back here. I recommend "How to Think Like
+ a Computer Scientist", available for free on the net (see
+ http://www.ibiblio.org/obp/thinkCSpy/); I found it useful myself when
+ I started learning Python; be warned, however, that it refers to the rather
+ old Python version 1.5.2. There are also excellent books
+ on the market (see http://www.awaretek.com/plf.html).
+ http://www.uselesspython.com/ is a good resource to find recensions
+ about available Python books. For free books, look at
+ http://www.tcfb.com/freetechbooks/bookphyton.html .
+ This is *not* another Python tutorial.
+
+2. If you know already (at least) another programming language, but you don't
+ know Python, then this book is *not* for you (again ;-). Read the FAQ, the
+ Python Tutorial and play a little with the Standard Library (all this
+ material can be downloaded for free from http://www.python.org), then
+ come back here.
+
+3. If you have passed steps 1 and 2, and you are confortable with Python
+ at the level of simple procedural programming, but have no clue about
+ objects and classes, *then* this book is for you. Read this book till
+ the end and your knowledge of OOP will pass from zero to a quite advanced
+ level (hopefully). Of course, you will have to play with the code in
+ this book and write a lot of code on your own, first ;-)
+
+4. If you are confortable with Python and you also known OOP from other
+ languages or from earlier version of Python, then this book is for
+ you, too: you are ready to read the more advanced chapters.
+
+5. If you are a Python guru, then you should read the book, too. I expect
+ you will find the errors and send me feedback, helping me to improve
+ this tutorial.
+
+About the scripts in this book
+-----------------------------------------------------------------------------
+
+All the scripts in this book are free. You are expected to play
+with them, to modify them and to improve them.
+
+In order to facilitate the extraction of the scripts from the main text, both
+visually for the reader and automatically for Python, I use the
+convention of sandwiching the body of the example scripts in blocks like this
+
+ ::
+
+ #<myfirstscript.py>
+
+ print "Here Starts the Python Way to Object Oriented Programming !"
+
+ #</myfirstscript.py>
+
+You may extract the source of this script with the a Python program
+called "test.py" and provided in the distribution. Simply give the
+following command:
+
+ ::
+
+ $ python test.py myfirstscript.py
+
+This will create a file called "myfirstscript.py", containing the
+source of ``myfirstscript.py``; moreover it will execute the script
+and write its output in a file called "output.txt". I have tested
+all the scripts in this tutorial under Red Hat Linux 7.x and
+Windows 98SE. You should not have any problem in running them,
+but if a problem is there, "test.py" will probably discover it,
+even if, unfortunately, it will not provide the solution :-(.
+Notice that test.py requires Python 2.3+ to work, since most of
+the examples in this book heavily depends on the new features
+introduced in Python 2.2-2.3. Since the installation of Python
+2.3 is simple, quick and free, I think I am requiring to my readers
+who haven't upgraded yet a very little effort. This is well worth
+the pain since Python 2.3 fixes few bugs of 2.2 (notably in the subject of
+attribute descriptors and the ``super`` built-in) that makes
+
+You may give more arguments to test.py, as in this example:
+
+ ::
+
+ $ python test.py myfirstscript.py mysecondscript.py
+
+The output of both scripts will still be placed in the file "output.txt".
+Notice that if you give an argument which is not the name of a script in the
+book, it will be simply ignored. Morever, if you will not give any argument,
+"test.py" will automatically executes all the tutorial scripts, writing their
+output in "output.txt" [#]_ . You may want to give a look at this file, once
+you have finished the tutorial. It also contains the source code of
+the scripts, for better readability.
+
+Many examples of this tutorial depend on utility functions defined
+in a external module called ``oopp`` (``oopp`` is an obvious abbreviation
+for the title of the tutorial). The module ``oopp`` is automatically generated
+by "test.py", which works by extracting from the tutorial
+text blocks of code of the form ``#<oopp.py> something #</oopp.py>``
+and saving them in a file called "oopp.py".
+Let me give an example. A very recent enhancement to Python (in
+Python 2.3) has been the addition of a built-in boolean type with
+values True and False:
+
+ ::
+
+ $ python
+ Python 2.3a1 (#1, Jan 6 2003, 10:31:14)
+ [GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> 1+1==2
+ True
+ >>> 1+1==3
+ False
+ >>> type(True)
+ <type 'bool'>
+ >>> type(False)
+ <type 'bool'>
+
+
+However, previous version of Python use the integers 1 and 0 for
+True and False respectively.
+
+ ::
+
+ $ python
+ Python 2.2 (#1, Apr 12 2002, 15:29:57)
+ [GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-109)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> 1+1==2
+ 1
+ >>> 1+1==3
+ 0
+
+Following the 2.3 convension, in this tutorial I will use the names
+``True`` and ``False`` to denotes the numbers 1 and 0 respectively.
+This is automatic in Python 2.2.1+, but not in Python 2.2. Therefore,
+for sake of compatibility, it is convenient to set the values ``True``
+and ``False`` in our utility module:
+
+ ::
+
+ #<oopp.py>
+
+ import __builtin__
+ try:
+ __builtin__.True #look if True is already defined
+ except AttributeError: # if not add True and False to the builtins
+ __builtin__.True = 1
+ __builtin__.False = 0
+
+ #</oopp.py>
+
+
+Here there is an example of usage:
+
+ ::
+
+ #<mysecondscript.py>
+
+ import oopp
+ print "True =",True,
+ print "False =",False
+
+ #</mysecondscript.py>
+
+The output is "True = 1 False = 0" under Python 2.2 and
+"True = True False = False" under Python 2.3+.
+
+.. [#] "test.py", invoked without arguments, does not create '.py' files,
+ since I don't want to kludge the distribution with dozens of ten-line
+ scripts. I expect you may want to save only few scripts as standalone
+ programs, and cut and paste the others.
+
+Conventions used in this book
+----------------------------------------------------------------------
+
+Python expressions are denoted with monospaced fonts when in the text.
+Sections marked with an asterisk can be skipped in a first reading.
+Typically they have the purpose of clarifying some subtle point and
+are not needed for the rest of the book. These sections are intended
+for the advanced reader, but could confuse the beginner.
+An example is the section about the difference between methods and
+functions, or the difference between the inheritance constraint and
+the metaclass constraint.
+
+Introduction
+===========================================================================
+
+ .. line-block::
+
+ *A language that doesn't affect the way you think about programming,
+ is not worth knowing.* -- Alan Perlis
+
+
+Why OOP ?
+----------------------------
+
+I guess some of my readers, like me, have started programming in the mid-80's,
+when traditional (i.e. non object-oriented) Basic and Pascal where popular as
+first languages. At the time OOP was not as pervasive in software development
+how it is now, most of the mainstream languages were non-object-oriented and
+C++ was just being released. That was a time when the transition from
+spaghetti-code to structured code was already well accomplished, but
+the transition from structured programming to (the first phase of)
+OOP was at the beginning.
+
+Nowaydays, we live in a similar time of transition . Today, the transition
+to (the first phase of) OOP is well accomplished and essentially all
+mainstream
+languages support some elementary form of OOP. To be clear, when I say
+mainstream langauges, I have in mind Java and C++: C is a remarkable
+exception to the rule, since it is mainstream but not object-oriented.
+
+However, both Java an C++ (I mean standard Java and C++, not special
+extension like DTS C++, that have quite powerful object oriented features)
+are quite poor object-oriented languages: they provides only the most
+elementary aspects of OOP, the features of the *first phase* of OOP.
+
+Hence, today the transition to the *second phase* of OOP is only at the
+beginning, i.e mainstream language are not yet really OO, but they will
+become OOP in the near future.
+
+By second phase of OOP I mean the phase in which the primary
+objects of concern for the programmer are no more the objects, but the
+metaobjects. In elementary OOP one works on objects, which have attributes
+and methods (the evolution of old-fashioned data and functions) defined
+by their classes; in the second phase of OOP one works on classes
+which behavior is described by metaclasses. We no more modify objects
+trough classes: nowadays we modify classes and class hierarchies
+through metaclasses and multiple inheritance.
+
+It would be tempting to represent the history of programming in the last
+quarter of century with an evolutionary table like that:
+
+======================== ==================== ====================== =======
+ ~1975 ~1985 ~1995 ~2005
+======================== ==================== ====================== =======
+ procedural programming OOP1 OOP2 ?
+ data,functions objects,classes classes,metaclasses ?
+======================== ==================== ====================== =======
+
+The problem is that table would be simply wrong, since in truth
+Smalltalk had metaclasses already 25 years ago! And also Lisp
+had *in nuce* everything a long *long* time ago.
+The truth is that certains languages where too much ahead of their
+time ;-)
+
+Therefore, today we already have all the ideas
+and the conceptual tools to go beyond the first phase of OOP
+(they where invented 20-30 years ago), nevertheless those ideas are
+not yet universally known, nor implemented in mainstream languages.
+
+Fortunately, there are good languages
+where you can access the bonus of the second phase of OOP (Smalltalk, CLOS,
+Dylan, ...): unfortunately
+most of them are academic and/or little known in the real world
+(often for purely commercial reasons, since typically languages are not
+chosen accordingly to their merits, helas!). Python is an exception to this
+rule, in the sense that it is an eminently practical language (it started
+as a scripting language to do Operating System administrative jobs),
+which is relatively known and used in that application niche (even if some
+people *wrongly* think that should not be used for 'serious' things).
+
+There are various reasons why most mainstream languages are rather
+poor languages, i.e. underfeatured languages (as Java) or powerful, but too
+tricky to use, as C++. Some are good reasons (for instance *efficiency*: if
+efficiency is the first concern, then poor languages can be much
+better suited to the goal: for instance Fortran for number crunching
+and C for system programming), some are less good (economical
+monopoly). There is nothing to do against these reasons: if you
+need efficiency, or if you are forced to use a proprietary language
+because it is the language used by your employer. However, if you
+are free from these restrictions, there is another reason why you
+could not choose to use a poweful language. The reason is that,
+till now, programmers working in the industrial world mostly had simple
+problems (I mean conceptually simple problems). In order to solve
+simple problems one does not need a powerful language, and the effort
+spent in learning it is not worth.
+
+However, nowadays the situations has changed. Now, with Internet and graphics
+programming everywhere, and object-oriented languages so widespread,
+now it is the time when actually people *needs* metaprogramming, the
+ability to changing classes and programs. Now everybody is programming
+in the large.
+
+In this situation, it is justified to spend some time to learn better
+way of programming. And of course, it is convenient to start from
+the language with the flattest learning curve of all.
+
+Why Python ?
+-----------------------------------------------------------------------
+
+ .. line-block::
+
+ *In many ways, it's a dull language, borrowing solid old concepts from
+ many other languages & styles: boring syntax, unsurprising semantics,
+ few automatic coercions, etc etc. But that's one of the things I like
+ about it.* --Tim Peters on Python, 16 Sep 93
+
+If you are reading this book, I assume you already have some experience
+with Python. If this is the case, you already know the obvious advantages
+of Python such as readability, easy of use and short development time.
+Nevertheless, you could only have used Python as a fast and simple
+scripting language. If you are in this situation, then your risk to
+have an incorrect opinion on the language like "it is a nice little
+language, but too simple to be useful in 'real' applications". The
+truth is that Python is designed to be *simple*, and actually it
+is; but by no means it is a "shallow" language. Actually, it goes
+quite *deep*, but it takes some time to appreciate this fact.
+
+Let me contrast Python with Lisp, for instance. From the beginning,
+Lisp was intended to be a language for experts, for people with difficult
+problems to solve. The first
+users of Lisp were academicians, professors of CS and scientists.
+On the contrary, from the beginning Python
+was intended to be language for everybody (Python predecessor was ABC,
+a language invented to teach CS to children). Python makes great a first
+language for everybody, whereas Lisp would require especially
+clever and motivated students (and we all know that there is lack
+of them ;-)
+
+From this difference of origins, Python inherits an easy to learn syntax,
+whereas Lisp syntax is horrible for the beginner (even if not as
+horrible as C++ syntax ;-)
+
+
+ .. line-block::
+
+ *Macros are a powerful extension to weak languages.
+ Powerful languages don't need macros by definition.*
+ -- Christian Tismer on c.l.p. (referring to C)
+
+Despite the differences, Python borrows quite a lot from Lisp and it
+is nearly as expressive as it (I say nearly since Python is
+not as powerful as Lisp: by tradition, Lisp has always been on the top of
+hierarchy of programming language with respect to power of abstraction).
+It is true that Python lacks some powerful Lisp features: for instance
+Python object model lacks multiple dispatching (for the time being ;-)
+and the language lacks Lisp macros (but this unlikely to change in the
+near future since Pythonistas see the lack of macro as a Good Thing [#]_):
+nevertheless, the point is that Python is much *much* easier to learn.
+You have (nearly) all the power, but without the complexity.
+
+One of the reasons, is that Python
+try to be as *less* innovative as
+possible: it takes the proven good things from others, more innovative
+languages, and avoids their pitfalls. If you are an experienced
+programmer , it will be even easier to you to learn Python, since
+there is more or less nothing which is really original to Python.
+For instance:
+
+1. the object model is took from languages that are good at it, such
+ as Smalltalk;
+2. multiple inheritance has been modeled from languages good in it. such
+ as CLOS and Dylan;
+3. regular expression follows the road opened by Perl;
+4. functional features are borrowed from functional languages;
+5. the idea of documentation strings come from Lisp;
+6. list comprehension come from Haskell;
+7. iterators and generators come from Icon;
+8. etc. etc. (many other points here)
+
+I thinks the really distinctive feature of Python with respect to
+any other serious language I know, is that Python is *easy*. You have the
+power (I mean power in conceptual sense, not computational power: in
+the sense of computational power the best languages are
+non-object-oriented ones)
+of the most powerful languages with a very little investement.
+In addition to that, Python has a relatively large user base
+(as compared to Smalltalk or Ruby, or the various fragmented Lisp
+communities). Of course,
+there is quite a difference between the user base of Python with
+respect to the user base of, let say, VisualBasic or Perl. But
+I would never take in consideration VisualBasic for anything serious,
+whereas Perl is too ugly for my taste ;-).
+Finally, Python is *practical*. With this I mean the fact that
+Python has libraries that
+allow the user to do nearly everything, since you can access all the C/C++
+libraries with little or no effort, and all the Java libraries, though the
+Python implementation known as Jython. In particular, one has the choice
+between many excellent GUI's trough PyQt, wxPython, Tkinter, etc.
+
+Python started as an Object Oriented Programming
+Languages from the beginning, nevertheless is was never intended to be
+a *pure* OOPL as SmallTalk or, more recently, Ruby. Python is a
+*multiparadigm*
+language such a Lisp, that you choose your programming style according
+to your problem: spaghetti-code, structured programming, functional
+programming, object-oriented programming are all supported. You can
+even write bad code in Python, even if it is less simple than in other
+languages ;-). Python is a language which has quite evolved in its twelve
+years of life (the first public release was released in February 1991)
+and many new features have been integrated in the language with time.
+In particular, Python 2.2 (released in 2002) was a major breakthrough
+in the history of the language
+for what concerns support to Object Oriented Programming (OOP).
+Before the 2.2 revolution, Python Object
+Orientation was good; now it is *excellent*. All the fundamental features
+of OOP, including pretty sophisticated ones, as metaclasses and multiple
+inheritance, have now a very good support (the only missing thing is
+multiple dispatching).
+
+.. [#]
+ Python lacks macros for an intentional design choice: many people
+ in the community (including Guido itself) feel that macros are
+ "too powerful". If you give the user the freedom to create her
+ own language, you must face at least three problems: i) the risk
+ to split the original language in dozens of different dialects;
+ ii) in collaborative projects, the individual programmer must
+ spend an huge amount of time and effort would be spent in learning
+ macro systems written by others; iii) not all users are good
+ language designers: the programmer will have to fight with badly
+ designed macro systems. Due to these problems, it seems unlikely
+ that macros will be added to Python in the future.
+
+.. [#]
+ For a good comparison between Python and Lisp I remind the reader to
+ the excellent Peter Norvig's article in
+ http://www.norvig.com/python-lisp.html
+
+Further thoughts
+---------------------------------------------------------------------------
+
+Actually, the principal reasons why I begun studying
+Python was the documentation and the newsgroup: Python has an outstanding
+freely available documentation and an incredibly helpful newsgroup that
+make extremely easy to learn the language. If I had found a comparable
+free documentation/newsgroup for C++ or Lisp, I would have studied that
+languages instead.
+
+Unfortunately, the enormous development at the software level, had no
+correspondence with with an appropriate development of documentation.
+As a consequence, the many beatiful, powerful and extremely *useful*
+new features of Python 2.2+ object orientation are mostly remained
+confined to developers and power users: the average Python programmer
+has remained a little a part from the rapid development and she
+*wrongly* thinks she has no use for the new features. There have
+also been *protestations* of the users against developers of the
+kind "please, stop adding thousands of complicated new extensions
+to the language for which we have no use" !
+
+Extending a language is always a delicate thing to do, for a whole
+bunch of reasons:
+
+1. once one extension is done, it is there *forever*.
+
+
+My experience has been the following.
+
+When I first read about metaclasses, in Guido's essay
+"Unifying types and classes in Python 2.2", I thought "Wow,
+classes of classes, cool concept, but how useful is it?
+Are metaclasses really providing some new functionality?
+What can I do with metaclasses that I cannot do without?"
+
+Clearly, in these terms, the question is rather retorical, since in principle
+any Turing-complete programming languages contains all the features provided
+by metaclasses. Python metaclasses themselves are implemented in C, that has
+no metaclasses. Therefore, my real question was not "What can I do
+with metaclasses that I cannot do without?" but "How big is the convenience
+provided by metaclasses, with respect to my typical applications?".
+
+The answer depends on the kind of problem you are considering. For certain
+classes of problems it can be *very* large, as I will show in this and in
+the next chapters.
+
+I think the biggest advantage of metaclasses is *elegance*. Altough it
+is true that most of what you can do with metaclasses, can be done without
+metaclasses, not using metaclasses can result in a much *uglier* solution.
+
+
+One needs difficult problems in order to appreciate the advantage
+of powerful methods.
+
+
+If all you need is to write few scripts for copying two or three files,
+there is no point in learning OOP.On the other hand, if you only
+write simple programs where you define only one of two classes, there
+is no point in using metaclasses. Metaclasses becomes relevant only
+when you have many classes, whole classes of classes with similar
+features that you want to modify.
+
+In this sense, metaprogramming is for experts only, i.e. with people
+with difficult problems. The point however, is that nowaydays,
+many persons have difficult problems.
+
+Finally, let me conclude this preface by recalling the
+gist of Python wisdom.
+
+ >>> import this
+ The Zen of Python, by Tim Peters
+ .
+ Beautiful is better than ugly.
+ Explicit is better than implicit.
+ Simple is better than complex.
+ Complex is better than complicated.
+ Flat is better than nested.
+ Sparse is better than dense.
+ Readability counts.
+ Special cases aren't special enough to break the rules.
+ Although practicality beats purity.
+ Errors should never pass silently.
+ Unless explicitly silenced.
+ In the face of ambiguity, refuse the temptation to guess.
+ There should be one-- and preferably only one --obvious way to do it.
+ Although that way may not be obvious at first unless you're Dutch.
+ Now is better than never.
+ Although never is often better than *right* now.
+ If the implementation is hard to explain, it's a bad idea.
+ If the implementation is easy to explain, it may be a good idea.
+ Namespaces are one honking great idea -- let's do more of those!
+
+
+
+FIRST THINGS, FIRST
+==============================================================================
+
+This is an introductory chapter, with the main purpose of fixing the
+terminology used in the sequel. In particular, I give the definitions
+of objects, classes, attributes and methods. I discuss a few examples
+and I show some of the most elementary Python introspection features.
+
+What's an object?
+----------------------------------------------------------------------------
+
+ .. line-block::
+
+ *So Everything Is An object.
+ I'm sure the Smalltalkers are very happy :)*
+
+ -- Michael Hudson on comp.lang.python
+
+"What's an object" is the obvious question raised by anybody starting
+to learn Object Oriented Programming. The answer is simple: in Python,
+everything in an object!
+
+An operative definition is the following: an *object*
+is everything that can be labelled with an *object reference*.
+
+In practical terms, the object reference is implemented as
+the object memory address, that is an integer number which uniquely
+specify the object. There is a simple way to retrieve the object reference:
+to use the builtin ``id`` function. Informations on ``id`` can be retrieved
+via the ``help`` function [#]_:
+
+ >>> help(id)
+ Help on built-in function id:
+ id(...)
+ id(object) -> integer
+ Return the identity of an object. This is guaranteed to be unique among
+ simultaneously existing objects. (Hint: it's the object's memory address.)
+
+The reader is strongly encouraged to try the help function on everything
+(including help(help) ;-). This is the best way to learn how Python works,
+even *better* than reading the standard documentation, since the on-line
+help is often more update.
+
+Suppose for instance we wonder if the number ``1`` is an object:
+it is easy enough to ask Python for the answer:
+
+ >>> id(1)
+ 135383880
+
+Therefore the number 1 is a Python object and it is stored at the memory
+address 135383880, at least in my computer and during the current session.
+Notice that the object reference is a dynamic thing; nevertheless it
+is guaranteed to be unique and constant for a given object during its
+lifetime (two objects whose lifetimes are disjunct may have the same id()
+value, though).
+
+Here there are other examples of built-in objects:
+
+ >>> id(1L) # long
+ 1074483312
+ >>> id(1.0) #float
+ 135682468
+ >>> id(1j) # complex
+ 135623440
+ >>> id('1') #string
+ 1074398272
+ >>> id([1]) #list
+ 1074376588
+ >>> id((1,)) #tuple
+ 1074348844
+ >>> id({1:1}) # dict
+ 1074338100
+
+Even functions are objects:
+
+ >>> def f(x): return x #user-defined function
+ >>> id(f)
+ 1074292020
+ >>> g=lambda x: x #another way to define functions
+ >>> id(g)
+ 1074292468
+ >>> id(id) #id itself is a built-in function
+ 1074278668
+
+Modules are objects, too:
+
+ >>> import math
+ >>> id(math) #module of the standard library
+ 1074239068
+ >>> id(math.sqrt) #function of the standard library
+ 1074469420
+
+``help`` itself is an object:
+
+ >>> id(help)
+ 1074373452
+
+Finally, we may notice that the reserved keywords are not objects:
+
+ >>> id(print) #error
+ File "<string>", line 1
+ id(print) ^
+ SyntaxError: invalid syntax
+
+The operative definition is convenient since it gives a practical way
+to check if something is an object and, more importantly, if two
+objects are the same or not:
+
+ .. doctest
+
+ >>> s1='spam'
+ >>> s2='spam'
+ >>> s1==s2
+ True
+ >>> id(s1)==id(s2)
+ True
+
+A more elegant way of spelling ``id(obj1)==id(obj2)`` is to use the
+keyword ``is``:
+
+ >>> s1 is s2
+ True
+
+However, I should warn the reader that sometimes ``is`` can be surprising:
+
+ >>> id([]) == id([])
+ True
+ >>> [] is []
+ False
+
+This is happening because writing ``id([])`` dynamically creates an unique
+object (a list) which goes away when you're finished with it. So when an
+expression needs both at the same time (``[] is []``), two unique objects
+are created, but when an expression doesn't need both at the same time
+(``id([]) == id([])``), an object gets created with an ID, is destroyed,
+and then a second object is created with the same ID (since the last one
+just got reclaimed) and their IDs compare equal. In other words, "the
+ID is guaranteed to be unique *only* among simultaneously existing objects".
+
+Another surprise is the following:
+
+ >>> a=1
+ >>> b=1
+ >>> a is b
+ True
+ >>> a=556
+ >>> b=556
+ >>> a is b
+ False
+
+The reason is that integers between 0 and 99 are pre-instantiated by the
+interpreter, whereas larger integers are recreated each time.
+
+Notice the difference between '==' and 'is':
+
+ >>> 1L==1
+ True
+
+but
+
+ >>> 1L is 1
+ False
+
+since they are different objects:
+
+ >>> id(1L) # long 1
+ 135625536
+ >>> id(1) # int 1
+ 135286080
+
+
+The disadvantage of the operative definition is that it gives little
+understanding of what an object can be used for. To this aim, I must
+introduce the concept of *class*.
+
+.. [#] Actually ``help`` is not a function but a callable object. The
+ difference will be discussed in a following chapter.
+
+Objects and classes
+---------------------------------------------------------------------------
+
+It is convenient to think of an object as an element of a set.
+
+It you think a bit, this is the most general definition that actually
+grasps what we mean by object in the common language.
+For instance, consider this book, "Object Oriented Programming in Python":
+this book is an object, in the sense that it is a specific representative
+of the *class* of all possible books.
+According to this definition, objects are strictly related to classes, and
+actually we say that objects are *instances* of classes.
+
+Classes are nested: for
+instance this book belongs to the class of books about programming
+language, which is a subset of the class of all possible books;
+moreover we may further specify this book as a Python book; moreover
+we may specify this book as a Python 2.2+ book. There is no limit
+to the restrictions we may impose to our classes.
+On the other hand. it is convenient to have a "mother" class,
+such that any object belongs to it. All strongly Object Oriented
+Language have such a class [#]_; in Python it is called *object*.
+
+The relation between objects and classes in Python can be investigated
+trough the built-in function ``type`` [#]_ that gives the class of any
+Python object.
+
+Let me give some example:
+
+1. Integers numbers are instances of the class ``int`` or ``long``:
+
+ >>> type(1)
+ <type 'int'>
+ >>> type(1L)
+ <type 'long'>
+
+2. Floating point numbers are instances of the class ``float``:
+
+ >>> type(1.0)
+ <type 'float'>
+
+
+3. Complex numbers are instances of the class ``complex``:
+
+ >>> type(1.0+1.0j)
+ <type 'complex'>
+
+4. Strings are instances of the class ``str``:
+
+ >>> type('1')
+ <type 'str'>
+
+
+5. List, tuples and dictionaries are instances of ``list``, ``tuple`` and
+ ``dict`` respectively:
+
+ >>> type('1')
+ <type 'str'>
+ >>> type([1])
+ <type 'list'>
+ >>> type((1,))
+ <type 'tuple'>
+ >>> type({1:1})
+ <type 'dict'>
+
+6. User defined functions are instances of the ``function`` built-in type
+
+ >>> type(f)
+ <type 'function'>
+ >>> type(g)
+ <type 'function'>
+
+All the previous types are subclasses of object:
+
+ >>> for cl in int,long,float,str,list,tuple,dict: issubclass(cl,object)
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+
+However, Python is not a 100% pure Object
+Oriented Programming language and its object model has still some minor
+warts, due to historical accidents.
+
+Paraphrasing George Orwell, we may say that in Python 2.2-2.3,
+all objects are equal, but some objects are more equal than others.
+Actually, we may distinguish Python objects in new style objects,
+or rich man objects, and old style objects, or poor man objects.
+New style objects are instances of new style classes whereas old
+style objects are instances of old style classes.
+The difference is that new style classes are subclasses of object whereas
+old style classes are not.
+
+Old style classes are there for sake of compatibility with previous
+releases of Python, but starting from Python 2.2 practically all built-in
+classes are new style classes.
+
+Instance of old style classes are called old style objects. I will give
+few examples of old style objects in the future.
+
+In this tutorial with the term
+object *tout court* we will mean new style objects, unless the contrary
+is explicitely stated.
+
+
+.. [#] one may notice that C++ does not have such a class, but C++
+ is *not* a strongly object oriented language ;-)
+
+.. [#] Actually ``type`` is not a function, but a metaclass; nevertheless,
+ since this is an advanced concept, discussed in the fourth chapter;
+ for the time being it is better to think of ``type`` as a built-in
+ function analogous to ``id``.
+
+Objects have attributes
+----------------------------------------------------------------------------
+
+All objects have attributes describing their characteristics, that may
+be accessed via the dot notation
+
+ ::
+
+ objectname.objectattribute
+
+The dot notation is common to most Object Oriented programming languages,
+therefore the reader with a little of experience should find it not surprising
+at all (Python strongly believes in the Principle of Least Surprise). However,
+Python objects also have special attributes denoted by the double-double
+underscore notation
+
+ ::
+
+ objectname.__specialattribute__
+
+with the aim of helping the wonderful Python introspection features, that
+does not have correspondence in all OOP language.
+
+Consider for example the string literal "spam". We may discover its
+class by looking at its special attribute *__class__*:
+
+ >>> 'spam'.__class__
+ <type 'str'>
+
+
+Using the ``__class__`` attribute is not always equivalent to using the
+``type`` function, but it works for all built-in types. Consider for instance
+the number *1*: we may extract its class as follows:
+
+ >>> (1).__class__
+ <type 'int'>
+
+Notice that the parenthesis are needed to avoid confusion between the integer
+1 and the float (1.).
+
+The non-equivalence type/class is the key to distinguish new style objects from
+old style, since for old style objects ``type(obj)<>obj.__class__``.
+We may use this knowledge to make and utility function that discovers
+if an object is a "real" object (i.e. new style) or a poor man object:
+
+ ::
+
+ #<oopp.py>
+
+ def isnewstyle(obj):
+ try: #some objects may lack a __class__ attribute
+ obj.__class__
+ except AttributeError:
+ return False
+ else: #look if there is unification type/class
+ return type(obj) is obj.__class__
+ #</oopp.py>
+
+Let us check this with various examples:
+
+ >>> from oopp import isnewstyle
+ >>> isnewstyle(1)
+ True
+ >>> isnewstyle(lambda x:x)
+ True
+ >>> isnewstyle(id)
+ True
+ >>> isnewstyle(type)
+ True
+ >>> isnewstyle(isnewstyle)
+ True
+ >>> import math
+ >>> isnewstyle(math)
+ True
+ >>> isnewstyle(math.sqrt)
+ True
+ >>> isnewstyle('hello')
+ True
+
+It is not obvious to find something which is not a real object,
+between the built-in objects, however it is possible. For instance,
+the ``help`` "function" is an old style object:
+
+ >>> isnewstyle(help)
+ False
+
+since
+
+ >>> help.__class__
+ <class site._Helper at 0x8127c94>
+
+is different from
+
+ >>> type(help)
+ <type 'instance'>
+
+Regular expression objects are even poorer objects with no ``__class__``
+attribute:
+
+ >>> import re
+ >>> reobj=re.compile('somestring')
+ >>> isnewstyle(reobj)
+ False
+ >>> type(reobj)
+ <type '_sre.SRE_Pattern'>
+ >>> reobj.__class__ #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: __class__
+
+There other special attributes other than ``__class__``; a particularly useful
+one is ``__doc__``, that contains informations on the class it
+refers to. Consider for instance the ``str`` class: by looking at its
+``__doc__`` attribute we can get information on the usage of this class:
+
+ >>> str.__doc__
+ str(object) -> string
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+From that docstring we learn how to convert generic objects in strings;
+for instance we may convert numbers, lists, tuples and dictionaries:
+
+ >>> str(1)
+ '1'
+ >>> str([1])
+ '[1]'
+ >>> str((1,))
+ (1,)'
+ >>> str({1:1})
+ '{1: 1}'
+
+``str`` is implicitely called each time we use the ``print`` statement, since
+``print obj`` is actually syntactic sugar for ``print str(obj)``.
+
+Classes and modules have another interesting special attribute, the
+``__dict__`` attribute that gives the content of the class/module.
+For instance, the contents of the standard ``math`` module can be retrieved
+as follows:
+
+ >>> import math
+ >>> for key in math.__dict__: print key,
+ ...
+ fmod atan pow __file__ cosh ldexp hypot sinh __name__ tan ceil asin cos
+ e log fabs floor tanh sqrt __doc__ frexp atan2 modf exp acos pi log10 sin
+
+Alternatively, one can use the built-in function ``vars``:
+
+ >>> vars(math) is math.__dict__
+ True
+
+This identity is true for any object with a ``__dict__`` attribute.
+Two others interesting special attributes are ``__doc__``
+
+ >>> print math.__doc__
+ This module is always available. It provides access to the
+ mathematical functions defined by the C standard.
+
+and ``__file__``:
+
+ >>> math.__file__ #gives the file associated with the module
+ '/usr/lib/python2.2/lib-dynload/mathmodule.so'
+
+Objects have methods
+----------------------------------------------------------------------------
+
+In addition to attributes, objects also have *methods*, i.e.
+functions attached to their classes [#]_.
+Methods are also invoked with the dot notation, but
+they can be distinguished by attributes because they are typically
+called with parenthesis (this is a little simplistic, but it is enough for
+an introductory chapter). As a simple example, let me show the
+invocation of the ``split`` method for a string object:
+
+ >>> s='hello world!'
+ >>> s.split()
+ ['hello', 'world!']
+
+In this example ``s.split`` is called a *bount method*, since it is
+applied to the string object ``s``:
+
+ >>> s.split
+ <built-in method split of str object at 0x81572b8>
+
+An *unbound method*, instead, is applied to the class: in this case the
+unbound version of ``split`` is applied to the ``str`` class:
+
+ >>> str.split
+ <method 'split' of 'str' objects>
+
+A bound method is obtained from its corresponding unbound
+method by providing the object to the unbound method: for instance
+by providing ``s`` to ``str.split`` we obtain the same effect of `s.split()`:
+
+ >>> str.split(s)
+ ['hello', 'world!']
+
+This operation is called *binding* in the Python literature: when write
+``str.split(s)`` we bind the unbound method ``str.split`` to the object ``s``.
+It is interesting to recognize that the bound and unbound methods are
+*different* objects:
+
+ >>> id(str.split) # unbound method reference
+ 135414364
+ >>> id(s.split) # this is a different object!
+ 135611408
+
+The unbound method (and therefore the bound method) has a ``__doc__``
+attribute explaining how it works:
+
+ >>> print str.split.__doc__
+ S.split([sep [,maxsplit]]) -> list of strings
+ Return a list of the words in the string S, using sep as the
+ delimiter string. If maxsplit is given, at most maxsplit
+ splits are done. If sep is not specified or is None, any
+ whitespace string is a separator.
+
+
+.. [#] A precise definition will be given in chapter 5 that introduces the
+ concept of attribute descriptors. There are subtle
+ differences between functions and methods.
+
+Summing objects
+--------------------------------------------------------------------------
+
+In a pure object-oriented world, there are no functions and everything is
+done trough methods. Python is not a pure OOP language, however quite a
+lot is done trough methods. For instance, it is quite interesting to analyze
+what happens when an apparently trivial statement such as
+
+ >>> 1+1
+ 2
+
+is executed in an object-oriented world.
+
+The key to understand, is to notice that the number 1 is an object, specifically
+an instance of class ``int``: this means that that 1 inherits all the methods
+of the ``int`` class. In particular it inherits a special method called
+``__add__``: this means 1+1 is actually syntactic sugar for
+
+ >>> (1).__add__(1)
+ 2
+
+which in turns is syntactic sugar for
+
+ >>> int.__add__(1,1)
+ 2
+
+The same is true for subtraction, multiplication, division and other
+binary operations.
+
+ >>> 'hello'*2
+ 'hellohello'
+ >>> (2).__mul__('hello')
+ 'hellohello'
+ >>> str.__mul__('hello',2)
+ 'hellohello'
+
+However, notice that
+
+ >>> str.__mul__(2,'hello') #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: descriptor '__mul__' requires a 'str' object but received a 'int'
+
+The fact that operators are implemented as methods, is the key to
+*operator overloading*: in Python (as well as in other OOP languages)
+the user can redefine the operators. This is already done by default
+for some operators: for instance the operator ``+`` is overloaded
+and works both for integers, floats, complex numbers and for strings.
+
+Inspecting objects
+---------------------------------------------------------------------------
+
+In Python it is possible to retrieve most of the attributes and methods
+of an object by using the built-in function ``dir()``
+(try ``help(dir)`` for more information).
+
+Let me consider the simplest case of a generic object:
+
+ >>> obj=object()
+ >>> dir(obj)
+ ['__class__', '__delattr__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__']
+
+As we see, there are plenty of attributes available
+even to a do nothing object; many of them are special attributes
+providing introspection capabilities which are not
+common to all programming languages. We have already discussed the
+meaning of some of the more obvious special attributes.
+The meaning of some of the others is quite non-obvious, however.
+The docstring is invaluable in providing some clue.
+
+Notice that there are special *hidden* attributes that cannot be retrieved
+with ``dir()``. For instance the ``__name__`` attribute, returning the
+name of the object (defined for classes, modules and functions)
+and the ``__subclasses__`` method, defined for classes and returning the
+list of immediate subclasses of a class:
+
+ >>> str.__name__
+ 'str'
+ >>> str.__subclasses__.__doc__
+ '__subclasses__() -> list of immediate subclasses'
+ >>> str.__subclasses__() # no subclasses of 'str' are currently defined
+ []
+
+For instance by doing
+
+ >>> obj.__getattribute__.__doc__
+ "x.__getattribute__('name') <==> x.name"
+
+we discover that the expression ``x.name`` is syntactic sugar for
+
+ ``x.__getattribute__('name')``
+
+Another equivalent form which is more often used is
+
+ ``getattr(x,'name')``
+
+We may use this trick to make a function that retrieves all the
+attributes of an object except the special ones:
+
+ ::
+
+ #<oopp.py>
+
+ def special(name): return name.startswith('__') and name.endswith('__')
+
+ def attributes(obj,condition=lambda n,v: not special(n)):
+ """Returns a dictionary containing the accessible attributes of
+ an object. By default, returns the non-special attributes only."""
+ dic={}
+ for attr in dir(obj):
+ try: v=getattr(obj,attr)
+ except: continue #attr is not accessible
+ if condition(attr,v): dic[attr]=v
+ return dic
+
+ getall = lambda n,v: True
+
+ #</oopp.py>
+
+Notice that certain attributes may be unaccessible (we will see how
+to make attributes unaccessible in a following chapter)
+and in this case they are simply ignored.
+For instance you may retrieve the regular (i.e. non special)
+attributes of the built-in functions:
+
+ >>> from oopp import attributes
+ >>> attributes(f).keys()
+ ['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']
+
+In the same vein of the ``getattr`` function, there is a built-in
+``setattr`` function (that actually calls the ``__setattr__`` built-in
+method), that allows the user to change the attributes and methods of
+and object. Informations on ``setattr`` can be retrieved from the help
+function:
+
+ ::
+
+ >>> help(setattr)
+ Help on built-in function setattr:
+ setattr(...)
+ setattr(object, name, value)
+ Set a named attribute on an object; setattr(x, 'y', v) is equivalent to
+ ``x.y = v''.
+
+``setattr`` can be used to add attributes to an object:
+
+ ::
+
+ #<oopp.py>
+
+ import sys
+
+ def customize(obj,errfile=None,**kw):
+ """Adds attributes to an object, if possible. If not, writes an error
+ message on 'errfile'. If errfile is None, skips the exception."""
+ for k in kw:
+ try:
+ setattr(obj,k,kw[k])
+ except: # setting error
+ if errfile:
+ print >> errfile,"Error: %s cannot be set" % k
+
+ #</oopp.py>
+
+The attributes of built-in objects cannot be set, however:
+
+ >>> from oopp import customize,sys
+ >>> customize(object(),errfile=sys.stdout,newattr='hello!') #error
+ AttributeError: newattr cannot be set
+
+On the other hand, the attributes of modules can be set:
+
+ >>> import time
+ >>> customize(time,newattr='hello!')
+ >>> time.newattr
+ 'hello!'
+
+Notice that this means we may enhances modules at run-time, but adding
+new routines, not only new data attributes.
+
+The ``attributes`` and ``customize`` functions work for any kind of objects;
+in particular, since classes are a special kind of objects, they work
+for classes, too. Here are the attributes of the ``str``, ``list`` and
+``dict`` built-in types:
+
+ >>> from oopp import attributes
+ >>> attributes(str).keys()
+ ['startswith', 'rjust', 'lstrip', 'swapcase', 'replace','encode',
+ 'endswith', 'splitlines', 'rfind', 'strip', 'isdigit', 'ljust',
+ 'capitalize', 'find', 'count', 'index', 'lower', 'translate','join',
+ 'center', 'isalnum','title', 'rindex', 'expandtabs', 'isspace',
+ 'decode', 'isalpha', 'split', 'rstrip', 'islower', 'isupper',
+ 'istitle', 'upper']
+ >>> attributes(list).keys()
+ ['append', 'count', 'extend', 'index', 'insert', 'pop',
+ 'remove', 'reverse', 'sort']
+ >>> attributes(dict).keys()
+ ['clear','copy','fromkeys', 'get', 'has_key', 'items','iteritems',
+ 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault',
+ 'update', 'values']
+
+Classes and modules have a special attribute ``__dict__`` giving the
+dictionary of their attributes. Since it is often a quite large dictionary,
+it is convenient to define an utility function printing this dictionary in a
+nice form:
+
+ ::
+
+ #<oopp.py>
+
+ def pretty(dic):
+ "Returns a nice string representation for the dictionary"
+ keys=dic.keys(); keys.sort() # sorts the keys
+ return '\n'.join(['%s = %s' % (k,dic[k]) for k in keys])
+
+ #</oopp.py>
+
+I encourage the use of this function in order to retrieve more
+information about the modules of the standard library:
+
+ >>> from oopp import pretty
+ >>> import time #look at the 'time' standard library module
+ >>> print pretty(vars(time))
+ __doc__ = This module provides various functions to manipulate time values.
+ There are two standard representations of time. One is the number
+ of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer
+ or a floating point number (to represent fractions of seconds).
+ The Epoch is system-defined; on Unix, it is generally January 1st, 1970.
+ The actual value can be retrieved by calling gmtime(0).
+ The other representation is a tuple of 9 integers giving local time.
+ The tuple items are:
+ year (four digits, e.g. 1998)
+ month (1-12)
+ day (1-31)
+ hours (0-23)
+ minutes (0-59)
+ seconds (0-59)
+ weekday (0-6, Monday is 0)
+ Julian day (day in the year, 1-366)
+ DST (Daylight Savings Time) flag (-1, 0 or 1)
+ If the DST flag is 0, the time is given in the regular time zone;
+ if it is 1, the time is given in the DST time zone;
+ if it is -1, mktime() should guess based on the date and time.
+ Variables:
+ timezone -- difference in seconds between UTC and local standard time
+ altzone -- difference in seconds between UTC and local DST time
+ daylight -- whether local time should reflect DST
+ tzname -- tuple of (standard time zone name, DST time zone name)
+ Functions:
+ time() -- return current time in seconds since the Epoch as a float
+ clock() -- return CPU time since process start as a float
+ sleep() -- delay for a number of seconds given as a float
+ gmtime() -- convert seconds since Epoch to UTC tuple
+ localtime() -- convert seconds since Epoch to local time tuple
+ asctime() -- convert time tuple to string
+ ctime() -- convert time in seconds to string
+ mktime() -- convert local time tuple to seconds since Epoch
+ strftime() -- convert time tuple to string according to format specification
+ strptime() -- parse string to time tuple according to format specification
+ __file__ = /usr/local/lib/python2.3/lib-dynload/time.so
+ __name__ = time
+ accept2dyear = 1
+ altzone = 14400
+ asctime = <built-in function asctime>
+ clock = <built-in function clock>
+ ctime = <built-in function ctime>
+ daylight = 1
+ gmtime = <built-in function gmtime>
+ localtime = <built-in function localtime>
+ mktime = <built-in function mktime>
+ newattr = hello!
+ sleep = <built-in function sleep>
+ strftime = <built-in function strftime>
+ strptime = <built-in function strptime>
+ struct_time = <type 'time.struct_time'>
+ time = <built-in function time>
+ timezone = 18000
+ tzname = ('EST', 'EDT')
+
+The list of the built-in Python types can be found in the ``types`` module:
+
+ >>> import types
+ >>> t_dict=dict([(k,v) for (k,v) in vars(types).iteritems()
+ ... if k.endswith('Type')])
+ >>> for t in t_dict: print t,
+ ...
+ DictType IntType TypeType FileType CodeType XRangeType EllipsisType
+ SliceType BooleanType ListType MethodType TupleType ModuleType FrameType
+ StringType LongType BuiltinMethodType BufferType FloatType ClassType
+ DictionaryType BuiltinFunctionType UnboundMethodType UnicodeType
+ LambdaType DictProxyType ComplexType GeneratorType ObjectType
+ FunctionType InstanceType NoneType TracebackType
+
+For a pedagogical account of the most elementary
+Python introspection features,
+Patrick O' Brien:
+http://www-106.ibm.com/developerworks/linux/library/l-pyint.html
+
+Built-in objects: iterators and generators
+---------------------------------------------------------------------------
+
+At the end of the last section , I have used the ``iteritems`` method
+of the dictionary, which returns an iterator:
+
+ >>> dict.iteritems.__doc__
+ 'D.iteritems() -> an iterator over the (key, value) items of D'
+
+Iterators (and generators) are new features of Python 2.2 and could not be
+familiar to all readers. However, since they are unrelated to OOP, they
+are outside the scope of this book and will not be discussed here in detail.
+Nevertheless, I will give a typical example of use of a generator, since
+this construct will be used in future chapters.
+
+At the syntactical level, a generator is a "function" with (at least one)
+``yield`` statement (notice that in Python 2.2 the ``yield`` statement is
+enabled trough the ``from __future__ import generators`` syntax):
+
+
+ ::
+
+ #<oopp.py>
+
+ import re
+
+ def generateblocks(regexp,text):
+ "Generator splitting text in blocks according to regexp"
+ start=0
+ for MO in regexp.finditer(text):
+ beg,end=MO.span()
+ yield text[start:beg] # actual text
+ yield text[beg:end] # separator
+ start=end
+ lastblock=text[start:]
+ if lastblock: yield lastblock; yield ''
+
+ #</oopp.py>
+
+In order to understand this example, the reader my want to refresh his/her
+understanding of regular expressions; since this is not a subject for
+this book, I simply remind the meaning of ``finditer``:
+
+ >>> import re
+ >>> help(re.finditer)
+ finditer(pattern, string)
+ Return an iterator over all non-overlapping matches in the
+ string. For each match, the iterator returns a match object.
+ Empty matches are included in the result.
+
+Generators can be thought of as resumable functions that stop at the
+``yield`` statement and resume from the point where they left.
+
+ >>> from oopp import generateblocks
+ >>> text='Python_Rules!'
+ >>> g=generateblocks(re.compile('_'),text)
+ >>> g
+ <generator object at 0x401b140c>
+ >>> dir(g)
+ ['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
+ '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__',
+ '__repr__', '__setattr__', '__str__', 'gi_frame', 'gi_running', 'next']
+
+Generator objects can be used as iterators in a ``for`` loop.
+In this example the generator takes a text and a regular expression
+describing a fixed delimiter; then it splits the text in blocks
+according to the delimiter. For instance, if the delimiter is
+'_', the text 'Python Rules!' is splitted as 'Python', '_' and 'Rules!':
+
+ >>> for n, block in enumerate(g): print n, block
+ ...
+ 0 Python
+ 1
+ 2 Rules!
+ 3
+
+This example also show the usage of the new Python 2.3 built-in ``enumerate``.
+
+Under the hood the ``for`` loop is calling the generator via its
+``next`` method, until the ``StopIteration`` exception is raised.
+For this reason a new call to the ``for`` loop will have no effect:
+
+ >>> for n, block in enumerate(g): print n, block
+ ...
+
+The point is that the generator has already yield its last element:
+
+ >>> g.next() # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ StopIteration
+
+``generateblocks`` always returns an even number of blocks; odd blocks
+are delimiters whereas even blocks are the intertwining text; there may be
+empty blocks, corresponding to the null string ''.
+
+It must be remarked the difference with the 'str.split' method
+
+ >>> 'Python_Rules!'.split('_')
+ ['Python', 'Rules!']
+
+and the regular expression split method:
+
+ >>> re.compile('_').split('Python_Rules!')
+ ['Python', 'Rules!']
+
+both returns lists with an odd number of elements and both miss the separator.
+The regular expression split method can catch the separator, if wanted,
+
+ >>> re.compile('(_)').split('Python_Rules!')
+ ['Python', '_', 'Rules!']
+
+but still is different from the generator, since it returns a list. The
+difference is relevant if we want to split a very large text, since
+the generator avoids to build a very large list and thus it is much more
+memory efficient (it is faster, too). Moreover, ``generateblocks``
+works differently in the case of multiple groups:
+
+ >>> delim=re.compile('(_)|(!)') #delimiter is space or exclamation mark
+ >>> for n, block in enumerate(generateblocks(delim,text)):
+ ... print n, block
+ 0 Python
+ 1 _
+ 2 Rules
+ 3 !
+
+whereas
+
+ >>> delim.split(text)
+ ['Python', '_', None, 'Rules', None, '!', '']
+
+gives various unwanted ``None`` (which could be skipped with
+``[x for x in delim.split(text) if x is not None]``); notice, that
+there are no differences (apart from the fact that ``delim.split(text)``
+has an odd number of elements) when one uses a single group regular expression:
+
+ >>> delim=re.compile('(_|!)')
+ >>> delim.split(text)
+ ['Python', '_', 'Rules', '!', '']
+
+The reader unfamiliar with iterators and generators is encouraged
+to look at the standard documentation and other
+references. For instance, there are Alex Martelli's notes on iterators at
+http://www.strakt.com/dev_talks.html
+and there is a good article on generators by David Mertz
+http://www-106.ibm.com/developerworks/linux/library/l-pycon.html
+
+
+THE CONVENIENCE OF FUNCTIONS
+============================================================================
+
+Functions are the most basic Python objects. They are also the simplest
+objects where one can apply the metaprogramming techniques that are
+the subject of this book. The tricks used in this chapter and the utility
+functions defined here will be used over all the book. Therefore this
+is an *essential* chapter.
+
+Since it is intended to be a gentle introduction, the tone will be
+informal.
+
+Introduction
+-------------
+
+One could be surprised that a text on OOP begins with a chapter on the
+well known old-fashioned functions. In some sense, this is also
+against the spirit of an important trend in OOP, which tries to
+shift the focus from functions to data. In pure OOP languages,
+there are no more functions, only methods. [#]_
+
+However, there are good reasons for that:
+
+1. In Python, functions *are* objects. And particularly useful ones.
+2. Python functions are pretty powerful and all their secrets are probably
+ *not* well known to the average Python programmer.
+3. In the solutions of many problems, you don't need the full apparatus
+ of OOP: good old functions can be enough.
+
+Moreover, I am a believer in the multiparadigm approach to programming,
+in which you choose your tools according to your problem.
+With a bazooka you can kill a mosquito, yes, but this does not mean
+that you must use the bazooka *always*.
+In certain languages, you have no choice, and you must define
+a class (involving a lot of boiler plate code) even for the most trivial
+application. Python's philosophy is to keep simple things simple, but
+having the capability of doing even difficult things with a reasonable
+amount of effort. The message of this chapter will be: "use functions when
+you don't need classes". Functions are good because:
+
+1. They are easy to write (no boiler plate);
+2. They are easy to understand;
+3. They can be reused in your code;
+4. Functions are an essential building block in the construction of objects.
+
+Even if I think that OOP is an extremely effective strategy, with
+enormous advantages on design, maintanibility and reusability of code,
+nevertheless this book is *not* intended to be a panegyric of OOP. There
+are cases in which you don't need OOP. I think the critical parameter is
+the size of the program. These are the rules I follows usually (to be
+taken as indicative):
+
+1. If I have to write a short script of 20-30 lines, that copies two or
+ three files and prints some message, I use fast and dirty spaghetti-code;
+ there is no use for OOP.
+2. If your script grows to one-hundred lines or more, I structure
+ it write a few routines and a main program: but still I can live
+ without OOP.
+3. If the script goes beyond the two hundred lines, I start
+ collecting my routines in few classes.
+4. If the script goes beyond the five hundred lines, I split the program
+ in various files and modules and convert it to a package.
+5. I never write a function longer than 50 lines, since 50 lines is more
+ or less the size of a page in my editor, and I need to be able to
+ see the entire function in a page.
+
+Of course your taste could be different and you could prefer to write a
+monolitic program of five thousand lines; however the average size of
+the modules in the Python standard library is of 111 lines.
+I think this is a *strong* suggestion towards
+a modular style of programming, which
+is *very* well supported in Python.
+
+The point is that OOP is especially useful for *large* programs: if you
+only use Python for short system administration scripts you may well
+live without OOP. Unfortunaly, as everybody knows, short scripts have
+an evil tendency to become medium size scripts, and medium size scripts
+have the even more evil tendency to become large scripts and possible
+even full featured applications ! For this reason it is very probable
+that at a certain moment you will feel the need for OOP.
+
+I remember my first big program, a long time ago: I wrote a program
+to draw mathematical functions in AmigaBasic. It was good and nice
+until it had size of few hundred lines; but when it passed a thousand
+of lines, it became rapidly unmanageable and unmaintenable. There where
+three problems:
+
+1. I could not split the program in modules, as I wanted, due to the
+ limitations of AmigaBasic;
+
+2. I was missing OOP to keep the logic of the program all together, but
+ at the time I didn't know that;
+
+3. I was missing effective debugging techniques.
+
+4. I was missing effective refactoring tools.
+
+I am sure anybody who has ever written a large program has run in these
+limitations: and the biggest help of OOP is in overcoming these limitations.
+Obviously, miracles are impossible, and even object oriented programs can
+grow to a size where they become unmaintanable: the point is that the
+critical limit is much higher than the thousand lines of structured programs.
+I haven't yet reached the limit of unmanageability with Python. The fact
+that the standard library is 66492 lines long (as result from the total
+number of lines in ``/usr/local/lib/python2.2/``), but it is still manageable,
+give me an hope ;-)
+
+ .. [#] However, one could argue that having functions distinguished from
+ methods is the best thing to do, even in a strongly object-oriented
+ world. For instance, generic functions can be used to implement
+ multimethods. See for instance Lisp, Dylan and MultiJava. This latter
+ is forced to introduce the concept of function outside a class,
+ foreign to traditional Java, just to implement multimethods.
+
+A few useful functions
+------------------------------------------------------------------------------
+
+It is always a good idea to have a set of useful function collected in
+a user defined module. The first function we want to have in our module
+is the ``do_nothing`` function:
+
+ ::
+
+ #<oopp.py>
+
+ def do_nothing(*args,**kw): pass
+
+ #</oopp.py>
+
+This function accept a variable number of arguments and keywords (I
+defer the reader to the standard documentation if she is unfamiliar
+with these concept; this is *not* another Python tutorial ;-) and
+return ``None``. It is very useful for debugging purposes, when in a
+complex program you may want concentrate your attention to few crucial
+functions and set the non-relevant functions to ``do_nothing`` functions.
+
+A second function which is useful in developing programs is a timer
+function. Very ofter indeed, we may want to determine the bottleneck
+parts of a program, we are interested in profiling them and in seeing
+if we can improve the speed by improving the algorithm, or by using
+a Python "compiler" such as Psyco, or if really we need to write a C
+extension. In my experience, I never needed to write a C extension,
+since Python is fast enough. Nevertheless, to profile a program is
+always a good idea and Python provides a profiler module in the
+stardard library with this aim. Still, it is convenient to have
+a set of user defined functions to test the execution speed of
+few selected routines (whereas the standard profiler profiles everything).
+
+We see from the standard library documentation that
+the current time can be retrieved from the ``time`` module: [#]_
+
+ >>> import time
+ >>> time.asctime()
+ 'Wed Jan 15 12:46:03 2003'
+
+Since we are not interested in the date but only in the time, we need
+a function to extract it. This is easily implemented:
+
+ ::
+
+ #<oopp.py>
+
+ import time
+
+ def get_time():
+ "Return the time of the system in the format HH:MM:SS"
+ return time.asctime().split()[3]
+
+ #</oopp.py>
+
+ >>> from oopp import get_time
+ >>> get_time()
+ '13:03:49'
+
+Suppose, for instance, we want to know how much it takes to Python
+to write a Gigabyte of data. This can be a quite useful benchmark
+to have an idea of the I/O bottlenecks in our system. Since to take in memory
+a file of a Gigabyte can be quite problematic, let me compute the
+time spent in writing 1024 files of one Megabyte each. To this
+aim we need a ``writefile`` function
+
+ ::
+
+ #<oopp.py>
+
+ def writefile(fname,data):
+ f=file(fname,'w')
+ f.write(data)
+ f.close()
+
+ #</oopp.py>
+
+and timing function. The idea is to wrap the ``writefile`` function in
+a ``with_clock`` function as follows:
+
+ ::
+
+ #<oopp.py>
+
+ def with_clock(func,n=1):
+ def _(*args,**kw): # this is a closure
+ print "Process started on",get_time()
+ print ' .. please wait ..'
+ for i in range(n): func(*args,**kw)
+ print "Process ended on",get_time()
+ return _
+
+ #</oopp.py>
+
+The wrapper function ``with_clock`` has converted the function ``writefile``
+in a function ``with_clock(writefile)`` which has the same arguments
+of ``writefile``, but contains additional features: in this case
+timing capabilities. Technically speaking, the internal function ``_``
+is called a *closure*. Closures are very common in functional languages
+and can be used in Python too, with very little effort [#]_.
+
+I will use closures very often in the following, and I will use
+the convention of denoting with "_" the inner
+function in the closure, since there is no reason of giving to it a
+descriptive name (the name 'with_clock' in the outer function
+is descriptive enough). For the same, reason I do not use a
+docstring for "_". If Python would allow multistatement lambda
+functions, "_" would be a good candidate for an anonymous function.
+
+Here is an example of usage:
+
+ >>> from oopp import *
+ >>> data='*'*1024*1024 #one megabyte
+ >>> with_clock(writefile,n=1024)('datafile',data) #.
+ Process started on 21:20:01
+ .. please wait ..
+ Process ended on 21:20:57
+
+This example shows that Python has written one Gigabyte of data (splitted in
+1024 chunks of one Megabyte each) in less than a minute. However,the
+result depends very much on the filesystem. I always suggest people
+to profile their programs, since one *always* find surprises.
+For instance, I have checked the performance of my laptop,
+a dual machine Windows 98 SE/ Red Hat Linux 7.3.
+The results are collected in the following table:
+
+ ================= ===================== ========================
+ Laptop
+ Linux ext-3 FAT under Linux FAT under Windows 98
+ ================= ===================== ========================
+ 24-25 s 56-58 s 86-88 s
+ ================= ===================== ========================
+
+
+We see that Linux is *much* faster: more than three times faster than
+Windows, using the same machine! Notice that the FAT filesystem under
+Linux (where it is *not* native) is remarkably faster than the FAT
+under Windows 98, where it is native !! I think that now my readers
+can begin to understand why this book has been written under Linux
+and why I *never* use Windows for programming (actually I use it only
+to see the DVD's ;-).
+
+I leave as an exercise for the reader to check the results on this
+script on their machine. Since my laptop is quite old, you will probably
+have much better performances (for instance on my linux desktop I can
+write a Gigabyte in less than 12 seconds!). However, there are *always*
+surprises: my desktop is a dual Windows 2000 machine with three different
+filesystems, Linux ext-2, FAT and NTFS. Surprisingly enough, the NT
+filesystem is the more inefficient for writing, *ten times slower*
+than Linux!
+
+ ================= ===================== ========================
+ Desktop
+ Linux ext-2 FAT under Win2000 NTFS under Win2000
+ ================= ===================== ========================
+ 11-12 s 95-97 s 117-120 s
+ ================= ===================== ========================
+
+.. [#] Users of Python 2.3 can give a look to the new ``datetime`` module,
+ if they are looking for a sophisticated clock/calendar.
+
+.. [#] There are good references on functional programming in Python;
+ I suggest the Python Cookbook and the articles by David Mertz
+ www.IBM.dW.
+
+
+Functions are objects
+---------------------------------------------------------------------------
+
+As we said in the first chapter, objects have attributes accessible with the
+dot notation. This is not surprising at all. However, it could be
+surprising to realize that since Python functions are objects, they
+can have attributes, too. This could be surprising since this feature is quite
+uncommon: typically or i) the language is
+not object-oriented, and therefore functions are not objects, or ii)
+the language is strongly object-oriented and does not have functions, only
+methods. Python is a multiparadigm language (which I prefer to the
+term "hybrid" language), therefore it has functions that are objects,
+as in Lisp and other functional languages.
+Consider for instance the ``get_time`` function.
+That function has at least an useful attribute, its doctring:
+
+ >>> from oopp import get_time
+ >>> print get_time.func_doc
+ Return the time of the system in the format HH:MM:SS
+
+The docstring can also be obtained with the ``help`` function:
+
+ >>> help(get_time)
+ Help on function get_time in module oopp:
+ get_time()
+ Return the time of the system in the format HH:MM:SS
+
+Therefore ``help`` works on user-defined functions, too, not only on
+built-in functions. Notice that ``help`` also returns the argument list of
+the function. For instance, this is
+the help message on the ``round`` function that we will use in the
+following:
+
+ >>> help(round)
+ Help on built-in function round:
+ round(...)
+ round(number[, ndigits]) -> floating point number
+ Round a number to a given precision in decimal digits (default 0
+ digits).This always returns a floating point number. Precision may
+ be negative.
+
+I strongly recommend Python programmers to use docstrings, not
+only for clarity sake during the development, but especially because
+it is possible to automatically generate nice HTML documentation from
+the docstrings, by using the standard tool "pydoc".
+
+One can easily add attributes to a function. For instance:
+
+ >>> get_time.more_doc='get_time invokes the function time.asctime'
+ >>> print get_time.more_doc
+ get_time invokes the function time.asctime
+
+Attributes can be functions, too:
+
+ >>> def IamAfunction(): print "I am a function attached to a function"
+ >>> get_time.f=IamAfunction
+ >>> get_time.f()
+ I am a function attached to a function
+
+This is a quite impressive potentiality of Python functions, which has
+no direct equivalent in most other languages.
+
+One possible application is to fake C "static" variables. Suppose
+for instance we need a function remembering how may times it is
+called: we can simply use
+
+ ::
+
+ #<double.py>
+
+ def double(x):
+ try: #look if double.counter is defined
+ double.counter
+ except AttributeError:
+ double.counter=0 #first call
+ double.counter+=1
+ return 2*x
+
+ double(double(2))
+ print "double has been called %s times" % double.counter
+
+ #</double.py>
+
+with output ``double has been called 2 times``.
+A more elegant approach involves closures. A closure can enhance an
+ordinary function, providing to it the capability of remembering
+the results of its previous calls and avoiding the duplication of
+computations:
+
+::
+
+ #<oopp.py>
+
+ def withmemory(f):
+ """This closure invokes the callable object f only if need there is"""
+ argskw=[]; result=[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ i=argskw.index(akw)
+ except ValueError: # there is no previously stored result
+ res=f(*args,**kw) # returns the new result
+ argskw.append(akw) # update argskw
+ result.append(res) # update result
+ return res
+ else:
+ return result[i]
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+ def memoize(f):
+ """This closure remembers all f invocations"""
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+ #</oopp.py>
+
+Now, if we call the wrapped function ``f`` twice with the same arguments,
+Python can give the result without repeating the (possibly very long)
+computation.
+
+ >>> def f(x):
+ ... print 'called f'
+ ... return x*x
+ >>> wrapped_f=withmemory(f)
+ >>> wrapped_f(2) #first call with the argument 2; executes the computation
+ called f
+ 4
+ >>> wrapped_f(2) #does not repeat the computation
+ 4
+ >>> wrapped_f.result
+ [4]
+ >>> wrapped_f.argskw
+ [((2,), {})]
+
+Profiling functions
+---------------------------------------------------------------------------
+
+The ``with_clock`` function provided before was intended to be
+pedagogical; as such it is a quite poor solution to the
+problem of profiling a Python routine. A better solution involves
+using two others functions in the time library, ``time.time()``
+that gives that time in seconds elapsed from a given date, and
+``time.clock()`` that gives the time spent by the CPU in a given
+computation. Notice that ``time.clock()`` has not an infinite
+precision (the precision depends on the system) and one
+should expect relatively big errors if the function runs in
+a very short time. That's the reason why it is convenient
+to execute multiple times short functions and divide the total
+time by the number of repetitions. Moreover, one should subtract the
+overhead do to the looping. This can be computed with the following
+routine:
+
+ ::
+
+ #<oopp.py>
+
+ def loop_overhead(N):
+ "Computes the time spent in empty loop of N iterations"
+ t0=time.clock()
+ for i in xrange(N): pass
+ return time.clock()-t0
+
+ #</oopp.py>
+
+For instance, on my laptop an empty loop of one million of iterations
+is performed in 1.3 seconds. Typically the loop overhead is negligible,
+whereas the real problem is the function overhead.
+
+Using the attribute trick discussed above, we may
+define a ``with_timer`` function that enhances quite a bit
+``with_clock``:
+
+ ::
+
+ #<oopp.py>
+
+ def with_timer(func, modulename='__main__', n=1, logfile=sys.stdout):
+ """Wraps the function func and executes it n times (default n=1).
+ The average time spent in one iteration, express in milliseconds,
+ is stored in the attributes func.time and func.CPUtime, and saved
+ in a log file which defaults to the standard output.
+ """
+ def _(*args,**kw): # anonymous function
+ time1=time.time()
+ CPUtime1=time.clock()
+ print 'Executing %s.%s ...' % (modulename,func.__name__),
+ for i in xrange(n): res=func(*args,**kw) # executes func n times
+ time2=time.time()
+ CPUtime2=time.clock()
+ func.time=1000*(time2-time1)/n
+ func.CPUtime=1000*(CPUtime2-CPUtime1-loop_overhead(n))/n
+ if func.CPUtime<10: r=3 #better rounding
+ else: r=1 #default rounding
+ print >> logfile, 'Real time: %s ms' % round(func.time,r),
+ print >> logfile, ' CPU time: %s ms' % round(func.CPUtime,r)
+ return res
+ return _
+
+ #</oopp.py>
+
+Here it is an example of application:
+
+ >>> from oopp import with_timer,writefile
+ >>> data='*'*1024*1024 #one megabyte
+ >>> with_timer(writefile,n=1024)('datafile',data) #.
+ Executing writefile ... Real time: 60.0 ms CPU time: 42.2 ms
+
+The CPU time can be quite different from the real time,
+as you can see in the following example:
+
+ >>> import time
+ >>> def sleep(): time.sleep(1)
+ ...
+ >>> with_timer(sleep)() #.
+ Executing sleep ... Real time: 999.7 ms CPU time: 0.0 ms
+
+We see that Python has run for 999.7 ms (i.e. 1 second, up to
+approximation errors in the system clock) during which the CPU has
+worked for 0.0 ms (i.e. the CPU took a rest ;-).
+The CPU time is the relevant time to use with the purpose of
+benchmarking Python speed.
+
+I should notice that the approach pursued in ``with_timer`` is still
+quite simple. A better approach would be to
+plot the time versus the number of iteration, do a linear interpolation
+and extract the typical time for iteration from that. This allows
+to check visually that the machine is not doing something strange
+during the execution time and it is what
+I do in my personal benchmark routine; doing something similar is
+left as an exercise for the reader ;-).
+
+Another approach is to use the ``timeit.py`` module (new in Python 2.3,
+but works also with Python 2.2):
+
+ ::
+
+ #<oopp.py>
+
+ import timeit,__main__,warnings
+
+ warnings.filterwarnings('ignore',
+ 'import \* only allowed at module level',SyntaxWarning)
+
+ def timeit_(stmt,setup='from __main__ import *',n=1000):
+ t=timeit.Timer(stmt,setup)
+ try: print t.repeat(number=n) # class timeit 3 times
+ except: t.print_exc()
+
+ #</oopp.py>
+
+It is often stated that Python is slow and quite ineffective
+in application involving hard computations. This is generally speaking
+true, but how bad is the situation ? To test the (in)efficiency of
+Python on number crunching, let me give a function to compute the
+Mandelbrot set, which I have found in the Python Frequently Asked
+Question (FAQ 4.15. *Is it possible to write obfuscated one-liners
+in Python?*).
+This function is due to Ulf Bartelt and you should ask him to know how
+does it work ;-)
+
+ ::
+
+ #<oopp.py>
+
+ def mandelbrot(row,col):
+ "Computes the Mandelbrot set in one line"
+ return (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(
+ lambda x,y:x+y,map(lambda y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=
+ lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, Sx=Sx,Sy=Sy:reduce(
+ lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, i=i,
+ Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)
+ or (x*x+y*y>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):
+ f(xc,yc,x,y,k,f):chr(64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),
+ range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(
+ -2.1, 0.7, -1.2, 1.2, 30, col, row)
+ # \___ ___/ \___ ___/ | | |_ lines on screen
+ # V V | |______ columns on screen
+ # | | |__________ maximum of "iterations"
+ # | |_________________ range on y axis
+ # |____________________________ range on x axis
+
+ #</oopp.py>
+
+Here there is the benchmark on my laptop:
+
+ >>> from oopp import mandelbrot,with_timer
+ >>> row,col=24,75
+ >>> output=with_timer(mandelbrot,n=1)(row,col)
+ Executing __main__.mandelbrot ... Real time: 427.9 ms CPU time: 410.0 ms
+ >>> for r in range(row): print output[r*col:(r+1)*col]
+ ...
+ BBBBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC
+ BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+ BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+ BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+ BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+ BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+ BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+ BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+ BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+ BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+ BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+ BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+ BBB_____________________________________________________________OKIHGFFEEEE
+ BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+ BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+ BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+ BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+ BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+ BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+ BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+ BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+ BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+ BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+ BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+
+I am willing to concede that this code is not typical Python code and
+actually it could be an example of *bad* code, but I wanted a nice ASCII
+picture on my book ... :) Also, this prove that Python is not necessarily
+readable and easy to understand ;-)
+I leave for the courageous reader to convert the previous algorithm to C and
+measure the difference in speed ;-)
+
+
+About Python speed
+---------------------------------------------------
+
+The best way to improved the speed is to improve the algorithm; in
+this sense Python is an ideal language since it allows you to test
+many algorithms in an incredibly short time: in other words, the time you
+would spend fighting with the compiler in other languages, in Python
+can be used to improve the algorithm.
+However in some cases, there is little to do: for instance, in many
+problems one has to run lots of loops, and Python loops are horribly
+inefficients as compared to C loops. In this case the simplest possibility
+is to use Psyco. Psyco is a specialing Python compiler written by Armin
+Rigo. It works for 386 based processors and allows Python to run loops at
+C speed. Installing Psyco requires $0.00 and ten minutes of your time:
+nine minutes to find the program, download it, and install it; one
+minute to understand how to use it.
+
+The following script explains both the usage and the advantages of Psyco:
+
+ ::
+
+ #<psyco1.py>
+
+ import oopp,sys
+ try:
+ import psyco
+ except ImportError:
+ print "Psyco is not installed, sorry."
+ else:
+ n=1000000 # 1,000,000 loops
+
+ without=oopp.loop_overhead(n)
+ print "Without Psyco:",without
+
+ psyco.bind(oopp.loop_overhead) #compile the empty_loop
+
+ with=oopp.loop_overhead(n)
+ print "With Psyco:",with
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+ #</psyco1.py>
+
+The output is impressive:
+
+ ::
+
+ Without Psyco: 1.3
+ With Psyco: 0.02
+ Speedup = 65.0x
+
+
+Notice that repeating the test, you will obtain different speedups.
+On my laptop, the speedup for an empty loop of 10,000,000 of
+iteration is of the order of 70x, which is the same speed of a C loop,
+actually (I checked it). On my desktop, I have even found a speedup of
+94x !
+
+However, I must say that Psyco has some limitations. The problem is
+the function call overhead. Psyco enhances the overhead and in some
+programs it can even *worsen* the performance (this is way you should
+*never* use the ``psyco.jit()`` function that wraps all the functions of
+your program: you should only wrap the bottleneck loops). Generally speaking,
+you should expect a much more modest improvement, a factor of 2 or 3
+is what I obtain usually in my programs.
+
+Look at this second example, which essentially measure the function
+call overhead by invoking the ``do_nothing`` function:
+
+ ::
+
+ #<psyco2.py>
+
+ import oopp
+ try:
+ import psyco
+ except ImportError:
+ print "Psyco is not installed, sorry."
+ else:
+ n=10000 # 10,000 loops
+
+ def do_nothing_loop():
+ for i in xrange(n): oopp.do_nothing()
+
+ print "Without Psyco:\n"
+ oopp.with_timer(do_nothing_loop,n=5)() #50,000 times
+
+ without=do_nothing_loop.CPUtime
+
+ psyco.bind(do_nothing_loop)
+ print "With Psyco:\n"
+ oopp.with_timer(do_nothing_loop,n=5)() #50,000 times
+
+ with=do_nothing_loop.CPUtime
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+ #</psyco2.py>
+
+The output is less incredible:
+
+ ::
+
+ Without Psyco:
+ Executing do_nothing_loop ... Real time: 138.2 ms CPU time: 130.0 ms
+ With Psyco:
+ Executing do_nothing_loop ... Real time: 70.0 ms CPU time: 68.0 ms
+ Speedup = 1.9x
+
+
+
+However, this is still impressive, if you think that you can double
+the speed of your program by adding *a line* of code! Moreover this
+example is not fair since Psyco cannot improve very much the performance
+for loops invoking functions with a variable number of arguments. On the
+other hand, it can do quite a lot for loops invoking functions with
+a fixed number of arguments. I have checked that you can easily reach
+speedups of 20x (!). The only disadvantage is that a program invoking
+Psyco takes much more memory, than a normal Python program, but this
+is not a problem for most applications in nowadays computers.
+Therefore, often Psyco
+can save you the effort of going trough a C extension. In some cases,
+however, there is no hope: I leave as an exercise for the reader
+to check (at least the version 0.4.1 I am using now) is unable to
+improve the performance on the Mandelbrot set example. This proves
+that in the case bad code, there is no point in using a compiler:
+you have to improve the algorithm first !
+
+By the way, if you really want to go trough a C extension with a minimal
+departure from Python, you can use Pyrex by Greg Ewing. A Pyrex program
+is essentially a Python program with variable declarations that is
+automatically converted to C code. Alternatively, you can inline
+C functions is Python with ``weave`` of ...
+Finally, if you want to access C/C++ libraries, there tools
+like Swig, Booster and others.
+
+Tracing functions
+---------------------------------------------------------------------------
+
+Typically, a script contains many functions that call themselves each
+other when some conditions are satisfied. Also, typically during
+debugging things do not work the way we would like and it is not
+clear which functions are called, in which order they are called,
+and which parameters are passed. The best way to know all these
+informations, is to trace the functions in our script, and to write
+all the relevant informations in a log file. In order to keep the
+distinction between the traced functions and the original one, it
+is convenient to collect all the wrapped functions in a separate dictionary.
+The tracing of a single function can be done with a closure
+like this:
+
+::
+
+ #<oopp.py>
+
+ def with_tracer(function,namespace='__main__',output=sys.stdout, indent=[0]):
+ """Closure returning traced functions. It is typically invoked
+ trough an auxiliary function fixing the parameters of with_tracer."""
+ def _(*args,**kw):
+ name=function.__name__
+ i=' '*indent[0]; indent[0]+=4 # increases indentation
+ output.write("%s[%s] Calling '%s' with arguments\n" %
+ (i,namespace,name))
+ output.write("%s %s ...\n" % (i,str(args)+str(kw)))
+ res=function(*args,**kw)
+ output.write("%s[%s.%s] called with result: %s\n"
+ % (i,namespace,name,str(res)))
+ indent[0]-=4 # restores indentation
+ return res
+ return _ # the traced function
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> from oopp import with_tracer
+ >>> def fact(n): # factorial function
+ ... if n==1: return 1
+ ... else: return n*fact(n-1)
+ >>> fact=with_tracer(fact)
+ >>> fact(3)
+ [__main__] Calling 'fact' with arguments
+ (3,){} ...
+ [__main__] Calling 'fact' with arguments
+ (2,){} ...
+ [__main__] Calling 'fact' with arguments
+ (1,){} ...
+ [__main__.fact] called with result: 1
+ [__main__.fact] called with result: 2
+ [__main__.fact] called with result: 6
+ 6
+
+The logic behind ``with_tracer`` should be clear; the only trick is the
+usage of a default list as a way to store a global indentation parameter.
+Since ``indent`` is mutable, the value of ``indent[0]`` changes at any
+recursive call of the traced function, resulting in a nested display.
+
+Typically, one wants to trace all the functions in a given module;
+this can be done trough the following function:
+
+ ::
+
+ #<oopp.py>
+
+ from types import *
+
+ isfunction=lambda f: isinstance(f,(FunctionType,BuiltinFunctionType))
+
+ def wrapfunctions(obj,wrapper,err=None,**options):
+ "Traces the callable objects in an object with a dictionary"
+ namespace=options.get('namespace',getattr(obj,'__name__',''))
+ output=options.get('output',sys.stdout)
+ dic=dict([(k,wrapper(v,namespace,output))
+ for k,v in attributes(obj).items() if isfunction(v)])
+ customize(obj,err,**dic)
+
+ #</oopp.py>
+
+Notice that 'wrapfunctions' accepts as first argument an object with
+a ``__dict__`` attribute (such as a module or a class) or with some
+explicit attributes (such as a simple object) and modifies it. One can
+trace a module as in this example:
+
+ ::
+
+ #<tracemodule.py>
+
+ import oopp,random
+
+ oopp.wrapfunctions(random,oopp.with_tracer)
+
+ random.random()
+
+ #</tracemodule.py>
+
+with output
+
+ ::
+
+ [random] Calling 'random' with arguments
+ (){} ...
+ -> 'random.random' called with result: 0.175450439202
+
+The beauty of the present approach is its generality: 'wrap' can be
+used to add any kind of capabilities to a pre-existing module.
+For instance, we could time the functions in a module, with the
+purpose of looking at the bottlenecks. To this aim, it is enough
+to use a 'timer' nested closure:
+
+An example of calling is ``wrapfunction(obj,timer,iterations=1)``.
+
+We may also compose our closures; for instance one could define a
+``with_timer_and_tracer`` closure:
+
+ >>> with_timer_and_tracer=lambda f: with_timer(with_tracer(f))
+
+It should be noticed that Python comes with a standard profiler
+(in my system it is located in ``/usr/local/lib/python2.2/profile.py``)
+that allows to profile a script or a module (try
+python /usr/local/lib/python2.2/profile.py oopp.py)
+
+or
+
+ >>> import profile; help(profile)
+
+and see the on-line documentation.
+
+Tracing objects
+----------------------------------------------------------------------
+
+In this section, I will give a more sophisticated example, in which
+one can easily understand why the Python ability of changing methods and
+attributes during run-time, is so useful.
+As a preparation to the real example, let me
+first introduce an utility routine that allows the user
+to add tracing capabilities to a given object.
+Needless to say, this feature can be invaluable during debugging, or in trying
+to understand the behaviour of a program written by others.
+
+This routine is a little complex and needs some explanation.
+
+1. The routine looks in the attributes of the object and try to access them.
+
+2. If the access is possible, the routines looks for methods (methods
+ are recognized trough the ``inspect.isroutine`` function in the
+ standard library) and ignores regular attributes;
+
+3. The routine try to override the original methods with improved ones,
+ that possess tracing capabilities;
+
+4. the traced method is obtained with the wrapping trick discussed before.
+
+I give now the real life example that I have anticipated before.
+Improvements and elaborations of this example can be useful to the
+professional programmer, too. Suppose you have an XML text you want
+to parse. Python provides excellent support for this kind of operation
+and various standard modules. One of the most common is the ``expat``
+module (see the standard library documentation for more).
+
+If you are just starting using the module, it is certainly useful
+to have a way of tracing its behaviour; this is especially true if
+you you find some unexpected error during the parsing of a document
+(and this may happens even if you are an experience programmer ;-).
+
+The tracing routine just defined can be used to trace the parser, as
+it is exemplified in the following short script:
+
+ ::
+
+ #<expat.py>
+
+ import oopp, xml.parsers.expat, sys
+
+ # text to be parsed
+ text_xml="""\
+ <?xml version="1.0"?>
+ <parent id="dad">
+ <child name="kid">Text goes here</child>
+ </parent>"""
+
+ # a few do nothing functions
+ def start(*args): pass
+ def end(*args): pass
+ def handler(*args): pass
+
+ # a parser object
+ p = xml.parsers.expat.ParserCreate()
+
+ p.StartElementHandler = start
+ p.EndElementHandler = end
+ p.CharacterDataHandler = handler
+
+ #adds tracing capabilities to p
+ oopp.wrapfunctions(p,oopp.with_tracer, err=sys.stdout)
+
+ p.Parse(text_xml)
+
+ #</expat.py>
+
+The output is:
+
+ ::
+
+ Error: SetBase cannot be set
+ Error: Parse cannot be set
+ Error: ParseFile cannot be set
+ Error: GetBase cannot be set
+ Error: SetParamEntityParsing cannot be set
+ Error: ExternalEntityParserCreate cannot be set
+ Error: GetInputContext cannot be set
+ [] Calling 'start' with arguments
+ (u'parent', {u'id': u'dad'}){} ...
+ [.start] called with result: None
+ [] Calling 'handler' with arguments
+ (u'\n',){} ...
+ [.handler] called with result: None
+ [] Calling 'start' with arguments
+ (u'child', {u'name': u'kid'}){} ...
+ [.start] called with result: None
+ [] Calling 'handler' with arguments
+ (u'Text goes here',){} ...
+ [.handler] called with result: None
+ [] Calling 'end' with arguments
+ (u'child',){} ...
+ [.end] called with result: None
+ [] Calling 'handler' with arguments
+ (u'\n',){} ...
+ [.handler] called with result: None
+ [] Calling 'end' with arguments
+ (u'parent',){} ...
+ [.end] called with result: None
+
+
+This is a case where certain methods cannot be managed with
+``getattr/setattr``, because they are internally coded in C: this
+explain the error messages at the beginning. I leave as an exercise
+for the reader to understand the rest ;-)
+
+Inspecting functions
+----------------------------------------------------------------------
+
+Python wonderful introspection features are really impressive when applied
+to functions. It is possible to extract a big deal of informations
+from a Python function, by looking at its associated *code object*.
+For instance, let me consider my, ``do_nothing`` function: its associated
+code object can be extracted from the ``func_code`` attribute:
+
+ >>> from oopp import *
+ >>> co=do_nothing.func_code # extracts the code object
+ >>> co
+ <code object do_nothing at 0x402c5d20, file "oopp.py", line 48>
+ >>> type(co)
+ <type 'code'>
+
+The code object is far being trivial: the docstring says it all:
+
+ >>> print type(co).__doc__
+ code(argcount, nlocals, stacksize, flags, codestring, constants, names,
+ varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])
+ Create a code object. Not for the faint of heart.
+
+In the case of my ``do_nothing`` function, the code object
+possesses the following attributes:
+
+ >>> print pretty(attributes(co))
+ co_argcount = 0
+ co_cellvars = ()
+ co_code = dS
+ co_consts = (None,)
+ co_filename = oopp.py
+ co_firstlineno = 48
+ co_flags = 15
+ co_freevars = ()
+ co_lnotab =
+ co_name = do_nothing
+ co_names = ()
+ co_nlocals = 2
+ co_stacksize = 1
+ co_varnames = ('args', 'kw')
+
+Some of these arguments are pretty technical and implementation dependent;
+however, some of these are pretty clear and useful:
+
+ - co_argcount is the total number of arguments
+ - co_filename is the name of the file where the function is defined
+ - co_firstlineno is the line number where the function is defined
+ - co_name is the name of the function
+ - co_varnames are the names
+
+The programmer that it is not a "faint of heart" can study
+the built-in documentation on code objects; s/he should try
+
+ ::
+
+ for k,v in attributes(co).iteritems(): print k,':',v.__doc__,'\n'
+
+ # does not work now !!
+
+ ::
+
+ add=[lambda x,i=i: x+i for i in range(10)]
+
+ >>> def f(y):
+ ... return lambda x: x+y
+ ...
+ >>> f(1).func_closure #closure cell object
+ (<cell at 0x402b56bc: int object at 0x811d6c8>,)
+
+func.defaults, closure, etc.
+
+#how to extract (non-default) arguments as help does.
+
+print (lambda:None).func_code.co_filename
+
+One cannot change the name of a function:
+
+ >>> def f(): pass
+ ...
+ >>> f.__name__='ciao' # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: readonly attribute
+
+However, one can create a copy with a different name:
+
+ ::
+
+ #<oopp.py>
+
+ def copyfunc(f,newname=None): # works under Python 2.3
+ if newname is None: newname=f.func_name # same name
+ return FunctionType(f.func_code, globals(), newname,
+ f.func_defaults, f.func_closure)
+
+ #</oopp.py>
+
+ >>> copyfunc(f,newname='f2')
+ <function f2 at 0x403e233c>
+
+Notice that the ``copy`` module would not do the job:
+
+ >>> import copy
+ >>> copy.copy(f) # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "/usr/local/lib/python2.3/copy.py", line 84, in copy
+ y = _reconstruct(x, reductor(), 0)
+ File "/usr/local/lib/python2.3/copy_reg.py", line 57, in _reduce
+ raise TypeError, "can't pickle %s objects" % base.__name__
+ TypeError: can't pickle function objects
+
+
+THE BEAUTY OF OBJECTS
+===========================================================================
+
+In this chapter I will show how to define generic objects in Python, and
+how to manipulate them.
+
+User defined objects
+--------------------------------------------------------------------------
+
+In Python, one cannot directly modify methods and attributes of built-in
+types, since this would be a potentially frightening source of bugs.
+Imagine for instance of changing the sort method of a list and invoking an
+external module expecting the standard sort: all kind of hideous outcome
+could happen.
+
+Nevertheless, in Python, as in all OOP languages, the user can define
+her own kind of objects, customized to satisfy her needs. In order to
+define a new object, the user must define the class of the objects she
+needs. The simplest possible class is a do-nothing class:
+
+ ::
+
+ #<oopp.py>
+
+ class Object(object):
+ "A convenient Object class"
+
+ #</oopp.py>
+
+Elements of the ``Object`` class can be created (instantiated) quite
+simply:
+
+ >>> from oopp import Object
+ >>> obj1=Object()
+ >>> obj1
+ <oopp.Object object at 0x81580ec>
+ >>> obj2=Object()
+ obj2
+ <object.Object object at 0x8156704>
+
+Notice that the hexadecimal number 0x81580ec is nothing else that the
+unique object reference to ``obj1``
+
+ >>> hex(id(obj1))
+ '0x81580ec'
+
+whereas 0x8156704 is the object reference of ``obj2``:
+
+ >>> hex(id(obj2))
+ '0x8156704'
+
+However, at this point ``obj1`` and ``obj2`` are generic
+doing nothing objects . Nevertheless, they have
+at least an useful attribute, the class docstring:
+
+ >>> obj1.__doc__ #obj1 docstring
+ 'A convenient Object class'
+ >>> obj2.__doc__ # obj2 docstring: it's the same
+ 'A convenient Object class'
+
+Notice that the docstring is associate to the class and therefore all
+the instances share the same docstring, unless one explicitly assigns
+a different docstring to some instance. ``__doc__``
+is a class attribute (or a static attribute for readers familiar with the
+C++/Java terminology) and the expression is actually syntactic sugar for
+
+ >>> class Object(object): # with explicit assignement to __doc__
+ ... __doc__ = "A convenient Object class"
+
+
+Since instances of 'Object' can be modified, I can transform them in
+anything I want. For instance, I can create a simple clock:
+
+ >>> myclock=Object()
+ >>> myclock
+ <__main__.Object object at 0x8124614>
+
+A minimal clock should at least print the current time
+on the system. This is given by the ``get_time`` function
+we defined in the first chapter. We may "attach" that function
+to our clock as follows:
+
+ >>> import oopp
+ >>> myclock.get_time=oopp.get_time
+ >>> myclock.get_time # this is a function, not a method
+ <function get_time at 0x815c40c>
+
+In other words, we have converted the ``oopp.get_time`` function to a
+``get_time`` function of the object ``myclock``. The procedure works
+
+ >>> myclock.get_time()
+ '15:04:57'
+
+but has a disadvantage: if we instantiate another
+clock
+
+ >>> from oopp import Object
+ >>> otherclock=Object()
+
+the other clock will ``not`` have a get_time method:
+
+ >>> otherclock.get_time() #first attempt; error
+ AttributeError: 'Object' object has no attribute 'get_time'
+
+Notice instead that the docstring is a *class attribute*, i.e. it
+is defined both for the class and *all instances* of the class,
+therefore even for ``otherclock``:
+
+ >>> Object.__doc__
+ 'A convenient Object class'
+ >>> otherclock.__doc__
+ 'A convenient Object class'
+
+We would like to convert the ``get_time`` function to a
+``get_time`` method for the *entire* class 'Object', i.e. for all its
+instances. Naively, one would be tempted to write the following:
+
+ >>> Object.get_time=oopp.get_time
+
+However this would not work:
+
+ >>> otherclock.get_time() #second attempt; still error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: oopp.get_time() takes no arguments (1 given)
+
+This error message is something that all Python beginners encounter
+(and sometimes even non-beginners ;-). The solution is to introduce
+an additional argument:
+
+ >>> Object.get_time=lambda self : oopp.get_time()
+ >>> otherclock.get_time # this is method now, not a function
+ <bound method Object.<lambda> of <__main__.Object object at 0x815881c>>
+ >>> otherclock.get_time() #third attempt
+ '15:28:41'
+
+Why this works ? The explanation is the following:
+when Python encounters an expression of the form
+``objectname.methodname()`` it looks if there is a already a method
+*attached* to the object:
+
+ a. if yes it invokes it with no arguments
+ (this is why our first example worked);
+ b. if not it looks at the class of the object; if there is a method
+ bound to the class it invokes that method *by passing the
+ object as first argument*.
+
+When we invoked ``otherclock.get_time()`` in our second attempt, Python
+found that the function ``get_time`` was defined at the class level,
+and sent it the ``otherclock`` object as first argument: however ``get_time``
+was bind to ``func_get_time``, which is function with *no* arguments: whence
+the error message. The third attempt worked since, thanks to the
+lambda function trick, the ``get_time`` function has been converted to
+a function accepting a first argument.
+
+Therefore that's the rule: in Python, one can define methods
+at the class level, provided one explitely introduces a first argument
+containing the object on which the method is invoked.
+
+This first argument is traditionally called ``self``; the name 'self' is not
+enforced, one could use any other valid Python identifier, however the
+convention is so widespread that practically everybody uses it;
+pychecker will even raise a warning in the case you don't follow the
+convention.
+
+I have just shown one the most interesting features of Python, its
+*dynamicity*: you can create the class first and add methods to it later.
+That logic cannot be followed in typical compiled language as C++. On the
+other hand, one can also define methods in a static, more traditional way:
+
+ ::
+
+ #<clock1.py>
+
+ "Shows how to define methods inside the class (statically)"
+
+ import oopp
+
+ class Clock(object):
+ 'Clock class; version 0.1'
+ def get_time(self): # method defined inside the class
+ return oopp.get_time()
+
+ myclock=Clock() #creates a Clock instance
+ print myclock.get_time() # print the current time
+
+ #</clock1.py>
+
+In this case we have defined the ``get_time`` method inside the class as a
+normal function with an explicit first argument called self; this is
+entirely equivalent to the use of a lambda function.
+
+The syntax ``myclock.get_time()`` is actually syntactic sugar for
+``Clock.get_time(myclock)``.
+
+In this second form, it is clear the ``get_time`` is really "attached" to the
+class, not to the instance.
+
+Objects have static methods and classmethods
+-----------------------------------------------------------------------------
+
+ .. line-block::
+
+ *There should be one--and preferably only one--obvious way to do it*
+ -- Tim Peters, *The Zen of Python*.
+
+
+For any rule, there is an exception, and despite the Python's motto
+there are many ways to define methods in classes. The way I presented
+before was the obvious one before the Python 2.2 revolution; however,
+nowadays there is another possibility that, even if less obvious, has the
+advantage of some elegance (and it is also slightly more efficient too, even if
+efficiency if never a primary concern for a Python programmer).
+We see that the first argument in the ``get_time`` method is useless,
+since the time is computed from the ``time.asctime()`` function which
+does not require any information about the object that is calling
+it. This waste is ugly, and since according to the Zen of Python
+
+ *Beautiful is better than ugly.*
+
+we should look for another way. The solution is to use a *static method*:
+when a static method is invoked, the calling object is *not* implicitly passed
+as first argument. Therefore we may use a normal function with no additional
+first argument to define the ``get_time`` method:
+
+ ::
+
+ #<oopp.py>
+
+ class Clock(object):
+ 'Clock with a staticmethod'
+ get_time=staticmethod(get_time)
+
+ #</oopp.py>
+
+Here is how it works:
+
+ >>> from oopp import Clock
+ >>> Clock().get_time() # get_time is bound both to instances
+ '10:34:23'
+ >>> Clock.get_time() # and to the class
+ '10:34:26'
+
+The staticmethod idiom converts the lambda function to a
+static method of the class 'Clock'. Notice that one can avoid the
+lambda expression and use the (arguably more Pythonic) idiom
+
+ ::
+
+ def get_time()
+ return oopp.get_time()
+ get_time=staticmethod(oopp.get_time)
+
+as the documentation suggests:
+
+ >>> print staticmethod.__doc__
+ staticmethod(function) -> method
+ Convert a function to be a static method.
+ A static method does not receive an implicit first argument.
+ To declare a static method, use this idiom:
+ class C:
+ def f(arg1, arg2, ...): ...
+ f = staticmethod(f)
+ It can be called either on the class (e.g. C.f()) or on an instance
+ (e.g. C().f()). The instance is ignored except for its class.
+ Static methods in Python are similar to those found in Java or C++.
+ For a more advanced concept, see the classmethod builtin.
+
+At the present the notation for static methods is still rather ugly,
+but it is expected to improve in future versions of Python (probably
+in Python 2.4). Documentation for static methods can
+be found in Guido's essay and in the PEP.. : however this is intended for
+developers.
+
+As the docstring says, static methods are also "attached" to the
+class and may be called with the syntax ``Clock.get_time()``.
+
+A similar remark applies for the so called *classmethods*:
+
+ >>> print classmethod.__doc__
+ classmethod(function) -> method
+ Convert a function to be a class method.
+ A class method receives the class as implicit first argument,
+ just like an instance method receives the instance.
+ To declare a class method, use this idiom:
+ class C:
+ def f(cls, arg1, arg2, ...): ...
+ f = classmethod(f)
+ It can be called either on the class (e.g. C.f()) or on an instance
+ (e.g. C().f()). The instance is ignored except for its class.
+ If a class method is called for a derived class, the derived class
+ object is passed as the implied first argument.
+ Class methods are different than C++ or Java static methods.
+ If you want those, see the staticmethod builtin.
+
+
+#When a regular method is invoked, a reference to the calling object is
+#implicitely passed as first argument; instead, when a static method is
+#invoked, no reference to the calling object is passed.
+
+As the docstring says, classmethods are convenient when one wants to pass
+to a method the calling *class*, not the calling object. Here there is an
+example:
+
+ >>> class Clock(object): pass
+ >>> Clock.name=classmethod(lambda cls: cls.__name__)
+ >>> Clock.name() # called by the class
+ 'Clock'
+ >>> Clock().name() # called by an instance
+ 'Clock'
+
+Notice that classmethods (and staticmethods too)
+can only be attached to classes, not to objects:
+
+ >>> class Clock(object): pass
+ >>> c=Clock()
+ >>> c.name=classmethod(lambda cls: cls.__name__)
+ >>> c.name() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: 'classmethod' object is not callable
+
+gives a TypeError. The reason is that classmethods and staticmethods
+are implemented
+trough *attribute descriptors*. This concept will be discussed in detail in a
+forthcoming in chapter 6.
+
+Notice that classmethods are not proving any fundamental feature, since
+one could very well use a normal method and retrieve the class with
+``self.__class__`` as we did in the first chapter.
+Therefore, we could live without (actually, I think they are a non-essential
+complication to the language).
+Nevertheless, now that we have them, we can use them, since
+they come handy in various circumstances, as we will see in the following.
+
+Objects have their privacy
+---------------------------------------------------------------------------
+
+In some situations, it is convenient to give to the developer
+some information that should be hided to the final user. To this
+aim Python uses private names (i.e. names starting with a single
+underscore) and private/protected attributes (i.e. attributes starting with
+a double underscore).
+
+
+Consider for instance the following script:
+
+ ::
+
+ #<privacy.py>
+
+ import time
+
+ class Clock(object):
+ __secret="This Clock is quite stupid."
+
+ myclock=Clock()
+ try: print myclock.__secret
+ except Exception,e: print "AttributeError:",e
+
+ #</privacy.py>
+
+The output of this script is
+
+ ::
+
+ AttributeError: 'Clock' object has no attribute '__secret'
+
+Therefore, even if the Clock object *does* have a ``__secret`` attribute,
+the user cannot access it ! In this way she cannot discover that
+actually "This Clock is quite stupid."
+
+In other programming languages, attributes like ``__secret`` are
+called "private" attributes. However, in Python private attributes
+are not really private and their secrets can be accessed with very
+little effort.
+
+First of all, we may notice that ``myclock`` really contains a secret
+by using the builtin function ``dir()``:
+
+ ::
+
+ dir(myclock)
+ ['_Clock__secret', '__class__', '__delattr__', '__dict__', '__doc__',
+ '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
+ '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__']
+
+We see that the first attribute of myclock is '_Clock__secret``,
+which we may access directly:
+
+ ::
+
+ print myclock._Clock__secret
+ This clock is quite stupid.
+
+We see here the secret of private variables in Python: the *name mangling*.
+When Python sees a name starting with two underscores (and not ending
+with two underscores, otherwise it would be interpreted as a special
+attribute), internally it manage it as ``_Classname__privatename``.
+Notice that if 'Classname' begins with underscores, the leading underscores
+are stripped in such a way to guarantee that the private name starts with
+only *one* underscore. For instance, the '__secret' private attribute
+of classes such as 'Clock', '_Clock', '__Clock', '___Clock', etc. is
+mangled to '_Clock__secret'.
+
+Private names in Python are *not* intended to keep secrets: they
+have other uses.
+
+1. On one hand, private names are a suggestion to the developer.
+ When the Python programmer sees a name starting with one or two
+ underscores in a program written by others, she understands
+ that name should not be of concern for the final user, but it
+ only concerns the internal implementation.
+
+2. On the other hand, private names are quite useful in class
+ inheritance, since they provides safety with respect to the overriding
+ operation. This point we will discussed in the next chapter.
+
+3. Names starting with one (or more) underscores are not imported by the
+ statement ``from module import *``
+
+Remark: it makes no sense to define names with double underscores
+outside classes, since the name mangling doesn't work in this case.
+Let me show an example:
+
+ >>> class Clock(object): __secret="This Clock is quite stupid"
+ >>> def tellsecret(self): return self.__secret
+ >>> Clock.tellsecret=tellsecret
+ >>> Clock().tellsecret() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 2, in tellsecret
+ AttributeError: 'Clock' object has no attribute '__secret'
+
+The explanation is that since ``tellsecret()`` is defined outside the class,
+``__secret`` is not expanded to ``_Clock__secret`` and therefore cannot be
+retrieved, whereas
+
+ >>> class Clock(object):
+ ... __secret="This Clock is quite stupid"
+ ... def tellsecret(self): return self.__secret
+ >>> Clock().tellsecret()
+ This Clock is quite stupid
+
+will work. In other words, private variables are attached to classes,
+not objects.
+
+Objects have properties
+-------------------------------------------------------------------------------
+
+In the previous section we have shown that private variables are of
+little use for keeping secrets: if a developer really wants to restrict
+the access to some methods or attributes, she has to resort to
+*properties*.
+
+Let me show an example:
+
+ ::
+
+ #<secret.py>
+
+ import oopp
+
+ class Clock(object):
+ 'Clock class with a secret'
+
+ you_know_the_pw=False #default
+
+ def give_pw(self,pw):
+ """Check if your know the password. For security, one should crypt
+ the password."""
+ self.you_know_the_pw=(pw=="xyz")
+
+ def get_secret(self):
+ if self.you_know_the_pw:
+ return "This clock doesn't work."
+ else:
+ return "You must give the right password to access 'secret'"
+
+ secret=property(get_secret)
+
+ c=Clock()
+ print c.secret # => You must give the right password to access 'secret'
+ c.give_pw('xyz') # gives the right password
+ print c.secret # => This clock doesn't work.
+ print Clock.secret # => <property object at 0x814c1b4>
+
+ #</secret.py>
+
+In this script, one wants to restrict the access to the attribute
+'secret', which can be accessed only is the user provide the
+correct password. Obviously, this example is not very secure,
+since I have hard coded the password 'xyz' in the source code,
+which is easily accessible. In reality, one should crypt the
+password a perform a more sophisticated test than the trivial
+check ``(pw=="xyz")``; anyway, the example is only intended to
+shown the uses of properties, not to be really secure.
+
+The key action is performed by the descriptor class ``property`` that
+converts the function ``get_secret`` in a property object. Additional
+informations on the usage of ``property`` can be obtained from the
+docstring:
+
+ >>> print property.__doc__
+ property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
+ fget is a function to be used for getting an attribute value, and likewise
+ fset is a function for setting, and fdel a function for del'ing, an
+ attribute. Typical use is to define a managed attribute x:
+ class C(object):
+ def getx(self): return self.__x
+ def setx(self, value): self.__x = value
+ def delx(self): del self.__x
+ x = property(getx, setx, delx, "I'm the 'x' property.")
+
+Properties are another example of attribute descriptors.
+
+Objects have special methods
+---------------------------------------------------------------------------
+
+From the beginning, we stressed that objects have special attributes that
+may turn handy, as for instance the docstring ``__doc__`` and the class
+name attribute ``__class__``. They have special methods, too.
+
+With little doubt, the most useful special method is the ``__init__``
+method, that *initializes* an object right after its creation. ``__init__``
+is typically used to pass parameters to *object factories*. Let me an
+example with geometric figures:
+
+ ::
+
+ #<oopp.py>
+
+ class GeometricFigure(object): #an example of object factory
+ """This class allows to define geometric figures according to their
+ equation in the cartesian plane. It will be extended later."""
+ def __init__(self,equation,**parameters):
+ "Specify the cartesian equation of the object and its parameters"
+ self.eq=equation
+ self.par=parameters
+ for k,v in self.par.items(): #replaces the parameters in the equation
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq)
+ # dynamically creates the function 'contains'
+
+ #</oopp.py>
+
+Here it is how it works:
+
+ >>> from oopp import *
+ >>> disk=GeometricFigure('(x-x0)**2+(y-y0)**2 <= r**2', x0=0,y0=0,r=5)
+ >>> # creates a disk of radius 5 centered in the origing
+ >>> disk.contains(1,2) #asks if the point (1,2) is inside the disk
+ True
+ >>> disk.contains(4,4) #asks if the point (4,4) is inside the disk
+ False
+
+
+Let me continue the section on special methods with some some observations on
+``__repr__`` and ``__str__``.Notice that I
+will not discuss all the subtleties; for a thought discussion, see the
+thread "Using __repr__ or __str__" in c.l.p. (Google is your friend).
+The following discussion applies to new style classes, old style classes
+are subtly different; moreover.
+
+When one writes
+
+ >>> disk
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+one obtains the *string representation* of the object. Actually, the previous
+line is syntactic sugar for
+
+ >>> print repr(disk)
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+or
+
+ >>> print disk.__repr__()
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+The ``repr`` function extracts the string representation from the
+the special method ``__repr__``, which can be redefined in order to
+have objects pretty printed. Notice that ``repr`` is conceptually
+different from the ``str`` function that controls the output of the ``print``
+statement. Actually, ``print o`` is syntactic sugar for ``print str(o)``
+which is sugar for ``print o.__str__()``.
+
+If for instance we define
+
+ ::
+
+ #<oopp.py>
+
+ class PrettyPrinted(object):
+ formatstring='%s' # default
+ def __str__(self):
+ """Returns the name of self in quotes, possibly formatted via
+ self.formatstring. If self has no name, returns the name
+ of its class in angular brackets."""
+ try: #look if the selfect has a name
+ name="'%s'" % self.__name__
+ except AttributeError: #if not, use the name of its class
+ name='<%s>' % type(self).__name__
+ if hasattr(self,'formatstring'):
+ return self.formatstring % name
+ else:
+ return name
+
+ #</oopp.py>
+
+then we have
+
+ >>> from oopp import PrettyPrinted
+ >>> o=PrettyPrinted() # o is an instance of PrettyPrinted
+ >>> print o #invokes o.__str__() which in this case returns o.__class__.name
+ <PrettyPrinted>
+
+whereas
+
+ >>> o # i.e. print repr(o)
+ <oopp.PrettyPrinted object at 0x400a006c>
+
+However, in most cases ``__repr__`` and ``__str__`` gives the same
+output, since if ``__str__`` is not explicitely defined it defaults
+to ``__repr__``. Therefore, whereas modifying ``__str__``
+does not change ``__repr__``, modifying ``__repr__`` changes ``__str__``,
+if ``__str__`` is not explicitely given:
+
+ ::
+
+ #<fairytale1.py>
+
+ "__repr__ can also be a regular method, not a classmethod"
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ jack=Frog(); print repr(jack),jack
+ charles=Prince(); print repr(charles),charles
+
+ #</fairytale1.py>
+
+The output of this script is:
+
+ ::
+
+ <Frog object at 0x81866ec> I am a poor, small, ugly Frog
+ <Prince object at 0x818670c> I am a rich, tall, beautiful Prince
+
+for jack and charles respectively.
+
+``__str__`` and ``__repr__`` are also called by the formatting
+operators "%s" and "%r".
+
+Notice that i) ``__str__`` can be most naturally
+rewritten as a class method; ii) Python is magic:
+
+ ::
+
+ #<fairytale2.py>
+
+ """Shows two things:
+ 1) redefining __repr__ automatically changes the output of __str__
+ 2) the class of an object can be dinamically changed! """
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __repr__(cls):
+ return "I am a "+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __repr__(cls):
+ return "I am a "+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+ def princess_kiss(frog):
+ frog.__class__=Prince
+
+ jack=Frog()
+ princess_kiss(jack)
+ print jack # the same as repr(jack)
+
+ #</fairytale2.py>
+
+Now the output for jack is "I am a rich, tall, beautiful Prince" !
+In Python you may dynamically change the class of an object!!
+
+Of course, this is a feature to use with care ;-)
+
+There are many others special methods, such as __new__, __getattr__,
+__setattr__, etc. They will be discussed in the next chapters, in
+conjunction with inheritance.
+
+Objects can be called, added, subtracted, ...
+---------------------------------------------------------------------------
+
+Python provides a nice generalization of functions, via the concept
+of *callable objects*. A callable object is an object with a ``__call__``
+special method. They can be used to define "functions" that remember
+how many times they are invoked:
+
+ ::
+
+ #<call.py>
+
+ class MultiplyBy(object):
+ def __init__(self,n):
+ self.n=n
+ self.counter=0
+ def __call__(self,x):
+ self.counter+=1
+ return self.n*x
+
+ double=MultiplyBy(2)
+ res=double(double(3)) # res=12
+ print "double is callable: %s" % callable(double)
+ print "You have called double %s times." % double.counter
+
+ #</call.py>
+
+With output
+
+ ::
+
+ double is callable: True
+ You have called double 2 times.
+
+The script also show that callable objects (including functions)
+can be recognized with the ``callable`` built-in function.
+
+Callable object solves elegantly the problem of having "static" variables
+inside functions (cfr. with the 'double' example in chapter 2).
+A class with a ``__call__`` method can be used to generate an entire
+set of customized "functions". For this reason, callable objects are
+especially useful in the conjunction with object factories. Let me show
+an application to my factory of geometric figures:
+
+ ::
+
+ #<oopp.py>
+
+ class Makeobj(object):
+ """A factory of object factories. Makeobj(cls) returns instances
+ of cls"""
+ def __init__(self,cls,*args):
+ self.cls=cls
+ self.args=args
+ def __call__(self,**pars):
+ return self.cls(*self.args,**pars)
+
+ #</oopp.py>
+
+ #<factory.py>
+
+ from oopp import Makeobj,GeometricFigure
+
+ makedisk=Makeobj(GeometricFigure,'(x-x0)**2+(y-y0)**2<r**2')
+ makesquare=Makeobj(GeometricFigure,'abs(x-x0)<L and abs(y-y0)<L')
+ disk=makedisk(x0=0,y0=0,r=10) # make a disk of radius 10
+ square=makesquare(x0=0,y0=0,L=20) # make a disk of side 10
+
+ print disk.contains(9,9) # => False
+ print square.contains(9,9) # => True
+ #etc.
+
+ #</factory.py>
+
+This factory generates callable objects, such as ``makedisk`` and
+``makesquare`` that returns geometric objects. It gives a nicer interface
+to the object factory provided by 'GeometricFigure'.
+
+Notice that the use of the expression ``disk.contains(9,9)`` in order to
+know if the point of coordinates (9,9) is contained in the disk, it is
+rather inelegant: it would be much better to be able to ask if
+``(9,9) in disk``. This is possibile, indeed: and the secrets is to
+define the special method ``__contains__``. This is done in the next
+example, that I think give a good taste of the beauty of objects
+
+ ::
+
+ #<funnyformatter.py>
+
+ from oopp import Makeobj
+
+ Nrow=50; Ncol=78
+
+ class GeometricFigure(object):
+ """This class allows to define geometric figures according to their
+ equation in the cartesian plane. Moreover addition and subtraction
+ of geometric figures are defined as union and subtraction of sets."""
+ def __init__(self,equation,**parameters):
+ "Initialize "
+ self.eq=equation
+ self.par=parameters
+ for (k,v) in self.par.items(): #replaces the parameters
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq)
+ def combine(self,fig,operator):
+ """Combine self with the geometric figure fig, using the
+ operators "or" (addition) and "and not" (subtraction)"""
+ comboeq="("+self.eq+")"+operator+"("+fig.eq+")"
+ return GeometricFigure(comboeq)
+ def __add__(self,fig):
+ "Union of sets"
+ return self.combine(fig,' or ')
+ def __sub__(self,fig):
+ "Subtraction of sets"
+ return self.combine(fig,' and not')
+ def __contains__(self,point): #point is a tuple (x,y)
+ return self.contains(*point)
+
+ makedisk=Makeobj(GeometricFigure,'(x-x0)**2/4+(y-y0)**2 <= r**2')
+ upperdisk=makedisk(x0=38,y0=7,r=5)
+ smalldisk=makedisk(x0=38,y0=30,r=5)
+ bigdisk=makedisk(x0=38,y0=30,r=14)
+
+ def format(text,shape):
+ "Format the text in the shape given by figure"
+ text=text.replace('\n',' ')
+ out=[]; i=0; col=0; row=0; L=len(text)
+ while 1:
+ if (col,row) in shape:
+ out.append(text[i]); i+=1
+ if i==L: break
+ else:
+ out.append(" ")
+ if col==Ncol-1:
+ col=0; out.append('\n') # starts new row
+ if row==Nrow-1: row=0 # starts new page
+ else: row+=1
+ else: col+=1
+ return ''.join(out)
+
+ composition=bigdisk-smalldisk+upperdisk
+ print format(text='Python Rules!'*95,shape=composition)
+
+ #</funnyformatter.py>
+
+I leave as an exercise for the reader to understand how does it work and to
+play with other geometric figures (he can also generate them trough the
+'Makeobj' factory). I think it is nicer to show its output:
+
+ ::
+
+
+ Pyt
+ hon Rules!Pyt
+ hon Rules!Python
+ Rules!Python Rules!
+ Python Rules!Python
+ Rules!Python Rules!P
+ ython Rules!Python
+ Rules!Python Rules!
+ Python Rules!Pyth
+ on Rules!Pyth
+ on
+
+
+
+ Rul
+ es!Python Rules!Pytho
+ n Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Pyt
+ hon Rules!Python Rules!Python Rules!Python Rules!
+ Python Rules!Python Rules!Python Rules!Python Rules
+ !Python Rules!Python Rule s!Python Rules!Python Rul
+ es!Python Rules!Pyth on Rules!Python Rule
+ s!Python Rules!Pyth on Rules!Python Rul
+ es!Python Rules!Py thon Rules!Python
+ Rules!Python Rules !Python Rules!Pyth
+ on Rules!Python Ru les!Python Rules!P
+ ython Rules!Python Rules!Python Rule
+ s!Python Rules!Pyt hon Rules!Python R
+ ules!Python Rules!P ython Rules!Python
+ Rules!Python Rules!P ython Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Python
+ Rules!Python Rules!Python Rules!Python Rules!Pytho
+ n Rules!Python Rules!Python Rules!Python Rules!Py
+ thon Rules!Python Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!
+ Python Rules!Python R
+ ule
+
+
+
+
+
+
+
+ s!
+
+Remark.
+
+Unfortunately, "funnyformatter.py" does not reuse old code: in spite of the
+fact that we already had in our library the 'GeometricFigure' class, with
+an "__init__" method that is exactly the same of the "__init__" method in
+"funnyformatter.py", we did not reuse that code. We simply did a cut
+and paste. This means that if we later find a bug in the ``__init__`` method,
+we will have to fix it twice, both in the script and in the library. Also,
+if we plan to extend the method later, we will have to extend it twice.
+Fortunately, this nasty situation can be avoided: but this requires the
+power of inheritance.
+
+
+THE POWER OF CLASSES
+==========================================================================
+
+This chapter is devoted to the concept of class inheritance. I will discuss
+single inheritance, cooperative methods, multiple inheritance and more.
+
+
+The concept of inheritance
+----------------------------------------------------------------------
+
+Inheritance is perhaps the most important basic feature in OOP, since it
+allows the reuse and incremental improvement of old code.
+To show this point, let me come back to one of the
+examples I have introduced in the last chapter, 'fairytale1.py' script,
+where I defined the classes 'Frog' and 'Prince' as
+
+ ::
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+We see that the way we followed here was very bad since:
+
+1. The ``__str__`` method is duplicated both in Frog and in Prince: that
+ means that if we find a bug a later, we have to fix it twice!
+
+2. The ``__str__`` was already defined in the PrettyPrinted class (actually
+ more elegantly), therefore we have triplicated the work and worsened the
+ situation!
+
+This is very much against the all philosophy of OOP:
+
+ *never cut and paste!*
+
+We should *reuse* old code, not paste it!
+
+The solution is *class inheritance*. The idea behind inheritance is to
+define new classes as subclasses of a *parent* classes, in such a way that
+the *children* classes possess all the features of the parents.
+That means that we do not need to
+redefine the properties of the parents explicitely.
+In this example, we may derive both 'Frog' and 'Prince' from
+the 'PrettyPrinted' class, thus providing to both 'Frog' and 'Prince'
+the ``PrettyPrinted.__str__`` method with no effort:
+
+ >>> from oopp import PrettyPrinted
+ >>> class Frog(PrettyPrinted): attributes="poor, small, ugly"
+ ...
+ >>> class Prince(PrettyPrinted): attributes="rich, tall, beautiful"
+ ...
+ >>> print repr(Frog()), Frog()
+ <__main__.Frog object at 0x401cbeac> <Frog>
+ >>> print Prince()
+ >>> print repr(Prince()),Prince()
+ <__main__.Prince object at 0x401cbaac> <Prince>
+
+Let me show explicitly that both 'Frog' and 'Prince' share the
+'PrettyPrinted.__str__' method:
+
+ >>> id(Frog.__str__) # of course, YMMV
+ 1074329476
+ >>> id(Prince.__str__)
+ 1074329476
+ >>> id(PrettyPrinted.__str__)
+ 1074329476
+
+The method is always the same, since the object reference is the same
+(the precise value of the reference is not guaranteed to be 1074329476,
+however!).
+
+This example is good to show the first advantage of inheritance:
+*avoiding duplication of code*.
+Another advantage of inheritance, is *extensibility*: one can very easily
+improve existing code. For instance, having written the ``Clock`` class once,
+I can reuse it in many different ways. for example I can build a ``Timer``
+to be used for benchmarks. It is enough to reuse the function ``with_timer``
+introduced in the first chapter (functions are good for reuse of code, too ;):
+
+ ::
+
+ #<oopp.py>
+
+ class Timer(Clock):
+ "Inherits the get_time staticmethod from Clock"
+ execute=staticmethod(with_timer)
+ loop_overhead=staticmethod(loop_overhead)
+
+
+ #</oopp.py>
+
+Here there is an example of application:
+
+ >>> from oopp import Timer
+ >>> Timer.get_time()
+ '16:07:06'
+
+Therefore 'Timer' inherits 'Clock.get_time'; moreover it has the additional
+method ``execute``:
+
+ >>> def square(x): return x*x
+ ...
+ >>> Timer.execute(square,n=100000)(1)
+ executing square ...
+ Real time: 0.01 ms CPU time: 0.008 ms
+
+The advantage of putting the function ``execute`` in a class is that
+now we may *inherit* from that class and improve out timer *ad
+libitum*.
+
+Inheritance versus run-time class modifications
+-------------------------------------------------------------------------
+
+Naively, one could think of substituting inheritance with run-time
+modification of classes, since this is allowed by Python. However,
+this is not such a good idea, in general. Let me give a simple example.
+Suppose we want to improve our previous clock, to show the date, too.
+We could reach that goal with the following script:
+
+ ::
+
+ #<clock2.py>
+
+ "Shows how to modify and enhances classes on the fly"
+
+ from oopp import *
+
+ clock=Clock() #creates a Clock instance
+ print clock.get_time() # print the current time
+
+ get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+ get_data_and_time=lambda : "Today is: %s \nThe time is: %s" % (
+ get_data(),get_time()) # enhances get_time
+
+ Clock.get_time=staticmethod(get_data_and_time)
+
+ print clock.get_time() # print the current time and data
+
+ #</clock2.py>
+
+The output of this script is:
+
+ 12:51:25
+ Today is: Sat Feb 22 2003
+ The time is: 12:51:25
+
+Notice that:
+
+1. I instantiated the ``clock`` object *before* redefining the ``get_time``
+ method, when it only could print the time and *not* the date.
+2. However, after the redefinition of the class, the behaviour of all its
+ instances is changed, *including the behaviour of objects instantiated
+ before the change!*. Then ``clock`` *can* print the date, too.
+
+This is not so surprising, once you recognize that Guido own a very famous
+time-machine ... ;-)
+
+Seriously, the reason is that an object does not contains a reserved copy
+of the attributes and methods of its class: it only contains *references*
+to them. If we change them in the class, the references to them in the
+object will stay the same, but the contents will change.
+
+In this example, I have solved the problem of enhancing the 'Clock' class
+without inheritance, but dynamically replaceing its ``get_time``
+(static) method with the `get_data_and_time`` (static) method.
+The dynamics modification of methods can be cool, but it should be avoided
+whenever possible, at least for two reasons [#]_:
+
+1. having a class and therefore all its instances (including the instances
+ created before the modification !) changed during the life-time of the
+ program can be very confusing to the programmer, if not to the interpreter.
+
+2. the modification is destructive: I cannot have the old ``get_time`` method
+ and the new one at the same time, unless one explicitly gives to it
+ a new name (and giving new names increases the pollution of the namespace).
+
+Both these disadvantages can be solved by resorting to the mechanism of
+inheritance. For instance, in this example, we can derive a new class
+``NewClock`` from ``Clock`` as follows:
+
+ ::
+
+ #<newclock.py>
+
+ import oopp,time
+
+ get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+ get_data_and_time=lambda : "Today is: %s \nThe time is: %s" % (
+ get_data(),oopp.get_time()) # enhances get_time
+
+ class NewClock(oopp.Clock):
+ """NewClock is a class that inherits from Clock, provides get_data
+ and overrides get_time."""
+ get_data=staticmethod(get_data)
+ get_time=staticmethod(get_data_and_time)
+
+ clock=oopp.Clock(); print 'clock output=',clock.get_time()
+ newclock=NewClock(); print 'newclock output=',newclock.get_time()
+
+ #</newclock.py>
+
+The output of this script is:
+
+ ::
+
+ clock output= 16:29:17
+ newclock output= Today is: Sat Feb 22 2003
+ The time is: 16:29:17
+
+We see that the two problems previously discussed are solved since:
+
+i) there is no cut and paste: the old method ``Clock.get_time()`` is used
+ in the definition of the new method ``NewClock.get_time()``;
+ii) the old method is still accessible as ``Clock.get_time()``; there is
+ no need to invent a new name like ``get_time_old()``.
+
+We say that the method ``get_time`` in ``NewClock`` *overrides* the method
+``get_time`` in Clock.
+
+
+This simple example shows the power of inheritance in code
+reuse, but there is more than that.
+
+Inheritance is everywhere in Python, since
+all classes inherit from object. This means that all classes
+inherit the methods and attributes of the object class, such as ``__doc__``,
+``__class__``, ``__str__``, etc.
+
+
+ .. [#] There are cases when run-time modifications of classes is useful
+ anyway: particularly when one wants to modify the behavior of
+ classes written by others without changing the source code. I
+ will show an example in next chapter.
+
+Inheriting from built-in types
+-----------------------------------------------------------------------
+
+However, one can subclass a built-in type, effectively creating an
+user-defined type with all the feature of a built-in type, and modify it.
+
+Suppose for instance one has a keyword dictionary such as
+
+ >>> kd={'title': "OOPP", 'author': "M.S.", 'year': 2003}
+
+it would be nice to be able to access the attributes without
+excessive quoting, i.e. using ``kd.author`` instead of ``kd["author"]``.
+This can be done by subclassing the built-in class ``dict`` and
+by overriding the ``__getattr__`` and ``__setattr__`` special methods:
+
+ ::
+
+ #<oopp/py>
+
+ class kwdict(dict):
+ "Keyword dictionary base class"
+ def __getattr__(self,attr):
+ return self[attr]
+ def __setattr__(self,key,val):
+ self[key]=val
+ __str__ = pretty
+
+ #</oopp/py>
+
+
+Here there is an example of usage:
+
+ >>> from oopp import kwdict
+ >>> book=kwdict({'title': "OOPP", 'author': "M.S."})
+ >>> book.author #it works
+ 'M.S.'
+ >>> book["author"] # this also works
+ 'M.S.'
+ >>> book.year=2003 #you may also add new fields on the fly
+ >>> print book
+ author = M.S.
+ title = OOPP
+ year = 2003
+
+The advantage of subclassing the built-in 'dict', it that you have for free
+all the standard dictionary methods, without having to reimplement them.
+
+However, to subclass built-in it is not always a piece of cake. In
+many cases there are complications, indeed. Suppose for instance
+one wants to create an enhanced string type, with
+the ability of indent and dedent a block of text provided by
+the following functions:
+
+ ::
+
+ #<oopp.py>
+
+ def indent(block,n):
+ "Indent a block of code by n spaces"
+ return '\n'.join([' '*n+line for line in block.splitlines()])
+
+ def dedent(block):
+ "Dedent a block of code, if need there is"""
+ lines=block.splitlines()
+ for line in lines:
+ strippedline=line.lstrip()
+ if strippedline: break
+ spaces=len(line)-len(strippedline)
+ if not spaces: return block
+ return '\n'.join([line[spaces:] for line in lines])
+
+ #</oopp.py>
+
+The solution is to inherit from the built-in string type ``str``, and to
+add to the new class the ``indent`` and ``dedent`` methods:
+
+ >>> from oopp import indent,dedent
+ >>> class Str(str):
+ ... indent=indent
+ ... dedent=dedent
+ >>> s=Str('spam\neggs')
+ >>> type(s)
+ <class '__main__.Str'>
+ >>> print s.indent(4)
+ spam
+ eggs
+
+However, this approach has a disadvantage, since the output of ``indent`` is
+not a ``Str``, but a normal ``str``, therefore without the additional
+``indent`` and ``dedent`` methods:
+
+ >>> type(s.indent(4))
+ <type 'str'>
+ >>> s.indent(4).indent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+ AttributeError: 'str' object has no attribute 'indent'
+ >>> s.indent(4).dedent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+ AttributeError: 'str' object has no attribute 'dedent'
+
+We would like ``indent`` to return a ``Str`` object. To solve this problem
+it is enough to rewrite the class as follows:
+
+ ::
+
+ #<Str.py>
+
+ from oopp import indent,dedent
+
+ class Str(str):
+ def indent(self,n):
+ return Str(indent(self,n))
+ def dedent(self):
+ return Str(dedent(self))
+
+ s=Str('spam\neggs').indent(4)
+ print type(s)
+ print s # indented s
+ s=s.dedent()
+ print type(s)
+ print s # non-indented s
+
+ #</Str.py>
+
+Now, everything works and the output of the previous script is
+
+ ::
+
+ <class 'Str'>
+ spam
+ eggs
+ <class 'Str'>
+ spam
+ eggs
+
+The solution works because now ``indent()`` returns an instance
+of ``Str``, which therefore has an ``indent`` method. Unfortunately,
+this is not the end. Suppose we want to add another food to our list:
+
+ >>> s2=s+Str("\nham")
+ >>> s2.indent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'str' object has no attribute 'indent'
+
+The problem is the same, again: the type of ``s2`` is ``str``
+
+ >>> type(s2)
+ <type 'str'>
+
+and therefore there is no ``indent`` method available. There is a
+solution to this problem, i.e. to redefine the addition operator
+for objects of the class ``Str``. This can be done directly by hand,
+but it is *ugly* for the following reasons:
+
+1. If you derive a new class from ``Str`` you have to redefine the
+ addition operator (both the left addition and the right addition [#]_)
+ again (ughh!);
+2. There are others operators you must redefine, in particular the
+ the augumented assignement operator ``+=``, the repetition operator ``*``
+ and its augmented version ``*=``;
+3. In the case of numeric types, one must redefine, ``+,-,*,/,//, mod,``,
+ possibily ``<<,>>`` and others, including the corresponding
+ augumented assignement operators and the left and the right form of
+ the operators.
+
+This is a mess, especially since due to point 1, one has to redefined
+all the operators each time she defines a new subclass. I short, one has
+to write a lot of boilerplate for a stupid job that the language
+should be able to perform itself automatically. But here are the
+good news: Python *can* do all that automatically, in an elegant
+and beautiful way, which works for all types, too.
+
+But this requires the magic of metaclasses.
+
+ .. [#] The right addition works this way. Python looks at the expression x+y
+ and if x has an explicit__add__ method invokes it; on the other hand,
+ if x does not define an __add__ method, Python considers y+x.
+ If y defines a __radd__ method, it invokes it, otherwise
+ raises an exception. The same is done for right multiplication, etc.
+
+Controlling the creation of objects
+---------------------------------------------------------------------------
+
+Before introducing multiple inheritance, let me make a short digression on
+the mechanism of object creation in Python 2.2+. The important point is
+that new style classes have a ``__new__`` static method that allows
+the user to take complete control of object creation. To understand how
+``__new__`` works, I must explain what happens when an object is instantiated
+with a statement like
+
+ ::
+
+ s=Str("spam") #object creation
+
+What happens under the hood, is that the special static method ``__new__``
+of the class ``Str`` (inherited from the built-in ``str`` class)
+is invoked ``before`` the ``Str.__init__`` method. This means that
+the previous line should really be considered syntactic sugar for:
+
+ ::
+
+ s=Str.__new__(Str,"spam") # Str.__new__ is actually str.__new__
+ assert isinstance(s,Str)
+ Str.__init__(s,"spam") # Str.__init__ is actually str.__init__
+
+Put it more verbosely, what happens during the object creation is the
+following:
+
+1. the static method ``__new__`` is invoked with the class of the created
+ object as first argument [#]_;
+
+2. ``__new__`` returns an instance of that class.
+
+3. the instance is then initialized by the ``__init__`` method.
+
+Notice that both ``__new__`` and ``__init__`` are called with the same
+argument list, therefore one must make sure that they have a compatible
+signature.
+
+Let me discuss now why ``__new__`` must be a static method.
+First of all, it cannot be a normal method with a first argument which is an
+instance of the calling class, since at the time of ``__new__`` invocation
+that instance (``myclock`` in the example) has still to be created
+Since ``__new__`` needs information about the class calling it, one
+could think of implementing ``__new__`` as a class method. However,
+this would implicitly pass the caller class and return an instance
+of it. It is more convenient, to have the ability of creating
+instances of any class directly from C.__new__(B,*args,**kw)
+
+For this reasons, ``__new__`` must be a static method and pass explicitly
+the class which is calling it.
+
+Let me now show an important application of the ``__new__`` static method:
+forbidding object creation. For instance, sometimes it is useful to have
+classes that cannot be instantiated. This kind of classes can be
+obtained by inheriting from a ``NonInstantiable`` class:
+
+ ::
+
+ #<oopp.py>
+
+ class NonInstantiableError(Exception):
+ pass
+
+ class NonInstantiable(object):
+ def __new__(cls,*args,**kw):
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import NonInstantiable,get_time
+ >>> class Clock(NonInstantiable):
+ ... get_time=staticmethod(get_time)
+ >>> Clock.get_time() # works
+ '18:48:08'
+ Clock() #error
+ Traceback (most recent call last):
+ File "<pyshell#6>", line 1, in ?
+ Clock()
+ File "oopp.py", line 257, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+ NonInstantiableError: <class '__main__.Clock'> cannot be instantiated
+
+However, the approach pursued here has a disadvantage:``Clock`` was already
+defined as a subclass of ``object`` and I has to change the source code
+to make it a subclass of 'NonInstantiable'. But what happens if
+I cannot change the sources? How can I *reuse* the old code?
+
+The solution is provided by multiple inheritance.
+
+Notice that '__new__' is a staticmethod: [#]_
+
+ >>> type(NonInstantiable.__dict__['__new__'])
+ <type 'staticmethod'>
+
+ .. [#] This is how ``type(s)`` or ``s.__class__`` get to know that
+ ``s`` is an instance of ``Str``, since the class information is
+ explicitely passed to the newborn object trough ``__new__``.
+
+ .. [#] However ``object.__dict__['__new__']`` is not a staticmethod
+
+ >>> type(object.__dict__['__new__']) # special case
+ <type 'builtin_function_or_method'>
+
+
+Multiple Inheritance
+----------------------------------------------------------------------------
+
+Multiple Inheritance (often abbreviated as MI) is often
+considered one of the most advanced topic in Object Oriented Programming.
+It is also one of the most difficult features to implement
+in an Object Oriented Programming language. Even, some languages by design
+decided to avoid it. This is for instance the case of Java, that avoided
+MI having seen its implementation in C++ (which is not for the faint of
+heart ;-) and uses a poorest form of it trough interfaces.
+For what concerns the scripting languages, of which
+the most famous are Perl, Python and Ruby (in this order, even if
+the right order would be Python, Ruby and Perl), only Python
+implements Multiple Inheritance well (Ruby has a restricted form
+of it trough mix-ins, whereas Perl implementation is too difficult
+for me to understand what it does ;).
+
+The fact that Multiple Inheritance can be hairy, does not mean that it
+is *always* hairy, however. Multiple Inheritance is used with success
+in Lisp derived languages (including Dylan).
+
+The aims of this chapter is to discuss the
+Python support for MI in the most recent version (2.2 and 2.3), which
+has considerably improved with respect to previous versions.
+The message is the following: if Python 1.5 had a basic support for
+MI inheritance (basic but nevertheless with nice features, dynamic),
+Python 2.2 has *greatly* improved that support and with the
+change of the Method Resolution Order in Python 2.3, we may say
+that support for MI is now *excellent*.
+
+I strongly encourage Python programmers to use MI a lot: this will
+allows even a stronger reuse of code than in single inheritance.
+
+Often, inheritance is used when one has a complicate class B, and she wants
+to modify (or enhance) its behavior, by deriving a child class C, which is
+only slightly different from B. In this situation, B is already a standalone
+class, providing some non-trivial functionality, independently from
+the existence of C. This kind of design it typical of the so called
+*top-down* philosophy, where one builds the
+all structure as a monolithic block, leaving room only for minor improvements.
+An alternative approach is the so called *bottom-up* programming, in
+which one builds complicate things starting from very simple building blocks.
+In this logic, it is very appealing the idea of creating classes with the
+only purpose of being derived. The 'NonInstantiable' just defined is a
+perfect example of this kind of classes, though with multiple inheritance
+in mind and often called *mixin* classes.
+It can be used to create a new class ``NonInstantiableClock``
+that inherits from ``Clock`` and from ``NonInstantiable``.
+
+ ::
+
+ #<oopp.py>
+
+ class NonInstantiableClock(Clock,NonInstantiable):
+ pass
+
+ #</oopp.py>
+
+Now ``NonInstantiableClock`` is both a clock
+
+ >>> from oopp import NonInstantiableClock
+ >>> NonInstantiableClock.get_time() # works
+ '12:57:00'
+
+and a non-instantiable class:
+
+ >>> NonInstantiableClock() # as expected, give an error
+ Traceback (most recent call last):
+ File "<pyshell#2>", line 1, in ?
+ NonInstantiableClock() # error
+ File "oopp.py", line 245, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+ NonInstantiableError: <class 'oopp.NonInstantiableClock'>
+ cannot be instantiated
+
+Let me give a simple example of a situation where the mixin approach
+comes handy. Suppose that the owner of a 'Pizza-shop' needs a program to
+take care of all the pizzas to-go he sell. Pizzas are distinguished
+according to their size (small, medium or large) and their toppings.
+The problem can be solved by inheriting from a generic pizza factory
+like this:
+
+ ::
+
+ #<oopp.py>
+
+ class GenericPizza(object): # to be customized
+ toppinglist=[] # nothing, default
+ baseprice=1 # one dollar, default
+ topping_unit_price=0.5 # half dollar for each topping, default
+ sizefactor={'small':1, 'medium':2, 'large':3}
+ # a medium size pizza costs twice a small pizza,
+ # a large pizza costs three times
+ def __init__(self,size):
+ self.size=size
+ def price(self):
+ return (self.baseprice+
+ self.toppings_price())*self.sizefactor[self.size]
+ def toppings_price(self):
+ return len(self.toppinglist)*self.topping_unit_price
+ def __str__(self):
+ return '%s pizza with %s, cost $ %s' % (self.size,
+ ','.join(self.toppinglist),
+ self.price())
+
+ #</oopp.py>
+
+Here the base class 'GenericPizza' is written with inheritance in mind: one
+can derives many pizza classes from it by overriding the ``toppinglist``;
+for instance one could define
+
+ >>> from oopp import GenericPizza
+ >>> class Margherita(GenericPizza):
+ ... toppinglist=['tomato']
+
+The problem of this approach is that one must define dozens of
+different pizza subclasses (Marinara, Margherita, Capricciosa, QuattroStagioni,
+Prosciutto, ProsciuttoFunghi, PizzaDellaCasa, etc. etc. [#]_). In such a
+situation, it is better to perform the generation of subclasses in a smarter
+way, i.e. via a customizable class factory.
+A simpler approach is to use always the same class and to customize
+its instances just after creation. Both approaches can be implemented via
+the following 'Customizable' mixin class, not meant to be instantiated,
+but rather to be *inherited*:
+
+ ::
+
+ #<oopp.py>
+
+ class Customizable(object):
+ """Classes inhering from 'Customizable' have a 'with' method acting as
+ an object modifier and 'With' classmethod acting as a class factory"""
+ def with(self,**kw):
+ customize(self,**kw)# customize the instance
+ return self # returns the customized instance
+ def With(cls,**kw):
+ class ChildOf(cls): pass # a new class inheriting from cls
+ ChildOf.__name__=cls.__name__ # by default, with the same name
+ customize(ChildOf,**kw) # of the original class
+ return ChildOf
+ With=classmethod(With)
+
+ #</oopp.py>
+
+Descendants of 'Customizable' can be customized by using
+'with', that directly acts on the instances, or 'With', that returns
+new classes. Notice that one could make 'With' to customize the
+original class, without returning a new one; however, in practice,
+this would not be safe: I remind that changing a class modifies
+automatically all its instances, even instances created *before*
+the modification. This could produce bad surprises: it is better to
+returns new classes, that may have the same name of the original one,
+but are actually completely independent from it.
+
+In order to solve the pizza shop problem we may define a 'CustomizablePizza'
+class
+
+ ::
+
+ #<oopp.py>
+
+ class CustomizablePizza(GenericPizza,Customizable):
+ pass
+
+ #</oopp.py>
+
+which can be used in two ways: i) to customize instances just after creation:
+
+ >>> from oopp import CustomizablePizza
+ >>> largepizza=CustomizablePizza('large') # CustomizablePizza instance
+ >>> largemarinara=largepizza.with(toppinglist=['tomato'],baseprice=2)
+ >>> print largemarinara
+ large pizza with tomato mozzarella, cost $ 7.0
+
+and ii) to generated customized new classes:
+
+ >>> Margherita=CustomizablePizza.With(
+ ... toppinglist=['tomato','mozzarella'], __name__='Margherita')
+ >>> print Margherita('medium')
+ medium pizza with tomato,mozzarella, cost $ 4.0
+
+The advantage of the bottom-up approach, is that the 'Customizable' class
+can be reused in completely different problems; for instance, it could
+be used as a class factory. For instance we could use it to generate a
+'CustomizableClock' class as in this example:
+
+ >>> from oopp import *
+ >>> CustomizableClock=Customizable.With(get_time=staticmethod(Clock.get_time),
+ ... __name__='CustomizableClock') #adds get_time
+ >>> CustomizableClock.get_time() # now it works
+ '09:57:50'
+
+Here 'Customizable' "steal" the 'get_time' method from 'Clock'.
+However that would be a rather perverse usage ;) I wrote it to show
+the advantage of classmethods, more than to suggest to the reader that
+this is an example of good programming.
+
+ .. [#] In Italy, you can easily find "pizzerie" with more than 50 different
+ kinds of pizzas (once I saw a menu with something like one hundred
+ different combinations ;)
+
+Cooperative hierarchies
+-----------------------------------------------------------------------
+
+The examples of multiple inheritance hierarchies given until now were pretty
+easy. The reason is that there was no interaction between the methods of the
+children and of the parents. However, things get more complicated (and
+interesting ;) when the methods in the hierarchy call each other.
+Let me consider an example coming from paleoantropology:
+
+ ::
+
+ #<paleoanthropology1.py>
+
+ class HomoHabilis(object):
+ def can(self):
+ print self,'can:'
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self): #overrides HomoHabilis.can
+ HomoHabilis.can(self)
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self): #overrides HomoSapiens.can
+ HomoSapiens.can(self)
+ print " - make art"
+
+ modernman=HomoSapiensSapiens()
+ modernman.can()
+
+ #</paleoanthropology1.py>
+
+In this example children methods call parent methods:
+'HomoSapiensSapiens.can' calls 'HomoSapiens.can' that in turns calls
+'HomoHabilis.can' and the final output is:
+
+ ::
+
+ <__main__.HomoSapiensSapiens object at 0x814e1fc> can:
+ - make tools
+ - make abstractions
+ - make art
+
+The script works, but it is far from ideal, if code reuse and refactoring
+are considered important requirements. The point is that (very likely, as the
+research in paleoanthropology progresses) we may want to extend the
+hierarchy, for instance by adding a class on the top or in the middle.
+In the present form, this would require a non-trivial modification of
+the source code (especially
+if one think that the hierarchy could be fleshed out with dozens of others
+methods and attributes). However, the aim of OOP is to avoid as
+much as possible source code modifications. This goal can be attained in
+practice, if the source code is written to be friendly to extensions and
+improvements as much as possible. I think it is worth to spend some time
+in improving this example, since what can be learn here,
+can be lifted to real life cases.
+
+First of all, let me define a generic *Homo* class, to be used
+as first ring of the inheritance chain (actually the first ring is
+'object'):
+
+ ::
+
+ #<oopp.py>
+
+ class Homo(PrettyPrinted):
+ """Defines the method 'can', which is intended to be overriden
+ in the children classes, and inherits '__str__' from PrettyPrinted,
+ ensuring a nice printing representation for all children."""
+ def can(self):
+ print self,'can:'
+
+ #</oopp.py>
+
+Now, let me point out one of the shortcomings of the previous code: in each
+subclass, we explicitly call its parent class (also called super class)
+by its name. This is inconvenient, both because a change of name in
+later stages of the project would require a lot of search and replace
+(actually not a lot in this toy example, but you can imagine having
+a very big projects with dozens of named method calls) and because it makes
+difficult to insert a new element in the inheritance hierarchy.
+The solution to this problems is the
+``super`` built-in, which provides an easy access to the methods
+of the superclass.
+``super`` objects comes in two flavors: ``super(cls,obj)`` objects return
+bound methods whereas ``super(cls)`` objects return unbound methods.
+In the next code we will use the first form. The hierarchy can more elegantly
+be rewritten as [#]_ :
+
+ ::
+
+ #<paleo2.py>
+
+ from oopp import Homo
+
+ class HomoHabilis(Homo):
+ def can(self):
+ super(HomoHabilis,self).can()
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(HomoSapiens,self).can()
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(HomoSapiensSapiens,self).can()
+ print " - make art"
+
+
+ HomoSapiensSapiens().can()
+
+ #</paleo2.py>
+
+with output
+
+ ::
+
+ <HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art
+
+This is not yet the most elegant form, since even
+if ``super`` avoids naming the base class explicitely, still it
+requires to explicitely name the class where it is defined. This is
+rather annoying.
+Removing that restriction, i.e. implementing really anonymous
+``super`` calls, is possible but requires a good understand of
+private variables in inheritance.
+
+Inheritance and privacy
+----------------------------------------------------------------------
+
+In order to define anonymous cooperative super calls, we need classes
+that know themselves, i.e. containing a reference to themselves. This
+is not an obvious problem as it could seems, since it cannot be solved
+without incurring in the biggest annoyance in inheritance:
+*name clashing*. Name clashing happens when names and attributes defined
+in different ancestors overrides each other in a unwanted order.
+Name clashing is especially painful in the case of cooperative
+hierarchies and particularly in in the problem at hand.
+
+
+A naive solution would be to attach a plain (i.e. non-private)
+attribute '.this' to the class, containing a reference
+to itself, that can be invoked by the methods of the class.
+Suppose, for instance, that I want to use that attribute in the ``__init__``
+method of that class. A naive attempt would be to write something like:
+
+ >>> class B(object):
+ ... def __init__(self):
+ ... print self.this,'.__init__' # .this defined later
+ >>> B.this=B # B.this can be set only after B has been created
+ >>> B()
+ <class '__main__.B'>
+
+Unfortunately, this approach does not work with cooperative hierarchies.
+Consider, for instance, extending 'B' with a cooperative children
+class 'C' as follows:
+
+ >>> class C(B):
+ ... def __init__(self):
+ ... super(self.this,self).__init__() # cooperative call
+ ... print type(self).this,'.__init__'
+ >>> C.this=C
+
+``C.__init__`` calls ``B.__init__`` by passing a 'C' instance, therefore
+``C.this`` is printed and not ``B.this``:
+
+ >>> C()
+ <class '__main__.C'> .__init__
+ <class '__main__.C'> .__init__
+ <__main__.C object at 0x4042ca6c>
+
+The problem is that the ``C.this`` overrides ``B.this``. The only
+way of avoiding the name clashing is to use a private attribute
+``.__this``, as in the following script:
+
+ ::
+
+ #<privateh.py>
+
+ class B(object):
+ def __init__(self):
+ print self.__this,'.__init__'
+ B._B__this=B
+
+ class C(B):
+ def __init__(self):
+ super(self.__this,self).__init__() # cooperative __init__
+ print self.__this,'.__init__'
+ C._C__this=C
+
+ C()
+
+ # output:
+ # <class '__main__.B'> .__init__
+ # <class '__main__.C'> .__init__
+
+ #</privateh.py>
+
+The script works since, due to the magic of the mangling mechanism,
+in ``B.__init__``, ``self._B__this`` i.e. ``B`` is retrieved, whereas in
+``C.__init__`` ``self._C__this`` i.e. ``C`` is retrieved.
+
+The elegance of the mechanism can be improved with an helper function
+that makes its arguments reflective classes, i.e. classes with a
+``__this`` private attribute:
+
+ ::
+
+ #<oopp.py>
+
+ def reflective(*classes):
+ """Reflective classes know themselves, i.e. they possess a private
+ attribute __this containing a reference to themselves. If the class
+ name starts with '_', the underscores are stripped."""
+ for c in classes:
+ name=c.__name__ .lstrip('_') # in 2.3
+ setattr(c,'_%s__this' % name,c)
+
+ #</oopp.py>
+
+It is trivial to rewrite the paleonthropological hierarchy in terms of
+anonymous cooperative super calls by using this trick.
+
+ ::
+
+ #<oopp.py>
+
+ class HomoHabilis(Homo):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make art"
+
+ reflective(HomoHabilis,HomoSapiens,HomoSapiensSapiens)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import *
+ >>> man=HomoSapiensSapiens(); man.can()
+ <HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art
+
+We may understand why it works by looking at the attributes of man:
+
+ >>> print pretty(attributes(man))
+ _HomoHabilis__this = <class 'oopp.HomoHabilis'>
+ _HomoSapiensSapiens__this = <class 'oopp.HomoSapiensSapiens'>
+ _HomoSapiens__this = <class 'oopp.HomoSapiens'>
+ can = <bound method HomoSapiensSapiens.can of
+ <oopp.HomoSapiensSapiens object at 0x404292ec>>
+ formatstring = %s
+
+It is also interesting to notice that the hierarchy can be entirely
+rewritten without using cooperative methods, but using private attributes,
+instead. This second approach is simpler, as the following script shows:
+
+ ::
+
+ #<privatehierarchy.py>
+
+ from oopp import PrettyPrinted,attributes,pretty
+
+ class Homo(PrettyPrinted):
+ def can(self):
+ print self,'can:'
+ for attr,value in attributes(self).iteritems():
+ if attr.endswith('__attr'): print value
+ class HomoHabilis(Homo):
+ __attr=" - make tools"
+ class HomoSapiens(HomoHabilis):
+ __attr=" - make abstractions"
+ class HomoSapiensSapiens(HomoSapiens):
+ __attr=" - make art"
+
+ modernman=HomoSapiensSapiens()
+ modernman.can()
+ print '----------------------------------\nAttributes of',modernman
+ print pretty(attributes(modernman))
+
+ #</privatehierarchy.py>
+
+Here I have replaced the complicate chain of cooperative methods with
+much simpler private attributes. Only the 'can' method in the 'Homo'
+class survives, and it is modified to print the value of the '__attr'
+attributes. Moreover, all the classes of the hierarchy have been made
+'Customizable', in view of future extensions.
+
+The second script is much shorter and much more elegant than the original
+one, however its logic can be a little baffling, at first. The solution
+to the mistery is provided by the attribute dictionary of 'moderman',
+given by the second part of the output:
+
+ ::
+
+ <HomoSapiensSapiens> can:
+ - make abstractions
+ - make art
+ - make tools
+ ------------------------------------------
+ Attributes of <HomoSapiensSapiens>:
+ _HomoHabilis__attr = - make tools
+ _HomoSapiensSapiens__attr = - make art
+ _HomoSapiens__attr = - make abstractions
+ can = <bound method HomoSapiensSapiens.can of
+ <__main__.HomoSapiensSapiens object at 0x402d892c>>
+ formatstring = %s
+
+We see that, in addition to the 'can' method inherited from 'Homo',
+the 'with' and 'With' method inherited from 'Customizable' and
+the 'formatstring' inherited from 'PrettyPrinted',
+``moderman`` has the attributes
+
+ ::
+
+ _HomoHabilis__attr:' - make tools' # inherited from HomoHabilis
+ _HomoSapiens__attr:' - make abstractions'# inherited from HomoSapiens
+ _HomoSapiensSapiens__attr: ' - make art' # inherited from HomoSapiensSapiens
+
+which origin is obvious, once one reminds the mangling mechanism associated
+with private variables. The important point is that the trick would *not*
+have worked for normal attributes. Had I used as variable name
+'attr' instead of '__attr', the name would have been overridden: the only
+attribute of 'HomoSapiensSapiens' would have been ' - make art'.
+
+This example explains the advantages of private variables during inheritance:
+they cannot be overridden. Using private name guarantees the absence of
+surprises due to inheritance. If a class B has only private variables,
+deriving a class C from B cannot cause name clashes.
+
+Private variables have a drawbacks, too. The most obvious disadvantages is
+the fact that in order to customize private variables outside their
+defining class, one needs to pass explicitly the name of the class.
+
+For instance we could not change an attribute with the syntax
+``HomoHabilis.With(__attr=' - work the stone')``, we must write the
+more verbose, error prone and redundant
+``HomoHabilis.With(_HomoHabilis__attr=' - work the stone')``
+
+A subtler drawback will be discussed in chapter 6.
+
+ .. [#] In single inheritance hierarchies, ``super`` can be dismissed
+ in favor of ``__base__``: for instance,
+ ``super(HomoSapiens,self).can()`` is equivalent to
+ ``HomoSapiens.__base__.can(self)``. Nevertheless, in view
+ of possible extensions to multiple inheritance, using ``super`` is a
+ much preferable choice.
+
+
+THE SOPHISTICATION OF DESCRIPTORS
+===========================================================================
+
+Attribute descriptors are important metaprogramming tools that allows
+the user to customize the behavior of attributes in custom classes.
+For instance, attribute descriptors (or descriptors for short)
+can be used as method wrappers,
+to modify or enhance methods (this is the case for the well
+known staticmethods and classmethods attribute descriptors); they
+can also be used as attribute wrappers, to change or restrict the access to
+attributes (this is the case for properties). Finally, descriptors
+allows the user to play with the resolution order of attributes:
+for instance, the ``super`` built-in object used in (multiple) inheritance
+hierarchies, is implemented as an attribute descriptor.
+
+In this chapter, I will show how the user can define its own attribute
+descriptors and I will give some example of useful things you can do with
+them (in particular to add tracing and timing capabilities).
+
+Motivation
+---------------------------------------------------------------------------
+Attribute descriptors are a recent idea (they where first introduced in
+Python 2.2) nevertheless, under the hood, are everywhere in Python. It is
+a tribute to Guido's ability of hiding Python complications that
+the average user can easily miss they existence.
+If you need to do simple things, you can very well live without
+the knowledge of descriptors. On the other hand, if you need difficult
+things (such as tracing all the attribute access of your modules)
+attribute descriptors, allow you to perform
+impressive things.
+Let me start by showing why the knowledge of attribute descriptors is
+essential for any user seriously interested in metaprogramming applications.
+Suppose I want to trace the methods of a clock:
+
+ >>> import oopp
+ >>> clock=oopp.Clock()
+
+This is easily done with the ``with_tracer`` closure of chapter 2:
+
+ >>> oopp.wrapfunctions(clock,oopp.with_tracer)
+ <oopp.Clock object at 0x4044c54c>
+ >>> clock.get_time()
+ [] Calling 'get_time' with arguments
+ (){} ...
+ -> '.get_time' called with result: 19:55:07
+ '19:55:07'
+
+However, this approach fails if I try to trace the entire class:
+
+ >>> oopp.wrapfunctions(oopp.Clock,oopp.with_tracer)
+ <class 'oopp.Clock'>
+ >>> oopp.Clock.get_time() # error
+ Traceback (most recent call last):
+ File "<stdin>", line 6, in ?
+ TypeError: unbound method _() must be called with Clock instance
+ as first argument (got nothing instead)
+
+The reason is that ``wrapfunctions`` sets the attributes of 'Clock'
+by invoking ``customize``, which uses ``setattr``. This converts
+'_' (i.e. the traced version of ``get_time``) in a regular method, not in
+a staticmethod!
+In order to trace staticmethods, one has to understand the nature
+of attribute descriptors.
+
+Functions versus methods
+----------------------------------------------------------------------
+
+Attribute descriptors are essential for the implementation
+of one of the most basic Python features: the automatic conversion
+of functions in methods. As I already anticipated in chapter 1, there is
+a sort of magic when one writes ``Clock.get_time=lambda self: get_time()``
+and Python automagically converts the right hand side, that is a
+function, to a left hand side that is a (unbound) method. In order to
+understand this magic, one needs a better comprehension of the
+relation between functions and methods.
+Actually, this relationship is quite subtle
+and has no analogous in mainstream programming languages.
+For instance, C is not OOP and has only functions, lacking the concept
+of method, whereas Java (as other OOP languages)
+has no functions, only methods.
+C++ has functions and methods, but functions are completely
+different from methods On the other hand, in Python,
+functions and methods can be transformed both ways.
+
+To show how it works, let me start by defining a simple printing
+function:
+
+ ::
+
+ #<oopp.py>
+
+ import __main__ # gives access to the __main__ namespace from the module
+
+ def prn(s):
+ """Given an evaluable string, print its value and its object reference.
+ Notice that the evaluation is done in the __main__ dictionary."""
+ try: obj=eval(s,__main__.__dict__)
+ except: print 'problems in evaluating',s
+ else: print s,'=',obj,'at',hex(id(obj))
+
+ #</oopp.py>
+
+Now, let me define a class with a method ``m`` equals to the identity
+function ``f``:
+
+ >>> def f(x): "Identity function"; return x
+ ...
+ >>> class C(object):
+ ... m=f
+ ... print m #here m is the function f
+ <function f at 0x401c2b1c>
+
+We see that *inside* its defining class, ``m`` coincides with the function
+``f`` (the object reference is the same):
+
+ >>> f
+ <function f at 0x401c2b1c>
+
+We may retrieve ``m`` from *outside* the class via the class dictionary [#]_:
+
+ >>> C.__dict__['m']
+ <function prn at 0x401c2b1c>
+
+However, if we invoke ``m`` with
+the syntax ``C.m``, then it (magically) becomes a (unbound) method:
+
+ >>> C.m #here m has become a method!
+ <unbound method C.f>
+
+But why it is so? How comes that in the second syntax the function
+``f`` is transformed in a (unbound) method? To answer that question, we have
+to understand how attributes are really invoked in Python, i.e. via
+attribute descriptors.
+
+Methods versus functions
+-----------------------------------------------------------------------------
+
+First of all, let me point out the differences between methods and
+functions. Here, ``C.m`` does *not* coincides with ``C.__dict__['m']``
+i.e. ``f``, since its object reference is different:
+
+ >>> from oopp import prn,attributes
+ >>> prn('C.m')
+ C.m = <unbound method C.prn> at 0x81109b4
+
+The difference is clear since methods and functions have different attributes:
+
+ >>> attributes(f).keys()
+ ['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']
+
+whereas
+
+ >>> attributes(C.m).keys()
+ ['im_func', 'im_class', 'im_self']
+
+We discussed few of the functions attributes in the chapter
+on functions. The instance method attributes are simpler: ``im_self``
+returns the object to which the method is attached,
+
+ >>> print C.m.im_self #unbound method, attached to the class
+ None
+ >>> C().m.im_self #bound method, attached to C()
+ <__main__.C object at 0x81bf4ec>
+
+``im_class`` returns the class to which the
+method is attached
+
+ >>> C.m.im_class #class of the unbound method
+ <class '__main__.C'>
+ >>> C().m.im_class #class of the bound method,
+ <class '__main__.C'>
+
+and ``im_func`` returns the function equivalent to
+the method.
+
+ >>> C.m.im_func
+ <function m at 0x8157f44>
+ >>> C().m.im_func # the same
+ <function m at 0x8157f44>
+
+As the reference manual states, calling
+``m(*args,**kw)`` is completely equivalent to calling
+``m.im_func(m.im_self, *args,**kw)``".
+
+As a general rule, an attribute descriptor is an object with a ``__get__``
+special method. The most used descriptors are the good old functions:
+they have a ``__get__`` special method returning a *method-wrapper object*
+
+ >>> f.__get__
+ <method-wrapper object at 0x815cdc4>
+
+method-wrapper objects can be transformed in (both bound and unbound) methods:
+
+ >>> f.__get__(None,C)
+ <unbound method C.f>
+ >>> f.__get__(C(),C)
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+The general calling syntax for method-wrapper objects is
+``.__get__(obj,cls=None)``, where the first argument is an
+instance object or None and the second (optional) argument is the class (or a
+generic superclass) of the first one.
+
+Now we see what happens when we use the syntax ``C.m``: Python interprets
+this as a shortcut for ``C.__dict['m'].__get__(None,C)`` (if ``m`` is
+in the 'C' dictionary, otherwise it looks for ancestor dictionaries).
+We may check that everything is correct by observing that
+``f.__get__(None,C)`` has exactly the same object reference than ``C.m``,
+therefore they are the same object:
+
+ >>> hex(id(f.__get__(None,C))) # same as hex(id(C.m))
+ '0x811095c'
+
+The process works equally well for the syntax ``getattr``:
+
+ >>> print getattr(C,'m'), hex(id(getattr(C,'m')))
+ <unbound method C.f> 0x811095c
+
+and for bound methods: if
+
+ >>> c=C()
+
+is an instance of the class C, then the syntax
+
+ >>> getattr(c,'m') #same as c.m
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+is a shortcut for
+
+ >>> type(c).__dict__['m'].__get__(c,C) # or f.__get__(c,C)
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+(notice that the object reference for ``c.m`` and ``f.__get__(c,C)`` is
+the same, they are *exactly* the same object).
+
+Both the unbound method C.m and the bound method c.m refer to the same
+object at hexadecimal address 0x811095c. This object is common to all other
+instances of C:
+
+ >>> c2=C()
+ >>> print c2.m,hex(id(c2.m)) #always the same method
+ <bound method C.m of <__main__.C object at 0x815768c>> 0x811095c
+
+One can also omit the second argument:
+
+ >>> c.m.__get__(c)
+ <bound method ?.m of <__main__.C object at 0x81597dc>>
+
+Finally, let me point out that methods are attribute descriptors too,
+since they have a ``__get__`` attribute returning a method-wrapper
+object:
+
+ >>> C.m.__get__
+ <method-wrapper object at 0x815d51c>
+
+Notice that this method wrapper is *not* the same than the ``f.__get__``
+method wrapper.
+
+ .. [#] If ``C.__dict['m']`` is not defined, Python looks if ``m`` is defined
+ in some ancestor of C. For instance if `B` is the base of `C`, it
+ looks in ``B.__dict['m']``, etc., by following the MRO.
+
+Static methods and class methods
+--------------------------------------------------------------------------
+
+Whereas functions and methods are implicit attribute descriptors,
+static methods and class methods are examples of explicit
+descriptors. They allow to convert regular functions to
+specific descriptor objects. Let me show a trivial example.
+Given the identity function
+
+ >>> def f(x): return x
+
+we may convert it to a staticmethod object
+
+ >>> sm=staticmethod(f)
+ >>> sm
+ <staticmethod object at 0x4018a0a0>
+
+or to a classmethod object
+
+ >>> cm=classmethod(f)
+ >>> cm
+ <classmethod object at 0x4018a0b0>
+
+In both cases the ``__get__`` special method returns a method-wrapper object
+
+ >>> sm.__get__
+ <method-wrapper object at 0x401751ec>
+ >>> cm.__get__
+ <method-wrapper object at 0x4017524c>
+
+However the static method wrapper is quite different from the class
+method wrapper. In the first case the wrapper returns a function:
+
+ >>> sm.__get__(C(),C)
+ <function f at 0x4027a8b4>
+ >>> sm.__get__(C())
+ <function f at 0x4027a8b4>
+
+in the second case it returns a method
+
+ >>> cm.__get__(C(),C)
+ <bound method type.f of <class '__main__.C'>>
+
+Let me discuss more in detail the static methods, first.
+
+It is always possible to extract the function from the static method
+via the syntaxes ``sm.__get__(a)`` and ``sm.__get__(a,b)`` with *ANY* valid
+a and b, i.e. the result does not depend on a and b. This is correct,
+since static methods are actually function that have nothing to do
+with the class and the instances to which they are bound.
+
+This behaviour of the method wrapper makes clear why the relation between
+methods and functions is inversed for static methods with respect to
+regular methods:
+
+ >>> class C(object):
+ ... s=staticmethod(lambda : None)
+ ... print s
+ ...
+ <staticmethod object at 0x8158ec8>
+
+Static methods are non-trivial objects *inside* the class, whereas
+they are regular functions *outside* the class:
+
+ >>> C.s
+ <function <lambda> at 0x8158e7c>
+ >>> C().s
+ <function <lambda> at 0x8158e7c>
+
+The situation is different for classmethods: inside the class they
+are non-trivial objects, just as static methods,
+
+ >>> class C(object):
+ ... cm=classmethod(lambda cls: None)
+ ... print cm
+ ...
+ <classmethod object at 0x8156100>
+
+but outside the class they are methods bound to the class,
+
+ >>> c=C()
+ >>> prn('c.cm')
+ <bound method type.<lambda> of <class '__main__.C'>>
+ 0x811095c
+
+and not to the instance 'c'. The reason is that the ``__get__`` wrapper method
+can be invoked with the syntax ``__get__(a,cls)`` which
+is only sensitive to the second argument or with the syntax
+``__get__(obj)`` which is only sensitive to the type of the first
+argument:
+
+ >>> cm.__get__('whatever',C) # the first argument is ignored
+ <bound method type.f of <class '__main__.C'>>
+
+sensitive to the type of 'whatever':
+
+ >>> cm.__get__('whatever') # in Python 2.2 would give a serious error
+ <bound method type.f of <type 'str'>>
+
+Notice that the class method is actually bound to C's class, i.e.
+to 'type'.
+
+Just as regular methods (and differently
+from static methods) classmethods have attributes ``im_class``, ``im_func``,
+and ``im_self``. In particular one can retrieve the function wrapped inside
+the classmethod with
+
+ >>> cm.__get__('whatever','whatever').im_func
+ <function f at 0x402c2534>
+
+The difference with regular methods is that ``im_class`` returns the
+class of 'C' whereas ``im_self`` returns 'C' itself.
+
+ >>> C.cm.im_self # a classmethod is attached to the class
+ <class '__main__.C'>
+ >>> C.cm.im_class #the class of C
+ <type 'type'>
+
+Remark: Python 2.2.0 has a bug in classmethods (fixed in newer versions):
+when the first argument of __get__ is None, then one must specify
+the second argument (otherwise segmentation fault :-()
+
+Properties
+----------------------------------------------------------------------
+
+Properties are a more general kind of attribute descriptors than
+staticmethods and classmethods, since their effect can be customized
+trough arbitrary get/set/del functions. Let me give an example:
+
+ >>> def getp(self): return 'property' # get function
+ ...
+ >>> p=property(getp) # property object
+ >>> p
+ <property object at 0x815855c>
+
+``p`` has a ``__get__`` special method returning a method-wrapper
+object, just as it happens for other descriptors:
+
+ >>> p.__get__
+ <method-wrapper object at 0x8158a7c>
+
+The difference is that
+
+ >>> p.__get__(None,type(p))
+ <property object at 0x4017016c>
+ >>> p.__get__('whatever')
+ 'property'
+ >>> p.__get__('whatever','whatever')
+ 'property'
+
+As for static methods, the ``__get__`` method wrapper is independent from
+its arguments, unless the first one is None: in such a case it returns
+the property object, in all other circumstances it returns the result
+of ``getp``. This explains the behavior
+
+ >>> class C(object): p=p
+ >>> C.p
+ <property object at 0x815855c>
+ >>> C().p
+ 'property'
+
+Properties are a dangerous feature, since they change the semantics
+of the language. This means that apparently trivial operations can have
+any kind of side effects:
+
+ >>> def get(self):return 'You gave me the order to destroy your hard disk!!'
+ >>> class C(object): x=property(get)
+ >>> C().x
+ 'You gave me the order to destroy your hard disk!!'
+
+Invoking 'C.x' could very well invoke an external program who is going
+to do anything! It is up to the programmer to not abuse properties.
+The same is true for user defined attribute descriptors.
+
+There are situations in which they are quite handy, however. For
+instance, properties can be used to trace the access data attributes.
+This can be especially useful during debugging, or for logging
+purposes.
+
+Notice that this approach has the problem that now data attributes cannot
+no more be called trough their class, but only though their instances.
+Moreover properties do not work well with ``super`` in cooperative
+methods.
+
+User-defined attribute descriptors
+----------------------------------------------------------------------
+
+As we have seen, there are plenty of predefined attribute descriptors,
+such as staticmethods, classmethods and properties (the built-in
+``super`` is also an attribute descriptor which, for sake of
+convenience, will be discussed in the next section).
+In addition to them, the user can also define customized attribute
+descriptors, simply trough classes with a ``__get__`` special method.
+Let me give an example:
+
+ ::
+
+ #<simpledescr.py>
+
+ class ChattyAttr(object):
+ """Chatty descriptor class; descriptor objects are intended to be
+ used as attributes in other classes"""
+ def __get__(self, obj, cls=None):
+ binding=obj is not None
+ if binding:
+ return 'You are binding %s to %s' % (self,obj)
+ else:
+ return 'Calling %s from %s' % (self,cls)
+
+ class C(object):
+ d=ChattyAttr()
+
+ c=C()
+
+ print c.d # <=> type(c).__dict__['d'].__get__(c,type(c))
+ print C.d # <=> C.__dict__['d'].__get__(None,C)
+
+ #</simpledescr.py>
+
+with output:
+
+ ::
+
+ You are binding <ChattyAttr object at 0x401bc1cc> to
+ <C object at 0x401bc2ec>
+ Calling <ChattyAttr object at 0x401bc1cc> from <class 'C'>
+
+
+Invoking a method with the syntax ``C.d`` or ``c.d`` involves calling
+``__get__``. The ``__get__`` signature is fixed: it is
+`` __get__=__get__(self,obj,cls=None)``, since the notation
+``self.descr_attr`` automatically passes ``self`` and ``self.__class__`` to
+``__get__``.
+
+Custom descriptors can be used to restrict the access to objects in a
+more general way than trough properties. For instance, suppose one
+wants to raise an error if a given attribute 'a' is accessed, both
+from the class and from the instance: a property cannot help here,
+since it works only from the instance. The solution is the following
+custom descriptor:
+
+ ::
+
+ #<oopp.py>
+
+ class AccessError(object):
+ """Descriptor raising an AttributeError when the attribute is
+ accessed""" #could be done with a property
+ def __init__(self,errormessage):
+ self.msg=errormessage
+ def __get__(self,obj,cls=None):
+ raise AttributeError(self.msg)
+
+ #</oopp.py>
+
+ >>> from oopp import AccessError
+ >>> class C(object):
+ ... a=AccessError("'a' cannot be accessed")
+ >>> c=C()
+ >>> c.a #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 313, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: 'a' cannot be accessed
+ >>> C.a #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 313, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: 'a' cannot be accessed
+
+It is always possibile to convert plain attributes (i.e. attributes
+without a "__get__" method) to descriptor objects:
+
+ ::
+
+ #<oopp.py>
+
+ class convert2descriptor(object):
+ """To all practical means, this class acts as a function that, given an
+ object, adds to it a __get__ method if it is not already there. The
+ added __get__ method is trivial and simply returns the original object,
+ independently from obj and cls."""
+ def __new__(cls,a):
+ if hasattr(a,"__get__"): # do nothing
+ return a # a is already a descriptor
+ else: # creates a trivial attribute descriptor
+ cls.a=a
+ return object.__new__(cls)
+ def __get__(self,obj,cls=None):
+ "Returns self.a independently from obj and cls"
+ return self.a
+
+ #</oopp.py>
+
+This example also shows the magic of ``__new__``, that allows to use a
+class as a function. The output of 'convert2descriptor(a)' can be both
+an instance of 'convert2descriptor' (in this case 'convert2descriptor' acts as
+a normal class, i.e. as an object factory) or 'a' itself
+(if 'a' is already a descriptor): in this case 'convert2descriptor' acts
+as a function.
+
+For instance, a string is converted to a descriptor
+
+ >>> from oopp import convert2descriptor
+ >>> a2=convert2descriptor('a')
+ >>> a2
+ <oopp.convert2descriptor object at 0x4017506c>
+ >>> a2.__get__('whatever')
+ 'a'
+
+whereas a function is untouched:
+
+ >>> def f(): pass
+ >>> f2=convert2descriptor(f) # does nothing
+ >>> f2
+ <function f at 0x4019110c>
+
+Data descriptors
+-------------------------------------------------------------------------
+
+It is also possible to specify a ``__set__`` method (descriptors
+with a ``__set__`` method are typically data descriptors) with
+the signature ``__set__(self,obj,value)`` as in the following
+example:
+
+ ::
+
+ #<datadescr.py>
+
+ class DataDescriptor(object):
+ value=None
+ def __get__(self, obj, cls=None):
+ if obj is None: obj=cls
+ print "Getting",obj,"value =",self.value
+ return self.value
+ def __set__(self, obj, value):
+ self.value=value
+ print "Setting",obj,"value =",value
+
+ class C(object):
+ d=DataDescriptor()
+
+ c=C()
+
+ c.d=1 #calls C.__dict__['d'].__set__(c,1)
+ c.d #calls C.__dict__['d'].__get__(c,C)
+ C.d #calls C.__dict__['d'].__get__(None,C)
+ C.d=0 #does *not* call __set__
+ print "C.d =",C.d
+
+ #</datadescr.py>
+
+With output:
+
+ ::
+
+ Setting <C object at 0x401bc1ec> value = 1
+ Getting <C object at 0x401bc42c> value = 1
+ Getting <class 'C'> value = 1
+ C.d = 0
+
+With this knowledge, we may now reconsider the clock example given
+in chapter 3. #NO!??
+
+ >>> import oopp
+ >>> class Clock(object): pass
+ >>> myclock=Clock()
+ ...
+ >>> myclock.get_time=oopp.get_time # this is a function
+ >>> Clock.get_time=lambda self : oopp.get_time() # this is a method
+
+In this example, ``myclock.get_time``, which is attached to the ``myclock``
+object, is a function, whereas ``Clock.get_time``, which is attached to
+the ``Clock`` class is a method. We may also check this by using the ``type``
+function:
+
+ >>> type(myclock.get_time)
+ <type 'function'>
+
+whereas
+
+ >>> type(Clock.get_time)
+ <type 'instance method'>
+
+
+It must be remarked that user-defined attribute descriptors, just as
+properties, allow to arbitrarily change the semantics of the language
+and should be used with care.
+
+The ``super`` attribute descriptor
+------------------------------------------------------------------------
+
+
+super has also a second form, where it is more used as a descriptor.
+
+``super`` objects are attribute descriptors, too, with a ``__get__``
+method returning a method-wrapper object:
+
+ >>> super(C,C()).__get__
+ <method-wrapper object at 0x8161074>
+
+Here I give some example of acceptable call:
+
+ >>> super(C,C()).__get__('whatever')
+ <super: <class 'C'>, <C object>>
+ >>> super(C,C()).__get__('whatever','whatever')
+ <super: <class 'C'>, <C object>>
+
+
+Unfortunately, for the time being
+(i.e. for Python 2.3), the ``super`` mechanism has various limitations.
+To show the issues, let me start by considering the following base class:
+
+ ::
+
+ #<oopp.py>
+
+ class ExampleBaseClass(PrettyPrinted):
+ """Contains a regular method 'm', a staticmethod 's', a classmethod
+ 'c', a property 'p' and a data attribute 'd'."""
+ m=lambda self: 'regular method of %s' % self
+ s=staticmethod(lambda : 'staticmethod')
+ c=classmethod(lambda cls: 'classmethod of %s' % cls)
+ p=property(lambda self: 'property of %s' % self)
+ a=AccessError('Expected error')
+ d='data'
+
+ #</oopp.py>
+
+Now, let me derive a new class C from ExampleBaseClass:
+
+ >>> from oopp import ExampleBaseClass
+ >>> class C(ExampleBaseClass): pass
+ >>> c=C()
+
+Ideally, we would like to retrieve the methods and attributes of
+ExampleBaseClass from C, by using the ``super`` mechanism.
+
+1. We see that ``super`` works without problems for regular methods,
+ staticmethods and classmethods:
+
+ >>> super(C,c).m()
+ 'regular method of <C>'
+ >>> super(C,c).s()
+ 'staticmethod'
+ >>> super(C,c).c()
+ "classmethod of <class '__main__.C'>"
+
+It also works for user defined attribute descriptors:
+
+ >>> super(C,c).a # access error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 340, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: Expected error
+
+and for properties (only for Python 2.3+):
+
+ >>> ExampleBaseClass.p
+ <property object at 0x81b30fc>
+
+In Python 2.2 one would get an error, instead
+
+ >>> super(C,c).p #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute 'p'
+
+3. Moreover, certain attributes of the superclass, such as its
+``__name__``, cannot be retrieved:
+
+ >>> ExampleBaseClass.__name__
+ 'ExampleBaseClass'
+ >>> super(C,c).__name__ #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute '__name__'
+
+4. There is no direct way to retrieve the methods of the super-superclass
+ (i.e. the grandmother class, if you wish) or in general the furthest
+ ancestors, since ``super`` does not chain.
+
+5. Finally, there are some subtle issues with the ``super(cls)`` syntax:
+
+
+ >>> super(C).m #(2) error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute 'm'
+
+means ``super(C).__get__(None,C)``, but only
+``super(C).__get__(c,C).m==super(C,c)`` works.
+
+ On the other hand,
+
+ >>> super(C).__init__ #(1)
+ <built-in method __init__ of type object at 0x80e6fc0>
+ >>> super(C).__new__ #(1)
+ <built-in method __init__ of type object at 0x80e6fc0>
+
+
+ seems to work, whereas in reality does not. The reason is that since
+ ``super`` objects are instances
+ of ``object``, they inherit object's methods, and in particular
+ ``__init__`` ; therefore the ``__init__`` method in (1) is *not*
+ the ``ExampleBaseClass.__init__`` method. The point is that ``super``
+ objects are attribute descriptors and not references to the superclass.
+
+Probably, in future versions of Python the ``super`` mechanism will be
+improved. However, for the time being, one must provide a workaround for
+dealing with these issues. This will be discussed in the next chapter.
+
+Method wrappers
+----------------------------------------------------------------------
+
+One of the most typical applications of attribute descriptors is their
+usage as *method wrappers*.
+
+Suppose, for instance, one wants to add tracing capabilities to
+the methods of a class for debugging purposes. The problem
+can be solved with a custom descriptor class:
+
+ ::
+
+ #<oopp.py>
+
+ import inspect
+
+ class wrappedmethod(Customizable):
+ """Customizable method factory intended for derivation.
+ The wrapper method is overridden in the children."""
+
+ logfile=sys.stdout # default
+ namespace='' # default
+
+ def __new__(cls,meth): # meth is a descriptor
+ if isinstance(meth,FunctionType):
+ kind=0 # regular method
+ func=meth
+ elif isinstance(meth,staticmethod):
+ kind=1 # static method
+ func=meth.__get__('whatever')
+ elif isinstance(meth,classmethod):
+ kind=2 # class method
+ func=meth.__get__('whatever','whatever').im_func
+ elif isinstance(meth,wrappedmethod): # already wrapped
+ return meth # do nothing
+ elif inspect.ismethoddescriptor(meth):
+ kind=0; func=meth # for many builtin methods
+ else:
+ return meth # do nothing
+ self=super(wrappedmethod,cls).__new__(cls)
+ self.kind=kind; self.func=func # pre-initialize
+ return self
+
+ def __init__(self,meth): # meth not used
+ self.logfile=self.logfile # default values
+ self.namespace=self.namespace # copy the current
+
+ def __get__(self,obj,cls): # closure
+ def _(*args,**kw):
+ if obj is None: o=() # unbound method call
+ else: o=(obj,) # bound method call
+ allargs=[o,(),(cls,)][self.kind]+args
+ return self.wrapper()(*allargs,**kw)
+ return _ # the wrapped function
+ # allargs is the only nontrivial line in _; it adds
+ # 0 - obj if meth is a regular method
+ # 1 - nothing if meth is a static method
+ # 2 - cls if meth is a class method
+
+ def wrapper(self): return self.func # do nothing, to be overridden
+
+ #</oopp.py>
+
+This class is intended for derivation: the wrapper method has to be overridden
+in the children in order to introduce the wanted feature. If I want to
+implement the capability of tracing methods, I can reuse the ``with_tracer``
+closure introduced in chapter 2:
+
+ ::
+
+ #<oopp.py>
+
+ class tracedmethod(wrappedmethod):
+ def wrapper(self):
+ return with_tracer(self.func,self.namespace,self.logfile)
+
+ #</oopp.py>
+
+Nothing prevents me from introducing timing features by reusing the
+``with_timer`` closure:
+
+ ::
+
+ #<oopp.py>
+
+ class timedmethod(wrappedmethod):
+ iterations=1 # additional default parameter
+
+ def __init__(self,meth):
+ super(timedmethod,self).__init__(self,meth)
+ self.iterations=self.iterations # copy
+
+ def wrapper(self):
+ return with_timer(self.func,self.namespace,
+ self.iterations,self.logfile)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+The dictionary of wrapped functions is then built from an utility function
+
+ ::
+
+ #<oopp.py>
+
+ def wrap(obj,wrapped,condition=lambda k,v: True, err=None):
+ "Retrieves obj's dictionary and wraps it"
+ if isinstance(obj,dict): # obj is a dictionary
+ dic=obj
+ else:
+ dic=getattr(obj,'__dict__',{}).copy() # avoids dictproxy objects
+ if not dic: dic=attributes(obj) # for simple objects
+ wrapped.namespace=getattr(obj,'__name__','')
+ for name,attr in dic.iteritems(): # modify dic
+ if condition(name,attr): dic[name]=wrapped(attr)
+ if not isinstance(obj,dict): # modify obj
+ customize(obj,err,**dic)
+
+ #</oopp.py>
+
+ ::
+
+ #<tracingmethods.py>
+
+ from oopp import *
+
+ class C(object):
+ "Class with traced methods"
+
+ def f(self): return self
+ f=tracedmethod(f)
+
+ g=staticmethod(lambda:None)
+ g=tracedmethod(g)
+
+ h=classmethod(do_nothing)
+ h=tracedmethod(h)
+
+ c=C()
+
+ #unbound calls
+ C.f(c)
+ C.g()
+ C.h()
+
+ #bound calls
+ c.f()
+ c.g()
+ c.h()
+
+ #</tracingmethods.py>
+
+Output:
+
+ ::
+
+ [C] Calling 'f' with arguments
+ (<C object at 0x402042cc>,){} ...
+ -> 'C.f' called with result: <C object at 0x402042cc>
+
+ [C] Calling '<lambda>' with arguments
+ (){} ...
+ -> 'C.<lambda>' called with result: None
+
+ [C] Calling 'do_nothing' with arguments
+ (<class 'C'>,){} ...
+ -> 'C.do_nothing' called with result: None
+
+ [C] Calling 'f' with arguments
+ (<C object at 0x402042cc>,){} ...
+ -> 'C.f' called with result: <C object at 0x402042cc>
+
+ [C] Calling '<lambda>' with arguments
+ (){} ...
+ -> 'C.<lambda>' called with result: None
+
+ [C] Calling 'do_nothing' with arguments
+ (<class 'C'>,){} ...
+ -> 'C.do_nothing' called with result: None
+
+
+The approach in 'tracingmethods.py' works, but it is far from
+being elegant, since I had to explicitly wrap each method in the
+class by hand.
+
+Both problems can be avoided.
+
+ >>> from oopp import *
+ >>> wrap(Clock,tracedmethod)
+ >>> Clock.get_time()
+ [Clock] Calling 'get_time' with arguments
+ (){} ...
+ -> 'Clock.get_time' called with result: 21:56:52
+ '21:56:52'
+
+
+THE SUBTLETIES OF MULTIPLE INHERITANCE
+==========================================================================
+
+In chapter 4 we introduced the concept of multiple inheritance and discussed
+its simplest applications in absence of name collisions. When with methods
+with different names are derived from different classes multiple inheritance
+is pretty trivial. However, all kind of subtilites comes in presence of name
+clashing, i.e. when we multiply inherits different methods defined in different
+classes but with the *same* name.
+In order to understand what happens in this situation, it is essential to
+understand the concept of Method Resolution Order (MRO). For reader's
+convenience, I collect in this chapter some of the information
+reported in http://www.python.org/2.3/mro.html.
+
+A little bit of history: why Python 2.3 has changed the MRO
+------------------------------------------------------------------------------
+
+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, 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).
+
+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. In order to do that, we need the
+concept of *merging* lists, since the rule says that
+
+ *the linearization of C is the sum of C plus the merge of a) the
+ linearizations of the parents and b) the list of the parents.*
+
+In symbolic notation:
+
+ L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
+
+How is the merge computed? The rule is the following:
+
+ *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:
+
+1. C is the ``object`` class, which has no parents; in this case its
+ linearization coincides with itself,
+
+ L[object] = object.
+
+2. 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 ;-)
+
+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.
+
+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 #under Python 2.3 this is an error
+ '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 ;-)
+
+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.
+
+.. [#] 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
+
+.. [#] The (in)famous book on metaclasses, *Putting Metaclasses to Work*:
+ Ira R. Forman, Scott Danforth, Addison-Wesley 1999 (out of print,
+ but probably still available on http://www.amazon.com)
+
+
+Understanding the Method Resolution Order
+--------------------------------------------------------------------------
+
+The MRO of any given (new style) Python class is given
+by the special attribute ``__mro__``. Notice that since
+Python is an extremely dynamic language it is possible
+to delete and to generate whole classes at run time, therefore the MRO
+is a dynamic concept. For instance, let me show how it is possibile to
+remove a class from my
+paleoanthropological hierarchy: for instance I can
+replace the last class 'HomoSapiensSapiens' with 'HomoSapiensNeardenthalensis'
+(changing a class in the middle of the hierarchy would be more difficult). The
+following lines do the job dynamically:
+
+ >>> from oopp import *
+ >>> del HomoSapiensSapiens
+ >>> class HomoSapiensNeardenthalensis(HomoSapiens):
+ ... def can(self):
+ ... super(self.__this,self).can()
+ ... print " - make something"
+ >>> reflective(HomoSapiensNeardenthalensis)
+ >>> HomoSapiensNeardenthalensis().can()
+ HomoSapiensNeardenthalensis can:
+ - make tools
+ - make abstractions
+ - make something
+
+In this case the MRO of 'HomoSapiensNeardenthalensis', i.e. the list of
+all its ancestors, is
+
+ >>> HomoSapiensNeardenthalensis.__mro__
+ [<class '__main__.HomoSapiensNeardenthalensis'>,<class 'oopp.HomoSapiens'>,
+ <class 'oopp.HomoHabilis'>, <class 'oopp.Homo'>,
+ <class 'oopp.PrettyPrinted'>, <class 'oopp.object'>]
+
+The ``__mro__`` attribute gives the *linearization* of the class, i.e. the
+ordered list of its ancestors, starting from the class itself and ending
+with object. The linearization of a class is essential in order to specify
+the resolution order of methods and attributes, i.e. the Method Resolution
+Order (MRO). In the case of single inheritance hierarchies, such the
+paleonthropological example, the MRO is pretty obvious; on the contrary
+it is a quite non-trivial concept in the case of multiple inheritance
+hierarchies.
+
+For instance, let me reconsider my first example of multiple inheritance,
+the ``NonInstantiableClock`` class, inheriting from 'NonInstantiable' and
+'Clock'. I may represent the hierarchy with the following inheritance graph:
+
+ ::
+
+
+ -- object --
+ / (__new__) \
+ / \
+ / \
+ Clock NonInstantiable
+ (get_time) (__new__)
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ NonInstantiableClock
+ (get_time,__new__)
+
+
+The class ``Clock`` define a ``get_time`` method, whereas the class
+``NonInstantiable`` overrides the ``__new__`` method of the ``object`` class;
+the class ``NonInstantiableClock`` inherits ``get_time`` from 'Clock' and
+``__new__`` from 'NonInstantiable'.
+
+The linearization of 'NonInstantiableClock' is
+
+ >>> NonInstantiableClock.mro()
+ [<class '__main__.NonInstantiableClock'>, <class 'oopp.Clock'>,
+ <class 'oopp.NonInstantiable'>, <type 'object'>]
+
+
+In particular, since 'NonInstantiable' precedes 'object', its ``__new__``
+method overrides the ``object`` new method. However, with the MRO used before
+Python 2.2, the linearization would have been ``NonInstantiableClock, Clock,
+object, NonInstantiable, object`` and the ``__new__`` method of object would
+have (hypothetically, of course, since before Python 2.2 there was not
+``__new__`` method! ;-) overridden the ``__new__``
+method of ``NonInstantiable``, therefore ``NonInstantiableClock`` would
+have lost the property of being non-instantiable!
+
+This simple example shows that the choice of a correct Method Resolution
+Order is far from being obvious in general multiple inheritance hierarchies.
+After a false start in Python 2.2, (with a MRO failing in some subtle cases)
+Python 2.3 decided to adopt the so-called C3 MRO, invented by people working
+on Dylan (even if Dylan itself uses the MRO of Common Lisp CLOS). Since this
+is quite a technical matter, I defer the interested reader to appendix 2
+for a full discussion of the C3 algorithm.
+
+Here, I prefer to point out how the built-in
+``super`` object works in multiple inheritance situations. To this aim, it
+is convenient to define an utility function that retrieves the ancestors
+of a given class with respect to the MRO of one of its subclasses:
+
+ ::
+
+ #<oopp.py>
+
+ def ancestor(C,S=None):
+ """Returns the ancestors of the first argument with respect to the
+ MRO of the second argument. If the second argument is None, then
+ returns the MRO of the first argument."""
+ if C is object:
+ raise TypeError("There is no superclass of object")
+ elif S is None or S is C:
+ return list(C.__mro__)
+ elif issubclass(S,C): # typical case
+ mro=list(S.__mro__)
+ return mro[mro.index(C):] # compute the ancestors from the MRO of S
+ else:
+ raise TypeError("S must be a subclass of C")
+
+ #</oopp.py>
+
+Let me show how the function ``ancestor`` works.
+Consider the class ``Clock`` in isolation: then
+its direct superclass, i.e. the first ancestor, is ``object``,
+
+ >>> from oopp import *
+ >>> ancestor(Clock)[1]
+ <type 'object'>
+
+therefore ``super(Clock).__new__`` retrieves the ``object.__new__`` method:
+
+ >>> super(Clock).__new__
+ <built-in method __new__ of type object at 0x80e6fc0>
+
+Consider now the ``Clock`` class together with its subclass
+``NonInstantiableClock``:
+in this case the first ancestor of ``Clock``, *with respect to the MRO of
+'NonInstantiableClock'* is ``NonInstantiable``
+
+ >>> ancestor(Clock,NonInstantiableClock)[1]
+ <class 'oopp.NonInstantiable'>
+
+Therefore ``super(Clock,NonInstantiableClock).__new__`` retrieves the
+``NonInstantiable.__new__`` method:
+
+ >>> super(Clock,NonInstantiableClock).__new__
+ <function __new__ at 0x81b293c>
+ >>> NonInstantiable.__new__
+ <function __new__ at 0x81b293c>
+
+It must be pointed out that ``super(C,S)`` is equivalent but not the same
+than ``ancestor(C,S)[1]``, since it does not return the superclass:
+it returns a super object, instead:
+
+ >>> super(Clock,NonInstantiableClock)
+ <super: <class 'Clock'>, <type object>>
+
+ #<oopp.py>
+
+ #class Super(super):
+ # def __init__(self,C,S=None):
+ # super(Super,self).__init__(C,S)
+ # self.__name__="Super(%s)" % C.__name__
+
+ #</oopp.py>
+
+Finally, there is little quirk of super:
+
+ >>> class C(PrettyPrinted): pass
+ >>> s=super(C,C())
+ >>> s.__str__()
+
+but
+
+ >>> str(s) # idem for print s
+ "<super: <class 'C'>, <C object>>"
+
+Idem for non-pre-existing methods:
+
+ >>> class D(list): pass
+ ...
+ >>> s=super(D,D())
+ >>> s.__len__()
+ 0
+ >>> len(s) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: len() of unsized object
+
+The same problem comes with ``__getattr__``:
+
+ >>> class E(object):
+ ... def __getattr__(self,name):
+ ... if name=='__len__': return lambda:0
+ ...
+ >>> e=E()
+ >>> e.__len__()
+ 0
+ >>> len(e) # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: len() of unsized object
+
+Counting instances
+----------------------------------------------------------------------
+
+ .. line-block::
+
+ *Everything should be built top-down, except the first time.*
+ -- Alan Perlis
+
+Multiple inheritance adds a step further to the bottom-up philosophy and
+it makes appealing the idea of creating classes with the only
+purpose of being derived. Whereas in the top-down approach one starts
+with full featured standalone classes, to be further refined, in the
+mix-in approach one starts with bare bone classes, providing very simple
+or even trivial features, with the purpose of providing
+basic reusable components in multiple inheritance hierarchies.
+At the very end, the idea is to generate a library of *mixin* classes, to be
+composed with other classes. We already saw a couple of examples of
+mixin classes: 'NonInstantiable' and 'Customizable'. In this paragraph
+I will show three other examples: 'WithCounter','Singleton' and
+'AvoidDuplication'.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+``__init__`` method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting feature in a separate mix-in class: then the feature can be
+added to the original class via multiple inheritance, without touching
+the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+``__new__`` method of the counter class must me cooperative, and preferably
+via an anonymous super call.
+
+ ::
+
+ #<oopp.py>
+
+ class WithCounter(object):
+ """Mixin class counting the total number of its instances and storing
+ it in the class attribute counter."""
+
+ counter=0 # class attribute (or static attribute in C++/Java terms)
+
+ def __new__(cls,*args,**kw):
+ cls.counter+=1 # increments the class attribute
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ #anonymous cooperative call to the superclass's method __new__
+
+ reflective(WithCounter)
+
+ #</oopp.py>
+
+Each time an instance of 'WithCounter' is initialized, the counter 'count' is
+incremented and when 'WithCounter' is composed trough multiple inheritance,
+its '__new__' method cooperatively invokes the ``__new__`` method
+of the other components.
+
+For instance, I can use 'WithCounter' to implement a 'Singleton', i.e.
+a class that can have only one instance. This kind of classes can be
+obtained as follows:
+
+ ::
+
+ #<oopp.py>
+
+ class Singleton(WithCounter):
+ "If you inherit from me, you can only have one instance"
+ def __new__(cls,*args,**kw):
+ if cls.counter==0: #first call
+ cls.instance=super(cls.__this,cls).__new__(cls,*args,**kw)
+ return cls.instance
+
+ reflective(Singleton)
+
+ #</oopp.py>
+
+As an application, I can create a
+class ``SingleClock`` that inherits from ``Clock``
+*and* from ``Singleton``. This means that ``SingleClock`` is both a
+'Clock' and a 'Singleton', i.e. there can be only a clock:
+
+ >>> from oopp import Clock,Singleton
+ >>> class SingleClock(Clock,Singleton): pass
+ ...
+ >>> clock1=SingleClock()
+ >>> clock2=SingleClock()
+ >>> clock1 is clock2
+ True
+
+Instantiating many clocks is apparently possible (i.e. no error
+message is given) but you always obtain the same instance. This makes
+sense, since there is only one time on the system and a single
+clock is enough.
+
+A variation of the 'Singleton' is a class that generates a new
+instance only when a certain condition is satisfied. Suppose for instance
+one has a 'Disk' class, to be instantiated with the syntax
+``Disk(xpos,ypos,radius)``.
+It is clear that two disks with the same radius and the same position in
+the cartesian plane, are essentially the same disk (assuming there are no
+additional attributes such as the color). Therefore it is a vaste of memory
+to instantiate two separate objects to describe the same disk. To solve
+this problem, one possibility is to store in a list the calling arguments.
+When it is time to instanciate a new objects with arguments args = xpos,ypos,
+radius, Python should check if a disk with these arguments has already
+been instanciated: in this case that disk should be returned, not a new
+one. This logic can be elegantly implemented in a mix-in class such as the
+following (compare with the ``withmemory`` wrapper in chapter 2):
+
+ ::
+
+ #<oopp.py>
+
+ class AvoidDuplication(object):
+ def __new__(cls,*args,**kw):
+ return super(cls.__this,cls).__new__(cls,*args,**kw)
+ __new__=withmemory(__new__) # collects the calls in __new__.result
+
+ reflective(AvoidDuplication)
+
+ #</oopp.py>
+
+Notice that 'AvoidDuplication' is introduced with the only purpose of
+giving its functionality to 'Disk': in order to reach this goal, it is enough
+to derive 'Disk' from this class and our previously
+introduced 'GeometricFigure' class by writing something like
+
+ >>> from oopp import *
+ >>> class Disk(GeometricFigure,AvoidDuplication):
+ ... def __init__(self,xpos,ypos,radius):
+ ... return super(Disk,self).__init__('(x-x0)**2+(y-y0)**2 <= r**2',
+ ... x0=xpos,y0=ypos,r=radius)
+
+Now, if we create a disk
+
+ >>> c1=Disk(0,0,10) #creates a disk of radius 10
+
+it is easy enough to check that trying to instantiate a new disk with the
+*same* arguments return the old disk:
+
+ >>> c2=Disk(0,0,10) #returns the *same* old disk
+ >>> c1 is c2
+ True
+
+Here, everything works, because through the
+cooperative ``super`` mechanism, ``Disk.__init__`` calls
+``AvoidDuplication.__init__`` that calls ``GeometricFigure.__init__``
+that in turns initialize the disk. Inverting the order of
+'AvoidDuplication' and 'GeometricFigure' would case a disaster, since
+``GeometricFigure.__init__`` would override ``AvoidDuplication.__init__``.
+
+Alternatively, one could use the object factory 'Makeobj' implemented in
+chapter 3:
+
+ >>> class NonDuplicatedFigure(GeometricFigure,AvoidDuplication): pass
+ >>> makedisk=Makeobj(NonDuplicatedFigure,'(x-x0)**2/4+(y-y0)**2 <= r**2')
+ >>> disk1=makedisk(x0=38,y0=7,r=5)
+ >>> disk2=makedisk(x0=38,y0=7,r=5)
+ >>> disk1 is disk2
+ True
+
+Remark: it is interesting to notice that the previous approach would not work
+for keyword arguments, directly, since dictionary are unhashable.
+
+The pizza-shop example
+----------------------------------------------------------------
+
+Now it is time to give a non-trivial example of multiple inheritance with
+cooperative and non-cooperative classes. The point is that multiple
+inheritance can easily leads to complicated hierarchies: where the
+resolution order of methods is far from being obvious and actually
+can give bad surprises.
+
+To explain the issue, let me extend the program for the pizza-shop owner of
+chapter 4, by following the bottom-up approach and using anonymous
+cooperative super calls.
+In this approach, one starts from the simplest thing.
+It is clear that the pizza-shop owner has interest in recording all the
+pizzas he sell.
+To this aim, he needs a class providing logging capabilities:
+each time a new instance is created, its features are stored in a log file. In
+order to count the total number of instances, 'WithLogger' must derive from
+the 'WithCounter' class. In order to have a nicely printed message,
+'WithLogger' must derive from 'PrettyPrinted'. Finally,
+since 'WithLogger' must be a general purpose
+class that I will reuse in other problem as a mixin class, it must be
+cooperative. 'WithLogger' can be implemented as follows:
+
+ ::
+
+ #<oopp.py>
+
+ class WithLogger(WithCounter,PrettyPrinted):
+ """WithLogger inherits from WithCounter the 'count' class attribute;
+ moreover it inherits '__str__' from PrettyPrinted"""
+ logfile=sys.stdout #default
+ verboselog=False #default
+ def __init__(self,*args,**kw):
+ super(self.__this,self).__init__(*args,**kw) # cooperative
+ dic=attributes(self) # non-special attributes dictionary
+ print >> self.logfile,'*'*77
+ print >> self.logfile, time.asctime()
+ print >> self.logfile, "%s. Created %s" % (type(self).counter,self)
+ if self.verboselog:
+ print >> self.logfile,"with accessibile non-special attributes:"
+ if not dic: print >> self.logfile,"<NOTHING>",
+ else: print >> self.logfile, pretty(dic)
+
+ reflective(WithLogger)
+
+ #</oopp.py>
+
+Here I could well use ``super(self.__this,self).__init__(*args,**kw)``
+instead of ``super(self.__this,self).__init__(*args,**kw)``, nevertheless
+the standard ``super`` works in this case and I can use it with better
+performances.
+Thanks to the power of multiple inheritance, we may give logging features
+to the 'CustomizablePizza' class defined in chapter 4
+with just one line of code:
+
+ >>> from oopp import *
+ >>> class Pizza(WithLogger,CustomizablePizza):
+ ... "Notice, WithLogger is before CustomizablePizza"
+ >>> Pizza.With(toppinglist=['tomato'])('small')
+ ****************************************************************************
+ Sat Feb 22 14:54:44 2003
+ 1. Created <Pizza>
+ <__main__.Pizza object at 0x816927c>
+
+It is also possible to have a more verbose output:
+
+ >>> Pizza.With(verboselog=True)
+ <class '__main__.Pizza'>
+ >>> Pizza('large')
+ ****************************************************************************
+ Sat Feb 22 14:59:51 2003
+ 1. Created <Pizza>
+ with accessibile non-special attributes:
+ With = <bound method type.customized of <class '__main__.Pizza'>>
+ baseprice = 1
+ count = 2
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <bound method Pizza.price of <__main__.Pizza object at 0x402f6c8c>>
+ size = large
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato']
+ toppings_price = <bound method Pizza.toppings_price of
+ <__main__.Pizza object at 0x402f6c8c>>
+ verboselog = True
+ with = <bound method Pizza.customized of
+ <__main__.Pizza object at 0x402f6c8c>>
+ <__main__.Pizza object at 0x401ce7ac>
+
+However, there is a problem here, since the output is '<Pizza>' and
+not the nice 'large pizza with tomato, cost $ 4.5' that we would
+expect from a child of 'CustomizablePizza'. The solution to the
+puzzle is given by the MRO:
+
+ >>> Pizza.mro()
+ [<class '__main__.Pizza'>, <class 'oopp.WithLogger'>,
+ <class 'oopp.WithCounter'>, <class 'oopp.PrettyPrinted'>,
+ <class 'oopp.CustomizablePizza'>, <class 'oopp.GenericPizza'>,
+ <class 'oopp.Customizable'>, <type 'object'>]
+
+The inheritance graph is rather complicated:
+
+ ::
+
+ object 7
+
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ / / \ \
+ 2 WithCounter PrettyPrinted 3 GenericPizza 5 Customizable 6
+ (__new__) (__str__,__init__) (__str__) /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / / /
+ \ / CustomizablePizza 4
+ \ / /
+ 1 WithLogger /
+ (__init__) /
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+
+ Pizza O
+
+
+As we see, the precedence in the resolution of methods is far from being
+trivial. It is denoted in the graph with numbers
+from 0 to 7: first the methods of 'Pizza' (level 0), then the methods of
+'WithLogger' (level 1), then the methods of 'WithCounter' (level 2), then
+the methods of 'PrettyPrinted' (level 3), then the methods of
+'CustomizablePizza' (level 4), then the methods of 'GenericPizza' (level 5),
+then the level of 'Customizable' (level 6), finally the 'object' methods
+(level 7).
+
+The reason why the MRO is so, can be understood by studying
+appendix 1.
+
+We see that the ``__init__`` methods of 'WithLogger' and
+the ``__new__`` method of 'WithCounter' are cooperative.
+``WithLogger.__init__``
+calls ``WithCounter.__init__`` that is
+inherited from ``CustomizablePizza.__init__`` which is not cooperative,
+but this is not dangerous since ``CustomizablePizza.__init__`` does not need
+to call any other ``__init__``.
+
+However, ``PrettyPrinted.__str__`` and ``GenericPizza.__str__`` are not
+cooperative and since 'PrettyPrinted' precedes 'GenericPizza', the
+``GenericPizza.__str__`` method is overridden, which is bad.
+
+If ``WithLogger.__init__`` and ``WithCounter.__new__`` were not
+cooperative, they would therefore badly breaking the program.
+
+The message is: when you inherit from both cooperative and non-cooperative
+classes, put cooperative classes first. The will be fair and will not
+blindly override methods of the non-cooperative classes.
+
+
+With multiple inheritance you can reuse old code a lot,
+however the price to pay, is to have a non-trivial hierarchy. If from
+the beginning we knew that 'Pizza' was needing a 'WithLogger',
+a 'WithCounter' and the
+ability to be 'Customizable' we could have put everything in an unique
+class. The problem is that in real life one never knows ;)
+Fortunately, Python dynamism allows to correct design mistakes
+
+Remark: in all text books about inheritance, the authors always stress
+that inheritance should be used as a "is-a" relation, not
+and "has-a" relation. In spite of this fact, I have decided to implement
+the concept of having a logger (or a counter) via a mixin class. One
+should not blindly believe text books ;)
+
+Fixing wrong hierarchies
+-----------------------------------------------------------------------------
+
+A typical metaprogramming technique, is the run-time modification of classes.
+As I said in a previous chapter, this feature can confuse the programmer and
+should not be abused (in particular it should not be used as a replacement
+of inheritance!); nevertheless, there applications where the ability of
+modifying classes at run time is invaluable: for instance,
+it can be used to correct design mistakes.
+
+In this case we would like the ``__str__ method`` of 'PrettyPrinted' to be
+overridden by ``GenericPizza.__str__``. Naively, this can be solved by
+putting 'WithLogger' after 'GenericPizza'. Unfortunately, doing so
+would cause ``GenericPizza.__init__`` to override ``WithLogger.__init__``,
+therefore by loosing logging capabilitiesr, unless countermeasures
+are taken.
+
+A valid countermeasure could be to replace the non-cooperative
+``GenericPizza.__init__`` with a cooperative one. This can miraculously
+done at run time in few lines of code:
+
+ ::
+
+ #<oopp.py>
+
+ def coop_init(self,size): # cooperative __init__ for GenericPizza
+ self.size=size
+ super(self._GenericPizza__this,self).__init__(size)
+
+ GenericPizza.__init__=coop_init # replace the old __init__
+
+ reflective(GenericPizza) # define GenericPizza.__this
+
+ #</oopp.py>
+
+Notice the usage of the fully qualified private attribute
+``self._GenericPizza__this`` inside ``coop_init``: since this function
+is defined outside any class, the automatica mangling mechanism cannot
+work and has to be implemented by hand. Notice also that
+``super(self._GenericPizza__this,self)`` could be replaced by
+``super(GenericPizza,self)``; however the simpler approach is
+less safe against possible future manipulations of the hierarchy.
+Suppose, for example, we want to create a copy of the hierarchy
+with the same name but slightly different features (actually,
+in chapter 8 we will implement a traced copy of the pizza hierarchy,
+useful for debugging purposes): then, using ``super(GenericPizza,self)``
+would raise an error, since self would be an instance of the traced
+hierarchy and ``GenericPizza`` the original nontraced class. Using
+the form ``super(self._GenericPizza__this,self)`` and making
+``self._GenericPizza__this`` pointing to the traced 'GenericPizza'
+class (actually this will happen automatically) the problems goes
+away.
+
+Now everything works if 'WithLogger' is put after 'CustomizablePizza'
+
+ >>> from oopp import *
+ >>> class PizzaWithLog(CustomizablePizza,WithLogger): pass
+ >>> PizzaWithLog.With(toppinglist=['tomato'])('large')
+ ****************************************************************************
+ Sun Apr 13 16:19:12 2003
+ 1. Created large pizza with tomato, cost $ 4.5
+ <class '__main__.PizzaWithLog'>
+
+The log correctly says ``Created large pizza with tomato, cost $ 4.5`` and not
+``Created <Pizza>`` as before since now ``GenericPizza.__str__``
+overrides ``PrettyPrinted.__str__``. Moreover, the hierarchy is logically
+better organized:
+
+ >>> PizzaWithLog.mro()
+ [<class '__main__.PizzaWithLog'>, <class 'oopp.CustomizablePizza'>,
+ <class 'oopp.GenericPizza'>, <class 'oopp.Customizable'>,
+ <class 'oopp.WithLogger'>, <class 'oopp.WithCounter'>,
+ <class 'oopp.PrettyPrinted'>, <type 'object'>]
+
+I leave as an exercise for the reader to make the ``__str__`` methods
+cooperative ;)
+
+Obviously, in this example it would have been better to correct the
+original hierarchy, by leaving 'Beautiful' instantiable from the beginning
+(that's why I said the 'Beautiful' is an example of wrong mix-in class):
+nevertheless, sometimes, one has do to with wrong hierarchies written by
+others, and it can be a pain to fix them, both directly by modifying the
+original source code, and indirectly
+by inheritance, since one must change all the names, in order to distinghish
+the original classes from the fixed ones. In those cases Python
+dynamism can save your life. This also allows you enhance original
+classes which are not wrong, but that simply don't do something you want
+to implement.
+
+Modifying classes at run-time can be trivial, as in the examples I have
+shown here, but can also be rather tricky, as in this example
+
+ >>> from oopp import PrettyPrinted
+ >>> class PrettyPrintedWouldBe(object): __str__ = PrettyPrinted.__str__
+ >>> print PrettyPrintedWouldBe() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: unbound method __str__() must be called with PrettyPrinted
+ instance as first argument (got nothing instead)
+
+As the error message says, the problem here, is that the
+``PrettyPrinted.__str__`` unbound method, has not received any argument.
+This is because in this
+form ``PrettyPrintedWouldBe.__str__`` has been defined as an attribute,
+not as a real method. The solution is to write
+
+ >>> class PrettyPrintedWouldBe(object):
+ ... __str__ = PrettyPrinted.__dict__['__str__']
+ ...
+ >>> print PrettyPrintedWouldBe() # now it works
+ <PrettyPrintedWouldBe>
+
+This kind of run-time modifications does not work when private variables
+are involved:
+
+ ::
+
+ #<changewithprivate.py>
+
+ class C(object):
+ __x='C.__init__'
+ def __init__(self):
+ print self.__x # okay
+
+ class D(object):
+ __x='D.__init__'
+ __init__=C.__dict__['__init__'] # error
+
+ class New:
+ class C(object):
+ __x='New.C.__init__'
+ __init__=C.__dict__['__init__'] # okay
+
+ C()
+ try: D()
+ except AttributeError,e: print e
+
+ #</changewithprivate.py>
+
+Gives as result
+
+ ::
+
+ C.__init__
+ 'D' object has no attribute '_C__x'
+ New.C.__init__
+
+The problem is that when ``C.__dict__['__init__']`` is compiled
+(to byte-code) ``self.__x`` is expanded to ``self._C__x``. However,
+when one invokes ``D.__init__``, a D-object is passed, which has
+a ``self._D__x`` attribute, but not a ``self._C__x`` attribute (unless
+'D' is a subclass of 'C'. Fortunately, Python wisdom
+
+ *Namespaces are one honking great idea -- let's do more of those!*
+
+suggests the right solution: to use a new class with the *same name*
+of the old one, but in a different namespace, in order to avoid
+confusion. The simplest way to generate a new namespace is to
+declare a new class (the class 'New' in this example): then 'New.C'
+becomes an inner class of 'New'. Since it has the same name of the
+original class, private variables are correctly expanded and one
+can freely exchange methods from 'C' to 'New.C' (and viceversa, too).
+
+Modifying hierarchies
+-------------------------------------------------------------------------
+
+ ::
+
+ def mod(cls): return cls
+
+ class New: pass
+
+ for c in HomoSapiensSapiens.__mro__:
+ setattr(New,c.__name__,mod(c))
+
+Inspecting Python code
+-------------------------------------------------------------------------
+
+how to inspect a class, by retrieving useful informations about its
+information.
+
+A first possibility is to use the standard ``help`` function.
+The problem of this approach is that ``help`` gives too much
+information.
+
+ ::
+
+ #<oopp.py>
+
+ #plaindata=
+ plainmethod=lambda m:m #identity function
+
+ class Get(object):
+ """Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod,
+ property, plainmethod, plaindata, returns the corresponding
+ attributes as a keyword dictionary. It works by internally calling
+ the routine inspect.classify_class_attrs. Notice that data
+ attributes with double underscores are not retrieved
+ (this is by design)."""
+ def __init__(self,cls):
+ self.staticmethods=kwdict()
+ self.classmethods=kwdict()
+ self.properties=kwdict()
+ self.methods=kwdict()
+ self.data=kwdict()
+ for name, kind, klass, attr in inspect.classify_class_attrs(cls):
+ if kind=='static method':
+ self.staticmethods[name]=attr
+ elif kind=='class method':
+ self.classmethods[name]=attr
+ elif kind=='property':
+ self.properties[name]=attr
+ elif kind=='method':
+ self.methods[name]=attr
+ elif kind=='data':
+ if not special(name): self.data[name]=attr
+ def __call__(self,descr): #could be done with a dict
+ if descr==staticmethod: return self.staticmethods
+ elif descr==classmethod: return self.classmethods
+ elif descr==property: return self.properties
+ elif descr==plainmethod: return self.methods
+ elif descr==plaindata: return self.data
+ else: raise SystemExit("Invalid descriptor")
+
+ #</oopp.py>
+
+With similar tricks one can automatically recognize cooperative methods:
+#it is different, (better NOT to use descriptors)
+
+ ::
+
+ #<oopp.py>
+
+ #class Cooperative(Class):
+ # __metaclass__ = WithWrappingCapabilities
+ #
+ # def cooperative(method):
+ # """Calls both the superclass method and the class
+ # method (if the class has an explicit method).
+ # Works for methods returning None."""
+ # name,cls=Cooperative.parameters # fixed by the meta-metaclass
+ # def _(*args,**kw):
+ # getattr(super(cls,args[0]),name)(*args[1:],**kw)
+ # if method: method(*args,**kw) # call it
+ # return _
+ #
+ # cooperative=staticmethod(cooperative)
+
+
+ #</oopp.py>
+
+ ::
+
+ #<oopp.py>
+
+ def wrapH(cls):
+ for c in cls.__mro__[:-2]:
+ tracer.namespace=c.__name__
+ new=vars(c).get('__new__',None)
+ if new: c.__new__=tracedmethod(new)
+
+ #</oopp.py>
+
+
+
+THE MAGIC OF METACLASSES - PART I
+==========================================================================
+
+ .. line-block::
+
+ *Metaclasses are deeper magic than 99% of users should ever
+ worry about. If you wonder whether you need them, you don't
+ (the people who actually need them know with certainty that
+ they need them, and don't need an explanation about why).*
+ --Tim Peters
+
+Python always had metaclasses, since they are inherent to its object
+model. However, before Python 2.2, metaclasses where tricky and their
+study could cause the programmer's brain to explode [#]_. Nowadays,
+the situation has changed, and the reader should be able to understand
+this chapter without risk for his/her brain (however I do not give any
+warranty ;)
+
+Put it shortly, metaclasses give to the Python programmer
+complete control on the creation of classes. This simple statement
+has far reaching consequences, since the ability of interfering with
+the process of class creation, enable the programmer to make miracles.
+
+In this and in the following chapters, I will show some of these
+miracles.
+
+This chapter will focus on subtle problems of metaclasses in inheritance
+and multiple inheritance, including multiple inheritance of metaclasses
+with classes and metaclasses with metaclasses.
+
+The next chapter will focus more on applications.
+
+
+.. [#] Metaclasses in Python 1.5 [A.k.a the killer joke]
+ http://www.python.org/doc/essays/metaclasses/
+
+There is very little documentation about metaclasses, except Guido's
+essays and the papers by David Mertz and myself published in IBMdeveloperWorks
+
+ http://www-106.ibm.com/developerworks/library/l-pymeta.html
+
+Metaclasses as class factories
+------------------------------------------------------------------------
+
+In the Python object model (inspired from the Smalltalk, that had metaclasses
+a quarter of century ago!) classes themselves are objects.
+Now, since objects are instances of classes, that means that classes
+themselves can be seen as instances of special classes called *metaclasses*.
+Notice that things get hairy soon, since by following this idea, one could
+say the metaclasses themselves are classes and therefore objects; that
+would mean than even metaclasses can be seen as
+instances of special classes called meta-metaclasses. On the other hand,
+meta-meta-classes can be seen as instances of meta-meta-metaclasses,
+etc. Now, it should be obvious why metaclasses have gained such a
+reputation of brain-exploders ;). However, fortunately, the situation
+is not so bad in practice, since the infinite recursion of metaclasses is
+avoided because there is a metaclass that is the "mother of all metaclasses":
+the built-in metaclass *type*. 'type' has the property of being its own
+metaclass, therefore the recursion stops. Consider for instance the following
+example:
+
+ >>> class C(object): pass # a generic class
+ >>> type(C) #gives the metaclass of C
+ <type 'type'>
+ >>> type(type(C)) #gives the metaclass of type
+ <type 'type'>
+
+The recursion stops, since the metaclass of 'type' is 'type'.
+One cool consequence of classes being instances of 'type',
+is that since *type* is a subclass of object,
+
+ >>> issubclass(type,object)
+ True
+
+any Python class is not only a subclass of ``object``, but also
+an instance of 'object':
+
+ >>> isinstance(C,type)
+ True
+ >>> isinstance(C,object)
+ True
+ >>> issubclass(C,object)
+ True
+
+Notice that 'type' is an instance of itself (!) and therefore of 'object':
+
+ >>> isinstance(type,type) # 'type' is an instance of 'type'
+ True
+ >>> isinstance(type,object) # therefore 'type' is an instance of 'object'
+ True
+
+As it is well known, ``type(X)`` returns the type of ``X``; however,
+``type`` has also a second form in which it acts as a class factory.
+The form is ``type(name,bases,dic)`` where ``name`` is the name of
+the new class to be created, bases is the tuple of its bases and dic
+is the class dictionary. Let me give a few examples:
+
+ >>> C=type('C',(),{})
+ >>> C
+ <class '__main__.C'>
+ >>> C.__name__
+ 'C'
+ >>> C.__bases__
+ (<type 'object'>,)
+ >>> C.__dict__
+ <dict-proxy object at 0x8109054>
+
+Notice that since all metaclasses inherits from ``type``, as a consequences
+all metaclasses can be used as class factories.
+
+A fairy tale example will help in understanding the concept
+and few subtle points on how attributes are transmitted from metaclasses
+to their instances.
+
+Let me start by defining a 'Nobility' metaclass :
+
+ >>> class Nobility(type): attributes="Power,Richness,Beauty"
+
+instances of 'Nobility' are classes such 'Princes', 'Dukes', 'Barons', etc.
+
+ >>> Prince=Nobility("Prince",(),{})
+
+Instances of 'Nobility' inherits its attributes, just as instances of normal
+classes inherits the class docstring:
+
+ >>> Prince.attributes
+ 'Power,Richness,Beauty'
+
+Nevertheless, 'attributes' will not be retrieved by the ``dir`` function:
+
+ >>> print dir(Prince)
+ ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__weakref__']
+
+However, this is a limitation of ``dir``, in reality ``Prince.attributes``
+is there. On the other hand, the situation is different for a specific
+'Prince' object
+
+ >>> charles=Prince()
+ >>> charles.attributes #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'Prince' object has no attribute 'attributes'
+
+The transmission of metaclass attributes is not transitive:
+instances of the metaclass inherits the attributes, but not the instances
+of the instances. This behavior is by design and is needed in order to avoid
+troubles with special methods. This point will be throughly
+explained in the last paragraph. For the moment, I my notice that the
+behaviour is reasonable, since the abstract qualities 'Power,Richness,Beauty'
+are more qualities of the 'Prince' class than of one specific representative.
+They can always be retrieved via the ``__class__`` attribute:
+
+ >>> charles.__class__.attributes
+ 'Power,Richness,Beauty'
+
+Le me now define a metaclass 'Froggyness':
+
+ >>> class Frogginess(type): attributes="Powerlessness,Poverty,Uglyness"
+
+Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.
+
+ >>> Frog=Frogginess("Frog",(),{})
+ >>> Frog.attributes
+ 'Powerlessness,Poverty,Uglyness'
+
+However, in Python miracles can happen:
+
+ >>> def miracle(Frog): Frog.__class__=Nobility
+ >>> miracle(Frog); Frog.attributes
+ 'Powerlessness,Richness,Beauty'
+
+In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.
+
+However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:
+
+ >>> Frog.attributes="poor, small, ugly"
+ >>> jack=Frog(); jack.attributes
+ 'poor, small, ugly'
+
+Then the miracle cannot work:
+
+ ::
+
+ #<fairytale2.py>
+
+ class Nobility(type): attributes="Power, Richness, Beauty"
+ Prince=Nobility("Prince",(),{})
+ charles=Prince()
+
+ class Frogginess(type): attributes="Inpuissance, Poverty, Uglyness"
+ Frog=Frogginess("Frog",(),{})
+ Frog.attributes="poor, small, ugly"
+ jack=Frog()
+
+ def miracle(Frog): Frog.__class__=Nobility
+
+ miracle(Frog)
+
+ print "I am",Frog.attributes,"even if my class is",Frog.__class__
+
+ #</fairytale2.py>
+
+Output:
+
+ ::
+
+ I am poor, small, ugly even if my class is <class '__main__.Nobility'>
+
+The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is ``poor, small, ugly``. If you think a bit, it makes sense.
+
+Remark:
+
+In Python 2.3 there are restrictions when changing the ``__class__``
+attribute for classes:
+
+ >>> C=type('C',(),{})
+ >>> C.__class__ = Nobility #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: __class__ assignment: only for heap types
+
+Here changing ``C.__class__`` is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:
+
+ >>> print bool.__doc__ # in Python 2.2 would give an error
+ bool(x) -> bool
+ Returns True when the argument x is true, False otherwise.
+ The builtins True and False are the only two instances of the class bool.
+ The class bool is a subclass of the class int, and cannot be subclassed.
+
+In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:
+
+ >>> class M1(type): f=lambda cls: 'M1.f' #metaclass1
+ >>> class M2(type): f=lambda cls: 'M2.f' #metaclass2
+ >>> B=M1('B',(),{}) # B receives M1.f
+ >>> class C(B): pass #C receives M1.f
+ >>> B.f()
+ 'M1.f'
+ B.__class__=M2 #change the metaclass
+ >>> B.f() #B receives M2.f
+ 'M2.f'
+ C.f() #however C does *not* receive M2.f
+ >>> C.f()
+ 'M1.f'
+ >>> type(B)
+ <class '__main__.M2'>
+ >>> type(C)
+ <class '__main__.M1'>
+
+Metaclasses as class modifiers
+----------------------------------------------------------------------
+
+The interpretation of metaclasses in terms of class factories is quite
+straightforward and I am sure that any Pythonista will be at home
+with the concept. However, metaclasses have such a reputation of black
+magic since their typical usage is *not* as class factories, but as
+*class modifiers*. This means that metaclasses are typically
+used to modify *in fieri* classes. The trouble is that the
+modification can be utterly magical.
+Here there is another fairy tale example showing the syntax
+(via the ``__metaclass__`` hook) and the magic of the game:
+
+ ::
+
+ #<oopp.py>
+
+ class UglyDuckling(PrettyPrinted):
+ "A plain, regular class"
+ formatstring="Not beautiful, I am %s"
+
+ class MagicallyTransformed(type):
+ "Metaclass changing the formatstring of its instances"
+ def __init__(cls,*args):
+ cls.formatstring="Very beautiful, since I am %s"
+
+ class TransformedUglyDuckling(PrettyPrinted):
+ "A class metamagically modified"
+ __metaclass__ = MagicallyTransformed
+ formatstring="Not beautiful, I am %s" # will be changed
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> print UglyDuckling()
+ Not beautiful, I am <UglyDuckling>
+
+In this example, even if in 'TransformedUglyDuckling' we explicitely
+set the formatstring to "Not beautiful, I am %s", the metaclass changes
+it to "Very beautiful, even if I am %s" and thus
+
+ >>> print TransformedUglyDuckling() # gives
+ Very beautiful, since I am <TransformedUglyDuckling>
+
+Notice that the ``__metaclass__`` hook passes to the metaclass
+``MagicallyTransformed`` the name, bases and dictionary of the class
+being created, i.e. 'TransformedUglyDucking'.
+
+Metaclasses, when used as class modifiers, act *differently*
+from functions, when inheritance is
+involved. To clarify this subtle point, consider a subclass 'Swan'
+of 'UglyDuckling':
+
+
+ >>> from oopp import *
+ >>> class Swan(UglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+Now, let me define a simple function acting as a class modifier:
+
+ >>> def magicallyTransform(cls):
+ ... "Modifies the class formatstring"
+ ... customize(cls,formatstring="Very beautiful, even if I am %s")
+ ... return cls
+
+The function works:
+
+ >>> magicallyTransform(UglyDuckling)
+ >>> print UglyDuckling()
+ Very beautiful, even if I am <UglyDuckling>
+
+This approach is destructive, since we cannot have the original
+and the transformed class at the same time, and has potentially bad side
+effects in the derived classes. Nevertheless, in this case it works
+and it is not dangereous for the derived class 'Swan', since 'Swan'
+explicitly overrides the 'formatstring' attribute and doesn't care about
+the change in 'UglyDuckling.formatstring'. Therefore the output
+of
+
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+is still the same as before the action of the function ``magicallyTransform``.
+The situation is quite different if we use the 'MagicallyTransformed'
+metaclass:
+
+ >>> from oopp import *
+ >>> class Swan(TransformedUglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+
+ >>> print TransformedUglyDuckling()
+ Very beautiful, since I am <UglyDuckling>
+ >>> print Swan() # does *not* print "Very beautiful, I am <Swan>"
+ Very beautiful, since I am <Swan>
+
+Therefore, not only the metaclass has magically transformed the
+'TransformedUglyDuckling.formatstring', it has also transformed the
+'Swan.formatstring'! And that, despite the fact that
+'Swan.formatstring' is explicitly set.
+
+The reason for this behaviour is that since 'UglyDuckling' is a base
+class with metaclass 'MagicallyTransformed', and since 'Swan' inherits from
+'UglyDuckling', then 'Swan' inherits the metaclass 'MagicallyTransformed',
+which is automatically called at 'Swan' creation time.
+That's the reason why metaclasses are much more magical and much
+more dangerous than
+functions: functions do not override attributes in the derived classes,
+metaclasses do, since they are automagically called at the time of
+creation of the subclass. In other words, functions are explicit,
+metaclasses are implicit. Nevertheless, this behavior can be pretty
+useful in many circumstances, and it is a feature, not a bug. In the
+situations where this behavior is not intended, one should use a function,
+not a metaclass. In general, metaclasses are better than functions,
+since metaclasses are classes and as such they can inherit one from each
+other. This means that one can improve a basic metaclass trough
+(multiple) inheritance, with *reuse* of code.
+
+A few caveats about the usage of metaclasses
+------------------------------------------------------------------------
+
+Let me start with some caveats about the ``__metaclass__`` hook, which
+commonly used and quite powerful, but also quite dangereous.
+
+Let's imagine a programmer not
+knowing about metaclasses and looking at the 'TransformedUglyDuckling'
+code (assuming there are no comments): she would probably think
+that "__metaclass__" is some special attribute used for introspection
+purposes only, with no other effects, and she would probably expect
+the output of the script to be "Not much, I am the class
+TransformedUglyDucking" whereas it is exacly the contrary! In other
+words, when metaclasses are involved, *what you see, is not what you get*.
+The situation is even more implicit when the metaclass is inherited
+from some base class, therefore lacking also the visual clue of the hook.
+
+For these reasons, metaclasses are something to be used with great care;
+they can easily make your code unreadable and confuse inexpert programmers.
+Moreover, it is more difficult to debug programs involving metaclasses, since
+methods are magically transformed by routines defined in the metaclass,
+and the code you see in the class is *not* what Python sees. I think
+the least confusing way of using metaclasses, is to concentrate all
+the dynamics on them and to write empty classes except for the
+metaclass hook. If you write a class with no methods such as
+
+ ::
+
+ class TransformedUglyDuckling(object):
+ __metaclass__=MagicallyTransformed
+
+then the only place to look at, is the metaclass. I have found extremely
+confusing to have some of the methods defined in the class and some in
+the metaclass, especially during debugging.
+
+Another point to make, is that the ``__metaclass__``
+hook should not be used to modify pre-existing classes,
+since it requires modifying the source code (even if it is enough to
+change one line only). Moreover, it is confusing, since adding a
+``__metaclass__`` attribute *after* the class creation would not do the job:
+
+ >>> from oopp import UglyDuckling, MagicallyTransformed
+ >>> UglyDuckling.__metaclass__=MagicallyTransformed
+ >>> print UglyDuckling()
+ "Not much, I am the class UglyDuckling"
+
+The reason is that we have to think of UglyDuckling as an instance of
+``type``, the built-in metaclasses; merely adding a ``__metaclass__``
+attribute does not re-initialize the class.
+The problem is elegantly solved by avoiding the hook and creating
+an enhanced copy of the original class trough ``MagicallyTransformed``
+used as a class factory.
+
+ >>> name=UglyDuckling.__name__
+ >>> bases=UglyDuckling.__bases__
+ >>> dic=UglyDuckling.__dict__.copy()
+ >>> UglyDuckling=MagicallyTransformed(name,bases,dic)
+
+Notice that I have recreated 'UglyDuckling', giving to the new class
+the old identifier.
+
+ >>> print UglyDuckling()
+ Very beautiful, since I am <UglyDuckling>>
+
+The metaclass of this new 'UglyDuckling' has been specified and will
+accompanies all future children of 'UglyDuckling':
+
+ >>> class Swan(UglyDuckling): pass
+ ...
+ >>> type(Swan)
+ <class '__main__.MagicallyTransformed'>
+
+Another caveat, is in the overridding of `` __init__`` in the metaclass.
+This is quite common in the case of metaclasses called trough the
+``__metaclass__`` hook mechanism, since in this case the class
+has been already defined (if not created) in the class statement,
+and we are interested in initializing it, more than in recreating
+it (which is still possible, by the way).
+The problem is that overriding ``__init__`` has severe limitations
+with respect to overriding ``__new__``,
+since the 'name', 'bases' and 'dic' arguments cannot be directly
+changed. Let me show an example:
+
+ ::
+
+ #<init_in_metaclass.py>
+
+ from oopp import *
+
+ class M(type):
+ "Shows that dic cannot be modified in __init__, only in __new__"
+ def __init__(cls,name,bases,dic):
+ name='C name cannot be changed in __init__'
+ bases='cannot be changed'
+ dic['changed']=True
+
+ class C(object):
+ __metaclass__=M
+ changed=False
+
+ print C.__name__ # => C
+ print C.__bases__ # => (<type 'object'>,)
+ print C.changed # => False
+
+ #</init_in_metaclass.py>
+
+The output of this script is ``False``: the dictionary cannot be changed in
+``__init__`` method. However, replacing ``dic['changed']=True`` with
+``cls.changed=True`` would work. Analougously, changing ``cls.__name__``
+would work. On the other hand, ``__bases__`` is a read-only attribute and
+cannot be changed once the class has been created, therefore there is no
+way it can be touched in ``__init__``. However, ``__bases__`` could be
+changed in ``__new__`` before the class creation.
+
+Metaclasses and inheritance
+-------------------------------------------------------------------------
+
+It is easy to get confused about the difference between a metaclass
+and a mix-in class in multiple inheritance, since
+both are denoted by adjectives and both share the same idea of
+enhancing a hierarchy. Moreover, both mix-in classes and metaclasses
+can be inherited in the whole hierarchy.
+Nevertheless, they behaves differently
+and there are various subtle point to emphasize. We have already
+noticed in the first section that attributes of a metaclass
+are transmitted to its instances, but not to the instances of the
+instances, whereas the normal inheritance is transitive: the
+grandfather transmits its attributes to the children and to the grandchild
+too. The difference can be represented with the following picture, where
+'M' is the metaclass, 'B' a base class, 'C' a children of 'B'
+and c an instance of 'C':
+
+ ::
+
+ M (attr) B (attr)
+ : |
+ C (attr) C (attr)
+ : :
+ c () c (attr)
+
+Notice that here the relation of instantiation is denoted by a dotted line.
+
+This picture is valid when C has metaclass M but not base class, on when C
+has base class but not metaclass. However, what happens whrn the class C has
+both a metaclass M and a base class B ?
+
+ >>> class M(type): a='M.a'
+ >>> class B(object): a='B.a'
+ >>> class C(B): __metaclass__=M
+ >>> c=C()
+
+The situation can be represented by in the following graph,
+
+ ::
+
+ (M.a) M B (B.a)
+ : /
+ : /
+ (?) C
+ :
+ :
+ (?) c
+
+
+Here the metaclass M and the base class B are fighting one against the other.
+Who wins ? C should inherit the attribute 'B.a' from its base B, however,
+the metaclass would like to induce an attribute 'M.a'.
+The answer is that the inheritance constraint wins on the metaclass contraint:
+
+ >>> C.a
+ 'B.a'
+ >>> c.a
+ 'B.a'
+
+The reason is the same we discussed in the fairy tale example: 'M.a' is
+an attribute of the metaclass, if its instance C has already a specified
+attributed C.a (in this case specified trough inheritance from B), then
+the attribute is not modified. However, one could *force* the modification:
+
+ >>> class M(type):
+ ... def __init__(cls,*args): cls.a='M.a'
+ >>> class C(B): __metaclass__=M
+ >>> C.a
+ 'M.a'
+
+In this case the metaclass M would win on the base class B. Actually,
+this is not surprising, since it is explicit. What could be surprising,
+had we not explained why inheritance silently wins, is that
+
+ >>> c.a
+ 'B.a'
+
+This explain the behaviour for special methods like
+``__new__,__init__,__str__``,
+etc. which are defined both in the class and the metaclass with the same
+name (in both cases,they are inherited from ``object``).
+
+In the chapter on objects, we learned that the printed representation of
+an object can be modified by overring the ``__str__`` methods of its
+class. In the same sense, the printed representation of a class can be
+modified by overring the ``__str__`` methods of its metaclass. Let me show an
+example:
+
+ ::
+
+ #<oopp.py>
+
+ class Printable(PrettyPrinted,type):
+ """Apparently does nothing, but actually makes PrettyPrinted acting as
+ a metaclass."""
+
+ #</oopp.py>
+
+Instances of 'Printable' are classes with a nice printable representation:
+
+ >>> from oopp import Printable
+ >>> C=Printable('Classname',(),{})
+ >>> print C
+ Classname
+
+However, the internal string representation stays the same:
+
+ >>> C # invokes Printable.__repr__
+ <class '__main__.Classname'>
+
+Notice that the name of class 'C' is ``Classname`` and not 'C' !
+
+Consider for instance the following code:
+
+ >>> class M(type):
+ ... def __str__(cls):
+ ... return cls.__name__
+ ... def method(cls):
+ ... return cls.__name__
+ ...
+ >>> class C(object):
+ ... __metaclass__=M
+ >>> c=C()
+
+In this case the ``__str__`` method in ``M`` cannot override the
+``__str__`` method in C, which is inherited from ``object``.
+Moreover, if you experiment a little, you will see that
+
+ >>> print C # is equivalent to print M.__str__(C)
+ C
+ >>> print c # is equivalent to print C.__str__(c)
+ <__main__.C object at 0x8158f54>
+
+
+The first ``__str__`` is "attached" to the metaclass and the
+second to the class.
+
+Consider now the standard method "method". It is both attached to the
+metaclass
+
+ >>> print M.method(C)
+ C
+
+and to the class
+
+ >>> print C.method() #in a sense, this is a class method, i.e. it receives
+ C #the class as first argument
+
+Actually it can be seen as a class method of 'C' (cfr. Guido van Rossum
+"Unifying types and classes in Python 2.2". When he discusses
+classmethods he says: *"Python also has real metaclasses, and perhaps
+methods defined in a metaclass have more right to the name "class method";
+but I expect that most programmers won't be using metaclasses"*). Actually,
+this is the SmallTalk terminology, Unfortunately, in Python the word
+``classmethod`` denotes an attribute descriptor, therefore it is better
+to call the methods defined in a metaclass *metamethods*, in order to avoid
+any possible confusion.
+
+The difference between ``method`` and ``__str__`` is that you cannot use the
+syntax
+
+ >>> print C.__str__() #error
+ TypeError: descriptor '__str__' of 'object' object needs an argument
+
+because of the confusion with the other __str__; you can only use the
+syntax
+
+ >>> print M.__str__(C)
+
+Suppose now I change C's definition by adding a method called "method":
+
+ ::
+
+ class C(object):
+ __metaclass__=M
+ def __str__(self):
+ return "instance of %s" % self.__class__
+ def method(self):
+ return "instance of %s" % self.__class__
+
+If I do so, then there is name clashing and the previously working
+statement print C.method() gives now an error:
+
+ ::
+
+ Traceback (most recent call last):
+ File "<stdin>", line 24, in ?
+ TypeError: unbound method method() must be called with C instance as
+ first argument (got nothing instead)
+
+Conclusion: ``__str__, __new__, __init__`` etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. ``M.__str__(C)``),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. ``C.method()`` instead of
+``M.method(C)``).
+Metaclass methods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for ``__str__``,
+``___init__``, etc.
+
+Conflicting metaclasses
+----------------------------------------------------------------------------
+
+Consider a class 'A' with metaclass 'M_A' and a class 'B' with
+metaclass 'M_B'; suppose I derive 'C' from 'A' and 'B'. The question is:
+what is the metaclass of 'C' ? Is it 'M_A' or 'M_B' ?
+
+The correct answer (see "Putting metaclasses to work" for a thought
+discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+'M_A' and 'M_B', as in the following graph:
+
+
+ .. figure:: fig1.gif
+
+However, Python is not yet that magic, and it does not automatically create
+'M_C'. Instead, it will raise a ``TypeError``, warning the programmer of
+the possible confusion:
+
+ >>> class M_A(type): pass
+ >>> class M_B(type): pass
+ >>> A=M_A('A',(),{})
+ >>> B=M_B('B',(),{})
+ >>> class C(A,B): pass #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metatype conflict among bases
+
+This is an example where the metaclasses 'M_A' and 'M_B' fight each other
+to generate 'C' instead of cooperating. The metatype conflict can be avoided
+by assegning the correct metaclass to 'C' by hand:
+
+ >>> class C(A,B): __metaclass__=type("M_AM_B",(M_A,M_B),{})
+ >>> type(C)
+ <class '__main__.M_AM_B'>
+
+In general, a class A(B, C, D , ...) can be generated without conflicts only
+if type(A) is a subclass of each of type(B), type(C), ...
+
+In order to avoid conflicts, the following function, that generates
+the correct metaclass by looking at the metaclasses of the base
+classes, is handy:
+
+ ::
+
+ #<oopp.py>
+
+ metadic={}
+
+ def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+ #</oopp.py>
+
+This function is particularly smart since:
+
+ 1. Avoid duplications ..
+
+ 2. Remember its results.
+
+We may generate the child of a tuple of base classes with a given metaclass
+and avoiding metatype conflicts thanks to the following ``child`` function:
+
+ ::
+
+ #<oopp.py>
+
+ def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> class C(A,B): __metaclass__=makecls()
+ >>> print C,type(C)
+ <class 'oopp.AB_'> <class 'oopp._M_AM_B'>
+
+Notice that the automatically generated metaclass does not pollute the
+namespace:
+
+ >>> _M_A_M_B #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ NameError: name '_M_A_M_B' is not defined
+
+It can only be accessed as ``type(C)``.
+
+Put it shortly, the ``child`` function allows to generate a child from bases
+enhanced by different custom metaclasses, by generating under the hood a
+compatibile metaclass via multiple inheritance from the original metaclasses.
+However, this logic can only work if the original metaclasses are
+cooperative, i.e. their methods are written in such a way to avoid
+collisions. This can be done by using the cooperative the ``super`` call
+mechanism discussed in chapter 4.
+
+Cooperative metaclasses
+----------------------------------------------------------------------------
+
+In this section I will discuss how metaclasses can be composed with
+classes and with metaclasses, too. Since we will discusss even
+complicated hierarchies, it is convenient to have an utility
+routine printing the MRO of a given class:
+
+ ::
+
+ #<oopp.py>
+
+ def MRO(cls):
+ count=0; out=[]
+ print "MRO of %s:" % cls.__name__
+ for c in cls.__mro__:
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=" %s - %s(%s)" % (count,name,bases)
+ if type(c) is not type: s+="[%s]" % type(c).__name__
+ out.append(s); count+=1
+ return '\n'.join(out)
+
+ #</oopp.py>
+
+Notice that ``MRO`` also prints the metaclass' name in square brackets, for
+classes enhanced by a non-trivial metaclass.
+
+Consider for instance the following hierarchy:
+
+ >>> from oopp import MRO
+ >>> class B(object): pass
+ >>> class M(B,type): pass
+ >>> class C(B): __metaclass__=M
+
+Here 'M' is a metaclass that inherits from 'type' and the base class 'B'
+and 'C' is both an instance of 'M' and a child of 'B'. The inheritance
+graph can be draw as
+
+ ::
+
+ object
+ / \
+ B type
+ | \ /
+ | M
+ \ :
+ \ :
+ C
+
+Suppose now we want to retrieve the ``__new__`` method of B's superclass
+with respect to the MRO of C: obviously, this is ``object.__new__``, since
+
+ >>> print MRO(C)
+ MRO of C:
+ 0 - C(B)[M]
+ 1 - B(object)
+ 2 - object()
+
+This allows to create an instance of 'C' in this way:
+
+ >>> super(B,C).__new__(C)
+ <__main__.C object at 0x4018750c>
+
+It is interesting to notice that this would not work in Python 2.2,
+due to a bug in the implementation of ``super``, therefore do not
+try this trick with older version of Python.
+
+Notice that everything works
+only because ``B`` inherits the ``object.__new__`` staticmethod that
+is cooperative and it turns out that it calls ``type.__new__``. However,
+if I give to 'B' a non-cooperative method
+
+ >>> B.__new__=staticmethod(lambda cls,*args: object.__new__(cls))
+
+things do not work:
+
+ >>> M('D',(),{}) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 1, in <lambda>
+ TypeError: object.__new__(M) is not safe, use type.__new__()
+
+A cooperative method would solve the problem:
+
+ >>> B.__new__=staticmethod(lambda m,*args: super(B,m).__new__(m,*args))
+ >>> M('D',(),{}) # calls B.__new__(M,'D',(),{})
+ <class '__main__.D'>
+
+Metamethods vs class methods
+-------------------------------------------------------------------
+
+Meta-methods, i.e. methods defined in
+a metaclass.
+
+Python has already few built-in metamethods: ``.mro()``
+and ``__subclass__``. These are methods of the metaclass 'type' and
+there of any of its sub-metaclasses.
+
+ >>> dir(type)
+ ['__base__', '__bases__', '__basicsize__', '__call__', '__class__',
+ '__cmp__', '__delattr__', '__dict__', '__dictoffset__', '__doc__',
+ '__flags__', '__getattribute__', '__hash__', '__init__', '__itemsize__',
+ '__module__', '__mro__', '__name__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__subclasses__', '__weakrefoffset__', 'mro']
+
+
+ >>> print type.mro.__doc__
+ mro() -> list
+ return a type's method resolution order
+ >>> print type.__subclasses__.__doc__
+ __subclasses__() -> list of immediate subclasses
+
+ >>> class A(object): pass
+ >>> class B(A): pass
+ >>> B.mro()
+ [<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
+ >>> A.__subclasses__()
+ [<class '__main__.B'>]
+
+Notice that ``mro()`` and ``__subclasses__`` are not retrieved by ``dir``.
+
+Let me constrast metamethods with the more traditional classmethods.
+In many senses, the to concepts are akin:
+
+ >>> class M(type):
+ ... "Metaclass with a (meta)method mm"
+ ... def mm(cls): return cls
+ >>> D=M('D',(),{'cm':classmethod(lambda cls: cls)})
+ >>> # instance of M with a classmethod cm
+ >>> D.mm # the metamethod
+ <bound method M.mm of <class '__main__.C'>>
+ >>> D.cm # the classmethod
+ <unbound method D.<lambda>>
+
+Notice the similarities between the classmethod and the metamethod:
+
+ >>> D.mm.im_self, D.cm.im_self # the same
+ (<class '__main__.D'>, <class '__main__.D'>)
+ >>> D.mm.im_class, D.cm.im_class # still the same
+ (<class '__main__.M'>, <class '__main__.M'>)
+
+There are no surprises for ``im_func``:
+
+ >>> D.mm.im_func, D.cm.im_func
+ (<function mm at 0x402c272c>, <function <lambda> at 0x402c280c>)
+
+Nevertheless, there are differences: metamethods are not bounded to
+instances of the class
+
+ >>> D().cm() # the classmethod works fine
+ <class '__main__.D'>
+ >>> D().mm() # the metamethod does not: error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'D' object has no attribute 'mm'
+
+and they are not retrieved by ``dir``:
+
+ >>> from oopp import *
+ >>> attributes(D).keys() # mm is not retrieved, only cm
+ ['cm']
+
+ >>> cm.__get__('whatever') #under Python 2.2.0 would give a serious error
+ Segmentation fault
+ >>> cm.__get__(None) #under Python 2.3 there is no error
+ <bound method type.<lambda> of <type 'NoneType'>>
+
+Moreover metamethods behaves differently with respect to multiple
+inheritance. If a class A define a classmethod cA and a class B
+defines a classmethod cB, then the class C(A,B) inherits both the
+classmethods cA and cB. In the case of metamethods defined in M_A
+and M_B, the same is true only if one resolves the meta-type
+conflict by hand, by generating the metaclass M_C(M_A,M_B). In this
+sense, classmethods are simpler to use than metamethods.
+
+
+THE MAGIC OF METACLASSES - PART 2
+===========================================================================
+
+Metaclasses are so powerful that a single chapter is not enough to make
+justice to them ;) In this second chapter on metaclasses I will
+unravel their deepest secrets, covering topics such as meta-metaclasses,
+anonymous inner metaclasses, global metaclasses and advanced class factories.
+
+Moreover, I will give various magical applications of metaclasses,
+in the realm of enhancing the Python language itself. Actually, this is
+probably the most idiomatic application of metaclasses (Guido's examples
+on the metaclass usage are all in this area). I will show
+how metaclasses can be used to enhance the ``super`` cooperatice call
+mechanism.
+
+This is not a chapter for the faint of heart.
+
+The secrets of the ``__metaclass__`` hook
+------------------------------------------------------------------------
+
+In the previous chapter we have seen how the ``__metaclass__`` hook can
+be used as a way of metaclass enhancing pre-existing classes
+with a minimal change of the sourcecode.
+
+But it has much deeper secrets.
+
+The first and simplest of them,
+is the fact that the hook can be used it can also be defined
+at the module level, *outside* the class. This allows a number of neat
+tricks, since in presence of a ``__metaclass__`` hook at the module
+level *all* the old style classes in the module (including nested ones!)
+acquire that hook. A first application is to rejuvenate old style classes
+to new style classes.
+
+I remind that old style classes are retained with compability with old
+code, but they are a pain in the back, if you want to use features
+intended for new style classes only (for instance properties etc.).
+Naively, one would expect the conversion from old style classes
+to new style to be long and error prone: suppose you have a very large
+application with hundreds of old style classes defined in dozens of modules.
+Suppose you want to update your application to Python 2.2+ classes in order
+to take advantage of the new features I have discussed extensively in this
+book: the naive way to go would be to go trough the source, look for
+all classes definitions and change
+
+ ::
+
+ Classname: --> Classname(object)
+
+One could solve this problem with a regular expression search and replace
+in all modules, but this would require to change *all* the source.
+This is againt the spirit of OOP, we must *reuse* old code.
+
+Metaclasses are particularly handy to solve this problem: actually it is
+enough to add to your modules the following line as first line:
+
+ ::
+
+ __metaclass__ = type
+
+Then, all your old style classes will have 'type' as their metaclass: this
+is akin to say that all the old style classes are *automagically* rejuvenate
+to new style classes! And this also works for *nested* classes!!
+
+ ::
+
+ #<rejuvenate.py>
+
+ __metaclass__ = type # this rejuvanate all the class in the module
+
+ class C:
+ class D: pass
+
+ print dir(C) # both C and C.D
+ print dir(C.D) # are now new style classes
+
+ #</rejuvenate.py>
+
+This very first example add consistence (if needed) to the
+widespread belief that metaclasses have a well deserved reputation of magic.
+
+The explanation is that defining a global metaclass called ``__metaclass__``
+automatically makes all old style classes (new style class simply ignore
+the existence of the global ``__metaclass__``) defined in you module
+instances of the given metaclass; this automatically converts them to
+new style classes.
+
+Anonymous inner metaclasses
+---------------------------------------------------------------------------
+
+A second, deeper secret of the ``__metaclass__`` hook is that it can be
+used to define anonymous *inner metaclasses*. The following example
+explain what I mean:
+
+ ::
+
+ #<oopp.py>
+
+ def totuple(arg):
+ "Converts the argument to a tuple, if need there is"
+ if isinstance(arg,tuple): return arg # do nothing
+ else: return (arg,) # convert to tuple
+
+ class BracketCallable(object):
+ """Any subclass C(BracketCallable) can be called with the syntax C[t],
+ where t is a tuple of arguments stored in bracket_args; returns the
+ class or an instance of it, depending on the flag 'returnclass'."""
+
+ returnclass=True
+ class __metaclass__(type): # anonymous inner metaclass
+ def __getitem__(cls,args): # non cooperative metamethod
+ if cls.returnclass:
+ c=type(cls.__name__,(cls,),{'bracket_args':totuple(args)})
+ return c # a customized copy of the original class
+ else:
+ self=cls(); self.bracket_args=totuple(args)
+ return self
+
+ #</oopp.py>
+
+In this code 'BracketCallable.__metaclass__' is the anonymous (actually
+it has a special name, ``__metaclass__``) inner metaclass of 'BracketCallable'.
+
+The effect of 'BracketCallable.__metaclass__' is the following: it makes
+'BracketCallable' and its descendants callable with brackets. Since
+the 'returnclass' flag is set, ``__getitem__`` returns the class
+with an attribute 'bracket_args' containing the tuple of the passed
+arguments (otherwise it returns an instance of the class).
+This works since when
+Python encounters an expression of kind ``cls[arg]`` it interprets it
+as ``type(cls).__getitem__(cls,arg)``. Therefore, if ``cls`` is a subclass
+of 'BracketCallable', this means that
+
+ ::
+
+ cls[arg] <=> BracketCallable.__metaclass__.__getitem__(cls,arg)
+
+Let me give few examples:
+
+ >>> from oopp import BracketCallable
+ >>> type(BracketCallable)
+ <class 'oopp.__metaclass__'>
+ >>> print type(BracketCallable).__name__ # not really anonymous
+ __metaclass__
+ >>> print BracketCallable['a1'].bracket_args
+ ('a1',)
+ >>> print BracketCallable['a1','a2'].bracket_args
+ ('a1', 'a2')
+
+This syntactical feature is an example of a thing that can be done
+*trough metaclasses only*: it cannot be emulated by functions.
+
+
+Anonymous inner metaclasses are the least verbose manner
+of defining metamethods. Moreover, they are a neat trick to define
+mix-in classes that, when inherited, can metamagically enhance
+an entire multiple inheritance hierarchy.
+
+In the previous example ``__getitem__`` is noncooperative, but nothing
+forbids anonymous inner metaclasses from being made cooperative. However,
+there is some subtlety one must be aware of.
+Let me give an example. My 'WithCounter' class counts how many instances
+of 'WithCounter' and its subclasses are generated. However, it does not
+distinguishes bewteen different subclasses.
+This was correct in the pizza shop example, simple only the total
+number of produced pizzas mattered, however, in other situations,
+one may want to reset the counter each time a new subclass is created.
+This can be done automagically by a cooperative inner metaclass:
+
+ ::
+
+ class WithMultiCounter(WithCounter):
+ """Each time a new subclass is derived, the counter is reset"""
+ class __metaclass__(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.__this,cls).__init__(*args)
+ reflective(__metaclass__)
+
+Notice that the order of execution of this code is subtle:
+
+1) first, the fact that WithMulticounter has a non-trivial metaclass is
+ registered, but nothing else is done;
+2) then, the line ``reflective(__metaclass__)`` is executed: this means
+ that the inner metaclass (and therefore its instances) get an
+ attribute ``._metaclass__this`` containing a reference to the
+ inner metaclass;
+3) then, the outer class is passed to its inner metaclass and created
+ by the inherited metaclass' ``__new__`` method;
+4) at this point ``cls`` exists and ``cls.__this`` is inherited from
+ ``__metaclass__._metaclass__this``; this means that the expression
+ ``super(cls.__this,cls).__init__(*args)`` is correctly recognized and
+ 'WithMultiCounter' can be initialized;
+5) only after that, the name 'WithMultiCounter' enters in the global namespace
+ and can be recognized.
+
+Notice in particular that inside ``super``, we could also
+use ``cls.__metaclass__`` instead of ``cls.__this``, but this
+would not work inside ``__new__``, whereas ``__this`` would be
+recognized even in ``__new__``.
+
+ >>> from oopp import *
+ >>> print MRO(WithMultiCounter)
+ 1 - WithMultiCounter(WithCounter)[__metaclass__]
+ 2 - WithCounter(object)
+ 3 - object()
+
+For sake of readability, often it is convenient
+to give a name even to inner classes:
+
+::
+
+ #<oopp.py>
+
+ class WithMultiCounter(WithCounter):
+ """Each time a new subclass is derived, the counter is reset"""
+ class ResetsCounter(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.ResetsCounter,cls).__init__(*args)
+ __metaclass__=ResetsCounter
+
+ #</oopp.py>
+
+Notice that inside super we used the expression ``cls.ResetsCounter`` and
+not ``WithMultiCounter.ResetsCounter``: doing that would generate a
+``NameError: global name 'WithMultiCounter' is not defined`` since at the
+time when ``ResetsCounter.__init__`` is called for the first time,
+the class ``WithMultiCounter`` exists but is has not yet entered the global
+namespace: this will happens only after the initialization in the
+``ResetsCounter`` metaclass, as we said before.
+
+Without the metaclass one can reset the counter by hand each time, or
+can reset the counter on all the classes of the hierarchy with a
+convenient function (akin to the 'traceH' routine defined in chapter 6).
+
+Example:
+
+ >>> from oopp import *
+ >>> class GrandFather(WithMultiCounter): pass
+ >>> class Father(GrandFather): pass
+ >>> class Child(Father): pass
+ >>> GrandFather()
+ <__main__.GrandFather object at 0x402f7f6c> # first GrandFather instance
+ >>> Father()
+ <__main__.Father object at 0x402f79ec> # first Father instance
+ >>> Father()
+ <__main__.Father object at 0x402f7d4c> # second Father instance
+ >>> Child.counter # zero instances
+ 0
+ >>> Father.counter # two instances
+ 2
+ >>> GrandFather.counter # one instance
+ 1
+
+I leave as an exercise for the reader to show that the original 'WithCounter'
+would fail to count correctly the different subclasses and would put the
+total number of instances in 'Child'.
+
+Passing parameters to (meta) classes
+-------------------------------------------------------------------------
+
+Calling a class with brackets is a way of passing parameters to it (or
+to its instances, if the 'returnclass' flag is not set).
+There additional ways for of doing that.
+One can control the instantiation syntax of classes by redefining the
+``__call__`` method of the metaclass. The point is that when we instantiate
+an object with the syntax ``c=C()``, Python looks
+at the ``__call__`` method of the metaclass of 'C'; the default behaviour
+it is to call ``C.__new__`` and ``C.__init__`` in succession, however, that
+behavior can be overridden. Let me give an example without using
+anonymous metaclasses (for sake of clarity only).
+
+ ::
+
+ #<metacall.py>
+
+ class M(type): # this is C metaclass
+ def __call__(cls):
+ return "Called M.__call__"
+
+ C=M('C',(),{}) # calls type(M).__call__
+ c=C() # calls type(C).__call__
+ # attention: c is a string!
+ print c #=> Called M.__call__
+
+ #</metacall.py>
+
+In this example, ``M.__call__`` simply
+returns the string ``Called M.__call__``, and the class
+'C' is *not* instantiated. Overriding the metaclass
+``__call__ `` method therefore provides another way to implement
+the ``Singleton`` pattern. However, savage overridings as the one in
+this example, are not a good idea, since it will confuse everybody.
+This is an example where metaclasses change the semantics: whereas
+usually the notation ``C()`` means "creates a C instance", the
+metaclass can give to the syntax ``C()`` any meaning we want.
+Here there is both the power and the danger of metaclasses: they
+allows to make both miracles and disasters. Nevertheless, used with
+a grain of salt, they provide a pretty nice convenience.
+
+Anyway, overriding the '__call__' method of the metaclass can be
+confusing, since parenthesis are usually reserved to mean instantion,
+therefore I will prefere to pass arguments trough brackets.
+
+The beauty and the magic of metaclasses stays in the fact that this mechanism
+is completely general: since metaclasses themselves are classes, we can
+'CallableWithBrackets' to pass arguments to a metaclass, i.e.
+'CallableWithBrackets' can also be used as a meta-metaclass!
+
+I leave as an exercise for the reader to figure out
+how to define meta-meta-metaclasses, meta-meta-meta-metaclasses, etc.
+etc. (there is no limit to the abstraction level you can reach with
+metaclasses;-)
+
+Let me show an example: a magical way of making methods cooperative.
+This can be done trough a 'Cooperative' metaclass that inherits from
+'BracketCallable' and therefore has 'BracketCallable.__metaclass__'
+as (meta)metaclass:
+
+ ::
+
+ #<oopp.py>
+
+ class Cooperative(BracketCallable,type):
+ """Bracket-callable metaclass implementing cooperative methods. Works
+ well for plain methods returning None, such as __init__"""
+ def __init__(cls,*args):
+ methods=cls.bracket_args
+ for meth in methods:
+ setattr(cls,meth,cls.coop_method(meth,vars(cls).get(meth)))
+ def coop_method(cls,name,method): # method can be None
+ """Calls both the superclass method and the class method (if the
+ class has an explicit method). Implemented via a closure"""
+ def _(self,*args,**kw):
+ getattr(super(cls,self),name)(*args,**kw) # call the supermethod
+ if method: method(self,*args,**kw) # call the method
+ return _
+
+ #</oopp.py>
+
+The code above works for methods returing ``None``, such as ``__init__``.
+Here I give a first example of application: a hierarchy where the ``__init__``
+methods are automatically called (similar to automatic initialization
+in Java).
+
+ ::
+
+ #<cooperative.py>
+
+ from oopp import Cooperative
+
+ class B(object):
+ """Cooperative base class; all its descendants will automagically
+ invoke their ancestors __init__ methods in chain."""
+ __metaclass__=Cooperative['__init__']
+ def __init__(self,*args,**kw):
+ print "This is B.__init__"
+
+ class C(B):
+ "Has not explicit __init__"
+
+ class D(C):
+ """The metaclass makes D.__init__ to call C.__init__ and
+ therefore B.__init__"""
+ def __init__(self,*args,**kw):
+ print "This is D.__init__"
+
+ d=D()
+
+ print "The metaclass of B is",type(B)
+ print "The meta-metaclass of B is", type(type(B))
+
+ #</cooperative.py>
+
+Output:
+
+ ::
+
+ This is B.__init__
+ This is D.__init__
+ The metaclass of B is <class 'oopp.Cooperative'>
+ The meta-metaclass of B is <class 'oopp.__metaclass__'>
+
+A second example, is the following, an alternative way of
+making the paleoanthropological hierarchy of chapter 4 cooperative:
+
+ ::
+
+ #<paleo.py>
+
+ from oopp import Cooperative,Homo
+
+ class HomoHabilis(Homo):
+ __metaclass__=Cooperative['can']
+ def can(self):
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ print " - make art"
+
+ HomoSapiensSapiens().can()
+
+ # Output:
+
+ # <HomoSapiensSapiens> can:
+ # - make tools
+ # - make abstractions
+ # - make art
+
+ #</paleo.py>
+
+Metaclasses can be used to violate the old good rule "explicit is
+better than implicit". Looking at the source code for 'HomoSapiens'
+and 'HomoSapiensSapiens' one would never imagine the ``can`` is
+somewhat special. That is why in the following I will prefer to
+use the anonymous super call mechanism, which is explicit, instead
+of the implicit cooperative mechanism.
+
+Meta-functions
+---------------------------------------------------------------------
+
+The third and deepest secret of the ``__metaclass__`` hook is that, even if
+it is typically used in conjunction with metaclasses, actually the hook
+can refer to generic class factories callable with the signature
+``(name,bases,dic)``. Let me show a few examples
+where ``__metaclass__`` is a function or a generic callable object
+instead of being a metaclass:
+
+ ::
+
+ #<metafun.py>
+
+ from oopp import kwdict
+
+ class Callable(object):
+ def __call__(self,name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+ callableobj=Callable()
+
+ class C: __metaclass__=callableobj
+
+ print "type of C:",C.__class__
+
+ def f(name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+ class D: __metaclass__=f
+
+ print "type of D:",D.__class__
+
+ class B(object):
+ def __metaclass__(name,bases,dic):
+ """In this form, the __metaclass__ attribute is a function.
+ In practice, it works as a special static method analogous
+ to __new__"""
+ print "name: ", name
+ print "bases:", bases
+ print "dic:\n",kwdict(dic)
+ return type(name,bases,dic)
+
+ class E(B): pass
+
+ print "type of E:",E.__class__
+ print "Non-called E.__metaclass__:", E.__metaclass__
+
+ #</metafun.py>
+
+With output
+
+ ::
+
+ C ()
+ __metaclass__ = <Callable object at 0x401c964c>
+ __module__ = __builtin__
+ type of C: <type 'type'>
+ D ()
+ __metaclass__ = <function f at 0x401c4994>
+ __module__ = __builtin__
+ type of D: <type 'type'>
+ name: B
+ bases: (<type 'object'>,)
+ dic:
+ __metaclass__ = <function __metaclass__ at 0x401c4a3c>
+ __module__ = __builtin__
+ type of E: <type 'type'>
+ Non-called E.__metaclass__: <unbound method E.__metaclass__>
+
+The advantage/disadvantage of this solution is that the ``__metaclass__``
+hook is called only once, i.e. it is not called again if a new class
+is derived from the original one. For instance in this example 'E' is
+derived from 'B', but the function ``B.__metaclass__`` is *not* called
+during the creation of 'E'.
+
+Metafunctions can also be used when one does not want to transmit the
+metaclass contraint. Therefore they usage is convenient in exactly
+the opposite situation of a cooperative metaclass.
+
+Anonymous cooperative super calls
+-----------------------------------------------------------------------
+
+As I noticed in the previous chapters, the ``super``
+mechanism has an annoying
+problem: one needs to pass explicitely the name of the base class. Typically,
+this is simply an
+inelegance since it is annoying to be forced to retype the name of the base
+class. However, in particular
+cases, it can be a problem. This happens for instance if we try to
+pass the class's methods to a different class: one cannot do that,
+since the methods contains an explicit reference to the original class
+and would not work with the new one. Moreover, having named super calls
+is annoying in view of refactoring. Consider for
+instance the previous ``supernew.py`` script: in the ``__new__`` method
+defined inside the class 'B', we called ``Super`` with the syntax
+``Super(B,cls)`` by repeating the name of the class 'B'. Now,
+if in the following I decide to give to 'B' a more descriptive
+name, I have to go trough the source, search all the ``super``
+calls, and change them accordingly to the new name. It would be
+nice having Python do the job for me. A first solution is to call
+``super`` (or ``Super``) with the syntax ``super(self.__this,obj)``,
+where the special name ``__this`` is explicitly replaced by the name
+of the class where the call is defined by the 'reflective' function
+of last chapter. This approach has the disadvantage that each time we
+derive a new class, we need to invoke *explicitely* the routine
+``reflective``. It would be marvelous to instruct Python to invoke
+``reflective`` automatically at each class creation. Actually, this
+seems to be deep magic and indeed it is: fortunately, a custom metaclass
+can perform this deep magic in few lines:
+
+ ::
+
+ #<oopp.py>
+
+ class Reflective(type):
+ """Cooperative metaclass that defines the private variable __this in
+ its instances. __this contains a reference to the class, therefore
+ it allows anonymous cooperative super calls in the class."""
+ def __init__(cls,*args):
+ super(Reflective,cls).__init__(*args)
+ reflective(cls)
+
+ #</oopp.py>
+
+Now, let me show how 'Reflective' can be used in a practical example.
+
+By deriving new metaclasses from 'Reflective', one can easily
+create powerful class factories that generate reflective classes.
+
+Suppose I want to define a handy class
+factory with the abilitity of counting the number of its instances.
+
+This can be done by noticing that metaclasses are just classes, therefore
+they can be composed with regular classes in multiple inheritance. In
+particular one can derive a 'Logged' metaclass from 'WithLogger': in
+this way we send a message to a log file each time a new class is created.
+This can be done by composing 'WithLogger' with 'WithMultiCounter.__metaclass__'
+and with 'Reflective':
+
+ ::
+
+ #<oopp.py>
+
+ class Logged(WithLogger,Reflective):
+ """Metaclass that reuses the features provided by WithLogger. In particular
+ the classes created by Logged are Reflective, PrettyPrinted
+ and Customizable.""" #WithLogger provides logfile and verboselog
+ def __init__(cls,*args,**kw):
+ super(Logged,cls).__init__(*args,**kw)
+ bases=','.join([c.__name__ for c in cls.__bases__])
+ print >> cls.logfile, "%s is a child of %s" % (cls,bases)
+ print >> cls.logfile,'and an instance of %s' % type(cls).__name__
+
+ #</oopp.py>
+
+The MRO is
+
+ >>> print MRO(Logged)
+ MRO of Logged:
+ 0 - Logged(WithLogger,Reflective)
+ 1 - WithLogger(WithCounter,PrettyPrinted)
+ 2 - WithCounter(object)
+ 3 - PrettyPrinted(object)
+ 4 - Reflective(type)
+ 5 - type(object)
+ 6 - object()
+
+and the inheritance graph can be drawn as follows:
+
+ ::
+
+
+ _____________________ object 6 ___
+ / / \
+ 2 WithCounter 3 PrettyPrinted type 5
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ 1 WithLogger Reflective 4
+ \ /
+ \ /
+ \ /
+ \ /
+ Logged 0
+ :
+ :
+ C1
+
+'WithCounter' acts now as a metaclass, since WithCounter.__new__ invokes
+type.__new__. Since ``type.__new__`` is non-cooperative,
+in the composition of a metaclass with a regular class, the metaclass
+should be put first: this guarantees that ``__new__`` derives from
+``type.__new__``, thus avoiding the error message.
+
+ >>> Logged.verboselog=True
+ >>> C1=Logged('C1',(),{})
+ *****************************************************************************
+ Tue Apr 22 18:47:05 2003
+ 1. Created 'C1'
+ with accessibile non-special attributes:
+ _C1__this = 'C1'
+ 'C1' is a child of object
+ and an instance of Logged
+
+Notice that any instance of 'WithCounterReflective' inherits the 'WithCounter'
+attribute ``counter``, that counts the number of classes that have been
+instantiated (however it is not retrieved by ``dir``; moreover the
+instances of 'WithCounterReflective' instances have no ``counter`` attribute).
+
+ >>> C1.counter
+ 1
+
+More on metaclasses as class factories
+----------------------------------------------------------------------------
+
+A slight disadvantage of the approach just described,
+is that 'Logged' cooperatively invokes the ``type.__new__``
+static method, therefore, when we invoke the metaclass, we must explicitly
+provide a name, a tuple of base classes and a dictionary, since the
+``type.__new__`` staticmethod requires that signature. Actually,
+the expression
+
+ ::
+
+ C=Logged(name,bases,dic)
+
+is roughly syntactic sugar for
+
+ ::
+
+ C=Logged.__new__(Logged,name,bases,dic)
+ assert isinstance(C,Logged)
+ Logged.__init__(C,name,bases,dic)
+
+If a different interface is desired, the best way is to use a class
+factory 'ClsFactory' analogous to the object factory 'Makeobj'
+defined in chapter 4. It is convenient to make 'ClsFactory'
+bracket-callable.
+
+ ::
+
+ #<oopp.py>
+
+ class ClsFactory(BracketCallable):
+ """Bracket callable non-cooperative class acting as
+ a factory of class factories.
+
+ ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+ . They automatically converts functions to static methods
+ if the input object is not a class. If an explicit name is not passed
+ the name of the created class is obtained by adding an underscore to
+ the name of the original object."""
+
+ returnclass=False # ClsFactory[X] returns an *instance* of ClsFactory
+
+ def __call__(self, *args):
+ """Generates a new class using self.meta and avoiding conflicts.
+ The first metaobject can be a dictionary, an object with a
+ dictionary (except a class), or a simple name."""
+
+ # default attributes
+ self.name="CreatedWithClsFactory"
+ self.bases=()
+ self.dic={}
+ self.metas=self.bracket_args
+
+ if len(args)==1:
+ arg=args[0]
+ if isinstance(arg,str): # is a name
+ self.name=arg
+ elif hasattr(arg,'__name__'): # has a name
+ self.name=arg.__name__+'_'
+ self.setbasesdic(arg)
+ elif len(args)==2:
+ self.name=args[0]
+ assert isinstance(self.name,str) # must be a name
+ self.setbasesdic(args[1])
+ elif len(args)==3: # must be name,bases,dic
+ self.name=args[0]
+ self.bases+=args[1]
+ self.dic.update(args[2])
+ if len(args)<3 and not self.bases: # creating class from a non-class
+ for k,v in self.dic.iteritems():
+ if isfunction(v): self.dic[k]=staticmethod(v)
+ #return child(*self.bases,**vars(self))
+ return makecls(*self.metas)(self.name,self.bases,self.dic)
+
+ def setbasesdic(self,obj):
+ if isinstance(obj,tuple): # is a tuple
+ self.bases+=obj
+ elif hasattr(obj,'__bases__'): # is a class
+ self.bases+=obj.__bases__
+ if isinstance(obj,dict): # is a dict
+ self.dic.update(obj)
+ elif hasattr(obj,"__dict__"): # has a dict
+ self.dic.update(obj.__dict__)
+
+ #</oopp.py>
+
+'ClsFactory[X]' where 'X' is a metaclass returns callable objects acting as
+class factories. For instance
+
+ ::
+
+ #<oopp.py>
+
+ Class=ClsFactory[type] # generates non-conflicting classes
+ Mixin=ClsFactory[Reflective] # generates reflective classes
+
+ #</oopp.py>
+
+can be used as a class factories that automatically provides a default name,
+base classes and dictionary, and avoids meta-type conflicts.
+'Mixin' generates reflective classes that can be used as mixin in multiple
+inheritance hierarchies. Here I give few example of usage of 'Class':
+
+ >>> from oopp import *
+ >>> C1,C2,C3=[Class('C'+str(i+1)) for i in range(3)]
+ >>> C1
+ <class 'oopp.C1'>
+ >>> C2
+ <class 'oopp.C2'>
+ >>> C3
+ <class 'oopp.C3'>]
+
+ >>> Clock=Class('Clock',{'get_time':get_time})
+ >>> Clock
+ <class 'oopp.Clock'>
+ >>> Clock.get_time()
+ 16:01:02
+
+Another typical usage of 'Class' is the conversion of a module in a class:
+for instance
+
+ >>> time_=Class(time)
+ >>> time_
+ <class 'oopp.time_'>
+
+Notice the convention of adding an underscore to the name of the class
+generated from the 'time' module.
+
+ >>> time_.asctime()
+ 'Mon Jan 20 16:33:21 2003'
+
+Notice that all the functions in the module ``time`` has been magically
+converted in staticmethods of the class ``time_``. An advantage of this
+approach is that now the module is a class and can be enhanced with
+metaclasses: for instance we could add tracing capabilities, debugging
+features, etc.
+
+By design, 'Class' and 'Reflective' also works when the first argument
+is a class or a tuple of base classes:
+
+ >>> ClsFactory_=Class(ClsFactory)
+ >>> type(ClsFactory_)
+ <class 'oopp.__metaclass__'>
+ >>> ClsFactory_=Mixin(ClsFactory)
+ >>> type(ClsFactory_) # automagically generated metaclass
+ <class 'oopp._Reflective__metaclass__'>
+
+Programming with metaclasses
+--------------------------------------------------------------------------
+In order to how a non-trivial application of metaclasses in real life,
+let me come back to the pizza shop example discussed in chapter 4 and 6.
+
+ ::
+
+ #<oopp.py>
+
+ def Pizza(toppings,**dic):
+ """This function produces classes inheriting from GenericPizza and
+ WithLogger, using a metaclass inferred from Logged"""
+ toppinglist=toppings.split()
+ name='Pizza'+''.join([n.capitalize() for n in toppinglist])
+ dic['toppinglist']=toppinglist
+ return ClsFactory[Logged](name,
+ (GenericPizza,WithLogger,WithMultiCounter),dic)
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> Margherita=Pizza('tomato mozzarella',verboselog=True)
+ *****************************************************************************
+ Tue May 13 14:42:17 2003
+ 1. Created 'PizzaTomatoMozzarella'
+ with accessibile non-special attributes:
+ ResetsCounter = <class 'oopp.ResetsCounter'>
+ _GenericPizza__this = <class 'oopp.GenericPizza'>
+ _WithCounter__this = <class 'oopp.WithCounter'>
+ _WithLogger__this = <class 'oopp.WithLogger'>
+ baseprice = 1
+ counter = 0
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <unbound method PizzaTomatoMozzarella.price>
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato', 'mozzarella']
+ toppings_price = <unbound method PizzaTomatoMozzarella.toppings_price>
+ verboselog = True
+ 'PizzaTomatoMozzarella' is a child of GenericPizza,WithLogger,
+ WithMultiCounter and an instance of _LoggedResetsCounter
+
+Notice the *deep* magic: ``Pizza`` invokes ``ClsFactory[Logged]`` which in
+turns calls the class factory ``child`` that creates 'Margherita' from
+'GenericPizza', 'WithLogger' and 'WithMultiCounter' by using the
+metaclass 'Logged': however, since 'WithMultiCounter', has the internal
+metaclass 'ResetsCounter' , there is a metatype conflict:
+``child`` *automagically* solves the conflict by creating the metaclass
+'_LoggedResetsCounter' that inherits both from 'Logged' and 'ResetsCounter'.
+At this point, 'Margherita' can be safely created
+by '_LoggedResetsCounter'. As such, the creation of 'Margherita'
+will be registered in the log file and 'Margherita' (with all its
+children) will continue to be able to recognize the special identifier
+``this``.
+
+ >>> print Margherita('large')
+ *****************************************************************************
+ Tue May 13 14:47:03 2003
+ 1. Created large pizza with tomato,mozzarella, cost $ 6.0
+ with accessibile non-special attributes:
+ ResetsCounter = <class 'oopp.ResetsCounter'>
+ _GenericPizza__this = <class 'oopp.GenericPizza'>
+ _WithCounter__this = <class 'oopp.WithCounter'>
+ _WithLogger__this = <class 'oopp.WithLogger'>
+ baseprice = 1
+ counter = 1
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <bound method PizzaTomatoMozzarella.price of
+ <oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+ size = large
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato', 'mozzarella']
+ toppings_price = <bound method PizzaTomatoMozzarella.toppings_price of
+ <oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+ verboselog = True
+ large pizza with tomato,mozzarella, cost $ 6.0
+ >>> print MRO(Margherita)
+ MRO of PizzaTomatoMozzarella:
+ 0 - PizzaTomatoMozzarella(GenericPizza,WithLogger)[_LoggedResetsCounter]
+ 1 - GenericPizza(object)
+ 2 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 3 - WithMultiCounter(WithCounter)[ResetsCounter]
+ 4 - WithCounter(object)
+ 5 - PrettyPrinted(object)
+ 6 - object()
+
+Notice that
+
+ >>> print Margherita
+ 'PizzaTomatoMozzarella'
+
+The power of inheritance in this example is quite impressive, since
+I have reused the same class 'WithLogger' (and its children) both in the
+metaclass hierarchy and in the regular hierarchy: this means that I have added
+logging capabilities both to classes and their instances in a
+strike! And there is no confusion between the two. For instance,
+there is a ``counter`` attribute for the metaclass 'Logged'
+and many independent ``counter`` attributes for any generated class,
+i.e. for any kind of pizza.
+
+ It is interesting to notice that '' itself is an instance of
+ its inner metaclass, as ``type()`` would show. This technique
+ avoids the need for inventing a new name for the metaclass. The inner
+ metaclass is automatically inherited by classes inheriting from the outer
+ class.
+
+Metaclass-aided operator overloading
+---------------------------------------------------------------------------
+
+As we discussed in chapter 4, inheriting from built-in types is generally
+painful. The problem is that if P is a primitive class, i.e. a
+Python built-in type, and D=D(P) is a derived class, then the
+primitive methods returning P-objects have to be modified (wrapped) in
+such a way to return D-objects.
+
+The problem is expecially clear in the context of operator overloading.
+
+Consider for instance the problem of defining a 'Vector' class in the
+mathematical sense. Mathematically-speaking, vectors are defined as objects
+that can be summed each other and multiplied by numbers; they can be represented
+by (finite or infinite) sequences. In the case of finite sequences, vectors
+can be represented with lists and a vector class can be naturally
+implemented by subclassing ``list``:
+
+ ::
+
+ #<vector.py>
+
+ class Vector(list):
+ """Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)"""
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [el*scalar for el in self]
+ def __rmul__(self,scalar):
+ return [scalar*el for el in self]
+
+ v=Vector([1,0])
+ w=Vector([0,1])
+
+ print v+w, type(v+w)
+ print 2*v, type(2*v)
+ print v*2, type(v*2)
+
+ #</vector.py>
+
+With output
+
+ ::
+
+ [1, 1] <type 'list'>
+ [2, 0] <type 'list'>
+ [2, 0] <type 'list'>
+
+The problem is that the overloaded methods must be wrapped in such a way
+to return ``Vector`` object and not ``list`` object; moreover, if
+``Vector`` is subclassed (for instance by defining a ``NumericVector``),
+the overloaded methods must return instances of the subclass. There is
+only one way of doing that automatically: trough the magic of metaclasses.
+
+Here is the solution, involving an ``autowrappedmethod`` descriptor class,
+that wraps the overloaded operators and is automatically invoked by
+the metaclass ``AutoWrapped``.
+
+ ::
+
+ #<oopp.py>
+
+ class autowrappedmethod(wrappedmethod):
+ """Makes the method returning cls instances, by wrapping its
+ output with cls"""
+ klass=None # has to be fixed dynamically from outside
+ def __init__(self,meth):
+ super(autowrappedmethod,self).__init__(meth) # cooperative
+ self.klass=self.klass # class variable -> instance variable
+ def wrapper(self): # closure
+ return lambda *args,**kw: self.klass(self.func(*args,**kw))
+
+ class AutoWrapped(type):
+ """Metaclass that looks at the methods declared in the attributes
+ builtinlist and wraplist of its instances and wraps them with
+ autowrappedmethod."""
+ def __init__(cls,name,bases,dic):
+ super(AutoWrapped,cls).__init__(name,bases,dic) # cooperative
+ cls.builtinlist=getattr(cls,'builtinlist',[])
+ if not hasattr(cls,'diclist') : # true only at the first call
+ cls.diclist=[(a,vars(bases[0])[a]) for a in cls.builtinlist]
+ if dic.has_key('wraplist'): # can be true at any call
+ cls.diclist+=[(a,dic[a]) for a in cls.wraplist]
+ wrapper=autowrappedmethod.With(klass=cls)
+ d=dict([(a,wrapper(v)) for a,v in cls.diclist])
+ customize(cls,**d)
+
+ #</oopp.py>
+
+Now the ``Vector`` class can be written as
+
+ ::
+
+ #<oopp.py>
+
+ class Vector(list):
+ """Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)"""
+ __metaclass__=AutoWrapped
+ wraplist='__add__ __radd__ __mul__ __rmul__'.split()
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [scalar*el for el in self]
+ def __rmul__(self,scalar):
+ return [el*scalar for el in self]
+
+ #</oopp.py>
+
+Here the ``AutoWrapped`` metaclass wraps the output of ``__add__,
+__radd__, __mul__, __rmul__``, guaranteeing that they returns ``Vector``
+instances or instances of some subclass of ``Vector``, if ``Vector`` is
+subclassed. This is an example of usage:
+
+ .. doctest
+
+ >>> from oopp import Vector
+ >>> v=Vector([1,0])
+ >>> v
+ <oopp.Vector object at 0x4032858c>
+ >>> w=Vector([0,1])
+ >>> v+2*w
+ <oopp.Vector object at 0x403190ac>
+ >>> print v+2*w
+ [1, 2]
+
+It should be clear by now that metaclasses are the natural framework where
+to discuss operator overloading
+(at least in languages that have metaclasses ;-). After all, operator
+overloading is another kind of (very nice) syntactic sugar and we know
+already that metaclasses are very good when we need syntactic sugar.
+
+
+ADVANCED METAPROGRAMMING TECHNIQUES
+========================================================================
+
+In elementary OOP, the programmer works with objects; in advanced OOP,
+the programmer works with classes, taking full advantage of
+(multiple) inheritance and metaclasses. Metaprograming is the activity of
+building, composing and modifying classes.
+
+I will give various examples of metaprogramming techniques
+using run-time class modifications
+multiple inheritance, metaclasses, attribute descriptors and
+even simple functions.
+
+Moreover, I will show show metaclasses can change the
+semantics of Python programs: hence theire reputation of *black* magic.
+That is to say that the techniques explained here are dangerous!
+
+On code processing
+--------------------------------------------------------------------
+
+It is a good programming practice to avoid the direct modification
+of source code. Nevertheless, there are situations where the ability of
+modifying the source code *dynamically* is invaluable. Python has the
+capability of
+
+1) generating new code from scratch;
+
+2) modifying pre-existing source code;
+
+3) executing the newly created/modified code at run-time.
+
+The capability of creating source code and executing it *immediately* has
+no equivalent in static languages such as C/C++/Java and it is maybe the
+most poweful feature of dynamics languages such as Java/Python/Perl.
+This feature has been exploited to its ultimate consequences in the languages
+of the Lisp family, in which one can use incredibly poweful macros, which
+in a broad sense, are programs that write themselves
+
+In this chapter I will discuss how to implement macros in Python and I will
+present some of the miracles you may perform with this technique. To this
+aim, I will discuss various ways of manipulating Python source code, by
+using regular expressions and state machines.
+
+
+Regular expressions
+-------------------------------------------------------------------------
+
+ .. line-block::
+
+ *Some people, when confronted with a problem,
+ think "I know, I'll use regular expressions."
+ Now they have two problems.*
+ -- Jamie Zawinski
+
+
+Python source code is a kind of text and can manipulated with the same
+techniques that are used to manipulate text:
+
+1. the trivial search and replace;
+
+2. regular expressions;
+
+3. state machines;
+
+4. parsers
+
+There is not very much to say about the search and replace methods: it
+is fast, efficient and it works. It should always be used whenever
+possible. However, in this chapter I will only be interested in cases
+where something more sophisticated than a plain search and replace is
+needed. Cases that can be managed with regular expressions
+or with something even more sophisticated than them: a state machine or
+even a full featured parser.
+
+
+I will *not* give a primer on regular expression here, since they are
+already well documented in the standard documentation (see Andrew's
+Kuchling 'Howto') as well in many books (for instance 'Mastering Regular
+Expression' first edition and 'Python in a Nutshell').
+Instead, I will give various practical examples of usage.
+
+ >>> import re
+ >>> reobj=re.compile(r'x')
+
+
+More on metaclasses and subclassing built-in types
+-------------------------------------------------------------------------
+
+Subclassing ``list`` is easy since there are no methods returning lists
+except the methods correspondings to the '+' and '*' operators.
+Subclassing ``str`` is more complicated, since one has many methods
+that return strings. Nevertheless, it can be done with the ``AutoWrapped``
+metaclass, simply by specifying the list of the builtins to be wrapped.
+
+ ::
+
+ #<oopp.py>
+
+ class Str(str):
+ __metaclass__=AutoWrapped
+ builtinlist="""__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
+ center expandtabs join ljust lower lstrip replace rjust rstrip strip
+ swapcase title translate upper zfill""".split()
+
+ #</oopp.py>
+
+Here I show various tests.
+
+ .. doctest
+
+ >>> from oopp import Str
+ >>> sum=Str('a')+Str('b') # check the sum
+ >>> print sum, type(sum)
+ ab <class 'oopp.Str'>
+ >>> rprod=Str('a')*2 # check the right product
+ >>> print rprod,type(rprod)
+ aa <class 'oopp.Str'>
+ >>> lprod=2*Str('a') # check the left product
+ >>> print lprod,type(lprod)
+ aa <class 'oopp.Str'>
+ >>> r=Str('a').replace('a','b') # check replace
+ >>> print r,type(r)
+ b <class 'oopp.Str'>
+ >>> r=Str('a').capitalize() # check capitalize
+ >>> print r,type(r)
+ A <class 'oopp.Str'>
+
+``Str`` acts as a nice base class to built abstractions based on strings.
+In particular, regular expressions can be built on top of strings describing
+their representation (I remind that if ``x`` is a regular expression object,
+``x.pattern`` is its string representation). Then, the sum of two regular
+expressions ``x`` and ``y`` can be defined as the sum of their string
+representation, ``(x+y).pattern=x.pattern+y.pattern``. Moreover, it is
+convenient to define the ``__or__`` method of two regular expression in
+such a way that ``(x | y).pattern=x.pattern+'|'+y.pattern``.
+All this can be achieved trough the following class:
+
+ ::
+
+ #<oopp.py>
+
+ class BaseRegexp(Str):
+
+ builtinlist=['__radd__', '__ror__']
+ wraplist=['__add__','__or__']
+
+ __add__ = lambda self,other: self.pattern + other
+ __or__ = lambda self,other: self.pattern+'|'+other
+
+ def __init__ (self,regexp):
+ "Adds to str methods the regexp methods"
+ reobj=re.compile(regexp)
+ for attr in dir(reobj)+['pattern']:
+ setattr(self,attr,getattr(reobj,attr))
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> aob=BaseRegexp('a')|BaseRegexp('b'); print aob
+ a|b
+ >>> print pretty(attributes(aob))
+ encode = <built-in method encode of BaseRegexp object at 0x401b25cc>
+ endswith = <built-in method endswith of BaseRegexp object at 0x401b25cc>
+ expandtabs = <function _ at 0x401b69cc>
+ find = <built-in method find of BaseRegexp object at 0x401b25cc>
+ findall = <built-in method findall of _sre.SRE_Pattern object at 0x4019b890>
+ finditer = <built-in method finditer of _sre.SRE_Pattern object at
+ 0x4019b890>
+ index = <built-in method index of BaseRegexp object at 0x401b25cc>
+ isalnum = <built-in method isalnum of BaseRegexp object at 0x401b25cc>
+ isalpha = <built-in method isalpha of BaseRegexp object at 0x401b25cc>
+ isdigit = <built-in method isdigit of BaseRegexp object at 0x401b25cc>
+ islower = <built-in method islower of BaseRegexp object at 0x401b25cc>
+ isspace = <built-in method isspace of BaseRegexp object at 0x401b25cc>
+ istitle = <built-in method istitle of BaseRegexp object at 0x401b25cc>
+ isupper = <built-in method isupper of BaseRegexp object at 0x401b25cc>
+ join = <function _ at 0x401b6a74>
+ ljust = <function _ at 0x401b6b1c>
+ lower = <function _ at 0x401b6cdc>
+ lstrip = <function _ at 0x401b6d84>
+ match = <built-in method match of _sre.SRE_Pattern object at 0x4019b890>
+ pattern = ba
+ replace = <function _ at 0x401b6ed4>
+ rfind = <built-in method rfind of BaseRegexp object at 0x401b25cc>
+ rindex = <built-in method rindex of BaseRegexp object at 0x401b25cc>
+ rjust = <function _ at 0x401ba0d4>
+ rstrip = <function _ at 0x401ba10c>
+ scanner = <built-in method scanner of _sre.SRE_Pattern object at 0x4019b890>
+ search = <built-in method search of _sre.SRE_Pattern object at 0x4019b890>
+ split = <built-in method split of _sre.SRE_Pattern object at 0x4019b890>
+ splitlines = <built-in method splitlines of BaseRegexp object at 0x401b25cc>
+ startswith = <built-in method startswith of BaseRegexp object at 0x401b25cc>
+ strip = <function _ at 0x401ba25c>
+ sub = <built-in method sub of _sre.SRE_Pattern object at 0x4019b890>
+ subn = <built-in method subn of _sre.SRE_Pattern object at 0x4019b890>
+ swapcase = <function _ at 0x401ba294>
+ title = <function _ at 0x401ba4fc>
+ translate = <function _ at 0x401ba534>
+ upper = <function _ at 0x401ba5dc>
+ wraplist = ['__add__', '__radd__', '__or__', '__ror__']
+ zfill = <function _ at 0x401ba614>
+
+ #<oopp.py>
+
+ class Regexp(BaseRegexp):
+ class __metaclass__(BaseRegexp.__metaclass__):
+ def __setattr__(cls,name,value):
+ if name==name.upper(): # all caps means regexp constant
+ if not isinstance(value,cls): value=cls(value)
+ value.name=name # set regexp name
+ BaseRegexp.__metaclass__.__setattr__(cls,name,value)
+ # basic setattr
+
+ def named(self,name=None):
+ name=getattr(self,'name',name)
+ if name is None: raise 'Unnamed regular expression'
+ return self.__class__('(?P<%s>%s)' % (name,self.pattern))
+
+ generateblocks=generateblocks
+
+ #</oopp.py>
+
+The magic of ``Regexp.__metaclass__`` allows to generate a library of
+regular expressions in an elegant way:
+
+ ::
+
+ #<oopp.py>
+
+ r=Regexp
+
+ customize(r,
+ DOTALL =r'(?s)' , # starts the DOTALL mode; must be at the beginning
+ NAME =r'\b[a-zA-Z_]\w*', # plain Python name
+ EXTNAME=r'\b[a-zA-Z_][\w\.]*', # Python name with or without dots
+ DOTNAME=r'\b[a-zA-Z_]\w*\.[\w\.]*',# Python name with (at least one) dots
+ COMMENT=r"#.*?(?=\n)", # Python comment
+ QUOTED1="'.+?'", # single quoted string '
+ QUOTED2='".+?"', # single quoted string "
+ TRIPLEQ1="'''.+?'''", # triple quoted string '
+ TRIPLEQ2='""".+?"""' # triple quoted string "
+ )
+
+ r.STRING=r.TRIPLEQ1|r.TRIPLEQ2|r.QUOTED1|r.QUOTED2
+ r.CODESEP=r.DOTALL+r.COMMENT.named()|r.STRING.named()
+
+ #</oopp.py>
+
+The trick is in the redefinition of ``__setattr__``, which magically converts
+all caps attributes in ``Regexp`` objects.
+
+The features of ``Regexp`` can be tested with the following code:
+
+ ::
+
+ #<test_re.py>
+
+ """This script looks at its own source code and extracts dotted names,
+ i.e. names containing at least one dot, such as object.attribute or
+ more general one, such as obj.attr.subattr."""
+
+ # Notice that dotted.names in comments and literal strings are ignored
+
+ from oopp import *
+ import __main__
+
+ text=inspect.getsource(__main__)
+
+ regexp=Regexp.CODESEP| Regexp.DOTNAME.named()
+
+ print 'Using the regular expression',regexp
+
+ print "I have found the following dotted names:\n%s" % [
+ MO.group() for MO in regexp.finditer(text) if MO.lastgroup=='DOTNAME']
+
+ #</test_re.py>
+
+with output:
+
+ ::
+
+ Using the regular expression (?s)(?P<COMMENT>#.*?(?=\n))|(?P<STRING>
+ '''.+?'''|""".+?"""|'.+?'|".+?")|(?P<DOTNAME>[a-zA-Z_]\w*\.[\w\.]*)
+ I have found the following dotted names:
+ ['inspect.getsource', 'Regexp.CODESEP', 'Regexp.DOTNAME.named', 'MO.group',
+ 'dotname.finditer', 'MO.lastgroup']
+
+Now one can define a good ``CodeStr`` class with replacing features
+
+Let me consider for instance the solution to the problem discussed in chapter
+4, i.e. the definition of a ``TextStr`` class able to indent and dedent
+blocks of text.
+
+ ::
+
+ #<oopp.py>
+
+ def codeprocess(code,TPO): # TPO=text processing operator
+ code=code.replace("\\'","\x01").replace('\\"','\x02')
+ genblock,out = Regexp.CODESEP.generateblocks(code),[]
+ for block in genblock:
+ out.append(TPO(block))
+ out.append(genblock.next())
+ return ''.join(out).replace("\x01","\\'").replace('\x02','\\"')
+
+ def quotencode(text):
+ return text.replace("\\'","\x01").replace('\\"','\x02')
+
+ def quotdecode(text):
+ return text.replace("\x01","\\'").replace('\x02','\\"')
+
+ #</oopp.py>
+
+Here is an example of usage: replacing 'Print' with 'print' except in
+comments and literal strings.
+
+ ::
+
+ #<codeproc.py>
+
+ from oopp import codeprocess
+
+ wrongcode=r'''
+ """Code processing example: replaces 'Print' with 'print' except in
+ comments and literal strings"""
+ Print "This program prints \"Hello World!\"" # look at this line!
+ '''
+
+ fixPrint=lambda s: s.replace('Print','print')
+ validcode=codeprocess(wrongcode,fixPrint)
+
+ print 'Source code:\n',validcode
+ print 'Output:\n'; exec validcode
+
+ #</codeproc.py>
+
+with output
+
+ ::
+
+ Source code:
+
+ """Code processing example: replaces 'Print' with 'print' except in
+ comments and literal strings"""
+ print "Prints \"Hello World!\"" # look at this line!
+
+ Output:
+
+ This program prints "Hello World!"
+
+A simple state machine
+---------------------------------------------------------------------------
+
+Regular expression, however powerful, are limited in scope since they
+cannot recognize recursive structures. For instance, they cannot parse
+parenthesized expression.
+
+The simplest way to parse a parenthesized expression is to use a state
+machine.
+
+ ::
+
+ (?:...) non-grouping
+ (?P<name>...)
+
+ (?=...) look-ahead
+ (?!...) negative
+ (?<=...) look-behind
+ (?<!...) negative
+
+ dec=\
+ """
+ paren = ( .. )
+ bracket = [ .. ]
+ brace = { .. }
+ comment = # .. \n
+ """
+
+ actions=\
+ """
+ reobj1 : R' .. (?<!\\)' -> Regexp(r''' .. ''')
+ reobj2 : R" .. (?<!\\)" -> Regexp(r""" .. """)
+ string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
+ string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
+ """
+
+ beg=0; end=1
+
+ string1[beg]=r"(?<!')'(?!')" # an isolated single quote
+ string2[beg]=r'(?<!")"(?!")' # an isolated double quote
+ string1[end]=r"(?<!\\)'(?!')" # ending single quote
+ string2[end]=r'(?<!\\)"(?!")' # ending double quote
+
+ reobj1[beg]=r"R'"
+ reobj2[beg]=r'R"'
+ reobj1[end]=string1[end] # ending single quote
+ reobj2[end]=string2[end] # ending double quote
+
+ actions=\
+ """
+ reobj1 : R' .. (?<!\\)' -> Regexp(r''' .. ''')
+ reobj2 : R" .. (?<!\\)" -> Regexp(r""" .. """)
+ string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
+ string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
+ """
+
+ beg={}; end={}; ls=[]
+ for line in decl.splitlines():
+ mode,rest=line.split(' : ')
+ s,r=rest.split(' -> ')
+
+ beg[mode],end[mode]=s.split(' .. ')
+ ls.append('(?P<beg_%s>%s)' % (mode,beg[mode]))
+ ls.append('(?P<end_%s>%s)' % (mode,end[mode]))
+
+ beg2[mode],end2[mode]=r.split(' .. ')
+ ls.append(beg2[mode])
+ ls.append(end2[mode])
+
+ delimiters='(%s)' % re.compile('|'.join(ls))
+ splitlist=['']+delimiters.split(source)
+ for delim,text in splitlist:
+ delimiters.match(delim).lastgroup
+
+Creating classes
+----------------------------------------------------------------------
+
+TODO
+
+Modifying modules
+-----------------------------------------------------------
+
+Metaclasses are extremely
+useful since they allows to change the behaviour of the code without
+changing the sources. For instance, suppose you have a large library written
+by others that you want to enhance in some way.
+
+Typically, it is always a bad idea to modify the sources, for many reasons:
+
++ touching code written by others, you may introduce new bugs;
++ you may have many scripts that requires the original version
+ of the library, not the modified one;
++ if you change the sources and then you buy the new version of the
+ library, you have to change the sources again!
+
+The solution is to enhance the proprierties of the library at run
+time, when the module is imported, by using metaclasses.
+
+To show a concrete example, let me consider the case of the module
+*commands* in the Standard Library. This module is Unix-specific,
+and cannot be used under Windows. It would be nice to have a
+metaclass able to enhance the module in such a way that
+when it is invoked on a Windows platform, Windows specific replacement
+of the Unix functions provided in the module are used. However,
+for sake of brevity, I will only give a metaclasses that display
+a nice message in the case we are in a Window platform, without
+raising an error (one could easily implement such a behaviour,
+however).
+
+ ::
+
+ #<recognizewindows.py>
+
+ import oopp,sys,commands
+
+ class WindowsAware(type):
+ def __init__(cls,*args):
+ if sys.platform=='win32':
+ for key,val in vars(cls).iteritems():
+ if isinstance(val,staticmethod):
+ setattr(cls,key,staticmethod(lambda *args:
+ "Sorry, you are (or I pretend you are) on Windows,"
+ " you cannot use the %s.module" % cls.__name__))
+
+ sys.platform="win32" #just in case you are not on Windows
+
+ commands=oopp.ClsFactory[WindowsAware](commands)
+
+ print commands.getoutput('date') #cannot be executed on Windows
+
+ #</recognizewindows.py>
+
+The output of this script is
+
+ ::
+
+ Sorry, you are on Windows, you cannot use the commands.module
+
+However, if you are on Linux and you comment out the line
+
+ ::
+
+ sys.platform="win32"
+
+you will see that the script works.
+
+Notice that the line ``commands=WindowsAware(commands)`` actually
+converts the 'commands' module in a 'commands' class, but since
+the usage is the same, this will fool all programs using the
+commands module. In this case the class factory 'WindowsAware'
+can also be thought as a module modifier. In this sense, it is
+very useful to denote the metaclass with an *adjective*.
+
+Metaclasses and attribute descriptors
+----------------------------------------------------------------------
+
+Descriptors are especially useful in conjunction with metaclasses, since
+a custom metaclass can use them as low level tools to modify the methods
+and the attributes of its instances. This allows to implement very
+sophisticated features with few lines of code.
+
+
+Notice, anyway, that
+even plain old function can be thought of as of descriptors.
+
+Descriptors share at least two features with metaclasses:
+
+1. as metaclasses, descriptors are best used as adjectives, since they
+ are intended to modify and enhance standard methods and attributes, in the
+ same sense metaclasses modify and enhance standard classes;
+
+2. as metaclasses, descriptors can change the *semantics* of Python, i.e.
+ what you see is not necessarely what you get. As such, they are a
+ dangerous feature. Use them with judgement!
+
+Now I will show a possible application of properties.
+Suppose one has a given class with various kind of
+attributes (plain methods, regular methods, static methods,
+class methods, properties and data attributes) and she wants
+to trace to access to the data attributes (notice that the motivation
+for the following problem come from a real question asked in
+comp.lang.python). Then one needs to retrieve data
+attributes from the class and convert them in properties
+controlling their access syntax. The first problem is solved
+by a simple function
+
+ ::
+
+ #<oopp.py>
+
+ def isplaindata(a):
+ """A data attribute has no __get__ or __set__ attributes, is not
+ a built-in function, nor a built-in method."""
+ return not(hasattr(a,'__get__') or hasattr(a,'__set__')
+ or isinstance(a,BuiltinMethodType) or
+ isinstance(a,BuiltinFunctionType))
+
+ #</oopp.py>
+
+whereas the second problem is elegantly solved by a custom metaclass:
+
+ ::
+
+ #<tracedaccess.py>
+
+ from oopp import isplaindata,inspect
+
+ class TracedAccess(type):
+ "Metaclass converting data attributes to properties"
+ def __init__(cls,name,bases,dic):
+ cls.datadic={}
+ for a in dic:
+ if isplaindata(a):
+ cls.datadic[a]=dic[a]
+ def get(self,a=a):
+ v=cls.datadic[a]
+ print "Accessing %s, value=%s" % (a,v)
+ return v
+ def set(self,v,a=a):
+ print "Setting %s, value=%s" % (a,v)
+ cls.datadic[a]=v
+ setattr(cls,a,property(get,set))
+
+ class C(object):
+ __metaclass__ = TracedAccess
+ a1='x'
+
+ class D(C): # shows that the approach works well with inheritance
+ a2='y'
+
+ i=D()
+ i.a1 # => Accessing a1, value=x
+ i.a2 # => Accessing a2, value=y
+ i.a1='z' # => Setting a1, value=z
+ i.a1 # => Accessing a1, value=z
+
+ #</tracedaccess.py>
+
+In this example the metaclass looks at the plain data attributes (recognized
+thanks ot the ``isplaindata`` function) of its instances and put them
+in the dictionary ``cls.datadic``. Then the original attributes are replaced
+with property objects tracing the access to them. The solution is a 4-line
+custom metaclass doing the boring job for me:
+
+ ::
+
+ #<oopp.py>
+
+ class Wrapped(Customizable,type):
+ """A customizable metaclass to wrap methods with a given wrapper and
+ a given condition"""
+ __metaclass__=Reflective
+ wrapper=wrappedmethod
+ condition=lambda k,v: True # wrap all
+ def __init__(cls,*args):
+ super(cls.__this,cls).__init__(*args)
+ wrap(cls,cls.wrapper,cls.condition.im_func)
+
+ Traced=Wrapped.With(wrapper=tracedmethod,__name__='Traced')
+ Timed=Wrapped.With(wrapper=timedmethod,__name__='Timed')
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> from oopp import *
+ >>> time_=ClsFactory[Traced](time)
+ >>> print time_.asctime()
+ [time_] Calling 'asctime' with arguments
+ (){} ...
+ -> 'time_.asctime' called with result: Sun May 4 07:30:51 2003
+ Sun May 4 07:30:51 2003
+
+Another is
+
+ ::
+
+ #<tracemain.py>
+
+ from oopp import ClsFactory,Traced,Reflective
+
+ def f1(x): return x # nested functions
+ def f2(x): return f1(x) # we want to trace
+
+ f1orf2=lambda k,v : v is f1 or v is f2
+ make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]
+ traced=make('traced',globals())
+
+ traced.f2('hello!') # call traced.f2
+
+ #</tracemain.py>
+
+with output
+
+ ::
+
+ [__main__] Calling 'f2' with arguments
+ ('hello!',){} ...
+ [__main__] Calling 'f1' with arguments
+ ('hello!',){} ...
+ -> '__main__.f1' called with result: hello!
+ -> '__main__.f2' called with result: hello!
+
+Modifying hierarchies
+---------------------------------------------------------
+
+Suppose one wants to enhance a pre-existing class, for instance
+by adding tracing capabilities to it. The problem is non-trivial
+since it is not enough to derive a new class from the original
+class using the 'Traced' metaclass. For instance, we could imagine of
+tracing the 'Pizza' class introduced in chapter 4 by defining
+
+ >>> from oopp import *
+ >>> class TracedTomatoPizza(GenericPizza,WithLogger):
+ ... __metaclass__=ClsFactory[Traced]
+ ... toppinglist=['tomato']
+
+However, this would only trace the methods of the newly defined class,
+not of the original one. Since the new class does not introduce any
+non-trivial method, the addition of 'Traced' is practically without
+any effect:
+
+ >>> marinara=TracedTomatoPizza('small') # nothing happens
+ *****************************************************************************
+ Tue Apr 15 11:00:17 2003
+ 1. Created small pizza with tomato, cost $ 1.5
+
+Tracing hierarchies
+------------------------------------------------------------------------------
+
+ ::
+
+ #<traceH.py>
+
+ from oopp import *
+
+ def wrapMRO(cls,wrapped):
+ for c in cls.__mro__[:-1]:
+ wrap(c,wrapped)
+
+ tracing=tracedmethod.With(logfile=file('trace.txt','w'))
+ wrapMRO(HomoSapiensSapiens,tracing)
+ HomoSapiensSapiens().can()
+
+ #</traceH.py>
+
+with output in trace.txt
+
+ ::
+
+ [HomoSapiensSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [HomoSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [HomoHabilis] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [Homo] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [PrettyPrinted] Calling '__str__' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [PrettyPrinted.__str__] called with result:
+ <HomoSapiensSapiens>
+ [Homo.can] called with result: None
+ [HomoHabilis.can] called with result: None
+ [HomoSapiens.can] called with result: None
+ [HomoSapiensSapiens.can] called with result: None
+
+Modifying source code
+------------------------------------------------------------------------
+
+The real solution would be to derive the original class 'GenericPizza'
+from 'Traced' and not from 'object'. One could imagine of creating
+a new class inhering from 'Traced' and with all the methods of the
+original 'GenericPizza' class; then one should create copies of
+all the classes in the whole multiple inheritance hierarchy.
+This would be a little annoying, but feasable; the real problem is
+that this approach would not work with cooperative methods, since
+cooperative calls in the derived classes would invoked methods in
+the original classes, which are not traced.
+
+This is a case where the modification of the original source code is
+much more appealing and simpler that any other method: it is enough
+to perform a search and replace in the original source code, by adding
+the metaclass 'Traced', to enhance the whole multiple inheritance hierarchy.
+Let me assume that the hierarchy is contained in a module (which is
+typical case). The idea, is to generate *dynamically* a new module from the
+modified source code, with a suitable name to avoid conflicts with the
+original module. Incredibily enough, this can be done in few lines:
+
+ ::
+
+ #<oopp.py>
+
+ def modulesub(s,r,module):
+ "Requires 2.3"
+ name=module.__name__
+ source=inspect.getsource(module).replace(s,r)
+ dic={name: module}; exec source in dic # exec the modified module
+ module2=ModuleType(name+'2') # creates an an empty module
+ customize(module2,**dic) # populates it with dic
+ return module2
+
+ #</oopp.py>
+
+Notice that the ``sub`` function, that modifies the source code of
+a given module and returns a modified module, requires Python 2.3
+to work. This is a due to a subtle bug in ``exec`` in Python 2.2.
+Anyway, the restriction to Python 2.3 allows me to take advantage
+of one of the most elegant convenience of Python 2.3: the name in
+the ``types`` module acts are type factories and in particular
+``ModuleType(s)`` returns an (empty) module named ``s``.
+Here is an example of usage:
+
+ >>> import oopp
+ >>> s='GenericPizza(object):'
+ >>> oopp2=oopp.modulesub(s,s+'\n __metaclass__=oopp.Traced',oopp)
+
+Name clashes are avoided, being 'oopp2' a different module from
+'oopp'; we have simultaneously access to both the original hierarchy
+in 'oopp' (non-traced) and the modified one in 'oopp2' (traced).
+In particular 'oopp2.CustomizablePizza' is traced and therefore
+
+ >>> class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
+ ... __metaclass__=makecls()
+ >>> marinara=PizzaLog.With(toppinglist=['tomato'])('small')
+
+gives the output
+
+ ::
+
+ [PizzaLog] Calling '__init__' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>, 'small'){} ...
+ -> 'PizzaLog.__init__' called with result: None
+
+ *****************************************************************************
+ Thu Mar 27 09:18:28 2003
+ [PizzaLog] Calling '__str__' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ [PizzaLog] Calling 'price' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ [PizzaLog] Calling 'toppings_price' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ -> 'PizzaLog.toppings_price' called with result: 0.5
+
+ -> 'PizzaLog.price' called with result: 1.5
+
+ -> 'PizzaLog.__str__' called with result: small pizza with tomato, cost $ 1.5
+
+ 1. Created small pizza with tomato, cost $ 1.5
+
+From that we understand what is happening:
+
+- ``PizzaLog.__init__`` calls ``GenericPizza.__init__`` that defines size and
+ cooperatively calls ``WithLogger.__init__``
+
+- WithLogger.__init__ cooperatively calls ``WithCounter.__init__``
+ that increments the count attribute;
+
+- at this point, the instruction 'print self' in ``WithLogger.__init__`` calls
+ ``PizzaLog.__str__`` (inherited from ``GenericPizza.__str__``);
+
+- ``GenericPizza.__str__`` calls 'price' that in turns calls
+ 'toppings_price'.
+
+On top of that, notice that the metaclass of 'PizzaLog' is
+``_TracedReflective`` that has been automagically generated by
+``makecls`` from the metaclasses of 'CustomizablePizza' (i.e. 'Traced')
+and of 'WithLogger' (i.e. 'Reflective'); the leading underscore helps
+to understand the dynamical origin of '_TracedReflective'.
+It turns out that '_TracedReflective' has a dynamically
+generated (meta-meta)class:
+
+ >>> print type(type(PizzaLog)) #meta-metaclass
+ <class 'oopp._WithWrappingCapabilitiesReflective'>
+
+Therefore this example has a non-trivial class hierarchy
+
+ >>> print oopp.MRO(PizzaLog)
+ MRO of PizzaLog:
+ 0 - PizzaLog(CustomizablePizza,WithLogger)[Traced]
+ 1 - CustomizablePizza(GenericPizza,Customizable)[Traced]
+ 2 - GenericPizza(object)[Traced]
+ 3 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 4 - WithCounter(object)
+ 5 - Customizable(object)
+ 6 - PrettyPrinted(object)
+ 7 - object()
+
+a non-trivial metaclass hierarchy,
+
+ >>> print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
+ MRO of Traced:
+ 0 - Traced(Reflective)[WithWrappingCapabilities]
+ 1 - Reflective(type)
+ 2 - type(object)
+ 3 - object()
+
+and a non-trivial meta-metaclass hierarchy:
+
+ >>> print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
+ MRO of WithWrappingCapabilities:
+ 0 - WithWrappingCapabilities(BracketCallable)
+ 1 - CallableWithBrackets(type)
+ 2 - type(object)
+ 3 - object()
+
+Pretty much complicated, isn't it ? ;)
+
+This example is there to show what kind of maintenance one can have
+with programs doing a large use of metaclasses, particularly, when
+they should be understood by somebody else than the autor ...
+
+Metaclass regenerated hierarchies
+--------------------------------------------------------------------------
+
+ ::
+
+ import types
+
+ def hierarchy(self,cls):
+ d=dict([(t.__name__,t) for t in vars(types).itervalues()
+ if isinstance(t,type)])
+ def new(c):
+ bases=tuple([d[b.__name__] for b in c.__bases__])
+ return self(c.__name__, bases, c.__dict__.copy())
+ mro=list(cls.__mro__[:-1])
+ mro.reverse()
+ for c in mro:
+ if not c.__name__ in d:
+ d[c.__name__]=new(c)
+ customize(self,**d)
+
+ ClsFactory.hierarchy=hierarchy
+ traced=ClsFactory[Traced,Reflective]
+
+Unfortunately, this approach does not work if the original hierarchy makes
+named cooperative super calls.
+
+Therefore the source-code run-time modification has its advantages.
+
+
+THE PROGRAMMABLE PROGRAMMING LANGUAGE
+=========================================================================
+
+ *I think that lisp is a better applications language than Python.
+ However, Python is close enough, or at least so much better than the
+ alternatives, that Python's social and glue language advantages are
+ often decisive.* -- Andy Freeman on c.l.p.
+
+I go in *really* DEEP BLACK MAGIC here.
+
+Lisp has been called the *programmable programming language* [#]_
+since its macros allow the programmer to change the *syntax* of
+the language. Python has no macros and the syntax of the language
+cannot be changed. Nevertheless, Python metaclasses allows
+to change the *semantics* of the language. In this sense, they
+are even more powerful and more dangerous than Lisp macros.
+Python metaclass allow the user to customize the language (if not
+its syntax). This is cool enough, however it can make your programs
+unreadable by others. The techniques explained in this
+chapter should be used with care. Nevertheless, I trust the judgement
+of the programmer who has been able to reach this chapter, and I don't
+mind providing him further rope to shoot in his/her foot ;)
+
+.. [#] Paul Graham, 'OnLisp'
+ citing
+
+Enhancing the Python language
+--------------------------------------------------------------------------
+
+Let me start with some minor usage of metaclasses. In this section I
+will show how the user can implement in few lines features that are
+built-in in other languages, through a minimal usage of metaclasses.
+
+For instance, suppose one wants to define a class which cannot be
+derived: in Java this can be done with the "final" keyword.
+In Python there is no need to add a new keyword to the language:
+
+ ::
+
+ #<oopp.py>
+
+ class NonDerivableError(Exception): pass
+
+ class Final(type): # better derived from WithCounter,type
+ "Instances of Final cannot be derived"
+ def __new__(meta,name,bases,dic):
+ try:
+ meta.already_called is True
+ except AttributeError: # not already called
+ meta.already_called=True
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else: #if already called
+ raise NonDerivableError("I cannot derive from %s" % bases)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import Final
+ >>> class C:
+ ... __metaclass__=Final
+ ...
+ >>> class D(C): pass #error
+ ...
+ NonDerivableError: D not created from (<class 'oopp.C'>,)
+
+It is interesting to notice that a similar effect can be reached
+with a ``singletonClass`` class factory: a 'MetaSingleton' inherits
+from ``Singleton`` and from 'type' (therefore it is a metaclass):
+
+ ::
+
+ #<oopp.py>
+
+ class S(Singleton,type): pass
+ singletonClass=ClsFactory[S]
+
+ #</oopp.py>
+
+If we write
+
+ >>> from oopp import singletonClass
+ >>> C=singletonClass()
+ >>> class D(C):
+ ... pass
+
+
+we see that actually 'D' is not a new instance of 'Singleton', but
+it coincides with 'C', instead:
+
+ >>> id(C),id(D)
+ (135622140, 135622140)
+ >>> C is D
+ True
+ >>> type(C)
+ <class '__main__._Singleton'>
+ >>> type(C).__bases__
+ (<class 'oopp.Singleton'>, <type 'type'>)
+ >>> c=C(); d=D()
+ >>> id(c),id(d)
+ (1075378028, 1075378924)
+
+Notice the order: 'SingletonClass' must inherit from 'Singleton'
+first and from ``Class`` second, otherwise the ``Class.__new__`` method would
+override the ``Singleton.__new__``, therefore losing the 'Singleton'
+basic property of having only one instance. On the other hand, in
+the correct order, 'Singleton' first and 'Class' second, the inheritance
+diagram is
+
+ ::
+
+
+ object 5
+ (__new__)
+ / \
+ / \
+ 2 WithCounter type 4
+ (__new__) (__new__)
+ | |
+ | |
+ 1 Singleton Class 3
+ (__new__) (__new__)
+ \ /
+ \ /
+ SingletonClass 0
+ (Singleton.__new__)
+
+
+ ::
+
+ object
+ / \
+ / |
+ WithCounter |
+ | |
+ Singleton type
+ \ /
+ \ /
+ MetaSingleton
+ :
+ :
+ : instantiation
+ :
+ :
+ C = D
+
+
+whereas 'SingletonClass' inherits ``Singleton.__new__`` which, trough
+the ``super`` mechanism, calls 'type.__new__' and therefore creates
+the class 'C'. Notice that class 'D' is never created, it is simply
+an alias for 'C'.
+
+I think it is simpler to write down the class 'Final' explicitely
+(explicit is better than implicit) as I did; however a fanatic of code
+reuse could derive it from 'SingletonClass':
+
+ ::
+
+ #<final.py>
+
+ from oopp import *
+
+ class Final(Singleton,type):
+ "Inherits the 'instance' attribute from Singleton (default None)"
+ def __new__(meta,name,bases,dic):
+ if meta.counter==0: # first call
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else:
+ raise NonDerivableError("I cannot derive from %s" % bases)
+
+ class C: __metaclass__=Final
+
+ try:
+ class D(C): pass
+ except NonDerivableError,e:
+ print e
+
+ #</final.py>
+
+The reader can check that this script has the correct output
+"I cannot derive from <class 'oopp.C'>". I leave to the reader
+to understand the issues with trying to implement 'NonDerivable'
+from 'NonInstantiable'. #And why an inner metaclass would not work.
+
+Restricting Python dynamism
+-----------------------------------------------------------
+
+ ::
+
+ #<oopp.py>
+
+ def frozen(self,name,value):
+ if hasattr(self,name):
+ type(self).__bases__[0].__setattr__(self,name,value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+ class Frozen(object):
+ """Subclasses of Frozen are frozen, i.e. it is impossibile to add
+ new attributes to them and their instances"""
+ __setattr__ = frozen
+ class __metaclass__(type):
+ __setattr__ = frozen
+
+ #</oopp.py>
+
+
+ #<frozen.py>
+
+ from oopp import *
+
+ class C(Frozen):
+ c=1
+ def __init__(self):
+ #self.x=5 # won't work anymore, __new__ will be okay
+ pass
+
+ class D(C):
+ d=2
+
+ C.c=2
+
+ print D().d
+
+ #</frozen.py>
+
+Changing the language without changing the language
+--------------------------------------------------------------------------
+
+In Lisp the user has the possibility of changing the syntax of the
+language to suit her purposes (or simply to fit her taste).
+In Python, the user cannot change the basic grammar of the language,
+nevertheless, to a great extent, metaclasses allows to emulate this effect.
+Notice that using metaclasses to this aim is not necessarely
+a good idea, since once you start
+changing the Python standard behaviour, it will become impossible for
+others to understand your programs (which is what happened to Lisp ;).
+
+Let me show how metaclasses can be used to provide notational convenience
+(i.e. syntactic sugar) for Python.
+
+As first example, I will show how we may use metaclasses to provide some
+convenient notation for staticmethods and classmethods:
+
+ ::
+
+ class MetaSugar(type):
+ def __init__(cls,name,bases,clsdict):
+ for key,value in clsdict.iteritems():
+ if key.startswith("static_"):
+ setattr(cls,key[7:],staticmethod(value))
+ elif key.startwith("class_"):
+ setattr(cls,key[6:],classmethod(value))
+
+The same effect can be obtained trough normal inheritance
+
+ ::
+
+ class SyntacticSugar(object):
+ def __init__(self):
+ for k,v in self.__class__.__dict__.iteritems():
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+
+Let me now implement some syntactic sugar for the __metaclass__ hook.
+
+ ::
+
+ #<oopp.py>
+
+ import re
+ squarednames=re.compile('\[([A-Za-z_][\w\., ]*)\]')
+
+ def inferredfromdocstring(name,bases,dic):
+ docstring=dic['__doc__']
+ match=squarednames.match(docstring)
+ if not match: return ClsFactory[Reflective](name,bases,dic)
+ metanames=[name.strip() for name in match.group(1).split(',')]
+ metaname=''.join(metanames)
+ if len(metanames)>1: # creates a new metaclass
+ metaclass=type(metaname,tuple(map(eval,metanames)),{})
+ else:
+ metaclass=eval(metaname)
+ return ClsFactory[metaclass](name,bases,dic)
+
+ #</oopp.py>
+
+ #<sugar.py>
+
+ from oopp import *
+ __metaclass__ = inferredfromdocstring
+ class B:
+ "Do nothing class"
+
+ class C:
+ "[Reflective]"
+ " Do nothing class"
+
+ class D:
+ "[WithLogger,Final]"
+ "Do nothing class"
+
+ class E(C):
+ pass
+
+ #</sugar.py>
+
+With output:
+
+ ::
+
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class Logged_C descending from (),
+ instance of <class 'oopp.Logged'>
+
+ Logged_C dictionary:
+ __doc__ = Do nothing class
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class Logged_Final_D descending from (),
+ instance of <class 'oopp.LoggedFinal'>
+
+ Logged_Final_D dictionary:
+ __doc__ = Do nothing class
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class E descending from (<class 'oopp.Logged_C'>,),
+ instance of <class 'oopp.Logged'>
+
+ E dictionary:
+ <EMPTY>
+
+At the end, let me point out few observations:
+Metaclasses can be used to provide syntactic sugar, as I have shown
+in the previous example. However, I have given the previous
+routines as a proof of concept: I do *not* use these routines in
+my actual code for many good reasons:
+
+1. At the end a convenient notation will be provided in Python 2.4
+2. I don't want to use magic tricks on my code, I want others to
+ be able to understand what the code is doing;
+3. I want to be able myself to understand my own code in six months
+ from today ;)
+
+Anyway, I think it is a good thing to know about this potentiality
+of metaclasses, that can turn out to be very convenient in certain
+applications: but this does not mean that should be blindly used
+and/or abused. In other words: with great powers come
+great responsabilities ;)
+
+Recognizing magic comments
+--------------------------------------------------------------------------
+
+In this section, I will begin to unravel the secrets of the black magic art
+of changing Python semantics and I will show that with few lines
+involving metaclasses
+and the standard library 'inspect' module, even comments can be made
+significant! (let me continue with my series "how to do what should not
+be done").
+
+To this aim, I need a brief digression on regular expressions.
+
+ ::
+
+ class RecognizesMagicComments(object):
+ form=r'def %s(NAME)(args):#!\s?staticmethod'
+ class __metaclass__(type):
+ def __new__(meta,name,bases,dic):
+ code=[]
+ for attr in dic:
+ source=inspect.getsource(dic[attr]).splitlines()
+ for line in source:
+ split=line.split('#!')
+ if len(split)==2:
+ descriptor=split[1]; code.append(split[0])
+ else: code.append(line)
+
+ class C(RecognizesMagicComments):
+ #!staticmethod
+ def f(x): #!staticmethod
+ return x
+
+Interpreting Python source code on the fly
+---------------------------------------------------------------------------
+
+At this point, I can really go *DEEP* in black magic.
+
+ ::
+
+ import sys, inspect, linecache, re
+
+ def cls_source(name,module):
+ lines = linecache.getlines(inspect.getsourcefile(module))
+ if not lines: raise IOError, 'could not get source code'
+ pat = re.compile(r'^\s*class\s*' + name + r'\b')
+ for i in range(len(lines)):
+ if pat.match(lines[i]): break
+ else: raise IOError, 'could not find class definition'
+ lines, lnum = inspect.getblock(lines[i:]), i + 1
+ return ''.join(lines)
+
+ class Interpreter(object):
+ def __init__(self,CPO): # possible composition of code processing opers
+ self.repl=CPO
+ def __call__(self,name,bases,dic):
+ try:
+ modulename=dic['__module__'] # module where the class is defined
+ except KeyError: # no __module__ attribute
+ raise IOError("Class %s cannot be defined dynamically or in the\n"
+ "interpreter and the source code cannot came from a pipe"% name)
+ module=sys.modules[modulename]
+ source=self.repl(cls_source(name,module))
+ source=re.sub('__metaclass__=.*','__metaclass__=type',source)
+ #print source
+ loc={}; exec source in vars(module),loc
+ return loc[name]
+
+ regexp_expand=Interpreter(regexp)
+
+Implementing lazy evaluation
+---------------------------------------------------------------------------
+
+At this point of our knowledge, it becomes trivial to implement lazy
+evaluation and then a ternary operator. (My original, simpler, implementation
+is posted on c.l.p.; see the thread 'PEP 312 (and thus 308) implemented
+with a black magic trick')
+
+Implementing a ternary operator
+---------------------------------------------------------------------------
+
+ ::
+
+ # module ternary.py
+
+ "PEP 308 and 312 implemented via a metaclass-powered dirty trick"
+
+ import inspect,__main__
+
+ # the ternary operator:
+
+ def if_(cond,f,g):
+ "Short circuiting ternary operator implemented via lambdas"
+ if cond: return f()
+ else: return g()
+
+ # the metaclass black magic:
+
+ class DirtyTrick(type):
+ """Cooperative metaclass that looks at the source code of its instances
+ and replaces the string '~' with 'lambda :' before the class creation"""
+ def __new__(meta,name,bases,dic):
+ for attr in dic.values():
+ if inspect.isfunction(attr):
+ code=inspect.getsource(attr)
+ if code.find('~')==-1: continue # no '~' found, skip
+ code=code.replace('~','lambda :')
+ code=dedent(code)+'\n'
+ exec code in __main__.__dict__,dic # modifies dic
+ return super(DirtyTrick,meta).__new__(meta,name,bases,dic)
+
+ # a convenient base class:
+
+ class RecognizesImplicitLambdas:
+ "Children of this class do recognize implicit lambdas"
+ __metaclass__=DirtyTrick
+
+Here there is an example of usage:
+
+ ::
+
+ from ternary import if_, RecognizesImplicitLambdas
+ from math import sqrt
+
+ class C(RecognizesImplicitLambdas):
+ def safesqrt(self,x):
+ return if_( x>0, ~sqrt(x), ~0) #short-circuiting ternary operator
+
+ c=C()
+ print c.safesqrt(4), c.safesqrt(-4)
+
+
+
+
+
diff --git a/pypers/app1.txt b/pypers/app1.txt
new file mode 100755
index 0000000..8b13789
--- /dev/null
+++ b/pypers/app1.txt
@@ -0,0 +1 @@
+
diff --git a/pypers/app2.txt b/pypers/app2.txt
new file mode 100755
index 0000000..df768ab
--- /dev/null
+++ b/pypers/app2.txt
@@ -0,0 +1,668 @@
+---------------------------------------------------------------------------
+CHAPTER 4. THE SECRETS OF MULTIPLE INHERITANCE
+---------------------------------------------------------------------------
+
+Introduction
+---------------------------------------------------------------------------
+
+In my opinion, Multiple Inheritance (often abbreviated as MI) is the most
+advanced topic in Object Oriented Programming. It is also true that
+Multiple Inheritance is one of the most difficult features to implement
+in an Object Oriented Programming language. Even, some languages by design
+decided to avoid it. This is for instance the case of Java, that avoided
+MI having seen its implementation in C++ (which is not for the faint of
+heart ;-); for what concerns the so called scripting languages, of which
+the most famous are Perl, Python and Ruby (in this order, even if
+the right order would be Python, Ruby and Perl), only Python implements
+Multiple Inheritance.
+The fact that Multiple Inheritance can be hairy, does not mean that it
+is *always* hairy, however. Multiple Inheritance is used with success
+in Lisp derived languages (CLOS, Dylan,..), for instance.
+The aims of this chapter is to show how much the
+Python support for MI has improved in the most recent version (2.2 and 2.3).
+The message is the following: if Python 1.5 had a basic support for
+MI inheritance (basic but nevertheless with nice features, dynamic),
+Python 2.2 has *greatly* improved that support and with the
+change of the Method Resolution Order in Python 2.3, we may say
+that support for MI is now *excellent*.
+
+I strongly encourage Python programmers to use MI a lot: this will
+allows even a stronger reuse of code than in single inheritance.
+
+Why Multiple Inheritance ?
+--------------------------
+
+I think the best way to explain the power of MI is trough a simple example.
+
+#singleton, noninstantiable
+
+
+The Python 2.3 Method Resolution Order
+--------------------------------------
+
+ *Felix qui potuit de rerum cognoscere causas* -- Virgilius
+
+Everything started with a post by Samuele Pedroni in 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 has been invented by people working on Dylan and it is described in
+a paper intended for lisper readers [[#]_]. 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 all 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 Python 2.2 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 knows how multiple
+inheritance works, then this paper is for you. The good news is
+that things are not as complicated as you could expect.
+
+Let me begin with some basic definitions.
+
+1) Given a class in a complicate 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 the class.
+
+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 the class C.
+
+3) The *Method Resolution Order* (MRO) is the set of rules that
+ allow to 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) In the case of a
+ single inheritance hierarchy in which C is a subclass of C1
+ which in turn is a subclass of C2, the linearization of C
+ is the list [C, C1 , C2]. In multiple inheritance hierarchies,
+ the construction of the linearization is cumbersome, since it
+ has to respect the essential constraints of *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 it 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 good 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 class object, 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 (ZABXYO in this case).
+
+
+The C3 Method Resolution Order
+------------------------------
+
+Let me introduce few simple notations which will be useful for the
+following discussion. I 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 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, i.e. I give
+the rules to compute the linearization of any given class C.
+
+Rule 0:
+ If C is the "object" class, which has no parents, its linearization
+ coincides with itself:
+
+ L(O) = O
+
+Rule 1:
+ If C = C(B) is a class with an unique parent B (single inheritance)
+ its linearization is simply the sum of the class with the
+ linearization of the parent:
+
+ L(C(B)) = C + L(B)
+
+Rule 2:
+ In the case of multiple inheritance things are more cumbersome
+ and one has to introduce the concept of *merge* of lists.
+ Consider for instance the case of two parents B1 and B2.
+ Then the linearization of C is given by C plus the merge
+ of the three sequences:
+
+ - linearization of the first parent;
+ - linearization of the second parent;
+ - list of the two parents
+
+In symbolic notation,
+
+ L(C(B1,B2)) = C + merge(L(B1), L(B2), B1 B2)
+
+How the merge is computed ? The rule is the following:
+
+ *take the head of the first sequence, i.e L(B1)[0]; if this
+ head is not in the tail of any of the other sequences, then
+ add it to the linearization of C and remove it from the sequences
+ in the merge, otherwise look at the head
+ of the next sequence and take it, if it is a good head.
+ Then repeat the operation. If there
+ are no good heads, then it is impossible to construct the merge.
+ In this case Python 2.3 will raise an error and will refuse
+ to create the class C.*
+
+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 before) then the merge cannnot be
+computed.
+
+The generalization to the case of a class with
+three or more parents is obvious:
+
+ L(C(B1, ... , BN)) = C + merge(L(B1), ... ,L(BN), B1 ... BN)
+
+The merge rule is the heart of the C3 algorithm: once you have
+understood that, you have understood the C3 Method Resolution Order.
+But I don't expect that you can understand the rule without a couple
+of examples ;-)
+
+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
+
+With the same argument 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 an upper level.
+
+The 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
+obvious to compute the linearizations of the classes 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.
+
+
+
+Bad Method Resolution Orders
+----------------------------
+
+A MRO is *bad* when it breaks such fundamental properties as local
+precedence ordering and/or 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),{})
+
+We see that the 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 there is a difference with 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
+
+ ::
+
+ O
+ |
+ F
+ | \
+ | E
+ | /
+ G
+
+should be avoided, since it is unclear if F should override E or
+viceversa. Python 2.3 solves the ambiguity buy raising an exception
+in the creation of class G, effectively stopping the programming
+from generating ambiguous hierarchies.
+The reason for that is that the C3 algorithm fails, since 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.
+Python 2.3 forces the programmer to write good hierarchies (or,
+at least, less prone to errors).
+
+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
+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 error message.
+
+
+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 the attributes, not only of the methods;
+
+2. the default food for Pythonistas is spam ! (but you already knew that ;-)
+
+
+Having discussed the issue with the local precedence ordering, let
+me now consider the issue of monotonicity. My goal is to show that
+both the MRO for classic classes and
+for new style classes for Python 2.2 are not 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 sees 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,
+that 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 break 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 again a violation of monotonicity which cannot be allowed.
+On top of that, 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] where K2 precedes K3,
+whereas in the linearization of Z we have that K2 *follows* K3.
+These problems explain why the 2.2 rule has been dismissed in favor of the C3
+rule.
+
+Here I give a short Python 2.2 script that allows anybody to compute the
+2.3 MRO without risk for his brain.
+Simply change the last line to play with the various examples
+I have discussed in this paper.
+
+ ::
+
+ #<c3.py>
+
+ """C3 algorithm by Samuele Pedroni (with readability enhanced by me).
+ Remark: it only works with Python 2.2 (not 2.3+ nor 2.1-)."""
+
+ 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)
+ 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)
+
+ #</c3.py>
+
+
+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
+
+.. [#] The (in)famous book on metaclasses, *Putting Metaclasses to Work*:
+ Ira R. Forman, Scott Danforth, Addison-Wesley 1999.
diff --git a/pypers/bolzano/add_to.py b/pypers/bolzano/add_to.py
new file mode 100755
index 0000000..b612eb8
--- /dev/null
+++ b/pypers/bolzano/add_to.py
@@ -0,0 +1,17 @@
+import sys
+def add_to(obj):
+ def wrapper(f):
+ setattr(obj, f.__name__, f)
+ return f
+ return wrapper
+
+###################################
+
+def obj(): pass
+
+@add_to(obj)
+def identity(x):
+ return x
+
+print identity
+print obj.identity
diff --git a/pypers/bolzano/biblioteca.py b/pypers/bolzano/biblioteca.py
new file mode 100755
index 0000000..6ac3882
--- /dev/null
+++ b/pypers/bolzano/biblioteca.py
@@ -0,0 +1,27 @@
+class Libro(object):
+ def __init__(self, titolo, autore):
+ self.titolo = titolo
+ self.autore = autore
+ def __str__(self):
+ return "%s | %s" % (self. titolo, self.autore)
+
+class Biblioteca(object):
+ def __init__(self, libri):
+ self.libri = libri
+ def ordina_per(self, campo):
+ def compara(l1, l2):
+ return cmp(getattr(l1, campo), getattr(l2, campo))
+ lista = self.libri[:]
+ lista.sort(compara)
+ return lista
+
+l1 = Libro("Il Signore degli Anelli", "Tolkien")
+l2 = Libro("La Luna e' una severa maestra", "Heinlein")
+
+archivio = Biblioteca([l1, l2])
+
+for libro in archivio.ordina_per("titolo"):
+ print libro
+
+for libro in archivio.ordina_per("autore"):
+ print libro
diff --git a/pypers/bolzano/cgi-bin/box_radio.py b/pypers/bolzano/cgi-bin/box_radio.py
new file mode 100755
index 0000000..9cff61a
--- /dev/null
+++ b/pypers/bolzano/cgi-bin/box_radio.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python2.4
+import os, sys, cgi
+from ms.html_utils import cgipage, cgiform, getscriptname, interp, html_form
+
+def entry(form):
+ checkbox="""
+ C1<input type=checkbox name=check1><br/>
+ C2<input type=checkbox name=check2><br/>
+ C3<input type=checkbox name=check3><br/>
+ """
+ radiobuttons="""
+ R1<input type=radio name=choice value=1><br/>
+ R2<input type=radio name=choice value=2><br/>
+ R3<input type=radio name=choice value=3><br/>
+ """
+ submit="""
+ <input type='submit' name='submit' value='confirm'>
+ """
+ return cgipage(html_form(checkbox + radiobuttons + submit))
+
+def exit(form):
+ return cgipage(interp("""Thank you. You submitted:<br/>
+ check1 $check1<br/>
+ check2 $check2<br/>
+ check3 $check3<br/>
+ choice $choice<br/>
+ """, form))
+
+if __name__ == "__main__":
+ print cgiform(entry, exit)
diff --git a/pypers/bolzano/cgi-bin/example1.py b/pypers/bolzano/cgi-bin/example1.py
new file mode 100755
index 0000000..0447ed4
--- /dev/null
+++ b/pypers/bolzano/cgi-bin/example1.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2.4
+"""
+usage: %prog [options]
+-i, --input=/tmp: search directory
+-o, --output=.: output directory
+-s, --size: order by size
+"""
+
+import os, sys, cgi
+from ms.optionparse import OptionParser, option_parser_from
+from ms.html_utils import template, simplepage, printpage, cgiform, \
+ getscriptname
+
+@template()
+def checkbox(option, flags):
+ for flag in flags:
+ name = flag.dest
+ if not name or name == "submit": continue
+ checked = ["", "checked"][getattr(option, name) or 0]
+ yield "<tr>\n"
+ yield "<td>%s</td>" % flag.help
+ yield "<td><input type='checkbox' name=%r %s></td>" % (name, checked)
+ yield "</tr>\n"
+
+@template()
+def entrybox(option, option_args):
+ for o_arg in option_args:
+ name = o_arg.dest
+ if not name: continue
+ val = getattr(option, name) or ""
+ yield "<tr>"
+ yield "<td>%s</td>" % o_arg.help
+ yield "<td><input type='text' name=%r value=%r></td>" % (name, val)
+ yield "</tr>"
+
+@template()
+def inputform(option, op):
+ yield "<form action=%r>" % getscriptname()
+ yield "<table summary='Input form'>"
+ yield entrybox(option, op.get_option_args())
+ yield checkbox(option, op.get_flags())
+ yield "</table>"
+ yield "<input type='submit' name='submit' value='confirm'>"
+ yield "</form>"
+
+def entryform(form):
+ printpage(inputform(option, op), title="Example 1")
+
+def exitform(form):
+ printpage("Thank you for filling this form.")
+
+if __name__ == "__main__":
+ op = option_parser_from(__doc__)
+ op.add_option("-S", "--submit", action="store_true", default=False,
+ help="Submit the form")
+ option, args = op.parse_args()
+ if option.submit: # script called from the command line
+ os.environ["QUERY_STRING"] = "&submit=confirm"
+ cgiform(entryform, exitform)
+
diff --git a/pypers/bolzano/cgi-bin/textarea.py b/pypers/bolzano/cgi-bin/textarea.py
new file mode 100755
index 0000000..a4dd9b6
--- /dev/null
+++ b/pypers/bolzano/cgi-bin/textarea.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.4
+import os, sys, cgi
+from ms.html_utils import cgipage, cgiform, getscriptname, interp, html_form
+
+def entry(form):
+ textarea="""
+ <textarea name='text'>
+ Write something here
+ </textarea>
+ """
+ submit="""
+ <input type='submit' name='submit' value='confirm'>
+ """
+ return cgipage(html_form(textarea + submit))
+
+def exit(form):
+ return cgipage(interp("""Thank you. You submitted:<br/>
+ $text
+ """, dict((k, form.getfirst(k)) for k in form)))
+
+if __name__ == "__main__":
+ print cgiform(entry, exit)
diff --git a/pypers/bolzano/db/BooksOnline.py b/pypers/bolzano/db/BooksOnline.py
new file mode 100755
index 0000000..836c744
--- /dev/null
+++ b/pypers/bolzano/db/BooksOnline.py
@@ -0,0 +1,49 @@
+from quixote_utils import RootDirectory
+from quixote.form.form import Form
+from quixote.form.widget import *
+from bookdb import Book, BookDatabase
+
+class DBInterface(RootDirectory):
+ _q_exports = ["", "show", "add_book", "edit_book"]
+ def __init__(self, db):
+ super(DBInterface, self).__init__()
+ self.db = db
+ self.currentbook = self.db.get_book("000000")
+ def _q_index(self):
+ return """Welcome to our online library! <br/><br/>
+ Please go to our <a href="show">show</a> page."""
+ def show(self):
+ return "list of books"
+ def edit_book(self):
+ ""
+ form = Form()
+ for field in Book.FIELDS:
+ form.add(StringWidget, field, title=field,
+ value=getattr(self.currentbook, field))
+ form.add(SubmitWidget, "submit")
+ if not form.is_submitted():
+ return form.render()
+ else:
+ self.db.edit_book(self.currentbook.dbkey,
+ *[form[field] for field in Book.FIELDS])
+ self.db.commit()
+ return "The book has been edited!"
+
+ def add_book(self):
+ form = Form()
+ for field in Book.FIELDS:
+ form.add(StringWidget, field, title=field)
+ form.add(SubmitWidget, "submit")
+ if not form.is_submitted():
+ return form.render()
+ else:
+ self.db.add_book(*[form[field] for field in Book.FIELDS])
+ self.db.commit()
+ return "A new book has been added!"
+
+ def add_from_text_file(self):
+ return "Please choose the book file."
+
+
+root = DBInterface(BookDatabase("books"))
+root.publish_show("edit_book", browser="mozilla")
diff --git a/pypers/bolzano/db/bookdb.py b/pypers/bolzano/db/bookdb.py
new file mode 100755
index 0000000..8b8c744
--- /dev/null
+++ b/pypers/bolzano/db/bookdb.py
@@ -0,0 +1,63 @@
+import shelve
+
+class Book(object):
+ FIELDS = ["title", "score", "author", "date", "genre", "nation"]
+ def __init__(self, title, score, author, date, genre, nation, dbkey):
+ self.title = title
+ self.score = score
+ self.author = author
+ self.date = date
+ self.genre = genre
+ self.nation = nation
+ self.dbkey = dbkey
+ def __str__(self):
+ return """<%(dbkey)s %(title)s %(score)s %(author)s %(date)s
+%(genre)s %(nation)s>""" % vars(self)
+
+class BookDatabase(object):
+ def __init__(self, datafile):
+ self.db = shelve.open(datafile)
+ self.recno = len(self.dbkeys())
+ self.print_all()
+ def commit(self):
+ self.db.sync()
+ def close(self):
+ self.db.close()
+ def getkey(self):
+ return "%06d" % self.recno
+ def dbkeys(self):
+ dbkeys = self.db.keys()
+ dbkeys.sort()
+ return dbkeys
+ def add_book(self, title, score, author, date, genre, nation):
+ self.db[self.getkey()] = Book(
+ title, score, author, date, genre, nation, self.getkey())
+ self.recno += 1
+ def edit_book(self, dbkey, title, score, author, date, genre, nation):
+ self.db[dbkey] = Book(
+ title, score, author, date, genre, nation, dbkey)
+ def add_from_file(self, filename):
+ for line in file(filename):
+ title = line[0:33].strip()
+ score = line[33:38].strip()
+ author = line[38:51].strip()
+ dd, mm, aa = line[51:61].strip().split("-")
+ genre = line[61:65].strip()
+ nation = line[65:67]
+ if "87" <= aa <= "99":
+ aaaa = "19" + aa
+ else:
+ aaaa = "20" + aa
+ date = "-".join([aaaa, mm, dd])
+ self.add_book(title, score, author, date, genre, nation)
+ def del_book(self, book):
+ del self.db[book.dbkey]
+ def get_book(self, dbkey):
+ return self.db[dbkey]
+ def print_all(self):
+ for key in self.dbkeys():
+ print self.db[key]
+
+if __name__ == "__main__":
+ bd = BookDatabase("books")
+
diff --git a/pypers/bolzano/db/books87.txt b/pypers/bolzano/db/books87.txt
new file mode 100755
index 0000000..1490f5b
--- /dev/null
+++ b/pypers/bolzano/db/books87.txt
@@ -0,0 +1,109 @@
+La sintesi Einsteiniana OOO Born 03-01-87 SF SU
+I robots dell'alba OOO+ Asimov 08-01-87 FS SU
+Elementi di Astrofisica OO+ Santi 14-01-87 AS IT
+Il mago di Oz OOO Baum 25-01-87 FA SU
+Il nome della rosa OOO Eco 06-02-87 RS IT
+Figli di domani OO Van Vogt 07-02-87 FS SU
+Il mondo nuovo OO+ Huxley 14-02-87 FS GB
+Cosmo profondo OO+ Tubb 18-02-87 FS SU
+Ars amatoria OOO Ovidio 26-02-87 LL IT
+Heroides OO Ovidio 27-02-87 LL IT
+Orlando Furioso OOO Bignami 27-02-87 FA IT
+Unico indizio: la Luna piena OOO King 27-02-87 TR SU
+Contact OO Sagan 04-03-87 FS SU
+Asterix e Cleopatra OOO Uderzo 05-03-87 CO FR
+Il pianeta degli schiavi OO Jenifer 05-03-87 FS SU
+La Regina delle Nevi OOO Vinge 08-03-87 FS SU
+Il cielo OOO Cecchini 18-03-87 AS IT
+Yargo + Susann 24-03-87 FS SU
+La civilta' dei Solari OO Spinrad 25-03-87 FS SU
+Logo: ali per la mente OOO Reggini 30-03-87 IN IT
+Fisica Statistica OO+ U. Berkeley 03-04-87 SF SU
+Partiranno OO+ D'Eramo 16-04-87 FS SU
+Il popolo dell'Autunno OOO+ Bradbury 17-04-87 TR SU
+Racconti incompiuti OOO Tolkien 20-04-87 FA GB
+Meccanica OOO U. Berkeley 02-05-87 SF SU
+Il vagabondo delle scienze OOO Asimov 05-05-87 SG SU
+Il Rosso e il Nero OO Stendhal 05-05-87 LF FR
+L'assalto al treno OO Arpino 20-05-87 FA IT
+Racconti filosofici OOO Voltaire 24-05-87 LF FR
+La mano sinistra delle tenebre OOO Le Guin 25-05-87 FS SU
+Introduzione alla programmazione OO Ralston 26-05-87 IN SU
+Pascal OO+ Watt 08-06-87 IN SU
+Il problema marziano OO Vari 09-06-87 FS SU
+Teoria dei giochi OO Morgenstein 16-06-87 SM SU
+Le avventure di Alice OOO Carroll 16-06-87 FA SU
+Antiche sere OO+ Mailer 19-06-87 RS SU
+Le avventure di Sherlock Holmes OOO+ Doyle 04-07-87 GI GB
+Guerra eterna OOO Haldeman 07-07-87 FS SU
+Noi marziani OOO Dick 08-07-87 FS SU
+Le vie della Frontiera OO+ Chandler 09-07-87 FS SU
+Shock 1 OOO Matheson 10-07-87 TR SU
+Shock 2 OO+ Matheson 11-07-87 TR SU
+Fuochi OO Yourcennar 13-07-87 LF FR
+Il gioco dei pianeti O+ Bradbury 13-07-87 FS SU
+Le quattro ore di Satana OO Hubbard 14-07-87 TR SU
+E. T. il libro del Pianeta Verde OOO Kotzwinkle 15-07-87 FS SU
+Le armi di Isher OOO Van Vogt 16-07-87 FS SU
+Le grandi storie della FS 2 OOO+ Vari 19-07-87 FS SU
+La Fantascienza OO+ Montanari 20-07-87 SG IT
+Shock 3 OO Matheson 21-07-87 TR SU
+Universo a sette incognite OOO Vari 21-07-87 FS SU
+Shock 4 OO+ Matheson 22-07-87 TR SU
+La svastica sul sole OO+ Dick 23-07-87 FS SU
+I Principi Demoni OOO Vance 24-07-87 FS SU
+Forth OO Vickers 25-07-87 IN SU
+C OO Wagner 25-07-87 IN SU
+Robot speciale OO+ Vari 26-07-87 FS SU
+Stella doppia OO+ Heinlein 26-07-87 FS SU
+All'ombra degli dei OO+ Vari 28-07-87 FS SU
+Ponte di cenere OO+ Zelazny 30-07-87 FS SU
+Dizionario del Basic OO+ Lien 30-07-87 IN SU
+La danzatrice di Atlantide OO+ Anderson 02-08-87 FS SU
+Il morbo di San Francesco OO+ Hughes 03-08-87 FS SU
+Intervista sul PC OO+ Didday 03-08-87 IN SU
+Come si programma in Pascal OO Margaroli 04-08-87 IN IT
+Il giorno del cosmo + Malzberg 04-08-87 FS SU
+Programmare in Assembler OO Pinaut 06-08-87 IN FR
+C'era una volta un pianeta OO Iohannis 07-08-87 FS SU
+La ruota a tre punte OO+ Anderson 08-08-87 FS SU
+Il mercante delle stelle OO+ Anderson 08-08-87 FS SU
+Pascal OO+ Le Beaux 09-08-87 IN FR
+Guerra al Grande Nulla OO+ Blish 10-08-87 FS SU
+Il guerriero del tramonto OO+ Lusthaden 10-08-87 FS SU
+Il pianeta dei venti OOO Martin 12-08-87 FS SU
+Le nebbie di Avalon OOO+ Bradley 14-08-87 FA SU
+Guerra fredda OO Pohl 16-08-87 FS SU
+Il mondo di Theodore Sturgeon OO Sturgeon 19-08-87 FS SU
+Robot speciale: antologia OOO Van Vogt 22-08-87 FS SU
+Di terrore si muore OO+ Lee 24-08-87 TR SU
+Parapsicosi O+ Bunker 24-08-87 TR SU
+Gummer Street OO+ Krohn 24-08-87 CO SU
+Arturo e Zoe OO Bushmiller 24-08-87 CO SU
+Avvertite il mondo OO+ Brunner 25-08-87 FS SU
+Le tre stimmate di Palmer Dietr. OO Dick 25-08-87 FS SU
+Leggende alla fine del tempo OO Moorcock 27-08-87 FS SU
+La legge dei Soal OO+ Kilworth 30-08-87 FS SU
+John Carter di Marte OOO Burroghs 10-09-87 FS SU
+La mente di Marte OOO Burroghs 12-09-87 FS SU
+Il dottor Oss OO+ Verne 12-09-87 FS SU
+Il medico delle isole OO+ Magri 15-09-87 RS IT
+Comunicare con il computer OO+ Mauri 16-09-87 IN IT
+Il computer da scrivere OOO Eco 17-09-87 IN IT
+Il mondo dei quanti OO Polkinghorne 17-09-87 SF IT
+Incontri programmati OOO Enstrom 18-09-87 FS SU
+I Promessi Sposi: sunti e note OO Stirati 24-09-87 LI IT
+Guida ai fantasmi inglesi OO Halifax 04-10-87 TR GB
+Bollettino Universitario OO+ U. Padova 09-10-87 SF IT
+I Robot e l'Impero OOO Asimov 01-11-87 FS SU
+Lucrezia Borgia OO Bellonci 01-11-87 ST SU
+Teorie cosmologiche rivali OOO Vari 16-11-87 AS SU
+Gravitazione OO+ Vari 18-11-87 AS SU
+Frankenstein OOO+ Shelley 04-12-87 TR GB
+Cosmolinea B-1 OOO Brown 12-12-87 FS SU
+Nova SF: l'ultima stella OO+ Vari 15-12-87 FS SU
+Fortran 77 OO+ Page 17-12-87 IN SU
+Assembler 68000 OO Erskine 17-12-87 IN SU
+Mastro Don Gesualdo OOO Verga 23-12-87 LI IT
+Storia della Fantascienza I OOO Vari 28-12-87 FS SU
+Disputationum Tusculanarum I OO+ Cicerone 31-12-87 LL IT
diff --git a/pypers/bolzano/db/design.txt b/pypers/bolzano/db/design.txt
new file mode 100755
index 0000000..d80312f
--- /dev/null
+++ b/pypers/bolzano/db/design.txt
@@ -0,0 +1,13 @@
+FIELDS
+ titolo
+ giudizio
+ autore
+ data
+ genere
+ nazionalita
+
+FEATURES
+ add_book
+ add_from_text_file
+ edit_book
+ delete_book
diff --git a/pypers/bolzano/db/esempio.txt b/pypers/bolzano/db/esempio.txt
new file mode 100755
index 0000000..662a0de
--- /dev/null
+++ b/pypers/bolzano/db/esempio.txt
@@ -0,0 +1,3 @@
+Magliette 10
+Pantaloni 20
+Berretti 5
diff --git a/pypers/bolzano/db/esempio2.txt b/pypers/bolzano/db/esempio2.txt
new file mode 100755
index 0000000..4b306fa
--- /dev/null
+++ b/pypers/bolzano/db/esempio2.txt
@@ -0,0 +1,2 @@
+ciao Michele
+ciao Marco
diff --git a/pypers/bolzano/db/iter_utils.py b/pypers/bolzano/db/iter_utils.py
new file mode 100755
index 0000000..78ce0ec
--- /dev/null
+++ b/pypers/bolzano/db/iter_utils.py
@@ -0,0 +1,151 @@
+"""General utilities involving iterables."""
+
+import sets, itertools, os
+try: # Python 2.4 vs. Python 2.3
+ set
+except NameError:
+ from sets import Set as set
+
+def check(it):
+ """
+ Checks if an iterator is empty. Returns a copy of the original iterator.
+
+ >>> it = check(iter([1]))
+ >>> if it: it.next()
+ 1
+ """
+ try:
+ first = it.next()
+ except StopIteration:
+ return False
+ else:
+ return itertools.chain([first], it)
+
+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
+
+def chop(iterable, batchsize):
+ """Chop an iterable. For instance
+
+ >>> 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]]
+
+ It trunks the remainder elements, if the
+ iterable is not divisible by batchsize.
+ """
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+# used in the voting package
+def first_duplicate_ls(it):
+ """Returns None or a list with the duplicated element."""
+ dupl = sets.Set()
+ for el in it:
+ if el in dupl:
+ return [el]
+ else:
+ dupl.add(el)
+
+# useful to display the list of votes in a short form
+class PackedList(list):
+ """Take a list with repetitions and pack it in the form
+
+ PackedList([elements ..]) --> [(number_of_repetitions, element) ...]
+
+ Gives a nice printing representation. Usage:
+ PackedList(<list>, <string-repr-method> = str)
+ It is possible to specify a custom string representation for
+ the list elements."""
+
+ def __init__(self, ls, to_str = str):
+ self.to_str = to_str
+ self.packedls = []
+ self.pack(list(ls))
+ self.extend(self.packedls)
+
+ def pack(self, lst):
+ """Recursive packer. At each call removes an element from ls and
+ adds it to self.packedls. Returns when ls is fully depleted.
+ """
+ if not lst: return
+ el, ls= lst[0], lst[1:]
+ count = 1 # number of repetitions
+ for i, elem in enumerate(ls[:]):
+ if elem == el: # remove the duplicated element
+ del ls[i+1-count] # in the right position
+ count += 1
+ self.packedls.append((count, el))
+ self.pack(ls) # recurse until ls is empty
+
+ def __str__(self):
+ """Returns a table <number of repetions>: <element>"""
+ return "\n".join(["%s: %s" % (t[0], self.to_str(t[1])) for t in self])
+
+# a reiterable cycle going in both directions
+class Cycle(object):
+ def __init__(self, seq):
+ self._list = list(seq)
+ self._len = len(self._list)
+ self.index = 0
+ def __iter__(self):
+ return itertools.cycle(self._list)
+ def next(self):
+ self.index += 1
+ return self()
+ def prev(self):
+ self.index -= 1
+ return self()
+ def __len__(self):
+ return self._len
+ def __call__(self):
+ return self._list[self.index % self._len]
+ def __getitem__(self, i):
+ return self._list[i % self._len]
+ def __setitem__(self, i, v):
+ self._list[i % self._len] = v
+
+############ OLD VERSIONS ###############
+
+## def chop_trunk(it, n = 2):
+## tup = (iter(it), ) * n
+## return itertools.izip(*tup)
+
+## def chop_notrunk(iterable, binsize):
+## bin = []
+## for i, el in enumerate(iterable):
+## bin.append(el)
+## if i % binsize == binsize-1:
+## yield bin; bin = []
+## if bin:
+## yield bin
+
+## def chop(iterable, binsize, trunk=False):
+## if trunk:
+## return chop_trunk(iterable, binsize)
+## else:
+## return chop_notrunk(iterable, binsize)
+
+if __name__ == "__main__": # test Cycle
+ c = Cycle([1,2,3])
+ print c()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
diff --git a/pypers/bolzano/db/mysql/cycle.py b/pypers/bolzano/db/mysql/cycle.py
new file mode 100755
index 0000000..d3d8c80
--- /dev/null
+++ b/pypers/bolzano/db/mysql/cycle.py
@@ -0,0 +1,66 @@
+#from itertools import cycle
+
+class CycleOld(object):
+ def __init__(self, seq):
+ self.seq = list(seq)
+ self.index = 0
+ self.min_index = 0
+ self.max_index = len(self.seq) - 1
+ def __call__(self):
+ return self.seq[self.index]
+ def prev(self):
+ if self.index == self.min_index:
+ self.index = self.max_index
+ else:
+ self.index -= 1
+ return self()
+ def next(self):
+ if self.index == self.max_index:
+ self.index = self.min_index
+ else:
+ self.index += 1
+ return self()
+
+class Cycle(object):
+ def __init__(self, seq):
+ self.seq = list(seq)
+ self.len = len(self.seq)
+ self.index = 0
+ def __call__(self):
+ return self.seq[self.index % self.len]
+ def prev(self):
+ self.index -= 1
+ return self()
+ def next(self):
+ self.index += 1
+ return self()
+
+def chop(seq, binsize):
+ bin = []
+ for i, el in enumerate(seq):
+ bin.append(el)
+ if i % binsize == binsize - 1:
+ yield bin; bin = []
+ if bin:
+ yield bin
+
+if __name__ == "__main__":
+ print list(chop("precipitevolissimevolmente", 3))
+
+# chop([1,2,3,4,5,6], 2)
+# [[1,2], [3,4], [4.6]]
+
+
+## cycle = Cycle2([1,2,3])
+## print cycle()
+## print cycle.prev()
+## print cycle.prev()
+## print cycle.prev()
+## print cycle.prev()
+## print "-"*10
+## print cycle.next()
+## print cycle.next()
+## print cycle.next()
+## print cycle.next()
+
+
diff --git a/pypers/bolzano/db/mysql/ex_dec.py b/pypers/bolzano/db/mysql/ex_dec.py
new file mode 100755
index 0000000..532decd
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ex_dec.py
@@ -0,0 +1,11 @@
+def double(func):
+ def wrapped_func(i):
+ return 2*i
+ return wrapped_func
+
+def f(i):
+ return i
+
+f = double(f)
+
+print f(5)
diff --git a/pypers/bolzano/db/mysql/ex_gen.py b/pypers/bolzano/db/mysql/ex_gen.py
new file mode 100755
index 0000000..82e1f84
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ex_gen.py
@@ -0,0 +1,19 @@
+def prova():
+ yield "ciao"
+ yield "marco"
+
+for line in prova():
+ print line
+
+error = False
+it = prova()
+while not error:
+ try:
+ print it.next()
+ except StopIteration:
+ error = True
+
+print repr("\n".join(prova()))
+
+print str.join("\n", prova())
+["A", 2].join("\n")
diff --git a/pypers/bolzano/db/mysql/insert_books.py b/pypers/bolzano/db/mysql/insert_books.py
new file mode 100755
index 0000000..8fc5aec
--- /dev/null
+++ b/pypers/bolzano/db/mysql/insert_books.py
@@ -0,0 +1,38 @@
+import MySQLdb
+
+DATAFILE = "/home/micheles/md/varie/libri/libri.txt"
+
+def make_record(line):
+ title = line[0:33].strip()
+ score = line[33:38].strip()
+ author = line[38:51].strip()
+ dd, mm, aa = line[51:61].strip().split("-")
+ genre = line[61:65].strip()
+ nation = line[65:67]
+ if "87" <= aa <= "99":
+ aaaa = "19" + aa
+ else:
+ aaaa = "20" + aa
+ date = "-".join([aaaa, mm, dd])
+ return title, score, author, date, genre, nation
+
+def escape(s):
+ s = s.replace("'", "\\'")
+ s = s.replace('"', '\\"')
+ return s
+
+def tup2str(tup):
+ return "(%s)" % ", ".join(['"%s"' % escape(s) for s in tup])
+
+if __name__ == "__main__":
+ # populate the books table of the books database
+ cx = MySQLdb.connect()
+ cu = cx.cursor()
+ cu.execute("create database if not exists books;")
+ cu.execute("use books;")
+ cu.execute("drop table if exists books;")
+ cu.execute("""create table books
+ (title varchar(30), score char(4), author varchar(20), read_date date,
+ genre char(2), nation char(2));""")
+ for line in file(DATAFILE):
+ cu.execute("insert into books values %s;" % tup2str(make_record(line)))
diff --git a/pypers/bolzano/db/mysql/iter_utils.py b/pypers/bolzano/db/mysql/iter_utils.py
new file mode 100755
index 0000000..78ce0ec
--- /dev/null
+++ b/pypers/bolzano/db/mysql/iter_utils.py
@@ -0,0 +1,151 @@
+"""General utilities involving iterables."""
+
+import sets, itertools, os
+try: # Python 2.4 vs. Python 2.3
+ set
+except NameError:
+ from sets import Set as set
+
+def check(it):
+ """
+ Checks if an iterator is empty. Returns a copy of the original iterator.
+
+ >>> it = check(iter([1]))
+ >>> if it: it.next()
+ 1
+ """
+ try:
+ first = it.next()
+ except StopIteration:
+ return False
+ else:
+ return itertools.chain([first], it)
+
+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
+
+def chop(iterable, batchsize):
+ """Chop an iterable. For instance
+
+ >>> 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]]
+
+ It trunks the remainder elements, if the
+ iterable is not divisible by batchsize.
+ """
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+# used in the voting package
+def first_duplicate_ls(it):
+ """Returns None or a list with the duplicated element."""
+ dupl = sets.Set()
+ for el in it:
+ if el in dupl:
+ return [el]
+ else:
+ dupl.add(el)
+
+# useful to display the list of votes in a short form
+class PackedList(list):
+ """Take a list with repetitions and pack it in the form
+
+ PackedList([elements ..]) --> [(number_of_repetitions, element) ...]
+
+ Gives a nice printing representation. Usage:
+ PackedList(<list>, <string-repr-method> = str)
+ It is possible to specify a custom string representation for
+ the list elements."""
+
+ def __init__(self, ls, to_str = str):
+ self.to_str = to_str
+ self.packedls = []
+ self.pack(list(ls))
+ self.extend(self.packedls)
+
+ def pack(self, lst):
+ """Recursive packer. At each call removes an element from ls and
+ adds it to self.packedls. Returns when ls is fully depleted.
+ """
+ if not lst: return
+ el, ls= lst[0], lst[1:]
+ count = 1 # number of repetitions
+ for i, elem in enumerate(ls[:]):
+ if elem == el: # remove the duplicated element
+ del ls[i+1-count] # in the right position
+ count += 1
+ self.packedls.append((count, el))
+ self.pack(ls) # recurse until ls is empty
+
+ def __str__(self):
+ """Returns a table <number of repetions>: <element>"""
+ return "\n".join(["%s: %s" % (t[0], self.to_str(t[1])) for t in self])
+
+# a reiterable cycle going in both directions
+class Cycle(object):
+ def __init__(self, seq):
+ self._list = list(seq)
+ self._len = len(self._list)
+ self.index = 0
+ def __iter__(self):
+ return itertools.cycle(self._list)
+ def next(self):
+ self.index += 1
+ return self()
+ def prev(self):
+ self.index -= 1
+ return self()
+ def __len__(self):
+ return self._len
+ def __call__(self):
+ return self._list[self.index % self._len]
+ def __getitem__(self, i):
+ return self._list[i % self._len]
+ def __setitem__(self, i, v):
+ self._list[i % self._len] = v
+
+############ OLD VERSIONS ###############
+
+## def chop_trunk(it, n = 2):
+## tup = (iter(it), ) * n
+## return itertools.izip(*tup)
+
+## def chop_notrunk(iterable, binsize):
+## bin = []
+## for i, el in enumerate(iterable):
+## bin.append(el)
+## if i % binsize == binsize-1:
+## yield bin; bin = []
+## if bin:
+## yield bin
+
+## def chop(iterable, binsize, trunk=False):
+## if trunk:
+## return chop_trunk(iterable, binsize)
+## else:
+## return chop_notrunk(iterable, binsize)
+
+if __name__ == "__main__": # test Cycle
+ c = Cycle([1,2,3])
+ print c()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
diff --git a/pypers/bolzano/db/mysql/memoize.py b/pypers/bolzano/db/mysql/memoize.py
new file mode 100755
index 0000000..58374f0
--- /dev/null
+++ b/pypers/bolzano/db/mysql/memoize.py
@@ -0,0 +1,21 @@
+import time
+
+memoize_dic = {}
+
+# memoize decorator
+def memoize(func):
+ 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__
+ return wrapped_func
+
+@memoize
+def create_graph(n):
+ time.sleep(3)
+ return "graph%s" % n
+
diff --git a/pypers/bolzano/db/mysql/memoize_simple.py b/pypers/bolzano/db/mysql/memoize_simple.py
new file mode 100755
index 0000000..2439157
--- /dev/null
+++ b/pypers/bolzano/db/mysql/memoize_simple.py
@@ -0,0 +1,18 @@
+import time
+
+## def memoize(func):
+## return func
+
+memoize_dic = {}
+
+def create_graph(n):
+ if n in memoize_dic:
+ return memoize_dic[n]
+ time.sleep(3)
+ result = "graph%s" % n
+ memoize_dic[n] = result
+ return result
+
+print "created ", create_graph(1)
+print "created ", create_graph(1)
+
diff --git a/pypers/bolzano/db/mysql/mysite.py b/pypers/bolzano/db/mysql/mysite.py
new file mode 100755
index 0000000..068ea2a
--- /dev/null
+++ b/pypers/bolzano/db/mysql/mysite.py
@@ -0,0 +1,21 @@
+from quixote_utils import RootDirectory, get_user
+from quixote.publish import SessionPublisher
+from registration import register, login, logout, private
+import MySQLdb
+
+class MySite(RootDirectory):
+ _q_exports = ["register", "login", "logout", "mainpage", "hello"]
+ def __init__(self):
+ self.cx = MySQLdb.connect(db="site_db")
+ register = register
+ login = login
+ logout = logout
+
+ @private
+ def hello(self):
+ return "hello"
+
+if __name__ == "__main__":
+ site = MySite()
+ site.set_default(Publisher=SessionPublisher)
+ site.publish_show("hello")
diff --git a/pypers/bolzano/db/mysql/prova.py b/pypers/bolzano/db/mysql/prova.py
new file mode 100755
index 0000000..7f69e78
--- /dev/null
+++ b/pypers/bolzano/db/mysql/prova.py
@@ -0,0 +1,9 @@
+import MySQLdb
+cx = MySQLdb.connect(db="books")
+cu = cx.cursor()
+
+def getfields(cu, tablename):
+ cu.execute("describe %s;" % tablename)
+ return [tupl[0] for tupl in cu.fetchall()]
+
+print getfields(cu, "books")
diff --git a/pypers/bolzano/db/mysql/quixote_utils.py b/pypers/bolzano/db/mysql/quixote_utils.py
new file mode 100755
index 0000000..0a93d42
--- /dev/null
+++ b/pypers/bolzano/db/mysql/quixote_utils.py
@@ -0,0 +1,191 @@
+import os, sys, time, webbrowser
+from quixote.directory import Directory
+from quixote.publish import Publisher, SessionPublisher
+from quixote.session import Session, SessionManager
+from quixote.server import simple_server
+from quixote.errors import AccessError
+from quixote import get_response, get_user
+from quixote.html import href, htmltext
+from quixote.form.form import Form
+from quixote.form.widget import *
+
+webbrowser.register("konqueror", webbrowser.Konqueror)
+elinks = webbrowser.GenericBrowser('xterm -e elinks %s')
+lynx = webbrowser.GenericBrowser('xterm -e lynx -accept_all_cookies %s')
+webbrowser.register("elinks", webbrowser.GenericBrowser, elinks)
+webbrowser.register("lynx", webbrowser.GenericBrowser, lynx) # second choice
+
+class RecognizeExports(type):
+ def __init__(cls, name, bases, dic):
+ super(RecognizeExports, cls).__init__(cls, name, bases, dic)
+ for k in dic: setattr(cls, k, dic[k])
+ def __setattr__(cls, name, value):
+ if hasattr(value, "_q_exported"):
+ cls._q_exports.append(name)
+ super(RecognizeExports, cls).__setattr__(name, value)
+
+# by definition, the root directory is a singleton
+class RootDirectory(Directory):
+ _q_exports = [""]
+ __metaclass__ = RecognizeExports
+ __Publisher = Publisher
+ __server = simple_server
+ __port = 7080
+
+ def _q_index(self):
+ return "Welcome to the root of your application."
+
+ def set_default(self, server=simple_server, Publisher=Publisher,
+ Session=Session, session_mapping=None, port=7080):
+ self.__server = server
+ self.__Publisher = Publisher
+ self.__Session = Session
+ self.__session_mapping = session_mapping
+ self.__port = port
+
+ __init__ = set_default
+
+ def publish(self):
+ if issubclass(self.__Publisher, SessionPublisher):
+ create_pub = lambda : self.__Publisher(
+ self, SessionManager(self.__Session, self.__session_mapping))
+ else:
+ create_pub = lambda : self.__Publisher(self)
+ self.__server.run(create_pub, '', self.__port)
+
+ def publish_show(self, page="", browser="mozilla"):
+ if os.fork(): # parent
+ self.publish()
+ else: # child
+ webbrowser.get(browser).open(
+ "http://localhost:%s/%s" % (self.__port, page))
+
+
+class UnauthorizedError(AccessError):
+ """The request requires user authentication.
+
+ This subclass of AccessError sends a 401 instead of a 403,
+ hinting that the client should try again with authentication.
+ """
+ status_code = 401
+ title = "Unauthorized"
+ description = "You are not authorized to access this resource."
+
+ def __init__(self, realm='Protected', public_msg=None, private_msg=None):
+ self.realm = realm
+ AccessError.__init__(self, public_msg, private_msg)
+
+ def format(self):
+ get_response().set_header(
+ 'WWW-Authenticate', 'Basic realm="%s"' % self.realm)
+ return AccessError.format(self)
+
+
+class User(object):
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+ def __str__(self):
+ return "<User: %s %s>" % (self.username, self.password)
+ def permissions(self):
+ """Returns the list of methods starting with 'can_'."""
+ return [perm for perm in dir(self) if perm.startswith("can_")]
+
+def public(f):
+ f._q_exported = True
+ return f
+
+class private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join([
+ cls.__name__ for cls in self.valid_groups])
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href(
+ "login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ wrapper.func_name = meth_name
+ wrapper._q_exported = True
+ return wrapper
+
+######################## deprecated ############################
+
+def old_public(f):
+ """Append f.__name__ to the caller's _q_exports. If the caller has
+ no _q_exports, creates it."""
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(f.__name__)
+ return f
+
+class old_private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join([cls.__name__ for cls
+ in self.valid_groups])
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href("login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(meth_name)
+ wrapper.func_name = meth_name
+ return wrapper
+
+from iter_utils import Cycle, chop
+
+class MultipageTable(object):
+ # use Quixote forms
+ def __init__(self, body, header=[], maxsize=20):
+ self.header = header
+ self.maxsize = maxsize
+ self.section = Cycle(chop(body, maxsize))
+ self.sect = self.section[0] # default
+ self.ismultipage = len(self.section) > 1
+
+ def makerow(self, row, header=False):
+ if header:
+ r = " ".join(["<th>%s</th>" % col for col in row])
+ else:
+ r = " ".join(["<td>%s</td>" % col for col in row])
+ return "<tr>%s</tr>" % r
+
+ def maketable(self):
+ if self.ismultipage:
+ form = Form()
+ form.add(SubmitWidget, "prev", "Prev")
+ form.add(SubmitWidget, "next", "Next")
+ if form["next"]: # is submitted
+ self.sect = self.section.next()
+ if form["prev"]: # is submitted
+ self.sect = self.section.prev()
+ yield "Page #%s of %s" % (self.section.index+1, len(self.section))
+ yield "<table border='1'>"
+ if self.header:
+ yield self.makerow(self.header)
+ for row in self.sect:
+ yield self.makerow(row)
+ yield "</table>"
+ if self.ismultipage:
+ yield form.render()
+
+ def render(self):
+ return htmltext("\n").join(map(htmltext, self.maketable()))
diff --git a/pypers/bolzano/db/mysql/quixote_utils24.py b/pypers/bolzano/db/mysql/quixote_utils24.py
new file mode 100755
index 0000000..6288b1b
--- /dev/null
+++ b/pypers/bolzano/db/mysql/quixote_utils24.py
@@ -0,0 +1,259 @@
+"""
+Example of usage:
+
+from ms.quixote_utils24 import SimpleDirectory, Publish
+from quixote.server import twisted_server
+
+class Root(SimpleDirectory):
+ def _q_index(self):
+ return "hello"
+
+Publish(Root(), showpage="", server=twisted_server)
+"""
+import os, sys, threading, webbrowser
+from quixote.directory import Directory
+from quixote.session import Session, SessionManager
+from quixote.server import simple_server
+from quixote.errors import AccessError
+from quixote.form.form import Form
+from quixote.form.widget import StringWidget, PasswordWidget, SubmitWidget
+from quixote import get_response, get_user, get_session
+from quixote.publish import Publisher
+from quixote.html import href, htmltext
+
+################## PUBLICATION MACHINERY ###########################
+
+elinks = webbrowser.GenericBrowser('xterm -e elinks %s')
+lynx = webbrowser.GenericBrowser('xterm -e lynx -accept_all_cookies %s')
+webbrowser.register("elinks", webbrowser.GenericBrowser, elinks)
+webbrowser.register("lynx", webbrowser.GenericBrowser, lynx) # second choice
+webbrowser.register("konqueror", webbrowser.Konqueror)
+
+class Publish(object):
+ def __init__(self, root, Publisher=Publisher, Session=Session,
+ session_mapping=None, server=simple_server,
+ showpage=None, browser="mozilla", host='', port=7080):
+ self.root = root
+ self.Publisher = Publisher
+ self.Session = Session
+ self.session_mapping = session_mapping
+ self.server = server
+ self.host = host
+ self.port = port
+ self.showpage = showpage
+ self.browser = browser
+ if showpage is not None: # wait a bit and then open a browser
+ threading.Timer(0.1, self.show).start()
+ self.publish()
+
+ def make_publisher(self):
+ return self.Publisher(self.root, session_manager=SessionManager(
+ self.Session, self.session_mapping))
+
+ def publish(self):
+ try:
+ self.server.run(self.make_publisher, self.host, self.port)
+ except KeyboardInterrupt:
+ print "Server stopped by CTRL-C."
+
+ def show(self):
+ webbrowser.get(self.browser).open("http://localhost:%s/%s" % (
+ self.port, self.showpage))
+
+def public(f):
+ f._q_exported = True
+ return f
+
+class AutoExport(type):
+ """Attributes of instances of AutoExport with a "_q_exported"
+ flag are automatically added to the class _q_exports list.
+ """
+ def __init__(cls, name, bases, dic):
+ cls._q_exports = sum(
+ (getattr(base, "_q_exports", []) for base in bases), []) or [""]
+ for k in dic:
+ setattr(cls, k, dic[k])
+ super(AutoExport, cls).__init__(name, bases, dic)
+ def __setattr__(cls, name, value):
+ if hasattr(value, "_q_exported"):
+ cls._q_exports.append(name)
+ super(AutoExport, cls).__setattr__(name, value)
+
+class SimpleDirectory(Directory):
+ __metaclass__ = AutoExport
+
+ def _q_index(self): # to be overridden in subclasses
+ return "Welcome to the root of your application."
+
+################# AUTHENTICATION MACHINERY #####################
+
+class User(object):
+ def __init__(self, username=None, password=None):
+ self.username = username
+ self.password = password
+ def __str__(self):
+ return "<User: %s %s>" % (self.username, self.password)
+ def permissions(self):
+ """Returns the list of methods starting with 'can_'."""
+ return [perm for perm in dir(self) if perm.startswith("can_")]
+
+class private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ msg = """\
+You are trying to access a page restricted to %r.
+Please <a href='login'>login</a> as a valid user."""
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join(
+ cls.__name__ for cls in self.valid_groups)
+ return self.msg % valid_groups
+ else:
+ return method(root)
+ meth_name = method.func_name
+ wrapper.func_name = meth_name
+ wrapper._q_exported = True
+ return wrapper
+
+import shelve
+
+def un_pw_form():
+ form = Form()
+ form.add(StringWidget, 'username', title="Username")
+ form.add(PasswordWidget, 'password', title="Password")
+ form.add(SubmitWidget, "submit", "Submit")
+ return form
+
+class WebDirectory(SimpleDirectory):
+ """A simple directory plus a login form with a resume capability."""
+ resume = None
+ registered_users = shelve.open("registered_users")
+
+ @public
+ def mainpage(self):
+ if not get_user():
+ self.resume = 'mainpage'
+ return """Welcome to the best site of the World! If this is
+ your first visit, please <a href='register'>register</a> else
+ <a href='login'>login</a>."""
+ else:
+ return htmltext(
+ "Content of the site."
+ "Click <a href='logout'>here</a> to logout.")
+
+ _q_index = mainpage
+
+ @public
+ def register(self):
+ form = un_pw_form()
+ if not form.is_submitted():
+ return htmltext("<h1>Registration form</h1>") + form.render()
+ else:
+ self.registered_users[form["username"]] = form["password"]
+ self.registered_users.sync()
+ return htmltext("Thank you for registering. ") + self.login()
+
+ @public
+ def login(self, User=User):
+ """Subclassess are free to override the login form. This is an example
+ of how to do it:
+
+ class MyWebDirectory(WebDirectory):
+ @public
+ def login(self, User=MyUserClass):
+ return super(MyWebDirectory, self).login(MyUserClass)
+ """
+ form = un_pw_form()
+ if not form.is_submitted():
+ return htmltext("<h1>Login form</h1>") + form.render()
+ un, pw = form["username"], form["password"]
+ if un not in self.registered_users:
+ return """\
+ You are not a registered user.
+ Please <a href='register'>register</a> first."""
+ elif pw != self.registered_users[un]:
+ return "Wrong password. Please <a href='login'>retry</a>."""
+
+ user = User(un, pw)
+ get_session().set_user(user)
+ msg = "Now you are logged in as %r. " % user.username
+ if self.resume is not None:
+ msg += htmltext(
+ "You can <a href=%r>resume</a> from where you left." % self.resume)
+ return msg
+
+ @private()
+ def logout(self):
+ get_session().set_user(None)
+ return "You are now logged out."
+
+########################### NOT ESSENTIAL STUFF ############################
+
+class UnauthorizedError(AccessError):
+ """The request requires user authentication.
+
+ This subclass of AccessError sends a 401 instead of a 403,
+ hinting that the client should try again with authentication.
+ """
+ status_code = 401
+ title = "Unauthorized"
+ description = "You are not authorized to access this resource."
+
+ def __init__(self, realm='Protected', public_msg=None, private_msg=None):
+ self.realm = realm
+ AccessError.__init__(self, public_msg, private_msg)
+
+ def format(self):
+ get_response().set_header(
+ 'WWW-Authenticate', 'Basic realm="%s"' % self.realm)
+ return AccessError.format(self)
+
+################## UI STUFF ##################################
+
+from ms.iter_utils import Cycle, chop
+
+class MultipageTable(object):
+ # use Quixote forms
+ def __init__(self, body, header=[], maxsize=20):
+ self.header = header
+ self.maxsize = maxsize
+ self.section = Cycle(chop(body, maxsize))
+ self.sect = self.section[0] # default
+ self.ismultipage = len(self.section) > 1
+
+ def makerow(self, row, header=False):
+ if header:
+ r = " ".join("<th>%s</th>" % col for col in row)
+ else:
+ r = " ".join("<td>%s</td>" % col for col in row)
+ return "<tr>%s</tr>" % r
+
+ def maketable(self):
+ #yield "<div align='center'>"
+ if self.ismultipage:
+ form = Form()
+ form.add(SubmitWidget, "prev", "Prev")
+ form.add(SubmitWidget, "next", "Next")
+ if form["next"]: # is submitted
+ self.sect = self.section.next()
+ if form["prev"]: # is submitted
+ self.sect = self.section.prev()
+ yield "Page #%s of %s" % (self.section.index+1, len(self.section))
+ yield "<table border='1'>"
+ if self.header:
+ yield self.makerow(self.header)
+ for row in self.sect:
+ yield self.makerow(row)
+ yield "</table>"
+ if self.ismultipage:
+ yield form.render()
+ #yield "</div>"
+
+ def render(self):
+ return htmltext("\n").join(map(htmltext, self.maketable()))
diff --git a/pypers/bolzano/db/mysql/registration.py b/pypers/bolzano/db/mysql/registration.py
new file mode 100755
index 0000000..7de5c09
--- /dev/null
+++ b/pypers/bolzano/db/mysql/registration.py
@@ -0,0 +1,55 @@
+"""Method to be added to MySite."""
+
+from quixote.form.form import Form
+from quixote.form.widget import *
+from quixote.html import htmltext
+from user_passwd_db import add_user_passwd, valid_user,ErrorAlreadyExistingUser
+from quixote import get_user, get_session
+
+def user_passwd_form():
+ form = Form()
+ form.add(StringWidget, "un", title = "Username")
+ form.add(PasswordWidget, "pw", title = "Password")
+ form.add(SubmitWidget, "submit", "Submit")
+ return form
+
+def register(self):
+ form = user_passwd_form()
+ if form.is_submitted():
+ try:
+ add_user_passwd(self.cx.cursor(), form["un"], form["pw"])
+ except ErrorAlreadyExistingUser, e:
+ return str(e)
+ return "You are now registered!"
+ else:
+ return htmltext("<h1>Registration Form</h1>") + form.render()
+
+def login(self):
+ user = get_user()
+ if user is None:
+ form = user_passwd_form()
+ if form.is_submitted():
+ if valid_user(self.cx.cursor(), form["un"], form["pw"]):
+ get_session().set_user(form["un"]) # this is the point!
+ return "You are logged in."
+ else:
+ return "You are not registered or your password is invalid!"
+ else:
+ return htmltext("<h1>Login Form</h1>") + form.render()
+ else:
+ return "You are already logged in!"
+
+def logout(self):
+ get_session().set_user(None)
+ return "Now you are logged out."
+
+def private(page):
+ def private_page(self):
+ user = get_user()
+ if user:
+ return page(self)
+ else:
+ return "This page can only be accessed by registered members. " \
+ "Please <a href='login'>login</a>."
+ private_page.__name__ = page.__name__
+ return private_page
diff --git a/pypers/bolzano/db/mysql/stat_books.py b/pypers/bolzano/db/mysql/stat_books.py
new file mode 100755
index 0000000..ad499b5
--- /dev/null
+++ b/pypers/bolzano/db/mysql/stat_books.py
@@ -0,0 +1,32 @@
+import MySQLdb, subprocess
+
+def percent(n, ntot):
+ return float(n)/ntot * 100
+
+def gen_histo(cx, kind, datafile):
+ cu = cx.cursor()
+ cu.execute("""select %s, count(*) from books group by %s
+ order by %s;""" % (kind, kind, kind))
+ books_per_kind = cu.fetchall()
+ xtics = []; i = 0
+ f = file(datafile, "w")
+ for k, counts in books_per_kind:
+ print >> f, counts
+ xtics.append("%r %s" % (k, i))
+ i += 1
+ yield "set term png"
+ yield "set output '%s.png'" % datafile[:-4]
+ yield "set style fill solid"
+ yield "set style fill border 2"
+ yield "set xrange [-1:15]"
+ yield "set xtics (%s)" % ", ".join(xtics)
+ yield "plot %r with boxes" % datafile
+
+def make_plot(cx, kind, datafile):
+ gnuplot = subprocess.Popen(["gnuplot"], stdin=subprocess.PIPE)
+ # for line in gen_histo(cx, kind, datafile): print line
+ gnuplot.communicate("\n".join(gen_histo(cx, kind, datafile)))
+
+if __name__ == "__main__":
+ cx = MySQLdb.connect(db="books")
+ make_plot(cx, "nation", "nation.dat")
diff --git a/pypers/bolzano/db/mysql/stat_books_OO.py b/pypers/bolzano/db/mysql/stat_books_OO.py
new file mode 100755
index 0000000..c8a9883
--- /dev/null
+++ b/pypers/bolzano/db/mysql/stat_books_OO.py
@@ -0,0 +1,41 @@
+import MySQLdb, subprocess, os, memoize
+
+def percent(n, ntot):
+ return float(n)/ntot * 100
+
+class BookPlotter(object):
+ def __init__(self, cx):
+ self.cx = cx
+
+ @ memoize.memoize
+ def plot(self, kind, datafile):
+ gnuplot = subprocess.Popen(["gnuplot"], stdin=subprocess.PIPE)
+ gnuplot.communicate("\n".join(self.gen_histo(kind, datafile)))
+
+ def gen_histo(self, kind, datafile):
+ cu = self.cx.cursor()
+ cu.execute("""select %s, count(*) from books group by %s
+ order by %s;""" % (kind, kind, kind))
+ books_per_kind = cu.fetchall()
+ xtics = []; i = 0
+ f = file(datafile, "w")
+ for k, counts in books_per_kind:
+ print >> f, counts
+ xtics.append("%r %s" % (k, i))
+ i += 1
+ self.imagefile = os.path.abspath(datafile[:-4] + ".png")
+ yield "set term png"
+ yield "set output %r" % self.imagefile
+ yield "set style fill solid"
+ yield "set style fill border 2"
+ yield "set xrange [-1:15]"
+ yield "set xtics (%s)" % ", ".join(xtics)
+ yield "plot %r with boxes" % datafile
+
+ def show(self):
+ subprocess.call(["display", self.imagefile])
+
+if __name__ == "__main__":
+ plotter = BookPlotter(MySQLdb.connect(db="books"))
+ plotter.plot("genre", "genre.dat")
+ plotter.show()
diff --git a/pypers/bolzano/db/mysql/ui/HTMLTable.py b/pypers/bolzano/db/mysql/ui/HTMLTable.py
new file mode 100755
index 0000000..5b6fe53
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/HTMLTable.py
@@ -0,0 +1,47 @@
+import os
+from cycle import Cycle, chop
+from quixote.form.form import Form
+import quixote.form.widget as w
+from quixote_utils import RootDirectory, htmltext
+
+class HTMLTable(object):
+ def __init__(self, body, header=None, maxsize=20):
+ self.header = header or []
+ self.body = body
+ self.maxsize = maxsize
+ self.cycle = Cycle(chop(self.body, maxsize))
+
+ def make_row(self, row, typ="td"):
+ return "<tr>%s</tr>" % "".join(["<%s>%s</%s>" %
+ (typ, col, typ) for col in row])
+ def gen_table(self):
+ yield "<table border='1'>"
+ yield self.make_row(self.header, "th")
+ for row in self.cycle():
+ yield self.make_row(row)
+ yield "</table>"
+
+
+ def render(self):
+ return "\n".join(self.gen_table())
+
+if __name__ == "__main__": # test
+
+
+ class Root(RootDirectory):
+ _q_exports = ["show_table"]
+ table = HTMLTable([["a", i] for i in range(100)])
+ def show_table(self):
+ form = Form()
+ form.add(w.SubmitWidget, "prev", "prev")
+ form.add(w.SubmitWidget, "next", "next")
+ if form.is_submitted():
+ if form["prev"]:
+ self.table.cycle.prev()
+ elif form["next"]:
+ self.table.cycle.next()
+ return htmltext(self.table.render()) + form.render()
+
+ Root().publish_show("show_table")
+
+
diff --git a/pypers/bolzano/db/mysql/ui/__init__.py b/pypers/bolzano/db/mysql/ui/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/__init__.py
diff --git a/pypers/bolzano/db/mysql/ui/cycle.py b/pypers/bolzano/db/mysql/ui/cycle.py
new file mode 100755
index 0000000..d3d8c80
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/cycle.py
@@ -0,0 +1,66 @@
+#from itertools import cycle
+
+class CycleOld(object):
+ def __init__(self, seq):
+ self.seq = list(seq)
+ self.index = 0
+ self.min_index = 0
+ self.max_index = len(self.seq) - 1
+ def __call__(self):
+ return self.seq[self.index]
+ def prev(self):
+ if self.index == self.min_index:
+ self.index = self.max_index
+ else:
+ self.index -= 1
+ return self()
+ def next(self):
+ if self.index == self.max_index:
+ self.index = self.min_index
+ else:
+ self.index += 1
+ return self()
+
+class Cycle(object):
+ def __init__(self, seq):
+ self.seq = list(seq)
+ self.len = len(self.seq)
+ self.index = 0
+ def __call__(self):
+ return self.seq[self.index % self.len]
+ def prev(self):
+ self.index -= 1
+ return self()
+ def next(self):
+ self.index += 1
+ return self()
+
+def chop(seq, binsize):
+ bin = []
+ for i, el in enumerate(seq):
+ bin.append(el)
+ if i % binsize == binsize - 1:
+ yield bin; bin = []
+ if bin:
+ yield bin
+
+if __name__ == "__main__":
+ print list(chop("precipitevolissimevolmente", 3))
+
+# chop([1,2,3,4,5,6], 2)
+# [[1,2], [3,4], [4.6]]
+
+
+## cycle = Cycle2([1,2,3])
+## print cycle()
+## print cycle.prev()
+## print cycle.prev()
+## print cycle.prev()
+## print cycle.prev()
+## print "-"*10
+## print cycle.next()
+## print cycle.next()
+## print cycle.next()
+## print cycle.next()
+
+
diff --git a/pypers/bolzano/db/mysql/ui/iter_utils.py b/pypers/bolzano/db/mysql/ui/iter_utils.py
new file mode 100755
index 0000000..78ce0ec
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/iter_utils.py
@@ -0,0 +1,151 @@
+"""General utilities involving iterables."""
+
+import sets, itertools, os
+try: # Python 2.4 vs. Python 2.3
+ set
+except NameError:
+ from sets import Set as set
+
+def check(it):
+ """
+ Checks if an iterator is empty. Returns a copy of the original iterator.
+
+ >>> it = check(iter([1]))
+ >>> if it: it.next()
+ 1
+ """
+ try:
+ first = it.next()
+ except StopIteration:
+ return False
+ else:
+ return itertools.chain([first], it)
+
+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
+
+def chop(iterable, batchsize):
+ """Chop an iterable. For instance
+
+ >>> 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]]
+
+ It trunks the remainder elements, if the
+ iterable is not divisible by batchsize.
+ """
+ it = iter(iterable)
+ while True:
+ batch = list(itertools.islice(it, batchsize))
+ if batch: yield batch
+ else: break
+
+# used in the voting package
+def first_duplicate_ls(it):
+ """Returns None or a list with the duplicated element."""
+ dupl = sets.Set()
+ for el in it:
+ if el in dupl:
+ return [el]
+ else:
+ dupl.add(el)
+
+# useful to display the list of votes in a short form
+class PackedList(list):
+ """Take a list with repetitions and pack it in the form
+
+ PackedList([elements ..]) --> [(number_of_repetitions, element) ...]
+
+ Gives a nice printing representation. Usage:
+ PackedList(<list>, <string-repr-method> = str)
+ It is possible to specify a custom string representation for
+ the list elements."""
+
+ def __init__(self, ls, to_str = str):
+ self.to_str = to_str
+ self.packedls = []
+ self.pack(list(ls))
+ self.extend(self.packedls)
+
+ def pack(self, lst):
+ """Recursive packer. At each call removes an element from ls and
+ adds it to self.packedls. Returns when ls is fully depleted.
+ """
+ if not lst: return
+ el, ls= lst[0], lst[1:]
+ count = 1 # number of repetitions
+ for i, elem in enumerate(ls[:]):
+ if elem == el: # remove the duplicated element
+ del ls[i+1-count] # in the right position
+ count += 1
+ self.packedls.append((count, el))
+ self.pack(ls) # recurse until ls is empty
+
+ def __str__(self):
+ """Returns a table <number of repetions>: <element>"""
+ return "\n".join(["%s: %s" % (t[0], self.to_str(t[1])) for t in self])
+
+# a reiterable cycle going in both directions
+class Cycle(object):
+ def __init__(self, seq):
+ self._list = list(seq)
+ self._len = len(self._list)
+ self.index = 0
+ def __iter__(self):
+ return itertools.cycle(self._list)
+ def next(self):
+ self.index += 1
+ return self()
+ def prev(self):
+ self.index -= 1
+ return self()
+ def __len__(self):
+ return self._len
+ def __call__(self):
+ return self._list[self.index % self._len]
+ def __getitem__(self, i):
+ return self._list[i % self._len]
+ def __setitem__(self, i, v):
+ self._list[i % self._len] = v
+
+############ OLD VERSIONS ###############
+
+## def chop_trunk(it, n = 2):
+## tup = (iter(it), ) * n
+## return itertools.izip(*tup)
+
+## def chop_notrunk(iterable, binsize):
+## bin = []
+## for i, el in enumerate(iterable):
+## bin.append(el)
+## if i % binsize == binsize-1:
+## yield bin; bin = []
+## if bin:
+## yield bin
+
+## def chop(iterable, binsize, trunk=False):
+## if trunk:
+## return chop_trunk(iterable, binsize)
+## else:
+## return chop_notrunk(iterable, binsize)
+
+if __name__ == "__main__": # test Cycle
+ c = Cycle([1,2,3])
+ print c()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.next()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
+ print c.prev()
diff --git a/pypers/bolzano/db/mysql/ui/quixote_utils.py b/pypers/bolzano/db/mysql/ui/quixote_utils.py
new file mode 100755
index 0000000..0a93d42
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/quixote_utils.py
@@ -0,0 +1,191 @@
+import os, sys, time, webbrowser
+from quixote.directory import Directory
+from quixote.publish import Publisher, SessionPublisher
+from quixote.session import Session, SessionManager
+from quixote.server import simple_server
+from quixote.errors import AccessError
+from quixote import get_response, get_user
+from quixote.html import href, htmltext
+from quixote.form.form import Form
+from quixote.form.widget import *
+
+webbrowser.register("konqueror", webbrowser.Konqueror)
+elinks = webbrowser.GenericBrowser('xterm -e elinks %s')
+lynx = webbrowser.GenericBrowser('xterm -e lynx -accept_all_cookies %s')
+webbrowser.register("elinks", webbrowser.GenericBrowser, elinks)
+webbrowser.register("lynx", webbrowser.GenericBrowser, lynx) # second choice
+
+class RecognizeExports(type):
+ def __init__(cls, name, bases, dic):
+ super(RecognizeExports, cls).__init__(cls, name, bases, dic)
+ for k in dic: setattr(cls, k, dic[k])
+ def __setattr__(cls, name, value):
+ if hasattr(value, "_q_exported"):
+ cls._q_exports.append(name)
+ super(RecognizeExports, cls).__setattr__(name, value)
+
+# by definition, the root directory is a singleton
+class RootDirectory(Directory):
+ _q_exports = [""]
+ __metaclass__ = RecognizeExports
+ __Publisher = Publisher
+ __server = simple_server
+ __port = 7080
+
+ def _q_index(self):
+ return "Welcome to the root of your application."
+
+ def set_default(self, server=simple_server, Publisher=Publisher,
+ Session=Session, session_mapping=None, port=7080):
+ self.__server = server
+ self.__Publisher = Publisher
+ self.__Session = Session
+ self.__session_mapping = session_mapping
+ self.__port = port
+
+ __init__ = set_default
+
+ def publish(self):
+ if issubclass(self.__Publisher, SessionPublisher):
+ create_pub = lambda : self.__Publisher(
+ self, SessionManager(self.__Session, self.__session_mapping))
+ else:
+ create_pub = lambda : self.__Publisher(self)
+ self.__server.run(create_pub, '', self.__port)
+
+ def publish_show(self, page="", browser="mozilla"):
+ if os.fork(): # parent
+ self.publish()
+ else: # child
+ webbrowser.get(browser).open(
+ "http://localhost:%s/%s" % (self.__port, page))
+
+
+class UnauthorizedError(AccessError):
+ """The request requires user authentication.
+
+ This subclass of AccessError sends a 401 instead of a 403,
+ hinting that the client should try again with authentication.
+ """
+ status_code = 401
+ title = "Unauthorized"
+ description = "You are not authorized to access this resource."
+
+ def __init__(self, realm='Protected', public_msg=None, private_msg=None):
+ self.realm = realm
+ AccessError.__init__(self, public_msg, private_msg)
+
+ def format(self):
+ get_response().set_header(
+ 'WWW-Authenticate', 'Basic realm="%s"' % self.realm)
+ return AccessError.format(self)
+
+
+class User(object):
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+ def __str__(self):
+ return "<User: %s %s>" % (self.username, self.password)
+ def permissions(self):
+ """Returns the list of methods starting with 'can_'."""
+ return [perm for perm in dir(self) if perm.startswith("can_")]
+
+def public(f):
+ f._q_exported = True
+ return f
+
+class private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join([
+ cls.__name__ for cls in self.valid_groups])
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href(
+ "login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ wrapper.func_name = meth_name
+ wrapper._q_exported = True
+ return wrapper
+
+######################## deprecated ############################
+
+def old_public(f):
+ """Append f.__name__ to the caller's _q_exports. If the caller has
+ no _q_exports, creates it."""
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(f.__name__)
+ return f
+
+class old_private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join([cls.__name__ for cls
+ in self.valid_groups])
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href("login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(meth_name)
+ wrapper.func_name = meth_name
+ return wrapper
+
+from iter_utils import Cycle, chop
+
+class MultipageTable(object):
+ # use Quixote forms
+ def __init__(self, body, header=[], maxsize=20):
+ self.header = header
+ self.maxsize = maxsize
+ self.section = Cycle(chop(body, maxsize))
+ self.sect = self.section[0] # default
+ self.ismultipage = len(self.section) > 1
+
+ def makerow(self, row, header=False):
+ if header:
+ r = " ".join(["<th>%s</th>" % col for col in row])
+ else:
+ r = " ".join(["<td>%s</td>" % col for col in row])
+ return "<tr>%s</tr>" % r
+
+ def maketable(self):
+ if self.ismultipage:
+ form = Form()
+ form.add(SubmitWidget, "prev", "Prev")
+ form.add(SubmitWidget, "next", "Next")
+ if form["next"]: # is submitted
+ self.sect = self.section.next()
+ if form["prev"]: # is submitted
+ self.sect = self.section.prev()
+ yield "Page #%s of %s" % (self.section.index+1, len(self.section))
+ yield "<table border='1'>"
+ if self.header:
+ yield self.makerow(self.header)
+ for row in self.sect:
+ yield self.makerow(row)
+ yield "</table>"
+ if self.ismultipage:
+ yield form.render()
+
+ def render(self):
+ return htmltext("\n").join(map(htmltext, self.maketable()))
diff --git a/pypers/bolzano/db/mysql/ui/table.html b/pypers/bolzano/db/mysql/ui/table.html
new file mode 100755
index 0000000..c914c52
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/table.html
@@ -0,0 +1,23 @@
+<table border='1'>
+<tr><th>Variable</th><th>Value</th></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+</table>
diff --git a/pypers/bolzano/db/mysql/ui/x.html b/pypers/bolzano/db/mysql/ui/x.html
new file mode 100755
index 0000000..999462e
--- /dev/null
+++ b/pypers/bolzano/db/mysql/ui/x.html
@@ -0,0 +1,104 @@
+[['p', 'r', 'e'], ['c', 'i', 'p'], ['i', 't', 'e'], ['v', 'o', 'l'], ['i', 's', 's'], ['i', 'm', 'e'], ['v', 'o', 'l'], ['m', 'e', 'n'], ['t', 'e']]
+<table border='1'>
+<tr><th>Variable</th><th>Value</th></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+<tr><td>a</td><td>1</td></tr>
+</table>
diff --git a/pypers/bolzano/db/mysql/user_passwd_db.py b/pypers/bolzano/db/mysql/user_passwd_db.py
new file mode 100755
index 0000000..4493b7e
--- /dev/null
+++ b/pypers/bolzano/db/mysql/user_passwd_db.py
@@ -0,0 +1,36 @@
+"""Interface to a Username/Password database implemented via MySQL."""
+
+import MySQLdb
+
+class ErrorAlreadyExistingUser(Exception):
+ pass
+
+def create_users_db():
+ cx = MySQLdb.connect()
+ cu = cx.cursor()
+ cu.execute("create database if not exists site_db;")
+ cu.execute("use site_db;")
+ cu.execute("""create table if not exists site_users
+ (username varchar(10), password varchar(10));""")
+ return cx
+
+def valid_user(cu, username, password):
+ return cu.execute("""select * from site_users where username=%r and
+ password=%r;""" % (username, password))
+
+def already_exists(cu, username):
+ return cu.execute("select * from site_users where username=%r;" % username)
+
+def add_user_passwd(cu, username, password):
+ if already_exists(cu, username):
+ raise ErrorAlreadyExistingUser("This username is already taken!")
+ cu.execute("insert into site_users values (%r, %r);" % (
+ username, password))
+
+
+if __name__ == "__main__":
+ cx = create_users_db()
+ cu = cx.cursor()
+ add_user_passwd(cu, "simona", "simona")
+ cx.close()
+
diff --git a/pypers/bolzano/db/mysql/website.py b/pypers/bolzano/db/mysql/website.py
new file mode 100755
index 0000000..9044207
--- /dev/null
+++ b/pypers/bolzano/db/mysql/website.py
@@ -0,0 +1,59 @@
+import MySQLdb
+from quixote_utils import RootDirectory, MultipageTable
+from ui.HTMLTable import HTMLTable
+from quixote.html import htmltext
+from cycle import Cycle, chop
+from quixote.form.form import Form
+import quixote.form.widget as w
+from quixote_utils import RootDirectory, htmltext
+from stat_books_OO import BookPlotter
+
+def getfields(cu, tablename):
+ cu.execute("describe %s;" % tablename)
+ return [tupl[0] for tupl in cu.fetchall()]
+
+class Archive(RootDirectory):
+ _q_exports = ["show_books", "show_table", "show_histo", "select_histo"]
+
+ def __init__(self, db):
+ self.cx = MySQLdb.connect(db=db)
+ self.plotter = BookPlotter(self.cx)
+
+ def _q_index(self):
+ return "Archivio libri"
+
+ def show_books(self):
+ cu = self.cx.cursor()
+ hits = cu.execute("select * from books;")
+ self.table = HTMLTable(cu.fetchall(),
+ header=getfields(cu, "books"))
+ return """Found %s hits.
+ Click <a href='show_table'>here</a> to see the results.""" % hits
+
+ def show_table(self):
+ form = Form()
+ form.add(w.SubmitWidget, "prev", "prev")
+ form.add(w.SubmitWidget, "next", "next")
+ if form.is_submitted():
+ if form["prev"]:
+ self.table.cycle.prev()
+ elif form["next"]:
+ self.table.cycle.next()
+ return htmltext(self.table.render()) + form.render()
+
+ def select_histo(self):
+ form = Form()
+ form.add(w.SingleSelectWidget, "kind", options=["genre", "nation"])
+ form.add(w.SubmitWidget, "submit", "Choose!")
+ if form.is_submitted():
+ return self.show_histo(form["kind"])
+ else:
+ return form.render()
+
+ def show_histo(self, kind="genre"):
+ self.plotter.plot(kind, kind + ".dat")
+ return """<h1>Histogram by %s</h1>
+ <img src='file://%s'>
+ """ % (kind, self.plotter.imagefile)
+
+Archive("books").publish_show("select_histo")
diff --git a/pypers/bolzano/db/mysql/x.html b/pypers/bolzano/db/mysql/x.html
new file mode 100755
index 0000000..4a29f1c
--- /dev/null
+++ b/pypers/bolzano/db/mysql/x.html
@@ -0,0 +1,2 @@
+<h1>Histogram by genre</h1>
+ <img src='genre.png'>
diff --git a/pypers/bolzano/db/nomi.txt b/pypers/bolzano/db/nomi.txt
new file mode 100755
index 0000000..2add17e
--- /dev/null
+++ b/pypers/bolzano/db/nomi.txt
@@ -0,0 +1,4 @@
+Michele Simionato 0699XXXXX
+Antonio Russo 333XXXXXX
+Pinco Pallino 347XXXXXX
+
diff --git a/pypers/bolzano/db/populate_db.py b/pypers/bolzano/db/populate_db.py
new file mode 100755
index 0000000..bb0753e
--- /dev/null
+++ b/pypers/bolzano/db/populate_db.py
@@ -0,0 +1,4 @@
+from bookdb import BookDatabase
+bd = BookDatabase("books")
+bd.add_from_file("books87.txt")
+bd.close()
diff --git a/pypers/bolzano/db/quixote_utils.py b/pypers/bolzano/db/quixote_utils.py
new file mode 100755
index 0000000..8614109
--- /dev/null
+++ b/pypers/bolzano/db/quixote_utils.py
@@ -0,0 +1,185 @@
+import os, sys, time, webbrowser
+from quixote.directory import Directory
+from quixote.publish import Publisher
+from quixote.session import Session, SessionManager
+from quixote.server import simple_server
+from quixote.errors import AccessError
+from quixote import get_response, get_user
+from quixote.html import href
+
+elinks = webbrowser.GenericBrowser('xterm -e elinks %s')
+lynx = webbrowser.GenericBrowser('xterm -e lynx -accept_all_cookies %s')
+webbrowser.register("elinks", webbrowser.GenericBrowser, elinks)
+webbrowser.register("lynx", webbrowser.GenericBrowser, lynx) # second choice
+webbrowser.register("konqueror", webbrowser.Konqueror)
+
+class RecognizeExports(type):
+ def __init__(cls, name, bases, dic):
+ super(RecognizeExports, cls).__init__(cls, name, bases, dic)
+ for k in dic: setattr(cls, k, dic[k])
+ def __setattr__(cls, name, value):
+ if hasattr(value, "_q_exported"):
+ cls._q_exports.append(name)
+ super(RecognizeExports, cls).__setattr__(name, value)
+
+# by definition, the root directory is a singleton
+class RootDirectory(Directory):
+ _q_exports = [""]
+ __metaclass__ = RecognizeExports
+ __Publisher = Publisher
+ __port = 7080
+
+ def _q_index(self):
+ return "Welcome to the root of your application."
+
+ def __init__(self, server=simple_server, Publisher=Publisher,
+ Session=Session, session_mapping=None, port=7080):
+ self.__server = server
+ self.__Publisher = Publisher
+ self.__Session = Session
+ self.__session_mapping = session_mapping
+ self.__port = port
+
+ #set_default == __init__
+
+ def publish(self):
+ create_pub = lambda : self.__Publisher(self)
+ self.__server.run(create_pub, '', self.__port)
+
+ def publish_show(self, page="", browser="mozilla"):
+ if os.fork(): # parent
+ self.publish()
+ else: # child
+ webbrowser.get(browser).open(
+ "http://localhost:%s/%s" % (self.__port, page))
+
+
+class UnauthorizedError(AccessError):
+ """The request requires user authentication.
+
+ This subclass of AccessError sends a 401 instead of a 403,
+ hinting that the client should try again with authentication.
+ """
+ status_code = 401
+ title = "Unauthorized"
+ description = "You are not authorized to access this resource."
+
+ def __init__(self, realm='Protected', public_msg=None, private_msg=None):
+ self.realm = realm
+ AccessError.__init__(self, public_msg, private_msg)
+
+ def format(self):
+ get_response().set_header(
+ 'WWW-Authenticate', 'Basic realm="%s"' % self.realm)
+ return AccessError.format(self)
+
+
+class User(object):
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+ def __str__(self):
+ return "<User: %s %s>" % (self.username, self.password)
+ def permissions(self):
+ """Returns the list of methods starting with 'can_'."""
+ return [perm for perm in dir(self) if perm.startswith("can_")]
+
+def public(f):
+ f._q_exported = True
+ return f
+
+class private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join(
+ cls.__name__ for cls in self.valid_groups)
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href(
+ "login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ wrapper.func_name = meth_name
+ wrapper._q_exported = True
+ return wrapper
+
+######################## deprecated ############################
+
+def old_public(f):
+ """Append f.__name__ to the caller's _q_exports. If the caller has
+ no _q_exports, creates it."""
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(f.__name__)
+ return f
+
+class old_private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join(cls.__name__ for cls in self.valid_groups)
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href("login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(meth_name)
+ wrapper.func_name = meth_name
+ return wrapper
+
+from ms.iter_utils import Cycle, chop
+
+class MultipageTable(object):
+ # use Quixote forms
+ def __init__(self, body, header=[], maxsize=20):
+ self.header = header
+ self.maxsize = maxsize
+ self.section = Cycle(chop(body, maxsize))
+ self.sect = self.section[0] # default
+ self.ismultipage = len(self.section) > 1
+
+ def makerow(self, row, header=False):
+ if header:
+ r = " ".join("<th>%s</th>" % col for col in row)
+ else:
+ r = " ".join("<td>%s</td>" % col for col in row)
+ return "<tr>%s</tr>" % r
+
+ def maketable(self):
+ #yield "<div align='center'>"
+ if self.ismultipage:
+ form = Form()
+ form.add(SubmitWidget, "prev", "Prev")
+ form.add(SubmitWidget, "next", "Next")
+ if form["next"]: # is submitted
+ self.sect = self.section.next()
+ if form["prev"]: # is submitted
+ self.sect = self.section.prev()
+ yield "Page #%s of %s" % (self.section.index+1, len(self.section))
+ yield "<table border='1'>"
+ if self.header:
+ yield self.makerow(self.header)
+ for row in self.sect:
+ yield self.makerow(row)
+ yield "</table>"
+ if self.ismultipage:
+ yield form.render()
+ #yield "</div>"
+
+ def render(self):
+ return htmltext("\n").join(map(htmltext, self.maketable()))
diff --git a/pypers/bolzano/db/readbooks.py b/pypers/bolzano/db/readbooks.py
new file mode 100755
index 0000000..bf5ec16
--- /dev/null
+++ b/pypers/bolzano/db/readbooks.py
@@ -0,0 +1,36 @@
+class Book(object):
+ title_span = 0, 33
+ rate_span = 33, 38
+ author_span = 38, 51
+ date_span = 51, 61
+ genre_span = 61, 65
+ nation_span = 65, 67
+
+ def __init__(self, title, rate, author, date, genre, nation):
+ self.title = title
+ self.rate = rate
+ self.author = author
+ self.date = date
+ self.genre = genre
+ self.nation = nation
+
+ def __repr__(self):
+ return self.title + self.rate + self.author + self.date + \
+ self.genre + self.nation
+
+class Library(object):
+ def __init__(self):
+ self.books = []
+ def read(self, fname):
+ for line in file(fname):
+ title = line.__getslice__(*Book.title_span)
+ rate = line.__getslice__(*Book.rate_span)
+ author = line.__getslice__(*Book.author_span)
+ date = line.__getslice__(*Book.date_span)
+ genre = line.__getslice__(*Book.genre_span)
+ nation = line.__getslice__(*Book.nation_span)
+ self.books.append(Book(title, rate, author, date, genre, nation))
+
+lib = Library()
+lib.read("books87.txt")
+
diff --git a/pypers/bolzano/db/simpledb.py b/pypers/bolzano/db/simpledb.py
new file mode 100755
index 0000000..3ebe760
--- /dev/null
+++ b/pypers/bolzano/db/simpledb.py
@@ -0,0 +1,71 @@
+import os, shelve
+
+from Tkinter import *
+from Tkconstants import *
+
+class NameNumber(Frame):
+ def __init__(self, db, name, number, row, **kw):
+ Frame.__init__(self, db.root, **kw)
+ self.namelabel = Entry(db.root)
+ self.phonelabel = Entry(db.root)
+
+ def delete_key(name=name): db.del_data(name)
+
+ self.deletebutton = Button(db.root, text="del", command=delete_key)
+ self.namelabel.insert(END, name)
+ self.phonelabel.insert(END, number)
+ self.namelabel.grid(row=row, column=0)
+ self.phonelabel.grid(row=row, column=1)
+ self.deletebutton.grid(row=row, column=2)
+
+class SimpleDatabase(object):
+ def __init__(self):
+ self.root = Tk()
+ def read_data(self, datafile):
+ self.phone = shelve.open(datafile)
+ def read_text(self, datafile):
+ self.phone = shelve.open(datafile[:-4])
+ for line in file(datafile):
+ if not line.strip(): continue
+ name, number = line[:18].strip(), line[18:-1]
+ self.phone[name] = number
+ def change_data(self, key):
+ pass
+ def add_data(self, key):
+ pass
+ def del_data(self, key):
+ del self.phone[key]
+ i = self.entrydict[key]
+ del self.entrywidget[i]
+
+ def commit(self):
+ for entry in self.entrywidget:
+ name = entry.namelabel.get()
+ number = entry.phonelabel.get()
+ self.phone[name] = number
+ self.phone.sync()
+ print "Committed!"
+
+ def show_data(self):
+ row = 0
+ self.entrywidget = []
+ self.entrydict = {}
+ for name in self.phone:
+ namenumber = NameNumber(self, name, self.phone[name], row)
+ namenumber.grid()
+ self.entrywidget.append(namenumber)
+ self.entrydict[name]=row
+ row += 1
+
+ self.commit_button = Button(
+ self.root,
+ text = "Commit",
+ command = self.commit)
+ self.commit_button.grid()
+
+if __name__ == "__main__":
+ phonebook = SimpleDatabase()
+ # phonebook.read_text("nomi.txt")
+ phonebook.read_data("nomi")
+ phonebook.show_data()
+ phonebook.root.mainloop()
diff --git a/pypers/bolzano/db/sqlbooks.py b/pypers/bolzano/db/sqlbooks.py
new file mode 100755
index 0000000..215aebe
--- /dev/null
+++ b/pypers/bolzano/db/sqlbooks.py
@@ -0,0 +1,30 @@
+"""
+Add into the books database a books table generated from text files.
+"""
+
+def book_from(filename):
+ for id, line in enumerate(file(filename)):
+ title = line[0:33].strip()
+ score = line[33:38].strip()
+ author = line[38:51].strip()
+ dd, mm, aa = line[51:61].strip().split("-")
+ genre = line[61:65].strip()
+ nation = line[65:67]
+ if "87" <= aa <= "99":
+ aaaa = "19" + aa
+ else:
+ aaaa = "20" + aa
+ date = "-".join([aaaa, mm, dd])
+ yield id + 1, title, score, author, date, genre, nation
+
+def db_from_file(fname):
+ print "CREATE DATABASE IF NOT EXISTS books; use books;"
+ print """CREATE TABLE IF NOT EXISTS books
+ (id SMALLINT(3) NOT NULL, title VARCHAR(30), score CHAR(4),
+ author VARCHAR(20), date CHAR(10), genre CHAR(2), nation char(2),
+ PRIMARY KEY (id));"""
+ print "INSERT INTO books VALUES %s" % ",".join(
+ [str(vals) for vals in book_from(fname)])
+
+if __name__ == "__main__":
+ db_from_file("books87.txt")
diff --git a/pypers/bolzano/db/sqlreader.py b/pypers/bolzano/db/sqlreader.py
new file mode 100755
index 0000000..8d373dc
--- /dev/null
+++ b/pypers/bolzano/db/sqlreader.py
@@ -0,0 +1,10 @@
+import MySQLdb as m
+
+conn = m.connect()
+c = conn.cursor()
+print dir(c)
+print c.execute("use books;")
+print c.execute("select * from books;")
+print c.fetchone()
+print c.fetchmany(3)
+print c.fetchall()
diff --git a/pypers/bolzano/gui/canvas.py b/pypers/bolzano/gui/canvas.py
new file mode 100755
index 0000000..ffb89f3
--- /dev/null
+++ b/pypers/bolzano/gui/canvas.py
@@ -0,0 +1,18 @@
+from Tkinter import *
+
+def on_click(event):
+ print event
+ print event.x, event.y
+
+root = Tk()
+
+c = Canvas()
+
+c.config(background="red", selectforeground="green")
+c.create_rectangle(0, 0, 100, 100)
+c.create_line(0, 0, 10, 100, 200, 300)
+
+c.bind("<Button-1>", on_click)
+c.pack()
+
+mainloop()
diff --git a/pypers/bolzano/gui/canvas2.py b/pypers/bolzano/gui/canvas2.py
new file mode 100755
index 0000000..c10b476
--- /dev/null
+++ b/pypers/bolzano/gui/canvas2.py
@@ -0,0 +1,19 @@
+from Tkinter import *
+
+def on_keypress(event):
+ print dir(event)
+
+
+root = Tk()
+
+c = Canvas(takefocus=1)
+
+c.config(background="red", selectforeground="green")
+c.create_rectangle(0, 0, 100, 100)
+c.create_line(0, 0, 10, 100, 200, 300)
+
+c.bind("<KeyPress>", on_keypress)
+c.focus()
+c.pack()
+
+mainloop()
diff --git a/pypers/bolzano/gui/keys.py b/pypers/bolzano/gui/keys.py
new file mode 100755
index 0000000..bc1d9de
--- /dev/null
+++ b/pypers/bolzano/gui/keys.py
@@ -0,0 +1,15 @@
+from Tkinter import *
+
+class Output(Label):
+ def printkey(self, event):
+ self.config(text=event.keysym)
+
+root = Tk()
+label = Label(root, text='Press a key...')
+output = Output(root, takefocus=1)
+label.pack()
+output.pack()
+output.focus()
+output.bind('<KeyPress>', output.printkey)
+root.mainloop()
+
diff --git a/pypers/bolzano/gui/menus.py b/pypers/bolzano/gui/menus.py
new file mode 100755
index 0000000..dc10596
--- /dev/null
+++ b/pypers/bolzano/gui/menus.py
@@ -0,0 +1,33 @@
+from Tkinter import *
+
+root = Tk()
+
+def hello():
+ print "hello!"
+
+menubar = Menu(root)
+
+# create a pulldown menu, and add it to the menu bar
+filemenu = Menu(menubar, tearoff=0)
+
+filemenu.add_command(label="Open", command=hello)
+filemenu.add_command(label="Save", command=hello)
+filemenu.add_separator()
+filemenu.add_command(label="Exit", command=root.quit)
+menubar.add_cascade(label="File", menu=filemenu)
+
+# create more pulldown menus
+editmenu = Menu(menubar, tearoff=0)
+editmenu.add_command(label="Cut", command=hello)
+editmenu.add_command(label="Copy", command=hello)
+editmenu.add_command(label="Paste", command=hello)
+menubar.add_cascade(label="Edit", menu=editmenu)
+
+helpmenu = Menu(menubar, tearoff=0)
+helpmenu.add_command(label="About", command=hello)
+menubar.add_cascade(label="Help", menu=helpmenu)
+
+# display the menu
+root.config(menu=menubar)
+
+mainloop()
diff --git a/pypers/bolzano/gui/menus2.py b/pypers/bolzano/gui/menus2.py
new file mode 100755
index 0000000..f1f660e
--- /dev/null
+++ b/pypers/bolzano/gui/menus2.py
@@ -0,0 +1,23 @@
+from Tkinter import *
+
+root = Tk()
+
+def hello():
+ print "hello!"
+
+# create a popup menu
+menu = Menu(root, tearoff=0)
+menu.add_command(label="Undo", command=hello)
+menu.add_command(label="Redo", command=hello)
+
+# create a canvas
+frame = Frame(root, width=512, height=512)
+frame.pack()
+
+def popup(event):
+ menu.post(event.x_root, event.y_root)
+
+# attach popup to canvas
+frame.bind("<Button-3>", popup)
+
+mainloop()
diff --git a/pypers/bolzano/kirby_ex.py b/pypers/bolzano/kirby_ex.py
new file mode 100755
index 0000000..4fa9893
--- /dev/null
+++ b/pypers/bolzano/kirby_ex.py
@@ -0,0 +1,5 @@
+from kirbybase import KirbyBase
+
+db = KirbyBase()
+print db.select('/home/micheles/packages/KirbyBase-1.8/plane.tbl',
+ ['country','speed'],['USA','>400'])
diff --git a/pypers/bolzano/links.html b/pypers/bolzano/links.html
new file mode 100755
index 0000000..eee92cc
--- /dev/null
+++ b/pypers/bolzano/links.html
@@ -0,0 +1,58 @@
+<?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>RIFERIMENTI</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="riferimenti">
+<h1 class="title">RIFERIMENTI</h1>
+<div class="section" id="in-inglese">
+<h1><a name="in-inglese">In Inglese</a></h1>
+<p>Home Page Ufficiale:: www.python.org
+Newsgroup: groups-beta.google.com/group/comp.lang.python
+Tutor:</p>
+</div>
+<div class="section" id="in-italiano">
+<h1><a name="in-italiano">In Italiano</a></h1>
+<p>Home Page Ufficiale: www.python.it
+Newsgroup: groups-beta.google.com/group/it.comp.lang.python</p>
+</div>
+<div class="section" id="libri-tutorials-in-rete">
+<h1><a name="libri-tutorials-in-rete">Libri/Tutorials in rete</a></h1>
+<p>Tradotti in italiano in www.zonapython.it/doc/</p>
+<blockquote>
+<ul class="simple">
+<li>How to Think Like a Computer Scientist</li>
+<li>Dive Into Python</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="articoli-riviste">
+<h1><a name="articoli-riviste">Articoli/riviste</a></h1>
+<ol class="arabic simple">
+<li>la rivista italiana di Python: www.pyj.it</li>
+<li>IBMdeveloperWorks: www-130.ibm.com/developerworks/linux</li>
+<li>O' Reilly On LAMP: www.onlamp.com</li>
+</ol>
+</div>
+<div class="section" id="libri-cartacei">
+<h1><a name="libri-cartacei">Libri cartacei</a></h1>
+<p>Due soli libri in Italiano:</p>
+<ol class="arabic simple">
+<li>David Brueck, Stephan Tanner, Python 2.1 Tutto e oltre (Apogeo)</li>
+<li>Mark Lutz, David Asher, Programmare con Python (Hoeply)</li>
+</ol>
+<p>Decine di libri in inglesi. Consigliati:</p>
+<ol class="arabic simple">
+<li>Alex Martelli, Python in a Nushell (O' Reilly)</li>
+<li>Alex Martelli, Python Cookbook (O' Reilly)</li>
+<li>Mark Lutz, David Asher, Learning Python</li>
+</ol>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/bolzano/links.tex b/pypers/bolzano/links.tex
new file mode 100755
index 0000000..cac4cb1
--- /dev/null
+++ b/pypers/bolzano/links.tex
@@ -0,0 +1,176 @@
+\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{RIFERIMENTI}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={RIFERIMENTI}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+
+
+%___________________________________________________________________________
+
+\hypertarget{in-inglese}{}
+\pdfbookmark[0]{In Inglese}{in-inglese}
+\section*{In Inglese}
+
+Home Page Ufficiale:: www.python.org
+Newsgroup: groups-beta.google.com/group/comp.lang.python
+Tutor:
+
+
+%___________________________________________________________________________
+
+\hypertarget{in-italiano}{}
+\pdfbookmark[0]{In Italiano}{in-italiano}
+\section*{In Italiano}
+
+Home Page Ufficiale: www.python.it
+Newsgroup: groups-beta.google.com/group/it.comp.lang.python
+
+
+%___________________________________________________________________________
+
+\hypertarget{libri-tutorials-in-rete}{}
+\pdfbookmark[0]{Libri/Tutorials in rete}{libri-tutorials-in-rete}
+\section*{Libri/Tutorials in rete}
+
+Tradotti in italiano in www.zonapython.it/doc/
+\begin{quote}
+\begin{itemize}
+\item {}
+How to Think Like a Computer Scientist
+
+\item {}
+Dive Into Python
+
+\end{itemize}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{articoli-riviste}{}
+\pdfbookmark[0]{Articoli/riviste}{articoli-riviste}
+\section*{Articoli/riviste}
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1}.}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+la rivista italiana di Python: www.pyj.it
+
+\item {}
+IBMdeveloperWorks: www-130.ibm.com/developerworks/linux
+
+\item {}
+O' Reilly On LAMP: www.onlamp.com
+
+\end{list}
+
+
+%___________________________________________________________________________
+
+\hypertarget{libri-cartacei}{}
+\pdfbookmark[0]{Libri cartacei}{libri-cartacei}
+\section*{Libri cartacei}
+
+Due soli libri in Italiano:
+\newcounter{listcnt2}
+\begin{list}{\arabic{listcnt2}.}
+{
+\usecounter{listcnt2}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+David Brueck, Stephan Tanner, Python 2.1 Tutto e oltre (Apogeo)
+
+\item {}
+Mark Lutz, David Asher, Programmare con Python (Hoeply)
+
+\end{list}
+
+Decine di libri in inglesi. Consigliati:
+\newcounter{listcnt3}
+\begin{list}{\arabic{listcnt3}.}
+{
+\usecounter{listcnt3}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+Alex Martelli, Python in a Nushell (O' Reilly)
+
+\item {}
+Alex Martelli, Python Cookbook (O' Reilly)
+
+\item {}
+Mark Lutz, David Asher, Learning Python
+
+\end{list}
+
+\end{document}
+
diff --git a/pypers/bolzano/links.txt b/pypers/bolzano/links.txt
new file mode 100755
index 0000000..ce520bf
--- /dev/null
+++ b/pypers/bolzano/links.txt
@@ -0,0 +1,44 @@
+RIFERIMENTI
+===========
+
+In Inglese
+----------------------------
+
+Home Page Ufficiale:: www.python.org
+Newsgroup: groups-beta.google.com/group/comp.lang.python
+Tutor:
+
+In Italiano
+-------------------------------
+
+Home Page Ufficiale: www.python.it
+Newsgroup: groups-beta.google.com/group/it.comp.lang.python
+
+Libri/Tutorials in rete
+------------------------
+
+Tradotti in italiano in www.zonapython.it/doc/
+
+ - How to Think Like a Computer Scientist
+ - Dive Into Python
+
+Articoli/riviste
+-----------------------
+
+1. la rivista italiana di Python: www.pyj.it
+2. IBMdeveloperWorks: www-130.ibm.com/developerworks/linux
+3. O' Reilly On LAMP: www.onlamp.com
+
+Libri cartacei
+------------------
+
+Due soli libri in Italiano:
+
+1. David Brueck, Stephan Tanner, Python 2.1 Tutto e oltre (Apogeo)
+2. Mark Lutz, David Asher, Programmare con Python (Hoeply)
+
+Decine di libri in inglesi. Consigliati:
+
+1. Alex Martelli, Python in a Nushell (O' Reilly)
+2. Alex Martelli, Python Cookbook (O' Reilly)
+3. Mark Lutz, David Asher, Learning Python
diff --git a/pypers/bolzano/player/LabelWithImages.py b/pypers/bolzano/player/LabelWithImages.py
new file mode 100755
index 0000000..a4c740a
--- /dev/null
+++ b/pypers/bolzano/player/LabelWithImages.py
@@ -0,0 +1,14 @@
+from Tkinter import *
+from itertools import cycle
+
+class LabelWithImages(Label):
+
+ def __init__(self, master, images, **kw):
+ Label.__init__(self, master, **kw)
+ self.images = cycle(images)
+
+ def start_animation(self):
+ self.config(image = self.images.next())
+ self.after(2000, self.start_animation)
+
+
diff --git a/pypers/bolzano/player/animated_text1.py b/pypers/bolzano/player/animated_text1.py
new file mode 100755
index 0000000..cfc8b1e
--- /dev/null
+++ b/pypers/bolzano/player/animated_text1.py
@@ -0,0 +1,33 @@
+from Tkinter import *
+word = "Anime"
+DELTA_T = 100
+
+class Spacer(object):
+ nspaces = 0
+ action = "increment"
+ MAXSPACES = 10
+ def manage_spaces(self):
+ if self.action == "increment":
+ self.nspaces += 1
+ elif self.action == "decrement":
+ self.nspaces -= 1
+ if self.nspaces > self.MAXSPACES:
+ self.action = "decrement"
+ if self.nspaces < 0:
+ self.action = "increment"
+ return " " * self.nspaces
+
+spacer = Spacer()
+
+def make_animation(label):
+ spaces = spacer.manage_spaces()
+ centered_word = spaces.join(word).center(50)
+ label.config(text=centered_word)
+ root.after(DELTA_T, make_animation, label)
+
+if __name__ == "__main__":
+ root = Tk()
+ lab = Label(root)
+ lab.pack()
+ root.after(DELTA_T, make_animation, lab)
+ root.mainloop()
diff --git a/pypers/bolzano/player/animated_text2.py b/pypers/bolzano/player/animated_text2.py
new file mode 100755
index 0000000..5743a70
--- /dev/null
+++ b/pypers/bolzano/player/animated_text2.py
@@ -0,0 +1,38 @@
+from Tkinter import *
+
+class Spacer(object):
+ nspaces = 0
+ action = "increment"
+ MAXSPACES = 10
+ def manage_spaces(self):
+ if self.action == "increment":
+ self.nspaces += 1
+ elif self.action == "decrement":
+ self.nspaces -= 1
+ if self.nspaces > self.MAXSPACES:
+ self.action = "decrement"
+ if self.nspaces < 0:
+ self.action = "increment"
+ return " " * self.nspaces
+
+spacer = Spacer()
+
+class AnimatedLabel(Label):
+ DELTA_T = 100
+ def __init__(self, master, **kw):
+ Label.__init__(self, master, **kw)
+ self.master = master
+ self.word = self.cget("text")
+
+ def start_animation(self):
+ spaces = spacer.manage_spaces()
+ centered_word = spaces.join(self.word).center(50)
+ self.config(text=centered_word)
+ self.master.after(self.DELTA_T, self.start_animation)
+
+if __name__ == "__main__":
+ root = Tk()
+ lab = AnimatedLabel(root, text="Anime")
+ lab.pack()
+ lab.start_animation()
+ root.mainloop()
diff --git a/pypers/bolzano/player/animeplayer.py b/pypers/bolzano/player/animeplayer.py
new file mode 100755
index 0000000..321036b
--- /dev/null
+++ b/pypers/bolzano/player/animeplayer.py
@@ -0,0 +1,40 @@
+import os
+from Tkinter import *
+from Tkconstants import *
+from animated_text2 import AnimatedLabel
+from LabelWithImages import LabelWithImages
+from musicbox import Musicbox, ScrollableMusicbox
+
+def get_photoimages(directory):
+ for fname in os.listdir(directory):
+ if fname.endswith(".gif"):
+ yield PhotoImage(file=fname)
+
+
+root = Tk()
+label = LabelWithImages(root,
+ images = get_photoimages("."),
+ height = 100)
+label.start_animation()
+label.pack()
+
+a1 = AnimatedLabel(root, text = "Anime", fg="red")
+a2 = AnimatedLabel(root, text = "Music", fg="green")
+a1.pack()
+a2.pack()
+
+a1.start_animation()
+a2.start_animation()
+musicbox = Musicbox(
+ root, [f for f in os.listdir(".") if f.endswith(".mp3")])
+musicframe = ScrollableMusicbox(root, musicbox)
+
+musicframe.pack()
+
+exit_label = Label(text = "Click here to exit")
+exit_label.bind("<Button-1>", lambda e: musicbox.quit())
+exit_label.pack(side=BOTTOM)
+
+root.protocol("WM_DELETE_WINDOW", musicbox.quit)
+root.mainloop()
+
diff --git a/pypers/bolzano/player/bind_example.py b/pypers/bolzano/player/bind_example.py
new file mode 100755
index 0000000..73d0dfe
--- /dev/null
+++ b/pypers/bolzano/player/bind_example.py
@@ -0,0 +1,15 @@
+from Tkinter import *
+
+root = Tk()
+
+def callback(event):
+ print "clicked at", event.x, event.y
+
+def quit(event):
+ root.quit()
+
+lab = Label(root, bg="red")
+lab.bind("<1>", quit)
+lab.pack()
+
+root.mainloop()
diff --git a/pypers/bolzano/player/call_mpg123.py b/pypers/bolzano/player/call_mpg123.py
new file mode 100755
index 0000000..c06b4b0
--- /dev/null
+++ b/pypers/bolzano/player/call_mpg123.py
@@ -0,0 +1,6 @@
+import os, time
+from subprocess import Popen
+
+def play(fname):
+ player = Popen(["/usr/bin/mpg123", fname])
+ return player.pid
diff --git a/pypers/bolzano/player/change_spacing.py b/pypers/bolzano/player/change_spacing.py
new file mode 100755
index 0000000..38e4ad7
--- /dev/null
+++ b/pypers/bolzano/player/change_spacing.py
@@ -0,0 +1,12 @@
+word = "Anime"
+
+for n in range(3):
+ spaces = " " * n
+ print spaces.join(word)
+
+for n in range(3)[::-1]:
+ spaces = " " * n
+ print spaces.join(word)
+
+print range(3)
+print range(3)[::-1]
diff --git a/pypers/bolzano/player/clickable_label.py b/pypers/bolzano/player/clickable_label.py
new file mode 100755
index 0000000..efb0d1d
--- /dev/null
+++ b/pypers/bolzano/player/clickable_label.py
@@ -0,0 +1,12 @@
+from Tkinter import *
+
+class ClickableLabel(Label):
+ def __init__(self, master, **kw):
+ Label.__init__(self, master, **kw)
+ self.bind('<Button-1>', lambda e : self.quit())
+
+if __name__ == "__main__":
+ root = Tk()
+ cl = ClickableLabel(root)
+ cl.pack()
+ root.mainloop()
diff --git a/pypers/bolzano/player/cycle.py b/pypers/bolzano/player/cycle.py
new file mode 100755
index 0000000..467746f
--- /dev/null
+++ b/pypers/bolzano/player/cycle.py
@@ -0,0 +1,13 @@
+from itertools import cycle
+
+c = cycle("abcd")
+
+print c.next()
+
+print c.next()
+print c.next()
+print c.next()
+
+print c.next()
+
+print c.next()
diff --git a/pypers/bolzano/player/global.py b/pypers/bolzano/player/global.py
new file mode 100755
index 0000000..62e30ae
--- /dev/null
+++ b/pypers/bolzano/player/global.py
@@ -0,0 +1,9 @@
+x = 0
+
+def cambia_x():
+ global x
+ x = 1
+
+
+cambia_x()
+print x
diff --git a/pypers/bolzano/player/kw.py b/pypers/bolzano/player/kw.py
new file mode 100755
index 0000000..9c019ca
--- /dev/null
+++ b/pypers/bolzano/player/kw.py
@@ -0,0 +1,6 @@
+def f(**kw):
+ print kw
+
+f(a="qualcosa", b="qualcos'altro", c="")
+
+Label(text="", bg="", colfff)
diff --git a/pypers/bolzano/player/label.py b/pypers/bolzano/player/label.py
new file mode 100755
index 0000000..f6616b5
--- /dev/null
+++ b/pypers/bolzano/player/label.py
@@ -0,0 +1,29 @@
+from Tkinter import *
+from Tkconstants import *
+
+fg, bg = "red", "green"
+
+def simple_label(master):
+ mylabel = Label(master, text="ciao!", fg="red", font="Courier 36 bold",
+ bg="green")
+ mylabel.pack(side=LEFT)
+
+def flashing_label(master):
+ label = Label(master, text="ciao!",
+ foreground=fg, background=bg,
+ font="Courier 36 bold")
+ label.pack(side=RIGHT)
+ label.after(1000, flash, label)
+
+def flash(mylabel):
+ global fg, bg
+ fg, bg = bg, fg
+ mylabel.config(foreground=fg, background=bg)
+ mylabel.after(1000, flash, mylabel)
+
+
+if __name__ == "__main__":
+ root = Tk()
+ simple_label(root)
+ flashing_label(root)
+ root.mainloop()
diff --git a/pypers/bolzano/player/listbox.py b/pypers/bolzano/player/listbox.py
new file mode 100755
index 0000000..4492884
--- /dev/null
+++ b/pypers/bolzano/player/listbox.py
@@ -0,0 +1,37 @@
+import os
+from Tkinter import *
+from call_mpg123 import play
+from signal import SIGTERM
+
+mp3s = [f for f in os.listdir(".") if f.endswith(".mp3")]
+
+is_playing = False
+pid = None
+
+def playsong(e):
+ global is_playing, pid
+ i = int(listbox.curselection()[0])
+ if not is_playing:
+ is_playing = True
+ pid = play(mp3s[i])
+ else:
+ os.kill(pid, SIGTERM)
+ is_playing = True
+ pid = play(mp3s[i])
+
+def quit():
+ master.quit()
+ if is_playing:
+ os.kill(pid, SIGTERM)
+
+master = Tk()
+listbox = Listbox(master)
+
+for mp3 in mp3s:
+ listbox.insert('end', mp3)
+
+listbox.pack()
+listbox.bind("<Double-Button-1>", playsong)
+
+master.protocol("WM_DELETE_WINDOW", quit)
+master.mainloop()
diff --git a/pypers/bolzano/player/musicbox.py b/pypers/bolzano/player/musicbox.py
new file mode 100755
index 0000000..5eb7f8e
--- /dev/null
+++ b/pypers/bolzano/player/musicbox.py
@@ -0,0 +1,53 @@
+import os
+from Tkinter import *
+from call_mpg123 import play
+from signal import SIGTERM
+
+class Musicbox(Listbox):
+
+ def __init__(self, master, mp3s, **kw):
+ Listbox.__init__(self, master, **kw)
+ self.master = master
+ self.mp3s = mp3s
+ for mp3 in mp3s:
+ self.insert("end", mp3)
+ self.is_playing = False
+ self.pid = None
+ self.bind("<Double-Button-1>", self.playsong)
+
+ def playsong(self, e):
+ i = int(self.curselection()[0])
+ if not self.is_playing:
+ self.is_playing = True
+ self.pid = play(self.mp3s[i])
+ else:
+ os.kill(self.pid, SIGTERM)
+ self.is_playing = True
+ self.pid = play(self.mp3s[i])
+
+ def quit(self):
+ self.master.quit()
+ if self.is_playing:
+ os.kill(self.pid, SIGTERM)
+
+class ScrollableMusicbox(Frame):
+ def __init__(self, master, musicbox, **kw):
+ Frame.__init__(self, master, **kw)
+ self.master = master
+ self.musicbox = musicbox
+ self.scrollbar = Scrollbar(self)
+ self.scrollbar.config(
+ command=self.musicbox.yview)
+ self.musicbox.config(
+ yscrollcommand=self.scrollbar.set)
+ self.musicbox.pack(side=LEFT, fill=BOTH)
+ self.scrollbar.pack(side=RIGHT, fill=Y)
+
+
+if __name__ == "__main__":
+ master = Tk()
+ musicbox = Musicbox(master, [f for f in os.listdir(".")
+ if f.endswith(".mp3")])
+ musicbox.pack()
+ master.protocol("WM_DELETE_WINDOW", musicbox.quit)
+ master.mainloop()
diff --git a/pypers/bolzano/player/scrollbar.py b/pypers/bolzano/player/scrollbar.py
new file mode 100755
index 0000000..9500355
--- /dev/null
+++ b/pypers/bolzano/player/scrollbar.py
@@ -0,0 +1,17 @@
+from Tkinter import *
+
+root = Tk()
+
+scrollbar = Scrollbar(root)
+scrollbar.pack(side=RIGHT, fill=Y)
+
+listbox = Listbox(root, yscrollcommand=scrollbar.set)
+
+for i in range(1000):
+ listbox.insert(END, str(i))
+listbox.pack(side=LEFT, fill=BOTH)
+
+scrollbar.config(command=listbox.yview)
+
+mainloop()
+
diff --git a/pypers/bolzano/player/subprocess.py b/pypers/bolzano/player/subprocess.py
new file mode 100755
index 0000000..d115e87
--- /dev/null
+++ b/pypers/bolzano/player/subprocess.py
@@ -0,0 +1,1165 @@
+# subprocess - Subprocesses with accessible I/O streams
+#
+# For more information about this module, see PEP 324.
+#
+# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of the
+# author not be used in advertising or publicity pertaining to
+# distribution of the software without specific, written prior
+# permission.
+#
+# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+r"""subprocess - Subprocesses with accessible I/O streams
+
+This module allows you to spawn processes, connect to their
+input/output/error pipes, and obtain their return codes. This module
+intends to replace several other, older modules and functions, like:
+
+os.system
+os.spawn*
+os.popen*
+popen2.*
+commands.*
+
+Information about how the subprocess module can be used to replace these
+modules and functions can be found below.
+
+
+
+Using the subprocess module
+===========================
+This module defines one class called Popen:
+
+class Popen(args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+
+
+Arguments are:
+
+args should be a string, or a sequence of program arguments. The
+program to execute is normally the first item in the args sequence or
+string, but can be explicitly set by using the executable argument.
+
+On UNIX, with shell=False (default): In this case, the Popen class
+uses os.execvp() to execute the child program. args should normally
+be a sequence. A string will be treated as a sequence with the string
+as the only item (the program to execute).
+
+On UNIX, with shell=True: If args is a string, it specifies the
+command string to execute through the shell. If args is a sequence,
+the first item specifies the command string, and any additional items
+will be treated as additional shell arguments.
+
+On Windows: the Popen class uses CreateProcess() to execute the child
+program, which operates on strings. If args is a sequence, it will be
+converted to a string using the list2cmdline method. Please note that
+not all MS Windows applications interpret the command line the same
+way: The list2cmdline is designed for applications using the same
+rules as the MS C runtime.
+
+bufsize, if given, has the same meaning as the corresponding argument
+to the built-in open() function: 0 means unbuffered, 1 means line
+buffered, any other positive value means use a buffer of
+(approximately) that size. A negative bufsize means to use the system
+default, which usually means fully buffered. The default value for
+bufsize is 0 (unbuffered).
+
+stdin, stdout and stderr specify the executed programs' standard
+input, standard output and standard error file handles, respectively.
+Valid values are PIPE, an existing file descriptor (a positive
+integer), an existing file object, and None. PIPE indicates that a
+new pipe to the child should be created. With None, no redirection
+will occur; the child's file handles will be inherited from the
+parent. Additionally, stderr can be STDOUT, which indicates that the
+stderr data from the applications should be captured into the same
+file handle as for stdout.
+
+If preexec_fn is set to a callable object, this object will be called
+in the child process just before the child is executed.
+
+If close_fds is true, all file descriptors except 0, 1 and 2 will be
+closed before the child process is executed.
+
+if shell is true, the specified command will be executed through the
+shell.
+
+If cwd is not None, the current directory will be changed to cwd
+before the child is executed.
+
+If env is not None, it defines the environment variables for the new
+process.
+
+If universal_newlines is true, the file objects stdout and stderr are
+opened as a text files, but lines may be terminated by any of '\n',
+the Unix end-of-line convention, '\r', the Macintosh convention or
+'\r\n', the Windows convention. All of these external representations
+are seen as '\n' by the Python program. Note: This feature is only
+available if Python is built with universal newline support (the
+default). Also, the newlines attribute of the file objects stdout,
+stdin and stderr are not updated by the communicate() method.
+
+The startupinfo and creationflags, if given, will be passed to the
+underlying CreateProcess() function. They can specify things such as
+appearance of the main window and priority for the new process.
+(Windows only)
+
+
+This module also defines two shortcut functions:
+
+call(*args, **kwargs):
+ Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+
+
+Exceptions
+----------
+Exceptions raised in the child process, before the new program has
+started to execute, will be re-raised in the parent. Additionally,
+the exception object will have one extra attribute called
+'child_traceback', which is a string containing traceback information
+from the childs point of view.
+
+The most common exception raised is OSError. This occurs, for
+example, when trying to execute a non-existent file. Applications
+should prepare for OSErrors.
+
+A ValueError will be raised if Popen is called with invalid arguments.
+
+
+Security
+--------
+Unlike some other popen functions, this implementation will never call
+/bin/sh implicitly. This means that all characters, including shell
+metacharacters, can safely be passed to child processes.
+
+
+Popen objects
+=============
+Instances of the Popen class have the following methods:
+
+poll()
+ Check if child process has terminated. Returns returncode
+ attribute.
+
+wait()
+ Wait for child process to terminate. Returns returncode attribute.
+
+communicate(input=None)
+ Interact with process: Send data to stdin. Read data from stdout
+ and stderr, until end-of-file is reached. Wait for process to
+ terminate. The optional stdin argument should be a string to be
+ sent to the child process, or None, if no data should be sent to
+ the child.
+
+ communicate() returns a tuple (stdout, stderr).
+
+ Note: The data read is buffered in memory, so do not use this
+ method if the data size is large or unlimited.
+
+The following attributes are also available:
+
+stdin
+ If the stdin argument is PIPE, this attribute is a file object
+ that provides input to the child process. Otherwise, it is None.
+
+stdout
+ If the stdout argument is PIPE, this attribute is a file object
+ that provides output from the child process. Otherwise, it is
+ None.
+
+stderr
+ If the stderr argument is PIPE, this attribute is file object that
+ provides error output from the child process. Otherwise, it is
+ None.
+
+pid
+ The process ID of the child process.
+
+returncode
+ The child return code. A None value indicates that the process
+ hasn't terminated yet. A negative value -N indicates that the
+ child was terminated by signal N (UNIX only).
+
+
+Replacing older functions with the subprocess module
+====================================================
+In this section, "a ==> b" means that b can be used as a replacement
+for a.
+
+Note: All functions in this section fail (more or less) silently if
+the executed program cannot be found; this module raises an OSError
+exception.
+
+In the following examples, we assume that the subprocess module is
+imported with "from subprocess import *".
+
+
+Replacing /bin/sh shell backquote
+---------------------------------
+output=`mycmd myarg`
+==>
+output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
+
+
+Replacing shell pipe line
+-------------------------
+output=`dmesg | grep hda`
+==>
+p1 = Popen(["dmesg"], stdout=PIPE)
+p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+output = p2.communicate()[0]
+
+
+Replacing os.system()
+---------------------
+sts = os.system("mycmd" + " myarg")
+==>
+p = Popen("mycmd" + " myarg", shell=True)
+sts = os.waitpid(p.pid, 0)
+
+Note:
+
+* Calling the program through the shell is usually not required.
+
+* It's easier to look at the returncode attribute than the
+ exitstatus.
+
+A more real-world example would look like this:
+
+try:
+ retcode = call("mycmd" + " myarg", shell=True)
+ if retcode < 0:
+ print >>sys.stderr, "Child was terminated by signal", -retcode
+ else:
+ print >>sys.stderr, "Child returned", retcode
+except OSError, e:
+ print >>sys.stderr, "Execution failed:", e
+
+
+Replacing os.spawn*
+-------------------
+P_NOWAIT example:
+
+pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+pid = Popen(["/bin/mycmd", "myarg"]).pid
+
+
+P_WAIT example:
+
+retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+retcode = call(["/bin/mycmd", "myarg"])
+
+
+Vector example:
+
+os.spawnvp(os.P_NOWAIT, path, args)
+==>
+Popen([path] + args[1:])
+
+
+Environment example:
+
+os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
+==>
+Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
+
+
+Replacing os.popen*
+-------------------
+pipe = os.popen(cmd, mode='r', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
+
+pipe = os.popen(cmd, mode='w', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
+
+
+(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+
+(child_stdin,
+ child_stdout,
+ child_stderr) = os.popen3(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+(child_stdin,
+ child_stdout,
+ child_stderr) = (p.stdin, p.stdout, p.stderr)
+
+
+(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
+(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
+
+
+Replacing popen2.*
+------------------
+Note: If the cmd argument to popen2 functions is a string, the command
+is executed through /bin/sh. If it is a list, the command is directly
+executed.
+
+(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
+==>
+p = Popen(["somestring"], shell=True, bufsize=bufsize
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+
+(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
+==>
+p = Popen(["mycmd", "myarg"], bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen,
+except that:
+
+* subprocess.Popen raises an exception if the execution fails
+* the capturestderr argument is replaced with the stderr argument.
+* stdin=PIPE and stdout=PIPE must be specified.
+* popen2 closes all filedescriptors by default, but you have to specify
+ close_fds=True with subprocess.Popen.
+
+
+"""
+
+import sys
+mswindows = (sys.platform == "win32")
+
+import os
+import types
+import traceback
+
+if mswindows:
+ import threading
+ import msvcrt
+ if 0: # <-- change this to use pywin32 instead of the _subprocess driver
+ import pywintypes
+ from win32api import GetStdHandle, STD_INPUT_HANDLE, \
+ STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
+ from win32api import GetCurrentProcess, DuplicateHandle, \
+ GetModuleFileName, GetVersion
+ from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
+ from win32pipe import CreatePipe
+ from win32process import CreateProcess, STARTUPINFO, \
+ GetExitCodeProcess, STARTF_USESTDHANDLES, \
+ STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
+ from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
+ else:
+ from _subprocess import *
+ class STARTUPINFO:
+ dwFlags = 0
+ hStdInput = None
+ hStdOutput = None
+ hStdError = None
+ class pywintypes:
+ error = IOError
+else:
+ import select
+ import errno
+ import fcntl
+ import pickle
+
+__all__ = ["Popen", "PIPE", "STDOUT", "call"]
+
+try:
+ MAXFD = os.sysconf("SC_OPEN_MAX")
+except:
+ MAXFD = 256
+
+# True/False does not exist on 2.2.0
+try:
+ False
+except NameError:
+ False = 0
+ True = 1
+
+_active = []
+
+def _cleanup():
+ for inst in _active[:]:
+ inst.poll()
+
+PIPE = -1
+STDOUT = -2
+
+
+def call(*args, **kwargs):
+ """Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+ """
+ return Popen(*args, **kwargs).wait()
+
+
+def list2cmdline(seq):
+ """
+ Translate a sequence of arguments into a command line
+ string, using the same rules as the MS C runtime:
+
+ 1) Arguments are delimited by white space, which is either a
+ space or a tab.
+
+ 2) A string surrounded by double quotation marks is
+ interpreted as a single argument, regardless of white space
+ contained within. A quoted string can be embedded in an
+ argument.
+
+ 3) A double quotation mark preceded by a backslash is
+ interpreted as a literal double quotation mark.
+
+ 4) Backslashes are interpreted literally, unless they
+ immediately precede a double quotation mark.
+
+ 5) If backslashes immediately precede a double quotation mark,
+ every pair of backslashes is interpreted as a literal
+ backslash. If the number of backslashes is odd, the last
+ backslash escapes the next double quotation mark as
+ described in rule 3.
+ """
+
+ # See
+ # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
+ result = []
+ needquote = False
+ for arg in seq:
+ bs_buf = []
+
+ # Add a space to separate this argument from the others
+ if result:
+ result.append(' ')
+
+ needquote = (" " in arg) or ("\t" in arg)
+ if needquote:
+ result.append('"')
+
+ for c in arg:
+ if c == '\\':
+ # Don't know if we need to double yet.
+ bs_buf.append(c)
+ elif c == '"':
+ # Double backspaces.
+ result.append('\\' * len(bs_buf)*2)
+ bs_buf = []
+ result.append('\\"')
+ else:
+ # Normal char
+ if bs_buf:
+ result.extend(bs_buf)
+ bs_buf = []
+ result.append(c)
+
+ # Add remaining backspaces, if any.
+ if bs_buf:
+ result.extend(bs_buf)
+
+ if needquote:
+ result.extend(bs_buf)
+ result.append('"')
+
+ return ''.join(result)
+
+
+class Popen(object):
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+ """Create new Popen instance."""
+ _cleanup()
+
+ if not isinstance(bufsize, (int, long)):
+ raise TypeError("bufsize must be an integer")
+
+ if mswindows:
+ if preexec_fn is not None:
+ raise ValueError("preexec_fn is not supported on Windows "
+ "platforms")
+ if close_fds:
+ raise ValueError("close_fds is not supported on Windows "
+ "platforms")
+ else:
+ # POSIX
+ if startupinfo is not None:
+ raise ValueError("startupinfo is only supported on Windows "
+ "platforms")
+ if creationflags != 0:
+ raise ValueError("creationflags is only supported on Windows "
+ "platforms")
+
+ self.stdin = None
+ self.stdout = None
+ self.stderr = None
+ self.pid = None
+ self.returncode = None
+ self.universal_newlines = universal_newlines
+
+ # Input and output objects. The general principle is like
+ # this:
+ #
+ # Parent Child
+ # ------ -----
+ # p2cwrite ---stdin---> p2cread
+ # c2pread <--stdout--- c2pwrite
+ # errread <--stderr--- errwrite
+ #
+ # On POSIX, the child objects are file descriptors. On
+ # Windows, these are Windows file handles. The parent objects
+ # are file descriptors on both platforms. The parent objects
+ # are None when not using PIPEs. The child objects are None
+ # when not redirecting.
+
+ (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite) = self._get_handles(stdin, stdout, stderr)
+
+ self._execute_child(args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+ if p2cwrite:
+ self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
+ if c2pread:
+ if universal_newlines:
+ self.stdout = os.fdopen(c2pread, 'rU', bufsize)
+ else:
+ self.stdout = os.fdopen(c2pread, 'rb', bufsize)
+ if errread:
+ if universal_newlines:
+ self.stderr = os.fdopen(errread, 'rU', bufsize)
+ else:
+ self.stderr = os.fdopen(errread, 'rb', bufsize)
+
+ _active.append(self)
+
+
+ def _translate_newlines(self, data):
+ data = data.replace("\r\n", "\n")
+ data = data.replace("\r", "\n")
+ return data
+
+
+ if mswindows:
+ #
+ # Windows methods
+ #
+ def _get_handles(self, stdin, stdout, stderr):
+ """Construct and return tupel with IO objects:
+ p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+ """
+ if stdin == None and stdout == None and stderr == None:
+ return (None, None, None, None, None, None)
+
+ p2cread, p2cwrite = None, None
+ c2pread, c2pwrite = None, None
+ errread, errwrite = None, None
+
+ if stdin == None:
+ p2cread = GetStdHandle(STD_INPUT_HANDLE)
+ elif stdin == PIPE:
+ p2cread, p2cwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ p2cwrite = p2cwrite.Detach()
+ p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
+ elif type(stdin) == types.IntType:
+ p2cread = msvcrt.get_osfhandle(stdin)
+ else:
+ # Assuming file-like object
+ p2cread = msvcrt.get_osfhandle(stdin.fileno())
+ p2cread = self._make_inheritable(p2cread)
+
+ if stdout == None:
+ c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
+ elif stdout == PIPE:
+ c2pread, c2pwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ c2pread = c2pread.Detach()
+ c2pread = msvcrt.open_osfhandle(c2pread, 0)
+ elif type(stdout) == types.IntType:
+ c2pwrite = msvcrt.get_osfhandle(stdout)
+ else:
+ # Assuming file-like object
+ c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
+ c2pwrite = self._make_inheritable(c2pwrite)
+
+ if stderr == None:
+ errwrite = GetStdHandle(STD_ERROR_HANDLE)
+ elif stderr == PIPE:
+ errread, errwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ errread = errread.Detach()
+ errread = msvcrt.open_osfhandle(errread, 0)
+ elif stderr == STDOUT:
+ errwrite = c2pwrite
+ elif type(stderr) == types.IntType:
+ errwrite = msvcrt.get_osfhandle(stderr)
+ else:
+ # Assuming file-like object
+ errwrite = msvcrt.get_osfhandle(stderr.fileno())
+ errwrite = self._make_inheritable(errwrite)
+
+ return (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+
+ def _make_inheritable(self, handle):
+ """Return a duplicate of handle, which is inheritable"""
+ return DuplicateHandle(GetCurrentProcess(), handle,
+ GetCurrentProcess(), 0, 1,
+ DUPLICATE_SAME_ACCESS)
+
+
+ def _find_w9xpopen(self):
+ """Find and return absolut path to w9xpopen.exe"""
+ w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
+ "w9xpopen.exe")
+ if not os.path.exists(w9xpopen):
+ # Eeek - file-not-found - possibly an embedding
+ # situation - see if we can locate it in sys.exec_prefix
+ w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
+ "w9xpopen.exe")
+ if not os.path.exists(w9xpopen):
+ raise RuntimeError("Cannot locate w9xpopen.exe, which is "
+ "needed for Popen to work with your "
+ "shell or platform.")
+ return w9xpopen
+
+
+ def _execute_child(self, args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite):
+ """Execute program (MS Windows version)"""
+
+ if not isinstance(args, types.StringTypes):
+ args = list2cmdline(args)
+
+ # Process startup details
+ default_startupinfo = STARTUPINFO()
+ if startupinfo == None:
+ startupinfo = default_startupinfo
+ if not None in (p2cread, c2pwrite, errwrite):
+ startupinfo.dwFlags |= STARTF_USESTDHANDLES
+ startupinfo.hStdInput = p2cread
+ startupinfo.hStdOutput = c2pwrite
+ startupinfo.hStdError = errwrite
+
+ if shell:
+ default_startupinfo.dwFlags |= STARTF_USESHOWWINDOW
+ default_startupinfo.wShowWindow = SW_HIDE
+ comspec = os.environ.get("COMSPEC", "cmd.exe")
+ args = comspec + " /c " + args
+ if (GetVersion() >= 0x80000000L or
+ os.path.basename(comspec).lower() == "command.com"):
+ # Win9x, or using command.com on NT. We need to
+ # use the w9xpopen intermediate program. For more
+ # information, see KB Q150956
+ # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
+ w9xpopen = self._find_w9xpopen()
+ args = '"%s" %s' % (w9xpopen, args)
+ # Not passing CREATE_NEW_CONSOLE has been known to
+ # cause random failures on win9x. Specifically a
+ # dialog: "Your program accessed mem currently in
+ # use at xxx" and a hopeful warning about the
+ # stability of your system. Cost is Ctrl+C wont
+ # kill children.
+ creationflags |= CREATE_NEW_CONSOLE
+
+ # Start the process
+ try:
+ hp, ht, pid, tid = CreateProcess(executable, args,
+ # no special security
+ None, None,
+ # must inherit handles to pass std
+ # handles
+ 1,
+ creationflags,
+ env,
+ cwd,
+ startupinfo)
+ except pywintypes.error, e:
+ # Translate pywintypes.error to WindowsError, which is
+ # a subclass of OSError. FIXME: We should really
+ # translate errno using _sys_errlist (or simliar), but
+ # how can this be done from Python?
+ raise WindowsError(*e.args)
+
+ # Retain the process handle, but close the thread handle
+ self._handle = hp
+ self.pid = pid
+ ht.Close()
+
+ # Child is launched. Close the parent's copy of those pipe
+ # handles that only the child should have open. You need
+ # to make sure that no handles to the write end of the
+ # output pipe are maintained in this process or else the
+ # pipe will not close when the child process exits and the
+ # ReadFile will hang.
+ if p2cread != None:
+ p2cread.Close()
+ if c2pwrite != None:
+ c2pwrite.Close()
+ if errwrite != None:
+ errwrite.Close()
+
+
+ def poll(self):
+ """Check if child process has terminated. Returns returncode
+ attribute."""
+ if self.returncode == None:
+ if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
+ self.returncode = GetExitCodeProcess(self._handle)
+ _active.remove(self)
+ return self.returncode
+
+
+ def wait(self):
+ """Wait for child process to terminate. Returns returncode
+ attribute."""
+ if self.returncode == None:
+ obj = WaitForSingleObject(self._handle, INFINITE)
+ self.returncode = GetExitCodeProcess(self._handle)
+ _active.remove(self)
+ return self.returncode
+
+
+ def _readerthread(self, fh, buffer):
+ buffer.append(fh.read())
+
+
+ def communicate(self, input=None):
+ """Interact with process: Send data to stdin. Read data from
+ stdout and stderr, until end-of-file is reached. Wait for
+ process to terminate. The optional input argument should be a
+ string to be sent to the child process, or None, if no data
+ should be sent to the child.
+
+ communicate() returns a tuple (stdout, stderr)."""
+ stdout = None # Return
+ stderr = None # Return
+
+ if self.stdout:
+ stdout = []
+ stdout_thread = threading.Thread(target=self._readerthread,
+ args=(self.stdout, stdout))
+ stdout_thread.setDaemon(True)
+ stdout_thread.start()
+ if self.stderr:
+ stderr = []
+ stderr_thread = threading.Thread(target=self._readerthread,
+ args=(self.stderr, stderr))
+ stderr_thread.setDaemon(True)
+ stderr_thread.start()
+
+ if self.stdin:
+ if input != None:
+ self.stdin.write(input)
+ self.stdin.close()
+
+ if self.stdout:
+ stdout_thread.join()
+ if self.stderr:
+ stderr_thread.join()
+
+ # All data exchanged. Translate lists into strings.
+ if stdout != None:
+ stdout = stdout[0]
+ if stderr != None:
+ stderr = stderr[0]
+
+ # Translate newlines, if requested. We cannot let the file
+ # object do the translation: It is based on stdio, which is
+ # impossible to combine with select (unless forcing no
+ # buffering).
+ if self.universal_newlines and hasattr(open, 'newlines'):
+ if stdout:
+ stdout = self._translate_newlines(stdout)
+ if stderr:
+ stderr = self._translate_newlines(stderr)
+
+ self.wait()
+ return (stdout, stderr)
+
+ else:
+ #
+ # POSIX methods
+ #
+ def _get_handles(self, stdin, stdout, stderr):
+ """Construct and return tupel with IO objects:
+ p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+ """
+ p2cread, p2cwrite = None, None
+ c2pread, c2pwrite = None, None
+ errread, errwrite = None, None
+
+ if stdin == None:
+ pass
+ elif stdin == PIPE:
+ p2cread, p2cwrite = os.pipe()
+ elif type(stdin) == types.IntType:
+ p2cread = stdin
+ else:
+ # Assuming file-like object
+ p2cread = stdin.fileno()
+
+ if stdout == None:
+ pass
+ elif stdout == PIPE:
+ c2pread, c2pwrite = os.pipe()
+ elif type(stdout) == types.IntType:
+ c2pwrite = stdout
+ else:
+ # Assuming file-like object
+ c2pwrite = stdout.fileno()
+
+ if stderr == None:
+ pass
+ elif stderr == PIPE:
+ errread, errwrite = os.pipe()
+ elif stderr == STDOUT:
+ errwrite = c2pwrite
+ elif type(stderr) == types.IntType:
+ errwrite = stderr
+ else:
+ # Assuming file-like object
+ errwrite = stderr.fileno()
+
+ return (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+
+ def _set_cloexec_flag(self, fd):
+ try:
+ cloexec_flag = fcntl.FD_CLOEXEC
+ except AttributeError:
+ cloexec_flag = 1
+
+ old = fcntl.fcntl(fd, fcntl.F_GETFD)
+ fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
+
+
+ def _close_fds(self, but):
+ for i in range(3, MAXFD):
+ if i == but:
+ continue
+ try:
+ os.close(i)
+ except:
+ pass
+
+
+ def _execute_child(self, args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite):
+ """Execute program (POSIX version)"""
+
+ if isinstance(args, types.StringTypes):
+ args = [args]
+
+ if shell:
+ args = ["/bin/sh", "-c"] + args
+
+ if executable == None:
+ executable = args[0]
+
+ # For transferring possible exec failure from child to parent
+ # The first char specifies the exception type: 0 means
+ # OSError, 1 means some other error.
+ errpipe_read, errpipe_write = os.pipe()
+ self._set_cloexec_flag(errpipe_write)
+
+ self.pid = os.fork()
+ if self.pid == 0:
+ # Child
+ try:
+ # Close parent's pipe ends
+ if p2cwrite:
+ os.close(p2cwrite)
+ if c2pread:
+ os.close(c2pread)
+ if errread:
+ os.close(errread)
+ os.close(errpipe_read)
+
+ # Dup fds for child
+ if p2cread:
+ os.dup2(p2cread, 0)
+ if c2pwrite:
+ os.dup2(c2pwrite, 1)
+ if errwrite:
+ os.dup2(errwrite, 2)
+
+ # Close pipe fds. Make sure we doesn't close the same
+ # fd more than once.
+ if p2cread:
+ os.close(p2cread)
+ if c2pwrite and c2pwrite not in (p2cread,):
+ os.close(c2pwrite)
+ if errwrite and errwrite not in (p2cread, c2pwrite):
+ os.close(errwrite)
+
+ # Close all other fds, if asked for
+ if close_fds:
+ self._close_fds(but=errpipe_write)
+
+ if cwd != None:
+ os.chdir(cwd)
+
+ if preexec_fn:
+ apply(preexec_fn)
+
+ if env == None:
+ os.execvp(executable, args)
+ else:
+ os.execvpe(executable, args, env)
+
+ except:
+ exc_type, exc_value, tb = sys.exc_info()
+ # Save the traceback and attach it to the exception object
+ exc_lines = traceback.format_exception(exc_type,
+ exc_value,
+ tb)
+ exc_value.child_traceback = ''.join(exc_lines)
+ os.write(errpipe_write, pickle.dumps(exc_value))
+
+ # This exitcode won't be reported to applications, so it
+ # really doesn't matter what we return.
+ os._exit(255)
+
+ # Parent
+ os.close(errpipe_write)
+ if p2cread and p2cwrite:
+ os.close(p2cread)
+ if c2pwrite and c2pread:
+ os.close(c2pwrite)
+ if errwrite and errread:
+ os.close(errwrite)
+
+ # Wait for exec to fail or succeed; possibly raising exception
+ data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
+ os.close(errpipe_read)
+ if data != "":
+ os.waitpid(self.pid, 0)
+ child_exception = pickle.loads(data)
+ raise child_exception
+
+
+ def _handle_exitstatus(self, sts):
+ if os.WIFSIGNALED(sts):
+ self.returncode = -os.WTERMSIG(sts)
+ elif os.WIFEXITED(sts):
+ self.returncode = os.WEXITSTATUS(sts)
+ else:
+ # Should never happen
+ raise RuntimeError("Unknown child exit status!")
+
+ _active.remove(self)
+
+
+ def poll(self):
+ """Check if child process has terminated. Returns returncode
+ attribute."""
+ if self.returncode == None:
+ try:
+ pid, sts = os.waitpid(self.pid, os.WNOHANG)
+ if pid == self.pid:
+ self._handle_exitstatus(sts)
+ except os.error:
+ pass
+ return self.returncode
+
+
+ def wait(self):
+ """Wait for child process to terminate. Returns returncode
+ attribute."""
+ if self.returncode == None:
+ pid, sts = os.waitpid(self.pid, 0)
+ self._handle_exitstatus(sts)
+ return self.returncode
+
+
+ def communicate(self, input=None):
+ """Interact with process: Send data to stdin. Read data from
+ stdout and stderr, until end-of-file is reached. Wait for
+ process to terminate. The optional input argument should be a
+ string to be sent to the child process, or None, if no data
+ should be sent to the child.
+
+ communicate() returns a tuple (stdout, stderr)."""
+ read_set = []
+ write_set = []
+ stdout = None # Return
+ stderr = None # Return
+
+ if self.stdin:
+ # Flush stdio buffer. This might block, if the user has
+ # been writing to .stdin in an uncontrolled fashion.
+ self.stdin.flush()
+ if input:
+ write_set.append(self.stdin)
+ else:
+ self.stdin.close()
+ if self.stdout:
+ read_set.append(self.stdout)
+ stdout = []
+ if self.stderr:
+ read_set.append(self.stderr)
+ stderr = []
+
+ while read_set or write_set:
+ rlist, wlist, xlist = select.select(read_set, write_set, [])
+
+ if self.stdin in wlist:
+ # When select has indicated that the file is writable,
+ # we can write up to PIPE_BUF bytes without risk
+ # blocking. POSIX defines PIPE_BUF >= 512
+ bytes_written = os.write(self.stdin.fileno(), input[:512])
+ input = input[bytes_written:]
+ if not input:
+ self.stdin.close()
+ write_set.remove(self.stdin)
+
+ if self.stdout in rlist:
+ data = os.read(self.stdout.fileno(), 1024)
+ if data == "":
+ self.stdout.close()
+ read_set.remove(self.stdout)
+ stdout.append(data)
+
+ if self.stderr in rlist:
+ data = os.read(self.stderr.fileno(), 1024)
+ if data == "":
+ self.stderr.close()
+ read_set.remove(self.stderr)
+ stderr.append(data)
+
+ # All data exchanged. Translate lists into strings.
+ if stdout != None:
+ stdout = ''.join(stdout)
+ if stderr != None:
+ stderr = ''.join(stderr)
+
+ # Translate newlines, if requested. We cannot let the file
+ # object do the translation: It is based on stdio, which is
+ # impossible to combine with select (unless forcing no
+ # buffering).
+ if self.universal_newlines and hasattr(open, 'newlines'):
+ if stdout:
+ stdout = self._translate_newlines(stdout)
+ if stderr:
+ stderr = self._translate_newlines(stderr)
+
+ self.wait()
+ return (stdout, stderr)
+
+
+def _demo_posix():
+ #
+ # Example 1: Simple redirection: Get process list
+ #
+ plist = Popen(["ps"], stdout=PIPE).communicate()[0]
+ print "Process list:"
+ print plist
+
+ #
+ # Example 2: Change uid before executing child
+ #
+ if os.getuid() == 0:
+ p = Popen(["id"], preexec_fn=lambda: os.setuid(100))
+ p.wait()
+
+ #
+ # Example 3: Connecting several subprocesses
+ #
+ print "Looking for 'hda'..."
+ p1 = Popen(["dmesg"], stdout=PIPE)
+ p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+ print repr(p2.communicate()[0])
+
+ #
+ # Example 4: Catch execution error
+ #
+ print
+ print "Trying a weird file..."
+ try:
+ print Popen(["/this/path/does/not/exist"]).communicate()
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ print "The file didn't exist. I thought so..."
+ print "Child traceback:"
+ print e.child_traceback
+ else:
+ print "Error", e.errno
+ else:
+ print >>sys.stderr, "Gosh. No error."
+
+
+def _demo_windows():
+ #
+ # Example 1: Connecting several subprocesses
+ #
+ print "Looking for 'PROMPT' in set output..."
+ p1 = Popen("set", stdout=PIPE, shell=True)
+ p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE)
+ print repr(p2.communicate()[0])
+
+ #
+ # Example 2: Simple execution of program
+ #
+ print "Executing calc..."
+ p = Popen("calc")
+ p.wait()
+
+
+if __name__ == "__main__":
+ if mswindows:
+ _demo_windows()
+ else:
+ _demo_posix()
diff --git a/pypers/bolzano/web/biancheria.py b/pypers/bolzano/web/biancheria.py
new file mode 100755
index 0000000..750fb7c
--- /dev/null
+++ b/pypers/bolzano/web/biancheria.py
@@ -0,0 +1,51 @@
+from quixote_utils import RootDirectory
+from quixote.form.widget import StringWidget, SubmitWidget, CheckboxWidget, \
+ RadiobuttonsWidget
+from quixote.form.form import Form
+from quixote.util import htmltext
+
+class BiancheriaOnLine(RootDirectory):
+ _q_exports = ["", "magliette", "maglioni", "calzini", "mutande"]
+
+ def _q_index(self):
+ return "Benvenuti al nostro negozio di biancheria online!!"
+
+ def magliette(self):
+ return "<h1>magliette</h1>"
+
+ def maglioni(self):
+ return "maglioni"
+
+ def calzini(self):
+ msg = htmltext("""
+ Abbiamo disponili i seguenti modelli: <br/> <br/>
+ A. Calzettone lungo da neve <br/>
+ B. Calzino di seta estivo <br/>
+ C. Calzino nero lutto <br/>
+ """)
+ form = Form()
+ form.add(RadiobuttonsWidget, "calzini", options=list("ABC"))
+ form.add(SubmitWidget, "submit", "Compra!")
+ return msg + form.render()
+
+ def mutande(self):
+ msg = htmltext("""
+ Abbiamo disponili i seguenti modelli: <br/> <br/>
+ A. mutanda modello base <br/>
+ B. mutandina di pizzo per signore <br/>
+ C. mutandone ascellare modello Fantozzi <br/>
+ """)
+ form = Form()
+ form.add(CheckboxWidget, "modelloA", value=0)
+ form.add(CheckboxWidget, "modelloB", value=0)
+ form.add(CheckboxWidget, "modelloC", value=1)
+ form.add(SubmitWidget, "submit", "Compra!")
+ return msg + form.render()
+
+ def oxe(self):
+ return "XXX"
+
+
+
+if __name__ == "__main__":
+ BiancheriaOnLine().publish_show("calzini", browser="mozilla")
diff --git a/pypers/bolzano/web/cgi-bin/esempio1.py b/pypers/bolzano/web/cgi-bin/esempio1.py
new file mode 100755
index 0000000..49c850f
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/esempio1.py
@@ -0,0 +1 @@
+print "hello!"
diff --git a/pypers/bolzano/web/cgi-bin/ex_form.html b/pypers/bolzano/web/cgi-bin/ex_form.html
new file mode 100755
index 0000000..323edf8
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/ex_form.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+<form action="hello.py">
+<input type="text" name="phonenumber" value="XXXXX"><br/>
+<input type="submit" name="submit" value="ok">
+</form>
+</body>
+</html>
diff --git a/pypers/bolzano/web/cgi-bin/ex_form.py b/pypers/bolzano/web/cgi-bin/ex_form.py
new file mode 100755
index 0000000..4926574
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/ex_form.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+print "Content-type: text/html\n"
+form = """
+<html>
+<head>
+</head>
+<body>
+<form action="save_phonenumber.py">
+<input type="text" name="phonenumber" value="XXXXX"><br/>
+<input type="submit" name="submit" value="ok">
+</form>
+</body>
+</html>
+"""
+print form
diff --git a/pypers/bolzano/web/cgi-bin/hello.py b/pypers/bolzano/web/cgi-bin/hello.py
new file mode 100755
index 0000000..212415d
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/hello.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+print "Content-type: text/html\n"
+print "<html>"
+print "<head>"
+print "</head>"
+print "<body>"
+print "<h1>HELLO!</h1>"
+print "</body>"
+print "</html>"
diff --git a/pypers/bolzano/web/cgi-bin/hello_quixote.py b/pypers/bolzano/web/cgi-bin/hello_quixote.py
new file mode 100755
index 0000000..63f20bb
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/hello_quixote.py
@@ -0,0 +1,25 @@
+from quixote_utils import RootDirectory
+
+class BiancheriaOnLine(RootDirectory):
+ _q_exports = ["", "magliette", "maglioni", "calzini", "mutande"]
+
+ def _q_index(self):
+ return "Benvenuti al nostro negozio di biancheria online!!"
+
+ def magliette(self):
+ return "magliette"
+
+ def maglioni(self):
+ return "maglioni"
+
+ def calzini(self):
+ return "calzini"
+
+ def mutande(self):
+ return "mutande"
+
+ def oxe(self):
+ return "XXX"
+
+if __name__ == "__main__":
+ BiancheriaOnLine().publish_show()
diff --git a/pypers/bolzano/web/cgi-bin/save_phonenumber.py b/pypers/bolzano/web/cgi-bin/save_phonenumber.py
new file mode 100755
index 0000000..2d7b121
--- /dev/null
+++ b/pypers/bolzano/web/cgi-bin/save_phonenumber.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+import cgi
+print "Content-type: text/plain\n"
+
+
+form = cgi.FieldStorage()
+number = form["phonenumber"].value
+print "The submitted phone number (%s) has been saved!" % number
diff --git a/pypers/bolzano/web/for.py b/pypers/bolzano/web/for.py
new file mode 100755
index 0000000..c352bf3
--- /dev/null
+++ b/pypers/bolzano/web/for.py
@@ -0,0 +1,16 @@
+class MyClass(object):
+ pass
+
+myobj = MyClass()
+
+for i in 1,2,3:
+ def f(default=i):
+ print default
+ setattr(myobj, "a%s" % i, f)
+ f()
+
+print "-------"
+myobj.a1()
+myobj.a2()
+myobj.a3()
+
diff --git a/pypers/bolzano/web/monitor.py b/pypers/bolzano/web/monitor.py
new file mode 100755
index 0000000..0ab3411
--- /dev/null
+++ b/pypers/bolzano/web/monitor.py
@@ -0,0 +1,29 @@
+from quixote_utils import RootDirectory
+
+import os, time
+
+def second_counter():
+ global nsec
+ nsec = 0
+ while True:
+ nsec += 1
+ os.NSEC = str(nsec)
+ time.sleep(1)
+
+
+class Root(RootDirectory):
+ def _q_index(self):
+ return "Numero di secondi passati: %s" % os.NSEC
+
+if __name__ == "__main__":
+ os.NSEC = "0"
+ result = os.fork()
+ if result:
+ print "This the pid of the child process: %s. Now I am in the parent."\
+ % result
+ Root().publish_show("")
+ else:
+ print "I am in the child process."
+ second_counter()
+
+
diff --git a/pypers/bolzano/web/monitor_fork.py b/pypers/bolzano/web/monitor_fork.py
new file mode 100755
index 0000000..3139ed9
--- /dev/null
+++ b/pypers/bolzano/web/monitor_fork.py
@@ -0,0 +1,17 @@
+import os, time
+from subprocess import call
+from SimpleHTTPServer import test
+
+if __name__ == "__main__":
+ result = os.fork()
+ if result:
+ time.sleep(0.5)
+ print "This the pid of the child process: %s. Now I am in the parent."\
+ % result
+ call(["konqueror", "http://localhost:8000"])
+ else:
+ print "I am in the child process."
+ test()
+
+
+
diff --git a/pypers/bolzano/web/monitor_thread.py b/pypers/bolzano/web/monitor_thread.py
new file mode 100755
index 0000000..f60e1ff
--- /dev/null
+++ b/pypers/bolzano/web/monitor_thread.py
@@ -0,0 +1,27 @@
+from quixote_utils import RootDirectory
+import os, time, threading
+
+class Counter(threading.Thread):
+ def run(self):
+ global nsec
+ nsec = 0
+ self.running = True
+ while self.running:
+ nsec += 1
+ time.sleep(1)
+ def stop(self):
+ self.running = False
+
+
+class Root(RootDirectory):
+ def _q_index(self):
+ return "Numero di secondi passati: %s" % nsec
+
+if __name__ == "__main__":
+ counter = Counter()
+ counter.start()
+ try:
+ Root().publish_show("")
+ finally:
+ counter.stop()
+
diff --git a/pypers/bolzano/web/prova.html b/pypers/bolzano/web/prova.html
new file mode 100755
index 0000000..882f46c
--- /dev/null
+++ b/pypers/bolzano/web/prova.html
@@ -0,0 +1 @@
+<img src="file:///home/micheles/Desktop/photos-images/images/baldios.jpg">
diff --git a/pypers/bolzano/web/q_forms.py b/pypers/bolzano/web/q_forms.py
new file mode 100755
index 0000000..e3decf1
--- /dev/null
+++ b/pypers/bolzano/web/q_forms.py
@@ -0,0 +1,27 @@
+from quixote_utils import RootDirectory
+from quixote.form.widget import StringWidget, SubmitWidget
+from quixote.form.form import Form
+
+class Root(RootDirectory):
+ _q_exports = ["string_widget", "submit_button", "form_with_submit",
+ "thank_you"]
+
+ def string_widget(self):
+ return StringWidget("Telefono", value="0699944857").render()
+
+ def submit_button(self):
+ return SubmitWidget("submit", value="ok").render()
+
+ def thank_you(self, number):
+ return "Thank you for submitting %s" % number
+
+ def form_with_submit(self):
+ form = Form()
+ form.add(StringWidget, "Telefono", value="XXX")
+ form.add(SubmitWidget, "submit", value="ok")
+ if not form.is_submitted():
+ return form.render()
+ else:
+ return self.thank_you(form["Telefono"])
+
+Root().publish_show("form_with_submit")
diff --git a/pypers/bolzano/web/quixote_utils.py b/pypers/bolzano/web/quixote_utils.py
new file mode 100755
index 0000000..dc7005a
--- /dev/null
+++ b/pypers/bolzano/web/quixote_utils.py
@@ -0,0 +1,189 @@
+import os, sys, time, webbrowser
+from quixote.directory import Directory
+from quixote.publish import Publisher
+from quixote.session import Session, SessionManager
+from quixote.server import simple_server
+from quixote.errors import AccessError
+from quixote import get_response, get_user
+from quixote.html import href
+
+elinks = webbrowser.GenericBrowser('xterm -e elinks %s')
+lynx = webbrowser.GenericBrowser('xterm -e lynx -accept_all_cookies %s')
+webbrowser.register("elinks", webbrowser.GenericBrowser, elinks)
+webbrowser.register("lynx", webbrowser.GenericBrowser, lynx) # second choice
+webbrowser.register("konqueror", webbrowser.Konqueror)
+
+class RecognizeExports(type):
+ def __init__(cls, name, bases, dic):
+ super(RecognizeExports, cls).__init__(cls, name, bases, dic)
+ for k in dic: setattr(cls, k, dic[k])
+ def __setattr__(cls, name, value):
+ if hasattr(value, "_q_exported"):
+ cls._q_exports.append(name)
+ super(RecognizeExports, cls).__setattr__(name, value)
+
+# by definition, the root directory is a singleton
+class RootDirectory(Directory):
+ _q_exports = [""]
+ __metaclass__ = RecognizeExports
+ __Publisher = Publisher
+ __port = 7080
+
+ def _q_index(self):
+ return "Welcome to the root of your application."
+
+ def set_default(self, server=simple_server, Publisher=Publisher,
+ Session=Session, session_mapping=None, port=7080):
+ self.__server = server
+ self.__Publisher = Publisher
+ self.__Session = Session
+ self.__session_mapping = session_mapping
+ self.__port = port
+
+ __init__ = set_default
+
+ def publish(self):
+ if issubclass(self.__Publisher, SessionPublisher):
+ create_pub = lambda : self.__Publisher(
+ self, SessionManager(self.__Session, self.__session_mapping))
+ else:
+ create_pub = lambda : self.__Publisher(self)
+ self.__server.run(create_pub, '', self.__port)
+
+ def publish_show(self, page="", browser="mozilla"):
+ if os.fork(): # parent
+ self.publish()
+ else: # child
+ webbrowser.get(browser).open(
+ "http://localhost:%s/%s" % (self.__port, page))
+
+
+class UnauthorizedError(AccessError):
+ """The request requires user authentication.
+
+ This subclass of AccessError sends a 401 instead of a 403,
+ hinting that the client should try again with authentication.
+ """
+ status_code = 401
+ title = "Unauthorized"
+ description = "You are not authorized to access this resource."
+
+ def __init__(self, realm='Protected', public_msg=None, private_msg=None):
+ self.realm = realm
+ AccessError.__init__(self, public_msg, private_msg)
+
+ def format(self):
+ get_response().set_header(
+ 'WWW-Authenticate', 'Basic realm="%s"' % self.realm)
+ return AccessError.format(self)
+
+
+class User(object):
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+ def __str__(self):
+ return "<User: %s %s>" % (self.username, self.password)
+ def permissions(self):
+ """Returns the list of methods starting with 'can_'."""
+ return [perm for perm in dir(self) if perm.startswith("can_")]
+
+def public(f):
+ f._q_exported = True
+ return f
+
+class private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join(
+ cls.__name__ for cls in self.valid_groups)
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href(
+ "login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ wrapper.func_name = meth_name
+ wrapper._q_exported = True
+ return wrapper
+
+######################## deprecated ############################
+
+def old_public(f):
+ """Append f.__name__ to the caller's _q_exports. If the caller has
+ no _q_exports, creates it."""
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(f.__name__)
+ return f
+
+class old_private(object):
+ """Redirect to the login page if the user is not logged in or if he does
+ not have the right permissions."""
+ # obviously, this assumes a login page exists and is called 'login'
+ def __init__(self, *groups_with_access):
+ self.valid_groups = groups_with_access or (User,)
+ def __call__(self, method):
+ def wrapper(root):
+ user = get_user()
+ if not user or not isinstance(user, self.valid_groups):
+ root.resume = meth_name
+ valid_groups = ", ".join(cls.__name__ for cls in self.valid_groups)
+ return "You are trying to access a page restricted to %s. " % \
+ valid_groups + href("login", "Please login as a valid user.")
+ else:
+ return method(root)
+ meth_name = method.func_name
+ _q_exports = sys._getframe(1).f_locals.setdefault("_q_exports",[""])
+ _q_exports.append(meth_name)
+ wrapper.func_name = meth_name
+ return wrapper
+
+from ms.iter_utils import Cycle, chop
+
+class MultipageTable(object):
+ # use Quixote forms
+ def __init__(self, body, header=[], maxsize=20):
+ self.header = header
+ self.maxsize = maxsize
+ self.section = Cycle(chop(body, maxsize))
+ self.sect = self.section[0] # default
+ self.ismultipage = len(self.section) > 1
+
+ def makerow(self, row, header=False):
+ if header:
+ r = " ".join("<th>%s</th>" % col for col in row)
+ else:
+ r = " ".join("<td>%s</td>" % col for col in row)
+ return "<tr>%s</tr>" % r
+
+ def maketable(self):
+ #yield "<div align='center'>"
+ if self.ismultipage:
+ form = Form()
+ form.add(SubmitWidget, "prev", "Prev")
+ form.add(SubmitWidget, "next", "Next")
+ if form["next"]: # is submitted
+ self.sect = self.section.next()
+ if form["prev"]: # is submitted
+ self.sect = self.section.prev()
+ yield "Page #%s of %s" % (self.section.index+1, len(self.section))
+ yield "<table border='1'>"
+ if self.header:
+ yield self.makerow(self.header)
+ for row in self.sect:
+ yield self.makerow(row)
+ yield "</table>"
+ if self.ismultipage:
+ yield form.render()
+ #yield "</div>"
+
+ def render(self):
+ return htmltext("\n").join(map(htmltext, self.maketable()))
diff --git a/pypers/bolzano/web/viewer.py b/pypers/bolzano/web/viewer.py
new file mode 100755
index 0000000..d9b3299
--- /dev/null
+++ b/pypers/bolzano/web/viewer.py
@@ -0,0 +1,28 @@
+from quixote_utils import RootDirectory
+from quixote.util import htmltext
+from quixote.html import href
+import os
+
+imagedir = "/mnt/sda1/photos-images/images"
+
+class ImageViewer(RootDirectory):
+ _q_exports = [""]
+ def __init__(self, images):
+ self.images = images
+ self.links = []
+ for name in self.images:
+ nospaces_name = name.replace(" ", "_")
+ self._q_exports.append(nospaces_name)
+ self.links.append(href(nospaces_name, name))
+ def imagesrc(name=name):
+ return "<image src='file://%s'>" % \
+ os.path.join(imagedir, name)
+ setattr(self, nospaces_name, imagesrc)
+ def _q_index(self):
+ return htmltext("<h1>Available images</h1>")+ \
+ htmltext("<br/>").join(self.links)
+
+viewer = ImageViewer([img for img in os.listdir(imagedir)
+ if img.endswith(".jpg")])
+
+viewer.publish_show()
diff --git a/pypers/bolzano/web/viewer2.py b/pypers/bolzano/web/viewer2.py
new file mode 100755
index 0000000..1ba26c9
--- /dev/null
+++ b/pypers/bolzano/web/viewer2.py
@@ -0,0 +1,35 @@
+from quixote_utils import RootDirectory
+from quixote.util import htmltext
+from quixote.html import href
+import os
+
+imagedir = "/home/micheles/Desktop/photos-images/images"
+
+class ImageViewer(RootDirectory):
+ _q_exports = ["", "prev", "next"]
+ def __init__(self, images):
+ self.images = images
+ self.current_image = 0
+
+ def prev(self):
+ self.current_image -= 1
+ return self._q_index()
+
+ def next(self):
+ self.current_image += 1
+ return self._q_index()
+
+ def _q_index(self):
+ name = self.images[self.current_image]
+ return '''<html><head></head><body>
+ <a href="prev">prev</a>
+ <a href="next">next</a><br/>
+ Current image: %s <br/>
+ <img src="file://%s">
+ </body><html>
+ ''' % (self.current_image, os.path.join(imagedir, name))
+
+viewer = ImageViewer([img for img in os.listdir(imagedir)
+ if img.endswith(".jpg")])
+
+viewer.publish_show(browser="mozilla")
diff --git a/pypers/bolzano/webplayer.py b/pypers/bolzano/webplayer.py
new file mode 100755
index 0000000..5a46f52
--- /dev/null
+++ b/pypers/bolzano/webplayer.py
@@ -0,0 +1,67 @@
+import os
+from quixote.directory import Directory
+from ms.html_utils import simplepage, makelink
+from quixote.publish import Publisher
+from quixote import get_field, redirect
+from quixote.server.simple_server import run
+from quixote.form.form import Form
+from ms.misc_utils import Popen # fix Popen
+
+MUSIC = "/mnt/smb/Music"
+PLAYER = "/usr/bin/mpg123"
+
+class Current:
+ player = None
+ song = None
+
+def play(song, player = PLAYER):
+ print "Playing %s ..." % song
+ return Popen([player, song])
+
+def add_to(obj):
+ def wrapper(f):
+ setattr(obj, f.__name__, f)
+ return f
+ return wrapper
+
+top = Directory()
+
+top._q_exports= ["", "stopper"]
+
+songs=[os.path.join(MUSIC, x) for x in os.listdir(MUSIC) if x.endswith(".mp3")]
+
+@add_to(top)
+def _q_index():
+ return simplepage(
+ title="Welcome to the Web player!",
+ body=selector(songs))
+
+def selector(songs):
+ global player, song
+ chosen = get_field("select")
+ if chosen:
+ song = chosen
+ player = play(song)
+ redirect("stopper") # works with Mozilla, but not with lynx/elinks
+ else:
+ f = Form()
+ f.add_single_select("select", options=songs)
+ f.add_submit("play", "Play!")
+ return f.render()
+
+def stopper():
+ stop = get_field("stop")
+ if stop:
+ player.kill()
+ return simplepage(body = "%s stopped." % song)
+ else:
+ f = Form()
+ f.add_submit("stop", "Stop")
+ return simplepage(body= ("Playing %s" % song) +
+ f.render())
+
+top.stopper = lambda : stopper()
+
+if __name__ == '__main__':
+ print 'Listening on http://localhost:8080 ...'
+ run(lambda : Publisher(top), '', 8080)
diff --git a/pypers/bug.txt b/pypers/bug.txt
new file mode 100755
index 0000000..5bc410d
--- /dev/null
+++ b/pypers/bug.txt
@@ -0,0 +1,31 @@
+Ask why SyntaxError
+
+from oopp import customized
+
+class Customizable(object):
+ """Classes inhering from 'Customizable' have a 'with' method acting as
+ an object modifier"""
+ def __getitem__(self,**kw):
+ print kw
+ with=customized
+ With=classmethod(customized)
+ class __metaclass__(type):
+ def __getitem__(cls,kw=None):
+ print kw
+
+c=Customizable()
+c.with(a='a')
+print c.a
+
+Customizable.With(b='b')
+
+#Customizable[a='b']
+
+print c.b
+
+print c[kw='a'] # SyntaxError: invalid syntax
+
+
+def f(kw='b'): print kw
+
+f(kw='a') # works
diff --git a/pypers/classcreation.html b/pypers/classcreation.html
new file mode 100755
index 0000000..79674b0
--- /dev/null
+++ b/pypers/classcreation.html
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<div class="section" id="what-happens-when-python-executes-a-class-statement">
+<h1><a name="what-happens-when-python-executes-a-class-statement">What happens when Python executes a class statement?</a></h1>
+<p>It seems an easy question, isn't ?
+But the answer is pretty sophisticated ;)</p>
+<ol class="arabic">
+<li><p class="first">First of all, Python determines which is the currect metaclass to
+use in order to create the class. Here are the rules:</p>
+<ol class="arabic simple">
+<li>if an explicit <tt class="literal"><span class="pre">__metaclass__</span></tt> is given, use it;</li>
+<li>otherwise, determine the correct metaclass from the base classes's
+metaclass.</li>
+</ol>
+<p>The determination from the base
+classes' metaclasses is based on the following principles:</p>
+<ol class="arabic simple">
+<li>custom metaclasses have the precedence over the built-in metaclasses
+<tt class="literal"><span class="pre">type</span></tt> and <tt class="literal"><span class="pre">ClassType</span></tt>;</li>
+<li><tt class="literal"><span class="pre">type</span></tt>, i.e. the default metaclass of new style classes, has the precedence
+over <tt class="literal"><span class="pre">ClassType</span></tt>, the metaclass of old style classes;</li>
+<li>in the case of same precedence the first, i.e. the leftmost
+metaclass is used.</li>
+</ol>
+</li>
+<li><p class="first">Let's call <tt class="literal"><span class="pre">M</span></tt> the correct metaclass. Then <tt class="literal"><span class="pre">M</span></tt> is called in the form
+<tt class="literal"><span class="pre">M(n,b,d)</span></tt>, where <tt class="literal"><span class="pre">n</span></tt> is the name of the class to create, <tt class="literal"><span class="pre">b</span></tt> the tuple
+of its base classes and <tt class="literal"><span class="pre">d</span></tt> its dictionary. This is equivalent to call
+<tt class="literal"><span class="pre">type(M).__call__(M,n,b,d)</span></tt>.</p>
+<p>Now, unless <tt class="literal"><span class="pre">type(M).__call__</span></tt> is overridden with a meta-metaclass,
+the standard <tt class="literal"><span class="pre">type.__call__</span></tt> will be used.</p>
+</li>
+<li><p class="first">The following happens when <tt class="literal"><span class="pre">type.__call__(M,n,b,d)</span></tt> is invoked:</p>
+<ol class="arabic simple">
+<li>Python tries to create the class <tt class="literal"><span class="pre">C</span></tt> by using <tt class="literal"><span class="pre">M.__new__(M,n,b,d)</span></tt>,
+which at a certain moment will invoke <tt class="literal"><span class="pre">type.__new__(M,n,b,d)</span></tt>.</li>
+<li>If a metaclass conflict is found in <tt class="literal"><span class="pre">type.__new__</span></tt>, Python
+raises an exception.</li>
+<li>If not, Python initializes <tt class="literal"><span class="pre">C</span></tt> by using <tt class="literal"><span class="pre">type(C).__init__(C,n,b,d)</span></tt>.</li>
+</ol>
+<p>Notice that <tt class="literal"><span class="pre">type(C)</span></tt> is usually <tt class="literal"><span class="pre">M</span></tt>, unless <tt class="literal"><span class="pre">M.__new__</span></tt> is badly
+overridden by a metaclass novice or very subtly overridden by a metaclass
+wizard.</p>
+</li>
+</ol>
+<p>I argued these rules from experiment, but it should be possible to infer
+them from Python-2.3/Objects/typeobject.c, at least for experts of Python
+internals.</p>
+</div>
+<div class="section" id="an-example-of-badly-written-metaclass">
+<h1><a name="an-example-of-badly-written-metaclass">An example of badly written metaclass</a></h1>
+<p>Let me start by checking the validity of point 3.3, i.e the fact that the
+newly created class is initialized with <tt class="literal"><span class="pre">type(C).__init__(C,n,b,d)</span></tt>
+and not with <tt class="literal"><span class="pre">M.__init__(C,n,b,d)</span></tt>. This happens when <tt class="literal"><span class="pre">M.__new__</span></tt>
+is overridden in such a way that it does not return an instance of <tt class="literal"><span class="pre">M</span></tt>.
+Here is a simple example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... &quot;A badly written metaclass&quot;
+... def __new__(mcl,n,b,d):
+... print 'calling M.__new__, name = %s ...' % n
+... return type(n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M.__init__, name = %s ...' % n
+</pre>
+<p>In this example <tt class="literal"><span class="pre">M.__new__</span></tt> returns an instance of <tt class="literal"><span class="pre">type</span></tt>, so it
+will be initialized with <tt class="literal"><span class="pre">type.__init__</span></tt> and not with <tt class="literal"><span class="pre">M.__init__</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... __metaclass__=M
+calling M.__new__, name = C ...
+</pre>
+<p>As you see, <tt class="literal"><span class="pre">M.__init__</span></tt> is not called since</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(C)
+&lt;type 'type'&gt;
+</pre>
+<p>Typically, missing <tt class="literal"><span class="pre">M.__init__</span></tt> is not what you want. There are two
+solutions:</p>
+<ol class="arabic simple">
+<li>returns <tt class="literal"><span class="pre">type.__call__(mcl,n,b,d)</span></tt> (the poor man solution)</li>
+<li>returns <tt class="literal"><span class="pre">super(M,mcl).__new__(mcl,n,b,d)</span></tt> (the right solution)</li>
+</ol>
+<p>The cooperative solution is the right one because it will work always,
+whereas the solution number 1 may fail when <tt class="literal"><span class="pre">M</span></tt> is composed trough
+multiple inheritance with another metaclass.</p>
+</div>
+<div class="section" id="a-simple-example">
+<h1><a name="a-simple-example">A simple example.</a></h1>
+<p>Suppose we have two classes <tt class="literal"><span class="pre">A</span></tt> (old style) and <tt class="literal"><span class="pre">B</span></tt> (new style):</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A:
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... pass
+</pre>
+<p>We want to create a class <tt class="literal"><span class="pre">C</span></tt> with bases <tt class="literal"><span class="pre">A</span></tt> and <tt class="literal"><span class="pre">B</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B):
+... pass
+</pre>
+<p>In order to proceed with the creation, Python argues that the metaclass
+to use is the metaclass of <tt class="literal"><span class="pre">B</span></tt>, i.e. <tt class="literal"><span class="pre">type</span></tt> and not the metaclass of
+<tt class="literal"><span class="pre">A</span></tt>, i.e. <tt class="literal"><span class="pre">ClassType</span></tt>. Then the class statement is equivalent to</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C=type('C',(A,B),{})
+</pre>
+<p>i.e. to</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(type).__call__(type,'C',(A,B),{})
+&lt;class 'C'&gt;
+</pre>
+<p>i.e. to (since <tt class="literal"><span class="pre">type</span></tt> is its own metaclass)</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type.__call__(type,'C',(A,B),{})
+&lt;class 'C'&gt;
+</pre>
+<p>Notice that</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type.__call__('C',(A,B),{})
+Traceback (most recent call last):
+ ...
+TypeError: descriptor '__call__' requires a 'type' object but received a 'str'
+</pre>
+<p>will not work, i.e. it is not equivalent to
+<tt class="literal"><span class="pre">type(type).__call__(type,'C',(A,B),{})</span></tt> as it happens for regular callable
+objects. <tt class="literal"><span class="pre">type</span></tt> is special, since it is its own metaclass, so <tt class="literal"><span class="pre">__call__</span></tt>
+has to be defined on it with four and not three arguments.</p>
+</div>
+<div class="section" id="a-less-simple-example">
+<h1><a name="a-less-simple-example">A less simple example</a></h1>
+<p>In the previous examples, the class statement and the <tt class="literal"><span class="pre">type</span></tt>
+call were perfectly equivalent.
+If there is a non-trivial metaclass, the situation changes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M1(type):
+... &quot;A chatty metaclass&quot;
+... def __new__(mcl,n,b,d):
+... print 'calling M1.__new__, name = %s ...' % n
+... return super(M1,mcl).__new__(mcl,n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M1.__init__, name = %s ...' % n
+... super(M1,cls).__init__(n,b,d)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A:
+... __metaclass__=M1
+calling M1.__new__, name = A ...
+calling M1.__init__, name = A ...
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B):
+... pass
+calling M1.__new__, name = C ...
+calling M1.__init__, name = C ...
+</pre>
+<p>In this case the class statement is equivalent to <tt class="literal"><span class="pre">M1('C',(A,B),{})</span></tt>
+i.e. to <tt class="literal"><span class="pre">type(M1).___call__(M1,'C',(A,B),{})</span></tt> i.e.
+<tt class="literal"><span class="pre">type.___call__(M1,'C',(A,B),{})</span></tt>.
+However, it is interesting to notice that <tt class="literal"><span class="pre">type</span></tt> would work too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type('C',(A,B),{})
+calling M1.__new__, name = C ...
+calling M1.__init__, name = C ...
+&lt;class 'C'&gt;
+</pre>
+<p>The reason is that <tt class="literal"><span class="pre">type.__call__</span></tt> is able to dispatch to the correct
+metaclass. However, the class statement is not always equivalent to a
+<tt class="literal"><span class="pre">type.__call__</span></tt> and there is a very subtle difference between the
+two. The difference only manifests itself in the
+the case in which <tt class="literal"><span class="pre">A</span></tt> and <tt class="literal"><span class="pre">B</span></tt> have incompatible metaclasses.</p>
+</div>
+<div class="section" id="a-more-sophisticated-example">
+<h1><a name="a-more-sophisticated-example">A more sophisticated example</a></h1>
+<p>Here is an example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M2(type):
+... &quot;Another chatty metaclass&quot;
+... def __new__(mcl,n,b,d):
+... print 'calling M2.__new__, name = %s ...' % n
+... return super(M2,mcl).__new__(mcl,n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M2.__init__, name = %s ...' % n
+... super(M2,cls).__init__(n,b,d)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B:
+... __metaclass__=M2
+calling M2.__new__, name = B ...
+calling M2.__init__, name = B ...
+</pre>
+<p>If we try to create <tt class="literal"><span class="pre">C</span></tt> with <tt class="literal"><span class="pre">type</span></tt>, we simply get a conflict:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type.__call__(type,'C',(A,B),{})
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>On the other hand, with a class statement we get</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B):
+... pass
+calling M1.__new__, name = C ... # doctest bug here
+Traceback (most recent call last):
+ File &quot;pro.py&quot;, line 4, in __new__
+ return super(M1,mcl).__new__(mcl,n,b,d)
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>The difference is that <tt class="literal"><span class="pre">M1.__new__</span></tt> is called <em>first</em>, and only after
+the conflict is discovered, when <tt class="literal"><span class="pre">M1.__new__</span></tt> invokes <tt class="literal"><span class="pre">type.__new__</span></tt>.
+The error message is pretty clear indeed.
+In other words, the class statement in this case is equivalent to
+<tt class="literal"><span class="pre">M1('C',(A,B),{})</span></tt> i.e. to <tt class="literal"><span class="pre">type.__call__(M1,'C',(A,B),{})</span></tt>.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from safetype import safetype as type
+</pre>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="classcreation.txt">View document source</a>.
+Generated on: 2003-10-04 08:36 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/classcreation.txt b/pypers/classcreation.txt
new file mode 100755
index 0000000..fc994ec
--- /dev/null
+++ b/pypers/classcreation.txt
@@ -0,0 +1,280 @@
+What happens when Python executes a class statement?
+-----------------------------------------------------------------------------
+
+It seems an easy question, isn't ?
+But the answer is pretty sophisticated ;)
+
+
+### see also the documentation: file:/mnt/win/Python23/Doc/ref/metaclasses.html
+
+1. First of all, Python determines which is the currect metaclass to
+ use in order to create the class. Here are the rules:
+
+ 1. if an explicit ``__metaclass__`` is given, use it;
+ 2. otherwise, determine the correct metaclass from the base classes's
+ metaclass.
+
+ The determination from the base
+ classes' metaclasses is based on the following principles:
+
+ 1. custom metaclasses have the precedence over the built-in metaclasses
+ ``type`` and ``ClassType``;
+ 2. ``type``, i.e. the default metaclass of new style classes, has the
+ precedence over ``ClassType``, the default metaclass of old style
+ classes;
+ 3. if there is a global ``__metaclass__`` it acts as the new default
+ metaclass for old style classes;
+ 4. in the case of same precedence the first, i.e. the leftmost
+ metaclass is used.
+
+2. Let's call ``M`` the correct metaclass. Then ``M`` is called in the form
+ ``M(n,b,d)``, where ``n`` is the name of the class to create, ``b``
+ the tuple of its base classes and ``d`` its dictionary. This is equivalent
+ to call ``type(M).__call__(M,n,b,d)``.
+
+ Now, unless ``type(M).__call__`` is overridden with a meta-metaclass,
+ the standard ``type.__call__`` will be used.
+
+3. The following happens when ``type.__call__(M,n,b,d)`` is invoked:
+
+ 1. Python tries to create the class ``C`` by using ``M.__new__(M,n,b,d)``,
+ which at a certain moment will invoke ``type.__new__(M,n,b,d)``.
+ 2. ``type.__new__(M,n,b,d)`` looks at ``M`` and at the metaclasses
+ of the bases (again) and raises an exception if a metaclass
+ conflict is found;
+ 3. If not, it creates the class using the right metaclass;
+ 4. Finally, Python initializes ``C`` by using ``type(C).__init__(C,n,b,d)``.
+
+ Notice that ``type(C)`` is not necessarely ``M``. Moreover,
+ ``type.__call__`` is also able to manage the case when it is invoked
+ with only two arguments.
+
+The interesting point is that the check for metaclass conflicts is not
+immediate: first a tentative metaclass ``M`` is determined and called, then
+its ``__new__`` method dispatches to ``type.__new__`` and
+only at that moment there will be the check. This suggest the basic idea to solve
+the conflict: to override ``M.__new__`` in such a way to create the
+right metaclass as a submetaclass of ``M`` and the metaclasses of the
+bases; then dispatch to this metaclass ``__new__`` method.
+
+
+I argued these rules from experiment, but it should be possible to infer
+them from Python-2.3/Objects/typeobject.c, at least for experts of Python
+internals.
+
+How ``type.__call__`` works.
+---------------------------------------------------------------------------
+
+Let me start by discussing few examples where the metaclass is automatically
+determinated, i.e. there is no explicit ``__metaclass__`` hook.
+My simplest example is the case in which there are two base classes,
+one old style (``A``) and the other new style (``B``):
+
+>>> class A:
+... pass
+
+>>> class B(object):
+... pass
+
+We want to create a class ``C`` with bases ``A`` and ``B``:
+
+>>> class C(A,B):
+... pass
+
+In order to proceed with the creation, Python argues that the metaclass
+to use is the metaclass of ``B``, i.e. ``type`` and not the metaclass of
+``A``, i.e. ``ClassType``. Then the class statement is equivalent to
+
+>>> C=type('C',(A,B),{})
+
+i.e. to
+
+>>> type(type).__call__(type,'C',(A,B),{})
+<class 'C'>
+
+i.e. to (since ``type`` is its own metaclass)
+
+>>> type.__call__(type,'C',(A,B),{})
+<class 'C'>
+
+Notice that
+
+>>> type.__call__('C',(A,B),{})
+Traceback (most recent call last):
+ ...
+TypeError: descriptor '__call__' requires a 'type' object but received a 'str'
+
+will not work, i.e. it is not equivalent to
+``type(type).__call__(type,'C',(A,B),{})`` as it happens for regular callable
+objects. ``type`` is special, since it is its own metaclass, so ``type.__call__``
+has to be defined on it with four and not three arguments.
+
+A less simple example
+--------------------------------------------------------------------------
+
+In the previous examples, the class statement and the ``type``
+call were perfectly equivalent.
+If there is a non-trivial metaclass, the situation changes:
+
+>>> class M1(type):
+... "A chatty metaclass"
+... def __new__(mcl,n,b,d):
+... print 'calling M1.__new__, name = %s ...' % n
+... return super(M1,mcl).__new__(mcl,n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M1.__init__, name = %s ...' % n
+... super(M1,cls).__init__(n,b,d)
+
+>>> class A:
+... __metaclass__=M1
+calling M1.__new__, name = A ...
+calling M1.__init__, name = A ...
+
+>>> class C(A,B):
+... pass
+calling M1.__new__, name = C ...
+calling M1.__init__, name = C ...
+
+In this case the class statement is equivalent to ``M1('C',(A,B),{})``
+i.e. to ``type(M1).___call__(M1,'C',(A,B),{})`` i.e.
+``type.___call__(M1,'C',(A,B),{})``.
+However, it is interesting to notice that ``type`` would work too:
+
+>>> type('C',(A,B),{})
+calling M1.__new__, name = C ...
+calling M1.__init__, name = C ...
+<class 'C'>
+
+The reason is that ``type.__call__`` is able to dispatch to the correct
+metaclass ``__new__``.
+
+Moreover, there is the issue of what ``type`` does when it is called with
+only two arguments:
+
+>>> type.__call__(type,C)
+<class 'M1'>
+
+However, the class statement is not always equivalent to a
+``type.__call__`` and there is a very subtle difference between the
+two. The difference only manifests itself in the
+the case in which ``A`` and ``B`` have incompatible metaclasses.
+
+
+Differences between class statement and ``type`` invokation
+---------------------------------------------------------------------------
+
+Consider the following metaclass with a tricked ``__call__`` metamethod:
+
+>>> class M(type):
+... class __metaclass__(type):
+... def __call__(mcl,n,b,d):
+... print "You are calling type(M).__call__ ..."
+.. return super(mcl.__class__,mcl).__call__(n,b,d)
+... def __new__(mcl,n,b,d):
+... print 'calling M.__new__, name = %s ...' % n
+... return super(M,mcl).__new__(mcl,n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M.__init__, name = %s ...' % n
+... super(M,cls).__init__(n,b,d)
+
+>>> class B:
+... __metaclass__=M
+You are calling type(M).__call__ ...
+calling M.__new__, name = B ...
+calling M.__init__, name = B ...
+
+>>> class C(B):
+... pass
+You are calling type(M).__call__ ...
+calling M.__new__, name = C ...
+calling M.__init__, name = C ...
+
+This makes clear that the class statement is calling ``M('C',(),{})``
+and NOT ``type('C',(),{})``.
+
+A more sophisticated example
+--------------------------------------------------------------------------
+
+Here is an example:
+
+>>> class M2(type):
+... "Another chatty metaclass"
+... def __new__(mcl,n,b,d):
+... print 'calling M2.__new__, name = %s ...' % n
+... return super(M2,mcl).__new__(mcl,n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M2.__init__, name = %s ...' % n
+... super(M2,cls).__init__(n,b,d)
+
+>>> class B:
+... __metaclass__=M2
+calling M2.__new__, name = B ...
+calling M2.__init__, name = B ...
+
+If we try to create ``C`` with ``type``, we simply get a conflict:
+
+>>> type.__call__(type,'C',(A,B),{})
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+On the other hand, with a class statement we get
+
+>>> class C(A,B):
+... pass
+calling M1.__new__, name = C ... # doctest bug here
+Traceback (most recent call last):
+ File "pro.py", line 4, in __new__
+ return super(M1,mcl).__new__(mcl,n,b,d)
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The difference is that ``M1.__new__`` is called *first*, and only after
+the conflict is discovered, when ``M1.__new__`` invokes ``type.__new__``.
+The error message is pretty clear indeed.
+In other words, the class statement in this case is equivalent to
+``M1('C',(A,B),{})`` i.e. to ``type.__call__(M1,'C',(A,B),{})``.
+
+
+
+
+>>> from safetype import safetype as type
+
+An example of badly written metaclass
+--------------------------------------------------------------------------
+
+Let me start by checking the validity of point 3.3, i.e the fact that the
+newly created class is initialized with ``type(C).__init__(C,n,b,d)``
+and not with ``M.__init__(C,n,b,d)``. This happens when ``M.__new__``
+is overridden in such a way that it does not return an instance of ``M``.
+Here is a simple example:
+
+
+>>> class M(type):
+... "A badly written metaclass"
+... def __new__(mcl,n,b,d):
+... print 'calling M.__new__, name = %s ...' % n
+... return type(n,b,d)
+... def __init__(cls,n,b,d):
+... print 'calling M.__init__, name = %s ...' % n
+
+In this example ``M.__new__`` returns an instance of ``type``, so it
+will be initialized with ``type.__init__`` and not with ``M.__init__``:
+
+>>> class C:
+... __metaclass__=M
+calling M.__new__, name = C ...
+
+As you see, ``M.__init__`` is not called since
+
+>>> type(C)
+<type 'type'>
+
+Typically, missing ``M.__init__`` is not what you want. There are two
+solutions:
+
+1. returns ``type.__call__(mcl,n,b,d)`` (the poor man solution)
+2. returns ``super(M,mcl).__new__(mcl,n,b,d)`` (the right solution)
+
+The cooperative solution is the right one because it will work always,
+whereas the solution number 1 may fail when ``M`` is composed trough
+multiple inheritance with another metaclass.
diff --git a/pypers/classes.txt b/pypers/classes.txt
new file mode 100755
index 0000000..0c8e337
--- /dev/null
+++ b/pypers/classes.txt
@@ -0,0 +1,1093 @@
+THE POWER OF CLASSES
+==========================================================================
+
+This chapter is devoted to the concept of class inheritance. I will discuss
+single inheritance, cooperative methods, multiple inheritance and more.
+
+
+The concept of inheritance
+----------------------------------------------------------------------
+
+Inheritance is perhaps the most important basic feature in OOP, since it
+allows the reuse and incremental improvement of old code.
+To show this point, let me come back to one of the
+examples I have introduced in the last chapter, 'fairytale1.py' script,
+where I defined the classes 'Frog' and 'Prince' as
+
+ ::
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+We see that the way we followed here was very bad since:
+
+1. The ``__str__`` method is duplicated both in Frog and in Prince: that
+ means that if we find a bug a later, we have to fix it twice!
+
+2. The ``__str__`` was already defined in the PrettyPrinted class (actually
+ more elegantly), therefore we have triplicated the work and worsened the
+ situation!
+
+This is very much against the all philosophy of OOP:
+
+ *never cut and paste!*
+
+We should *reuse* old code, not paste it!
+
+The solution is *class inheritance*. The idea behind inheritance is to
+define new classes as subclasses of a *parent* classes, in such a way that
+the *children* classes possess all the features of the parents.
+That means that we do not need to
+redefine the properties of the parents explicitely.
+In this example, we may derive both 'Frog' and 'Prince' from
+the 'PrettyPrinted' class, thus providing to both 'Frog' and 'Prince'
+the ``PrettyPrinted.__str__`` method with no effort:
+
+ >>> from oopp import PrettyPrinted
+ >>> class Frog(PrettyPrinted): attributes="poor, small, ugly"
+ ...
+ >>> class Prince(PrettyPrinted): attributes="rich, tall, beautiful"
+ ...
+ >>> print repr(Frog()), Frog()
+ <__main__.Frog object at 0x401cbeac> <Frog>
+ >>> print Prince()
+ >>> print repr(Prince()),Prince()
+ <__main__.Prince object at 0x401cbaac> <Prince>
+
+Let me show explicitly that both 'Frog' and 'Prince' share the
+'PrettyPrinted.__str__' method:
+
+ >>> id(Frog.__str__) # of course, YMMV
+ 1074329476
+ >>> id(Prince.__str__)
+ 1074329476
+ >>> id(PrettyPrinted.__str__)
+ 1074329476
+
+The method is always the same, since the object reference is the same
+(the precise value of the reference is not guaranteed to be 1074329476,
+however!).
+
+This example is good to show the first advantage of inheritance:
+*avoiding duplication of code*.
+Another advantage of inheritance, is *extensibility*: one can very easily
+improve existing code. For instance, having written the ``Clock`` class once,
+I can reuse it in many different ways. for example I can build a ``Timer``
+to be used for benchmarks. It is enough to reuse the function ``with_timer``
+introduced in the first chapter (functions are good for reuse of code, too ;):
+
+ ::
+
+ #<oopp.py>
+
+ class Timer(Clock):
+ "Inherits the get_time staticmethod from Clock"
+ execute=staticmethod(with_timer)
+ loop_overhead=staticmethod(loop_overhead)
+
+
+ #</oopp.py>
+
+Here there is an example of application:
+
+ >>> from oopp import Timer
+ >>> Timer.get_time()
+ '16:07:06'
+
+Therefore 'Timer' inherits 'Clock.get_time'; moreover it has the additional
+method ``execute``:
+
+ >>> def square(x): return x*x
+ ...
+ >>> Timer.execute(square,n=100000)(1)
+ executing square ...
+ Real time: 0.01 ms CPU time: 0.008 ms
+
+The advantage of putting the function ``execute`` in a class is that
+now we may *inherit* from that class and improve out timer *ad
+libitum*.
+
+Inheritance versus run-time class modifications
+-------------------------------------------------------------------------
+
+Naively, one could think of substituting inheritance with run-time
+modification of classes, since this is allowed by Python. However,
+this is not such a good idea, in general. Let me give a simple example.
+Suppose we want to improve our previous clock, to show the date, too.
+We could reach that goal with the following script:
+
+ ::
+
+ #<clock2.py>
+
+ "Shows how to modify and enhances classes on the fly"
+
+ from oopp import *
+
+ clock=Clock() #creates a Clock instance
+ print clock.get_time() # print the current time
+
+ get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+ get_data_and_time=lambda : "Today is: %s \nThe time is: %s" % (
+ get_data(),get_time()) # enhances get_time
+
+ Clock.get_time=staticmethod(get_data_and_time)
+
+ print clock.get_time() # print the current time and data
+
+ #</clock2.py>
+
+The output of this script is:
+
+ 12:51:25
+ Today is: Sat Feb 22 2003
+ The time is: 12:51:25
+
+Notice that:
+
+1. I instantiated the ``clock`` object *before* redefining the ``get_time``
+ method, when it only could print the time and *not* the date.
+2. However, after the redefinition of the class, the behaviour of all its
+ instances is changed, *including the behaviour of objects instantiated
+ before the change!*. Then ``clock`` *can* print the date, too.
+
+This is not so surprising, once you recognize that Guido own a very famous
+time-machine ... ;-)
+
+Seriously, the reason is that an object does not contains a reserved copy
+of the attributes and methods of its class: it only contains *references*
+to them. If we change them in the class, the references to them in the
+object will stay the same, but the contents will change.
+
+In this example, I have solved the problem of enhancing the 'Clock' class
+without inheritance, but dynamically replaceing its ``get_time``
+(static) method with the `get_data_and_time`` (static) method.
+The dynamics modification of methods can be cool, but it should be avoided
+whenever possible, at least for two reasons [#]_:
+
+1. having a class and therefore all its instances (including the instances
+ created before the modification !) changed during the life-time of the
+ program can be very confusing to the programmer, if not to the interpreter.
+
+2. the modification is destructive: I cannot have the old ``get_time`` method
+ and the new one at the same time, unless one explicitly gives to it
+ a new name (and giving new names increases the pollution of the namespace).
+
+Both these disadvantages can be solved by resorting to the mechanism of
+inheritance. For instance, in this example, we can derive a new class
+``NewClock`` from ``Clock`` as follows:
+
+ ::
+
+ #<newclock.py>
+
+ import oopp,time
+
+ get_data=lambda : ' '.join(time.asctime().split()[0:3])+ \
+ ' '+time.asctime().split()[-1]
+
+ get_data_and_time=lambda : "Today is: %s \nThe time is: %s" % (
+ get_data(),oopp.get_time()) # enhances get_time
+
+ class NewClock(oopp.Clock):
+ """NewClock is a class that inherits from Clock, provides get_data
+ and overrides get_time."""
+ get_data=staticmethod(get_data)
+ get_time=staticmethod(get_data_and_time)
+
+ clock=oopp.Clock(); print 'clock output=',clock.get_time()
+ newclock=NewClock(); print 'newclock output=',newclock.get_time()
+
+ #</newclock.py>
+
+The output of this script is:
+
+ ::
+
+ clock output= 16:29:17
+ newclock output= Today is: Sat Feb 22 2003
+ The time is: 16:29:17
+
+We see that the two problems previously discussed are solved since:
+
+i) there is no cut and paste: the old method ``Clock.get_time()`` is used
+ in the definition of the new method ``NewClock.get_time()``;
+ii) the old method is still accessible as ``Clock.get_time()``; there is
+ no need to invent a new name like ``get_time_old()``.
+
+We say that the method ``get_time`` in ``NewClock`` *overrides* the method
+``get_time`` in Clock.
+
+
+This simple example shows the power of inheritance in code
+reuse, but there is more than that.
+
+Inheritance is everywhere in Python, since
+all classes inherit from object. This means that all classes
+inherit the methods and attributes of the object class, such as ``__doc__``,
+``__class__``, ``__str__``, etc.
+
+
+ .. [#] There are cases when run-time modifications of classes is useful
+ anyway: particularly when one wants to modify the behavior of
+ classes written by others without changing the source code. I
+ will show an example in next chapter.
+
+Inheriting from built-in types
+-----------------------------------------------------------------------
+
+However, one can subclass a built-in type, effectively creating an
+user-defined type with all the feature of a built-in type, and modify it.
+
+Suppose for instance one has a keyword dictionary such as
+
+ >>> kd={'title': "OOPP", 'author': "M.S.", 'year': 2003}
+
+it would be nice to be able to access the attributes without
+excessive quoting, i.e. using ``kd.author`` instead of ``kd["author"]``.
+This can be done by subclassing the built-in class ``dict`` and
+by overriding the ``__getattr__`` and ``__setattr__`` special methods:
+
+ ::
+
+ #<oopp/py>
+
+ class kwdict(dict):
+ "Keyword dictionary base class"
+ def __getattr__(self,attr):
+ return self[attr]
+ def __setattr__(self,key,val):
+ self[key]=val
+ __str__ = pretty
+
+ #</oopp/py>
+
+
+Here there is an example of usage:
+
+ >>> from oopp import kwdict
+ >>> book=kwdict({'title': "OOPP", 'author': "M.S."})
+ >>> book.author #it works
+ 'M.S.'
+ >>> book["author"] # this also works
+ 'M.S.'
+ >>> book.year=2003 #you may also add new fields on the fly
+ >>> print book
+ author = M.S.
+ title = OOPP
+ year = 2003
+
+The advantage of subclassing the built-in 'dict', it that you have for free
+all the standard dictionary methods, without having to reimplement them.
+
+However, to subclass built-in it is not always a piece of cake. In
+many cases there are complications, indeed. Suppose for instance
+one wants to create an enhanced string type, with
+the ability of indent and dedent a block of text provided by
+the following functions:
+
+ ::
+
+ #<oopp.py>
+
+ def indent(block,n):
+ "Indent a block of code by n spaces"
+ return '\n'.join([' '*n+line for line in block.splitlines()])
+
+ from textwrap import dedent # available in 2.3 only
+
+ #</oopp.py>
+
+The solution is to inherit from the built-in string type ``str``, and to
+add to the new class the ``indent`` and ``dedent`` methods:
+
+ >>> from oopp import indent,dedent
+ >>> class Str(str):
+ ... indent=indent
+ ... dedent=dedent
+ >>> s=Str('spam\neggs')
+ >>> type(s)
+ <class '__main__.Str'>
+ >>> print s.indent(4)
+ spam
+ eggs
+
+However, this approach has a disadvantage, since the output of ``indent`` is
+not a ``Str``, but a normal ``str``, therefore without the additional
+``indent`` and ``dedent`` methods:
+
+ >>> type(s.indent(4))
+ <type 'str'>
+ >>> s.indent(4).indent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+ AttributeError: 'str' object has no attribute 'indent'
+ >>> s.indent(4).dedent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 9, in ?
+ AttributeError: 'str' object has no attribute 'dedent'
+
+We would like ``indent`` to return a ``Str`` object. To solve this problem
+it is enough to rewrite the class as follows:
+
+ ::
+
+ #<Str.py>
+
+ from oopp import indent,dedent
+
+ class Str(str):
+ def indent(self,n):
+ return Str(indent(self,n))
+ def dedent(self):
+ return Str(dedent(self))
+
+ s=Str('spam\neggs').indent(4)
+ print type(s)
+ print s # indented s
+ s=s.dedent()
+ print type(s)
+ print s # non-indented s
+
+ #</Str.py>
+
+Now, everything works and the output of the previous script is
+
+ ::
+
+ <class 'Str'>
+ spam
+ eggs
+ <class 'Str'>
+ spam
+ eggs
+
+The solution works because now ``indent()`` returns an instance
+of ``Str``, which therefore has an ``indent`` method. Unfortunately,
+this is not the end. Suppose we want to add another food to our list:
+
+ >>> s2=s+Str("\nham")
+ >>> s2.indent(4) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'str' object has no attribute 'indent'
+
+The problem is the same, again: the type of ``s2`` is ``str``
+
+ >>> type(s2)
+ <type 'str'>
+
+and therefore there is no ``indent`` method available. There is a
+solution to this problem, i.e. to redefine the addition operator
+for objects of the class ``Str``. This can be done directly by hand,
+but it is *ugly* for the following reasons:
+
+1. If you derive a new class from ``Str`` you have to redefine the
+ addition operator (both the left addition and the right addition [#]_)
+ again (ughh!);
+2. There are others operators you must redefine, in particular the
+ the augumented assignement operator ``+=``, the repetition operator ``*``
+ and its augmented version ``*=``;
+3. In the case of numeric types, one must redefine, ``+,-,*,/,//, mod,``,
+ possibily ``<<,>>`` and others, including the corresponding
+ augumented assignement operators and the left and the right form of
+ the operators.
+
+This is a mess, especially since due to point 1, one has to redefined
+all the operators each time she defines a new subclass. I short, one has
+to write a lot of boilerplate for a stupid job that the language
+should be able to perform itself automatically. But here are the
+good news: Python *can* do all that automatically, in an elegant
+and beautiful way, which works for all types, too.
+
+But this requires the magic of metaclasses.
+
+ .. [#] The right addition works this way. Python looks at the expression x+y
+ and if x has an explicit__add__ method invokes it; on the other hand,
+ if x does not define an __add__ method, Python considers y+x.
+ If y defines a __radd__ method, it invokes it, otherwise
+ raises an exception. The same is done for right multiplication, etc.
+
+Controlling the creation of objects
+---------------------------------------------------------------------------
+
+Before introducing multiple inheritance, let me make a short digression on
+the mechanism of object creation in Python 2.2+. The important point is
+that new style classes have a ``__new__`` static method that allows
+the user to take complete control of object creation. To understand how
+``__new__`` works, I must explain what happens when an object is instantiated
+with a statement like
+
+ ::
+
+ s=Str("spam") #object creation
+
+What happens under the hood, is that the special static method ``__new__``
+of the class ``Str`` (inherited from the built-in ``str`` class)
+is invoked ``before`` the ``Str.__init__`` method. This means that
+the previous line should really be considered syntactic sugar for:
+
+ ::
+
+ s=Str.__new__(Str,"spam") # Str.__new__ is actually str.__new__
+ assert isinstance(s,Str)
+ Str.__init__(s,"spam") # Str.__init__ is actually str.__init__
+
+Put it more verbosely, what happens during the object creation is the
+following:
+
+1. the static method ``__new__`` is invoked with the class of the created
+ object as first argument [#]_;
+
+2. ``__new__`` returns an instance of that class.
+
+3. the instance is then initialized by the ``__init__`` method.
+
+Notice that both ``__new__`` and ``__init__`` are called with the same
+argument list, therefore one must make sure that they have a compatible
+signature.
+
+Let me discuss now why ``__new__`` must be a static method.
+First of all, it cannot be a normal method with a first argument which is an
+instance of the calling class, since at the time of ``__new__`` invocation
+that instance (``myclock`` in the example) has still to be created
+Since ``__new__`` needs information about the class calling it, one
+could think of implementing ``__new__`` as a class method. However,
+this would implicitly pass the caller class and return an instance
+of it. It is more convenient, to have the ability of creating
+instances of any class directly from C.__new__(B,*args,**kw)
+
+For this reasons, ``__new__`` must be a static method and pass explicitly
+the class which is calling it.
+
+Let me now show an important application of the ``__new__`` static method:
+forbidding object creation. For instance, sometimes it is useful to have
+classes that cannot be instantiated. This kind of classes can be
+obtained by inheriting from a ``NonInstantiable`` class:
+
+ ::
+
+ #<oopp.py>
+
+ class NonInstantiableError(Exception):
+ pass
+
+ class NonInstantiable(object):
+ def __new__(cls,*args,**kw):
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import NonInstantiable,get_time
+ >>> class Clock(NonInstantiable):
+ ... get_time=staticmethod(get_time)
+ >>> Clock.get_time() # works
+ '18:48:08'
+ Clock() #error
+ Traceback (most recent call last):
+ File "<pyshell#6>", line 1, in ?
+ Clock()
+ File "oopp.py", line 257, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+ NonInstantiableError: <class '__main__.Clock'> cannot be instantiated
+
+However, the approach pursued here has a disadvantage:``Clock`` was already
+defined as a subclass of ``object`` and I has to change the source code
+to make it a subclass of 'NonInstantiable'. But what happens if
+I cannot change the sources? How can I *reuse* the old code?
+
+The solution is provided by multiple inheritance.
+
+Notice that '__new__' is a staticmethod: [#]_
+
+ >>> type(NonInstantiable.__dict__['__new__'])
+ <type 'staticmethod'>
+
+ .. [#] This is how ``type(s)`` or ``s.__class__`` get to know that
+ ``s`` is an instance of ``Str``, since the class information is
+ explicitely passed to the newborn object trough ``__new__``.
+
+ .. [#] However ``object.__dict__['__new__']`` is not a staticmethod
+
+ >>> type(object.__dict__['__new__']) # special case
+ <type 'builtin_function_or_method'>
+
+
+Multiple Inheritance
+----------------------------------------------------------------------------
+
+Multiple Inheritance (often abbreviated as MI) is often
+considered one of the most advanced topic in Object Oriented Programming.
+It is also one of the most difficult features to implement
+in an Object Oriented Programming language. Even, some languages by design
+decided to avoid it. This is for instance the case of Java, that avoided
+MI having seen its implementation in C++ (which is not for the faint of
+heart ;-) and uses a poorest form of it trough interfaces.
+For what concerns the scripting languages, of which
+the most famous are Perl, Python and Ruby (in this order, even if
+the right order would be Python, Ruby and Perl), only Python
+implements Multiple Inheritance well (Ruby has a restricted form
+of it trough mix-ins, whereas Perl implementation is too difficult
+for me to understand what it does ;).
+
+The fact that Multiple Inheritance can be hairy, does not mean that it
+is *always* hairy, however. Multiple Inheritance is used with success
+in Lisp derived languages (including Dylan).
+
+The aims of this chapter is to discuss the
+Python support for MI in the most recent version (2.2 and 2.3), which
+has considerably improved with respect to previous versions.
+The message is the following: if Python 1.5 had a basic support for
+MI inheritance (basic but nevertheless with nice features, dynamic),
+Python 2.2 has *greatly* improved that support and with the
+change of the Method Resolution Order in Python 2.3, we may say
+that support for MI is now *excellent*.
+
+I strongly encourage Python programmers to use MI a lot: this will
+allows even a stronger reuse of code than in single inheritance.
+
+Often, inheritance is used when one has a complicate class B, and she wants
+to modify (or enhance) its behavior, by deriving a child class C, which is
+only slightly different from B. In this situation, B is already a standalone
+class, providing some non-trivial functionality, independently from
+the existence of C. This kind of design it typical of the so called
+*top-down* philosophy, where one builds the
+all structure as a monolithic block, leaving room only for minor improvements.
+An alternative approach is the so called *bottom-up* programming, in
+which one builds complicate things starting from very simple building blocks.
+In this logic, it is very appealing the idea of creating classes with the
+only purpose of being derived. The 'NonInstantiable' just defined is a
+perfect example of this kind of classes, though with multiple inheritance
+in mind and often called *mixin* classes.
+It can be used to create a new class ``NonInstantiableClock``
+that inherits from ``Clock`` and from ``NonInstantiable``.
+
+ ::
+
+ #<oopp.py>
+
+ class NonInstantiableClock(Clock,NonInstantiable):
+ pass
+
+ #</oopp.py>
+
+Now ``NonInstantiableClock`` is both a clock
+
+ >>> from oopp import NonInstantiableClock
+ >>> NonInstantiableClock.get_time() # works
+ '12:57:00'
+
+and a non-instantiable class:
+
+ >>> NonInstantiableClock() # as expected, give an error
+ Traceback (most recent call last):
+ File "<pyshell#2>", line 1, in ?
+ NonInstantiableClock() # error
+ File "oopp.py", line 245, in __new__
+ raise NonInstantiableError("%s cannot be instantiated" % cls)
+ NonInstantiableError: <class 'oopp.NonInstantiableClock'>
+ cannot be instantiated
+
+Let me give a simple example of a situation where the mixin approach
+comes handy. Suppose that the owner of a 'Pizza-shop' needs a program to
+take care of all the pizzas to-go he sell. Pizzas are distinguished
+according to their size (small, medium or large) and their toppings.
+The problem can be solved by inheriting from a generic pizza factory
+like this:
+
+ ::
+
+ #<oopp.py>
+
+ class GenericPizza(object): # to be customized
+ toppinglist=[] # nothing, default
+ baseprice=1 # one dollar, default
+ topping_unit_price=0.5 # half dollar for each topping, default
+ sizefactor={'small':1, 'medium':2, 'large':3}
+ # a medium size pizza costs twice a small pizza,
+ # a large pizza costs three times
+ def __init__(self,size):
+ self.size=size
+ def price(self):
+ return (self.baseprice+
+ self.toppings_price())*self.sizefactor[self.size]
+ def toppings_price(self):
+ return len(self.toppinglist)*self.topping_unit_price
+ def __str__(self):
+ return '%s pizza with %s, cost $ %s' % (self.size,
+ ','.join(self.toppinglist),
+ self.price())
+
+ #</oopp.py>
+
+Here the base class 'GenericPizza' is written with inheritance in mind: one
+can derives many pizza classes from it by overriding the ``toppinglist``;
+for instance one could define
+
+ >>> from oopp import GenericPizza
+ >>> class Margherita(GenericPizza):
+ ... toppinglist=['tomato']
+
+The problem of this approach is that one must define dozens of
+different pizza subclasses (Marinara, Margherita, Capricciosa, QuattroStagioni,
+Prosciutto, ProsciuttoFunghi, PizzaDellaCasa, etc. etc. [#]_). In such a
+situation, it is better to perform the generation of subclasses in a smarter
+way, i.e. via a customizable class factory.
+A simpler approach is to use always the same class and to customize
+its instances just after creation. Both approaches can be implemented via
+the following 'Customizable' mixin class, not meant to be instantiated,
+but rather to be *inherited*:
+
+ ::
+
+ #<oopp.py>
+
+ class Customizable(object):
+ """Classes inhering from 'Customizable' have a 'with' method acting as
+ an object modifier and 'With' classmethod acting as a class factory"""
+ def with(self,**kw):
+ customize(self,**kw)# customize the instance
+ return self # returns the customized instance
+ def With(cls,**kw):
+ class ChildOf(cls): pass # a new class inheriting from cls
+ ChildOf.__name__=cls.__name__ # by default, with the same name
+ customize(ChildOf,**kw) # of the original class
+ return ChildOf
+ With=classmethod(With)
+
+ #</oopp.py>
+
+Descendants of 'Customizable' can be customized by using
+'with', that directly acts on the instances, or 'With', that returns
+new classes. Notice that one could make 'With' to customize the
+original class, without returning a new one; however, in practice,
+this would not be safe: I remind that changing a class modifies
+automatically all its instances, even instances created *before*
+the modification. This could produce bad surprises: it is better to
+returns new classes, that may have the same name of the original one,
+but are actually completely independent from it.
+
+In order to solve the pizza shop problem we may define a 'CustomizablePizza'
+class
+
+ ::
+
+ #<oopp.py>
+
+ class CustomizablePizza(GenericPizza,Customizable):
+ pass
+
+ #</oopp.py>
+
+which can be used in two ways: i) to customize instances just after creation:
+
+ >>> from oopp import CustomizablePizza
+ >>> largepizza=CustomizablePizza('large') # CustomizablePizza instance
+ >>> largemarinara=largepizza.with(toppinglist=['tomato'],baseprice=2)
+ >>> print largemarinara
+ large pizza with tomato mozzarella, cost $ 7.0
+
+and ii) to generated customized new classes:
+
+ >>> Margherita=CustomizablePizza.With(
+ ... toppinglist=['tomato','mozzarella'], __name__='Margherita')
+ >>> print Margherita('medium')
+ medium pizza with tomato,mozzarella, cost $ 4.0
+
+The advantage of the bottom-up approach, is that the 'Customizable' class
+can be reused in completely different problems; for instance, it could
+be used as a class factory. For instance we could use it to generate a
+'CustomizableClock' class as in this example:
+
+ >>> from oopp import *
+ >>> CustomizableClock=Customizable.With(get_time=staticmethod(Clock.get_time),
+ ... __name__='CustomizableClock') #adds get_time
+ >>> CustomizableClock.get_time() # now it works
+ '09:57:50'
+
+Here 'Customizable' "steal" the 'get_time' method from 'Clock'.
+However that would be a rather perverse usage ;) I wrote it to show
+the advantage of classmethods, more than to suggest to the reader that
+this is an example of good programming.
+
+ .. [#] In Italy, you can easily find "pizzerie" with more than 50 different
+ kinds of pizzas (once I saw a menu with something like one hundred
+ different combinations ;)
+
+Cooperative hierarchies
+-----------------------------------------------------------------------
+
+The examples of multiple inheritance hierarchies given until now were pretty
+easy. The reason is that there was no interaction between the methods of the
+children and of the parents. However, things get more complicated (and
+interesting ;) when the methods in the hierarchy call each other.
+Let me consider an example coming from paleoantropology:
+
+ ::
+
+ #<paleoanthropology1.py>
+
+ class HomoHabilis(object):
+ def can(self):
+ print self,'can:'
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self): #overrides HomoHabilis.can
+ HomoHabilis.can(self)
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self): #overrides HomoSapiens.can
+ HomoSapiens.can(self)
+ print " - make art"
+
+ modernman=HomoSapiensSapiens()
+ modernman.can()
+
+ #</paleoanthropology1.py>
+
+In this example children methods call parent methods:
+'HomoSapiensSapiens.can' calls 'HomoSapiens.can' that in turns calls
+'HomoHabilis.can' and the final output is:
+
+ ::
+
+ <__main__.HomoSapiensSapiens object at 0x814e1fc> can:
+ - make tools
+ - make abstractions
+ - make art
+
+The script works, but it is far from ideal, if code reuse and refactoring
+are considered important requirements. The point is that (very likely, as the
+research in paleoanthropology progresses) we may want to extend the
+hierarchy, for instance by adding a class on the top or in the middle.
+In the present form, this would require a non-trivial modification of
+the source code (especially
+if one think that the hierarchy could be fleshed out with dozens of others
+methods and attributes). However, the aim of OOP is to avoid as
+much as possible source code modifications. This goal can be attained in
+practice, if the source code is written to be friendly to extensions and
+improvements as much as possible. I think it is worth to spend some time
+in improving this example, since what can be learn here,
+can be lifted to real life cases.
+
+First of all, let me define a generic *Homo* class, to be used
+as first ring of the inheritance chain (actually the first ring is
+'object'):
+
+ ::
+
+ #<oopp.py>
+
+ class Homo(PrettyPrinted):
+ """Defines the method 'can', which is intended to be overriden
+ in the children classes, and inherits '__str__' from PrettyPrinted,
+ ensuring a nice printing representation for all children."""
+ def can(self):
+ print self,'can:'
+
+ #</oopp.py>
+
+Now, let me point out one of the shortcomings of the previous code: in each
+subclass, we explicitly call its parent class (also called super class)
+by its name. This is inconvenient, both because a change of name in
+later stages of the project would require a lot of search and replace
+(actually not a lot in this toy example, but you can imagine having
+a very big projects with dozens of named method calls) and because it makes
+difficult to insert a new element in the inheritance hierarchy.
+The solution to this problems is the
+``super`` built-in, which provides an easy access to the methods
+of the superclass.
+``super`` objects comes in two flavors: ``super(cls,obj)`` objects return
+bound methods whereas ``super(cls)`` objects return unbound methods.
+In the next code we will use the first form. The hierarchy can more elegantly
+be rewritten as [#]_ :
+
+ ::
+
+ #<paleo2.py>
+
+ from oopp import Homo
+
+ class HomoHabilis(Homo):
+ def can(self):
+ super(HomoHabilis,self).can()
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(HomoSapiens,self).can()
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(HomoSapiensSapiens,self).can()
+ print " - make art"
+
+
+ HomoSapiensSapiens().can()
+
+ #</paleo2.py>
+
+with output
+
+ ::
+
+ <HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art
+
+This is not yet the most elegant form, since even
+if ``super`` avoids naming the base class explicitely, still it
+requires to explicitely name the class where it is defined. This is
+rather annoying.
+Removing that restriction, i.e. implementing really anonymous
+``super`` calls, is possible but requires a good understand of
+private variables in inheritance.
+
+Inheritance and privacy
+----------------------------------------------------------------------
+
+In order to define anonymous cooperative super calls, we need classes
+that know themselves, i.e. containing a reference to themselves. This
+is not an obvious problem as it could seems, since it cannot be solved
+without incurring in the biggest annoyance in inheritance:
+*name clashing*. Name clashing happens when names and attributes defined
+in different ancestors overrides each other in a unwanted order.
+Name clashing is especially painful in the case of cooperative
+hierarchies and particularly in in the problem at hand.
+
+
+A naive solution would be to attach a plain (i.e. non-private)
+attribute '.this' to the class, containing a reference
+to itself, that can be invoked by the methods of the class.
+Suppose, for instance, that I want to use that attribute in the ``__init__``
+method of that class. A naive attempt would be to write something like:
+
+ >>> class B(object):
+ ... def __init__(self):
+ ... print self.this,'.__init__' # .this defined later
+ >>> B.this=B # B.this can be set only after B has been created
+ >>> B()
+ <class '__main__.B'>
+
+Unfortunately, this approach does not work with cooperative hierarchies.
+Consider, for instance, extending 'B' with a cooperative children
+class 'C' as follows:
+
+ >>> class C(B):
+ ... def __init__(self):
+ ... super(self.this,self).__init__() # cooperative call
+ ... print type(self).this,'.__init__'
+ >>> C.this=C
+
+``C.__init__`` calls ``B.__init__`` by passing a 'C' instance, therefore
+``C.this`` is printed and not ``B.this``:
+
+ >>> C()
+ <class '__main__.C'> .__init__
+ <class '__main__.C'> .__init__
+ <__main__.C object at 0x4042ca6c>
+
+The problem is that the ``C.this`` overrides ``B.this``. The only
+way of avoiding the name clashing is to use a private attribute
+``.__this``, as in the following script:
+
+ ::
+
+ #<privateh.py>
+
+ class B(object):
+ def __init__(self):
+ print self.__this,'.__init__'
+ B._B__this=B
+
+ class C(B):
+ def __init__(self):
+ super(self.__this,self).__init__() # cooperative __init__
+ print self.__this,'.__init__'
+ C._C__this=C
+
+ C()
+
+ # output:
+ # <class '__main__.B'> .__init__
+ # <class '__main__.C'> .__init__
+
+ #</privateh.py>
+
+The script works since, due to the magic of the mangling mechanism,
+in ``B.__init__``, ``self._B__this`` i.e. ``B`` is retrieved, whereas in
+``C.__init__`` ``self._C__this`` i.e. ``C`` is retrieved.
+
+The elegance of the mechanism can be improved with an helper function
+that makes its arguments reflective classes, i.e. classes with a
+``__this`` private attribute:
+
+ ::
+
+ #<oopp.py>
+
+ def reflective(*classes):
+ """Reflective classes know themselves, i.e. they own a private
+ attribute __this containing a reference to themselves. If the class
+ name starts with '_', the underscores are stripped. This is needed
+ to make the mangling mechanism work."""
+ for c in classes:
+ name=c.__name__ .lstrip('_') # in 2.3
+ setattr(c,'_%s__this' % name,c)
+
+ #</oopp.py>
+
+It is trivial to rewrite the paleonthropological hierarchy in terms of
+anonymous cooperative super calls by using this trick.
+
+ ::
+
+ #<oopp.py>
+
+ class HomoHabilis(Homo):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ super(self.__this,self).can()
+ print " - make art"
+
+ reflective(HomoHabilis,HomoSapiens,HomoSapiensSapiens)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import *
+ >>> man=HomoSapiensSapiens(); man.can()
+ <HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art
+
+We may understand why it works by looking at the attributes of man:
+
+ >>> print pretty(attributes(man))
+ _HomoHabilis__this = <class 'oopp.HomoHabilis'>
+ _HomoSapiensSapiens__this = <class 'oopp.HomoSapiensSapiens'>
+ _HomoSapiens__this = <class 'oopp.HomoSapiens'>
+ can = <bound method HomoSapiensSapiens.can of
+ <oopp.HomoSapiensSapiens object at 0x404292ec>>
+ formatstring = %s
+
+It is also interesting to notice that the hierarchy can be entirely
+rewritten without using cooperative methods, but using private attributes,
+instead. This second approach is simpler, as the following script shows:
+
+ ::
+
+ #<privatehierarchy.py>
+
+ from oopp import PrettyPrinted,attributes,pretty
+
+ class Homo(PrettyPrinted):
+ def can(self):
+ print self,'can:'
+ for attr,value in attributes(self).iteritems():
+ if attr.endswith('__attr'): print value
+ class HomoHabilis(Homo):
+ __attr=" - make tools"
+ class HomoSapiens(HomoHabilis):
+ __attr=" - make abstractions"
+ class HomoSapiensSapiens(HomoSapiens):
+ __attr=" - make art"
+
+ modernman=HomoSapiensSapiens()
+ modernman.can()
+ print '----------------------------------\nAttributes of',modernman
+ print pretty(attributes(modernman))
+
+ #</privatehierarchy.py>
+
+Here I have replaced the complicate chain of cooperative methods with
+much simpler private attributes. Only the 'can' method in the 'Homo'
+class survives, and it is modified to print the value of the '__attr'
+attributes. Moreover, all the classes of the hierarchy have been made
+'Customizable', in view of future extensions.
+
+The second script is much shorter and much more elegant than the original
+one, however its logic can be a little baffling, at first. The solution
+to the mistery is provided by the attribute dictionary of 'moderman',
+given by the second part of the output:
+
+ ::
+
+ <HomoSapiensSapiens> can:
+ - make abstractions
+ - make art
+ - make tools
+ ------------------------------------------
+ Attributes of <HomoSapiensSapiens>:
+ _HomoHabilis__attr = - make tools
+ _HomoSapiensSapiens__attr = - make art
+ _HomoSapiens__attr = - make abstractions
+ can = <bound method HomoSapiensSapiens.can of
+ <__main__.HomoSapiensSapiens object at 0x402d892c>>
+ formatstring = %s
+
+We see that, in addition to the 'can' method inherited from 'Homo',
+the 'with' and 'With' method inherited from 'Customizable' and
+the 'formatstring' inherited from 'PrettyPrinted',
+``moderman`` has the attributes
+
+ ::
+
+ _HomoHabilis__attr:' - make tools' # inherited from HomoHabilis
+ _HomoSapiens__attr:' - make abstractions'# inherited from HomoSapiens
+ _HomoSapiensSapiens__attr: ' - make art' # inherited from HomoSapiensSapiens
+
+which origin is obvious, once one reminds the mangling mechanism associated
+with private variables. The important point is that the trick would *not*
+have worked for normal attributes. Had I used as variable name
+'attr' instead of '__attr', the name would have been overridden: the only
+attribute of 'HomoSapiensSapiens' would have been ' - make art'.
+
+This example explains the advantages of private variables during inheritance:
+they cannot be overridden. Using private name guarantees the absence of
+surprises due to inheritance. If a class B has only private variables,
+deriving a class C from B cannot cause name clashes.
+
+Private variables have a drawbacks, too. The most obvious disadvantages is
+the fact that in order to customize private variables outside their
+defining class, one needs to pass explicitly the name of the class.
+
+For instance we could not change an attribute with the syntax
+``HomoHabilis.With(__attr=' - work the stone')``, we must write the
+more verbose, error prone and redundant
+``HomoHabilis.With(_HomoHabilis__attr=' - work the stone')``
+
+A subtler drawback will be discussed in chapter 6.
+
+ .. [#] In single inheritance hierarchies, ``super`` can be dismissed
+ in favor of ``__base__``: for instance,
+ ``super(HomoSapiens,self).can()`` is equivalent to
+ ``HomoSapiens.__base__.can(self)``. Nevertheless, in view
+ of possible extensions to multiple inheritance, using ``super`` is a
+ much preferable choice.
diff --git a/pypers/classinitializer/Makefile b/pypers/classinitializer/Makefile
new file mode 100644
index 0000000..8eb2478
--- /dev/null
+++ b/pypers/classinitializer/Makefile
@@ -0,0 +1,11 @@
+RST = $(MS)/tools/rst.py
+
+all: classinitializer.txt
+ python doctester.py classinitializer.txt
+ $(RST) classinitializer
+ $(RST) -Pt classinitializer
+ zip classinitializer classinitializer.txt classinitializer.html \
+ classinitializer.pdf doctester.py _main.py
+upload:
+ scp classinitializer.html classinitializer.zip \
+ micheles@alpha.phyast.pitt.edu:public_html/python
diff --git a/pypers/classinitializer/_main.py b/pypers/classinitializer/_main.py
new file mode 100644
index 0000000..521938b
--- /dev/null
+++ b/pypers/classinitializer/_main.py
@@ -0,0 +1,64 @@
+# _main.py
+
+import sys
+
+def classinitializer(proc):
+ # basic idea stolen from zope.interface.advice, which credits P.J. Eby
+ def newproc(*args, **kw):
+ frame = sys._getframe(1)
+ if '__module__' in frame.f_locals and not \
+ '__module__' in frame.f_code.co_varnames: # we are in a class
+ if '__metaclass__' in frame.f_locals:
+ raise SyntaxError("Don't use two class initializers or\n"
+ "a class initializer together with a__metaclass__ hook")
+ def makecls(name, bases, dic):
+ try:
+ cls = type(name, bases, dic)
+ except TypeError, e:
+ if "can't have only classic bases" in str(e):
+ cls = type(name, bases + (object,), dic)
+ else: # other strange errors, such as __slots__ conflicts, etc
+ raise
+ proc(cls, *args, **kw)
+ return cls
+ frame.f_locals["__metaclass__"] = makecls
+ else:
+ proc(*args, **kw)
+ newproc.__name__ = proc.__name__
+ newproc.__module__ = proc.__module__
+ newproc.__doc__ = proc.__doc__
+ newproc.__dict__ = proc.__dict__
+ return newproc
+
+
+
+import datetime
+
+@classinitializer
+def def_properties(cls, schema):
+ """
+ Add properties to cls, according to the schema, which is a list
+ of pairs (fieldname, typecast). A typecast is a
+ callable converting the field value into a Python type.
+ The initializer saves the attribute names in a list cls.fields
+ and the typecasts in a list cls.types. Instances of cls are expected
+ to have private attributes with names determined by the field names.
+ """
+ cls.fields = []
+ cls.types = []
+ for name, typecast in schema:
+ if hasattr(cls, name): # avoid accidental overriding
+ raise AttributeError('You are overriding %s!' % name)
+ def getter(self, name=name):
+ return getattr(self, '_' + name)
+ def setter(self, value, name=name, typecast=typecast):
+ setattr(self, '_' + name, typecast(value))
+ setattr(cls, name, property(getter, setter))
+ cls.fields.append(name)
+ cls.types.append(typecast)
+
+def date(isodate): # add error checking if you like
+ "Convert an ISO date into a datetime.date object"
+ return datetime.date(*map(int, isodate.split('-')))
+
+
diff --git a/pypers/classinitializer/classinitializer.html b/pypers/classinitializer/classinitializer.html
new file mode 100644
index 0000000..111d480
--- /dev/null
+++ b/pypers/classinitializer/classinitializer.html
@@ -0,0 +1,530 @@
+<?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.5: http://docutils.sourceforge.net/" />
+<title>Let's keep it simple (or, how to do metaprogramming without metaclasses)</title>
+<meta name="author" content="Michele Simionato" />
+<meta name="date" content="10 July 2006" />
+
+</head>
+<body>
+<div class="document" id="let-s-keep-it-simple-or-how-to-do-metaprogramming-without-metaclasses">
+<h1 class="title">Let's keep it simple (or, how to do metaprogramming without metaclasses)</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>10 July 2006</td></tr>
+<tr><th class="docinfo-name">Version:</th>
+<td>0.5</td></tr>
+</tbody>
+</table>
+<div class="contents topic">
+<p class="topic-title first"><a id="contents" name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#introduction" id="id7" name="id7">Introduction</a></li>
+<li><a class="reference" href="#about-class-initialization" id="id8" name="id8">About class initialization</a></li>
+<li><a class="reference" href="#please-stop-abusing-metaclasses" id="id9" name="id9">Please stop abusing metaclasses</a></li>
+<li><a class="reference" href="#the-classinitializer-decorator" id="id10" name="id10">The <tt class="docutils literal"><span class="pre">classinitializer</span></tt> decorator</a></li>
+<li><a class="reference" href="#tricky-points-and-caveats" id="id11" name="id11">Tricky points and caveats</a></li>
+<li><a class="reference" href="#example-initializing-records" id="id12" name="id12">Example: initializing records</a></li>
+<li><a class="reference" href="#references" id="id13" name="id13">References</a></li>
+<li><a class="reference" href="#questions-and-answers" id="id14" name="id14">Questions and answers</a></li>
+</ul>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id7" id="introduction" name="introduction">Introduction</a></h1>
+<p>A few days ago I was at CERN, at the EuroPython 2006 conference. The
+conference was good, the organization perfect, the talks of
+very high level, the people extremely nice.</p>
+<p>Nevertheless, I saw a trend growing in the Python community
+that disturbed me a little and motivated
+me to write this paper. The trend I am alluding to, is the trend
+towards <em>cleverness</em>. Unfortunately, whereas once cleverness was
+mostly confined to Zope and Twisted, now is appearing everywhere.</p>
+<p>I personally don't have anything against cleverness for
+experimental projects and learning exercises. But I have a gripe
+against cleverness when I see it deployed in production frameworks
+that I am forced to cope with as an user.</p>
+<p>Cleverness means making things more complicated than
+needed, making things more fragile, making the learning curve
+steeper and, worse of all, making debugging harder.</p>
+<p>In this short paper I will try to give my small contribution against
+cleverness, at least in a case where I have some expertise, i.e.
+against metaclass abuses.</p>
+<p>Let me say that I consider <em>metaclass abuse</em> any usage of a metaclass
+in a situation where you could have solved the problem without a
+metaclass.</p>
+<p>I feel in part responsible for some of the abuses I see floating
+around in newsgroups, in conferences and in frameworks source code,
+because of my (together with David Mertz) contribution in popularizing
+metaclasses, so I have decided to make amend with this paper.</p>
+<p>This paper consider only one kind of metaprogramming technique, i.e.
+the creation at runtime of classes with attributes and methods which
+are dynamically generated. Contrarily to popular belief, this is a job
+where most of the time you <em>don't need</em> and you <em>don't want</em> a custom
+metaclass, as I will argue in the next section.</p>
+<p>The paper is intended for a double target of readers:</p>
+<ul class="simple">
+<li>average programmers, that would benefit from knowing a few
+meta-programming tricks, but are scared off by brain melting concepts;</li>
+<li>clever programmers, which are actually too clever and should know
+better <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a>.</li>
+</ul>
+<p>If you are a lazy reader, your may just read the next paragraph and
+the last one, and forget about the details.</p>
+<table class="docutils footnote" frame="void" id="id2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>The problem is that it is easy to be clever whereas it takes a
+lot of time to become unclever. For instance, it took me a few
+months to understand how to use metaclasses, but a few years
+to understand how <em>not</em> to use them.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id8" id="about-class-initialization" name="about-class-initialization">About class initialization</a></h1>
+<p>By class initialization I mean setting attributes and methods of
+classes immediately after their creation, once and for all <a class="footnote-reference" href="#id4" id="id3" name="id3">[2]</a>.</p>
+<p>There are various common situations where a programmer may want to
+initialize her classes: for instance, she may want to set some default class
+attributes according to parameters read from a configuration
+file, or she may want to set class properties according to the fields
+in a database table.</p>
+<p>The easiest way to perform class initialization
+is by using an imperative style: one first creates the class,
+and then adds the dynamically generated methods and attributes.</p>
+<p>For instance, if the problem is to generate properties for an <tt class="docutils literal"><span class="pre">Article</span></tt>
+record class, an imperative solution is something like the following:</p>
+<pre class="literal-block">
+class Article(object):
+ def somemethod(self):
+ pass
+ ...
+
+set_properties(Article, [('title', str), ('author', str), ('date', date)])
+</pre>
+<p>However, since (proper) class initialization should occur only once,
+it does not need to be distinguished by class
+creation, and it may be argued that it
+should be treated with a declarative style, with a syntax like the following:</p>
+<pre class="literal-block">
+class Article(object):
+
+ def_properties([('title', str), ('author', str), ('date', date)])
+
+ def somemethod(self):
+ pass
+
+ ...
+</pre>
+<p>This paper is about providing a generic facility to define <em>class initializers</em>
+to be used in the class scope, such as <tt class="docutils literal"><span class="pre">def_properties</span></tt>.</p>
+<p><strong>Disclaimer:</strong> the advantage of the solution I propose here,
+is that it works in current Python and it is less clever than
+metaclasses, Still I would consider it a little too clever. A clean solution
+to the problem would be to add class decorator to the language. That
+would allow a syntax like the following:</p>
+<pre class="literal-block">
+&#64;with_properties([('title', str), ('author', str), ('date', date)])
+class Article(object):
+ def somemethod(self):
+ pass
+ ...
+</pre>
+<p>However, it is not sure at the moment if and when Guido will add class
+decorators, so my proposed solution is the lesser of two evils.</p>
+<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="#id3" name="id4">[2]</a></td><td>Well, in Python methods and attributes can always be changed at
+a later time, but let us assume that nobody is cheating here.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id9" id="please-stop-abusing-metaclasses" name="please-stop-abusing-metaclasses">Please stop abusing metaclasses</a></h1>
+<p>Everybody knows how to initialize instances. One just overrides
+the class <tt class="docutils literal"><span class="pre">__init__</span></tt> method. Since classes are instances of metaclasses,
+the natural solution to the class initialization problem seems to be
+to use a custom metaclass and to override its <tt class="docutils literal"><span class="pre">__init__</span></tt> method.</p>
+<p>Un fortunately, things are not so easy, because of inheritance. The
+issue is that once you have defined a custom metaclass for your base
+class, all the derived classes will inherit the metaclass, so the
+initialization code will be run on all derived classes, magically and
+implicitly.</p>
+<p>That may be fine in specific circumstances (for instance,
+suppose you have to register in your framework all the classes you
+define: using a metaclass ensures that you cannot forget to register a
+derived class), however, in many cases you may not like this behavior because:</p>
+<ol class="arabic simple">
+<li>you may believe that <em>explicit is better than implicit</em>;</li>
+<li>if the derived classes have the same dynamic class attributes of
+the base class, implicitly setting them again for each derived
+class is a waste, since they would be available anyway by
+inheritance. This may be a real issue if the initialization code is
+slow, possibly because it must access a database, or perform some
+heavy computation: in this case one must add a check in the
+metaclass code to see if the attributes were already set in
+a parent class, but this adds plumbing and it does not give real
+control on a per-class basis;</li>
+<li>a custom metaclass will make your classes somewhat magic and
+nonstandard: you may not want to increase your chances to incur in
+metaclass conflicts, issues with <tt class="docutils literal"><span class="pre">__slots__</span></tt>, fights with (Zope)
+extension classes and other guru-level intricacies <a class="footnote-reference" href="#id6" id="id5" name="id5">[3]</a>;</li>
+<li>you feel that a custom metaclasses is overkill for the simple job
+of class initialization and you would rather use a simpler solution.</li>
+</ol>
+<p>In other words,you should use a custom metaclass only when your real
+intention is to have code running on derived classes without users of
+those classes noticing it. If this is not your case, please don't a
+metaclass and make your life (as well your users) happier.</p>
+<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="#id5" name="id6">[3]</a></td><td>Metaclasses are more fragile than many people realize. I
+personally have never used them for production code, even
+after four years of usage in experimental code.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id10" id="the-classinitializer-decorator" name="the-classinitializer-decorator">The <tt class="docutils literal"><span class="pre">classinitializer</span></tt> decorator</a></h1>
+<p>The aim of this paper is to provide a decorator called
+<tt class="docutils literal"><span class="pre">classinitializer</span></tt> that can be used to define
+class initializers, i.e. procedures taking classes and setting their
+attributes and methods. In order to be concrete, consider the following
+class initializer example:</p>
+<pre class="literal-block">
+def set_defaults(cls, **kw):
+ for k, v in kw.iteritems():
+ setattr(cls, k, v)
+</pre>
+<p>You may use it imperatively, right after a class definition:</p>
+<pre class="literal-block">
+class ClassToBeInitialized(object):
+ pass
+
+set_defaults(ClassToBeInitialized, a=1, b=2)
+</pre>
+<p>However the imperative solution has a few drawbacks:</p>
+<ol class="arabic simple">
+<li>it does not comply with DRY, i.e. the class name is repeated
+and if I change it during refactoring, I have to change it (at
+least) twice;</li>
+<li>readability is suboptimal: since class definition and class
+initialization are separated, for long class definitions I may end
+up not seeing the last line;</li>
+<li>it feels wrong to first define something and immediately right
+after to mutate it.</li>
+</ol>
+<p>Luckily, the <tt class="docutils literal"><span class="pre">classinitializer</span></tt> decorator provides a much nicer
+declarative solution. The decorator performs some deep magic and converts
+<tt class="docutils literal"><span class="pre">set_defaults(cls,</span> <span class="pre">**kw)</span></tt> into a function that can be
+used at the top scope into a class definition, with the current class
+automagically passed as first parameter:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; &#64;classinitializer # add magic to set_defaults
+... def set_defaults(cls, **kw):
+... for k, v in kw.iteritems():
+... setattr(cls, k, v)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ClassToBeInitialized(object):
+... set_defaults(a=1, b=2)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; ClassToBeInitialized.a
+1
+&gt;&gt;&gt; ClassToBeInitialized.b
+2
+</pre>
+<p>If you have used Zope interfaces, you may have seen examples of class
+initializers (I mean <tt class="docutils literal"><span class="pre">zope.interface.implements</span></tt>). In fact under the hood
+<tt class="docutils literal"><span class="pre">classinitializer</span></tt> is implemented by using a trick copied from
+<tt class="docutils literal"><span class="pre">zope.interface.advice</span></tt>, which credits Phillip J. Eby. The trick
+uses the <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook, but it <em>does not use</em> a custom
+metaclass, so that in this example <tt class="docutils literal"><span class="pre">ClassToBeInitialized</span></tt> will
+continue to keep its original metaclass, i.e. the plain old regular
+built-in metaclass <tt class="docutils literal"><span class="pre">type</span></tt> of new style classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(ClassToBeInitialized)
+&lt;type 'type'&gt;
+</pre>
+<p>In principle, the trick also works for old style classes,
+and it would be easy to write an implementation keeping old style
+classes old style. However, since according to Guido himself
+<em>old style classes are morally deprecated</em>, the current implementation
+automagically converts old style classes into new style classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class WasOldStyle:
+... set_defaults(a=1, b=2)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; WasOldStyle.a, WasOldStyle.b
+(1, 2)
+&gt;&gt;&gt; type(WasOldStyle)
+&lt;type 'type'&gt;
+</pre>
+<p>One of the motivations for the <tt class="docutils literal"><span class="pre">classinitializer</span></tt> decorator, is to hide the
+plumbing, and to make mere mortals able to implements their own
+class initializers in an easy way, without knowing the details of
+how class creation works and the secrets of the <tt class="docutils literal"><span class="pre">__metaclass__</span></tt>
+hook. The other motivation, is that even for Python wizards it is very
+unconvenient to rewrite the code managing the <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook
+every time one writes a new class initializer. So I have decided to
+use a decorator to allow separation of concerns and reuse of code.</p>
+<p>As a final note, let me point out that the decorated version of
+<tt class="docutils literal"><span class="pre">set_defaults</span></tt> is so smart that it will continue to
+work as the non-decorated version outside a class scope, provided that
+you pass to it an explicit class argument.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; set_defaults(WasOldStyle, a=2)
+&gt;&gt;&gt; WasOldStyle.a
+2
+</pre>
+<p>In other words, you <em>might</em> continue to use the imperative style.</p>
+<p>Here is the code for <tt class="docutils literal"><span class="pre">classinitializer</span></tt> (the point being that you
+don't need to be able to understand it to use the decorator):</p>
+<pre class="literal-block">
+#&lt;_main.py&gt;
+
+import sys
+
+def classinitializer(proc):
+ # basic idea stolen from zope.interface.advice, which credits P.J. Eby
+ def newproc(*args, **kw):
+ frame = sys._getframe(1)
+ if '__module__' in frame.f_locals and not \
+ '__module__' in frame.f_code.co_varnames: # we are in a class
+ if '__metaclass__' in frame.f_locals:
+ raise SyntaxError(&quot;Don't use two class initializers or\n&quot;
+ &quot;a class initializer together with a__metaclass__ hook&quot;)
+ def makecls(name, bases, dic):
+ try:
+ cls = type(name, bases, dic)
+ except TypeError, e:
+ if &quot;can't have only classic bases&quot; in str(e):
+ cls = type(name, bases + (object,), dic)
+ else: # other strange errors, such as __slots__ conflicts, etc
+ raise
+ proc(cls, *args, **kw)
+ return cls
+ frame.f_locals[&quot;__metaclass__&quot;] = makecls
+ else:
+ proc(*args, **kw)
+ newproc.__name__ = proc.__name__
+ newproc.__module__ = proc.__module__
+ newproc.__doc__ = proc.__doc__
+ newproc.__dict__ = proc.__dict__
+ return newproc
+
+#&lt;/_main.py&gt;
+</pre>
+<p>From the implementation it is clear how class initializers work:
+when you call a class initializer inside a class, your are actually defining a
+<tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook that will be called by
+the class' metaclass (typically <tt class="docutils literal"><span class="pre">type</span></tt>). The
+metaclass will create the class (as a new style one) and
+will pass it to the class initializer procedure.</p>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id11" id="tricky-points-and-caveats" name="tricky-points-and-caveats">Tricky points and caveats</a></h1>
+<p>Since class initializers (re)define the <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook,
+they don't play well with classes that define a <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook
+explicitly (as opposed to implicitly inheriting one). The issue is
+that if a <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook is defined <em>after</em> the
+class initializer, it <em>silently</em> overrides it.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... set_defaults(a=1)
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'set_defaults is silently ignored'
+... return cls
+...
+set_defaults is silently ignored
+&gt;&gt;&gt; C.a
+Traceback (most recent call last):
+ ...
+AttributeError: type object 'C' has no attribute 'a'
+</pre>
+<p>This is unfortunate, but there is no general solution to this issue, and I will
+simply document it (this is one of the reasons why I feel
+class initializers to be a clever hack that should be dismissed if we
+had class decorators).</p>
+<p>On the other hand, if you call a class initializer
+<em>after</em> the <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook, you will get an exception:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'calling explicit __metaclass__'
+... return cls
+... set_defaults(a=1)
+...
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook
+</pre>
+<p>I feel raising an error is preferable to silently overriding your
+explicit <tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hook. As a consequence, you will get an
+error if you try to use two class initializers at the same time, or
+if you call twice the same one:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... set_defaults(a=1)
+... set_defaults(b=2)
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook
+</pre>
+<p>I feel raising an error to be better than having the second
+initializer overriding the first one, i.e. in this example
+to set the <tt class="docutils literal"><span class="pre">b</span></tt> attribute <em>without</em> setting the <tt class="docutils literal"><span class="pre">a</span></tt> attribute,
+which would be very confusing.</p>
+<p>Finally, let me show that there are no issues for inherited
+<tt class="docutils literal"><span class="pre">__metaclass__</span></tt> hooks and for custom metaclasses:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B: # a base class with a custom metaclass
+... class __metaclass__(type):
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(B): # a class with both a custom metaclass AND a class initializer
+... set_defaults(a=1)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.a
+1
+&gt;&gt;&gt; type(C)
+&lt;class '_main.__metaclass__'&gt;
+</pre>
+<p>The class initializer does not disturb the metaclass of <tt class="docutils literal"><span class="pre">C</span></tt>, which is
+the one inherited by its base <tt class="docutils literal"><span class="pre">B</span></tt>, and the inherited metaclass does
+not disturb the class initializer, which does its job just fine.
+You would have run into trouble, instead, if you tried to call <tt class="docutils literal"><span class="pre">set_defaults</span></tt>
+directly in the base class.</p>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id12" id="example-initializing-records" name="example-initializing-records">Example: initializing records</a></h1>
+<p>In this section I will finally discuss the example I gave at the
+beginning, about how to define record classes with a class initializer.
+Here is the code for the class initializer, plus an helper function
+for managing dates:</p>
+<pre class="literal-block">
+#&lt;_main.py&gt;
+
+import datetime
+
+&#64;classinitializer
+def def_properties(cls, schema):
+ &quot;&quot;&quot;
+ Add properties to cls, according to the schema, which is a list
+ of pairs (fieldname, typecast). A typecast is a
+ callable converting the field value into a Python type.
+ The initializer saves the attribute names in a list cls.fields
+ and the typecasts in a list cls.types. Instances of cls are expected
+ to have private attributes with names determined by the field names.
+ &quot;&quot;&quot;
+ cls.fields = []
+ cls.types = []
+ for name, typecast in schema:
+ if hasattr(cls, name): # avoid accidental overriding
+ raise AttributeError('You are overriding %s!' % name)
+ def getter(self, name=name):
+ return getattr(self, '_' + name)
+ def setter(self, value, name=name, typecast=typecast):
+ setattr(self, '_' + name, typecast(value))
+ setattr(cls, name, property(getter, setter))
+ cls.fields.append(name)
+ cls.types.append(typecast)
+
+def date(isodate): # add error checking if you like
+ &quot;Convert an ISO date into a datetime.date object&quot;
+ return datetime.date(*map(int, isodate.split('-')))
+
+#&lt;/_main.py&gt;
+</pre>
+<p>As an example of application of the above class initializer,
+I will define an <em>Article</em> record class with fields <em>title</em>, <em>author</em>
+and <em>date</em>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Article(object):
+... # fields and types are dynamically set by the initializer
+... def_properties([('title', str), ('author', str), ('date', date)])
+... def __init__(self, values): # add error checking if you like
+... for field, cast, value in zip(self.fields, self.types, values):
+... setattr(self, '_' + field, cast(value))
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; a=Article(['How to use class initializers', 'M. Simionato', '2006-07-10'])
+&gt;&gt;&gt; a.title
+'How to use class initializers'
+&gt;&gt;&gt; a.author
+'M. Simionato'
+&gt;&gt;&gt; a.date
+datetime.date(2006, 7, 10)
+&gt;&gt;&gt; a.date = '2006-07-11'
+&gt;&gt;&gt; a.date
+datetime.date(2006, 7, 11)
+</pre>
+<p>The point of using the class initializer is that the class is completely
+dynamic and it can be built at runtime with a schema that can be read
+from a configuration file or by introspecting a database table. You
+have the advantages of a custom metaclass without any of the disadvantages.</p>
+<p>It is also interesting to notice that this approach avoids inheritance
+completely, so if you have a pre-existing record class and you want
+to change its implementation to use this trick, it is enough to add
+<tt class="docutils literal"><span class="pre">def_properties</span></tt>, you don't need any kind of (multiple)
+inheritance.</p>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id13" id="references" name="references">References</a></h1>
+<p>About metaclasses:
+<a class="reference" href="http://www-128.ibm.com/developerworks/linux/library/l-pymeta.html">http://www-128.ibm.com/developerworks/linux/library/l-pymeta.html</a> and
+<a class="reference" href="http://www-128.ibm.com/developerworks/linux/library/l-pymeta2">http://www-128.ibm.com/developerworks/linux/library/l-pymeta2</a></p>
+<p>About using decorators instead of metaclasses:</p>
+<p>&lt;David's last paper&gt;</p>
+<p>The code from which everything was born:</p>
+<p><a class="reference" href="http://svn.zope.org/Zope3/trunk/src/zope/interface/advice.py">http://svn.zope.org/Zope3/trunk/src/zope/interface/advice.py</a></p>
+</div>
+<div class="section">
+<h1><a class="toc-backref" href="#id14" id="questions-and-answers" name="questions-and-answers">Questions and answers</a></h1>
+<dl class="docutils">
+<dt>Q</dt>
+<dd>Is there any specific licence for the code discussed in the paper?</dd>
+<dt>A</dt>
+<dd>No, you may assume the Public Domain or the Python licence, whatever
+you are happier with. If you are using my code, or
+code heavily derived from my own in your frameworks/applications I
+would appreciated to be notified, just to gratify my ego.</dd>
+<dt>Q</dt>
+<dd>How do I extract snippets of code from the paper?</dd>
+<dt>A</dt>
+<dd><p class="first">Download <a class="reference" href="http://www.phyast.pitt.edu/~micheles/classinitializer.zip">http://www.phyast.pitt.edu/~micheles/classinitializer.zip</a>
+which contains the source
+version of the paper (as well as the HTML and PDF versions)
+and a doctester utility. Run</p>
+<p><tt class="docutils literal"><span class="pre">$</span> <span class="pre">python</span> <span class="pre">doctester.py</span> <span class="pre">classinitializer.txt</span></tt></p>
+<p class="last">This will run many doctests and generate a script called <tt class="docutils literal"><span class="pre">_main.py</span></tt>
+with the source code for <tt class="docutils literal"><span class="pre">classinitializer</span></tt> and <tt class="docutils literal"><span class="pre">def_properties</span></tt>.</p>
+</dd>
+<dt>Q</dt>
+<dd>The doctester is a really cool idea! Can I use it for my own projects?</dd>
+<dt>A</dt>
+<dd>Yes. See also
+<a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052</a></dd>
+</dl>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/classinitializer/classinitializer.tex b/pypers/classinitializer/classinitializer.tex
new file mode 100644
index 0000000..1ff223d
--- /dev/null
+++ b/pypers/classinitializer/classinitializer.tex
@@ -0,0 +1,696 @@
+\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{Let's keep it simple (or, how to do metaprogramming without metaclasses)}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Let's keep it simple (or, how to do metaprogramming without metaclasses)},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{Date}: &
+ 10 July 2006 \\
+\textbf{Version}: &
+ 0.5 \\
+\end{tabularx}
+\end{center}
+
+\setlength{\locallinewidth}{\linewidth}
+\hypertarget{contents}{}
+\pdfbookmark[0]{Contents}{contents}
+\subsubsection*{~\hfill Contents\hfill ~}
+\begin{list}{}{}
+\item {} \href{\#introduction}{Introduction}
+
+\item {} \href{\#about-class-initialization}{About class initialization}
+
+\item {} \href{\#please-stop-abusing-metaclasses}{Please stop abusing metaclasses}
+
+\item {} \href{\#the-classinitializer-decorator}{The \texttt{classinitializer} decorator}
+
+\item {} \href{\#tricky-points-and-caveats}{Tricky points and caveats}
+
+\item {} \href{\#example-initializing-records}{Example: initializing records}
+
+\item {} \href{\#references}{References}
+
+\item {} \href{\#questions-and-answers}{Questions and answers}
+
+\end{list}
+
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\pdfbookmark[0]{Introduction}{introduction}
+\section*{Introduction}
+
+A few days ago I was at CERN, at the EuroPython 2006 conference. The
+conference was good, the organization perfect, the talks of
+very high level, the people extremely nice.
+
+Nevertheless, I saw a trend growing in the Python community
+that disturbed me a little and motivated
+me to write this paper. The trend I am alluding to, is the trend
+towards \emph{cleverness}. Unfortunately, whereas once cleverness was
+mostly confined to Zope and Twisted, now is appearing everywhere.
+
+I personally don't have anything against cleverness for
+experimental projects and learning exercises. But I have a gripe
+against cleverness when I see it deployed in production frameworks
+that I am forced to cope with as an user.
+
+Cleverness means making things more complicated than
+needed, making things more fragile, making the learning curve
+steeper and, worse of all, making debugging harder.
+
+In this short paper I will try to give my small contribution against
+cleverness, at least in a case where I have some expertise, i.e.
+against metaclass abuses.
+
+Let me say that I consider \emph{metaclass abuse} any usage of a metaclass
+in a situation where you could have solved the problem without a
+metaclass.
+
+I feel in part responsible for some of the abuses I see floating
+around in newsgroups, in conferences and in frameworks source code,
+because of my (together with David Mertz) contribution in popularizing
+metaclasses, so I have decided to make amend with this paper.
+
+This paper consider only one kind of metaprogramming technique, i.e.
+the creation at runtime of classes with attributes and methods which
+are dynamically generated. Contrarily to popular belief, this is a job
+where most of the time you \emph{don't need} and you \emph{don't want} a custom
+metaclass, as I will argue in the next section.
+
+The paper is intended for a double target of readers:
+\begin{itemize}
+\item {}
+average programmers, that would benefit from knowing a few
+meta-programming tricks, but are scared off by brain melting concepts;
+
+\item {}
+clever programmers, which are actually too clever and should know
+better [\hyperlink{id2}{1}].
+
+\end{itemize}
+
+If you are a lazy reader, your may just read the next paragraph and
+the last one, and forget about the details.
+\begin{figure}[b]\hypertarget{id2}[1]
+The problem is that it is easy to be clever whereas it takes a
+lot of time to become unclever. For instance, it took me a few
+months to understand how to use metaclasses, but a few years
+to understand how \emph{not} to use them.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{about-class-initialization}{}
+\pdfbookmark[0]{About class initialization}{about-class-initialization}
+\section*{About class initialization}
+
+By class initialization I mean setting attributes and methods of
+classes immediately after their creation, once and for all [\hyperlink{id4}{2}].
+
+There are various common situations where a programmer may want to
+initialize her classes: for instance, she may want to set some default class
+attributes according to parameters read from a configuration
+file, or she may want to set class properties according to the fields
+in a database table.
+
+The easiest way to perform class initialization
+is by using an imperative style: one first creates the class,
+and then adds the dynamically generated methods and attributes.
+
+For instance, if the problem is to generate properties for an \texttt{Article}
+record class, an imperative solution is something like the following:
+\begin{quote}{\ttfamily \raggedright \noindent
+class~Article(object):~\\
+~~~def~somemethod(self):~\\
+~~~~~~~pass~\\
+~~~...~\\
+~\\
+set{\_}properties(Article,~{[}('title',~str),~('author',~str),~('date',~date){]})
+}\end{quote}
+
+However, since (proper) class initialization should occur only once,
+it does not need to be distinguished by class
+creation, and it may be argued that it
+should be treated with a declarative style, with a syntax like the following:
+\begin{quote}{\ttfamily \raggedright \noindent
+class~Article(object):~\\
+~\\
+~~~def{\_}properties({[}('title',~str),~('author',~str),~('date',~date){]})~\\
+~\\
+~~~def~somemethod(self):~\\
+~~~~~~~pass~\\
+~\\
+~~~...
+}\end{quote}
+
+This paper is about providing a generic facility to define \emph{class initializers}
+to be used in the class scope, such as \texttt{def{\_}properties}.
+
+\textbf{Disclaimer:} the advantage of the solution I propose here,
+is that it works in current Python and it is less clever than
+metaclasses, Still I would consider it a little too clever. A clean solution
+to the problem would be to add class decorator to the language. That
+would allow a syntax like the following:
+\begin{quote}{\ttfamily \raggedright \noindent
+@with{\_}properties({[}('title',~str),~('author',~str),~('date',~date){]})~\\
+class~Article(object):~\\
+~~~def~somemethod(self):~\\
+~~~~~~~pass~\\
+~~~...
+}\end{quote}
+
+However, it is not sure at the moment if and when Guido will add class
+decorators, so my proposed solution is the lesser of two evils.
+\begin{figure}[b]\hypertarget{id4}[2]
+Well, in Python methods and attributes can always be changed at
+a later time, but let us assume that nobody is cheating here.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{please-stop-abusing-metaclasses}{}
+\pdfbookmark[0]{Please stop abusing metaclasses}{please-stop-abusing-metaclasses}
+\section*{Please stop abusing metaclasses}
+
+Everybody knows how to initialize instances. One just overrides
+the class \texttt{{\_}{\_}init{\_}{\_}} method. Since classes are instances of metaclasses,
+the natural solution to the class initialization problem seems to be
+to use a custom metaclass and to override its \texttt{{\_}{\_}init{\_}{\_}} method.
+
+Un fortunately, things are not so easy, because of inheritance. The
+issue is that once you have defined a custom metaclass for your base
+class, all the derived classes will inherit the metaclass, so the
+initialization code will be run on all derived classes, magically and
+implicitly.
+
+That may be fine in specific circumstances (for instance,
+suppose you have to register in your framework all the classes you
+define: using a metaclass ensures that you cannot forget to register a
+derived class), however, in many cases you may not like this behavior because:
+\newcounter{listcnt0}
+\begin{list}{\arabic{listcnt0}.}
+{
+\usecounter{listcnt0}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+you may believe that \emph{explicit is better than implicit};
+
+\item {}
+if the derived classes have the same dynamic class attributes of
+the base class, implicitly setting them again for each derived
+class is a waste, since they would be available anyway by
+inheritance. This may be a real issue if the initialization code is
+slow, possibly because it must access a database, or perform some
+heavy computation: in this case one must add a check in the
+metaclass code to see if the attributes were already set in
+a parent class, but this adds plumbing and it does not give real
+control on a per-class basis;
+
+\item {}
+a custom metaclass will make your classes somewhat magic and
+nonstandard: you may not want to increase your chances to incur in
+metaclass conflicts, issues with \texttt{{\_}{\_}slots{\_}{\_}}, fights with (Zope)
+extension classes and other guru-level intricacies [\hyperlink{id6}{3}];
+
+\item {}
+you feel that a custom metaclasses is overkill for the simple job
+of class initialization and you would rather use a simpler solution.
+
+\end{list}
+
+In other words,you should use a custom metaclass only when your real
+intention is to have code running on derived classes without users of
+those classes noticing it. If this is not your case, please don't a
+metaclass and make your life (as well your users) happier.
+\begin{figure}[b]\hypertarget{id6}[3]
+Metaclasses are more fragile than many people realize. I
+personally have never used them for production code, even
+after four years of usage in experimental code.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-classinitializer-decorator}{}
+\pdfbookmark[0]{The classinitializer decorator}{the-classinitializer-decorator}
+\section*{The \texttt{classinitializer} decorator}
+
+The aim of this paper is to provide a decorator called
+\texttt{classinitializer} that can be used to define
+class initializers, i.e. procedures taking classes and setting their
+attributes and methods. In order to be concrete, consider the following
+class initializer example:
+\begin{quote}{\ttfamily \raggedright \noindent
+def~set{\_}defaults(cls,~**kw):~\\
+~~~~for~k,~v~in~kw.iteritems():~\\
+~~~~~~~~setattr(cls,~k,~v)
+}\end{quote}
+
+You may use it imperatively, right after a class definition:
+\begin{quote}{\ttfamily \raggedright \noindent
+class~ClassToBeInitialized(object):~\\
+~~~~pass~\\
+~\\
+set{\_}defaults(ClassToBeInitialized,~a=1,~b=2)
+}\end{quote}
+
+However the imperative solution has a few drawbacks:
+\setcounter{listcnt0}{0}
+\begin{list}{\arabic{listcnt0}.}
+{
+\usecounter{listcnt0}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item {}
+it does not comply with DRY, i.e. the class name is repeated
+and if I change it during refactoring, I have to change it (at
+least) twice;
+
+\item {}
+readability is suboptimal: since class definition and class
+initialization are separated, for long class definitions I may end
+up not seeing the last line;
+
+\item {}
+it feels wrong to first define something and immediately right
+after to mutate it.
+
+\end{list}
+
+Luckily, the \texttt{classinitializer} decorator provides a much nicer
+declarative solution. The decorator performs some deep magic and converts
+\texttt{set{\_}defaults(cls, **kw)} into a function that can be
+used at the top scope into a class definition, with the current class
+automagically passed as first parameter:
+\begin{verbatim}>>> @classinitializer # add magic to set_defaults
+... def set_defaults(cls, **kw):
+... for k, v in kw.iteritems():
+... setattr(cls, k, v)\end{verbatim}
+\begin{verbatim}>>> class ClassToBeInitialized(object):
+... set_defaults(a=1, b=2)\end{verbatim}
+\begin{verbatim}>>> ClassToBeInitialized.a
+1
+>>> ClassToBeInitialized.b
+2\end{verbatim}
+
+If you have used Zope interfaces, you may have seen examples of class
+initializers (I mean \texttt{zope.interface.implements}). In fact under the hood
+\texttt{classinitializer} is implemented by using a trick copied from
+\texttt{zope.interface.advice}, which credits Phillip J. Eby. The trick
+uses the \texttt{{\_}{\_}metaclass{\_}{\_}} hook, but it \emph{does not use} a custom
+metaclass, so that in this example \texttt{ClassToBeInitialized} will
+continue to keep its original metaclass, i.e. the plain old regular
+built-in metaclass \texttt{type} of new style classes:
+\begin{verbatim}>>> type(ClassToBeInitialized)
+<type 'type'>\end{verbatim}
+
+In principle, the trick also works for old style classes,
+and it would be easy to write an implementation keeping old style
+classes old style. However, since according to Guido himself
+\emph{old style classes are morally deprecated}, the current implementation
+automagically converts old style classes into new style classes:
+\begin{verbatim}>>> class WasOldStyle:
+... set_defaults(a=1, b=2)\end{verbatim}
+\begin{verbatim}>>> WasOldStyle.a, WasOldStyle.b
+(1, 2)
+>>> type(WasOldStyle)
+<type 'type'>\end{verbatim}
+
+One of the motivations for the \texttt{classinitializer} decorator, is to hide the
+plumbing, and to make mere mortals able to implements their own
+class initializers in an easy way, without knowing the details of
+how class creation works and the secrets of the \texttt{{\_}{\_}metaclass{\_}{\_}}
+hook. The other motivation, is that even for Python wizards it is very
+unconvenient to rewrite the code managing the \texttt{{\_}{\_}metaclass{\_}{\_}} hook
+every time one writes a new class initializer. So I have decided to
+use a decorator to allow separation of concerns and reuse of code.
+
+As a final note, let me point out that the decorated version of
+\texttt{set{\_}defaults} is so smart that it will continue to
+work as the non-decorated version outside a class scope, provided that
+you pass to it an explicit class argument.
+\begin{verbatim}>>> set_defaults(WasOldStyle, a=2)
+>>> WasOldStyle.a
+2\end{verbatim}
+
+In other words, you \emph{might} continue to use the imperative style.
+
+Here is the code for \texttt{classinitializer} (the point being that you
+don't need to be able to understand it to use the decorator):
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<{\_}main.py>~\\
+~\\
+import~sys~\\
+~\\
+def~classinitializer(proc):~\\
+~~{\#}~basic~idea~stolen~from~zope.interface.advice,~which~credits~P.J.~Eby~\\
+~~def~newproc(*args,~**kw):~\\
+~~~~~~frame~=~sys.{\_}getframe(1)~\\
+~~~~~~if~'{\_}{\_}module{\_}{\_}'~in~frame.f{\_}locals~and~not~{\textbackslash}~\\
+~~~~~~~~~'{\_}{\_}module{\_}{\_}'~in~frame.f{\_}code.co{\_}varnames:~{\#}~we~are~in~a~class~\\
+~~~~~~~~~~if~'{\_}{\_}metaclass{\_}{\_}'~in~frame.f{\_}locals:~\\
+~~~~~~~~~~~~raise~SyntaxError("Don't~use~two~class~initializers~or{\textbackslash}n"~\\
+~~~~~~~~~~~~"a~class~initializer~together~with~a{\_}{\_}metaclass{\_}{\_}~hook")~\\
+~~~~~~~~~~def~makecls(name,~bases,~dic):~\\
+~~~~~~~~~~~~~try:~\\
+~~~~~~~~~~~~~~~~cls~=~type(name,~bases,~dic)~\\
+~~~~~~~~~~~~~except~TypeError,~e:~\\
+~~~~~~~~~~~~~~~~if~"can't~have~only~classic~bases"~in~str(e):~\\
+~~~~~~~~~~~~~~~~~~~cls~=~type(name,~bases~+~(object,),~dic)~\\
+~~~~~~~~~~~~~~~~else:~{\#}~other~strange~errors,~such~as~{\_}{\_}slots{\_}{\_}~conflicts,~etc~\\
+~~~~~~~~~~~~~~~~~~~raise~\\
+~~~~~~~~~~~~~proc(cls,~*args,~**kw)~\\
+~~~~~~~~~~~~~return~cls~\\
+~~~~~~~~~~frame.f{\_}locals{[}"{\_}{\_}metaclass{\_}{\_}"{]}~=~makecls~\\
+~~~~~~else:~\\
+~~~~~~~~~~proc(*args,~**kw)~\\
+~~newproc.{\_}{\_}name{\_}{\_}~=~proc.{\_}{\_}name{\_}{\_}~\\
+~~newproc.{\_}{\_}module{\_}{\_}~=~proc.{\_}{\_}module{\_}{\_}~\\
+~~newproc.{\_}{\_}doc{\_}{\_}~=~proc.{\_}{\_}doc{\_}{\_}~\\
+~~newproc.{\_}{\_}dict{\_}{\_}~=~proc.{\_}{\_}dict{\_}{\_}~\\
+~~return~newproc~\\
+~\\
+{\#}</{\_}main.py>
+}\end{quote}
+
+From the implementation it is clear how class initializers work:
+when you call a class initializer inside a class, your are actually defining a
+\texttt{{\_}{\_}metaclass{\_}{\_}} hook that will be called by
+the class' metaclass (typically \texttt{type}). The
+metaclass will create the class (as a new style one) and
+will pass it to the class initializer procedure.
+
+
+%___________________________________________________________________________
+
+\hypertarget{tricky-points-and-caveats}{}
+\pdfbookmark[0]{Tricky points and caveats}{tricky-points-and-caveats}
+\section*{Tricky points and caveats}
+
+Since class initializers (re)define the \texttt{{\_}{\_}metaclass{\_}{\_}} hook,
+they don't play well with classes that define a \texttt{{\_}{\_}metaclass{\_}{\_}} hook
+explicitly (as opposed to implicitly inheriting one). The issue is
+that if a \texttt{{\_}{\_}metaclass{\_}{\_}} hook is defined \emph{after} the
+class initializer, it \emph{silently} overrides it.
+\begin{verbatim}>>> class C:
+... set_defaults(a=1)
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'set_defaults is silently ignored'
+... return cls
+...
+set_defaults is silently ignored
+>>> C.a
+Traceback (most recent call last):
+ ...
+AttributeError: type object 'C' has no attribute 'a'\end{verbatim}
+
+This is unfortunate, but there is no general solution to this issue, and I will
+simply document it (this is one of the reasons why I feel
+class initializers to be a clever hack that should be dismissed if we
+had class decorators).
+
+On the other hand, if you call a class initializer
+\emph{after} the \texttt{{\_}{\_}metaclass{\_}{\_}} hook, you will get an exception:
+\begin{verbatim}>>> class C:
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'calling explicit __metaclass__'
+... return cls
+... set_defaults(a=1)
+...
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook\end{verbatim}
+
+I feel raising an error is preferable to silently overriding your
+explicit \texttt{{\_}{\_}metaclass{\_}{\_}} hook. As a consequence, you will get an
+error if you try to use two class initializers at the same time, or
+if you call twice the same one:
+\begin{verbatim}>>> class C:
+... set_defaults(a=1)
+... set_defaults(b=2)
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook\end{verbatim}
+
+I feel raising an error to be better than having the second
+initializer overriding the first one, i.e. in this example
+to set the \texttt{b} attribute \emph{without} setting the \texttt{a} attribute,
+which would be very confusing.
+
+Finally, let me show that there are no issues for inherited
+\texttt{{\_}{\_}metaclass{\_}{\_}} hooks and for custom metaclasses:
+\begin{verbatim}>>> class B: # a base class with a custom metaclass
+... class __metaclass__(type):
+... pass\end{verbatim}
+\begin{verbatim}>>> class C(B): # a class with both a custom metaclass AND a class initializer
+... set_defaults(a=1)\end{verbatim}
+\begin{verbatim}>>> C.a
+1
+>>> type(C)
+<class '_main.__metaclass__'>\end{verbatim}
+
+The class initializer does not disturb the metaclass of \texttt{C}, which is
+the one inherited by its base \texttt{B}, and the inherited metaclass does
+not disturb the class initializer, which does its job just fine.
+You would have run into trouble, instead, if you tried to call \texttt{set{\_}defaults}
+directly in the base class.
+
+
+%___________________________________________________________________________
+
+\hypertarget{example-initializing-records}{}
+\pdfbookmark[0]{Example: initializing records}{example-initializing-records}
+\section*{Example: initializing records}
+
+In this section I will finally discuss the example I gave at the
+beginning, about how to define record classes with a class initializer.
+Here is the code for the class initializer, plus an helper function
+for managing dates:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}<{\_}main.py>~\\
+~\\
+import~datetime~\\
+~\\
+@classinitializer~\\
+def~def{\_}properties(cls,~schema):~\\
+~~~~"{}"{}"~\\
+~~~~Add~properties~to~cls,~according~to~the~schema,~which~is~a~list~\\
+~~~~of~pairs~(fieldname,~typecast).~A~typecast~is~a~\\
+~~~~callable~converting~the~field~value~into~a~Python~type.~\\
+~~~~The~initializer~saves~the~attribute~names~in~a~list~cls.fields~\\
+~~~~and~the~typecasts~in~a~list~cls.types.~Instances~of~cls~are~expected~\\
+~~~~to~have~private~attributes~with~names~determined~by~the~field~names.~\\
+~~~~"{}"{}"~\\
+~~~~cls.fields~=~{[}{]}~\\
+~~~~cls.types~=~{[}{]}~\\
+~~~~for~name,~typecast~in~schema:~\\
+~~~~~~~~if~hasattr(cls,~name):~{\#}~avoid~accidental~overriding~\\
+~~~~~~~~~~~~raise~AttributeError('You~are~overriding~{\%}s!'~{\%}~name)~\\
+~~~~~~~~def~getter(self,~name=name):~\\
+~~~~~~~~~~~~return~getattr(self,~'{\_}'~+~name)~\\
+~~~~~~~~def~setter(self,~value,~name=name,~typecast=typecast):~\\
+~~~~~~~~~~~~setattr(self,~'{\_}'~+~name,~typecast(value))~\\
+~~~~~~~~setattr(cls,~name,~property(getter,~setter))~\\
+~~~~~~~~cls.fields.append(name)~\\
+~~~~~~~~cls.types.append(typecast)~\\
+~\\
+def~date(isodate):~{\#}~add~error~checking~if~you~like~\\
+~~~~"Convert~an~ISO~date~into~a~datetime.date~object"~\\
+~~~~return~datetime.date(*map(int,~isodate.split('-')))~\\
+~\\
+{\#}</{\_}main.py>
+}\end{quote}
+
+As an example of application of the above class initializer,
+I will define an \emph{Article} record class with fields \emph{title}, \emph{author}
+and \emph{date}:
+\begin{verbatim}>>> class Article(object):
+... # fields and types are dynamically set by the initializer
+... def_properties([('title', str), ('author', str), ('date', date)])
+... def __init__(self, values): # add error checking if you like
+... for field, cast, value in zip(self.fields, self.types, values):
+... setattr(self, '_' + field, cast(value))\end{verbatim}
+\begin{verbatim}>>> a=Article(['How to use class initializers', 'M. Simionato', '2006-07-10'])
+>>> a.title
+'How to use class initializers'
+>>> a.author
+'M. Simionato'
+>>> a.date
+datetime.date(2006, 7, 10)
+>>> a.date = '2006-07-11'
+>>> a.date
+datetime.date(2006, 7, 11)\end{verbatim}
+
+The point of using the class initializer is that the class is completely
+dynamic and it can be built at runtime with a schema that can be read
+from a configuration file or by introspecting a database table. You
+have the advantages of a custom metaclass without any of the disadvantages.
+
+It is also interesting to notice that this approach avoids inheritance
+completely, so if you have a pre-existing record class and you want
+to change its implementation to use this trick, it is enough to add
+\texttt{def{\_}properties}, you don't need any kind of (multiple)
+inheritance.
+
+
+%___________________________________________________________________________
+
+\hypertarget{references}{}
+\pdfbookmark[0]{References}{references}
+\section*{References}
+
+About metaclasses:
+\href{http://www-128.ibm.com/developerworks/linux/library/l-pymeta.html}{http://www-128.ibm.com/developerworks/linux/library/l-pymeta.html} and
+\href{http://www-128.ibm.com/developerworks/linux/library/l-pymeta2}{http://www-128.ibm.com/developerworks/linux/library/l-pymeta2}
+
+About using decorators instead of metaclasses:
+
+{\textless}David's last paper{\textgreater}
+
+The code from which everything was born:
+
+\href{http://svn.zope.org/Zope3/trunk/src/zope/interface/advice.py}{http://svn.zope.org/Zope3/trunk/src/zope/interface/advice.py}
+
+
+%___________________________________________________________________________
+
+\hypertarget{questions-and-answers}{}
+\pdfbookmark[0]{Questions and answers}{questions-and-answers}
+\section*{Questions and answers}
+\begin{description}
+%[visit_definition_list_item]
+\item[{Q}] %[visit_definition]
+
+Is there any specific licence for the code discussed in the paper?
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[{A}] %[visit_definition]
+
+No, you may assume the Public Domain or the Python licence, whatever
+you are happier with. If you are using my code, or
+code heavily derived from my own in your frameworks/applications I
+would appreciated to be notified, just to gratify my ego.
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[{Q}] %[visit_definition]
+
+How do I extract snippets of code from the paper?
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[{A}] %[visit_definition]
+
+Download \href{http://www.phyast.pitt.edu/~micheles/classinitializer.zip}{http://www.phyast.pitt.edu/{\textasciitilde}micheles/classinitializer.zip}
+which contains the source
+version of the paper (as well as the HTML and PDF versions)
+and a doctester utility. Run
+
+\texttt{{\$} python doctester.py classinitializer.txt}
+
+This will run many doctests and generate a script called \texttt{{\_}main.py}
+with the source code for \texttt{classinitializer} and \texttt{def{\_}properties}.
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[{Q}] %[visit_definition]
+
+The doctester is a really cool idea! Can I use it for my own projects?
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[{A}] %[visit_definition]
+
+Yes. See also
+\href{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052}{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052}
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+
+\end{document}
+
diff --git a/pypers/classinitializer/classinitializer.txt b/pypers/classinitializer/classinitializer.txt
new file mode 100644
index 0000000..ec561ac
--- /dev/null
+++ b/pypers/classinitializer/classinitializer.txt
@@ -0,0 +1,517 @@
+Let's keep it simple (or, how to do metaprogramming without metaclasses)
+========================================================================
+
+
+:Author: Michele Simionato
+:Date: 10 July 2006
+:Version: 0.5
+
+.. contents::
+
+Introduction
+-----------------------------------------------------------------------
+
+A few days ago I was at CERN, at the EuroPython 2006 conference. The
+conference was good, the organization perfect, the talks of
+very high level, the people extremely nice.
+
+Nevertheless, I saw a trend growing in the Python community
+that disturbed me a little and motivated
+me to write this paper. The trend I am alluding to, is the trend
+towards *cleverness*. Unfortunately, whereas once cleverness was
+mostly confined to Zope and Twisted, now is appearing everywhere.
+
+I personally don't have anything against cleverness for
+experimental projects and learning exercises. But I have a gripe
+against cleverness when I see it deployed in production frameworks
+that I am forced to cope with as an user.
+
+Cleverness means making things more complicated than
+needed, making things more fragile, making the learning curve
+steeper and, worse of all, making debugging harder.
+
+In this short paper I will try to give my small contribution against
+cleverness, at least in a case where I have some expertise, i.e.
+against metaclass abuses.
+
+Let me say that I consider *metaclass abuse* any usage of a metaclass
+in a situation where you could have solved the problem without a
+metaclass.
+
+I feel in part responsible for some of the abuses I see floating
+around in newsgroups, in conferences and in frameworks source code,
+because of my (together with David Mertz) contribution in popularizing
+metaclasses, so I have decided to make amend with this paper.
+
+This paper consider only one kind of metaprogramming technique, i.e.
+the creation at runtime of classes with attributes and methods which
+are dynamically generated. Contrarily to popular belief, this is a job
+where most of the time you *don't need* and you *don't want* a custom
+metaclass, as I will argue in the next section.
+
+The paper is intended for a double target of readers:
+
+- average programmers, that would benefit from knowing a few
+ meta-programming tricks, but are scared off by brain melting concepts;
+
+- clever programmers, which are actually too clever and should know
+ better [#]_.
+
+If you are a lazy reader, your may just read the next paragraph and
+the last one, and forget about the details.
+
+.. [#] The problem is that it is easy to be clever whereas it takes a
+ lot of time to become unclever. For instance, it took me a few
+ months to understand how to use metaclasses, but a few years
+ to understand how *not* to use them.
+
+About class initialization
+-----------------------------------------------------------------------
+
+By class initialization I mean setting attributes and methods of
+classes immediately after their creation, once and for all [#]_.
+
+There are various common situations where a programmer may want to
+initialize her classes: for instance, she may want to set some default class
+attributes according to parameters read from a configuration
+file, or she may want to set class properties according to the fields
+in a database table.
+
+The easiest way to perform class initialization
+is by using an imperative style: one first creates the class,
+and then adds the dynamically generated methods and attributes.
+
+For instance, if the problem is to generate properties for an ``Article``
+record class, an imperative solution is something like the following::
+
+ class Article(object):
+ def somemethod(self):
+ pass
+ ...
+
+ set_properties(Article, [('title', str), ('author', str), ('date', date)])
+
+
+However, since (proper) class initialization should occur only once,
+it does not need to be distinguished by class
+creation, and it may be argued that it
+should be treated with a declarative style, with a syntax like the following::
+
+ class Article(object):
+
+ def_properties([('title', str), ('author', str), ('date', date)])
+
+ def somemethod(self):
+ pass
+
+ ...
+
+This paper is about providing a generic facility to define *class initializers*
+to be used in the class scope, such as ``def_properties``.
+
+**Disclaimer:** the advantage of the solution I propose here,
+is that it works in current Python and it is less clever than
+metaclasses, Still I would consider it a little too clever. A clean solution
+to the problem would be to add class decorator to the language. That
+would allow a syntax like the following::
+
+ @with_properties([('title', str), ('author', str), ('date', date)])
+ class Article(object):
+ def somemethod(self):
+ pass
+ ...
+
+However, it is not sure at the moment if and when Guido will add class
+decorators, so my proposed solution is the lesser of two evils.
+
+.. [#] Well, in Python methods and attributes can always be changed at
+ a later time, but let us assume that nobody is cheating here.
+
+Please stop abusing metaclasses
+-----------------------------------------------------------------------
+
+Everybody knows how to initialize instances. One just overrides
+the class ``__init__`` method. Since classes are instances of metaclasses,
+the natural solution to the class initialization problem seems to be
+to use a custom metaclass and to override its ``__init__`` method.
+
+Un fortunately, things are not so easy, because of inheritance. The
+issue is that once you have defined a custom metaclass for your base
+class, all the derived classes will inherit the metaclass, so the
+initialization code will be run on all derived classes, magically and
+implicitly.
+
+That may be fine in specific circumstances (for instance,
+suppose you have to register in your framework all the classes you
+define: using a metaclass ensures that you cannot forget to register a
+derived class), however, in many cases you may not like this behavior because:
+
+1. you may believe that *explicit is better than implicit*;
+
+2. if the derived classes have the same dynamic class attributes of
+ the base class, implicitly setting them again for each derived
+ class is a waste, since they would be available anyway by
+ inheritance. This may be a real issue if the initialization code is
+ slow, possibly because it must access a database, or perform some
+ heavy computation: in this case one must add a check in the
+ metaclass code to see if the attributes were already set in
+ a parent class, but this adds plumbing and it does not give real
+ control on a per-class basis;
+
+3. a custom metaclass will make your classes somewhat magic and
+ nonstandard: you may not want to increase your chances to incur in
+ metaclass conflicts, issues with ``__slots__``, fights with (Zope)
+ extension classes and other guru-level intricacies [#]_;
+
+4. you feel that a custom metaclasses is overkill for the simple job
+ of class initialization and you would rather use a simpler solution.
+
+In other words,you should use a custom metaclass only when your real
+intention is to have code running on derived classes without users of
+those classes noticing it. If this is not your case, please don't a
+metaclass and make your life (as well your users) happier.
+
+.. [#] Metaclasses are more fragile than many people realize. I
+ personally have never used them for production code, even
+ after four years of usage in experimental code.
+
+The ``classinitializer`` decorator
+------------------------------------------------------------------
+
+The aim of this paper is to provide a decorator called
+``classinitializer`` that can be used to define
+class initializers, i.e. procedures taking classes and setting their
+attributes and methods. In order to be concrete, consider the following
+class initializer example::
+
+ def set_defaults(cls, **kw):
+ for k, v in kw.iteritems():
+ setattr(cls, k, v)
+
+You may use it imperatively, right after a class definition::
+
+ class ClassToBeInitialized(object):
+ pass
+
+ set_defaults(ClassToBeInitialized, a=1, b=2)
+
+However the imperative solution has a few drawbacks:
+
+1. it does not comply with DRY, i.e. the class name is repeated
+ and if I change it during refactoring, I have to change it (at
+ least) twice;
+
+2. readability is suboptimal: since class definition and class
+ initialization are separated, for long class definitions I may end
+ up not seeing the last line;
+
+3. it feels wrong to first define something and immediately right
+ after to mutate it.
+
+Luckily, the ``classinitializer`` decorator provides a much nicer
+declarative solution. The decorator performs some deep magic and converts
+``set_defaults(cls, **kw)`` into a function that can be
+used at the top scope into a class definition, with the current class
+automagically passed as first parameter:
+
+>>> @classinitializer # add magic to set_defaults
+... def set_defaults(cls, **kw):
+... for k, v in kw.iteritems():
+... setattr(cls, k, v)
+
+>>> class ClassToBeInitialized(object):
+... set_defaults(a=1, b=2)
+
+>>> ClassToBeInitialized.a
+1
+>>> ClassToBeInitialized.b
+2
+
+If you have used Zope interfaces, you may have seen examples of class
+initializers (I mean ``zope.interface.implements``). In fact under the hood
+``classinitializer`` is implemented by using a trick copied from
+``zope.interface.advice``, which credits Phillip J. Eby. The trick
+uses the ``__metaclass__`` hook, but it *does not use* a custom
+metaclass, so that in this example ``ClassToBeInitialized`` will
+continue to keep its original metaclass, i.e. the plain old regular
+built-in metaclass ``type`` of new style classes:
+
+>>> type(ClassToBeInitialized)
+<type 'type'>
+
+In principle, the trick also works for old style classes,
+and it would be easy to write an implementation keeping old style
+classes old style. However, since according to Guido himself
+*old style classes are morally deprecated*, the current implementation
+automagically converts old style classes into new style classes:
+
+>>> class WasOldStyle:
+... set_defaults(a=1, b=2)
+
+>>> WasOldStyle.a, WasOldStyle.b
+(1, 2)
+>>> type(WasOldStyle)
+<type 'type'>
+
+One of the motivations for the ``classinitializer`` decorator, is to hide the
+plumbing, and to make mere mortals able to implements their own
+class initializers in an easy way, without knowing the details of
+how class creation works and the secrets of the ``__metaclass__``
+hook. The other motivation, is that even for Python wizards it is very
+unconvenient to rewrite the code managing the ``__metaclass__`` hook
+every time one writes a new class initializer. So I have decided to
+use a decorator to allow separation of concerns and reuse of code.
+
+As a final note, let me point out that the decorated version of
+``set_defaults`` is so smart that it will continue to
+work as the non-decorated version outside a class scope, provided that
+you pass to it an explicit class argument.
+
+>>> set_defaults(WasOldStyle, a=2)
+>>> WasOldStyle.a
+2
+
+In other words, you *might* continue to use the imperative style.
+
+Here is the code for ``classinitializer`` (the point being that you
+don't need to be able to understand it to use the decorator)::
+
+ #<_main.py>
+
+ import sys
+
+ def classinitializer(proc):
+ # basic idea stolen from zope.interface.advice, which credits P.J. Eby
+ def newproc(*args, **kw):
+ frame = sys._getframe(1)
+ if '__module__' in frame.f_locals and not \
+ '__module__' in frame.f_code.co_varnames: # we are in a class
+ if '__metaclass__' in frame.f_locals:
+ raise SyntaxError("Don't use two class initializers or\n"
+ "a class initializer together with a__metaclass__ hook")
+ def makecls(name, bases, dic):
+ try:
+ cls = type(name, bases, dic)
+ except TypeError, e:
+ if "can't have only classic bases" in str(e):
+ cls = type(name, bases + (object,), dic)
+ else: # other strange errors, such as __slots__ conflicts, etc
+ raise
+ proc(cls, *args, **kw)
+ return cls
+ frame.f_locals["__metaclass__"] = makecls
+ else:
+ proc(*args, **kw)
+ newproc.__name__ = proc.__name__
+ newproc.__module__ = proc.__module__
+ newproc.__doc__ = proc.__doc__
+ newproc.__dict__ = proc.__dict__
+ return newproc
+
+ #</_main.py>
+
+From the implementation it is clear how class initializers work:
+when you call a class initializer inside a class, your are actually defining a
+``__metaclass__`` hook that will be called by
+the class' metaclass (typically ``type``). The
+metaclass will create the class (as a new style one) and
+will pass it to the class initializer procedure.
+
+Tricky points and caveats
+------------------------------------
+
+Since class initializers (re)define the ``__metaclass__`` hook,
+they don't play well with classes that define a ``__metaclass__`` hook
+explicitly (as opposed to implicitly inheriting one). The issue is
+that if a ``__metaclass__`` hook is defined *after* the
+class initializer, it *silently* overrides it.
+
+>>> class C:
+... set_defaults(a=1)
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'set_defaults is silently ignored'
+... return cls
+...
+set_defaults is silently ignored
+>>> C.a
+Traceback (most recent call last):
+ ...
+AttributeError: type object 'C' has no attribute 'a'
+
+This is unfortunate, but there is no general solution to this issue, and I will
+simply document it (this is one of the reasons why I feel
+class initializers to be a clever hack that should be dismissed if we
+had class decorators).
+
+On the other hand, if you call a class initializer
+*after* the ``__metaclass__`` hook, you will get an exception:
+
+>>> class C:
+... def __metaclass__(name, bases, dic):
+... cls = type(name, bases, dic)
+... print 'calling explicit __metaclass__'
+... return cls
+... set_defaults(a=1)
+...
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook
+
+I feel raising an error is preferable to silently overriding your
+explicit ``__metaclass__`` hook. As a consequence, you will get an
+error if you try to use two class initializers at the same time, or
+if you call twice the same one:
+
+>>> class C:
+... set_defaults(a=1)
+... set_defaults(b=2)
+Traceback (most recent call last):
+ ...
+SyntaxError: Don't use two class initializers or
+a class initializer together with a__metaclass__ hook
+
+I feel raising an error to be better than having the second
+initializer overriding the first one, i.e. in this example
+to set the ``b`` attribute *without* setting the ``a`` attribute,
+which would be very confusing.
+
+Finally, let me show that there are no issues for inherited
+``__metaclass__`` hooks and for custom metaclasses:
+
+>>> class B: # a base class with a custom metaclass
+... class __metaclass__(type):
+... pass
+
+>>> class C(B): # a class with both a custom metaclass AND a class initializer
+... set_defaults(a=1)
+
+>>> C.a
+1
+>>> type(C)
+<class '_main.__metaclass__'>
+
+The class initializer does not disturb the metaclass of ``C``, which is
+the one inherited by its base ``B``, and the inherited metaclass does
+not disturb the class initializer, which does its job just fine.
+You would have run into trouble, instead, if you tried to call ``set_defaults``
+directly in the base class.
+
+Example: initializing records
+------------------------------------------
+
+In this section I will finally discuss the example I gave at the
+beginning, about how to define record classes with a class initializer.
+Here is the code for the class initializer, plus an helper function
+for managing dates::
+
+ #<_main.py>
+
+ import datetime
+
+ @classinitializer
+ def def_properties(cls, schema):
+ """
+ Add properties to cls, according to the schema, which is a list
+ of pairs (fieldname, typecast). A typecast is a
+ callable converting the field value into a Python type.
+ The initializer saves the attribute names in a list cls.fields
+ and the typecasts in a list cls.types. Instances of cls are expected
+ to have private attributes with names determined by the field names.
+ """
+ cls.fields = []
+ cls.types = []
+ for name, typecast in schema:
+ if hasattr(cls, name): # avoid accidental overriding
+ raise AttributeError('You are overriding %s!' % name)
+ def getter(self, name=name):
+ return getattr(self, '_' + name)
+ def setter(self, value, name=name, typecast=typecast):
+ setattr(self, '_' + name, typecast(value))
+ setattr(cls, name, property(getter, setter))
+ cls.fields.append(name)
+ cls.types.append(typecast)
+
+ def date(isodate): # add error checking if you like
+ "Convert an ISO date into a datetime.date object"
+ return datetime.date(*map(int, isodate.split('-')))
+
+ #</_main.py>
+
+As an example of application of the above class initializer,
+I will define an *Article* record class with fields *title*, *author*
+and *date*:
+
+>>> class Article(object):
+... # fields and types are dynamically set by the initializer
+... def_properties([('title', str), ('author', str), ('date', date)])
+... def __init__(self, values): # add error checking if you like
+... for field, cast, value in zip(self.fields, self.types, values):
+... setattr(self, '_' + field, cast(value))
+
+>>> a=Article(['How to use class initializers', 'M. Simionato', '2006-07-10'])
+>>> a.title
+'How to use class initializers'
+>>> a.author
+'M. Simionato'
+>>> a.date
+datetime.date(2006, 7, 10)
+>>> a.date = '2006-07-11'
+>>> a.date
+datetime.date(2006, 7, 11)
+
+The point of using the class initializer is that the class is completely
+dynamic and it can be built at runtime with a schema that can be read
+from a configuration file or by introspecting a database table. You
+have the advantages of a custom metaclass without any of the disadvantages.
+
+It is also interesting to notice that this approach avoids inheritance
+completely, so if you have a pre-existing record class and you want
+to change its implementation to use this trick, it is enough to add
+``def_properties``, you don't need any kind of (multiple)
+inheritance.
+
+References
+-------------------------------------------
+
+About metaclasses:
+http://www-128.ibm.com/developerworks/linux/library/l-pymeta.html and
+http://www-128.ibm.com/developerworks/linux/library/l-pymeta2
+
+
+About using decorators instead of metaclasses:
+
+<David's last paper>
+
+The code from which everything was born:
+
+http://svn.zope.org/Zope3/trunk/src/zope/interface/advice.py
+
+Questions and answers
+-----------------------------------------------
+
+Q
+ Is there any specific licence for the code discussed in the paper?
+A
+ No, you may assume the Public Domain or the Python licence, whatever
+ you are happier with. If you are using my code, or
+ code heavily derived from my own in your frameworks/applications I
+ would appreciated to be notified, just to gratify my ego.
+Q
+ How do I extract snippets of code from the paper?
+A
+ Download http://www.phyast.pitt.edu/~micheles/classinitializer.zip
+ which contains the source
+ version of the paper (as well as the HTML and PDF versions)
+ and a doctester utility. Run
+
+ ``$ python doctester.py classinitializer.txt``
+
+ This will run many doctests and generate a script called ``_main.py``
+ with the source code for ``classinitializer`` and ``def_properties``.
+Q
+ The doctester is a really cool idea! Can I use it for my own projects?
+A
+ Yes. See also
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410052
diff --git a/pypers/classinitializer/doctester.py b/pypers/classinitializer/doctester.py
new file mode 100755
index 0000000..1d9f1d0
--- /dev/null
+++ b/pypers/classinitializer/doctester.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python2.4
+# Author: michele.simionato@gmail.com
+"""\
+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)
+
+class file_(file):
+ """This is a file class which treats specially the filename "-",
+ returning stdin or stdout according to the mode."""
+ def __new__(cls, name, mode="r", buffering=1):
+ if name == "-" and "w" in mode or "a" in mode:
+ return sys.stdout
+ elif name == "-" and "r" in mode:
+ return sys.stdin
+ return super(file_, cls).__new__(cls, name, mode, buffering)
+
+# 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(fname, 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
+ return scriptdict
+
+# 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
+def runtests(fname, txt, verbose=False):
+ if "_main.py" in savescripts(fname, txt):
+ _main = __import__("_main") # real module
+ else: # dynamic module
+ _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
+ try:
+ fname = sys.argv[1]
+ except IndexError:
+ sys.exit(__doc__)
+ valid_options = set("-v -h".split())
+ options = set(sys.argv[2:])
+ assert options < valid_options, "Unrecognized option"
+ if "-h" in options: # print usage message and exit
+ sys.exit(__doc__)
+ runtests(fname, file_(fname).read(), "-v" in options)
diff --git a/pypers/codeproc.py b/pypers/codeproc.py
new file mode 100755
index 0000000..83cda30
--- /dev/null
+++ b/pypers/codeproc.py
@@ -0,0 +1,11 @@
+from oopp import codeprocess
+wrongcode=r'''
+"""Code processing example: replaces 'Print' with 'print' except in
+comments and literal strings"""
+Print "This program prints \"Hello World!\"" # look at this line!
+'''
+fixPrint=lambda s: s.replace('Print','print')
+validcode=codeprocess(wrongcode,fixPrint)
+print 'Source code:\n',validcode
+print 'Output:\n'; exec validcode
+
diff --git a/pypers/descr.html b/pypers/descr.html
new file mode 100755
index 0000000..00dab31
--- /dev/null
+++ b/pypers/descr.html
@@ -0,0 +1,1085 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>THE SOPHISTICATION OF DESCRIPTORS</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="the-sophistication-of-descriptors">
+<h1 class="title">THE SOPHISTICATION OF DESCRIPTORS</h1>
+<p>Attribute descriptors are important metaprogramming tools that allows
+the user to customize the behavior of attributes in custom classes.
+For instance, attribute descriptors (or descriptors for short)
+can be used as method wrappers,
+to modify or enhance methods (this is the case for the well
+known staticmethods and classmethods attribute descriptors); they
+can also be used as attribute wrappers, to change or restrict the access to
+attributes (this is the case for properties). Finally, descriptors
+allows the user to play with the resolution order of attributes:
+for instance, the <tt class="literal"><span class="pre">super</span></tt> built-in object used in (multiple) inheritance
+hierarchies, is implemented as an attribute descriptor.</p>
+<p>In this chapter, I will show how the user can define its own attribute
+descriptors and I will give some example of useful things you can do with
+them (in particular to add tracing and timing capabilities).</p>
+<div class="section" id="motivation">
+<h1><a name="motivation">Motivation</a></h1>
+<p>Attribute descriptors are a recent idea (they where first introduced in
+Python 2.2) nevertheless, under the hood, are everywhere in Python. It is
+a tribute to Guido's ability of hiding Python complications that
+the average user can easily miss they existence.
+If you need to do simple things, you can very well live without
+the knowledge of descriptors. On the other hand, if you need difficult
+things (such as tracing all the attribute access of your modules)
+attribute descriptors, allow you to perform
+impressive things.
+Let me start by showing why the knowledge of attribute descriptors is
+essential for any user seriously interested in metaprogramming applications.
+Suppose I want to trace the methods of a clock:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; clock=oopp.Clock()
+</pre>
+</blockquote>
+<p>This is easily done with the <tt class="literal"><span class="pre">with_tracer</span></tt> closure of chapter 2:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; oopp.wrapfunctions(clock,oopp.with_tracer)
+&lt;oopp.Clock object at 0x4044c54c&gt;
+&gt;&gt;&gt; clock.get_time()
+[] Calling 'get_time' with arguments
+(){} ...
+-&gt; '.get_time' called with result: 19:55:07
+'19:55:07'
+</pre>
+</blockquote>
+<p>However, this approach fails if I try to trace the entire class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; oopp.wrapfunctions(oopp.Clock,oopp.with_tracer)
+&lt;class 'oopp.Clock'&gt;
+&gt;&gt;&gt; oopp.Clock.get_time() # error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 6, in ?
+TypeError: unbound method _() must be called with Clock instance
+as first argument (got nothing instead)
+</pre>
+</blockquote>
+<p>The reason is that <tt class="literal"><span class="pre">wrapfunctions</span></tt> sets the attributes of 'Clock'
+by invoking <tt class="literal"><span class="pre">customize</span></tt>, which uses <tt class="literal"><span class="pre">setattr</span></tt>. This converts
+'_' (i.e. the traced version of <tt class="literal"><span class="pre">get_time</span></tt>) in a regular method, not in
+a staticmethod!
+In order to trace staticmethods, one has to understand the nature
+of attribute descriptors.</p>
+</div>
+<div class="section" id="functions-versus-methods">
+<h1><a name="functions-versus-methods">Functions versus methods</a></h1>
+<p>Attribute descriptors are essential for the implementation
+of one of the most basic Python features: the automatic conversion
+of functions in methods. As I already anticipated in chapter 1, there is
+a sort of magic when one writes <tt class="literal"><span class="pre">Clock.get_time=lambda</span> <span class="pre">self:</span> <span class="pre">get_time()</span></tt>
+and Python automagically converts the right hand side, that is a
+function, to a left hand side that is a (unbound) method. In order to
+understand this magic, one needs a better comprehension of the
+relation between functions and methods.
+Actually, this relationship is quite subtle
+and has no analogous in mainstream programming languages.
+For instance, C is not OOP and has only functions, lacking the concept
+of method, whereas Java (as other OOP languages)
+has no functions, only methods.
+C++ has functions and methods, but functions are completely
+different from methods On the other hand, in Python,
+functions and methods can be transformed both ways.</p>
+<p>To show how it works, let me start by defining a simple printing
+function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import __main__ # gives access to the __main__ namespace from the module
+
+def prn(s):
+ &quot;&quot;&quot;Given an evaluable string, print its value and its object reference.
+ Notice that the evaluation is done in the __main__ dictionary.&quot;&quot;&quot;
+ try: obj=eval(s,__main__.__dict__)
+ except: print 'problems in evaluating',s
+ else: print s,'=',obj,'at',hex(id(obj))
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me define a class with a method <tt class="literal"><span class="pre">m</span></tt> equals to the identity
+function <tt class="literal"><span class="pre">f</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): &quot;Identity function&quot;; return x
+...
+&gt;&gt;&gt; class C(object):
+... m=f
+... print m #here m is the function f
+&lt;function f at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>We see that <em>inside</em> its defining class, <tt class="literal"><span class="pre">m</span></tt> coincides with the function
+<tt class="literal"><span class="pre">f</span></tt> (the object reference is the same):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f
+&lt;function f at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>We may retrieve <tt class="literal"><span class="pre">m</span></tt> from <em>outside</em> the class via the class dictionary <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__['m']
+&lt;function prn at 0x401c2b1c&gt;
+</pre>
+</blockquote>
+<p>However, if we invoke <tt class="literal"><span class="pre">m</span></tt> with
+the syntax <tt class="literal"><span class="pre">C.m</span></tt>, then it (magically) becomes a (unbound) method:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m #here m has become a method!
+&lt;unbound method C.f&gt;
+</pre>
+</blockquote>
+<p>But why it is so? How comes that in the second syntax the function
+<tt class="literal"><span class="pre">f</span></tt> is transformed in a (unbound) method? To answer that question, we have
+to understand how attributes are really invoked in Python, i.e. via
+attribute descriptors.</p>
+</div>
+<div class="section" id="methods-versus-functions">
+<h1><a name="methods-versus-functions">Methods versus functions</a></h1>
+<p>First of all, let me point out the differences between methods and
+functions. Here, <tt class="literal"><span class="pre">C.m</span></tt> does <em>not</em> coincides with <tt class="literal"><span class="pre">C.__dict__['m']</span></tt>
+i.e. <tt class="literal"><span class="pre">f</span></tt>, since its object reference is different:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import prn,attributes
+&gt;&gt;&gt; prn('C.m')
+C.m = &lt;unbound method C.prn&gt; at 0x81109b4
+</pre>
+</blockquote>
+<p>The difference is clear since methods and functions have different attributes:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; attributes(f).keys()
+['func_closure', 'func_dict', 'func_defaults', 'func_name',
+'func_code', 'func_doc', 'func_globals']
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; attributes(C.m).keys()
+['im_func', 'im_class', 'im_self']
+</pre>
+</blockquote>
+<p>We discussed few of the functions attributes in the chapter
+on functions. The instance method attributes are simpler: <tt class="literal"><span class="pre">im_self</span></tt>
+returns the object to which the method is attached,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C.m.im_self #unbound method, attached to the class
+None
+&gt;&gt;&gt; C().m.im_self #bound method, attached to C()
+&lt;__main__.C object at 0x81bf4ec&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">im_class</span></tt> returns the class to which the
+method is attached</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.im_class #class of the unbound method
+&lt;class '__main__.C'&gt;
+&gt;&gt;&gt; C().m.im_class #class of the bound method,
+&lt;class '__main__.C'&gt;
+</pre>
+</blockquote>
+<p>and <tt class="literal"><span class="pre">im_func</span></tt> returns the function equivalent to
+the method.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.im_func
+&lt;function m at 0x8157f44&gt;
+&gt;&gt;&gt; C().m.im_func # the same
+&lt;function m at 0x8157f44&gt;
+</pre>
+</blockquote>
+<p>As the reference manual states, calling
+<tt class="literal"><span class="pre">m(*args,**kw)</span></tt> is completely equivalent to calling
+<tt class="literal"><span class="pre">m.im_func(m.im_self,</span> <span class="pre">*args,**kw)</span></tt>&quot;.</p>
+<p>As a general rule, an attribute descriptor is an object with a <tt class="literal"><span class="pre">__get__</span></tt>
+special method. The most used descriptors are the good old functions:
+they have a <tt class="literal"><span class="pre">__get__</span></tt> special method returning a <em>method-wrapper object</em></p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__
+&lt;method-wrapper object at 0x815cdc4&gt;
+</pre>
+</blockquote>
+<p>method-wrapper objects can be transformed in (both bound and unbound) methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; f.__get__(None,C)
+&lt;unbound method C.f&gt;
+&gt;&gt;&gt; f.__get__(C(),C)
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>The general calling syntax for method-wrapper objects is
+<tt class="literal"><span class="pre">.__get__(obj,cls=None)</span></tt>, where the first argument is an
+instance object or None and the second (optional) argument is the class (or a
+generic superclass) of the first one.</p>
+<p>Now we see what happens when we use the syntax <tt class="literal"><span class="pre">C.m</span></tt>: Python interprets
+this as a shortcut for <tt class="literal"><span class="pre">C.__dict['m'].__get__(None,C)</span></tt> (if <tt class="literal"><span class="pre">m</span></tt> is
+in the 'C' dictionary, otherwise it looks for ancestor dictionaries).
+We may check that everything is correct by observing that
+<tt class="literal"><span class="pre">f.__get__(None,C)</span></tt> has exactly the same object reference than <tt class="literal"><span class="pre">C.m</span></tt>,
+therefore they are the same object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; hex(id(f.__get__(None,C))) # same as hex(id(C.m))
+'0x811095c'
+</pre>
+</blockquote>
+<p>The process works equally well for the syntax <tt class="literal"><span class="pre">getattr</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print getattr(C,'m'), hex(id(getattr(C,'m')))
+&lt;unbound method C.f&gt; 0x811095c
+</pre>
+</blockquote>
+<p>and for bound methods: if</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>is an instance of the class C, then the syntax</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; getattr(c,'m') #same as c.m
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>is a shortcut for</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(c).__dict__['m'].__get__(c,C) # or f.__get__(c,C)
+&lt;bound method C.f of &lt;__main__.C object at 0x815cdc4&gt;&gt;
+</pre>
+</blockquote>
+<p>(notice that the object reference for <tt class="literal"><span class="pre">c.m</span></tt> and <tt class="literal"><span class="pre">f.__get__(c,C)</span></tt> is
+the same, they are <em>exactly</em> the same object).</p>
+<p>Both the unbound method C.m and the bound method c.m refer to the same
+object at hexadecimal address 0x811095c. This object is common to all other
+instances of C:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c2=C()
+&gt;&gt;&gt; print c2.m,hex(id(c2.m)) #always the same method
+&lt;bound method C.m of &lt;__main__.C object at 0x815768c&gt;&gt; 0x811095c
+</pre>
+</blockquote>
+<p>One can also omit the second argument:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.m.__get__(c)
+&lt;bound method ?.m of &lt;__main__.C object at 0x81597dc&gt;&gt;
+</pre>
+</blockquote>
+<p>Finally, let me point out that methods are attribute descriptors too,
+since they have a <tt class="literal"><span class="pre">__get__</span></tt> attribute returning a method-wrapper
+object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.m.__get__
+&lt;method-wrapper object at 0x815d51c&gt;
+</pre>
+</blockquote>
+<p>Notice that this method wrapper is <em>not</em> the same than the <tt class="literal"><span class="pre">f.__get__</span></tt>
+method wrapper.</p>
+<blockquote>
+<table class="footnote" frame="void" id="id2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>If <tt class="literal"><span class="pre">C.__dict['m']</span></tt> is not defined, Python looks if <tt class="literal"><span class="pre">m</span></tt> is defined
+in some ancestor of C. For instance if <cite>B</cite> is the base of <cite>C</cite>, it
+looks in <tt class="literal"><span class="pre">B.__dict['m']</span></tt>, etc., by following the MRO.</td></tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+<div class="section" id="static-methods-and-class-methods">
+<h1><a name="static-methods-and-class-methods">Static methods and class methods</a></h1>
+<p>Whereas functions and methods are implicit attribute descriptors,
+static methods and class methods are examples of explicit
+descriptors. They allow to convert regular functions to
+specific descriptor objects. Let me show a trivial example.
+Given the identity function</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): return x
+</pre>
+</blockquote>
+<p>we may convert it to a staticmethod object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm=staticmethod(f)
+&gt;&gt;&gt; sm
+&lt;staticmethod object at 0x4018a0a0&gt;
+</pre>
+</blockquote>
+<p>or to a classmethod object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm=classmethod(f)
+&gt;&gt;&gt; cm
+&lt;classmethod object at 0x4018a0b0&gt;
+</pre>
+</blockquote>
+<p>In both cases the <tt class="literal"><span class="pre">__get__</span></tt> special method returns a method-wrapper object</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm.__get__
+&lt;method-wrapper object at 0x401751ec&gt;
+&gt;&gt;&gt; cm.__get__
+&lt;method-wrapper object at 0x4017524c&gt;
+</pre>
+</blockquote>
+<p>However the static method wrapper is quite different from the class
+method wrapper. In the first case the wrapper returns a function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; sm.__get__(C(),C)
+&lt;function f at 0x4027a8b4&gt;
+&gt;&gt;&gt; sm.__get__(C())
+&lt;function f at 0x4027a8b4&gt;
+</pre>
+</blockquote>
+<p>in the second case it returns a method</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__(C(),C)
+&lt;bound method type.f of &lt;class '__main__.C'&gt;&gt;
+</pre>
+</blockquote>
+<p>Let me discuss more in detail the static methods, first.</p>
+<p>It is always possible to extract the function from the static method
+via the syntaxes <tt class="literal"><span class="pre">sm.__get__(a)</span></tt> and <tt class="literal"><span class="pre">sm.__get__(a,b)</span></tt> with <em>ANY</em> valid
+a and b, i.e. the result does not depend on a and b. This is correct,
+since static methods are actually function that have nothing to do
+with the class and the instances to which they are bound.</p>
+<p>This behaviour of the method wrapper makes clear why the relation between
+methods and functions is inversed for static methods with respect to
+regular methods:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... s=staticmethod(lambda : None)
+... print s
+...
+&lt;staticmethod object at 0x8158ec8&gt;
+</pre>
+</blockquote>
+<p>Static methods are non-trivial objects <em>inside</em> the class, whereas
+they are regular functions <em>outside</em> the class:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.s
+&lt;function &lt;lambda&gt; at 0x8158e7c&gt;
+&gt;&gt;&gt; C().s
+&lt;function &lt;lambda&gt; at 0x8158e7c&gt;
+</pre>
+</blockquote>
+<p>The situation is different for classmethods: inside the class they
+are non-trivial objects, just as static methods,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... cm=classmethod(lambda cls: None)
+... print cm
+...
+&lt;classmethod object at 0x8156100&gt;
+</pre>
+</blockquote>
+<p>but outside the class they are methods bound to the class,</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; prn('c.cm')
+&lt;bound method type.&lt;lambda&gt; of &lt;class '__main__.C'&gt;&gt;
+0x811095c
+</pre>
+</blockquote>
+<p>and not to the instance 'c'. The reason is that the <tt class="literal"><span class="pre">__get__</span></tt> wrapper method
+can be invoked with the syntax <tt class="literal"><span class="pre">__get__(a,cls)</span></tt> which
+is only sensitive to the second argument or with the syntax
+<tt class="literal"><span class="pre">__get__(obj)</span></tt> which is only sensitive to the type of the first
+argument:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever',C) # the first argument is ignored
+&lt;bound method type.f of &lt;class '__main__.C'&gt;&gt;
+</pre>
+</blockquote>
+<p>sensitive to the type of 'whatever':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever') # in Python 2.2 would give a serious error
+&lt;bound method type.f of &lt;type 'str'&gt;&gt;
+</pre>
+</blockquote>
+<p>Notice that the class method is actually bound to C's class, i.e.
+to 'type'.</p>
+<p>Just as regular methods (and differently
+from static methods) classmethods have attributes <tt class="literal"><span class="pre">im_class</span></tt>, <tt class="literal"><span class="pre">im_func</span></tt>,
+and <tt class="literal"><span class="pre">im_self</span></tt>. In particular one can retrieve the function wrapped inside
+the classmethod with</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; cm.__get__('whatever','whatever').im_func
+&lt;function f at 0x402c2534&gt;
+</pre>
+</blockquote>
+<p>The difference with regular methods is that <tt class="literal"><span class="pre">im_class</span></tt> returns the
+class of 'C' whereas <tt class="literal"><span class="pre">im_self</span></tt> returns 'C' itself.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.cm.im_self # a classmethod is attached to the class
+&lt;class '__main__.C'&gt;
+&gt;&gt;&gt; C.cm.im_class #the class of C
+&lt;type 'type'&gt;
+</pre>
+</blockquote>
+<p>Remark: Python 2.2.0 has a bug in classmethods (fixed in newer versions):
+when the first argument of __get__ is None, then one must specify
+the second argument (otherwise segmentation fault :-()</p>
+</div>
+<div class="section" id="properties">
+<h1><a name="properties">Properties</a></h1>
+<p>Properties are a more general kind of attribute descriptors than
+staticmethods and classmethods, since their effect can be customized
+trough arbitrary get/set/del functions. Let me give an example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def getp(self): return 'property' # get function
+...
+&gt;&gt;&gt; p=property(getp) # property object
+&gt;&gt;&gt; p
+&lt;property object at 0x815855c&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">p</span></tt> has a <tt class="literal"><span class="pre">__get__</span></tt> special method returning a method-wrapper
+object, just as it happens for other descriptors:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; p.__get__
+&lt;method-wrapper object at 0x8158a7c&gt;
+</pre>
+</blockquote>
+<p>The difference is that</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; p.__get__(None,type(p))
+&lt;property object at 0x4017016c&gt;
+&gt;&gt;&gt; p.__get__('whatever')
+'property'
+&gt;&gt;&gt; p.__get__('whatever','whatever')
+'property'
+</pre>
+</blockquote>
+<p>As for static methods, the <tt class="literal"><span class="pre">__get__</span></tt> method wrapper is independent from
+its arguments, unless the first one is None: in such a case it returns
+the property object, in all other circumstances it returns the result
+of <tt class="literal"><span class="pre">getp</span></tt>. This explains the behavior</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object): p=p
+&gt;&gt;&gt; C.p
+&lt;property object at 0x815855c&gt;
+&gt;&gt;&gt; C().p
+'property'
+</pre>
+</blockquote>
+<p>Properties are a dangerous feature, since they change the semantics
+of the language. This means that apparently trivial operations can have
+any kind of side effects:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def get(self):return 'You gave me the order to destroy your hard disk!!'
+&gt;&gt;&gt; class C(object): x=property(get)
+&gt;&gt;&gt; C().x
+'You gave me the order to destroy your hard disk!!'
+</pre>
+</blockquote>
+<p>Invoking 'C.x' could very well invoke an external program who is going
+to do anything! It is up to the programmer to not abuse properties.
+The same is true for user defined attribute descriptors.</p>
+<p>There are situations in which they are quite handy, however. For
+instance, properties can be used to trace the access data attributes.
+This can be especially useful during debugging, or for logging
+purposes.</p>
+<p>Notice that this approach has the problem that now data attributes cannot
+no more be called trough their class, but only though their instances.
+Moreover properties do not work well with <tt class="literal"><span class="pre">super</span></tt> in cooperative
+methods.</p>
+</div>
+<div class="section" id="user-defined-attribute-descriptors">
+<h1><a name="user-defined-attribute-descriptors">User-defined attribute descriptors</a></h1>
+<p>As we have seen, there are plenty of predefined attribute descriptors,
+such as staticmethods, classmethods and properties (the built-in
+<tt class="literal"><span class="pre">super</span></tt> is also an attribute descriptor which, for sake of
+convenience, will be discussed in the next section).
+In addition to them, the user can also define customized attribute
+descriptors, simply trough classes with a <tt class="literal"><span class="pre">__get__</span></tt> special method.
+Let me give an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;simpledescr.py&gt;
+
+class ChattyAttr(object):
+ &quot;&quot;&quot;Chatty descriptor class; descriptor objects are intended to be
+ used as attributes in other classes&quot;&quot;&quot;
+ def __get__(self, obj, cls=None):
+ binding=obj is not None
+ if binding:
+ return 'You are binding %s to %s' % (self,obj)
+ else:
+ return 'Calling %s from %s' % (self,cls)
+
+class C(object):
+ d=ChattyAttr()
+
+c=C()
+
+print c.d # &lt;=&gt; type(c).__dict__['d'].__get__(c,type(c))
+print C.d # &lt;=&gt; C.__dict__['d'].__get__(None,C)
+
+#&lt;/simpledescr.py&gt;
+</pre>
+</blockquote>
+<p>with output:</p>
+<blockquote>
+<pre class="literal-block">
+You are binding &lt;ChattyAttr object at 0x401bc1cc&gt; to
+&lt;C object at 0x401bc2ec&gt;
+Calling &lt;ChattyAttr object at 0x401bc1cc&gt; from &lt;class 'C'&gt;
+</pre>
+</blockquote>
+<p>Invoking a method with the syntax <tt class="literal"><span class="pre">C.d</span></tt> or <tt class="literal"><span class="pre">c.d</span></tt> involves calling
+<tt class="literal"><span class="pre">__get__</span></tt>. The <tt class="literal"><span class="pre">__get__</span></tt> signature is fixed: it is
+`` __get__=__get__(self,obj,cls=None)``, since the notation
+<tt class="literal"><span class="pre">self.descr_attr</span></tt> automatically passes <tt class="literal"><span class="pre">self</span></tt> and <tt class="literal"><span class="pre">self.__class__</span></tt> to
+<tt class="literal"><span class="pre">__get__</span></tt>.</p>
+<p>Custom descriptors can be used to restrict the access to objects in a
+more general way than trough properties. For instance, suppose one
+wants to raise an error if a given attribute 'a' is accessed, both
+from the class and from the instance: a property cannot help here,
+since it works only from the instance. The solution is the following
+custom descriptor:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class AccessError(object):
+ &quot;&quot;&quot;Descriptor raising an AttributeError when the attribute is
+ accessed&quot;&quot;&quot; #could be done with a property
+ def __init__(self,errormessage):
+ self.msg=errormessage
+ def __get__(self,obj,cls=None):
+ raise AttributeError(self.msg)
+
+#&lt;/oopp.py&gt;
+
+&gt;&gt;&gt; from oopp import AccessError
+&gt;&gt;&gt; class C(object):
+... a=AccessError(&quot;'a' cannot be accessed&quot;)
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.a #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 313, in __get__
+ raise AttributeError(self.msg)
+AttributeError: 'a' cannot be accessed
+&gt;&gt;&gt; C.a #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 313, in __get__
+ raise AttributeError(self.msg)
+AttributeError: 'a' cannot be accessed
+</pre>
+</blockquote>
+<p>It is always possibile to convert plain attributes (i.e. attributes
+without a &quot;__get__&quot; method) to descriptor objects:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class convert2descriptor(object):
+ &quot;&quot;&quot;To all practical means, this class acts as a function that, given an
+ object, adds to it a __get__ method if it is not already there. The
+ added __get__ method is trivial and simply returns the original object,
+ independently from obj and cls.&quot;&quot;&quot;
+ def __new__(cls,a):
+ if hasattr(a,&quot;__get__&quot;): # do nothing
+ return a # a is already a descriptor
+ else: # creates a trivial attribute descriptor
+ cls.a=a
+ return object.__new__(cls)
+ def __get__(self,obj,cls=None):
+ &quot;Returns self.a independently from obj and cls&quot;
+ return self.a
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This example also shows the magic of <tt class="literal"><span class="pre">__new__</span></tt>, that allows to use a
+class as a function. The output of 'convert2descriptor(a)' can be both
+an instance of 'convert2descriptor' (in this case 'convert2descriptor' acts as
+a normal class, i.e. as an object factory) or 'a' itself
+(if 'a' is already a descriptor): in this case 'convert2descriptor' acts
+as a function.</p>
+<p>For instance, a string is converted to a descriptor</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import convert2descriptor
+&gt;&gt;&gt; a2=convert2descriptor('a')
+&gt;&gt;&gt; a2
+&lt;oopp.convert2descriptor object at 0x4017506c&gt;
+&gt;&gt;&gt; a2.__get__('whatever')
+'a'
+</pre>
+</blockquote>
+<p>whereas a function is untouched:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(): pass
+&gt;&gt;&gt; f2=convert2descriptor(f) # does nothing
+&gt;&gt;&gt; f2
+&lt;function f at 0x4019110c&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="data-descriptors">
+<h1><a name="data-descriptors">Data descriptors</a></h1>
+<p>It is also possible to specify a <tt class="literal"><span class="pre">__set__</span></tt> method (descriptors
+with a <tt class="literal"><span class="pre">__set__</span></tt> method are typically data descriptors) with
+the signature <tt class="literal"><span class="pre">__set__(self,obj,value)</span></tt> as in the following
+example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;datadescr.py&gt;
+
+class DataDescriptor(object):
+ value=None
+ def __get__(self, obj, cls=None):
+ if obj is None: obj=cls
+ print &quot;Getting&quot;,obj,&quot;value =&quot;,self.value
+ return self.value
+ def __set__(self, obj, value):
+ self.value=value
+ print &quot;Setting&quot;,obj,&quot;value =&quot;,value
+
+class C(object):
+ d=DataDescriptor()
+
+c=C()
+
+c.d=1 #calls C.__dict__['d'].__set__(c,1)
+c.d #calls C.__dict__['d'].__get__(c,C)
+C.d #calls C.__dict__['d'].__get__(None,C)
+C.d=0 #does *not* call __set__
+print &quot;C.d =&quot;,C.d
+
+#&lt;/datadescr.py&gt;
+</pre>
+</blockquote>
+<p>With output:</p>
+<blockquote>
+<pre class="literal-block">
+Setting &lt;C object at 0x401bc1ec&gt; value = 1
+Getting &lt;C object at 0x401bc42c&gt; value = 1
+Getting &lt;class 'C'&gt; value = 1
+C.d = 0
+</pre>
+</blockquote>
+<p>With this knowledge, we may now reconsider the clock example given
+in chapter 3. #NO!??</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import oopp
+&gt;&gt;&gt; class Clock(object): pass
+&gt;&gt;&gt; myclock=Clock()
+...
+&gt;&gt;&gt; myclock.get_time=oopp.get_time # this is a function
+&gt;&gt;&gt; Clock.get_time=lambda self : oopp.get_time() # this is a method
+</pre>
+</blockquote>
+<p>In this example, <tt class="literal"><span class="pre">myclock.get_time</span></tt>, which is attached to the <tt class="literal"><span class="pre">myclock</span></tt>
+object, is a function, whereas <tt class="literal"><span class="pre">Clock.get_time</span></tt>, which is attached to
+the <tt class="literal"><span class="pre">Clock</span></tt> class is a method. We may also check this by using the <tt class="literal"><span class="pre">type</span></tt>
+function:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(myclock.get_time)
+&lt;type 'function'&gt;
+</pre>
+</blockquote>
+<p>whereas</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(Clock.get_time)
+&lt;type 'instance method'&gt;
+</pre>
+</blockquote>
+<p>It must be remarked that user-defined attribute descriptors, just as
+properties, allow to arbitrarily change the semantics of the language
+and should be used with care.</p>
+</div>
+<div class="section" id="the-super-attribute-descriptor">
+<h1><a name="the-super-attribute-descriptor">The <tt class="literal"><span class="pre">super</span></tt> attribute descriptor</a></h1>
+<p>super has also a second form, where it is more used as a descriptor.</p>
+<p><tt class="literal"><span class="pre">super</span></tt> objects are attribute descriptors, too, with a <tt class="literal"><span class="pre">__get__</span></tt>
+method returning a method-wrapper object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C()).__get__
+&lt;method-wrapper object at 0x8161074&gt;
+</pre>
+</blockquote>
+<p>Here I give some example of acceptable call:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C()).__get__('whatever')
+&lt;super: &lt;class 'C'&gt;, &lt;C object&gt;&gt;
+&gt;&gt;&gt; super(C,C()).__get__('whatever','whatever')
+&lt;super: &lt;class 'C'&gt;, &lt;C object&gt;&gt;
+</pre>
+</blockquote>
+<p>Unfortunately, for the time being
+(i.e. for Python 2.3), the <tt class="literal"><span class="pre">super</span></tt> mechanism has various limitations.
+To show the issues, let me start by considering the following base class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class ExampleBaseClass(PrettyPrinted):
+ &quot;&quot;&quot;Contains a regular method 'm', a staticmethod 's', a classmethod
+ 'c', a property 'p' and a data attribute 'd'.&quot;&quot;&quot;
+ m=lambda self: 'regular method of %s' % self
+ s=staticmethod(lambda : 'staticmethod')
+ c=classmethod(lambda cls: 'classmethod of %s' % cls)
+ p=property(lambda self: 'property of %s' % self)
+ a=AccessError('Expected error')
+ d='data'
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Now, let me derive a new class C from ExampleBaseClass:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import ExampleBaseClass
+&gt;&gt;&gt; class C(ExampleBaseClass): pass
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>Ideally, we would like to retrieve the methods and attributes of
+ExampleBaseClass from C, by using the <tt class="literal"><span class="pre">super</span></tt> mechanism.</p>
+<ol class="arabic simple">
+<li>We see that <tt class="literal"><span class="pre">super</span></tt> works without problems for regular methods,
+staticmethods and classmethods:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).m()
+'regular method of &lt;C&gt;'
+&gt;&gt;&gt; super(C,c).s()
+'staticmethod'
+&gt;&gt;&gt; super(C,c).c()
+&quot;classmethod of &lt;class '__main__.C'&gt;&quot;
+</pre>
+</blockquote>
+<p>It also works for user defined attribute descriptors:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).a # access error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+ File &quot;oopp.py&quot;, line 340, in __get__
+ raise AttributeError(self.msg)
+AttributeError: Expected error
+</pre>
+</blockquote>
+<p>and for properties (only for Python 2.3+):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ExampleBaseClass.p
+&lt;property object at 0x81b30fc&gt;
+</pre>
+</blockquote>
+<p>In Python 2.2 one would get an error, instead</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).p #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute 'p'
+</pre>
+</blockquote>
+<p>3. Moreover, certain attributes of the superclass, such as its
+<tt class="literal"><span class="pre">__name__</span></tt>, cannot be retrieved:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; ExampleBaseClass.__name__
+'ExampleBaseClass'
+&gt;&gt;&gt; super(C,c).__name__ #error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+</pre>
+</blockquote>
+<ol class="arabic simple" start="4">
+<li>There is no direct way to retrieve the methods of the super-superclass
+(i.e. the grandmother class, if you wish) or in general the furthest
+ancestors, since <tt class="literal"><span class="pre">super</span></tt> does not chain.</li>
+<li>Finally, there are some subtle issues with the <tt class="literal"><span class="pre">super(cls)</span></tt> syntax:</li>
+</ol>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C).m #(2) error
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute 'm'
+</pre>
+</blockquote>
+<p>means <tt class="literal"><span class="pre">super(C).__get__(None,C)</span></tt>, but only
+<tt class="literal"><span class="pre">super(C).__get__(c,C).m==super(C,c)</span></tt> works.</p>
+<blockquote>
+<blockquote>
+On the other hand,</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C).__init__ #(1)
+&lt;built-in method __init__ of type object at 0x80e6fc0&gt;
+&gt;&gt;&gt; super(C).__new__ #(1)
+&lt;built-in method __init__ of type object at 0x80e6fc0&gt;
+</pre>
+<blockquote>
+seems to work, whereas in reality does not. The reason is that since
+<tt class="literal"><span class="pre">super</span></tt> objects are instances
+of <tt class="literal"><span class="pre">object</span></tt>, they inherit object's methods, and in particular
+<tt class="literal"><span class="pre">__init__</span></tt> ; therefore the <tt class="literal"><span class="pre">__init__</span></tt> method in (1) is <em>not</em>
+the <tt class="literal"><span class="pre">ExampleBaseClass.__init__</span></tt> method. The point is that <tt class="literal"><span class="pre">super</span></tt>
+objects are attribute descriptors and not references to the superclass.</blockquote>
+</blockquote>
+<p>Probably, in future versions of Python the <tt class="literal"><span class="pre">super</span></tt> mechanism will be
+improved. However, for the time being, one must provide a workaround for
+dealing with these issues. This will be discussed in the next chapter.</p>
+</div>
+<div class="section" id="method-wrappers">
+<h1><a name="method-wrappers">Method wrappers</a></h1>
+<p>One of the most typical applications of attribute descriptors is their
+usage as <em>method wrappers</em>.</p>
+<p>Suppose, for instance, one wants to add tracing capabilities to
+the methods of a class for debugging purposes. The problem
+can be solved with a custom descriptor class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+import inspect
+
+class wrappedmethod(Customizable):
+ &quot;&quot;&quot;Customizable method factory intended for derivation.
+ The wrapper method is overridden in the children.&quot;&quot;&quot;
+
+ logfile=sys.stdout # default
+ namespace='' # default
+
+ def __new__(cls,meth): # meth is a descriptor
+ if isinstance(meth,FunctionType):
+ kind=0 # regular method
+ func=meth
+ elif isinstance(meth,staticmethod):
+ kind=1 # static method
+ func=meth.__get__('whatever')
+ elif isinstance(meth,classmethod):
+ kind=2 # class method
+ func=meth.__get__('whatever','whatever').im_func
+ elif isinstance(meth,wrappedmethod): # already wrapped
+ return meth # do nothing
+ elif inspect.ismethoddescriptor(meth):
+ kind=0; func=meth # for many builtin methods
+ else:
+ return meth # do nothing
+ self=super(wrappedmethod,cls).__new__(cls)
+ self.kind=kind; self.func=func # pre-initialize
+ return self
+
+ def __init__(self,meth): # meth not used
+ self.logfile=self.logfile # default values
+ self.namespace=self.namespace # copy the current
+
+ def __get__(self,obj,cls): # closure
+ def _(*args,**kw):
+ if obj is None: o=() # unbound method call
+ else: o=(obj,) # bound method call
+ allargs=[o,(),(cls,)][self.kind]+args
+ return self.wrapper()(*allargs,**kw)
+ return _ # the wrapped function
+ # allargs is the only nontrivial line in _; it adds
+ # 0 - obj if meth is a regular method
+ # 1 - nothing if meth is a static method
+ # 2 - cls if meth is a class method
+
+ def wrapper(self): return self.func # do nothing, to be overridden
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>This class is intended for derivation: the wrapper method has to be overridden
+in the children in order to introduce the wanted feature. If I want to
+implement the capability of tracing methods, I can reuse the <tt class="literal"><span class="pre">with_tracer</span></tt>
+closure introduced in chapter 2:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class tracedmethod(wrappedmethod):
+ def wrapper(self):
+ return with_tracer(self.func,self.namespace,self.logfile)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Nothing prevents me from introducing timing features by reusing the
+<tt class="literal"><span class="pre">with_timer</span></tt> closure:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+class timedmethod(wrappedmethod):
+ iterations=1 # additional default parameter
+
+ def __init__(self,meth):
+ super(timedmethod,self).__init__(self,meth)
+ self.iterations=self.iterations # copy
+
+ def wrapper(self):
+ return with_timer(self.func,self.namespace,
+ self.iterations,self.logfile)
+
+#&lt;/oopp.py&gt;
+</pre>
+</blockquote>
+<p>Here there is an example of usage:</p>
+<p>The dictionary of wrapped functions is then built from an utility function</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;oopp.py&gt;
+
+def wrap(obj,wrapped,condition=lambda k,v: True, err=None):
+ &quot;Retrieves obj's dictionary and wraps it&quot;
+ if isinstance(obj,dict): # obj is a dictionary
+ dic=obj
+ else:
+ dic=getattr(obj,'__dict__',{}).copy() # avoids dictproxy objects
+ if not dic: dic=attributes(obj) # for simple objects
+ wrapped.namespace=getattr(obj,'__name__','')
+ for name,attr in dic.iteritems(): # modify dic
+ if condition(name,attr): dic[name]=wrapped(attr)
+ if not isinstance(obj,dict): # modify obj
+ customize(obj,err,**dic)
+
+#&lt;/oopp.py&gt;
+</pre>
+<pre class="literal-block">
+#&lt;tracingmethods.py&gt;
+
+from oopp import *
+
+class C(object):
+ &quot;Class with traced methods&quot;
+
+ def f(self): return self
+ f=tracedmethod(f)
+
+ g=staticmethod(lambda:None)
+ g=tracedmethod(g)
+
+ h=classmethod(do_nothing)
+ h=tracedmethod(h)
+
+c=C()
+
+#unbound calls
+C.f(c)
+C.g()
+C.h()
+
+#bound calls
+c.f()
+c.g()
+c.h()
+
+#&lt;/tracingmethods.py&gt;
+</pre>
+</blockquote>
+<p>Output:</p>
+<blockquote>
+<pre class="literal-block">
+[C] Calling 'f' with arguments
+(&lt;C object at 0x402042cc&gt;,){} ...
+-&gt; 'C.f' called with result: &lt;C object at 0x402042cc&gt;
+
+[C] Calling '&lt;lambda&gt;' with arguments
+(){} ...
+-&gt; 'C.&lt;lambda&gt;' called with result: None
+
+[C] Calling 'do_nothing' with arguments
+(&lt;class 'C'&gt;,){} ...
+-&gt; 'C.do_nothing' called with result: None
+
+[C] Calling 'f' with arguments
+(&lt;C object at 0x402042cc&gt;,){} ...
+-&gt; 'C.f' called with result: &lt;C object at 0x402042cc&gt;
+
+[C] Calling '&lt;lambda&gt;' with arguments
+(){} ...
+-&gt; 'C.&lt;lambda&gt;' called with result: None
+
+[C] Calling 'do_nothing' with arguments
+(&lt;class 'C'&gt;,){} ...
+-&gt; 'C.do_nothing' called with result: None
+</pre>
+</blockquote>
+<p>The approach in 'tracingmethods.py' works, but it is far from
+being elegant, since I had to explicitly wrap each method in the
+class by hand.</p>
+<p>Both problems can be avoided.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from oopp import *
+&gt;&gt;&gt; wrap(Clock,tracedmethod)
+&gt;&gt;&gt; Clock.get_time()
+[Clock] Calling 'get_time' with arguments
+(){} ...
+-&gt; 'Clock.get_time' called with result: 21:56:52
+'21:56:52'
+</pre>
+</blockquote>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="descr.txt">View document source</a>.
+Generated on: 2004-02-18 07:07 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/descr.txt b/pypers/descr.txt
new file mode 100755
index 0000000..e2b3b81
--- /dev/null
+++ b/pypers/descr.txt
@@ -0,0 +1,973 @@
+THE SOPHISTICATION OF DESCRIPTORS
+===========================================================================
+
+Attribute descriptors are important metaprogramming tools that allows
+the user to customize the behavior of attributes in custom classes.
+For instance, attribute descriptors (or descriptors for short)
+can be used as method wrappers,
+to modify or enhance methods (this is the case for the well
+known staticmethods and classmethods attribute descriptors); they
+can also be used as attribute wrappers, to change or restrict the access to
+attributes (this is the case for properties). Finally, descriptors
+allows the user to play with the resolution order of attributes:
+for instance, the ``super`` built-in object used in (multiple) inheritance
+hierarchies, is implemented as an attribute descriptor.
+
+In this chapter, I will show how the user can define its own attribute
+descriptors and I will give some example of useful things you can do with
+them (in particular to add tracing and timing capabilities).
+
+Motivation
+---------------------------------------------------------------------------
+Attribute descriptors are a recent idea (they where first introduced in
+Python 2.2) nevertheless, under the hood, are everywhere in Python. It is
+a tribute to Guido's ability of hiding Python complications that
+the average user can easily miss they existence.
+If you need to do simple things, you can very well live without
+the knowledge of descriptors. On the other hand, if you need difficult
+things (such as tracing all the attribute access of your modules)
+attribute descriptors, allow you to perform
+impressive things.
+Let me start by showing why the knowledge of attribute descriptors is
+essential for any user seriously interested in metaprogramming applications.
+Suppose I want to trace the methods of a clock:
+
+ >>> import oopp
+ >>> clock=oopp.Clock()
+
+This is easily done with the ``with_tracer`` closure of chapter 2:
+
+ >>> oopp.wrapfunctions(clock,oopp.with_tracer)
+ <oopp.Clock object at 0x4044c54c>
+ >>> clock.get_time()
+ [] Calling 'get_time' with arguments
+ (){} ...
+ -> '.get_time' called with result: 19:55:07
+ '19:55:07'
+
+However, this approach fails if I try to trace the entire class:
+
+ >>> oopp.wrapfunctions(oopp.Clock,oopp.with_tracer)
+ <class 'oopp.Clock'>
+ >>> oopp.Clock.get_time() # error
+ Traceback (most recent call last):
+ File "<stdin>", line 6, in ?
+ TypeError: unbound method _() must be called with Clock instance
+ as first argument (got nothing instead)
+
+The reason is that ``wrapfunctions`` sets the attributes of 'Clock'
+by invoking ``customize``, which uses ``setattr``. This converts
+'_' (i.e. the traced version of ``get_time``) in a regular method, not in
+a staticmethod!
+In order to trace staticmethods, one has to understand the nature
+of attribute descriptors.
+
+Functions versus methods
+----------------------------------------------------------------------
+
+Attribute descriptors are essential for the implementation
+of one of the most basic Python features: the automatic conversion
+of functions in methods. As I already anticipated in chapter 1, there is
+a sort of magic when one writes ``Clock.get_time=lambda self: get_time()``
+and Python automagically converts the right hand side, that is a
+function, to a left hand side that is a (unbound) method. In order to
+understand this magic, one needs a better comprehension of the
+relation between functions and methods.
+Actually, this relationship is quite subtle
+and has no analogous in mainstream programming languages.
+For instance, C is not OOP and has only functions, lacking the concept
+of method, whereas Java (as other OOP languages)
+has no functions, only methods.
+C++ has functions and methods, but functions are completely
+different from methods On the other hand, in Python,
+functions and methods can be transformed both ways.
+
+To show how it works, let me start by defining a simple printing
+function:
+
+ ::
+
+ #<oopp.py>
+
+ import __main__ # gives access to the __main__ namespace from the module
+
+ def prn(s):
+ """Given an evaluable string, print its value and its object reference.
+ Notice that the evaluation is done in the __main__ dictionary."""
+ try: obj=eval(s,__main__.__dict__)
+ except: print 'problems in evaluating',s
+ else: print s,'=',obj,'at',hex(id(obj))
+
+ #</oopp.py>
+
+Now, let me define a class with a method ``m`` equals to the identity
+function ``f``:
+
+ >>> def f(x): "Identity function"; return x
+ ...
+ >>> class C(object):
+ ... m=f
+ ... print m #here m is the function f
+ <function f at 0x401c2b1c>
+
+We see that *inside* its defining class, ``m`` coincides with the function
+``f`` (the object reference is the same):
+
+ >>> f
+ <function f at 0x401c2b1c>
+
+We may retrieve ``m`` from *outside* the class via the class dictionary [#]_:
+
+ >>> C.__dict__['m']
+ <function prn at 0x401c2b1c>
+
+However, if we invoke ``m`` with
+the syntax ``C.m``, then it (magically) becomes a (unbound) method:
+
+ >>> C.m #here m has become a method!
+ <unbound method C.f>
+
+But why it is so? How comes that in the second syntax the function
+``f`` is transformed in a (unbound) method? To answer that question, we have
+to understand how attributes are really invoked in Python, i.e. via
+attribute descriptors.
+
+Methods versus functions
+-----------------------------------------------------------------------------
+
+First of all, let me point out the differences between methods and
+functions. Here, ``C.m`` does *not* coincides with ``C.__dict__['m']``
+i.e. ``f``, since its object reference is different:
+
+ >>> from oopp import prn,attributes
+ >>> prn('C.m')
+ C.m = <unbound method C.prn> at 0x81109b4
+
+The difference is clear since methods and functions have different attributes:
+
+ >>> attributes(f).keys()
+ ['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']
+
+whereas
+
+ >>> attributes(C.m).keys()
+ ['im_func', 'im_class', 'im_self']
+
+We discussed few of the functions attributes in the chapter
+on functions. The instance method attributes are simpler: ``im_self``
+returns the object to which the method is attached,
+
+ >>> print C.m.im_self #unbound method, attached to the class
+ None
+ >>> C().m.im_self #bound method, attached to C()
+ <__main__.C object at 0x81bf4ec>
+
+``im_class`` returns the class to which the
+method is attached
+
+ >>> C.m.im_class #class of the unbound method
+ <class '__main__.C'>
+ >>> C().m.im_class #class of the bound method,
+ <class '__main__.C'>
+
+and ``im_func`` returns the function equivalent to
+the method.
+
+ >>> C.m.im_func
+ <function m at 0x8157f44>
+ >>> C().m.im_func # the same
+ <function m at 0x8157f44>
+
+As the reference manual states, calling
+``m(*args,**kw)`` is completely equivalent to calling
+``m.im_func(m.im_self, *args,**kw)``".
+
+As a general rule, an attribute descriptor is an object with a ``__get__``
+special method. The most used descriptors are the good old functions:
+they have a ``__get__`` special method returning a *method-wrapper object*
+
+ >>> f.__get__
+ <method-wrapper object at 0x815cdc4>
+
+method-wrapper objects can be transformed in (both bound and unbound) methods:
+
+ >>> f.__get__(None,C)
+ <unbound method C.f>
+ >>> f.__get__(C(),C)
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+The general calling syntax for method-wrapper objects is
+``.__get__(obj,cls=None)``, where the first argument is an
+instance object or None and the second (optional) argument is the class (or a
+generic superclass) of the first one.
+
+Now we see what happens when we use the syntax ``C.m``: Python interprets
+this as a shortcut for ``C.__dict['m'].__get__(None,C)`` (if ``m`` is
+in the 'C' dictionary, otherwise it looks for ancestor dictionaries).
+We may check that everything is correct by observing that
+``f.__get__(None,C)`` has exactly the same object reference than ``C.m``,
+therefore they are the same object:
+
+ >>> hex(id(f.__get__(None,C))) # same as hex(id(C.m))
+ '0x811095c'
+
+The process works equally well for the syntax ``getattr``:
+
+ >>> print getattr(C,'m'), hex(id(getattr(C,'m')))
+ <unbound method C.f> 0x811095c
+
+and for bound methods: if
+
+ >>> c=C()
+
+is an instance of the class C, then the syntax
+
+ >>> getattr(c,'m') #same as c.m
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+is a shortcut for
+
+ >>> type(c).__dict__['m'].__get__(c,C) # or f.__get__(c,C)
+ <bound method C.f of <__main__.C object at 0x815cdc4>>
+
+(notice that the object reference for ``c.m`` and ``f.__get__(c,C)`` is
+the same, they are *exactly* the same object).
+
+Both the unbound method C.m and the bound method c.m refer to the same
+object at hexadecimal address 0x811095c. This object is common to all other
+instances of C:
+
+ >>> c2=C()
+ >>> print c2.m,hex(id(c2.m)) #always the same method
+ <bound method C.m of <__main__.C object at 0x815768c>> 0x811095c
+
+One can also omit the second argument:
+
+ >>> c.m.__get__(c)
+ <bound method ?.m of <__main__.C object at 0x81597dc>>
+
+Finally, let me point out that methods are attribute descriptors too,
+since they have a ``__get__`` attribute returning a method-wrapper
+object:
+
+ >>> C.m.__get__
+ <method-wrapper object at 0x815d51c>
+
+Notice that this method wrapper is *not* the same than the ``f.__get__``
+method wrapper.
+
+ .. [#] If ``C.__dict['m']`` is not defined, Python looks if ``m`` is defined
+ in some ancestor of C. For instance if `B` is the base of `C`, it
+ looks in ``B.__dict['m']``, etc., by following the MRO.
+
+Static methods and class methods
+--------------------------------------------------------------------------
+
+Whereas functions and methods are implicit attribute descriptors,
+static methods and class methods are examples of explicit
+descriptors. They allow to convert regular functions to
+specific descriptor objects. Let me show a trivial example.
+Given the identity function
+
+ >>> def f(x): return x
+
+we may convert it to a staticmethod object
+
+ >>> sm=staticmethod(f)
+ >>> sm
+ <staticmethod object at 0x4018a0a0>
+
+or to a classmethod object
+
+ >>> cm=classmethod(f)
+ >>> cm
+ <classmethod object at 0x4018a0b0>
+
+In both cases the ``__get__`` special method returns a method-wrapper object
+
+ >>> sm.__get__
+ <method-wrapper object at 0x401751ec>
+ >>> cm.__get__
+ <method-wrapper object at 0x4017524c>
+
+However the static method wrapper is quite different from the class
+method wrapper. In the first case the wrapper returns a function:
+
+ >>> sm.__get__(C(),C)
+ <function f at 0x4027a8b4>
+ >>> sm.__get__(C())
+ <function f at 0x4027a8b4>
+
+in the second case it returns a method
+
+ >>> cm.__get__(C(),C)
+ <bound method type.f of <class '__main__.C'>>
+
+Let me discuss more in detail the static methods, first.
+
+It is always possible to extract the function from the static method
+via the syntaxes ``sm.__get__(a)`` and ``sm.__get__(a,b)`` with *ANY* valid
+a and b, i.e. the result does not depend on a and b. This is correct,
+since static methods are actually function that have nothing to do
+with the class and the instances to which they are bound.
+
+This behaviour of the method wrapper makes clear why the relation between
+methods and functions is inversed for static methods with respect to
+regular methods:
+
+ >>> class C(object):
+ ... s=staticmethod(lambda : None)
+ ... print s
+ ...
+ <staticmethod object at 0x8158ec8>
+
+Static methods are non-trivial objects *inside* the class, whereas
+they are regular functions *outside* the class:
+
+ >>> C.s
+ <function <lambda> at 0x8158e7c>
+ >>> C().s
+ <function <lambda> at 0x8158e7c>
+
+The situation is different for classmethods: inside the class they
+are non-trivial objects, just as static methods,
+
+ >>> class C(object):
+ ... cm=classmethod(lambda cls: None)
+ ... print cm
+ ...
+ <classmethod object at 0x8156100>
+
+but outside the class they are methods bound to the class,
+
+ >>> c=C()
+ >>> prn('c.cm')
+ <bound method type.<lambda> of <class '__main__.C'>>
+ 0x811095c
+
+and not to the instance 'c'. The reason is that the ``__get__`` wrapper method
+can be invoked with the syntax ``__get__(a,cls)`` which
+is only sensitive to the second argument or with the syntax
+``__get__(obj)`` which is only sensitive to the type of the first
+argument:
+
+ >>> cm.__get__('whatever',C) # the first argument is ignored
+ <bound method type.f of <class '__main__.C'>>
+
+sensitive to the type of 'whatever':
+
+ >>> cm.__get__('whatever') # in Python 2.2 would give a serious error
+ <bound method type.f of <type 'str'>>
+
+Notice that the class method is actually bound to C's class, i.e.
+to 'type'.
+
+Just as regular methods (and differently
+from static methods) classmethods have attributes ``im_class``, ``im_func``,
+and ``im_self``. In particular one can retrieve the function wrapped inside
+the classmethod with
+
+ >>> cm.__get__('whatever','whatever').im_func
+ <function f at 0x402c2534>
+
+The difference with regular methods is that ``im_class`` returns the
+class of 'C' whereas ``im_self`` returns 'C' itself.
+
+ >>> C.cm.im_self # a classmethod is attached to the class
+ <class '__main__.C'>
+ >>> C.cm.im_class #the class of C
+ <type 'type'>
+
+Remark: Python 2.2.0 has a bug in classmethods (fixed in newer versions):
+when the first argument of __get__ is None, then one must specify
+the second argument (otherwise segmentation fault :-()
+
+Properties
+----------------------------------------------------------------------
+
+Properties are a more general kind of attribute descriptors than
+staticmethods and classmethods, since their effect can be customized
+trough arbitrary get/set/del functions. Let me give an example:
+
+ >>> def getp(self): return 'property' # get function
+ ...
+ >>> p=property(getp) # property object
+ >>> p
+ <property object at 0x815855c>
+
+``p`` has a ``__get__`` special method returning a method-wrapper
+object, just as it happens for other descriptors:
+
+ >>> p.__get__
+ <method-wrapper object at 0x8158a7c>
+
+The difference is that
+
+ >>> p.__get__(None,type(p))
+ <property object at 0x4017016c>
+ >>> p.__get__('whatever')
+ 'property'
+ >>> p.__get__('whatever','whatever')
+ 'property'
+
+As for static methods, the ``__get__`` method wrapper is independent from
+its arguments, unless the first one is None: in such a case it returns
+the property object, in all other circumstances it returns the result
+of ``getp``. This explains the behavior
+
+ >>> class C(object): p=p
+ >>> C.p
+ <property object at 0x815855c>
+ >>> C().p
+ 'property'
+
+Properties are a dangerous feature, since they change the semantics
+of the language. This means that apparently trivial operations can have
+any kind of side effects:
+
+ >>> def get(self):return 'You gave me the order to destroy your hard disk!!'
+ >>> class C(object): x=property(get)
+ >>> C().x
+ 'You gave me the order to destroy your hard disk!!'
+
+Invoking 'C.x' could very well invoke an external program who is going
+to do anything! It is up to the programmer to not abuse properties.
+The same is true for user defined attribute descriptors.
+
+There are situations in which they are quite handy, however. For
+instance, properties can be used to trace the access data attributes.
+This can be especially useful during debugging, or for logging
+purposes.
+
+Notice that this approach has the problem that now data attributes cannot
+no more be called trough their class, but only though their instances.
+Moreover properties do not work well with ``super`` in cooperative
+methods.
+
+User-defined attribute descriptors
+----------------------------------------------------------------------
+
+As we have seen, there are plenty of predefined attribute descriptors,
+such as staticmethods, classmethods and properties (the built-in
+``super`` is also an attribute descriptor which, for sake of
+convenience, will be discussed in the next section).
+In addition to them, the user can also define customized attribute
+descriptors, simply trough classes with a ``__get__`` special method.
+Let me give an example:
+
+ ::
+
+ #<simpledescr.py>
+
+ class ChattyAttr(object):
+ """Chatty descriptor class; descriptor objects are intended to be
+ used as attributes in other classes"""
+ def __get__(self, obj, cls=None):
+ binding=obj is not None
+ if binding:
+ return 'You are binding %s to %s' % (self,obj)
+ else:
+ return 'Calling %s from %s' % (self,cls)
+
+ class C(object):
+ d=ChattyAttr()
+
+ c=C()
+
+ print c.d # <=> type(c).__dict__['d'].__get__(c,type(c))
+ print C.d # <=> C.__dict__['d'].__get__(None,C)
+
+ #</simpledescr.py>
+
+with output:
+
+ ::
+
+ You are binding <ChattyAttr object at 0x401bc1cc> to
+ <C object at 0x401bc2ec>
+ Calling <ChattyAttr object at 0x401bc1cc> from <class 'C'>
+
+
+Invoking a method with the syntax ``C.d`` or ``c.d`` involves calling
+``__get__``. The ``__get__`` signature is fixed: it is
+`` __get__=__get__(self,obj,cls=None)``, since the notation
+``self.descr_attr`` automatically passes ``self`` and ``self.__class__`` to
+``__get__``.
+
+Custom descriptors can be used to restrict the access to objects in a
+more general way than trough properties. For instance, suppose one
+wants to raise an error if a given attribute 'a' is accessed, both
+from the class and from the instance: a property cannot help here,
+since it works only from the instance. The solution is the following
+custom descriptor:
+
+ ::
+
+ #<oopp.py>
+
+ class AccessError(object):
+ """Descriptor raising an AttributeError when the attribute is
+ accessed""" #could be done with a property
+ def __init__(self,errormessage):
+ self.msg=errormessage
+ def __get__(self,obj,cls=None):
+ raise AttributeError(self.msg)
+
+ #</oopp.py>
+
+ >>> from oopp import AccessError
+ >>> class C(object):
+ ... a=AccessError("'a' cannot be accessed")
+ >>> c=C()
+ >>> c.a #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 313, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: 'a' cannot be accessed
+ >>> C.a #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 313, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: 'a' cannot be accessed
+
+It is always possibile to convert plain attributes (i.e. attributes
+without a "__get__" method) to descriptor objects:
+
+ ::
+
+ #<oopp.py>
+
+ class convert2descriptor(object):
+ """To all practical means, this class acts as a function that, given an
+ object, adds to it a __get__ method if it is not already there. The
+ added __get__ method is trivial and simply returns the original object,
+ independently from obj and cls."""
+ def __new__(cls,a):
+ if hasattr(a,"__get__"): # do nothing
+ return a # a is already a descriptor
+ else: # creates a trivial attribute descriptor
+ cls.a=a
+ return object.__new__(cls)
+ def __get__(self,obj,cls=None):
+ "Returns self.a independently from obj and cls"
+ return self.a
+
+ #</oopp.py>
+
+This example also shows the magic of ``__new__``, that allows to use a
+class as a function. The output of 'convert2descriptor(a)' can be both
+an instance of 'convert2descriptor' (in this case 'convert2descriptor' acts as
+a normal class, i.e. as an object factory) or 'a' itself
+(if 'a' is already a descriptor): in this case 'convert2descriptor' acts
+as a function.
+
+For instance, a string is converted to a descriptor
+
+ >>> from oopp import convert2descriptor
+ >>> a2=convert2descriptor('a')
+ >>> a2
+ <oopp.convert2descriptor object at 0x4017506c>
+ >>> a2.__get__('whatever')
+ 'a'
+
+whereas a function is untouched:
+
+ >>> def f(): pass
+ >>> f2=convert2descriptor(f) # does nothing
+ >>> f2
+ <function f at 0x4019110c>
+
+Data descriptors
+-------------------------------------------------------------------------
+
+It is also possible to specify a ``__set__`` method (descriptors
+with a ``__set__`` method are typically data descriptors) with
+the signature ``__set__(self,obj,value)`` as in the following
+example:
+
+ ::
+
+ #<datadescr.py>
+
+ class DataDescriptor(object):
+ value=None
+ def __get__(self, obj, cls=None):
+ if obj is None: obj=cls
+ print "Getting",obj,"value =",self.value
+ return self.value
+ def __set__(self, obj, value):
+ self.value=value
+ print "Setting",obj,"value =",value
+
+ class C(object):
+ d=DataDescriptor()
+
+ c=C()
+
+ c.d=1 #calls C.__dict__['d'].__set__(c,1)
+ c.d #calls C.__dict__['d'].__get__(c,C)
+ C.d #calls C.__dict__['d'].__get__(None,C)
+ C.d=0 #does *not* call __set__
+ print "C.d =",C.d
+
+ #</datadescr.py>
+
+With output:
+
+ ::
+
+ Setting <C object at 0x401bc1ec> value = 1
+ Getting <C object at 0x401bc42c> value = 1
+ Getting <class 'C'> value = 1
+ C.d = 0
+
+With this knowledge, we may now reconsider the clock example given
+in chapter 3. #NO!??
+
+ >>> import oopp
+ >>> class Clock(object): pass
+ >>> myclock=Clock()
+ ...
+ >>> myclock.get_time=oopp.get_time # this is a function
+ >>> Clock.get_time=lambda self : oopp.get_time() # this is a method
+
+In this example, ``myclock.get_time``, which is attached to the ``myclock``
+object, is a function, whereas ``Clock.get_time``, which is attached to
+the ``Clock`` class is a method. We may also check this by using the ``type``
+function:
+
+ >>> type(myclock.get_time)
+ <type 'function'>
+
+whereas
+
+ >>> type(Clock.get_time)
+ <type 'instance method'>
+
+
+It must be remarked that user-defined attribute descriptors, just as
+properties, allow to arbitrarily change the semantics of the language
+and should be used with care.
+
+The ``super`` attribute descriptor
+------------------------------------------------------------------------
+
+
+super has also a second form, where it is more used as a descriptor.
+
+``super`` objects are attribute descriptors, too, with a ``__get__``
+method returning a method-wrapper object:
+
+ >>> super(C,C()).__get__
+ <method-wrapper object at 0x8161074>
+
+Here I give some example of acceptable call:
+
+ >>> super(C,C()).__get__('whatever')
+ <super: <class 'C'>, <C object>>
+ >>> super(C,C()).__get__('whatever','whatever')
+ <super: <class 'C'>, <C object>>
+
+
+Unfortunately, for the time being
+(i.e. for Python 2.3), the ``super`` mechanism has various limitations.
+To show the issues, let me start by considering the following base class:
+
+ ::
+
+ #<oopp.py>
+
+ class ExampleBaseClass(PrettyPrinted):
+ """Contains a regular method 'm', a staticmethod 's', a classmethod
+ 'c', a property 'p' and a data attribute 'd'."""
+ m=lambda self: 'regular method of %s' % self
+ s=staticmethod(lambda : 'staticmethod')
+ c=classmethod(lambda cls: 'classmethod of %s' % cls)
+ p=property(lambda self: 'property of %s' % self)
+ a=AccessError('Expected error')
+ d='data'
+
+ #</oopp.py>
+
+Now, let me derive a new class C from ExampleBaseClass:
+
+ >>> from oopp import ExampleBaseClass
+ >>> class C(ExampleBaseClass): pass
+ >>> c=C()
+
+Ideally, we would like to retrieve the methods and attributes of
+ExampleBaseClass from C, by using the ``super`` mechanism.
+
+1. We see that ``super`` works without problems for regular methods,
+ staticmethods and classmethods:
+
+ >>> super(C,c).m()
+ 'regular method of <C>'
+ >>> super(C,c).s()
+ 'staticmethod'
+ >>> super(C,c).c()
+ "classmethod of <class '__main__.C'>"
+
+It also works for user defined attribute descriptors:
+
+ >>> super(C,c).a # access error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 340, in __get__
+ raise AttributeError(self.msg)
+ AttributeError: Expected error
+
+and for properties (only for Python 2.3+):
+
+ >>> ExampleBaseClass.p
+ <property object at 0x81b30fc>
+
+In Python 2.2 one would get an error, instead
+
+ >>> super(C,c).p #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute 'p'
+
+3. Moreover, certain attributes of the superclass, such as its
+``__name__``, cannot be retrieved:
+
+ >>> ExampleBaseClass.__name__
+ 'ExampleBaseClass'
+ >>> super(C,c).__name__ #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute '__name__'
+
+4. There is no direct way to retrieve the methods of the super-superclass
+ (i.e. the grandmother class, if you wish) or in general the furthest
+ ancestors, since ``super`` does not chain.
+
+5. Finally, there are some subtle issues with the ``super(cls)`` syntax:
+
+
+ >>> super(C).m #(2) error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'super' object has no attribute 'm'
+
+means ``super(C).__get__(None,C)``, but only
+``super(C).__get__(c,C).m==super(C,c)`` works.
+
+ On the other hand,
+
+ >>> super(C).__init__ #(1)
+ <built-in method __init__ of type object at 0x80e6fc0>
+ >>> super(C).__new__ #(1)
+ <built-in method __init__ of type object at 0x80e6fc0>
+
+
+ seems to work, whereas in reality does not. The reason is that since
+ ``super`` objects are instances
+ of ``object``, they inherit object's methods, and in particular
+ ``__init__`` ; therefore the ``__init__`` method in (1) is *not*
+ the ``ExampleBaseClass.__init__`` method. The point is that ``super``
+ objects are attribute descriptors and not references to the superclass.
+
+Probably, in future versions of Python the ``super`` mechanism will be
+improved. However, for the time being, one must provide a workaround for
+dealing with these issues. This will be discussed in the next chapter.
+
+Method wrappers
+----------------------------------------------------------------------
+
+One of the most typical applications of attribute descriptors is their
+usage as *method wrappers*.
+
+Suppose, for instance, one wants to add tracing capabilities to
+the methods of a class for debugging purposes. The problem
+can be solved with a custom descriptor class:
+
+ ::
+
+ #<oopp.py>
+
+ import inspect
+
+ class wrappedmethod(Customizable):
+ """Customizable method factory intended for derivation.
+ The wrapper method is overridden in the children."""
+
+ logfile=sys.stdout # default
+ namespace='' # default
+
+ def __new__(cls,meth): # meth is a descriptor
+ if isinstance(meth,FunctionType):
+ kind=0 # regular method
+ func=meth
+ elif isinstance(meth,staticmethod):
+ kind=1 # static method
+ func=meth.__get__('whatever')
+ elif isinstance(meth,classmethod):
+ kind=2 # class method
+ func=meth.__get__('whatever','whatever').im_func
+ elif isinstance(meth,wrappedmethod): # already wrapped
+ return meth # do nothing
+ elif inspect.ismethoddescriptor(meth):
+ kind=0; func=meth # for many builtin methods
+ else:
+ return meth # do nothing
+ self=super(wrappedmethod,cls).__new__(cls)
+ self.kind=kind; self.func=func # pre-initialize
+ return self
+
+ def __init__(self,meth): # meth not used
+ self.logfile=self.logfile # default values
+ self.namespace=self.namespace # copy the current
+
+ def __get__(self,obj,cls): # closure
+ def _(*args,**kw):
+ if obj is None: o=() # unbound method call
+ else: o=(obj,) # bound method call
+ allargs=[o,(),(cls,)][self.kind]+args
+ return self.wrapper()(*allargs,**kw)
+ return _ # the wrapped function
+ # allargs is the only nontrivial line in _; it adds
+ # 0 - obj if meth is a regular method
+ # 1 - nothing if meth is a static method
+ # 2 - cls if meth is a class method
+
+ def wrapper(self): return self.func # do nothing, to be overridden
+
+ #</oopp.py>
+
+This class is intended for derivation: the wrapper method has to be overridden
+in the children in order to introduce the wanted feature. If I want to
+implement the capability of tracing methods, I can reuse the ``with_tracer``
+closure introduced in chapter 2:
+
+ ::
+
+ #<oopp.py>
+
+ class tracedmethod(wrappedmethod):
+ def wrapper(self):
+ return with_tracer(self.func,self.namespace,self.logfile)
+
+ #</oopp.py>
+
+Nothing prevents me from introducing timing features by reusing the
+``with_timer`` closure:
+
+ ::
+
+ #<oopp.py>
+
+ class timedmethod(wrappedmethod):
+ iterations=1 # additional default parameter
+
+ def __init__(self,meth):
+ super(timedmethod,self).__init__(self,meth)
+ self.iterations=self.iterations # copy
+
+ def wrapper(self):
+ return with_timer(self.func,self.namespace,
+ self.iterations,self.logfile)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+The dictionary of wrapped functions is then built from an utility function
+
+ ::
+
+ #<oopp.py>
+
+ def wrap(obj,wrapped,condition=lambda k,v: True, err=None):
+ "Retrieves obj's dictionary and wraps it"
+ if isinstance(obj,dict): # obj is a dictionary
+ dic=obj
+ else:
+ dic=getattr(obj,'__dict__',{}).copy() # avoids dictproxy objects
+ if not dic: dic=attributes(obj) # for simple objects
+ wrapped.namespace=getattr(obj,'__name__','')
+ for name,attr in dic.iteritems(): # modify dic
+ if condition(name,attr): dic[name]=wrapped(attr)
+ if not isinstance(obj,dict): # modify obj
+ customize(obj,err,**dic)
+
+ #</oopp.py>
+
+ ::
+
+ #<tracingmethods.py>
+
+ from oopp import *
+
+ class C(object):
+ "Class with traced methods"
+
+ def f(self): return self
+ f=tracedmethod(f)
+
+ g=staticmethod(lambda:None)
+ g=tracedmethod(g)
+
+ h=classmethod(do_nothing)
+ h=tracedmethod(h)
+
+ c=C()
+
+ #unbound calls
+ C.f(c)
+ C.g()
+ C.h()
+
+ #bound calls
+ c.f()
+ c.g()
+ c.h()
+
+ #</tracingmethods.py>
+
+Output:
+
+ ::
+
+ [C] Calling 'f' with arguments
+ (<C object at 0x402042cc>,){} ...
+ -> 'C.f' called with result: <C object at 0x402042cc>
+
+ [C] Calling '<lambda>' with arguments
+ (){} ...
+ -> 'C.<lambda>' called with result: None
+
+ [C] Calling 'do_nothing' with arguments
+ (<class 'C'>,){} ...
+ -> 'C.do_nothing' called with result: None
+
+ [C] Calling 'f' with arguments
+ (<C object at 0x402042cc>,){} ...
+ -> 'C.f' called with result: <C object at 0x402042cc>
+
+ [C] Calling '<lambda>' with arguments
+ (){} ...
+ -> 'C.<lambda>' called with result: None
+
+ [C] Calling 'do_nothing' with arguments
+ (<class 'C'>,){} ...
+ -> 'C.do_nothing' called with result: None
+
+
+The approach in 'tracingmethods.py' works, but it is far from
+being elegant, since I had to explicitly wrap each method in the
+class by hand.
+
+Both problems can be avoided.
+
+ >>> from oopp import *
+ >>> wrap(Clock,tracedmethod)
+ >>> Clock.get_time()
+ [Clock] Calling 'get_time' with arguments
+ (){} ...
+ -> 'Clock.get_time' called with result: 21:56:52
+ '21:56:52'
diff --git a/pypers/doctest_talk/Makefile b/pypers/doctest_talk/Makefile
new file mode 100755
index 0000000..d5e292b
--- /dev/null
+++ b/pypers/doctest_talk/Makefile
@@ -0,0 +1,5 @@
+talk: talk.txt
+ python maketalk.py talk.txt
+test: P01.html
+ tidy P01.html > /dev/null 2> x.txt; less x.txt
+
diff --git a/pypers/doctest_talk/P01.html b/pypers/doctest_talk/P01.html
new file mode 100755
index 0000000..1f655c6
--- /dev/null
+++ b/pypers/doctest_talk/P01.html
@@ -0,0 +1,115 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P27.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></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. <br/> <br/>
+
+</center></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P02.html b/pypers/doctest_talk/P02.html
new file mode 100755
index 0000000..a61f26c
--- /dev/null
+++ b/pypers/doctest_talk/P02.html
@@ -0,0 +1,110 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P02.html">P02</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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 about the future? </li>
+ <li> What's the final message?</li>
+<ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P03.html b/pypers/doctest_talk/P03.html
new file mode 100755
index 0000000..8522de5
--- /dev/null
+++ b/pypers/doctest_talk/P03.html
@@ -0,0 +1,105 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P03.html">P03</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P04.html b/pypers/doctest_talk/P04.html
new file mode 100755
index 0000000..fa763e9
--- /dev/null
+++ b/pypers/doctest_talk/P04.html
@@ -0,0 +1,111 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P04.html">P04</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P05.html b/pypers/doctest_talk/P05.html
new file mode 100755
index 0000000..23aa94d
--- /dev/null
+++ b/pypers/doctest_talk/P05.html
@@ -0,0 +1,110 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P05.html">P05</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P06.html b/pypers/doctest_talk/P06.html
new file mode 100755
index 0000000..47bc44f
--- /dev/null
+++ b/pypers/doctest_talk/P06.html
@@ -0,0 +1,111 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P06.html">P06</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P07.html b/pypers/doctest_talk/P07.html
new file mode 100755
index 0000000..02b39ab
--- /dev/null
+++ b/pypers/doctest_talk/P07.html
@@ -0,0 +1,110 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P07.html">P07</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P08.html b/pypers/doctest_talk/P08.html
new file mode 100755
index 0000000..8da995d
--- /dev/null
+++ b/pypers/doctest_talk/P08.html
@@ -0,0 +1,105 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P08.html">P08</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P09.html b/pypers/doctest_talk/P09.html
new file mode 100755
index 0000000..d46fe05
--- /dev/null
+++ b/pypers/doctest_talk/P09.html
@@ -0,0 +1,117 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P09.html">P09</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P10.html b/pypers/doctest_talk/P10.html
new file mode 100755
index 0000000..5d265ad
--- /dev/null
+++ b/pypers/doctest_talk/P10.html
@@ -0,0 +1,104 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P10.html">P10</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>What is doctest?</h1><br/>
+
+In its simplest form (not the form I use it) it allows
+you to include tests in the docstrings of your application.</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P11.html b/pypers/doctest_talk/P11.html
new file mode 100755
index 0000000..b068e9d
--- /dev/null
+++ b/pypers/doctest_talk/P11.html
@@ -0,0 +1,118 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P11.html">P11</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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 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__)
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P12.html b/pypers/doctest_talk/P12.html
new file mode 100755
index 0000000..2a74e07
--- /dev/null
+++ b/pypers/doctest_talk/P12.html
@@ -0,0 +1,122 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P12.html">P12</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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 the Italian Code Jam!")
+Expecting: ['hello', 'world!', 'welcome to the Italian Code Jam!']
+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/doctest_talk/P13.html b/pypers/doctest_talk/P13.html
new file mode 100755
index 0000000..dea66af
--- /dev/null
+++ b/pypers/doctest_talk/P13.html
@@ -0,0 +1,114 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P13.html">P13</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Why I do not use the standard 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/doctest_talk/P14.html b/pypers/doctest_talk/P14.html
new file mode 100755
index 0000000..b4f5394
--- /dev/null
+++ b/pypers/doctest_talk/P14.html
@@ -0,0 +1,122 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P14.html">P14</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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>
+
+Example:
+
+<pre>
+$ doct howto.txt
+split.txt: 42 tests passed in 0.42 seconds
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P15.html b/pypers/doctest_talk/P15.html
new file mode 100755
index 0000000..29976b8
--- /dev/null
+++ b/pypers/doctest_talk/P15.html
@@ -0,0 +1,116 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P15.html">P15</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>The split example revisited</h1><br/>
+
+<pre>
+
+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!']
+
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P16.html b/pypers/doctest_talk/P16.html
new file mode 100755
index 0000000..5d6563d
--- /dev/null
+++ b/pypers/doctest_talk/P16.html
@@ -0,0 +1,113 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P16.html">P16</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>The split example revisited (continued)</h1><br/>
+
+<pre>
+
+Notice that 'split' eats whitespaces:
+
+>>> split("hello , world")
+['hello', 'world']
+
+>>> split("hello , ; world")
+['hello', '', 'world']
+
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P17.html b/pypers/doctest_talk/P17.html
new file mode 100755
index 0000000..ccac807
--- /dev/null
+++ b/pypers/doctest_talk/P17.html
@@ -0,0 +1,121 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P17.html">P17</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P18.html b/pypers/doctest_talk/P18.html
new file mode 100755
index 0000000..29ea1c9
--- /dev/null
+++ b/pypers/doctest_talk/P18.html
@@ -0,0 +1,119 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P18.html">P18</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P19.html b/pypers/doctest_talk/P19.html
new file mode 100755
index 0000000..d9ffa42
--- /dev/null
+++ b/pypers/doctest_talk/P19.html
@@ -0,0 +1,110 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P19.html">P19</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Doctest caveats</h1><br/>
+<ul>
+
+<li> doctest does not stop at the first failed test.</li>
+<li> doctest is very strict about the expected output </li>
+<li> expected output must end with a newline </li>
+<li> expected output cannot contain a blank line </li>
+<li> output to stdout is captured, but not output to stderr</li>
+
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P20.html b/pypers/doctest_talk/P20.html
new file mode 100755
index 0000000..bf9c20a
--- /dev/null
+++ b/pypers/doctest_talk/P20.html
@@ -0,0 +1,118 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P20.html">P20</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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><em>new in Python 2.3!</em><h2>
+
+What about the future?
+----------------------
+
+Many enhacements to be expected!</td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P21.html b/pypers/doctest_talk/P21.html
new file mode 100755
index 0000000..8d4b5ff
--- /dev/null
+++ b/pypers/doctest_talk/P21.html
@@ -0,0 +1,122 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P21.html">P21</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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;
+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>
+
+Example:
+<pre>
+$ doct -u split.txt
+.
+----------------------------------------------------------------------
+Ran 1 test in 0.012s
+
+OK
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/doctest_talk/P22.html b/pypers/doctest_talk/P22.html
new file mode 100755
index 0000000..080ad51
--- /dev/null
+++ b/pypers/doctest_talk/P22.html
@@ -0,0 +1,111 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P22.html">P22</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P23.html b/pypers/doctest_talk/P23.html
new file mode 100755
index 0000000..af4cf12
--- /dev/null
+++ b/pypers/doctest_talk/P23.html
@@ -0,0 +1,120 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P23.html">P23</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P24.html b/pypers/doctest_talk/P24.html
new file mode 100755
index 0000000..13d32e0
--- /dev/null
+++ b/pypers/doctest_talk/P24.html
@@ -0,0 +1,111 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P24.html">P24</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P25.html b/pypers/doctest_talk/P25.html
new file mode 100755
index 0000000..1279884
--- /dev/null
+++ b/pypers/doctest_talk/P25.html
@@ -0,0 +1,118 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href="P26.html">Next</a></td> <td bgcolor="lightblue"><a href="P24.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P25.html">P25</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P26.html b/pypers/doctest_talk/P26.html
new file mode 100755
index 0000000..10220cd
--- /dev/null
+++ b/pypers/doctest_talk/P26.html
@@ -0,0 +1,115 @@
+<!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 = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<tr>
+ <td bgcolor="lightblue">
+<table border=0 summary='a table'>
+<tr>
+ <td bgcolor="lightblue"><a href="P27.html">Next</a></td> <td bgcolor="lightblue"><a href="P25.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P26.html">P26</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"></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/doctest_talk/P27.html b/pypers/doctest_talk/P27.html
new file mode 100755
index 0000000..5852f3a
--- /dev/null
+++ b/pypers/doctest_talk/P27.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P27</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 = "cjlogo.jpg" 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="P26.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P27.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P27.html">P27</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 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"><a href="P27.html">P27</a></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/doctest_talk/P28.html b/pypers/doctest_talk/P28.html
new file mode 100755
index 0000000..d3d0a7c
--- /dev/null
+++ b/pypers/doctest_talk/P28.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P28</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 = "cjlogo.jpg" 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="P27.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P28.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P28.html">P28</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 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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"><a href="P28.html">P28</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/doctest_talk/P29.html b/pypers/doctest_talk/P29.html
new file mode 100755
index 0000000..12ecce5
--- /dev/null
+++ b/pypers/doctest_talk/P29.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+ <html>
+ <head>
+ <meta name="generator" content="Generated by Python">
+ <title>P29</title>
+
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+
+ </head>
+<body bgcolor="lightblue">
+
+
+<table border=0>
+<tr>
+ <td bgcolor="lightblue"><small>
+<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="P01.html">Next</a></td> <td bgcolor="lightblue"><a href="P28.html">Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P01.html">First</a></td> <td bgcolor="lightblue"><a href="P29.html">Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P29.html">P29</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"><a href="P27.html">P27</a></td> <td bgcolor="lightblue"><a href="P28.html">P28</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href="P29.html">P29</a></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/doctest_talk/README.txt b/pypers/doctest_talk/README.txt
new file mode 100755
index 0000000..13d13b2
--- /dev/null
+++ b/pypers/doctest_talk/README.txt
@@ -0,0 +1,2 @@
+This directory contains my talk about doctest structured as a Zope
+product.
diff --git a/pypers/doctest_talk/__init__.py b/pypers/doctest_talk/__init__.py
new file mode 100755
index 0000000..df1cd5f
--- /dev/null
+++ b/pypers/doctest_talk/__init__.py
@@ -0,0 +1,7 @@
+## from Products.CMFCore import DirectoryView
+## from partecs_misc.debug_utils import LogFile
+
+## def initialize(context):
+## LogFile().display("registered!")
+## DirectoryView.registerDirectory('skins', globals())
+
diff --git a/pypers/doctest_talk/abstract.txt b/pypers/doctest_talk/abstract.txt
new file mode 100755
index 0000000..43e0492
--- /dev/null
+++ b/pypers/doctest_talk/abstract.txt
@@ -0,0 +1,9 @@
+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/doctest_talk/ex24.py b/pypers/doctest_talk/ex24.py
new file mode 100755
index 0000000..ce45cf8
--- /dev/null
+++ b/pypers/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/doctest_talk/ex_inner.py b/pypers/doctest_talk/ex_inner.py
new file mode 100755
index 0000000..b59dc3d
--- /dev/null
+++ b/pypers/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/doctest_talk/index.html b/pypers/doctest_talk/index.html
new file mode 100755
index 0000000..870e229
--- /dev/null
+++ b/pypers/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/doctest_talk/index.txt b/pypers/doctest_talk/index.txt
new file mode 100755
index 0000000..8988e3e
--- /dev/null
+++ b/pypers/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/doctest_talk/maketalk.py b/pypers/doctest_talk/maketalk.py
new file mode 100755
index 0000000..9c03b46
--- /dev/null
+++ b/pypers/doctest_talk/maketalk.py
@@ -0,0 +1,179 @@
+#!/usr/bin/python
+import exc_debugger, webbrowser
+from ms.html_utils import makelink
+import os, sys, re
+INCLUDE = re.compile(r"\n.. include::\s+([\w\./]+)")
+
+BGCOLOR = "lightblue"
+CSS = """
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+"""
+
+# used for my doctest talk; poor approach
+class TableList(list):
+ """A table is a list of lists/tuple of the same length.
+ A table has methods to display itself as plain text and
+ as HTML. """
+
+ border = 1
+ color = "white"
+
+ def __str__(self):
+ return self.to_html()
+
+ def to_text(self):
+ return "\n".join(["\t".join(map(str, t)) for t in self])
+
+ def _gen_html(self):
+ """Returns an HTML table as an iterator."""
+ yield "\n<table border=%r summary='a table'>\n" % self.border
+ header = self.header
+ for row in self:
+ yield "<tr>\n "
+ for el in row:
+ if header:
+ yield "<th>%s</th> " % el
+ else:
+ yield '<td bgcolor="%s">%s</td> ' % \
+ (getattr(row, "color", self.color), el)
+ yield "\n</tr>\n"
+ header = False
+ yield "</table>\n"
+
+ def to_html(self, header = False):
+ """Generates an HTML table."""
+ self.header = header
+ return "".join(self._gen_html())
+
+ def make(cls, *args, **kw): # convenience factory
+ ncols = kw.get("ncols", 2)
+ missingcols = ncols - (len(args) % ncols)
+ args += ('',) * missingcols # add empty columns to fill the table
+ table = cls(chop(args, ncols))
+ vars(table).update(kw)
+ return table
+ make = classmethod(make)
+
+ def row(cls, *args, **kw): # convenience factory
+ table = cls([args])
+ vars(table).update(kw)
+ return table
+ row = classmethod(row)
+
+ def col(cls, *args, **kw): # convenience factory
+ table = cls([(arg,) for arg in args])
+ vars(table).update(kw)
+ return table
+ col = classmethod(col)
+
+def testTable():
+ t=TableList([
+ ['a', 1, 2],
+ ['b',2, 3],
+ ['c',2, 3],
+ ])
+ print t.to_html()
+ print t
+
+## def testTemplate():
+## @template()
+## def hello(name="Unknown"):
+## yield "<body>"
+## yield "hello $name"
+## yield "</body>"
+## print hello()
+## print hello("Tania")
+
+## def testT():
+## ciao = "ciao"
+## @template()
+## def t(nina="nina"):
+## yield "$ciao $nina"
+## print t()
+
+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.first, "First"),
+ makelink(self.last, "Last"),
+ makelink(self.namext, self.name),
+ '',
+ border = 0,
+ color = BGCOLOR)
+ logo = TableList.col(
+ '<img src = "cjlogo.jpg" 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(logo, box, index,
+ border = 0, color = BGCOLOR),
+ self.txt, border = 0, color = BGCOLOR))
+ return self.html()
+
+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/doctest_talk/more.txt b/pypers/doctest_talk/more.txt
new file mode 100755
index 0000000..0e0b7fc
--- /dev/null
+++ b/pypers/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/doctest_talk/refresh.txt b/pypers/doctest_talk/refresh.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/doctest_talk/refresh.txt
diff --git a/pypers/doctest_talk/split-failure.txt b/pypers/doctest_talk/split-failure.txt
new file mode 100755
index 0000000..0bce3e2
--- /dev/null
+++ b/pypers/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/doctest_talk/split-failure_txt.py b/pypers/doctest_talk/split-failure_txt.py
new file mode 100755
index 0000000..6819fb5
--- /dev/null
+++ b/pypers/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/doctest_talk/split.html b/pypers/doctest_talk/split.html
new file mode 100755
index 0000000..3cc867a
--- /dev/null
+++ b/pypers/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/doctest_talk/split.py b/pypers/doctest_talk/split.py
new file mode 100755
index 0000000..1f4e788
--- /dev/null
+++ b/pypers/doctest_talk/split.py
@@ -0,0 +1,16 @@
+# 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 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/doctest_talk/split.txt b/pypers/doctest_talk/split.txt
new file mode 100755
index 0000000..8e3e8fe
--- /dev/null
+++ b/pypers/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/doctest_talk/talk.html b/pypers/doctest_talk/talk.html
new file mode 100755
index 0000000..a47dd9c
--- /dev/null
+++ b/pypers/doctest_talk/talk.html
@@ -0,0 +1,362 @@
+<?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></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<div class="section" id="automatic-testing-in-python-wonderful-doctest">
+<h1><a name="automatic-testing-in-python-wonderful-doctest">Automatic testing in Python: wonderful doctest!</a></h1>
+<p>&lt;center&gt;</p>
+<blockquote>
+<p>Italian Code Jam &lt;br/&gt;</p>
+<blockquote>
+09 Oct 2004 &lt;br/&gt;</blockquote>
+<p>Michele Simionato &lt;br/&gt;</p>
+<blockquote>
+Partecs s.r.l. &lt;br/&gt;</blockquote>
+</blockquote>
+<p>&lt;/center&gt;</p>
+<div class="section" id="summary">
+<h2><a name="summary">Summary</a></h2>
+<dl>
+<dt>&lt;ul&gt;</dt>
+<dd>&lt;li&gt; What is automatic testing? &lt;/li&gt;
+&lt;li&gt; Why automatic testing is better? &lt;/li&gt;
+&lt;li&gt; Which kind of automatic testing should I do? &lt;/li&gt;
+&lt;li&gt; Why I did start writing automatic tests? &lt;/li&gt;
+&lt;li&gt; A few real life examples &lt;/li&gt;</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 26)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p>&lt;ul&gt;</p>
+</div>
+<div class="section" id="what-is-automatic-testing">
+<h2><a name="what-is-automatic-testing">What is automatic testing</a></h2>
+<p>Any methodology that allows you to test
+your application mechanically, repeatedly
+and in a controlled reproducible way.</p>
+</div>
+<div class="section" id="why-automatic-testing-is-better">
+<h2><a name="why-automatic-testing-is-better">Why automatic testing is better</a></h2>
+<p>Try to perform <em>manual</em> testing and you will have the answer!</p>
+</div>
+<div class="section" id="automatic-testing-in-python">
+<h2><a name="automatic-testing-in-python">Automatic testing in Python ...</a></h2>
+<p>There are two standard testing frameworks in Python:</p>
+<dl>
+<dt>&lt;ol&gt;</dt>
+<dd>&lt;li&gt; unittest &lt;/li&gt;
+&lt;li&gt; doctest &lt;/li&gt;</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 51)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p>&lt;/ol&gt;</p>
+<p>Which one should I use?</p>
+</div>
+<div class="section" id="the-short-answer">
+<h2><a name="the-short-answer">The short answer</a></h2>
+<p>unitest is boring, doctest is cool, so let's
+talk about it!</p>
+<blockquote>
+;-)</blockquote>
+</div>
+<div class="section" id="more-seriously">
+<h2><a name="more-seriously">More seriously</a></h2>
+<p>Use different testing frameworks; each one has advantages
+and disadvantages; use combinations of them; invert your
+own testing procedure.</p>
+<p>I use combinations of</p>
+<dl>
+<dt>&lt;ul&gt;</dt>
+<dd>&lt;li&gt; unittest &lt;/li&gt;
+&lt;li&gt; doctest &lt;/li&gt;
+&lt;li&gt; custom tests &lt;/li&gt;
+&lt;li&gt; Makefile driven tests &lt;/li&gt;
+&lt;li&gt; poor man assertions &lt;/li&gt;
+&lt;li&gt; et al. &lt;/li&gt;</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 81)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p>&lt;/ul&gt;</p>
+</div>
+<div class="section" id="what-is-doctest">
+<h2><a name="what-is-doctest">What is doctest?</a></h2>
+<p>In its simplest form (not the form I use it ;) it allows
+you to include tests in the docstrings of your application.</p>
+</div>
+<div class="section" id="example">
+<h2><a name="example">Example</a></h2>
+<p># split.py
+import re</p>
+<dl>
+<dt>def split(text, sep = re.compile(r&quot;s*[,;]s*&quot;)):</dt>
+<dd>&quot;&quot;&quot;Split a string taking as separators &quot;,&quot; &quot;;&quot;.
+Example:
+&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!']
+&quot;&quot;&quot;
+return sep.split(text)</dd>
+<dt>if __name__ == &quot;__main__&quot;:</dt>
+<dd>import __main__, doctest
+doctest.testmod(__main__)</dd>
+</dl>
+</div>
+<div class="section" id="running-doctest-in-verbose-mode">
+<h2><a name="running-doctest-in-verbose-mode">Running doctest in verbose mode</a></h2>
+<p>&lt;pre&gt;
+$ 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(&quot;hello, world!; welcome to the Italian Code Jam!&quot;)
+Expecting: ['hello', 'world!', 'welcome to the Italian Code Jam!']
+ok
+0 of 2 examples failed in __main__.split.__doc__
+1 items had no tests:</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>talk.txt</tt>, line 113)</p>
+Unexpected indentation.</div>
+<blockquote>
+__main__</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 131)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<dl>
+<dt>1 items passed all tests:</dt>
+<dd>2 tests in __main__.split</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 133)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p>2 tests in 2 items.
+2 passed and 0 failed.
+Test passed.
+&lt;/pre&gt;</p>
+</div>
+<div class="section" id="why-i-do-not-use-the-standard-approach">
+<h2><a name="why-i-do-not-use-the-standard-approach">Why I do not use the standard approach</a></h2>
+<p>&lt;ul&gt;
+&lt;li&gt; It makes you end up with very large docstrings where the actual
+code is easily lost &lt;/li&gt;</p>
+<p>&lt;li&gt; It abuses the original purpose of docstrings&lt;/li&gt;</p>
+<p>&lt;li&gt; It conflates two different aspects (code and tests on the code)&lt;/li&gt;</p>
+<dl>
+<dt>&lt;li&gt; It is much easier to write the documentation in a separate</dt>
+<dd>text file &lt;/li&gt;</dd>
+</dl>
+<p>&lt;li&gt; Testing should be done by an external tool anyway &lt;/li&gt;
+&lt;/ul&gt;</p>
+</div>
+<div class="section" id="how-i-use-doctest">
+<h2><a name="how-i-use-doctest">How I use doctest</a></h2>
+<dl>
+<dt>&lt;ul&gt;</dt>
+<dd><p class="first">&lt;li&gt;I find that too long docstrings make reading the code difficult&lt;/li&gt;</p>
+<p>&lt;li&gt; I like keeping the documentation on a separate rst file&lt;/li&gt;</p>
+<p>&lt;li&gt;there is no sync problem since you run the tests all the time&lt;/li&gt;</p>
+<dl>
+<dt>&lt;li&gt; so I wrote a custom utility to extract doctests from the&lt;/li&gt;</dt>
+<dd>documentation file</dd>
+</dl>
+<p>&lt;li&gt;useful for writing articles ...&lt;/li&gt;</p>
+<p class="last">&lt;li&gt; ... but also tutorials for internal usage in the company&lt;/li&gt;</p>
+</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 173)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p>&lt;/ul&gt;</p>
+</div>
+<div class="section" id="the-split-example-revisited">
+<h2><a name="the-split-example-revisited">The split example revisited</a></h2>
+<p>&lt;pre&gt;</p>
+</div>
+</div>
+<div class="section" id="documentation-for-the-split-module">
+<h1><a name="documentation-for-the-split-module">Documentation for the 'split' module</a></h1>
+<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>&lt;/pre&gt;</p>
+<div class="section" id="the-split-example-revisited-continued">
+<h2><a name="the-split-example-revisited-continued">The split example revisited (continued)</a></h2>
+<p>&lt;pre&gt;</p>
+<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>
+<p>&lt;/pre&gt;</p>
+</div>
+<div class="section" id="when-tests-fail">
+<h2><a name="when-tests-fail">When tests fail</a></h2>
+<p>&lt;pre&gt;</p>
+<p>$ cat split-failure.txt
+An example of failed text:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from split import split
+&gt;&gt;&gt; split(&quot;hello, world&quot;)
+['hello', ' world']
+</pre>
+<div class="section" id="doct-split-failure-txt">
+<h3><a name="doct-split-failure-txt">$ doct split-failure.txt</a></h3>
+<p>Failure in example: split(&quot;hello, world&quot;)
+from line #5 of split-failure.txt
+Expected: ['hello', ' world']
+Got: ['hello', 'world']</p>
+<p>&lt;/pre&gt;</p>
+<p>Unfortunately doctest does not stop at the first failed test.</p>
+</div>
+</div>
+<div class="section" id="managing-exceptions">
+<h2><a name="managing-exceptions">Managing exceptions</a></h2>
+<p>It is possible to test that your program raises the exception you
+expect:</p>
+<p>&lt;pre&gt;</p>
+<p>$ echo &quot;&quot;&quot; # split cannot work on a list
+&gt;&gt;&gt; from split import split
+&gt;&gt;&gt; split([])
+Traceback (most recent call last):</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>talk.txt</tt>, line 230)</p>
+Unexpected indentation.</div>
+<blockquote>
+...</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 248)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>TypeError: expected string or buffer</p>
+<p>&quot;&quot;&quot; &gt; x.txt</p>
+<p>$ doct x.txt
+x.txt: 2 tests passed in 0.01 seconds</p>
+<p>&lt;/pre&gt;</p>
+<p>(notice however that relying on exception messags may be risky)
+(IGNORE_EXCEPTION_DETAIL)</p>
+</div>
+<div class="section" id="converting-doctests-to-unittests">
+<h2><a name="converting-doctests-to-unittests">Converting doctests to unittests</a></h2>
+<blockquote>
+<p>import unittest
+import doctest
+import my_module_with_doctests</p>
+<p>suite = doctest.DocTestSuite(my_module_with_doctests)
+runner = unittest.TextTestRunner()
+runner.run(suite)</p>
+</blockquote>
+</div>
+<div class="section" id="doctest-caveats">
+<h2><a name="doctest-caveats">Doctest caveats</a></h2>
+<p>&lt;ul&gt;</p>
+<p>&lt;li&gt; Expected output must end with a newline &lt;/li&gt;
+&lt;li&gt; Expected output cannot contain a blank line &lt;/li&gt;
+&lt;li&gt; Output to stdout is captured, but not output to stderr&lt;/li&gt;</p>
+<p>&lt;/ul&gt;</p>
+</div>
+<div class="section" id="why-doctest-is-cool">
+<h2><a name="why-doctest-is-cool">Why doctest is cool</a></h2>
+<dl>
+<dt>&lt;ul&gt;</dt>
+<dd><p class="first">&lt;li&gt; it makes you improve the quality of your documentation &lt;/li&gt;</p>
+<p>&lt;li&gt; it is easy to understand and to explain &lt;/li&gt;</p>
+<p>&lt;li&gt; it can be converted to unittest anyway &lt;/li&gt;</p>
+<p class="last">&lt;li&gt; it does not allow inheritance ;-) &lt;/li&gt;</p>
+</dd>
+</dl>
+<p>&lt;/ul&gt;</p>
+</div>
+<div class="section" id="doctest-is-becoming-even-cooler">
+<h2><a name="doctest-is-becoming-even-cooler">doctest is becoming even cooler</a></h2>
+<p>With Python 2.4 you can automatically convert
+doctests in external text files into unittests:</p>
+<p>&lt;pre&gt;</p>
+<blockquote>
+import doctest, unittest
+suite = doctest.DocFileSuite(my_documentation_file, package=mypackage)
+unittest.TextTestRunner().run(suite)</blockquote>
+<p>&lt;/pre&gt;</p>
+</div>
+<div class="section" id="python-2-4-recognizes-blank-lines">
+<h2><a name="python-2-4-recognizes-blank-lines">Python 2.4 recognizes blank lines</a></h2>
+<p>Blank lines can be marked with &amp;lt;BLANKLINE&amp;gt; :
+&lt;pre&gt;
+&gt;&gt;&gt; print 'foonnbarn'
+foo
+&amp;lt;BLANKLINE&amp;gt;
+bar
+&amp;lt;BLANKLINE&amp;gt;</p>
+<p>&lt;/pre&gt;</p>
+</div>
+<div class="section" id="python-2-4-recognizes-flags">
+<h2><a name="python-2-4-recognizes-flags">Python 2.4 recognizes flags!</a></h2>
+<p>&lt;ul&gt;
+&lt;li&gt; If the ellipsis flag is used, then '...' can be used to</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>talk.txt</tt>, line 316)</p>
+Unexpected indentation.</div>
+<blockquote>
+elide substrings in the desired output: &lt;pre&gt;</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 334)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<pre class="doctest-block">
+&gt;&gt;&gt; print range(1000) #doctest: +ELLIPSIS
+[0, 1, 2, ..., 999]
+</pre>
+<p>&lt;/pre&gt;&lt;/li&gt;</p>
+<dl>
+<dt>&lt;li&gt; </dt>
+<dd>If the whitespace normalization flag is used, then
+differences in whitespace are ignored.&lt;pre&gt;</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>talk.txt</tt>, line 342)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<pre class="doctest-block">
+&gt;&gt;&gt; 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>
+<p>&lt;/pre&gt;&lt;/li&gt;</p>
+<p>&lt;/ul&gt;</p>
+</div>
+<div class="section" id="conclusion-good-reasons-to-use-doctest">
+<h2><a name="conclusion-good-reasons-to-use-doctest">Conclusion: good reasons to use doctest</a></h2>
+<p>&lt;ol&gt;</p>
+<p>&lt;li&gt; it is cool &lt;/li&gt;
+&lt;/ol&gt;</p>
+</div>
+<div class="section" id="references">
+<h2><a name="references">References</a></h2>
+<p>&lt;ul&gt;</p>
+<p>&lt;li&gt;The standard library documentation
+<a class="reference" href="http://docs.python.org/lib/module-doctest.html">http://docs.python.org/lib/module-doctest.html</a> &lt;/li&gt;</p>
+<p>&lt;li&gt; The doctest talk by Tim Peters and Jim Fulton
+<a class="reference" href="http://www.python.org/pycon/dc2004/papers/4">http://www.python.org/pycon/dc2004/papers/4</a>/&lt;/li&gt;</p>
+<p>&lt;li&gt; doctest.py &lt;/li&gt;
+&lt;/ul&gt;</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/doctest_talk/talk.txt b/pypers/doctest_talk/talk.txt
new file mode 100755
index 0000000..9639816
--- /dev/null
+++ b/pypers/doctest_talk/talk.txt
@@ -0,0 +1,445 @@
+Automatic testing in Python: wonderful doctest!
+===============================================
+
+<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. <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 about the future? </li>
+ <li> What's the final 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 (not the form I use it) it 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 the Italian Code Jam!")
+Expecting: ['hello', 'world!', 'welcome to the Italian Code Jam!']
+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 standard 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>
+
+Example:
+
+<pre>
+$ doct howto.txt
+split.txt: 42 tests passed in 0.42 seconds
+</pre>
+
+
+The split example revisited
+------------------------------------------------
+
+<pre>
+
+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!']
+
+</pre>
+
+
+The split example revisited (continued)
+-----------------------------------------
+
+<pre>
+
+Notice that 'split' eats whitespaces:
+
+>>> split("hello , world")
+['hello', 'world']
+
+>>> split("hello , ; world")
+['hello', '', 'world']
+
+</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>
+
+
+Doctest caveats
+------------------------------------------------------------------
+<ul>
+
+<li> doctest does not stop at the first failed test.</li>
+<li> doctest is very strict about the expected output </li>
+<li> expected output must end with a newline </li>
+<li> expected output cannot contain a blank line </li>
+<li> output to stdout is captured, but not output to stderr</li>
+
+</ul>
+
+
+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><em>new in Python 2.3!</em><h2>
+
+What about the future?
+----------------------
+
+Many enhacements to be expected!
+
+
+doctest is becoming even better
+----------------------------------------------------
+
+With Python 2.4 you can run doctests on external text files;
+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>
+
+Example:
+<pre>
+$ doct -u split.txt
+.
+----------------------------------------------------------------------
+Ran 1 test in 0.012s
+
+OK
+</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/doctest_talk/x.html b/pypers/doctest_talk/x.html
new file mode 100755
index 0000000..57bfa5f
--- /dev/null
+++ b/pypers/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/doctest_talk/x.py b/pypers/doctest_talk/x.py
new file mode 100755
index 0000000..6c263e8
--- /dev/null
+++ b/pypers/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/doctest_talk/x.txt b/pypers/doctest_talk/x.txt
new file mode 100755
index 0000000..03a9bc9
--- /dev/null
+++ b/pypers/doctest_talk/x.txt
@@ -0,0 +1,57 @@
+line 17 column 27 - Warning: missing </small> before <table>
+line 98 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 61 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 38 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 38 column 59 - 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 59 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 92 column 3 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 92 column 33 - Warning: <td> attribute "bgcolor" has invalid value "lightblue"
+line 98 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
+48 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/dot/MROgraph.py b/pypers/dot/MROgraph.py
new file mode 100755
index 0000000..a586dc4
--- /dev/null
+++ b/pypers/dot/MROgraph.py
@@ -0,0 +1,103 @@
+# MROgraph.py
+
+"""
+Draw inheritance hierarchies via Dot (http://www.graphviz.org/)
+Author: M. Simionato
+E-mail: mis6@pitt.edu
+Date: August 2003
+License: Python-like
+Requires: Python 2.3, dot, standard Unix tools
+"""
+
+import os,itertools
+
+PSVIEWER='gv' # you may change these with
+PNGVIEWER='kview' # your preferred viewers
+PSFONT='Times' # you may change these too
+PNGFONT='Courier' # on my system PNGFONT=Times does not work
+
+def if_(cond,e1,e2=''):
+ "Ternary operator would be"
+ if cond: return e1
+ else: return e2
+
+def MRO(cls):
+ "Returns the MRO of cls as a text"
+ out=["MRO of %s:" % cls.__name__]
+ for counter,c in enumerate(cls.__mro__):
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=" %s - %s(%s)" % (counter,name,bases)
+ if type(c) is not type: s+="[%s]" % type(c).__name__
+ out.append(s)
+ return '\n'.join(out)
+
+class MROgraph(object):
+ def __init__(self,*classes,**options):
+ "Generates the MRO graph of a set of given classes."
+ if not classes:
+ raise "Missing class argument!"
+ filename = options.get('filename',"MRO_of_%s.ps" % classes[0].__name__)
+ self.labels = options.get('labels',2)
+ caption = options.get('caption',False)
+ setup = options.get('setup','')
+ name, dotformat = os.path.splitext(filename)
+ format = dotformat[1:]
+ fontopt = "fontname="+if_(format=='ps',PSFONT,PNGFONT)
+ nodeopt = ' node [%s];\n' % fontopt
+ edgeopt = ' edge [%s];\n' % fontopt
+ viewer = if_(format=='ps',PSVIEWER,PNGVIEWER)
+ self.textrepr = '\n'.join([MRO(cls) for cls in classes])
+ caption=if_(caption,
+ 'caption [shape=box,label="%s\n",fontsize=9];'
+ % self.textrepr).replace('\n','\\l')
+ setupcode=nodeopt+edgeopt+caption+'\n'+setup+'\n'
+ codeiter=itertools.chain(*[self.genMROcode(cls) for cls in classes])
+ self.dotcode='digraph %s{\n%s%s}' % (
+ name,setupcode,'\n'.join(codeiter))
+ os.system("echo '%s' | dot -T%s > %s; %s %s&" %
+ (self.dotcode,format,filename,viewer,filename))
+ def genMROcode(self,cls):
+ "Generates the dot code for the MRO of a given class"
+ for mroindex,c in enumerate(cls.__mro__):
+ name=c.__name__
+ manyparents=len(c.__bases__) > 1
+ if c.__bases__:
+ yield ''.join([
+ ' %s -> %s %s [style=solid];\n' % (
+ b.__name__,name,if_(manyparents and self.labels==2,
+ '[label="%s"]' % (i+1)))
+ for i,b in enumerate(c.__bases__)])
+ if manyparents:
+ yield " {rank=same; %s}\n" % ''.join([
+ '"%s"; ' % b.__name__ for b in c.__bases__])
+ number=if_(self.labels,"%s-" % mroindex)
+ label='label="%s"' % (number+name)
+ option=if_(issubclass(cls,type), # if cls is a metaclass
+ '[%s]' % label,
+ '[shape=box,%s]' % label)
+ yield(' %s %s;\n' % (name,option))
+ if type(c) is not type: # c has a custom metaclass
+ metaname=type(c).__name__
+ yield ' %s -> %s [style=dashed];' % (metaname,name)
+ def __repr__(self):
+ "Returns the Dot representation of the graph"
+ return self.dotcode
+ def __str__(self):
+ "Returns a text representation of the MRO"
+ return self.textrepr
+
+def testHierarchy(**options):
+ class M(type): pass # metaclass
+ class F(object): pass
+ class E(object): pass
+ class D(object): pass
+ class G(object): __metaclass__=M
+ class C(F,D,G): pass
+ class B(E,D): pass
+ class A(B,C): pass
+ print repr(MROgraph(A,M,**options))
+
+if __name__=="__main__":
+ testHierarchy() # generates a postscript diagram of A and M hierarchies
+
diff --git a/pypers/dot/UML.py b/pypers/dot/UML.py
new file mode 100755
index 0000000..8cc9062
--- /dev/null
+++ b/pypers/dot/UML.py
@@ -0,0 +1,42 @@
+"""Draws UML diagrams using dot"""
+
+# dot.py
+
+import inspect #contents=inspect.classify_class_attrs(cls)
+
+if "skeleton":
+ viewable = lambda name : False
+else:
+ viewable = lambda name : \
+ not (name.startswith("_") or name.endswith("__roles__"))
+
+def get_attrs(cls):
+ return "\\n".join(filter(viewable,cls.__dict__.iterkeys()))
+
+#def get_attrs(cls): return ""
+
+def dotcode(cls):
+ setup='node [color=Green,fontcolor=Blue,fontname=Courier]\n'
+ name='hierarchy_of_%s' % cls.__name__
+ code='\n'.join(codegenerator(cls))
+ page='page="8.3,11.7"' # split pages
+ #page="" # to have all in one page
+ return "digraph %s{\n%s;\n%s\n%s\n}" % (name, page, setup, code)
+
+def codegenerator(cls):
+ yield "node [shape=box,fontsize=8]"
+ for c in inspect.getmro(cls):
+ yield '%s [label="%s\\n---\\n%s"]' % (
+ c.__name__,c.__name__,get_attrs(c))
+ bases = c.__bases__
+ if bases: # generate edges parents -> child
+ yield ''.join([' %s -> %s\n' % ( b.__name__,c.__name__)
+ for b in bases])
+ if len(bases) > 1: # put all parents on the same level
+ yield " {rank=same; %s}\n" % ''.join([
+ '%s ' % b.__name__ for b in bases])
+
+if __name__=="__main__":
+ from Products.Archetypes.public import BaseFolder
+ print dotcode(BaseFolder)
+
diff --git a/pypers/dot/dot.html b/pypers/dot/dot.html
new file mode 100755
index 0000000..d3a13db
--- /dev/null
+++ b/pypers/dot/dot.html
@@ -0,0 +1,347 @@
+<?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.1: http://docutils.sourceforge.net/" />
+<title>Drawing graphs the easy way: an introduction to dot</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="drawing-graphs-the-easy-way-an-introduction-to-dot">
+<h1 class="title">Drawing graphs the easy way: an introduction to <tt class="literal"><span class="pre">dot</span></tt></h1>
+<div class="section" id="got-a-graphing-problem">
+<h1><a name="got-a-graphing-problem">Got a graphing problem?</a></h1>
+<p>You must give a presentation tomorrow and you haven't prepared any
+figure yet; you must document your last project and you need to plot
+your most hairy class hierarchies; you are asked to provide ten slightly
+different variations of the same picture; you are pathologically unable to put
+your finger on a mouse and drawing anything more complex that a square ...
+in all these cases, dont' worry! <tt class="literal"><span class="pre">dot</span></tt> comes at the rescue and
+can save your day!</p>
+</div>
+<div class="section" id="what-is-dot">
+<h1><a name="what-is-dot">What is <tt class="literal"><span class="pre">dot</span></tt>?</a></h1>
+<p><tt class="literal"><span class="pre">dot</span></tt> is a tool to generate nice-looking diagrams with a minimum of
+effort. <tt class="literal"><span class="pre">dot</span></tt> is distributed as a part of <tt class="literal"><span class="pre">GraphViz</span></tt>, an
+Open Source project developed at AT&amp;T and released under a MIT licence.
+It is a high quality and mature product, with very good
+documentation and support, available on all major platforms,
+including Unix/Linux, Windows and Mac. There is an official home-page and
+a supporting mailing list.</p>
+</div>
+<div class="section" id="what-can-i-do-with-dot">
+<h1><a name="what-can-i-do-with-dot">What can I do with <tt class="literal"><span class="pre">dot</span></tt> ?</a></h1>
+<p>First of all, let me make clear that <tt class="literal"><span class="pre">dot</span></tt> is not just another paint program,
+nor a vector graphics program. <tt class="literal"><span class="pre">dot</span></tt> is a scriptable batch-oriented graphing
+tool; it is to vector drawing programs as <tt class="literal"><span class="pre">LaTex</span></tt> is to word processors.
+If you want to have control on every single pixel in your diagram,
+or if you are an artistic person who likes to draw free hand, then <tt class="literal"><span class="pre">dot</span></tt>
+is not for you. <tt class="literal"><span class="pre">dot</span></tt> is a tool for the lazy developer, the one who wants
+the job done with the minimum effort and without caring too much about the details.</p>
+<p>Since <tt class="literal"><span class="pre">dot</span></tt> is not a WYSIWYG tool - even if it comes together with a WYSIWYG tool,
+<tt class="literal"><span class="pre">dotty</span></tt> - it is not intended to be used interactively:
+its strength is the ability to <em>programmatically</em> generate diagrams. To fullfill
+this aim, <tt class="literal"><span class="pre">dot</span></tt> uses a simple but powerful graph description language. You
+just give (very high level) instructions to <tt class="literal"><span class="pre">dot</span></tt> and it will draw the diagrams
+for you, taking into account all the low level details. Whereas the user
+has a faily large choice of customization
+options and can control the final output in many ways, it is not at all easy
+to force <tt class="literal"><span class="pre">dot</span></tt> to do <em>exactly</em> what one wants.</p>
+<p>Expecting that would mean to fight with the tool.
+You should think of <tt class="literal"><span class="pre">dot</span></tt> as a kind of smart boy,
+who likes to do things his own way and who is very good at it, but becomes
+nervous if the master tries to put too much pressure on him.
+The right attitude with <tt class="literal"><span class="pre">dot</span></tt> (just as with Latex) is to trust it and
+let it to do the job.
+At the end, when <tt class="literal"><span class="pre">dot</span></tt> has finished its part, the user can always
+refine the graph by hand, by using <tt class="literal"><span class="pre">dotty</span></tt>, the interactive editor
+of <tt class="literal"><span class="pre">dot</span></tt> diagrams which comes with GraphViz and has the ability to read
+and generate <tt class="literal"><span class="pre">dot</span></tt> code.
+But in most cases, the user is not expected to do anything manually,
+since <tt class="literal"><span class="pre">dot</span></tt> works pretty well. The right way to go is to customize
+<tt class="literal"><span class="pre">dot</span></tt> options, then the user can programmatically generate one or
+one hundred diagrams with the least effort.</p>
+<p><tt class="literal"><span class="pre">dot</span></tt> is especially useful in repetitive and automatic tasks, since
+it is not difficult to generate <tt class="literal"><span class="pre">dot</span></tt> code.
+For instance, <tt class="literal"><span class="pre">dot</span></tt> comes very handy in the area of automatic documentation
+of code. This kind of jobs can be down with UML tools, but <tt class="literal"><span class="pre">dot</span></tt> has an
+advantage over them in terms of easy of use, flat learning curve and
+flexibility. On top of that, <tt class="literal"><span class="pre">dot</span></tt> is very fast, since it is written in C
+and can generate very complicated diagrams in fractions of second.</p>
+</div>
+<div class="section" id="hello-world-from-dot">
+<h1><a name="hello-world-from-dot">Hello World from <tt class="literal"><span class="pre">dot</span></tt></a></h1>
+<p><tt class="literal"><span class="pre">dot</span></tt> code has a C-ish syntax and it is quite readable even from somebody
+who has not read the manual. For instance, this <tt class="literal"><span class="pre">dot</span></tt> script:</p>
+<blockquote>
+<pre class="literal-block">
+graph hello{
+
+// Comment: Hello World from ``dot``
+// a graph with a single node Node1
+
+Node1 [label=&quot;Hello, World!&quot;]
+
+}
+</pre>
+</blockquote>
+<p>generates the following picture:</p>
+<div class="figure">
+<p><img alt="fig1.png" src="fig1.png" /></p>
+</div>
+<p>Having saved this code in a file called <tt class="literal"><span class="pre">hello.dot</span></tt>, the graph can be
+generated and shown on the screen with a simple one-liner:</p>
+<blockquote>
+<pre class="literal-block">
+$ dot hello.dot -Tps | gv -
+</pre>
+</blockquote>
+<p>The <tt class="literal"><span class="pre">-Tps</span></tt> option generates postscript
+code, which is then piped to the ghostview utility. Notice that
+I am running my examples on a Linux machine with ghostview installed,
+but <tt class="literal"><span class="pre">dot</span></tt> works equally well under Windows, so you may trivially
+adapt the examples.</p>
+<p>If the user is satisfied with the output, it can save it into a file:</p>
+<blockquote>
+<pre class="literal-block">
+$ dot hello.dot -Tps -o hello.ps
+</pre>
+</blockquote>
+<p>Most probably the user may want to tweak with the options,
+for instance adding colors and changing the font size.
+This is not difficult:</p>
+<blockquote>
+<pre class="literal-block">
+graph hello2{
+
+// Hello World with nice colors and big fonts
+
+Node1 [label=&quot;Hello, World!&quot;, color=Blue, fontcolor=Red,
+ fontsize=24, shape=box]
+
+}
+</pre>
+</blockquote>
+<p>This draws a blue square with a red label:</p>
+<div class="figure">
+<p><img alt="fig2.png" src="fig2.png" /></p>
+</div>
+<p>All X-Window colors and fonts are available.</p>
+<p><tt class="literal"><span class="pre">dot</span></tt> is quite tolerant: the language is case insensitive and
+quoting the options (color=&quot;Blue&quot;, shape=&quot;box&quot;) will work too.
+Moreover, in order to make happy C fans, semicolons can be used
+to terminate statements and they will simply be ignored.</p>
+</div>
+<div class="section" id="basic-concepts-of-dot">
+<h1><a name="basic-concepts-of-dot">Basic concepts of <tt class="literal"><span class="pre">dot</span></tt></a></h1>
+<p>A generic <tt class="literal"><span class="pre">dot</span></tt> graph is composed by nodes and edges.
+Our <tt class="literal"><span class="pre">hello.dot</span></tt> example contains a single node and no edges.
+Edges enter in the game when there are relationships between nodes,
+for instance hierarchical relationships as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+digraph simple_hierarchy{
+
+B [label=&quot;The boss&quot;] // node B
+E [label=&quot;The employee&quot;] // node E
+
+B-&gt;E [label=&quot;commands&quot;, fontcolor=darkgreen] // edge B-&gt;E
+
+}
+</pre>
+</blockquote>
+<div class="figure">
+<p><img alt="fig3.png" src="fig3.png" /></p>
+</div>
+<p><tt class="literal"><span class="pre">dot</span></tt> is especially good at drawing directed graph such this, where
+there is a natural direction (notice that GraphViz also includes the <tt class="literal"><span class="pre">neato</span></tt>
+tool, which is quite similar to <tt class="literal"><span class="pre">dot</span></tt> and is especially targeted to
+undirected graphs).
+In this example the direction is from the boss, who commands,
+to the employee, who obeys. Of course in <tt class="literal"><span class="pre">dot</span></tt> one has the freedom
+to revert social hierarchies ;):</p>
+<blockquote>
+<pre class="literal-block">
+digraph revolution{
+
+B [label=&quot;The boss&quot;] // node B
+E [label=&quot;The employee&quot;] // node E
+
+B-&gt;E [label=&quot;commands&quot;, dir=back, fontcolor=red]
+// revert arrow direction
+
+}
+</pre>
+</blockquote>
+<div class="figure">
+<p><img alt="fig4.png" src="fig4.png" /></p>
+</div>
+<p>Sometimes, one wants to put on the same level things of the
+same importance; this can be done with the rank option, as
+in the following example, which describes a hierarchy with a boss,
+two employees of the same rank, John and Jack, and a lower
+rank employee Al who depends from John:</p>
+<blockquote>
+<pre class="literal-block">
+digraph hierarchy{
+
+nodesep=1.0 // increases the separation between nodes
+
+node [color=Red,fontname=Courier]
+edge [color=Blue, style=dashed] //setup options
+
+Boss-&gt;{ John Jack} // the boss has two employees
+
+{rank=same; John Jack} //they have the same rank
+
+John -&gt; Al // John has a subordinate
+
+John-&gt;Jack [dir=both] // but still is on the same level as Jack
+}
+</pre>
+</blockquote>
+<div class="figure">
+<p><img alt="fig5.png" src="fig5.png" /></p>
+</div>
+<p>This example shows a nifty feature of <tt class="literal"><span class="pre">dot</span></tt>: if the user forgets
+to give it explicit labels, it will use the name of the nodes as
+default labels. The default colors and style can be set for nodes and
+edges respectively. It is also possible to control the separation
+between (all) nodes by tuning the <tt class="literal"><span class="pre">nodesep</span></tt> option.
+We leave for our readers to see what happens without the rank option
+(hint: you get a very ugly graph).</p>
+<p><tt class="literal"><span class="pre">dot</span></tt> is quite sophisticated and
+there are dozen of options which are deeply discussed in the excellent
+documentation. In particular, the man page (<tt class="literal"><span class="pre">man</span> <span class="pre">dot</span></tt>) is especially
+useful and well done. The documentation also explain how to draw
+graphs containing subgraphs. However those are advanced features which
+are outside the scope of a brief presentation.</p>
+<p>Here we will discuss another feature instead: the ability to generate output
+in different formats.
+Depending on the requirements, different formats can be more or
+less suitable. For the purpose of generating printed documentation,
+the postscript format is quite handy. On the other hand, if the documentation
+has to be converted in html format and put on a Web page, a png
+format can be handy. It is quite trivial to get it:</p>
+<blockquote>
+<pre class="literal-block">
+$ dot hello.dot -Tpng -o hello.png
+</pre>
+</blockquote>
+<p>There are <em>many</em> others available formats, including all the common ones
+such as gif, jpg, wbmp, fig and more exotic ones.</p>
+</div>
+<div class="section" id="generating-dot-code">
+<h1><a name="generating-dot-code">Generating <tt class="literal"><span class="pre">dot</span></tt> code</a></h1>
+<p><tt class="literal"><span class="pre">dot</span></tt> is not a real programming language, nevertheless it is pretty easy
+to interface <tt class="literal"><span class="pre">dot</span></tt> with a real programming language. Bindings for
+many programming languages - including Java, Perl and Python - are already
+available. A more lightweight alternative is just to generate the <tt class="literal"><span class="pre">dot</span></tt> code
+from your preferred language.
+Doing so allows the user to completely automatize the graph generation.
+Here I will give a simple Python example using this technique.</p>
+<p>This example script shows how to draw Python class hierarchies
+with the least effort; it may help you in documenting your code.</p>
+<p>Here is the script:</p>
+<blockquote>
+<pre class="literal-block">
+# dot.py
+
+&quot;Require Python 2.3 (or 2.2. with from __future__ import generators)&quot;
+
+def dotcode(cls):
+ setup='node [color=Green,fontcolor=Blue,fontname=Courier]\n'
+ name='hierarchy_of_%s' % cls.__name__
+ code='\n'.join(codegenerator(cls))
+ return &quot;digraph %s{\n\n%s\n%s\n}&quot; % (name, setup, code)
+
+def codegenerator(cls):
+ &quot;Returns a line of dot code at each iteration.&quot;
+ # works for new style classes; see my Cookbook
+ # recipe for a more general solution
+ for c in cls.__mro__:
+ bases=c.__bases__
+ if bases: # generate edges parent -&gt; child
+ yield ''.join([' %s -&gt; %s\n' % ( b.__name__,c.__name__)
+ for b in bases])
+ if len(bases) &gt; 1: # put all parents on the same level
+ yield &quot; {rank=same; %s}\n&quot; % ''.join(
+ ['%s ' % b.__name__ for b in bases])
+
+if __name__==&quot;__main__&quot;:
+ # returns the dot code generating a simple diamond hierarchy
+ class A(object): pass
+ class B(A): pass
+ class C(A): pass
+ class D(B,C): pass
+ print dotcode(D)
+</pre>
+</blockquote>
+<p>The function <tt class="literal"><span class="pre">dotcode</span></tt> takes a class and returns the <tt class="literal"><span class="pre">dot</span></tt> source
+code needed to plot the genealogical tree of that class.
+The source code is generated by <tt class="literal"><span class="pre">codegenerator</span></tt>, which traverses the list
+of the ancestors of the class (a.k.a. the Method Resolution Order of
+the class) and determines the edges and the nodes of the hierarchy.
+<tt class="literal"><span class="pre">codegenerator</span></tt> is a generator which returns an iterator yielding
+a line of <tt class="literal"><span class="pre">dot</span></tt> code at each iteration. Generators are a cool
+recent addition to Python; they come particularly handy for the purpose
+of generating text or source code.</p>
+<p>The output of the script is the following self-explanatory <tt class="literal"><span class="pre">dot</span></tt> code:</p>
+<blockquote>
+<pre class="literal-block">
+digraph hierarchy_of_D{
+
+node [color=Green,fontcolor=Blue,font=Courier]
+
+ B -&gt; D
+ C -&gt; D
+
+ {rank=same; B C }
+
+ A -&gt; B
+
+ A -&gt; C
+
+ object -&gt; A
+
+}
+</pre>
+</blockquote>
+<p>Now the simple one-liner:</p>
+<blockquote>
+<pre class="literal-block">
+$ python dot.py|dot -Tpng -o x.png
+</pre>
+</blockquote>
+<p>generates the following picture:</p>
+<div class="figure">
+<p><img alt="fig6.png" src="fig6.png" /></p>
+</div>
+</div>
+<div class="section" id="references">
+<h1><a name="references">References</a></h1>
+<p>You may download <tt class="literal"><span class="pre">dot</span></tt> and the others tool coming with GraphViz at the
+official home-page of the project:</p>
+<p><a class="reference" href="http://www.graphviz.org">http://www.graphviz.org</a></p>
+<p>You will also find plenty of documentation and links to the mailing list.</p>
+<p>Perl and Python bindings are available here</p>
+<p><a class="reference" href="http://theoryx5.uwinnipeg.ca/CPAN/data/GraphViz/GraphViz.html">http://theoryx5.uwinnipeg.ca/CPAN/data/GraphViz/GraphViz.html</a></p>
+<p>(Perl bindings, thanks to Leon Brocard)</p>
+<p>and here</p>
+<p><a class="reference" href="http://www.cs.brown.edu/~er/software/">http://www.cs.brown.edu/~er/software/</a></p>
+<p>(Python bindings, thanks to Manos Renieris).</p>
+<p>The script <tt class="literal"><span class="pre">dot.py</span></tt> I presented in this article is rather minimalistic.
+This is on purpose. A much more sophisticated version with additional
+examples is discussed in my Python Cookbook recipe</p>
+<p><a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213898">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213898</a></p>
+</div>
+</div>
+<hr class="footer" />
+<div class="footer">
+<a class="reference" href="dot.txt">View document source</a>.
+Generated on: 2004-03-08 08:13 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/dot/dot.py b/pypers/dot/dot.py
new file mode 100755
index 0000000..150b311
--- /dev/null
+++ b/pypers/dot/dot.py
@@ -0,0 +1,21 @@
+# dot.py
+def dotcode(cls):
+ setup='node [color=Green,fontcolor=Blue,fontname=Courier]\n'
+ name='hierarchy_of_%s' % cls.__name__
+ code='\n'.join(codegenerator(cls))
+ return "digraph %s{\n\n%s\n%s\n}" % (name,setup,code)
+def codegenerator(cls):
+ for c in cls.__mro__:
+ bases=c.__bases__
+ if bases: # generate edges parents -> child
+ yield ''.join([' %s -> %s\n' % ( b.__name__,c.__name__)
+ for b in bases])
+ if len(bases) > 1: # put all parents on the same level
+ yield " {rank=same; %s}\n" % ''.join([
+ '%s ' % b.__name__ for b in bases])
+if __name__=="__main__":
+ class A(object): pass
+ class B(A): pass
+ class C(A): pass
+ class D(B,C): pass
+ print dotcode(D)
diff --git a/pypers/dot/dot.tex b/pypers/dot/dot.tex
new file mode 100755
index 0000000..98d239a
--- /dev/null
+++ b/pypers/dot/dot.tex
@@ -0,0 +1,460 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper]{geometry}
+%% 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}}
+% 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{Drawing graphs the easy way: an introduction to dot}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Drawing graphs the easy way: an introduction to dot}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+
+
+%___________________________________________________________________________
+
+\hypertarget{got-a-graphing-problem}{}
+\pdfbookmark[0]{Got a graphing problem?}{got-a-graphing-problem}
+\section*{Got a graphing problem?}
+
+You must give a presentation tomorrow and you haven't prepared any
+figure yet; you must document your last project and you need to plot
+your most hairy class hierarchies; you are asked to provide ten slightly
+different variations of the same picture; you are pathologically unable to put
+your finger on a mouse and drawing anything more complex that a square ...
+in all these cases, dont' worry! \texttt{dot} comes at the rescue and
+can save your day!
+
+
+%___________________________________________________________________________
+
+\hypertarget{what-is-dot}{}
+\pdfbookmark[0]{What is dot?}{what-is-dot}
+\section*{What is \texttt{dot}?}
+
+\texttt{dot} is a tool to generate nice-looking diagrams with a minimum of
+effort. \texttt{dot} is distributed as a part of \texttt{GraphViz}, an
+Open Source project developed at AT{\&}T and released under a MIT licence.
+It is a high quality and mature product, with very good
+documentation and support, available on all major platforms,
+including Unix/Linux, Windows and Mac. There is an official home-page and
+a supporting mailing list.
+
+
+%___________________________________________________________________________
+
+\hypertarget{what-can-i-do-with-dot}{}
+\pdfbookmark[0]{What can I do with dot ?}{what-can-i-do-with-dot}
+\section*{What can I do with \texttt{dot} ?}
+
+First of all, let me make clear that \texttt{dot} is not just another paint program,
+nor a vector graphics program. \texttt{dot} is a scriptable batch-oriented graphing
+tool; it is to vector drawing programs as \texttt{LaTex} is to word processors.
+If you want to have control on every single pixel in your diagram,
+or if you are an artistic person who likes to draw free hand, then \texttt{dot}
+is not for you. \texttt{dot} is a tool for the lazy developer, the one who wants
+the job done with the minimum effort and without caring too much about the details.
+
+Since \texttt{dot} is not a WYSIWYG tool - even if it comes together with a WYSIWYG tool,
+\texttt{dotty} - it is not intended to be used interactively:
+its strength is the ability to \emph{programmatically} generate diagrams. To fullfill
+this aim, \texttt{dot} uses a simple but powerful graph description language. You
+just give (very high level) instructions to \texttt{dot} and it will draw the diagrams
+for you, taking into account all the low level details. Whereas the user
+has a faily large choice of customization
+options and can control the final output in many ways, it is not at all easy
+to force \texttt{dot} to do \emph{exactly} what one wants.
+
+Expecting that would mean to fight with the tool.
+You should think of \texttt{dot} as a kind of smart boy,
+who likes to do things his own way and who is very good at it, but becomes
+nervous if the master tries to put too much pressure on him.
+The right attitude with \texttt{dot} (just as with Latex) is to trust it and
+let it to do the job.
+At the end, when \texttt{dot} has finished its part, the user can always
+refine the graph by hand, by using \texttt{dotty}, the interactive editor
+of \texttt{dot} diagrams which comes with GraphViz and has the ability to read
+and generate \texttt{dot} code.
+But in most cases, the user is not expected to do anything manually,
+since \texttt{dot} works pretty well. The right way to go is to customize
+\texttt{dot} options, then the user can programmatically generate one or
+one hundred diagrams with the least effort.
+
+\texttt{dot} is especially useful in repetitive and automatic tasks, since
+it is not difficult to generate \texttt{dot} code.
+For instance, \texttt{dot} comes very handy in the area of automatic documentation
+of code. This kind of jobs can be down with UML tools, but \texttt{dot} has an
+advantage over them in terms of easy of use, flat learning curve and
+flexibility. On top of that, \texttt{dot} is very fast, since it is written in C
+and can generate very complicated diagrams in fractions of second.
+
+
+%___________________________________________________________________________
+
+\hypertarget{hello-world-from-dot}{}
+\pdfbookmark[0]{Hello World from dot}{hello-world-from-dot}
+\section*{Hello World from \texttt{dot}}
+
+\texttt{dot} code has a C-ish syntax and it is quite readable even from somebody
+who has not read the manual. For instance, this \texttt{dot} script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{graph~hello{\{}}\\
+\mbox{}\\
+\mbox{//~Comment:~Hello~World~from~``dot``}\\
+\mbox{//~a~graph~with~a~single~node~Node1}\\
+\mbox{}\\
+\mbox{Node1~[label="Hello,~World!"]}\\
+\mbox{}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+generates the following picture:
+\begin{figure}
+
+\includegraphics{fig1.ps}
+\end{figure}
+
+Having saved this code in a file called \texttt{hello.dot}, the graph can be
+generated and shown on the screen with a simple one-liner:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~dot~hello.dot~-Tps~|~gv~-}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The \texttt{-Tps} option generates postscript
+code, which is then piped to the ghostview utility. Notice that
+I am running my examples on a Linux machine with ghostview installed,
+but \texttt{dot} works equally well under Windows, so you may trivially
+adapt the examples.
+
+If the user is satisfied with the output, it can save it into a file:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~dot~hello.dot~-Tps~-o~hello.ps}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Most probably the user may want to tweak with the options,
+for instance adding colors and changing the font size.
+This is not difficult:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{graph~hello2{\{}}\\
+\mbox{}\\
+\mbox{//~Hello~World~with~nice~colors~and~big~fonts}\\
+\mbox{}\\
+\mbox{Node1~[label="Hello,~World!",~color=Blue,~fontcolor=Red,}\\
+\mbox{~~~~fontsize=24,~shape=box]}\\
+\mbox{~}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This draws a blue square with a red label:
+\begin{figure}
+
+\includegraphics{fig2.ps}
+\end{figure}
+
+All X-Window colors and fonts are available.
+
+\texttt{dot} is quite tolerant: the language is case insensitive and
+quoting the options (color=``Blue'', shape=``box'') will work too.
+Moreover, in order to make happy C fans, semicolons can be used
+to terminate statements and they will simply be ignored.
+
+
+%___________________________________________________________________________
+
+\hypertarget{basic-concepts-of-dot}{}
+\pdfbookmark[0]{Basic concepts of dot}{basic-concepts-of-dot}
+\section*{Basic concepts of \texttt{dot}}
+
+A generic \texttt{dot} graph is composed by nodes and edges.
+Our \texttt{hello.dot} example contains a single node and no edges.
+Edges enter in the game when there are relationships between nodes,
+for instance hierarchical relationships as in this example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{digraph~simple{\_}hierarchy{\{}}\\
+\mbox{}\\
+\mbox{B~[label="The~boss"]~~~~~~//~node~B}\\
+\mbox{E~[label="The~employee"]~~//~node~E}\\
+\mbox{}\\
+\mbox{B->E~[label="commands",~fontcolor=darkgreen]~//~edge~B->E}\\
+\mbox{}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+\begin{figure}
+
+\includegraphics{fig3.ps}
+\end{figure}
+
+\texttt{dot} is especially good at drawing directed graph such this, where
+there is a natural direction (notice that GraphViz also includes the \texttt{neato}
+tool, which is quite similar to \texttt{dot} and is especially targeted to
+undirected graphs).
+In this example the direction is from the boss, who commands,
+to the employee, who obeys. Of course in \texttt{dot} one has the freedom
+to revert social hierarchies ;):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{digraph~revolution{\{}}\\
+\mbox{}\\
+\mbox{B~[label="The~boss"]~~~~~~//~node~B}\\
+\mbox{E~[label="The~employee"]~~//~node~E}\\
+\mbox{}\\
+\mbox{B->E~[label="commands",~dir=back,~fontcolor=red]~~}\\
+\mbox{//~revert~arrow~direction~}\\
+\mbox{}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+\begin{figure}
+
+\includegraphics{fig4.ps}
+\end{figure}
+
+Sometimes, one wants to put on the same level things of the
+same importance; this can be done with the rank option, as
+in the following example, which describes a hierarchy with a boss,
+two employees of the same rank, John and Jack, and a lower
+rank employee Al who depends from John:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{digraph~hierarchy{\{}}\\
+\mbox{}\\
+\mbox{nodesep=1.0~//~increases~the~separation~between~nodes}\\
+\mbox{}\\
+\mbox{node~[color=Red,fontname=Courier]}\\
+\mbox{edge~[color=Blue,~style=dashed]~//setup~options}\\
+\mbox{}\\
+\mbox{Boss->{\{}~John~Jack{\}}~//~the~boss~has~two~employees}\\
+\mbox{}\\
+\mbox{{\{}rank=same;~John~Jack{\}}~//they~have~the~same~rank}\\
+\mbox{}\\
+\mbox{John~->~Al~//~John~has~a~subordinate~}\\
+\mbox{}\\
+\mbox{John->Jack~[dir=both]~//~but~still~is~on~the~same~level~as~Jack}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+\begin{figure}
+
+\includegraphics{fig5.ps}
+\end{figure}
+
+This example shows a nifty feature of \texttt{dot}: if the user forgets
+to give it explicit labels, it will use the name of the nodes as
+default labels. The default colors and style can be set for nodes and
+edges respectively. It is also possible to control the separation
+between (all) nodes by tuning the \texttt{nodesep} option.
+We leave for our readers to see what happens without the rank option
+(hint: you get a very ugly graph).
+
+\texttt{dot} is quite sophisticated and
+there are dozen of options which are deeply discussed in the excellent
+documentation. In particular, the man page (\texttt{man dot}) is especially
+useful and well done. The documentation also explain how to draw
+graphs containing subgraphs. However those are advanced features which
+are outside the scope of a brief presentation.
+
+Here we will discuss another feature instead: the ability to generate output
+in different formats.
+Depending on the requirements, different formats can be more or
+less suitable. For the purpose of generating printed documentation,
+the postscript format is quite handy. On the other hand, if the documentation
+has to be converted in html format and put on a Web page, a png
+format can be handy. It is quite trivial to get it:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~dot~hello.dot~-Tpng~-o~hello.ps}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+There are \emph{many} others available formats, including all the common ones
+such as gif, jpg, wbmp, fig and more exotic ones.
+
+
+%___________________________________________________________________________
+
+\hypertarget{generating-dot-code}{}
+\pdfbookmark[0]{Generating dot code}{generating-dot-code}
+\section*{Generating \texttt{dot} code}
+
+\texttt{dot} is not a real programming language, nevertheless it is pretty easy
+to interface \texttt{dot} with a real programming language. Bindings for
+many programming languages - including Java, Perl and Python - are already
+available. A more lightweight alternative is just to generate the \texttt{dot} code
+from your preferred language.
+Doing so allows the user to completely automatize the graph generation.
+Here I will give a simple Python example using this technique.
+
+This example script shows how to draw Python class hierarchies
+with the least effort; it may help you in documenting your code.
+
+Here is the script:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}~dot.py~}\\
+\mbox{}\\
+\mbox{"Require~Python~2.3~(or~2.2.~with~from~{\_}{\_}future{\_}{\_}~import~generators)"}\\
+\mbox{}\\
+\mbox{def~dotcode(cls):}\\
+\mbox{~~~~setup='node~[color=Green,fontcolor=Blue,fontname=Courier]{\textbackslash}n'}\\
+\mbox{~~~~name='hierarchy{\_}of{\_}{\%}s'~{\%}~cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~code='{\textbackslash}n'.join(codegenerator(cls))}\\
+\mbox{~~~~return~"digraph~{\%}s{\{}{\textbackslash}n{\textbackslash}n{\%}s{\textbackslash}n{\%}s{\textbackslash}n{\}}"~{\%}~(name,~setup,~code)}\\
+\mbox{}\\
+\mbox{def~codegenerator(cls):}\\
+\mbox{~~~~"Returns~a~line~of~dot~code~at~each~iteration."}\\
+\mbox{~~~~{\#}~works~for~new~style~classes;~see~my~Cookbook}\\
+\mbox{~~~~{\#}~recipe~for~a~more~general~solution}\\
+\mbox{~~~~for~c~in~cls.{\_}{\_}mro{\_}{\_}:}\\
+\mbox{~~~~~~~~bases=c.{\_}{\_}bases{\_}{\_}}\\
+\mbox{~~~~~~~~if~bases:~{\#}~generate~edges~parent~->~child}\\
+\mbox{~~~~~~~~~~~~yield~''.join(['~{\%}s~->~{\%}s{\textbackslash}n'~{\%}~(~b.{\_}{\_}name{\_}{\_},c.{\_}{\_}name{\_}{\_})}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~for~b~in~bases])}\\
+\mbox{~~~~~~~~if~len(bases)~>~1:~{\#}~put~all~parents~on~the~same~level}\\
+\mbox{~~~~~~~~~~~~yield~"~{\{}rank=same;~{\%}s{\}}{\textbackslash}n"~{\%}~''.join(}\\
+\mbox{~~~~~~~~~~~~~~~~['{\%}s~'~{\%}~b.{\_}{\_}name{\_}{\_}~for~b~in~bases])}\\
+\mbox{}\\
+\mbox{if~{\_}{\_}name{\_}{\_}=="{\_}{\_}main{\_}{\_}":~}\\
+\mbox{~~~~{\#}~returns~the~dot~code~generating~a~simple~diamond~hierarchy}\\
+\mbox{~~~~class~A(object):~pass}\\
+\mbox{~~~~class~B(A):~pass}\\
+\mbox{~~~~class~C(A):~pass}\\
+\mbox{~~~~class~D(B,C):~pass}\\
+\mbox{~~~~print~dotcode(D)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The function \texttt{dotcode} takes a class and returns the \texttt{dot} source
+code needed to plot the genealogical tree of that class.
+The source code is generated by \texttt{codegenerator}, which traverses the list
+of the ancestors of the class (a.k.a. the Method Resolution Order of
+the class) and determines the edges and the nodes of the hierarchy.
+\texttt{codegenerator} is a generator which returns an iterator yielding
+a line of \texttt{dot} code at each iteration. Generators are a cool
+recent addition to Python; they come particularly handy for the purpose
+of generating text or source code.
+
+The output of the script is the following self-explanatory \texttt{dot} code:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{digraph~hierarchy{\_}of{\_}D{\{}}\\
+\mbox{}\\
+\mbox{node~[color=Green,fontcolor=Blue,font=Courier]}\\
+\mbox{}\\
+\mbox{~B~->~D}\\
+\mbox{~C~->~D}\\
+\mbox{}\\
+\mbox{~{\{}rank=same;~B~C~{\}}}\\
+\mbox{}\\
+\mbox{~A~->~B}\\
+\mbox{}\\
+\mbox{~A~->~C}\\
+\mbox{}\\
+\mbox{~object~->~A}\\
+\mbox{}\\
+\mbox{{\}}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Now the simple one-liner:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python~dot.py|dot~-Tpng~-o~x.ps}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+generates the following picture:
+\begin{figure}
+
+\includegraphics{fig6.ps}
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{references}{}
+\pdfbookmark[0]{References}{references}
+\section*{References}
+
+You may download \texttt{dot} and the others tool coming with GraphViz at the
+official home-page of the project:
+
+\href{http://www.graphviz.org}{http://www.graphviz.org}
+
+You will also find plenty of documentation and links to the mailing list.
+
+Perl and Python bindings are available here
+
+\href{http://theoryx5.uwinnipeg.ca/CPAN/data/GraphViz/GraphViz.html}{http://theoryx5.uwinnipeg.ca/CPAN/data/GraphViz/GraphViz.html}
+
+(Perl bindings, thanks to Leon Brocard)
+
+and here
+
+\href{http://www.cs.brown.edu/~er/software/}{http://www.cs.brown.edu/{\textasciitilde}er/software/}
+
+(Python bindings, thanks to Manos Renieris).
+
+The script \texttt{dot.py} I presented in this article is rather minimalistic.
+This is on purpose. A much more sophisticated version with additional
+examples is discussed in my Python Cookbook recipe
+
+\href{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213898}{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213898}
+
+\end{document}
+
diff --git a/pypers/dot/dot.txt b/pypers/dot/dot.txt
new file mode 100755
index 0000000..4b546e7
--- /dev/null
+++ b/pypers/dot/dot.txt
@@ -0,0 +1,351 @@
+Drawing graphs the easy way: an introduction to ``dot``
+=====================================================================
+
+Got a graphing problem?
+----------------------------
+
+You must give a presentation tomorrow and you haven't prepared any
+figure yet; you must document your last project and you need to plot
+your most hairy class hierarchies; you are asked to provide ten slightly
+different variations of the same picture; you are pathologically unable to put
+your finger on a mouse and drawing anything more complex that a square ...
+in all these cases, dont' worry! ``dot`` comes at the rescue and
+can save your day!
+
+What is ``dot``?
+--------------------------------
+
+``dot`` is a tool to generate nice-looking diagrams with a minimum of
+effort. ``dot`` is distributed as a part of ``GraphViz``, an
+Open Source project developed at AT&T and released under a MIT licence.
+It is a high quality and mature product, with very good
+documentation and support, available on all major platforms,
+including Unix/Linux, Windows and Mac. There is an official home-page and
+a supporting mailing list.
+
+What can I do with ``dot`` ?
+---------------------------------
+
+First of all, let me make clear that ``dot`` is not just another paint program,
+nor a vector graphics program. ``dot`` is a scriptable batch-oriented graphing
+tool; it is to vector drawing programs as ``LaTex`` is to word processors.
+If you want to have control on every single pixel in your diagram,
+or if you are an artistic person who likes to draw free hand, then ``dot``
+is not for you. ``dot`` is a tool for the lazy developer, the one who wants
+the job done with the minimum effort and without caring too much about the details.
+
+Since ``dot`` is not a WYSIWYG tool - even if it comes together with a WYSIWYG tool,
+``dotty`` - it is not intended to be used interactively:
+its strength is the ability to *programmatically* generate diagrams. To fullfill
+this aim, ``dot`` uses a simple but powerful graph description language. You
+just give (very high level) instructions to ``dot`` and it will draw the diagrams
+for you, taking into account all the low level details. Whereas the user
+has a faily large choice of customization
+options and can control the final output in many ways, it is not at all easy
+to force ``dot`` to do *exactly* what one wants.
+
+Expecting that would mean to fight with the tool.
+You should think of ``dot`` as a kind of smart boy,
+who likes to do things his own way and who is very good at it, but becomes
+nervous if the master tries to put too much pressure on him.
+The right attitude with ``dot`` (just as with Latex) is to trust it and
+let it to do the job.
+At the end, when ``dot`` has finished its part, the user can always
+refine the graph by hand, by using ``dotty``, the interactive editor
+of ``dot`` diagrams which comes with GraphViz and has the ability to read
+and generate ``dot`` code.
+But in most cases, the user is not expected to do anything manually,
+since ``dot`` works pretty well. The right way to go is to customize
+``dot`` options, then the user can programmatically generate one or
+one hundred diagrams with the least effort.
+
+``dot`` is especially useful in repetitive and automatic tasks, since
+it is not difficult to generate ``dot`` code.
+For instance, ``dot`` comes very handy in the area of automatic documentation
+of code. This kind of jobs can be down with UML tools, but ``dot`` has an
+advantage over them in terms of easy of use, flat learning curve and
+flexibility. On top of that, ``dot`` is very fast, since it is written in C
+and can generate very complicated diagrams in fractions of second.
+
+Hello World from ``dot``
+------------------------------
+
+``dot`` code has a C-ish syntax and it is quite readable even from somebody
+who has not read the manual. For instance, this ``dot`` script:
+
+ ::
+
+ graph hello{
+
+ // Comment: Hello World from ``dot``
+ // a graph with a single node Node1
+
+ Node1 [label="Hello, World!"]
+
+ }
+
+generates the following picture:
+
+.. figure:: fig1.png
+
+Having saved this code in a file called ``hello.dot``, the graph can be
+generated and shown on the screen with a simple one-liner:
+
+ ::
+
+ $ dot hello.dot -Tps | gv -
+
+The ``-Tps`` option generates postscript
+code, which is then piped to the ghostview utility. Notice that
+I am running my examples on a Linux machine with ghostview installed,
+but ``dot`` works equally well under Windows, so you may trivially
+adapt the examples.
+
+If the user is satisfied with the output, it can save it into a file:
+
+ ::
+
+ $ dot hello.dot -Tps -o hello.ps
+
+Most probably the user may want to tweak with the options,
+for instance adding colors and changing the font size.
+This is not difficult:
+
+ ::
+
+ graph hello2{
+
+ // Hello World with nice colors and big fonts
+
+ Node1 [label="Hello, World!", color=Blue, fontcolor=Red,
+ fontsize=24, shape=box]
+
+ }
+
+This draws a blue square with a red label:
+
+.. figure:: fig2.png
+
+All X-Window colors and fonts are available.
+
+``dot`` is quite tolerant: the language is case insensitive and
+quoting the options (color="Blue", shape="box") will work too.
+Moreover, in order to make happy C fans, semicolons can be used
+to terminate statements and they will simply be ignored.
+
+Basic concepts of ``dot``
+--------------------------------------------------------------------------
+
+A generic ``dot`` graph is composed by nodes and edges.
+Our ``hello.dot`` example contains a single node and no edges.
+Edges enter in the game when there are relationships between nodes,
+for instance hierarchical relationships as in this example:
+
+ ::
+
+ digraph simple_hierarchy{
+
+ B [label="The boss"] // node B
+ E [label="The employee"] // node E
+
+ B->E [label="commands", fontcolor=darkgreen] // edge B->E
+
+ }
+
+.. figure:: fig3.png
+
+``dot`` is especially good at drawing directed graph such this, where
+there is a natural direction (notice that GraphViz also includes the ``neato``
+tool, which is quite similar to ``dot`` and is especially targeted to
+undirected graphs).
+In this example the direction is from the boss, who commands,
+to the employee, who obeys. Of course in ``dot`` one has the freedom
+to revert social hierarchies ;):
+
+ ::
+
+ digraph revolution{
+
+ B [label="The boss"] // node B
+ E [label="The employee"] // node E
+
+ B->E [label="commands", dir=back, fontcolor=red]
+ // revert arrow direction
+
+ }
+
+.. figure:: fig4.png
+
+Sometimes, one wants to put on the same level things of the
+same importance; this can be done with the rank option, as
+in the following example, which describes a hierarchy with a boss,
+two employees of the same rank, John and Jack, and a lower
+rank employee Al who depends from John:
+
+ ::
+
+ digraph hierarchy{
+
+ nodesep=1.0 // increases the separation between nodes
+
+ node [color=Red,fontname=Courier]
+ edge [color=Blue, style=dashed] //setup options
+
+ Boss->{ John Jack} // the boss has two employees
+
+ {rank=same; John Jack} //they have the same rank
+
+ John -> Al // John has a subordinate
+
+ John->Jack [dir=both] // but still is on the same level as Jack
+ }
+
+.. figure:: fig5.png
+
+This example shows a nifty feature of ``dot``: if the user forgets
+to give it explicit labels, it will use the name of the nodes as
+default labels. The default colors and style can be set for nodes and
+edges respectively. It is also possible to control the separation
+between (all) nodes by tuning the ``nodesep`` option.
+We leave for our readers to see what happens without the rank option
+(hint: you get a very ugly graph).
+
+``dot`` is quite sophisticated and
+there are dozen of options which are deeply discussed in the excellent
+documentation. In particular, the man page (``man dot``) is especially
+useful and well done. The documentation also explain how to draw
+graphs containing subgraphs. However those are advanced features which
+are outside the scope of a brief presentation.
+
+Here we will discuss another feature instead: the ability to generate output
+in different formats.
+Depending on the requirements, different formats can be more or
+less suitable. For the purpose of generating printed documentation,
+the postscript format is quite handy. On the other hand, if the documentation
+has to be converted in html format and put on a Web page, a png
+format can be handy. It is quite trivial to get it:
+
+ ::
+
+ $ dot hello.dot -Tpng -o hello.png
+
+There are *many* others available formats, including all the common ones
+such as gif, jpg, wbmp, fig and more exotic ones.
+
+Generating ``dot`` code
+----------------------------------------------------------------------------
+
+``dot`` is not a real programming language, nevertheless it is pretty easy
+to interface ``dot`` with a real programming language. Bindings for
+many programming languages - including Java, Perl and Python - are already
+available. A more lightweight alternative is just to generate the ``dot`` code
+from your preferred language.
+Doing so allows the user to completely automatize the graph generation.
+Here I will give a simple Python example using this technique.
+
+This example script shows how to draw Python class hierarchies
+with the least effort; it may help you in documenting your code.
+
+Here is the script:
+
+ ::
+
+ # dot.py
+
+ "Require Python 2.3 (or 2.2. with from __future__ import generators)"
+
+ def dotcode(cls):
+ setup='node [color=Green,fontcolor=Blue,fontname=Courier]\n'
+ name='hierarchy_of_%s' % cls.__name__
+ code='\n'.join(codegenerator(cls))
+ return "digraph %s{\n\n%s\n%s\n}" % (name, setup, code)
+
+ def codegenerator(cls):
+ "Returns a line of dot code at each iteration."
+ # works for new style classes; see my Cookbook
+ # recipe for a more general solution
+ for c in cls.__mro__:
+ bases=c.__bases__
+ if bases: # generate edges parent -> child
+ yield ''.join([' %s -> %s\n' % ( b.__name__,c.__name__)
+ for b in bases])
+ if len(bases) > 1: # put all parents on the same level
+ yield " {rank=same; %s}\n" % ''.join(
+ ['%s ' % b.__name__ for b in bases])
+
+ if __name__=="__main__":
+ # returns the dot code generating a simple diamond hierarchy
+ class A(object): pass
+ class B(A): pass
+ class C(A): pass
+ class D(B,C): pass
+ print dotcode(D)
+
+The function ``dotcode`` takes a class and returns the ``dot`` source
+code needed to plot the genealogical tree of that class.
+The source code is generated by ``codegenerator``, which traverses the list
+of the ancestors of the class (a.k.a. the Method Resolution Order of
+the class) and determines the edges and the nodes of the hierarchy.
+``codegenerator`` is a generator which returns an iterator yielding
+a line of ``dot`` code at each iteration. Generators are a cool
+recent addition to Python; they come particularly handy for the purpose
+of generating text or source code.
+
+The output of the script is the following self-explanatory ``dot`` code:
+
+ ::
+
+ digraph hierarchy_of_D{
+
+ node [color=Green,fontcolor=Blue,font=Courier]
+
+ B -> D
+ C -> D
+
+ {rank=same; B C }
+
+ A -> B
+
+ A -> C
+
+ object -> A
+
+ }
+
+Now the simple one-liner:
+
+ ::
+
+ $ python dot.py|dot -Tpng -o x.png
+
+generates the following picture:
+
+.. figure:: fig6.png
+
+References
+--------------
+
+You may download ``dot`` and the others tool coming with GraphViz at the
+official home-page of the project:
+
+http://www.graphviz.org
+
+You will also find plenty of documentation and links to the mailing list.
+
+Perl and Python bindings are available here
+
+http://theoryx5.uwinnipeg.ca/CPAN/data/GraphViz/GraphViz.html
+
+(Perl bindings, thanks to Leon Brocard)
+
+and here
+
+http://www.cs.brown.edu/~er/software/
+
+(Python bindings, thanks to Manos Renieris).
+
+The script ``dot.py`` I presented in this article is rather minimalistic.
+This is on purpose. A much more sophisticated version with additional
+examples is discussed in my Python Cookbook recipe
+
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213898
diff --git a/pypers/dot/drawBaseFolder.py b/pypers/dot/drawBaseFolder.py
new file mode 100755
index 0000000..1e94c28
--- /dev/null
+++ b/pypers/dot/drawBaseFolder.py
@@ -0,0 +1,3 @@
+from drawMRO2 import MROgraph
+from Products.Archetypes.public import BaseFolder
+print MROgraph(BaseFolder,caption=True)
diff --git a/pypers/dot/drawMRO.txt b/pypers/dot/drawMRO.txt
new file mode 100755
index 0000000..e52b7e2
--- /dev/null
+++ b/pypers/dot/drawMRO.txt
@@ -0,0 +1,263 @@
+Drawing inheritance diagrams with "Dot"
+=================================================
+
+Dot is a very nice graph description language developed
+at MIT and available for free at http://www.graphviz.org/ .
+
+Combined with Python, it makes an ideal tool to
+draw automatically generated diagrams. As an
+example, I will describe here a short recipe which
+produce beautiful inheritance diagrams for Python classes
+(and metaclasses too). In particular the recipe allows to
+display the MRO (Method Resolution Order) for complicate
+inheritance hierarchies. Here is the code::
+
+ #<MROgraph.py>
+
+ """
+ Draw inheritance hierarchies via Dot (http://www.graphviz.org/)
+ Author: M. Simionato
+ E-mail: mis6@pitt.edu
+ Date: August 2003
+ License: Python-like
+ Requires: Python 2.3, dot, standard Unix tools
+ """
+
+ import os,itertools
+
+ PSVIEWER='gv' # you may change these with
+ PNGVIEWER='kview' # your preferred viewers
+ PSFONT='Times' # you may change these too
+ PNGFONT='Courier' # on my system PNGFONT=Times does not work
+
+ def if_(cond,e1,e2=''):
+ "Ternary operator would be"
+ if cond: return e1
+ else: return e2
+
+ def MRO(cls):
+ "Returns the MRO of cls as a text"
+ out=["MRO of %s:" % cls.__name__]
+ for counter,c in enumerate(cls.__mro__):
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=" %s - %s(%s)" % (counter,name,bases)
+ if type(c) is not type: s+="[%s]" % type(c).__name__
+ out.append(s)
+ return '\n'.join(out)
+
+ class MROgraph(object):
+ def __init__(self,*classes,**options):
+ "Generates the MRO graph of a set of given classes."
+ if not classes: raise "Missing class argument!"
+ filename=options.get('filename',"MRO_of_%s.ps" % classes[0].__name__)
+ self.labels=options.get('labels',2)
+ caption=options.get('caption',False)
+ setup=options.get('setup','')
+ name,dotformat=os.path.splitext(filename)
+ format=dotformat[1:]
+ fontopt="fontname="+if_(format=='ps',PSFONT,PNGFONT)
+ nodeopt=' node [%s];\n' % fontopt
+ edgeopt=' edge [%s];\n' % fontopt
+ viewer=if_(format=='ps',PSVIEWER,PNGVIEWER)
+ self.textrepr='\n'.join([MRO(cls) for cls in classes])
+ caption=if_(caption,
+ 'caption [shape=box,label="%s\n",fontsize=9];'
+ % self.textrepr).replace('\n','\\l')
+ setupcode=nodeopt+edgeopt+caption+'\n'+setup+'\n'
+ codeiter=itertools.chain(*[self.genMROcode(cls) for cls in classes])
+ self.dotcode='digraph %s{\n%s%s}' % (
+ name,setupcode,'\n'.join(codeiter))
+ os.system("echo '%s' | dot -T%s > %s; %s %s&" %
+ (self.dotcode,format,filename,viewer,filename))
+ def genMROcode(self,cls):
+ "Generates the dot code for the MRO of a given class"
+ for mroindex,c in enumerate(cls.__mro__):
+ name=c.__name__
+ manyparents=len(c.__bases__) > 1
+ if c.__bases__:
+ yield ''.join([
+ ' edge [style=solid]; %s -> %s %s;\n' % (
+ b.__name__,name,if_(manyparents and self.labels==2,
+ '[label="%s"]' % (i+1)))
+ for i,b in enumerate(c.__bases__)])
+ if manyparents:
+ yield " {rank=same; %s}\n" % ''.join([
+ '"%s"; ' % b.__name__ for b in c.__bases__])
+ number=if_(self.labels,"%s-" % mroindex)
+ label='label="%s"' % (number+name)
+ option=if_(issubclass(cls,type), # if cls is a metaclass
+ '[%s]' % label,
+ '[shape=box,%s]' % label)
+ yield(' %s %s;\n' % (name,option))
+ if type(c) is not type: # c has a custom metaclass
+ metaname=type(c).__name__
+ yield ' edge [style=dashed]; %s -> %s;' % (metaname,name)
+ def __repr__(self):
+ "Returns the Dot representation of the graph"
+ return self.dotcode
+ def __str__(self):
+ "Returns a text representation of the MRO"
+ return self.textrepr
+
+ def testHierarchy(**options):
+ class M(type): pass # metaclass
+ class F(object): pass
+ class E(object): pass
+ class D(object): pass
+ class G(object): __metaclass__=M
+ class C(F,D,G): pass
+ class B(E,D): pass
+ class A(B,C): pass
+ return MROgraph(A,M,**options)
+
+ if __name__=="__main__":
+ testHierarchy() # generates a postscript diagram of A and M hierarchies
+
+ #</MROgraph.py>
+
+The recipe should work as it is on Linux systems (it may require
+to customize the postscript and PNG viewers); Windows users
+must work a bit and change the ``os.system`` line.
+The recipe may be customized and extended at your will;
+but since I wanted the script to fit in one hundred lines I have restricted
+the currently available customization to the following options:
+
+ - *filename=<string>* sets the filename containing the picture;
+
+ - *labels=<int>* turns on/off the labeling of edges;
+
+ - *caption=<boolean>* turns on/off the insertion of a caption;
+
+ - *setup=<string>* allows the user to enter raw Dot code.
+
+By default, *filename* is equal to ``MRO_of_<classname>.ps`` and
+the picture is stored in a postscript format (you may want to
+change this). Dot recognizes many other formats; I only need the PNG
+format for graph to be inserted in Web pages, so
+the recipe currently only works for .ps and .png
+filename extensions, but it is trivial to add new formats.
+The option *labels=0* makes no label appearing in the graph; *labels=1*
+makes labels specifying the MRO order appearing in the graph; *labels=2*
+makes additional labels specifying the ordering of parents
+to appear. This latter option (which is the default) is useful
+since Dot changes the order of the parents in order to draw a nicer
+picture. *caption=True* adds an explanatory caption to the diagram; the
+default is *False*, i.e. no caption is displayed.
+The *setup* option can be used to initialize the graph;
+for instance to change the colors, to fix a size
+(in inches) and an aspect ratio, to set the orientation, etc.
+Here is an example:
+
+>>> from MROgraph import testHierarchy
+>>> colors='edge [color=blue]; node [color=red];'
+>>> g=testHierarchy(filename='A.png', labels=1, caption=True,
+... setup='size="8,6"; ratio=0.7; '+colors)
+
+ .. image:: A.png
+
+If an unrecognized option is passed, it is simply
+ignored and nothing happens: you may want to raise an
+error instead, but this is up to you. Also, you may want
+to add more customizable options; it is easy to change
+the code accordingly. The aim is not to wrap all the
+Dot features, here.
+
+Examples with both old style and new style classes
+----------------------------------------------------------------
+
+This recipe can be convenient for documenting programs,
+but it can also be used as a learning tool,
+when you are studying a large framework with
+a complicated class hierarchy. In this case
+there could be a problem, since by design the
+recipe works with new style only, whereas
+most of legacy Python code works with old-style
+classes; however it is trivial to convert
+an old-style class to new-style, simply
+by composing it with the ``object`` class.
+For instance, let me show what can be learned about the
+ScrolledText widget of Tkinter:
+
+>>> from MROgraph import MROgraph
+>>> import ScrolledText
+>>> class ScrolledText_(ScrolledText.ScrolledText,object):
+... "Creates a new style class from ScrolledText.ScrolledText"
+>>> g=MROgraph(ScrolledText_,
+... filename="ScrolledText.png",setup='size="5,5"; '+colors)
+
+ .. image:: ScrolledText.png
+
+We see here that Tkinter makes use of the mixins Pack, Place and Grid
+(in this order) which combined with BaseWidget generate a Widget. We
+also see that Misc has the precedence over Pack, Place and Grid.
+Finally, all Tkinter classes are old-style classes, instances
+of the metaclass ``classobj``.
+
+I should warn the reader that the MRO for new style and old style
+classes is different, so there are old style hierarchies which cannot
+be converted to new style one. Here is an example:
+
+>>> class A: pass # notice, old style!
+...
+>>> class B(A): pass
+...
+>>> class C(A,B): pass
+...
+>>> class D(C,object): pass
+...
+>>> g=MROgraph(D,filename="D.png",setup=colors,caption=True)
+...
+
+ .. image:: D.png
+
+This only works since A,B and C are old style, since the triangle
+configuration is forbidden for new style classes by the C3 MRO.
+If you try to derive A from object, you will get a MRO error.
+
+On the other hand, there are old style hierarchies which can be
+converted to new style one, but behave differently after the
+conversion. Consider for instance the following old style hierarchy:
+
+>>> class A: a='A' # makes A,B,C and D old style
+...
+>>> class B(A): pass
+...
+>>> class C(A): a='C'
+...
+>>> class D(B,C): pass
+...
+>>> class E(D,object): pass # makes E new style
+...
+>>> g=MROgraph(E,filename='oldstyle.png',setup=colors,caption=True)
+
+ .. image:: oldstyle.png
+
+In this example A comes before C,
+
+>>> E.a
+...
+'A'
+
+whereas for new style classes it would be the opposite:
+
+>>> class A(object): a='A' # makes the whole hierarchy new style
+...
+>>> class B(A): pass
+...
+>>> class C(A): a='C'
+...
+>>> class D(B,C): pass
+...
+>>> class E(D): pass
+...
+>>> g=MROgraph(E,filename='newstyle.png',setup=colors,caption=True)
+
+ .. image:: newstyle.png
+
+Here C has the precedence over A:
+
+>>> E.a
+...
+'C'
+
diff --git a/pypers/dot/drawclasses.py b/pypers/dot/drawclasses.py
new file mode 100755
index 0000000..ee13ba7
--- /dev/null
+++ b/pypers/dot/drawclasses.py
@@ -0,0 +1,102 @@
+"""Given a Python module or package, it draws the classes defined in it.
+It is smart enough to recognize disconnected inheritance hierarchies.
+Usage:
+
+$ python drawclasses.py <module-or-package-in-the-pythonpath>
+"""
+
+import os, types, inspect
+from ms.tools import minidoc
+from ms.iter_utils import skip_redundant as remove_dupl
+
+def ismeta(c):
+ return isinstance(c, type) and issubclass(c, type)
+
+def isclass(c):
+ return isinstance(c, (type, types.ClassType))
+
+def getclasses(module_or_package):
+ x = minidoc.import_(module_or_package)
+ if hasattr(x, "__path__"): # is package
+ packagecontent = os.listdir(x.__path__[0])
+ for fname in packagecontent:
+ if fname.endswith('.py') or fname.endswith('.pyc') \
+ or fname.endswith('.pyd') or fname.endswith('.pyo'):
+ module = x.__name__ + "." + minidoc.body(fname)
+ for cls in getclasses(module):
+ yield cls
+ else: # is module
+ for name,obj in x.__dict__.iteritems():
+ if isclass(obj):
+ yield obj
+
+def codegen(name, ls):
+ drawn = set()
+ yield 'digraph %s{' % name
+ # yield 'caption [shape=box,label=%s\n]' % name
+ yield 'size="7,10"'
+ # yield 'rotate=90'
+ for c in ls:
+ bases = list(c.__bases__)
+ if object in bases:
+ bases.remove(object)
+ for b in bases:
+ yield "%s-> %s" % (b.__name__,c.__name__)
+ drawn.add(b)
+ #yield "%s -> %s [style=dashed]" % (c.__name__,type(c).__name__)
+ drawn.add(c)
+ yield ''
+ for c in drawn:
+ if ismeta(c):
+ name = "%s" % c.__name__
+ else:
+ name = "%s [shape=box]" % c.__name__
+ if hasattr(c,'__metaclass__'):
+ name += ' [color=red]' # ' [style=dashed]'
+ yield name
+ yield '}'
+
+def mro(cls):
+ return inspect.getmro(cls)
+
+def samegraph(A, B): # always True for new-style
+ "Returns true if the MRO's of A and B have an intersection."
+ mro_A = set(mro(A))
+ mro_B = set(mro(B))
+ return bool(mro_A & mro_B)
+
+def graphs(classes):
+ "Given a set of classes, yields the connected subsets."
+ klasses = list(classes)
+ for a in klasses:
+ graph = [a]
+ for b in klasses[:]:
+ if b is not a and samegraph(b,a):
+ graph.append(b)
+ klasses.remove(b)
+ yield graph
+
+def graphgen(classes):
+ """Yields inheritance graphs."""
+ i = 1; singles = [] # single classes
+ for graph in graphs(classes):
+ if len(graph) > 1: # it's a hierarchy
+ name = '"Diagram #%s"' % i; i += 1
+ yield '\n'.join(remove_dupl(codegen(name, graph)))
+ else: # it's a single class (may derive from a built-in)
+ singles.append(graph[0])
+ if singles:
+ name = '"Diagram #%s"' % i
+ yield '\n'.join(remove_dupl(codegen(name, singles)))
+
+if __name__ == '__main__':
+ import sys
+ try:
+ package = sys.argv[1]
+ except:
+ sys.exit(__doc__)
+ classes = list(getclasses(package))
+ for code in graphgen(classes):
+ #os.popen('dot -Tps -o /tmp/x.ps; evince /tmp/x.ps','w').write(code)
+ os.popen('dot -Tpng -o /tmp/x.png; firefox /tmp/x.png','w').write(code)
+ print code
diff --git a/pypers/dot/drawzopefolder.py b/pypers/dot/drawzopefolder.py
new file mode 100755
index 0000000..c5f7903
--- /dev/null
+++ b/pypers/dot/drawzopefolder.py
@@ -0,0 +1,4 @@
+from drawMRO2 import MROgraph
+from OFS.Folder import Folder
+print MROgraph(Folder,caption=True).textrepr
+
diff --git a/pypers/dot/err.txt b/pypers/dot/err.txt
new file mode 100755
index 0000000..d8f9a87
--- /dev/null
+++ b/pypers/dot/err.txt
@@ -0,0 +1,2 @@
+Error: <stdin>:70: syntax error near line 70
+context: Node >>> -> <<< Element
diff --git a/pypers/dot/matcher.py b/pypers/dot/matcher.py
new file mode 100755
index 0000000..e1f78b9
--- /dev/null
+++ b/pypers/dot/matcher.py
@@ -0,0 +1,19 @@
+"""A regular expression based matcher"""
+
+# needs work to get something smart (maybe)
+
+import re
+
+class MetaMatcher(type):
+ def __init__(cls,name,bases,dic):
+ cls.rxs="(%s) % "")|(".join([func.rx for name,func in dic.iteritems()])
+ cls.rxo=re.compile(cls.rxs)
+
+class Matcher(object):
+ __metaclass__=MetaMatcher
+ def __init__(self, text):
+ self.it=self.__class__.rxo.finditer(text)
+ def __iter__(self):
+ return sel
+ def next(self):
+ return self.it.next()
diff --git a/pypers/dot/oldstyle.txt b/pypers/dot/oldstyle.txt
new file mode 100755
index 0000000..55ab058
--- /dev/null
+++ b/pypers/dot/oldstyle.txt
@@ -0,0 +1,53 @@
+Old style:
+
+>>> class A: a='A'
+...
+>>> class B(A): pass
+...
+>>> class C(A): a='C'
+...
+>>> class D(B,C): pass
+...
+>>> D.a
+...
+'A'
+
+New style:
+
+>>> class A(object): a='A'
+...
+>>> class B(A): pass
+...
+>>> class C(A): a='C'
+...
+>>> class D(B,C): pass
+...
+>>> D.a
+...
+'C'
+
+Old style:
+
+>>> class A: pass
+...
+>>> class B(A): pass
+...
+>>> class C(A,B): pass
+...
+>>> class D(C): pass
+...
+
+New style:
+
+>>> class A: pass
+...
+>>> class B(A): pass
+...
+>>> class C(A,B): pass
+...
+>>> class D(object,C): pass
+...
+>>> from MROgraph import MROgraph
+...
+>>> g=MROgraph(D)
+...
diff --git a/pypers/dot/samegraph.py b/pypers/dot/samegraph.py
new file mode 100755
index 0000000..eb6d755
--- /dev/null
+++ b/pypers/dot/samegraph.py
@@ -0,0 +1,30 @@
+"Decide if two classes are in the same inheritance diagram."
+
+import sets, inspect
+
+def samegraph(A,B):
+ "Returns true if the MRO's of A and B have an intersection."
+ mro_A = sets.Set(inspect.getmro(A))
+ mro_A.discard(object)
+ mro_B = sets.Set(inspect.getmro(B))
+ mro_B.discard(object)
+ return bool(mro_A & mro_B)
+
+def graphs(classes):
+ "Given a list of classes, yields the disconnected graphs in the list."
+ for a in classes:
+ #print 'c',classes
+ graph = [a]
+ for b in classes[:]:
+ if b is not a and samegraph(b,a):
+ graph.append(b)
+ classes.remove(b)
+ yield graph
+
+if __name__=='__main__': # test
+ class A: pass
+ class B(object): pass
+ class C(B): pass
+ assert samegraph(B,C)
+ assert not samegraph(A,B)
+ for graph in graphs([A,B,C]): print graph
diff --git a/pypers/dot/style.tex b/pypers/dot/style.tex
new file mode 100755
index 0000000..b695c98
--- /dev/null
+++ b/pypers/dot/style.tex
@@ -0,0 +1,20 @@
+% donot indent first line.
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{5pt plus 2pt minus 1pt}
+
+% sloppy
+% ------
+% Less strict (opposite to default fussy) space size between words. Therefore
+% less hyphenation.
+\sloppy
+
+% fonts
+% -----
+% times for pdf generation, gives smaller pdf files.
+%
+% But in standard postscript fonts: courier and times/helvetica do not fit.
+% Maybe use pslatex.
+\usepackage{times}
+
+% pagestyle
+\pagestyle{headings}
diff --git a/pypers/dot/zip.sh b/pypers/dot/zip.sh
new file mode 100755
index 0000000..2f1e831
--- /dev/null
+++ b/pypers/dot/zip.sh
@@ -0,0 +1,8 @@
+dot hello.dot -Tps -o fig1.ps
+dot hello2.dot -Tps -o fig2.ps
+dot simple_hierarchy.dot -Tps -o fig3.ps
+dot revolution.dot -Tps -o fig4.ps
+dot hierarchy.dot -Tps -o fig5.ps
+dot hierarchy_of_D.dot -Tps -o fig6.ps
+
+zip dot dot.txt dot.html default.css fig1.* fig2.* fig3.* fig4.* fig5.* fig6.* fig6.*
diff --git a/pypers/erf.py b/pypers/erf.py
new file mode 100755
index 0000000..d1bcc9e
--- /dev/null
+++ b/pypers/erf.py
@@ -0,0 +1,21 @@
+import math
+import psyco
+psyco.full()
+def erfc(x):
+ exp = math.exp
+ p = 0.3275911
+ a1 = 0.254829592
+ a2 = -0.284496736
+ a3 = 1.421413741
+ a4 = -1.453152027
+ a5 = 1.061405429
+ t = 1.0 / (1.0 + p*x)
+ erfcx = ( (a1 + (a2 + (a3 +
+ (a4 + a5*t)*t)*t)*t)*t ) * exp(-x*x)
+ return erfcx
+def main():
+ erg = 0.0
+ for i in xrange(1000000):
+ erg += erfc(0.456)
+ print "%f" % erg
+main()
diff --git a/pypers/europython05/Quixote-2.0/__init__.py b/pypers/europython05/Quixote-2.0/__init__.py
new file mode 100755
index 0000000..44bbf4a
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/__init__.py
@@ -0,0 +1,26 @@
+"""quixote
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/__init__.py $
+$Id: __init__.py 26539 2005-04-11 15:47:33Z dbinger $
+
+A highly Pythonic web application framework.
+"""
+
+__version__ = '2.0'
+
+# These are frequently needed by Quixote applications.
+from quixote.publish import \
+ get_publisher, get_request, get_response, get_path, redirect, \
+ get_session, get_session_manager, get_user, get_field, get_cookie
+
+
+def enable_ptl():
+ """
+ Installs the import hooks needed to import PTL modules. This must
+ be done explicitly because not all Quixote applications need to use
+ PTL, and import hooks are deep magic that can cause all sorts of
+ mischief and deeply confuse innocent bystanders. Thus, we avoid
+ invoking them behind the programmer's back. One known problem is
+ that, if you use ZODB, you must import ZODB before calling this
+ function.
+ """
+ import quixote.ptl.install
diff --git a/pypers/europython05/Quixote-2.0/config.py b/pypers/europython05/Quixote-2.0/config.py
new file mode 100755
index 0000000..0a8a651
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/config.py
@@ -0,0 +1,175 @@
+"""
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/config.py $
+$Id: config.py 26378 2005-03-17 14:04:45Z dbinger $
+
+Quixote configuration information. This module provides both the
+default configuration values, and some code that Quixote uses for
+dealing with configuration info. You should not edit the configuration
+values in this file, since your edits will be lost if you upgrade to a
+newer Quixote version in the future. However, this is the canonical
+source of information about Quixote configuration variables, and editing
+the defaults here is harmless if you're just playing around and don't
+care what happens in the future.
+"""
+
+
+# Note that the default values here are geared towards a production
+# environment, preferring security and performance over verbosity and
+# debug-ability. If you just want to get a Quixote application
+# up-and-running in a production environment, these settings are mostly
+# right; all you really need to customize are ERROR_EMAIL, and ERROR_LOG.
+# If you need to test/debug/develop a Quixote application, though, you'll
+# probably want to also change DISPLAY_EXCEPTIONS.
+# Again, you shouldn't edit this file unless you don't care what happens
+# in the future (in particular, an upgrade to Quixote would clobber your
+# edits).
+
+
+# E-mail address to send application errors to; None to send no mail at
+# all. This should probably be the email address of your web
+# administrator.
+ERROR_EMAIL = None
+#ERROR_EMAIL = 'webmaster@example.com'
+
+# Filename for writing the Quixote access log; None for no access log.
+ACCESS_LOG = None
+#ACCESS_LOG = "/www/log/quixote-access.log"
+
+# Filename for logging error messages and debugging output; if None,
+# everything will be sent to standard error (normally ending up in the
+# Web server's error log file.
+ERROR_LOG = None
+
+# Controls what's done when uncaught exceptions occur. If set to
+# 'plain', the traceback will be returned to the browser in addition
+# to being logged, If set to 'html' and the cgitb module is installed,
+# a more elaborate display will be returned to the browser, showing
+# the local variables and a few lines of context for each level of the
+# traceback. If set to None, a generic error display, containing no
+# information about the traceback, will be used.
+DISPLAY_EXCEPTIONS = None
+
+# Compress large pages using gzip if the client accepts that encoding.
+COMPRESS_PAGES = False
+
+# If true, then a cryptographically secure token will be inserted into forms
+# as a hidden field. The token will be checked when the form is submitted.
+# This prevents cross-site request forgeries (CSRF). It is off by default
+# since it doesn't work if sessions are not persistent across requests.
+FORM_TOKENS = False
+
+# Session-related variables
+# =========================
+
+# Name of the cookie that will hold the session ID string.
+SESSION_COOKIE_NAME = "QX_session"
+
+# Domain and path to which the session cookie is restricted. Leaving
+# these undefined is fine. Quixote does not have a default "domain"
+# option, meaning the session cookie will only be sent to the
+# originating server. If you don't set the cookie path, Quixote will
+# use your application's root URL (ie. SCRIPT_NAME in a CGI-like
+# environment), meaning the session cookie will be sent to all URLs
+# controlled by your application, but no other.
+SESSION_COOKIE_DOMAIN = None # eg. ".example.com"
+SESSION_COOKIE_PATH = None # eg. "/"
+
+
+# Mail-related variables
+# ======================
+# These are only used by the quixote.sendmail module, which is
+# provided for use by Quixote applications that need to send
+# e-mail. This is a common task for web apps, but by no means
+# universal.
+#
+# E-mail addresses can be specified either as a lone string
+# containing a bare e-mail address ("addr-spec" in the RFC 822
+# grammar), or as an (address, real_name) tuple.
+
+# MAIL_FROM is used as the default for the "From" header and the SMTP
+# sender for all outgoing e-mail. If you don't set it, your application
+# will crash the first time it tries to send e-mail without an explicit
+# "From" address.
+MAIL_FROM = None # eg. "webmaster@example.com"
+ # or ("webmaster@example.com", "Example Webmaster")
+
+# E-mail is sent by connecting to an SMTP server on MAIL_SERVER. This
+# server must be configured to relay outgoing e-mail from the current
+# host (ie., the host where your Quixote application runs, most likely
+# your web server) to anywhere on the Internet. If you don't know what
+# this means, talk to your system administrator.
+MAIL_SERVER = "localhost"
+
+# If MAIL_DEBUG_ADDR is set, then all e-mail will actually be sent to
+# this address rather than the intended recipients. This should be a
+# single, bare e-mail address.
+MAIL_DEBUG_ADDR = None # eg. "developers@example.com"
+
+
+# -- End config variables ----------------------------------------------
+# (no user serviceable parts after this point)
+
+class Config:
+ """Holds all Quixote configuration variables -- see above for
+ documentation of them. The naming convention is simple:
+ downcase the above variables to get the names of instance
+ attributes of this class.
+ """
+
+ config_vars = [
+ 'error_email',
+ 'access_log',
+ 'display_exceptions',
+ 'error_log',
+ 'compress_pages',
+ 'form_tokens',
+ 'session_cookie_domain',
+ 'session_cookie_name',
+ 'session_cookie_path',
+ 'check_session_addr',
+ 'mail_from',
+ 'mail_server',
+ 'mail_debug_addr',
+ ]
+
+ def __init__(self, **kwargs):
+ self.set_from_dict(globals()) # set defaults
+ for name, value in kwargs.items():
+ if name not in self.config_vars:
+ raise ValueError('unknown config variable %r' % name)
+ setattr(self, name, value)
+
+ def dump(self, file=None):
+ import sys
+ if file is None:
+ file = sys.stdout
+ file.write("<%s.%s instance at %x>:\n" %
+ (self.__class__.__module__,
+ self.__class__.__name__,
+ id(self)))
+ for var in self.config_vars:
+ file.write(" %s = %s\n" % (var, `getattr(self, var)`))
+
+ def set_from_dict(self, config_vars):
+ for name, value in config_vars.items():
+ if name.isupper():
+ name = name.lower()
+ if name not in self.config_vars:
+ raise ValueError('unknown config variable %r' % name)
+ setattr(self, name, value)
+
+ def read_file(self, filename):
+ """Read configuration from a file. Any variables already
+ defined in this Config instance, but not in the file, are
+ unchanged, so you can use this to build up a configuration
+ by accumulating data from several config files.
+ """
+ # The config file is Python code -- makes life easy.
+ config_vars = {}
+ try:
+ execfile(filename, config_vars)
+ except IOError, exc:
+ if exc.filename is None: # arg! execfile() loses filename
+ exc.filename = filename
+ raise exc
+ self.set_from_dict(config_vars)
diff --git a/pypers/europython05/Quixote-2.0/demo/__init__.py b/pypers/europython05/Quixote-2.0/demo/__init__.py
new file mode 100755
index 0000000..4efe0a7
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/demo/__init__.py
@@ -0,0 +1,10 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/demo/__init__.py $
+$Id: __init__.py 25575 2004-11-11 16:56:44Z nascheme $
+"""
+from quixote import enable_ptl
+from quixote.publish import Publisher
+enable_ptl()
+
+def create_publisher():
+ from quixote.demo.root import RootDirectory
+ return Publisher(RootDirectory(), display_exceptions='plain')
diff --git a/pypers/europython05/Quixote-2.0/demo/altdemo.py b/pypers/europython05/Quixote-2.0/demo/altdemo.py
new file mode 100755
index 0000000..fc32ca5
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/demo/altdemo.py
@@ -0,0 +1,205 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/demo/altdemo.py $
+$Id: altdemo.py 26377 2005-03-16 23:32:15Z dbinger $
+
+An alternative Quixote demo. This version is contained in a single module
+and does not use PTL. The easiest way to run this demo is to use the
+simple HTTP server included with Quixote. For example:
+
+ $ server/simple_server.py --factory quixote.demo.altdemo.create_publisher
+
+The server listens on localhost:8080 by default. Debug and error output
+will be sent to the terminal.
+
+If you have installed durus, you can run the same demo, except with
+persistent sessions stored in a durus database, by running:
+
+ $ server/simple_server.py --factory quixote.demo.altdemo.create_durus_publisher
+
+"""
+
+from quixote import get_user, get_session, get_session_manager, get_field
+from quixote.directory import Directory
+from quixote.html import href, htmltext
+from quixote.publish import Publisher
+from quixote.session import Session, SessionManager
+from quixote.util import dump_request
+
+def format_page(title, content):
+ request = htmltext(
+ '<div style="font-size: smaller;background:#eee">'
+ '<h1>Request:</h1>%s</div>') % dump_request()
+ return htmltext(
+ '<html><head><title>%(title)s</title>'
+ '<style type="text/css">\n'
+ 'body { border: thick solid green; padding: 2em; }\n'
+ 'h1 { font-size: larger; }\n'
+ 'th { background: #aaa; text-align:left; font-size: smaller; }\n'
+ 'td { background: #ccc; font-size: smaller; }\n'
+ '</style>'
+ '</head><body>%(content)s%(request)s</body></html>') % locals()
+
+def format_request():
+ return format_page('Request', dump_request())
+
+def format_link_list(targets):
+ return htmltext('<ul>%s</ul>') % htmltext('').join([
+ htmltext('<li>%s</li>') % href(target, target) for target in targets])
+
+class RootDirectory(Directory):
+
+ _q_exports = ['', 'login', 'logout']
+
+ def _q_index(self):
+ content = htmltext('')
+ if not get_user():
+ content += htmltext('<p>%s</p>' % href('login', 'login'))
+ else:
+ content += htmltext(
+ '<p>Hello, %s.</p>') % get_user()
+ content += htmltext('<p>%s</p>' % href('logout', 'logout'))
+ sessions = get_session_manager().items()
+ if sessions:
+ sessions.sort()
+ content += htmltext('<table><tr>'
+ '<th></th>'
+ '<th>Session</th>'
+ '<th>User</th>'
+ '<th>Number of Requests</th>'
+ '</tr>')
+ this_session = get_session()
+ for index, (id, session) in enumerate(sessions):
+ if session is this_session:
+ formatted_id = htmltext(
+ '<span style="font-weight:bold">%s</span>' % id)
+ else:
+ formatted_id = id
+ content += htmltext(
+ '<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td>' % (
+ index,
+ formatted_id,
+ session.user or htmltext("<em>None</em>"),
+ session.num_requests))
+ content += htmltext('</table>')
+ return format_page("Quixote Session Management Demo", content)
+
+ def login(self):
+ content = htmltext('')
+ if get_field("name"):
+ session = get_session()
+ session.set_user(get_field("name")) # This is the important part.
+ content += htmltext(
+ '<p>Welcome, %s! Thank you for logging in.</p>') % get_user()
+ content += href("..", "go back")
+ else:
+ content += htmltext(
+ '<p>Please enter your name here:</p>\n'
+ '<form method="POST" action="login">'
+ '<input name="name" />'
+ '<input type="submit" />'
+ '</form>')
+ return format_page("Quixote Session Demo: Login", content)
+
+ def logout(self):
+ if get_user():
+ content = htmltext('<p>Goodbye, %s.</p>') % get_user()
+ else:
+ content = htmltext('<p>That would be redundant.</p>')
+ content += href("..", "start over")
+ get_session_manager().expire_session() # This is the important part.
+ return format_page("Quixote Session Demo: Logout", content)
+
+
+class DemoSession(Session):
+
+ def __init__(self, id):
+ Session.__init__(self, id)
+ self.num_requests = 0
+
+ def start_request(self):
+ """
+ This is called from the main object publishing loop whenever
+ we start processing a new request. Obviously, this is a good
+ place to track the number of requests made. (If we were
+ interested in the number of *successful* requests made, then
+ we could override finish_request(), which is called by
+ the publisher at the end of each successful request.)
+ """
+ Session.start_request(self)
+ self.num_requests += 1
+
+ def has_info(self):
+ """
+ Overriding has_info() is essential but non-obvious. The
+ session manager uses has_info() to know if it should hang on
+ to a session object or not: if a session is "dirty", then it
+ must be saved. This prevents saving sessions that don't need
+ to be saved, which is especially important as a defensive
+ measure against clients that don't handle cookies: without it,
+ we might create and store a new session object for every
+ request made by such clients. With has_info(), we create the
+ new session object every time, but throw it away unsaved as
+ soon as the request is complete.
+
+ (Of course, if you write your session class such that
+ has_info() always returns true after a request has been
+ processed, you're back to the original problem -- and in fact,
+ this class *has* been written that way, because num_requests
+ is incremented on every request, which makes has_info() return
+ true, which makes SessionManager always store the session
+ object. In a real application, think carefully before putting
+ data in a session object that causes has_info() to return
+ true.)
+ """
+ return (self.num_requests > 0) or Session.has_info(self)
+
+ is_dirty = has_info
+
+
+def create_publisher():
+ return Publisher(RootDirectory(),
+ session_manager=SessionManager(session_class=DemoSession),
+ display_exceptions='plain')
+
+try:
+ # If durus is installed, define a create_durus_publisher() that
+ # uses a durus database to store persistent sessions.
+ import os, tempfile
+ from durus.persistent import Persistent
+ from durus.persistent_dict import PersistentDict
+ from durus.file_storage import FileStorage
+ from durus.connection import Connection
+ connection = None # set in create_durus_publisher()
+
+ class PersistentSession(DemoSession, Persistent):
+ pass
+
+ class PersistentSessionManager(SessionManager, Persistent):
+ def __init__(self):
+ sessions = PersistentDict()
+ SessionManager.__init__(self,
+ session_class=PersistentSession,
+ session_mapping=sessions)
+ def forget_changes(self, session):
+ print 'abort changes', get_session()
+ connection.abort()
+
+ def commit_changes(self, session):
+ print 'commit changes', get_session()
+ connection.commit()
+
+ def create_durus_publisher():
+ global connection
+ filename = os.path.join(tempfile.gettempdir(), 'quixote-demo.durus')
+ print 'Opening %r as a Durus database.' % filename
+ connection = Connection(FileStorage(filename))
+ root = connection.get_root()
+ session_manager = root.get('session_manager', None)
+ if session_manager is None:
+ session_manager = PersistentSessionManager()
+ connection.get_root()['session_manager'] = session_manager
+ connection.commit()
+ return Publisher(RootDirectory(),
+ session_manager=session_manager,
+ display_exceptions='plain')
+except ImportError:
+ pass # durus not installed.
diff --git a/pypers/europython05/Quixote-2.0/demo/mini_demo.py b/pypers/europython05/Quixote-2.0/demo/mini_demo.py
new file mode 100755
index 0000000..f422211
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/demo/mini_demo.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+"""
+A minimal Quixote demo. If you have the 'quixote' package in your Python
+path, you can run it like this:
+
+ $ python demo/mini_demo.py
+
+The server listens on localhost:8080 by default. Debug and error output
+will be sent to the terminal.
+"""
+
+from quixote.publish import Publisher
+from quixote.directory import Directory
+
+class RootDirectory(Directory):
+
+ _q_exports = ['', 'hello']
+
+ def _q_index(self):
+ return '''<html>
+ <body>Welcome to the Quixote demo. Here is a
+ <a href="hello">link</a>.
+ </body>
+ </html>
+ '''
+
+ def hello(self):
+ return '<html><body>Hello world!</body></html>'
+
+
+def create_publisher():
+ return Publisher(RootDirectory(),
+ display_exceptions='plain')
+
+
+if __name__ == '__main__':
+ from quixote.server.simple_server import run
+ print 'creating demo listening on http://localhost:8080/'
+ run(create_publisher, host='localhost', port=8080)
diff --git a/pypers/europython05/Quixote-2.0/directory.py b/pypers/europython05/Quixote-2.0/directory.py
new file mode 100755
index 0000000..e3a8816
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/directory.py
@@ -0,0 +1,109 @@
+"""$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/directory.py $
+$Id: directory.py 26327 2005-03-10 11:30:45Z dbinger $
+
+Logic for traversing directory objects and generating output.
+"""
+import quixote
+from quixote.errors import TraversalError
+
+class Directory(object):
+ """
+ Instance attributes: none
+ """
+
+ # A list containing strings or 2-tuples of strings that map external
+ # names to internal names. Note that the empty string will be
+ # implicitly mapped to '_q_index'.
+ _q_exports = []
+
+ def _q_translate(self, component):
+ """(component : string) -> string | None
+
+ Translate a path component into a Python identifier. Returning
+ None signifies that the component does not exist.
+ """
+ if component in self._q_exports:
+ if component == '':
+ return '_q_index' # implicit mapping
+ else:
+ return component
+ else:
+ # check for an explicit external to internal mapping
+ for value in self._q_exports:
+ if isinstance(value, tuple):
+ if value[0] == component:
+ return value[1]
+ else:
+ return None
+
+ def _q_lookup(self, component):
+ """(component : string) -> object
+
+ Lookup a path component and return the corresponding object (usually
+ a Directory, a method or a string). Returning None signals that the
+ component does not exist.
+ """
+ return None
+
+ def _q_traverse(self, path):
+ """(path: [string]) -> object
+
+ Traverse a path and return the result.
+ """
+ assert len(path) > 0
+ component = path[0]
+ path = path[1:]
+ name = self._q_translate(component)
+ if name is not None:
+ obj = getattr(self, name)
+ else:
+ obj = self._q_lookup(component)
+ if obj is None:
+ raise TraversalError(private_msg=('directory %r has no component '
+ '%r' % (self, component)))
+ if path:
+ return obj._q_traverse(path)
+ elif callable(obj):
+ return obj()
+ else:
+ return obj
+
+ def __call__(self):
+ if "" in self._q_exports and not quixote.get_request().form:
+ # Fix missing trailing slash.
+ path = quixote.get_path()
+ print "Adding slash to: %r " % path
+ return quixote.redirect(path + "/", permanent=True)
+ else:
+ raise TraversalError(private_msg=('directory %r is not '
+ 'callable' % self))
+
+class AccessControlled(object):
+ """
+ A mix-in class that calls the _q_access() method before traversing
+ into the directory.
+ """
+ def _q_access(self):
+ pass
+
+ def _q_traverse(self, path):
+ self._q_access()
+ return super(AccessControlled, self)._q_traverse(path)
+
+
+class Resolving(object):
+ """
+ A mix-in class that provides the _q_resolve() method. _q_resolve()
+ is called if a component name appears in the _q_exports list but is
+ not an instance attribute. _q_resolve is expected to return the
+ component object.
+ """
+ def _q_resolve(self, name):
+ return None
+
+ def _q_translate(self, component):
+ name = super(Resolving, self)._q_translate(component)
+ if name is not None and not hasattr(self, name):
+ obj = self._q_resolve(name)
+ setattr(self, name, obj)
+ return name
diff --git a/pypers/europython05/Quixote-2.0/doc/INSTALL.txt b/pypers/europython05/Quixote-2.0/doc/INSTALL.txt
new file mode 100755
index 0000000..ccc18c8
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/INSTALL.txt
@@ -0,0 +1,32 @@
+Installing Quixote
+==================
+
+Quixote requires Python 2.3 or later.
+
+If you have a previously installed quixote, we strongly recommend that
+you remove it before installing a new one.
+First, find out where your old Quixote installation is:
+
+ python -c "import os, quixote; print os.path.dirname(quixote.__file__)"
+
+and then remove away the reported directory. (If the import fails, then
+you don't have an existing Quixote installation.)
+
+Now install the new version by running (in the distribution directory),
+
+ python setup.py install
+
+and you're done.
+
+Quick start
+===========
+
+In a terminal window, run server/simple_server.py.
+In a browser, open http://localhost:8080
+
+
+Upgrading a Quixote 1 application to Quixote 2.
+===============================================
+
+See upgrading.txt for details.
+
diff --git a/pypers/europython05/Quixote-2.0/doc/Makefile b/pypers/europython05/Quixote-2.0/doc/Makefile
new file mode 100755
index 0000000..b09d2f3
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile to convert Quixote docs to HTML
+#
+# $Id: Makefile 20217 2003-01-16 20:51:53Z akuchlin $
+#
+
+TXT_FILES = $(wildcard *.txt)
+HTML_FILES = $(filter-out ZPL%,$(TXT_FILES:%.txt=%.html))
+
+RST2HTML = /www/python/bin/rst2html
+RST2HTML_OPTS = -o us-ascii
+
+DEST_HOST = staging.mems-exchange.org
+DEST_DIR = /www/www-docroot/software/quixote/doc
+
+SS = default.css
+
+%.html: %.txt
+ $(RST2HTML) $(RST2HTML_OPTS) $< $@
+
+all: $(HTML_FILES)
+
+clean:
+ rm -f $(HTML_FILES)
+
+install:
+ rsync -vptgo *.html $(SS) $(DEST_HOST):$(DEST_DIR)
+
+local-install:
+ dir=`pwd` ; \
+ cd $(DEST_DIR) && ln -sf $$dir/*.html $$dir/$(SS) .
diff --git a/pypers/europython05/Quixote-2.0/doc/PTL.txt b/pypers/europython05/Quixote-2.0/doc/PTL.txt
new file mode 100755
index 0000000..c0e4b0f
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/PTL.txt
@@ -0,0 +1,264 @@
+PTL: Python Template Language
+=============================
+
+Introduction
+------------
+
+PTL is the templating language used by Quixote. Most web templating
+languages embed a real programming language in HTML, but PTL inverts
+this model by merely tweaking Python to make it easier to generate
+HTML pages (or other forms of text). In other words, PTL is basically
+Python with a novel way to specify function return values.
+
+Specifically, a PTL template is designated by inserting a ``[plain]``
+or ``[html]`` modifier after the function name. The value of
+expressions inside templates are kept, not discarded. If the type is
+``[html]`` then non-literal strings are passed through a function that
+escapes HTML special characters.
+
+
+Plain text templates
+--------------------
+
+Here's a sample plain text template::
+
+ def foo [plain] (x, y = 5):
+ "This is a chunk of static text."
+ greeting = "hello world" # statement, no PTL output
+ print 'Input values:', x, y
+ z = x + y
+ """You can plug in variables like x (%s)
+ in a variety of ways.""" % x
+
+ "\n\n"
+ "Whitespace is important in generated text.\n"
+ "z = "; z
+ ", but y is "
+ y
+ "."
+
+Obviously, templates can't have docstrings, but otherwise they follow
+Python's syntactic rules: indentation indicates scoping, single-quoted
+and triple-quoted strings can be used, the same rules for continuing
+lines apply, and so forth. PTL also follows all the expected semantics
+of normal Python code: so templates can have parameters, and the
+parameters can have default values, be treated as keyword arguments,
+etc.
+
+The difference between a template and a regular Python function is that
+inside a template the result of expressions are saved as the return
+value of that template. Look at the first part of the example again::
+
+ def foo [plain] (x, y = 5):
+ "This is a chunk of static text."
+ greeting = "hello world" # statement, no PTL output
+ print 'Input values:', x, y
+ z = x + y
+ """You can plug in variables like x (%s)
+ in a variety of ways.""" % x
+
+Calling this template with ``foo(1, 2)`` results in the following
+string::
+
+ This is a chunk of static text.You can plug in variables like x (1)
+ in a variety of ways.
+
+Normally when Python evaluates expressions inside functions, it just
+discards their values, but in a ``[plain]`` PTL template the value is
+converted to a string using ``str()`` and appended to the template's
+return value. There's a single exception to this rule: ``None`` is the
+only value that's ever ignored, adding nothing to the output. (If this
+weren't the case, calling methods or functions that return ``None``
+would require assigning their value to a variable. You'd have to write
+``dummy = list.sort()`` in PTL code, which would be strange and
+confusing.)
+
+The initial string in a template isn't treated as a docstring, but is
+just incorporated in the generated output; therefore, templates can't
+have docstrings. No whitespace is ever automatically added to the
+output, resulting in ``...text.You can ...`` from the example. You'd
+have to add an extra space to one of the string literals to correct
+this.
+
+The assignment to the ``greeting`` local variable is a statement, not an
+expression, so it doesn't return a value and produces no output. The
+output from the ``print`` statement will be printed as usual, but won't
+go into the string generated by the template. Quixote directs standard
+output into Quixote's debugging log; if you're using PTL on its own, you
+should consider doing something similar. ``print`` should never be used
+to generate output returned to the browser, only for adding debugging
+traces to a template.
+
+Inside templates, you can use all of Python's control-flow statements::
+
+ def numbers [plain] (n):
+ for i in range(n):
+ i
+ " " # PTL does not add any whitespace
+
+Calling ``numbers(5)`` will return the string ``"1 2 3 4 5 "``. You can
+also have conditional logic or exception blocks::
+
+ def international_hello [plain] (language):
+ if language == "english":
+ "hello"
+ elif language == "french":
+ "bonjour"
+ else:
+ raise ValueError, "I don't speak %s" % language
+
+
+HTML templates
+--------------
+
+Since PTL is usually used to generate HTML documents, an ``[html]``
+template type has been provided to make generating HTML easier.
+
+A common error when generating HTML is to grab data from the browser
+or from a database and incorporate the contents without escaping
+special characters such as '<' and '&'. This leads to a class of
+security bugs called "cross-site scripting" bugs, where a hostile user
+can insert arbitrary HTML in your site's output that can link to other
+sites or contain JavaScript code that does something nasty (say,
+popping up 10,000 browser windows).
+
+Such bugs occur because it's easy to forget to HTML-escape a string,
+and forgetting it in just one location is enough to open a hole. PTL
+offers a solution to this problem by being able to escape strings
+automatically when generating HTML output, at the cost of slightly
+diminished performance (a few percent).
+
+Here's how this feature works. PTL defines a class called
+``htmltext`` that represents a string that's already been HTML-escaped
+and can be safely sent to the client. The function ``htmlescape(string)``
+is used to escape data, and it always returns an ``htmltext``
+instance. It does nothing if the argument is already ``htmltext``.
+
+If a template function is declared ``[html]`` instead of ``[text]``
+then two things happen. First, all literal strings in the function
+become instances of ``htmltext`` instead of Python's ``str``. Second,
+the values of expressions are passed through ``htmlescape()`` instead
+of ``str()``.
+
+``htmltext`` type is like the ``str`` type except that operations
+combining strings and ``htmltext`` instances will result in the string
+being passed through ``htmlescape()``. For example::
+
+ >>> from quixote.html import htmltext
+ >>> htmltext('a') + 'b'
+ <htmltext 'ab'>
+ >>> 'a' + htmltext('b')
+ <htmltext 'ab'>
+ >>> htmltext('a%s') % 'b'
+ <htmltext 'ab'>
+ >>> response = 'green eggs & ham'
+ >>> htmltext('The response was: %s') % response
+ <htmltext 'The response was: green eggs &amp; ham'>
+
+Note that calling ``str()`` strips the ``htmltext`` type and should be
+avoided since it usually results in characters being escaped more than
+once. While ``htmltext`` behaves much like a regular string, it is
+sometimes necessary to insert a ``str()`` inside a template in order
+to obtain a genuine string. For example, the ``re`` module requires
+genuine strings. We have found that explicit calls to ``str()`` can
+often be avoided by splitting some code out of the template into a
+helper function written in regular Python.
+
+It is also recommended that the ``htmltext`` constructor be used as
+sparingly as possible. The reason is that when using the htmltext
+feature of PTL, explicit calls to ``htmltext`` become the most likely
+source of cross-site scripting holes. Calling ``htmltext`` is like
+saying "I am absolutely sure this piece of data cannot contain malicious
+HTML code injected by a user. Don't escape HTML special characters
+because I want them."
+
+Note that literal strings in template functions declared with
+``[html]`` are htmltext instances, and therefore won't be escaped.
+You'll only need to use ``htmltext`` when HTML markup comes from
+outside the template. For example, if you want to include a file
+containing HTML::
+
+ def output_file [html] ():
+ '<html><body>' # does not get escaped
+ htmltext(open("myfile.html").read())
+ '</body></html>'
+
+In the common case, templates won't be dealing with HTML markup from
+external sources, so you can write straightforward code. Consider
+this function to generate the contents of the ``HEAD`` element::
+
+ def meta_tags [html] (title, description):
+ '<title>%s</title>' % title
+ '<meta name="description" content="%s">\n' % description
+
+There are no calls to ``htmlescape()`` at all, but string literals
+such as ``<title>%s</title>`` have all be turned into ``htmltext``
+instances, so the string variables will be automatically escaped::
+
+ >>> t.meta_tags('Catalog', 'A catalog of our cool products')
+ <htmltext '<title>Catalog</title>
+ <meta name="description" content="A catalog of our cool products">\n'>
+ >>> t.meta_tags('Dissertation on <HEAD>',
+ ... 'Discusses the "LINK" and "META" tags')
+ <htmltext '<title>Dissertation on &lt;HEAD&gt;</title>
+ <meta name="description"
+ content="Discusses the &quot;LINK&quot; and &quot;META&quot; tags">\n'>
+ >>>
+
+Note how the title and description have had HTML-escaping applied to them.
+(The output has been manually pretty-printed to be more readable.)
+
+Once you start using ``htmltext`` in one of your templates, mixing
+plain and HTML templates is tricky because of ``htmltext``'s automatic
+escaping; plain templates that generate HTML tags will be
+double-escaped. One approach is to just use HTML templates throughout
+your application. Alternatively you can use ``str()`` to convert
+``htmltext`` instances to regular Python strings; just be sure the
+resulting string isn't HTML-escaped again.
+
+Two implementations of ``htmltext`` are provided, one written in pure
+Python and a second one implemented as a C extension. Both versions
+have seen production use.
+
+
+PTL modules
+-----------
+
+PTL templates are kept in files with the extension .ptl. Like Python
+files, they are byte-compiled on import, and the byte-code is written to
+a compiled file with the extension ``.pyc``. Since vanilla Python
+doesn't know anything about PTL, Quixote provides an import hook to let
+you import PTL files just like regular Python modules. The standard way
+to install this import hook is by calling the ``enable_ptl()`` function::
+
+ from quixote import enable_ptl
+ enable_ptl()
+
+(Note: if you're using ZODB, always import ZODB *before* installing the
+PTL import hook. There's some interaction which causes importing the
+TimeStamp module to fail when the PTL import hook is installed; we
+haven't debugged the problem. A similar problem has been reported for
+BioPython and win32com.client imports.)
+
+Once the import hook is installed, PTL files can be imported as if they
+were Python modules. If all the example templates shown here were put
+into a file named ``foo.ptl``, you could then write Python code that did
+this::
+
+ from foo import numbers
+ def f():
+ return numbers(10)
+
+You may want to keep this little function in your ``PYTHONSTARTUP``
+file::
+
+ def ptl():
+ try:
+ import ZODB
+ except ImportError:
+ pass
+ from quixote import enable_ptl
+ enable_ptl()
+
+This is useful if you want to interactively play with a PTL module.
+
diff --git a/pypers/europython05/Quixote-2.0/doc/demo.txt b/pypers/europython05/Quixote-2.0/doc/demo.txt
new file mode 100755
index 0000000..4272223
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/demo.txt
@@ -0,0 +1,221 @@
+Running the Quixote Demos
+=========================
+
+Quixote comes with some demonstration applications in the demo directory.
+After quixote is installed (see INSTALL.txt for instructions),
+you can run the demos using the scripts located in the server directory.
+
+Each server script is written for a specific method of connecting a
+quixote publisher to a web server, and you will ultimately want to
+choose the one that matches your needs. More information about the
+different server scripts may be found in the scripts themselves and in
+web-server.txt. To start, though, the easiest way to view the demos
+is as follows: in a terminal window, run server/simple_server.py, and
+in a browser, open http://localhost:8080.
+
+The simple_server.py script prints a usage message if you run it with
+a '--help' command line argument. You can run different demos by
+using the '--factory' option to identify a callable that creates the
+publisher you want to use. In particular, you might try these demos:
+
+ simple_server.py --factory quixote.demo.mini_demo.create_publisher
+
+or
+
+ simple_server.py --factory quixote.demo.altdemo.create_publisher
+
+
+
+Understanding the mini_demo
+---------------------------
+
+Start the mini demo by running the command:
+ simple_server.py --factory quixote.demo.mini_demo.create_publisher
+
+In a browser, load http://localhost:8080. In your browser, you should
+see "Welcome ..." page. In your terminal window, you will see a
+"localhost - - ..." line for each request. These are access log
+messages from the web server.
+
+Look at the source code in demo/mini_demo.py. Near the bottom you
+will find the create_publisher() function. The create_publisher()
+function creates a Publisher instance whose root directory is an
+instance of the RootDirectory class defined just above. When a
+request arrives, the Publisher calls the _q_traverse() method on the
+root directory. In this case, the RootDirectory is using the standard
+_q_traverse() implementation, inherited from Directory.
+
+Look, preferably in another window, at the source code for
+_q_traverse() in directory.py. The path argument provided to
+_q_traverse() is a list of string components of the path part of the
+URL, obtained by splitting the request location at each '/' and
+dropping the first element (which is always '') For example, if the
+path part of the URL is '/', the path argument to _q_traverse() is
+['']. If the path part of the URL is '/a', the path argument to
+_q_traverse() is ['a']. If the path part of the URL is '/a/', the
+path argument to _q_traverse() is ['a', ''].
+
+Looking at the code of _q_traverse(), observe that it starts by
+splitting off the first component of the path and calling
+_q_translate() to see if there is a designated attribute name
+corresponding to this component. For the '/' page, the component is
+'', and _q_translate() returns the attribute name '_q_index'. The
+_q_traverse() function goes on to lookup the _q_index method and
+return the result of calling it.
+
+Looking back at mini_demo.py, you can see that the RootDirectory class
+includes a _q_index() method, and this method does return the HTML for
+http://localhost:8080/
+
+As mentioned above, the _q_translate() identifies a "designated"
+attribute name for a given component. The default implementation uses
+self._q_exports to define this designation. In particular, if the
+component is in self._q_exports, then it is returned as the attribute
+name, except in the special case of '', which is translated to the
+special attribute name '_q_index'.
+
+When you click on the link on the top page, you get
+http://localhost:8080/hello. In this case, the path argument to the
+_q_traverse() call is ['hello'], and the return value is the result of
+calling the hello() method.
+
+Feeling bold? (Just kidding, this won't hurt at all.) Try opening
+http://localhost:8080/bogus. This is what happens when _q_traverse()
+raises a TraversalError. A TraversalError is no big deal, but how
+does quixote handle more exceptional exceptions? To see, you can
+introduce one by editing mini_demo.py. Try inserting the line "raise
+'ouch'" into the hello() method. Kill the demo server (Control-c) and
+start a new one with the same command as before. Now load the
+http://localhost:8080/hello page. You should see a plain text python
+traceback followed by some information extracted from the HTTP
+request. This information is always printed to the error log on an
+exception. Here, it is also displayed in the browser because the
+create_publisher() function made a publisher using the 'plain' value
+for the display_exceptions keyword argument. If you omit that keyword
+argument from the Publisher constructor, the browser will get an
+"Internal Server Error" message instead of the full traceback. If you
+provide the value 'html', the browser displays a prettier version of
+the traceback.
+
+One more thing to try here. Replace your 'raise "ouch"' line in the hello() method with 'print "ouch"'. If you restart the server and load the /hello page,
+you will see that print statements go the the error log (in this case, your
+terminal window). This can be useful.
+
+
+Understanding the root demo
+---------------------------
+
+Start the root demo by running the command:
+ simple_server.py --factory quixote.demo.create_publisher
+
+In a browser, open http://localhost:8080 as before.
+Click around at will.
+
+This is the default demo, but it is more complicated than the
+mini_demo described above. The create_publisher() function in
+quixote.demo.__init__.py creates a publisher whose root directory is
+an instance of quixote.demo.root.RootDirectory. Note that the source
+code is a file named "root.ptl". The suffix of "ptl" indicates that
+it is a PTL file, and the import must follow a call to
+quixote.enable_ptl() or else the source file will not be found or
+compiled. The quixote.demo.__init__.py file takes care of that.
+
+Take a look at the source code in root.ptl. You will see code that
+looks like regular python, except that some function definitions have
+"[html]" between the function name and the parameter list. These
+functions are ptl templates. For details about PTL, see the PTL.txt
+file.
+
+This RootDirectory class is similar to the one in mini_demo.py, in
+that it has a _q_index() method and '' appears in the _q_exports list.
+One new feature here is the presence of a tuple in the _q_exports
+list. Most of the time, the elements of the _q_exports lists are just
+strings that name attributes that should be available as URL
+components. This pattern does not work, however, when the particular
+URL component you want to use includes characters (like '.') that
+can't appear in Python attribute names. To work around these cases,
+the _q_exports list may contain tuples such as ("favicon.ico",
+"favicon_ico") to designate "favicon_ico" as the attribute name
+corresponding the the "favicon.ico" URL component.
+
+Looking at the RootDirectoryMethods, including plain(), css() and
+favon_ico(), you will see examples where, in addition to returning a
+string containing the body of the HTTP response, the function also
+makes side-effect modifications to the response object itself, to set
+the content type and the expiration time for the response.
+Most of the time, these direct modifications to the response are
+not needed. When they are, though, the get_response() function
+gives you direct access to the response instance.
+
+The RootDirectory here also sets an 'extras' attribute to be an
+instance of ExtraDirectory, imported from the quixote.demo.extras
+module. Note that 'extras' also appears in the _q_exports list. This
+is the ordinary way to extend your URL space through another '/'.
+For example, the URL path '/extras/' will result in a call to
+the ExtraDirectory instance's _q_index() method.
+
+The _q_lookup() method
+----------------------
+
+Now take a look at the ExtraDirectory class in extras.ptl. This class
+exhibits some more advanced publishing features. If you look back at
+the default _q_traverse() implementation (in directory.py), you will
+see that the _q_traverse does not give up if _q_translate() returns
+None, indicating that the path component has no designated
+corresponding attribute name. In this case, _q_traverse() tries
+calling self._q_lookup() to see if the object of interest can be found
+in a different way. Note that _q_lookup() takes the component as an
+argument and must return either (if there is more path to traverse) a
+Directory instance, or else (if the component is the last in the path)
+a callable or a string.
+
+In this particular case, the ExtrasDirectory._q_lookup() call returns
+an instance of IntegerUI (a subclass of Directory). The interest
+here, unlike the ExtrasDirectory() instance itself, is created
+on-the-fly during the traversal, especially for this particular
+component. Try loading http://localhost:8080/extras/12/ to see how
+this behaves.
+
+Note that the correct URL to get to the IntegerUI(12)._q_index() call
+ends with a '/'. This can sometimes be confusing to people who expect
+http://localhost:8080/extras/12 to yield the same page as
+http://localhost:8080/extras/12/. If given the path ['extras', '12'],
+the default _q_traverse() ends up *calling* the instance of IntegerUI.
+The Directory.__call__() (see directory.py) determines the result: if
+no form values were submitted and adding a slash would produce a page,
+the call returns the result of calling quixote.redirect(). The
+redirect() call here causes the server to issue a permanent redirect
+response to the path with the slash added. When this automatic
+redirect is used, a message is printed to the error log. If the
+conditions for a redirect are not met, the call falls back to raising
+a TraversalError. [Note, if you don't like this redirect behavior,
+override, replace, or delete Directory.__call__]
+
+The _q_lookup() pattern is useful when you want to allow URL
+components that you either don't know or don't want to list in
+_q_exports ahead of time.
+
+The _q_resolve() method
+-----------------------
+
+Note that the ExtraDirectory class inherits from Resolving (in
+addition to Directory). The Resolving mixin modifies the
+_q_traverse() so that, when a component has an attribute name
+designated by _q_translate(), but the Directory instance does not
+actually *have* that attribute, the _q_resolve() method is called to
+"resolve" the trouble. Typically, the _q_resolve() imports or
+constructs what *should* be the value of the designated attribute.
+The modified _q_translate() sets the attribute value so that the
+_q_resolve() won't be called again for the same attribute. The
+_q_resolve() pattern is useful when you want to delay the work of
+constructing the values for exported attributes.
+
+Forms
+-----
+
+You can't get very far writing web applications without writing forms.
+The root demo includes, at http://localhost:8080/extras/form, a page
+that demonstrates basic usage of the Form class and widgets defined in
+the quixote.form package.
+
+$Id: demo.txt 25695 2004-11-30 20:53:44Z dbinger $
diff --git a/pypers/europython05/Quixote-2.0/doc/form2conversion.txt b/pypers/europython05/Quixote-2.0/doc/form2conversion.txt
new file mode 100755
index 0000000..290db38
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/form2conversion.txt
@@ -0,0 +1,358 @@
+Converting form1 forms to use the form2 library
+===============================================
+
+Note:
+-----
+The packages names have changed in Quixote 2.
+
+Quixote form1 forms are now in the package quixote.form1.
+(In Quixote 1, they were in quixote.form.)
+
+Quixote form2 forms are now in the package quixote.form.
+(In Quixote 1, they were in quixote.form2.)
+
+
+Introduction
+------------
+
+These are some notes and examples for converting Quixote form1 forms,
+that is forms derived from ``quixote.form1.Form``, to the newer form2
+forms.
+
+Form2 forms are more flexible than their form1 counterparts in that they
+do not require you to use the ``Form`` class as a base to get form
+functionality as form1 forms did. Form2 forms can be instantiated
+directly and then manipulated as instances. You may also continue to
+use inheritance for your form2 classes to get form functionality,
+particularly if the structured separation of ``process``, ``render``,
+and ``action`` is desirable.
+
+There are many ways to get from form1 code ported to form2. At one
+end of the spectrum is to rewrite the form class using a functional
+programing style. This method is arguably best since the functional
+style makes the flow of control clearer.
+
+The other end of the spectrum and normally the easiest way to port
+form1 forms to form2 is to use the ``compatibility`` module provided
+in the form2 package. The compatibility module's Form class provides
+much of the same highly structured machinery (via a ``handle`` master
+method) that the form1 framework uses.
+
+Converting form1 forms using using the compatibility module
+-----------------------------------------------------------
+
+Here's the short list of things to do to convert form1 forms to
+form2 using compatibility.
+
+ 1. Import the Form base class from ``quixote.form.compatibility``
+ rather than from quixote.form1.
+
+ 2. Getting and setting errors is slightly different. In your form's
+ process method, where errors are typically set, form2
+ has a new interface for marking a widget as having an error.
+
+ Form1 API::
+
+ self.error['widget_name'] = 'the error message'
+
+ Form2 API::
+
+ self.set_error('widget_name', 'the error message')
+
+ If you want to find out if the form already has errors, change
+ the form1 style of direct references to the ``self.errors``
+ dictionary to a call to the ``has_errors`` method.
+
+ Form1 API::
+
+ if not self.error:
+ do some more error checking...
+
+ Form2 API::
+
+ if not self.has_errors():
+ do some more error checking...
+
+ 3. Form2 select widgets no longer take ``allowed_values`` or
+ ``descriptions`` arguments. If you are adding type of form2 select
+ widget, you must provide the ``options`` argument instead. Options
+ are the way you define the list of things that are selectable and
+ what is returned when they are selected. the options list can be
+ specified in in one of three ways::
+
+ options: [objects:any]
+ or
+ options: [(object:any, description:any)]
+ or
+ options: [(object:any, description:any, key:any)]
+
+ An easy way to construct options if you already have
+ allowed_values and descriptions is to use the built-in function
+ ``zip`` to define options::
+
+ options=zip(allowed_values, descriptions)
+
+ Note, however, that often it is simpler to to construct the
+ ``options`` list directly.
+
+ 4. You almost certainly want to include some kind of cascading style
+ sheet (since form2 forms render with minimal markup). There is a
+ basic set of CSS rules in ``quixote.form.css``.
+
+
+Here's the longer list of things you may need to tweak in order for
+form2 compatibility forms to work with your form1 code.
+
+ * ``widget_type`` widget class attribute is gone. This means when
+ adding widgets other than widgets defined in ``quixote.form.widget``,
+ you must import the widget class into your module and pass the
+ widget class as the first argument to the ``add_widget`` method
+ rather than using the ``widget_type`` string.
+
+ * The ``action_url`` argument to the form's render method is now
+ a keyword argument.
+
+ * If you use ``OptionSelectWidget``, there is no longer a
+ ``get_current_option`` method. You can get the current value
+ in the normal way.
+
+ * ``ListWidget`` has been renamed to ``WidgetList``.
+
+ * There is no longer a ``CollapsibleListWidget`` class. If you need
+ this functionality, consider writing a 'deletable composite widget'
+ to wrap your ``WidgetList`` widgets in it::
+
+ class DeletableWidget(CompositeWidget):
+
+ def __init__(self, name, value=None,
+ element_type=StringWidget,
+ element_kwargs={}, **kwargs):
+ CompositeWidget.__init__(self, name, value=value, **kwargs)
+ self.add(HiddenWidget, 'deleted', value='0')
+ if self.get('deleted') != '1':
+ self.add(element_type, 'element', value=value,
+ **element_kwargs)
+ self.add(SubmitWidget, 'delete', value='Delete')
+ if self.get('delete'):
+ self.get_widget('deleted').set_value('1')
+
+ def _parse(self, request):
+ if self.get('deleted') == '1':
+ self.value = None
+ else:
+ self.value = self.get('element')
+
+ def render(self):
+ if self.get('deleted') == '1':
+ return self.get_widget('deleted').render()
+ else:
+ return CompositeWidget.render(self)
+
+
+Congratulations, now that you've gotten your form1 forms working in form2,
+you may wish to simplify this code using some of the new features available
+in form2 forms. Here's a list of things you may wish to consider:
+
+ * In your process method, you don't really need to get a ``form_data``
+ dictionary by calling ``Form.process`` to ensure your widgets are
+ parsed. Instead, the parsed value of any widget is easy to obtain
+ using the widget's ``get_value`` method or the form's
+ ``__getitem__`` method. So, instead of::
+
+ form_data = Form.process(self, request)
+ val = form_data['my_widget']
+
+ You can use::
+
+ val = self['my_widget']
+
+ If the widget may or may not be in the form, you can use ``get``::
+
+ val = self.get('my_widget')
+
+
+ * It's normally not necessary to provide the ``action_url`` argument
+ to the form's ``render`` method.
+
+ * You don't need to save references to your widgets in your form
+ class. You may have a particular reason for wanting to do that,
+ but any widget added to the form using ``add`` (or ``add_widget`` in
+ the compatibility module) can be retrieved using the form's
+ ``get_widget`` method.
+
+
+Converting form1 forms to form2 by functional rewrite
+-----------------------------------------------------
+
+The best way to get started on a functional version of a form2 rewrite
+is to look at a trivial example form first written using the form1
+inheritance model followed by it's form2 functional equivalent.
+
+First the form1 form::
+
+ class MyForm1Form(Form):
+ def __init__(self, request, obj):
+ Form.__init__(self)
+
+ if obj is None:
+ self.obj = Obj()
+ self.add_submit_button('add', 'Add')
+ else:
+ self.obj = obj
+ self.add_submit_button('update', 'Update')
+
+ self.add_cancel_button('Cancel', request.get_path(1) + '/')
+
+ self.add_widget('single_select', 'obj_type',
+ title='Object Type',
+ value=self.obj.get_type(),
+ allowed_values=list(obj.VALID_TYPES),
+ descriptions=['type1', 'type2', 'type3'])
+ self.add_widget('float', 'cost',
+ title='Cost',
+ value=obj.get_cost())
+
+ def render [html] (self, request, action_url):
+ title = 'Obj %s: Edit Object' % self.obj
+ header(title)
+ Form.render(self, request, action_url)
+ footer(title)
+
+ def process(self, request):
+ form_data = Form.process(self, request)
+
+ if not self.error:
+ if form_data['cost'] is None:
+ self.error['cost'] = 'A cost is required.'
+ elif form_data['cost'] < 0:
+ self.error['cost'] = 'The amount must be positive'
+ return form_data
+
+ def action(self, request, submit, form_data):
+ self.obj.set_type(form_data['obj_type'])
+ self.obj.set_cost(form_data['cost'])
+ if submit == 'add':
+ db = get_database()
+ db.add(self.obj)
+ else:
+ assert submit == 'update'
+ return request.redirect(request.get_path(1) + '/')
+
+Here's the same form using form2 where the function operates on a Form
+instance it keeps a reference to it as a local variable::
+
+ def obj_form(request, obj):
+ form = Form() # quixote.form.Form
+ if obj is None:
+ obj = Obj()
+ form.add_submit('add', 'Add')
+ else:
+ form.add_submit('update', 'Update')
+ form.add_submit('cancel', 'Cancel')
+
+ form.add_single_select('obj_type',
+ title='Object Type',
+ value=obj.get_type(),
+ options=zip(obj.VALID_TYPES,
+ ['type1', 'type2', 'type3']))
+ form.add_float('cost',
+ title='Cost',
+ value=obj.get_cost(),
+ required=1)
+
+ def render [html] ():
+ title = 'Obj %s: Edit Object' % obj
+ header(title)
+ form.render()
+ footer(title)
+
+ def process():
+ if form['cost'] < 0:
+ self.set_error('cost', 'The amount must be positive')
+
+ def action(submit):
+ obj.set_type(form['obj_type'])
+ obj.set_cost(form['cost'])
+ if submit == 'add':
+ db = get_database()
+ db.add(self.obj)
+ else:
+ assert submit == 'update'
+
+ exit_path = request.get_path(1) + '/'
+ submit = form.get_submit()
+ if submit == 'cancel':
+ return request.redirect(exit_path)
+
+ if not form.is_submitted() or form.has_errors():
+ return render()
+ process()
+ if form.has_errors():
+ return render()
+
+ action(submit)
+ return request.redirect(exit_path)
+
+
+As you can see in the example, the function still has all of the same
+parts of it's form1 equivalent.
+
+ 1. It determines if it's to create a new object or edit an existing one
+ 2. It adds submit buttons and widgets
+ 3. It has a function that knows how to render the form
+ 4. It has a function that knows how to do error processing on the form
+ 5. It has a function that knows how to register permanent changes to
+ objects when the form is submitted successfully.
+
+In the form2 example, we have used inner functions to separate out these
+parts. This, of course, is optional, but it does help readability once
+the form gets more complicated and has the additional advantage of
+mapping directly with it's form1 counterparts.
+
+Form2 functional forms do not have the ``handle`` master-method that
+is called after the form is initialized. Instead, we deal with this
+functionality manually. Here are some things that the ``handle``
+portion of your form might need to implement illustrated in the
+order that often makes sense.
+
+ 1. Get the value of any submit buttons using ``form.get_submit``
+ 2. If the form has not been submitted yet, return ``render()``.
+ 3. See if the cancel button was pressed, if so return a redirect.
+ 4. Call your ``process`` inner function to do any widget-level error
+ checks. The form may already have set some errors, so you
+ may wish to check for that before trying additional error checks.
+ 5. See if the form was submitted by an unknown submit button.
+ This will be the case if the form was submitted via a JavaScript
+ action, which is the case when an option select widget is selected.
+ The value of ``get_submit`` is ``True`` in this case and if it is,
+ you want to clear any errors and re-render the form.
+ 6. If the form has not been submitted or if the form has errors,
+ you simply want to render the form.
+ 7. Check for your named submit buttons which you expect for
+ successful form posting e.g. ``add`` or ``update``. If one of
+ these is pressed, call you action inner function.
+ 8. Finally, return a redirect to the expected page following a
+ form submission.
+
+These steps are illustrated by the following snippet of code and to a
+large degree in the above functional form2 code example. Often this
+``handle`` block of code can be simplified. For example, if you do not
+expect form submissions from unregistered submit buttons, you can
+eliminate the test for that. Similarly, if your form does not do any
+widget-specific error checking, there's no reason to have an error
+checking ``process`` function or the call to it::
+
+ exit_path = request.get_path(1) + '/'
+ submit = form.get_submit()
+ if not submit:
+ return render()
+ if submit == 'cancel':
+ return request.redirect(exit_path)
+ if submit == True:
+ form.clear_errors()
+ return render()
+ process()
+ if form.has_errors():
+ return render()
+ action(submit)
+ return request.redirect(exit_path)
diff --git a/pypers/europython05/Quixote-2.0/doc/multi-threaded.txt b/pypers/europython05/Quixote-2.0/doc/multi-threaded.txt
new file mode 100755
index 0000000..f3be1a5
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/multi-threaded.txt
@@ -0,0 +1,39 @@
+Multi-Threaded Quixote Applications
+===================================
+
+Starting with Quixote 0.6, it's possible to write multi-threaded Quixote
+applications. In previous versions, Quixote stored the current
+HTTPRequest object in a global variable, meaning that processing
+multiple requests in the same process simultaneously was impossible.
+
+However, the Publisher class as shipped still can't handle multiple
+simultaneous requests; you'll need to subclass Publisher to make it
+re-entrant. Here's a starting point::
+
+ import thread
+ from quixote.publish import Publisher
+
+ [...]
+
+ class ThreadedPublisher (Publisher):
+ def __init__ (self, root_namespace, config=None):
+ Publisher.__init__(self, root_namespace, config)
+ self._request_dict = {}
+
+ def _set_request(self, request):
+ self._request_dict[thread.get_ident()] = request
+
+ def _clear_request(self):
+ try:
+ del self._request_dict[thread.get_ident()]
+ except KeyError:
+ pass
+
+ def get_request(self):
+ return self._request_dict.get(thread.get_ident())
+
+Using ThreadedPublisher, you now have one current request per thread,
+rather than one for the entire process.
+
+
+$Id: multi-threaded.txt 20217 2003-01-16 20:51:53Z akuchlin $
diff --git a/pypers/europython05/Quixote-2.0/doc/programming.txt b/pypers/europython05/Quixote-2.0/doc/programming.txt
new file mode 100755
index 0000000..d94adf7
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/programming.txt
@@ -0,0 +1,157 @@
+Quixote Programming Overview
+============================
+
+This document explains how a Quixote application is structured.
+The demo.txt file should probably be read before you read this file.
+There are three components to a Quixote application:
+
+1) A driver script, usually a CGI or FastCGI script. This is the
+ interface between your web server (eg., Apache) and the bulk of your
+ application code. The driver script is responsible for creating a
+ Quixote publisher customized for your application and invoking its
+ publishing loop.
+
+2) A configuration file. This file specifies various features of the
+ Publisher class, such as how errors are handled, the paths of
+ various log files, and various other things. Read through
+ quixote/config.py for the full list of configuration settings.
+
+ The most important configuration parameters are:
+
+ ``ERROR_EMAIL``
+ e-mail address to which errors will be mailed
+ ``ERROR_LOG``
+ file to which errors will be logged
+
+ For development/debugging, you should also set ``DISPLAY_EXCEPTIONS``
+ true; the default value is false, to favor security over convenience.
+
+3) Finally, the bulk of the code will be called through a call (by the
+ Publisher) to the _q_traverse() method of an instance designated as
+ the ``root_directory``. Normally, the root_directory will be an
+ instance of the Directory class.
+
+
+Driver script
+-------------
+
+The driver script is the interface between your web server and Quixote's
+"publishing loop", which in turn is the gateway to your application
+code. Thus, there are two things that your Quixote driver script must
+do:
+
+* create a Quixote publisher -- that is, an instance of the Publisher
+ class provided by the quixote.publish module -- and customize it for
+ your application
+
+* invoke the publisher's process_request() method as needed to get
+ responses for one or more requests, writing the responses back
+ to the client(s).
+
+The publisher is responsible for translating URLs to Python objects and
+calling the appropriate function, method, or PTL template to retrieve
+the information and/or carry out the action requested by the URL.
+
+The most important application-specific customization done by the driver
+script is to set the root directory of your application.
+
+The quixote.servers package includes driver modules for cgi, fastcgi,
+scgi, medusa, twisted, and the simple_server. Each of these modules
+includes a ``run()`` function that you can use in a driver script that
+provides a function to create the publisher that you want. For an example
+of this pattern, see the __main__ part of demo/mini_demo.py. You could
+run the mini_demo.py with scgi by using the ``run()`` function imported
+from quixote.server.scgi_server instead of the one from
+quixote.server.simple_server. (You would also need your http server
+set up to use the scgi server.)
+
+That's almost the simplest possible case -- there's no
+application-specific configuration info apart from the root directory.
+
+Getting the driver script to actually run is between you and your web
+server. See the web-server.txt document for help.
+
+
+Configuration file
+------------------
+
+By default, the Publisher uses the configuration information from
+quixote/config.py. You should never edit the default values in
+quixote/config.py, because your edits will be lost if you upgrade to a
+newer Quixote version. You should certainly read it, though, to
+understand what all the configuration variables are. If you want to
+customize any of the configuration variables, your driver script
+should provide your customized Config instance as an argument to the
+Publisher constructor.
+
+Logging
+-------
+
+The publisher also accepts an optional ``logger`` keyword argument,
+that should, if provided, support the same methods as the
+default value, an instance of ``DefaultLogger``. Even if you
+use the default logger, you can still customize the behavior
+by setting configuration values for ``access_log``, ``error_log``, and/or
+``error_email``. These configuration variables are described
+more fully in config.py.
+
+Quixote writes one (rather long) line to the access log for each request
+it handles; we have split that line up here to make it easier to read::
+
+ 127.0.0.1 - 2001-10-15 09:48:43
+ 2504 "GET /catalog/ HTTP/1.1"
+ 200 'Opera/6.0 (Linux; U)' 0.10sec
+
+This line consists of:
+
+* client IP address
+* current user (according to Quixote session management mechanism,
+ so this will be "-" unless you're using a session manager that
+ does authentication)
+* date and time of request in local timezone, as YYYY-MM-DD hh:mm:ss
+* process ID of the process serving the request (eg. your CGI/FastCGI
+ driver script)
+* the HTTP request line (request method, URI, and protocol)
+* response status code
+* HTTP user agent string (specifically, this is
+ ``repr(os.environ.get('HTTP_USER_AGENT', ''))``)
+* time to complete the request
+
+If no access log is configured (ie., ``ACCESS_LOG`` is ``None``), then
+Quixote will not do any access logging.
+
+The error log is used for three purposes:
+
+* application output to ``sys.stdout`` and ``sys.stderr`` goes to
+ Quixote's error log
+* application tracebacks will be written to Quixote's error log
+
+If no error log is configured (with ``ERROR_LOG``), then all output is
+redirected to the stderr supplied to Quixote for this request by your
+web server. At least for CGI/FastCGI scripts under Apache, this winds
+up in Apache's error log.
+
+Having stdout redirected to the error log is useful for debugging. You
+can just sprinkle ``print`` statements into your application and the
+output will wind up in the error log.
+
+
+Application code
+----------------
+
+Finally, we reach the most complicated part of a Quixote application.
+However, thanks to Quixote's design, everything you've ever learned
+about designing and writing Python code is applicable, so there are no
+new hoops to jump through. You may, optionally, wish to use PTL,
+which is simply Python with a novel way of generating function return
+values -- see PTL.txt for details.
+
+Quixote's Publisher constructs a request, splits the path into a list
+of components, and calls the root directory's _q_traverse() method,
+giving the component list as an argument. The _q_traverse() will either
+return a value that will become the content of the HTTPResponse, or
+else it may raise an Exception. Exceptions are caught by the Publisher
+and handled as needed, depending on configuration variables and
+whether or not the Exception is an instance of PublisherError.
+
+
diff --git a/pypers/europython05/Quixote-2.0/doc/session-mgmt.txt b/pypers/europython05/Quixote-2.0/doc/session-mgmt.txt
new file mode 100755
index 0000000..19df072
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/session-mgmt.txt
@@ -0,0 +1,323 @@
+Quixote Session Management
+==========================
+
+HTTP was originally designed as a stateless protocol, meaning that every
+request for a document or image was conducted in a separate TCP
+connection, and that there was no way for a web server to tell if two
+separate requests actually come from the same user. It's no longer
+necessarily true that every request is conducted in a separate TCP
+connection, but HTTP is still fundamentally stateless. However, there
+are many applications where it is desirable or even essential to
+establish a "session" for each user, ie. where all requests performed by
+that user are somehow tied together on the server.
+
+HTTP cookies were invented to address this requirement, and they are
+still the best solution for establishing sessions on top of HTTP. Thus,
+the session management mechanism that comes with Quixote is
+cookie-based. (The most common alternative is to embed the session
+identifier in the URL. Since Quixote views the URL as a fundamental
+part of the web user interface, a URL-based session management scheme is
+considered un-Quixotic.)
+
+For further reading: the standard for cookies that is approximately
+implemented by most current browsers is RFC 2109; the latest version of
+the standard is RFC 2965.
+
+In a nutshell, session management with Quixote works like this:
+
+* when a user-agent first requests a page from a Quixote application
+ that implements session management, Quixote creates a Session object
+ and generates a session ID (a random 64-bit number). The Session
+ object is attached to the current HTTPRequest object, so that
+ application code involved in processing this request has access to
+ the Session object. The get_session() function provides uniform
+ access to the current Session object.
+
+* if, at the end of processing that request, the application code has
+ stored any information in the Session object, Quixote saves the
+ session in its SessionManager object for use by future requests and
+ sends a session cookie, called ``QX_session`` by default, to the user.
+ The session cookie contains the session ID encoded as a hexadecimal
+ string, and is included in the response headers, eg. ::
+
+ Set-Cookie: QX_session="928F82A9B8FA92FD"
+
+ (You can instruct Quixote to specify the domain and path for
+ URLs to which this cookie should be sent.)
+
+* the user agent stores this cookie for future requests
+
+* the next time the user agent requests a resource that matches the
+ cookie's domain and path, it includes the ``QX_session`` cookie
+ previously generated by Quixote in the request headers, eg.::
+
+ Cookie: QX_session="928F82A9B8FA92FD"
+
+* while processing the request, Quixote decodes the session ID and
+ looks up the corresponding Session object in its SessionManager. If
+ there is no such session, the session cookie is bogus or
+ out-of-date, so Quixote raises SessionError; ultimately the user
+ gets an error page. Otherwise, the Session object is made
+ available, through the get_session() function, as the application
+ code processes the request.
+
+There are two caveats to keep in mind before proceeding, one major and
+one minor:
+
+* Quixote's standard Session and SessionManager class do not
+ implement any sort of persistence, meaning that all sessions
+ disappear when the process handling web requests terminates.
+ Thus, session management is completely useless with a plain
+ CGI driver script unless you add some persistence to the mix;
+ see "Session persistence" below for information.
+
+* Quixote never expires sessions; if you want user sessions to
+ be cleaned up after a period of inactivity, you will have to
+ write code to do it yourself.
+
+
+Session management demo
+-----------------------
+
+There's a simple demo of Quixote's session management in demo/altdemo.py.
+If the durus (http://www.mems-exchange.org/software/durus/) package is
+installed, the demo uses a durus database to store sessions, so sessions
+will be preserved, even if your are running it with plain cgi.
+
+This particular application uses sessions to keep track of just two
+things: the user's identity and the number of requests made in this
+session. The first is addressed by Quixote's standard Session class --
+every Session object has a ``user`` attribute, which you can use for
+anything you like. In the session demo, we simply store a string, the
+user's name, which is entered by the user.
+
+Tracking the number of requests is a bit more interesting: from the
+DemoSession class in altdemo.py::
+
+ def __init__ (self, id):
+ Session.__init__(self, id)
+ self.num_requests = 0
+
+ def start_request (self):
+ Session.start_request(self)
+ self.num_requests += 1
+
+When the session is created, we initialize the request counter; and
+when we start processing each request, we increment it. Using the
+session information in the application code is simple. If you want the
+value of the user attribute of the current session, just call
+get_user(). If you want some other attribute or method Use
+get_session() to get the current Session if you need access to other
+attributes (such as ``num_requests`` in the demo) or methods of the
+current Session instance.
+
+Note that the Session class initializes the user attribute to None,
+so get_user() will return None if no user has been identified for
+this session. Application code can use this to change behavior,
+as in the following::
+
+ if not get_user():
+ content += htmltext('<p>%s</p>' % href('login', 'login'))
+ else:
+ content += htmltext(
+ '<p>Hello, %s.</p>') % get_user()
+ content += htmltext('<p>%s</p>' % href('logout', 'logout'))
+
+
+Note that we must quote the user's name, because they are free to enter
+anything they please, including special HTML characters like ``&`` or
+``<``.
+
+Of course, ``session.user`` will never be set if we don't set it
+ourselves. The code that processes the login form is just this (from
+``login()`` in ``demo/altdemo.py``) ::
+
+ if get_field("name"):
+ session = get_session()
+ session.set_user(get_field("name")) # This is the important part.
+
+This is obviously a very simple application -- we're not doing any
+verification of the user's input. We have no user database, no
+passwords, and no limitations on what constitutes a "user name". A real
+application would have all of these, as well as a way for users to add
+themselves to the user database -- ie. register with your web site.
+
+
+Configuring the session cookie
+------------------------------
+
+Quixote allows you to configure several aspects of the session cookie
+that it exchanges with clients. First, you can set the name of the
+cookie; this is important if you have multiple independent Quixote
+applications running on the same server. For example, the config file
+for the first application might have ::
+
+ SESSION_COOKIE_NAME = "foo_session"
+
+and the second application might have ::
+
+ SESSION_COOKIE_NAME = "bar_session"
+
+Next, you can use ``SESSION_COOKIE_DOMAIN`` and ``SESSION_COOKIE_PATH``
+to set the cookie attributes that control which requests the cookie is
+included with. By default, these are both ``None``, which instructs
+Quixote to send the cookie without ``Domain`` or ``Path`` qualifiers.
+For example, if the client requests ``/foo/bar/`` from
+www.example.com, and Quixote decides that it must set the session
+cookie in the response to that request, then the server would send ::
+
+ Set-Cookie: QX_session="928F82A9B8FA92FD"
+
+in the response headers. Since no domain or path were specified with
+that cookie, the browser will only include the cookie with requests to
+www.example.com for URIs that start with ``/foo/bar/``.
+
+If you want to ensure that your session cookie is included with all
+requests to www.example.com, you should set ``SESSION_COOKIE_PATH`` in your
+config file::
+
+ SESSION_COOKIE_PATH = "/"
+
+which will cause Quixote to set the cookie like this::
+
+ Set-Cookie: QX_session="928F82A9B8FA92FD"; Path="/"
+
+which will instruct the browser to include that cookie with *all*
+requests to www.example.com.
+
+However, think carefully about what you set ``SESSION_COOKIE_PATH`` to
+-- eg. if you set it to "/", but all of your Quixote code is under "/q/"
+in your server's URL-space, then your user's session cookies could be
+unnecessarily exposed. On shared servers where you don't control all of
+the code, this is especially dangerous; be sure to use (eg.) ::
+
+ SESSION_COOKIE_PATH = "/q/"
+
+on such servers. The trailing slash is important; without it, your
+session cookies will be sent to URIs like ``/qux`` and ``/qix``, even if
+you don't control those URIs.
+
+If you want to share the cookie across servers in your domain,
+eg. www1.example.com and www2.example.com, you'll also need to set
+``SESSION_COOKIE_DOMAIN``:
+
+ SESSION_COOKIE_DOMAIN = ".example.com"
+
+Finally, note that the ``SESSION_COOKIE_*`` configuration variables
+*only* affect Quixote's session cookie; if you set your own cookies
+using the ``HTTPResponse.set_cookie()`` method, then the cookie sent to
+the client is completely determined by that ``set_cookie()`` call.
+
+See RFCs 2109 and 2965 for more information on the rules browsers are
+supposed to follow for including cookies with HTTP requests.
+
+
+Writing the session class
+-------------------------
+
+You will almost certainly have to write a custom session class for your
+application by subclassing Quixote's standard Session class. Every
+custom session class has two essential responsibilities:
+
+* initialize the attributes that will be used by your application
+
+* override the ``has_info()`` method, so the session manager knows when
+ it must save your session object
+
+The first one is fairly obvious and just good practice. The second is
+essential, and not at all obvious. The has_info() method exists because
+SessionManager does not automatically hang on to all session objects;
+this is a defense against clients that ignore cookies, making your
+session manager create lots of session objects that are just used once.
+As long as those session objects are not saved, the burden imposed by
+these clients is not too bad -- at least they aren't sucking up your
+memory, or bogging down the database that you save session data to.
+Thus, the session manager uses has_info() to know if it should hang on
+to a session object or not: if a session has information that must be
+saved, the session manager saves it and sends a session cookie to the
+client.
+
+For development/testing work, it's fine to say that your session objects
+should always be saved::
+
+ def has_info (self):
+ return 1
+
+The opposite extreme is to forget to override ``has_info()`` altogether,
+in which case session management most likely won't work: unless you
+tickle the Session object such that the base ``has_info()`` method
+returns true, the session manager won't save the sessions that it
+creates, and Quixote will never drop a session cookie on the client.
+
+In a real application, you need to think carefully about what data to
+store in your sessions, and how ``has_info()`` should react to the
+presence of that data. If you try and track something about every
+single visitor to your site, sooner or later one of those a
+broken/malicious client that ignores cookies and ``robots.txt`` will
+come along and crawl your entire site, wreaking havoc on your Quixote
+application (or the database underlying it).
+
+
+Session persistence
+-------------------
+
+Keeping session data across requests is all very nice, but in the real
+world you want that data to survive across process termination. With
+CGI, this is essential, since each process serves exactly one request
+and then terminates. With other execution mechanisms, though, it's
+still important -- you don't want to lose all your session data just
+because your long-lived server process was restarted, or your server
+machine was rebooted.
+
+However, every application is different, so Quixote doesn't provide any
+built-in mechanism for session persistence. Instead, it provides a
+number of hooks, most in the SessionManager class, that let you plug in
+your preferred persistence mechanism.
+
+The first and most important hook is in the SessionManager
+constructor: you can provide an alternate mapping object that
+SessionManager will use to store session objects in. By default,
+SessionManager uses an ordinary dictionary; if you provide a mapping
+object that implements persistence, then your session data will
+automatically persist across processes.
+
+The second hook (two hooks, really) apply if you use a transactional
+persistence mechanism to provide your SessionManager's mapping. The
+``altdemo.py`` script does this with Durus, if the durus package is
+installed, but you could also use ZODB or a relational database for
+this purpose. The hooks make sure that session (and other) changes
+get committed or aborted at the appropriate times. SessionManager
+provides two methods for you to override: ``forget_changes()`` and
+``commit_changes()``. ``forget_changes()`` is called by
+SessionPublisher whenever a request crashes, ie. whenever your
+application raises an exception other than PublishError.
+``commit_changes()`` is called for requests that complete
+successfully, or that raise a PublishError exception. You'll have to
+use your own SessionManager subclass if you need to take advantage of
+these hooks for transactional session persistence.
+
+The third available hook is the Session's is_dirty() method. This is
+used when your mapping class uses a more primitive storage mechanism,
+as, for example, the standard 'shelve' module, which provides a
+mapping object on top of a DBM or Berkeley DB file::
+
+ import shelve
+ sessions = shelve.open("/tmp/quixote-sessions")
+ session_manager = SessionManager(session_mapping=sessions)
+
+If you use one of these relatively simple persistent mapping types,
+you'll also need to override ``is_dirty()`` in your Session class.
+That's in addition to overriding ``has_info()``, which determines if a
+session object is *ever* saved; ``is_dirty()`` is only called on
+sessions that have already been added to the session mapping, to see
+if they need to be "re-added". The default implementation always
+returns false, because once an object has been added to a normal
+dictionary, there's no need to add it again. However, with simple
+persistent mapping types like shelve, you need to store the object
+again each time it changes. Thus, ``is_dirty()`` should return true
+if the session object needs to be re-written. For a simple, naive,
+but inefficient implementation, making is_dirty an alias for
+``has_info()`` will work -- that just means that once the session has
+been written once, it will be re-written on every request.
+
+
diff --git a/pypers/europython05/Quixote-2.0/doc/static-files.txt b/pypers/europython05/Quixote-2.0/doc/static-files.txt
new file mode 100755
index 0000000..64254f4
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/static-files.txt
@@ -0,0 +1,51 @@
+Examples of serving static files
+================================
+
+The ``quixote.util`` module includes classes for making files and
+directories available as Quixote resources. Here are some examples.
+
+
+Publishing a Single File
+------------------------
+
+The ``StaticFile`` class makes an individual filesystem file (possibly
+a symbolic link) available. You can also specify the MIME type and
+encoding of the file; if you don't specify this, the MIME type will be
+guessed using the standard Python ``mimetypes.guess_type()`` function.
+The default action is to not follow symbolic links, but this behaviour
+can be changed using the ``follow_symlinks`` parameter.
+
+The following example publishes a file with the URL ``.../stylesheet_css``::
+
+ # 'stylesheet_css' must be in the _q_exports list
+ _q_exports = [ ..., 'stylesheet_css', ...]
+
+ stylesheet_css = StaticFile(
+ "/htdocs/legacy_app/stylesheet.css",
+ follow_symlinks=1, mime_type="text/css")
+
+
+If you want the URL of the file to have a ``.css`` extension, you use
+the external to internal name mapping feature of ``_q_exports``. For
+example::
+
+ _q_exports = [ ..., ('stylesheet.css', 'stylesheet_css'), ...]
+
+
+
+Publishing a Directory
+----------------------
+
+Publishing a directory is similar. The ``StaticDirectory`` class
+makes a complete filesystem directory available. Again, the default
+behaviour is to not follow symlinks. You can also request that the
+``StaticDirectory`` object cache information about the files in
+memory so that it doesn't try to guess the MIME type on every hit.
+
+This example publishes the ``notes/`` directory::
+
+ _q_exports = [ ..., 'notes', ...]
+
+ notes = StaticDirectory("/htdocs/legacy_app/notes")
+
+
diff --git a/pypers/europython05/Quixote-2.0/doc/upgrading.txt b/pypers/europython05/Quixote-2.0/doc/upgrading.txt
new file mode 100755
index 0000000..4d002cb
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/upgrading.txt
@@ -0,0 +1,324 @@
+Upgrading code from older versions of Quixote
+=============================================
+
+This document lists backward-incompatible changes in Quixote, and
+explains how to update application code to work with the newer
+version.
+
+Changes from 1.0 to 2.0
+-------------------------
+
+Change any imports you have from quixote.form to be from quixote.form1.
+
+Change any imports you have from quixote.form2 to be from quixote.form.
+
+Replace calls to HTTPRequest.get_form_var() with calls to get_field().
+
+Define a create_publisher() function to get the publisher you need
+and figure out how you want to connect it to web server.
+See files in demo and server for examples. Note that publish1.py
+contains a publisher that works more like the Quixote1 Publisher,
+and does not require the changes listed below.
+
+Make every namespace be an instance of quixote.directory.Directory.
+Update namespaces that are modules (or in the init.py of a package) by
+defining a new class in the module that inherits from Directory and
+moving your _q_exports and _q_* functions onto the class. Replace
+"request" parameters with "self" parameters on the new methods. If
+you have a _q_resolve method, include Resolving in the bases of your
+new class.
+
+Remove request from calls to _q_ functions. If request, session,
+user, path, or redirect is used in these new methods, replace as
+needed with calls to get_request(), get_session(), get_user(),
+get_path(), and/or redirect(), imported from quixote.
+
+In every namespace that formerly traversed into a module, import the
+new Directory class from the module and create an instance of the
+Directory in a variable whose name is the name of the module.
+
+In every namespace with a _q_exports and a _q_index, either add "" to
+_q_exports or make sure that _q_lookup handles "" by returning the result
+of a call to _q_index.
+
+If your code depends on the Publisher's namespace_stack attribute,
+try using quixote.util.get_directory_path() instead. If you need the
+namespace stack after the traversal, override Directory._q_traverse()
+to call get_directory_path() when the end of the path is reached, and
+record the result somewhere for later reference.
+
+If your code depends on _q_exception_handler, override the _q_traverse
+on your root namespace or on your own Directory class to catch exceptions
+and handle them the way you want. If you just want a general customization
+for exception responses, you can change or override
+Publisher.format_publish_error().
+
+If your code depended on _q_access, include the AccessControlled with
+the bases of your Directory classes as needed.
+
+Provide imports as needed to htmltext, TemplateIO, get_field,
+get_request, get_session, get_user, get_path, redirect, ?. You may
+find dulcinea/bin/unknown.py useful for identifying missing imports.
+
+Quixote 1's secure_errors configuration variable is not present in Quixote 2.
+
+Form.__init__ no longer has name or attrs keywords. If your existing
+code calls Form.__init__ with 'attrs=foo', you'll need to change it to
+'**foo'. Form instances no longer have a name attribute. If your code
+looks for form.name, you can find it with form.attrs.get('name').
+The Form.__init__ keyword parameter (and attribute) 'action_url' is now
+named 'action'.
+
+The SessionPublisher class is gone. Use the Publisher class instead.
+Also, the 'session_mgr' keyword has been renamed to 'session_manager'.
+
+
+Changes from 0.6.1 to 1.0
+-------------------------
+
+Sessions
+********
+
+A leading underscore was removed from the ``Session`` attributes
+``__remote_address``, ``__creation_time``, and ``__access_time``. If
+you have pickled ``Session`` objects you will need to upgrade them
+somehow. Our preferred method is to write a script that unpickles each
+object, renames the attributes and then re-pickles it.
+
+
+
+Changes from 0.6 to 0.6.1
+-------------------------
+
+``_q_exception_handler`` now called if exception while traversing
+*****************************************************************
+
+``_q_exception_handler`` hooks will now be called if an exception is
+raised during the traversal process. Quixote 0.6 had a bug that caused
+``_q_exception_handler`` hooks to only be called if an exception was
+raised after the traversal completed.
+
+
+
+Changes from 0.5 to 0.6
+-----------------------
+
+``_q_getname`` renamed to ``_q_lookup``
+***************************************
+
+The ``_q_getname`` special function was renamed to ``_q_lookup``,
+because that name gives a clearer impression of the function's
+purpose. In 0.6, ``_q_getname`` still works but will trigger a
+warning.
+
+
+Form Framework Changes
+**********************
+
+The ``quixote.form.form`` module was changed from a .ptl file to a .py
+file. You should delete or move the existing ``quixote/`` directory
+in ``site-packages`` before running ``setup.py``, or at least delete
+the old ``form.ptl`` and ``form.ptlc`` files.
+
+The widget and form classes in the ``quixote.form`` package now return
+``htmltext`` instances. Applications that use forms and widgets will
+likely have to be changed to use the ``[html]`` template type to avoid
+over-escaping of HTML special characters.
+
+Also, the constructor arguments to ``SelectWidget`` and its subclasses have
+changed. This only affects applications that use the form framework
+located in the ``quixote.form`` package.
+
+In Quixote 0.5, the ``SelectWidget`` constructor had this signature::
+
+ def __init__ (self, name, value=None,
+ allowed_values=None,
+ descriptions=None,
+ size=None,
+ sort=0):
+
+``allowed_values`` was the list of objects that the user could choose,
+and ``descriptions`` was a list of strings that would actually be
+shown to the user in the generated HTML.
+
+In Quixote 0.6, the signature has changed slightly::
+
+ def __init__ (self, name, value=None,
+ allowed_values=None,
+ descriptions=None,
+ options=None,
+ size=None,
+ sort=0):
+
+The ``quote`` argument is gone, and the ``options`` argument has been
+added. If an ``options`` argument is provided, ``allowed_values``
+and ``descriptions`` must not be supplied.
+
+The ``options`` argument, if present, must be a list of tuples with
+1,2, or 3 elements, of the form ``(value:any, description:any,
+key:string)``.
+
+ * ``value`` is the object that will be returned if the user chooses
+ this item, and must always be supplied.
+
+ * ``description`` is a string or htmltext instance which will be
+ shown to the user in the generated HTML. It will be passed
+ through the htmlescape() functions, so for an ordinary string
+ special characters such as '&' will be converted to '&amp;'.
+ htmltext instances will be left as they are.
+
+ * If supplied, ``key`` will be used in the value attribute
+ of the option element (``<option value="...">``).
+ If not supplied, keys will be generated; ``value`` is checked for a
+ ``_p_oid`` attribute and if present, that string is used;
+ otherwise the description is used.
+
+In the common case, most applications won't have to change anything,
+though the ordering of selection items may change due to the
+difference in how keys are generated.
+
+
+File Upload Changes
+*******************
+
+Quixote 0.6 introduces new support for HTTP upload requests. Any HTTP
+request with a Content-Type of "multipart/form-data" -- which is
+generally only used for uploads -- is now represented by
+HTTPUploadRequest, a subclass of HTTPRequest, and the uploaded files
+themselves are represented by Upload objects.
+
+Whenever an HTTP request has a Content-Type of "multipart/form-data",
+an instance of HTTPUploadRequest is created instead of HTTPRequest.
+Some of the fields in the request are presumably uploaded files and
+might be quite large, so HTTPUploadRequest will read all of the fields
+supplied in the request body and write them out to temporary files;
+the temporary files are written in the directory specified by the
+UPLOAD_DIR configuration variable.
+
+Once the temporary files have been written, the HTTPUploadRequest
+object is passed to a function or PTL template, just like an ordinary
+request. The difference between HTTPRequest and HTTPUploadRequest
+is that all of the form variables are represented as Upload objects.
+Upload objects have three attributes:
+
+``orig_filename``
+ the filename supplied by the browser.
+``base_filename``
+ a stripped-down version of orig_filename with unsafe characters removed.
+ This could be used when writing uploaded data to a permanent location.
+``tmp_filename``
+ the path of the temporary file containing the uploaded data for this field.
+
+Consult upload.txt for more information about handling file uploads.
+
+
+Refactored `Publisher` Class
+****************************
+
+Various methods in the `Publisher` class were rearranged. If your
+application subclasses Publisher, you may need to change your code
+accordingly.
+
+ * ``parse_request()`` no longer creates the HTTPRequest object;
+ instead a new method, ``create_request()``, handles this,
+ and can be overridden as required.
+
+ As a result, the method signature has changed from
+ ``parse_request(stdin, env)`` to ``parse_request(request)``.
+
+ * The ``Publisher.publish()`` method now catches exceptions raised
+ by ``parse_request()``.
+
+
+Changes from 0.4 to 0.5
+-----------------------
+
+Session Management Changes
+**************************
+
+The Quixote session management interface underwent lots of change and
+cleanup with Quixote 0.5. It was previously undocumented (apart from
+docstrings in the code), so we thought that this was a good opportunity
+to clean up the interface. Nevertheless, those brave souls who got
+session management working just by reading the code are in for a bit of
+suffering; this brief note should help clarify things. The definitive
+documentation for session management is session-mgmt.txt -- you should
+start there.
+
+
+Attribute renamings and pickled objects
++++++++++++++++++++++++++++++++++++++++
+
+Most attributes of the standard Session class were made private in order
+to reduce collisions with subclasses. The downside is that pickled
+Session objects will break. You might want to (temporarily) modify
+session.py and add this method to Session::
+
+ def __setstate__ (self, dict):
+ # Update for attribute renamings made in rev. 1.51.2.3
+ # (between Quixote 0.4.7 and 0.5).
+ self.__dict__.update(dict)
+ if hasattr(self, 'remote_address'):
+ self.__remote_address = self.remote_address
+ del self.remote_address
+ if hasattr(self, 'creation_time'):
+ self.__creation_time = self.creation_time
+ del self.creation_time
+ if hasattr(self, 'access_time'):
+ self.__access_time = self.access_time
+ del self.access_time
+ if hasattr(self, 'form_tokens'):
+ self._form_tokens = self.form_tokens
+ del self.form_tokens
+
+However, if your sessions were pickled via ZODB, this may not work. (It
+didn't work for us.) In that case, you'll have to add something like
+this to your class that inherits from both ZODB's Persistent and
+Quixote's Session::
+
+ def __setstate__ (self, dict):
+ # Blechhh! This doesn't work if I put it in Quixote's
+ # session.py, so I have to second-guess how Python
+ # treats "__" attribute names.
+ self.__dict__.update(dict)
+ if hasattr(self, 'remote_address'):
+ self._Session__remote_address = self.remote_address
+ del self.remote_address
+ if hasattr(self, 'creation_time'):
+ self._Session__creation_time = self.creation_time
+ del self.creation_time
+ if hasattr(self, 'access_time'):
+ self._Session__access_time = self.access_time
+ del self.access_time
+ if hasattr(self, 'form_tokens'):
+ self._form_tokens = self.form_tokens
+ del self.form_tokens
+
+It's not pretty, but it worked for us.
+
+
+Cookie domains and paths
+++++++++++++++++++++++++
+
+The session cookie config variables -- ``COOKIE_NAME``,
+``COOKIE_DOMAIN``, and ``COOKIE_PATH`` -- have been renamed to
+``SESSION_COOKIE_*`` for clarity.
+
+If you previously set the config variable ``COOKIE_DOMAIN`` to the name
+of your server, this is most likely no longer necessary -- it's now fine
+to leave ``SESSION_COOKIE_DOMAIN`` unset (ie. ``None``), which
+ultimately means browsers will only include the session cookie in
+requests to the same server that sent it to them in the first place.
+
+If you previously set ``COOKIE_PATH``, then you should probably preserve
+your setting as ``SESSION_COOKIE_PATH``. The default of ``None`` means
+that browsers will only send session cookies with requests for URIs
+under the URI that originally resulted in the session cookie being sent.
+See session-mgmt.txt and RFCs 2109 and 2965.
+
+If you previously set ``COOKIE_NAME``, change it to
+``SESSION_COOKIE_NAME``.
+
+
+
+
diff --git a/pypers/europython05/Quixote-2.0/doc/web-server.txt b/pypers/europython05/Quixote-2.0/doc/web-server.txt
new file mode 100755
index 0000000..2abfe21
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/web-server.txt
@@ -0,0 +1,258 @@
+Web Server Configuration for Quixote
+====================================
+
+For a simple Quixote installation, there are two things you have to get
+right:
+
+* installation of the Quixote modules to Python's library (the
+ trick here is that the ``quixote`` package must be visible to the user
+ that CGI scripts run as, not necessarily to you as an interactive
+ command-line user)
+
+* configuration of your web server to run Quixote driver scripts
+
+This document is concerned with the second of these.
+
+
+Which web servers?
+------------------
+
+We are only familiar with Apache, and we develop Quixote for use under
+Apache. However, Quixote doesn't rely on any Apache-specific tricks;
+if you can execute CGI scripts, then you can run Quixote applications
+(although they'll run a lot faster with mod_scgi or FastCGI). If you
+can redirect arbitrary URLs to a CGI script and preserve parts of the
+URL as an add-on to the script name (with ``PATH_INFO``), then you can
+run Quixote applications in the ideal manner, ie. with superfluous
+implementation details hidden from the user.
+
+
+Which operating systems?
+------------------------
+
+We are mainly familiar with Unix, and develop and deploy Quixote under
+Linux. However, we've had several reports of people using Quixote under
+Windows, more-or-less successfully. There are still a few Unix-isms in
+the code, but they are being rooted out in favor of portability.
+
+Remember that your system is only as secure as its weakest link.
+Quixote can't help you write secure web applications on an inherently
+insecure operating system.
+
+
+Basic CGI configuration
+-----------------------
+
+Throughout this document, I'm going to assume that:
+
+* CGI scripts live in the ``/www/cgi-bin`` directory of your web server,
+ and have the extension ``.cgi``
+
+* HTTP requests for ``/cgi-bin/foo.cgi`` will result in the execution
+ of ``/www/cgi-bin/foo.cgi`` (for various values of ``foo``)
+
+* if the web server is instructed to serve an executable file
+ ``bar.cgi``, the file is treated as a CGI script
+
+With Apache, these configuration directives will do the trick::
+
+ AddHandler cgi-script .cgi
+ ScriptAlias /cgi-bin/ /www/cgi-bin/
+
+Consult the Apache documentation for other ways of configuring CGI
+script execution.
+
+For other web servers, consult your server's documentation.
+
+
+Installing driver scripts
+-------------------------
+
+Given the above configuration, installing a Quixote driver script is the
+same as installing any other CGI script: copy it to ``/www/cgi-bin`` (or
+whatever). To install the Quixote demo's cgi driver script::
+
+ cp -p server/cgi_server.py /www/cgi-bin/demo.cgi
+
+(The ``-p`` option ensures that ``cp`` preserves the file mode, so that
+it remains executable.)
+
+
+URL rewriting
+-------------
+
+With the above configuration, users need to use URLs like ::
+
+ http://www.example.com/cgi-bin/demo.cgi
+
+to access the Quixote demo (or other Quixote applications installed in
+the same way). This works, but it's ugly and unnecessarily exposes
+implementation details.
+
+In our view, it's preferable to give each Quixote application its own
+chunk of URL-space -- a "virtual directory" if you like. For example,
+you might want ::
+
+ http://www.example.com/qdemo
+
+to handle the Quixote demo.
+
+With Apache, this is quite easy, as long as mod_rewrite is compiled,
+loaded, and enabled. (Building and loading Apache modules is beyond the
+scope of this document; consult the Apache documentation.)
+
+To enable the rewrite engine, use the ::
+
+ RewriteEngine on
+
+directive. If you have virtual hosts, make sure to repeat this for each
+``<VirtualHost>`` section of your config file.
+
+The rewrite rule to use in this case is ::
+
+ RewriteRule ^/qdemo(/.*) /www/cgi-bin/demo.cgi$1 [last]
+
+This is *not* a redirect; this is all handled with one HTTP
+request/response cycle, and the user never sees ``/cgi-bin/demo.cgi`` in
+a URL.
+
+Note that requests for ``/qdemo/`` and ``/qdemo`` are *not* the same; in
+particular, with the above rewrite rule, the former will succeed and the
+latter will not. (Look at the regex again if you don't believe me:
+``/qdemo`` doesn't match the regex, so ``demo.cgi`` is never invoked.)
+
+The solution for ``/qdemo`` is the same as if it corresponded to a
+directory in your document tree: redirect it to ``/qdemo/``. Apache
+(and, presumably, other web servers) does this automatically for "real"
+directories; however, ``/qdemo/`` is just a directory-like chunk of
+URL-space, so either you or Quixote have to take care of the redirect.
+
+It's almost certainly faster for you to take care of it in the web
+server's configuration. With Apache, simply insert this directive
+*before* the above rewrite rule::
+
+ RewriteRule ^/qdemo$ /qdemo/ [redirect=permanent]
+
+If, for some reason, you are unwilling or unable to instruct your web
+server to perform this redirection, Quixote will do it for you.
+However, you have to make sure that the ``/qdemo`` URL is handled by
+Quixote. Change the rewrite rule to::
+
+ RewriteRule ^/qdemo(/.*)?$ /www/cgi-bin/demo.cgi$1 [last]
+
+Now a request for ``/qdemo`` will be handled by Quixote, and it will
+generate a redirect to ``/qdemo/``. If you're using a CGI driver
+script, this will be painfully slow, but it will work.
+
+For redirecting and rewriting URLs with other web servers, consult your
+server's documentation.
+
+
+Long-running processes
+----------------------
+
+For serious web applications, CGI is unacceptably slow. For a CGI-based
+Quixote application, you have to start a Python interpreter, load the
+Quixote modules, and load your application's modules before you can
+start working. For sophisticated, database-backed applications, you'll
+probably have to open a new database connection as well for every hit.
+
+Small wonder so many high-performance alternatives to CGI exist. (The
+main advantages of CGI are that it is widely supported and easy to
+develop with. Even for large Quixote applications, running in CGI mode
+is nice in development because you don't have to kill a long-running
+driver script every time the code changes.) Quixote includes support
+for mod_scgi and FastCGI.
+
+
+mod_scgi configuration
+----------------------
+
+SCGI is a CGI replacement written by Neil Schemenauer, one of
+Quixote's developers, and is similar to FastCGI but is designed to be
+easier to implement. mod_scgi simply forwards requests to an
+already-running SCGI server on a different TCP port, and doesn't try
+to start or stop processes, leaving that up to the SCGI server.
+
+The SCGI code is available from http://www.mems-exchange.org/software/scgi/ .
+
+The quixote.server.scgi_server module is a script that
+publishes the demo quixote application via SCGI. You can use
+it for your application by importing it and calling the ``run()``
+function with arguments to run your application, on the port
+you choose. Here is an example::
+
+ #!/usr/bin/python
+ from quixote.server.scgi_server import run
+ from quixote.publish import Publisher
+ from mymodule import MyRootDirectory
+
+ def create_my_publisher():
+ return Publisher(MyRootDirectory())
+
+ run(create_my_publisher, port=3001)
+
+The following Apache directive will direct requests to an SCGI server
+running on port 3001::
+
+ <Location />
+ SCGIServer 127.0.0.1 3001
+ SCGIHandler On
+ </Location>
+
+[Note: the mod_scgi module for Apache 2 requires a colon, instead of a
+space, between the host and port on the SCGIServer line.]
+
+
+SCGI through CGI
+----------------
+
+Recent releases of the scgi package include cgi2scgi.c, a small program
+that offers an extremely convenient way to take advantage of SCGI using
+Apache or any web server that supports CGI. To use it, compile the
+cgi2scgi.c and install the compiled program as usual for your
+webserver. The default SCGI port is 3000, but you can change that
+by adding ``-DPORT=3001`` (for example) to your compile command.
+
+Although this method requires a new process to be launched for each
+request, the process is small and fast, so the performance is
+acceptable for many applications.
+
+
+FastCGI configuration
+---------------------
+
+If your web server supports FastCGI, you can significantly speed up your
+Quixote applications with a simple change to your configuration. You
+don't have to change your code at all (unless it makes assumptions about
+how many requests are handled by each process). (See
+http://www.fastcgi.com/ for more information on FastCGI.)
+
+To use FastCGI with Apache, you'll need to download mod_fastcgi from
+http://www.fastcgi.com/ and add it to your Apache installation.
+
+Configuring a FastCGI driver script is best done after reading the fine
+documentation for mod_fastcgi at
+http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html
+
+However, if you just want to try it with the Quixote demo to see if it
+works, add this directive to your Apache configuration::
+
+ AddHandler fastcgi-script .fcgi
+
+and copy server/fastcgi_server.py to demo.fcgi. If you're using a URL
+rewrite to map requests for (eg.) ``/qdemo`` to
+``/www/cgi-bin/demo.cgi``, be sure to change the rewrite -- it should
+now point to ``/www/cgi-bin/demo.fcgi``.
+
+After the first access to ``demo.fcgi`` (or ``/qdemo/`` with the
+modified rewrite rule), the demo should be noticeably faster. You
+should also see a ``demo.fcgi`` process running if you do ``ps -le``
+(``ps -aux`` on BSD-ish systems, or maybe ``ps aux``). (On my 800 MHz
+Athlon machine, there are slight but perceptible delays navigating the
+Quixote demo in CGI mode. In FastCGI mode, the delay between pages is
+no longer perceptible -- navigation is instantaneous.) The larger your
+application is, the more code it loads, and the more work it does at
+startup, the bigger a win FastCGI will be for you (in comparison to CGI).
+
+
diff --git a/pypers/europython05/Quixote-2.0/doc/web-services.txt b/pypers/europython05/Quixote-2.0/doc/web-services.txt
new file mode 100755
index 0000000..c89125c
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/web-services.txt
@@ -0,0 +1,169 @@
+Implementing Web Services with Quixote
+======================================
+
+This document will show you how to implement Web services using
+Quixote.
+
+
+An XML-RPC Service
+------------------
+
+XML-RPC is the simplest protocol commonly used to expose a Web
+service. In XML-RPC, there are a few basic data types such as
+integers, floats, strings, and dates, and a few aggregate types such
+as arrays and structs. The xmlrpclib module, part of the Python 2.2
+standard library and available separately from
+http://www.pythonware.com/products/xmlrpc/, converts between Python's
+standard data types and the XML-RPC data types.
+
+============== =====================
+XML-RPC Type Python Type or Class
+-------------- ---------------------
+<int> int
+<double> float
+<string> string
+<array> list
+<struct> dict
+<boolean> xmlrpclib.Boolean
+<base64> xmlrpclib.Binary
+<dateTime> xmlrpclib.DateTime
+============== =====================
+
+
+Making XML-RPC Calls
+--------------------
+
+Making an XML-RPC call using xmlrpclib is easy. An XML-RPC server
+lives at a particular URL, so the first step is to create an
+xmlrpclib.ServerProxy object pointing at that URL. ::
+
+ >>> import xmlrpclib
+ >>> s = xmlrpclib.ServerProxy(
+ 'http://www.stuffeddog.com/speller/speller-rpc.cgi')
+
+Now you can simply make a call to the spell-checking service offered
+by this server::
+
+ >>> s.speller.spellCheck('my speling isnt gud', {})
+ [{'word': 'speling', 'suggestions': ['apeling', 'spelding',
+ 'spelling', 'sperling', 'spewing', 'spiling'], 'location': 4},
+ {'word': 'isnt', 'suggestions': [``isn't'', 'ist'], 'location': 12}]
+ >>>
+
+This call results in the following XML being sent::
+
+ <?xml version='1.0'?>
+ <methodCall>
+ <methodName>speller.spellCheck</methodName>
+ <params>
+ <param>
+ <value><string>my speling isnt gud</string></value>
+ </param>
+ <param>
+ <value><struct></struct></value>
+ </param>
+ </params>
+ </methodCall>
+
+
+Writing a Quixote Service
+-------------------------
+
+In the quixote.util module, Quixote provides a function,
+``xmlrpc(request, func)``, that processes the body of an XML-RPC
+request. ``request`` is the HTTPRequest object that Quixote passes to
+every function it invokes. ``func`` is a user-supplied function that
+receives the name of the XML-RPC method being called and a tuple
+containing the method's parameters. If there's a bug in the function
+you supply and it raises an exception, the ``xmlrpc()`` function will
+catch the exception and return a ``Fault`` to the remote caller.
+
+Here's an example of implementing a simple XML-RPC handler with a
+single method, ``get_time()``, that simply returns the current
+time. The first task is to expose a URL for accessing the service. ::
+
+ from quixote.directory import Directory
+ from quixote.util import xmlrpc
+ from quixote import get_request
+
+ class RPCDirectory(Directory):
+
+ _q_exports = ['rpc']
+
+ def rpc (self):
+ return xmlrpc(get_request(), rpc_process)
+
+ def rpc_process (meth, params):
+ ...
+
+When the above code is placed in the __init__.py file for the Python
+package corresponding to your Quixote application, it exposes the URL
+``http://<hostname>/rpc`` as the access point for the XML-RPC service.
+
+Next, we need to fill in the contents of the ``rpc_process()``
+function::
+
+ import time
+
+ def rpc_process (meth, params):
+ if meth == 'get_time':
+ # params is ignored
+ now = time.gmtime(time.time())
+ return xmlrpclib.DateTime(now)
+ else:
+ raise RuntimeError, "Unknown XML-RPC method: %r" % meth
+
+``rpc_process()`` receives the method name and the parameters, and its
+job is to run the right code for the method, returning a result that
+will be marshalled into XML-RPC. The body of ``rpc_process()`` will
+therefore usually be an ``if`` statement that checks the name of the
+method, and calls another function to do the actual work. In this case,
+``get_time()`` is very simple so the two lines of code it requires are
+simply included in the body of ``rpc_process()``.
+
+If the method name doesn't belong to a supported method, execution
+will fall through to the ``else`` clause, which will raise a
+RuntimeError exception. Quixote's ``xmlrpc()`` will catch this
+exception and report it to the caller as an XML-RPC fault, with the
+error code set to 1.
+
+As you add additional XML-RPC services, the ``if`` statement in
+``rpc_process()`` will grow more branches. You might be tempted to pass
+the method name to ``getattr()`` to select a method from a module or
+class. That would work, too, and avoids having a continually growing
+set of branches, but you should be careful with this and be sure that
+there are no private methods that a remote caller could access. I
+generally prefer to have the ``if... elif... elif... else`` blocks, for
+three reasons: 1) adding another branch isn't much work, 2) it's
+explicit about the supported method names, and 3) there won't be any
+security holes in doing so.
+
+An alternative approach is to have a dictionary mapping method names
+to the corresponding functions and restrict the legal method names
+to the keys of this dictionary::
+
+ def echo (*params):
+ # Just returns the parameters it's passed
+ return params
+
+ def get_time ():
+ now = time.gmtime(time.time())
+ return xmlrpclib.DateTime(now)
+
+ methods = {'echo' : echo,
+ 'get_time' : get_time}
+
+ def rpc_process (meth, params):
+ func = methods.get[meth]
+ if methods.has_key(meth):
+ # params is ignored
+ now = time.gmtime(time.time())
+ return xmlrpclib.DateTime(now)
+ else:
+ raise RuntimeError, "Unknown XML-RPC method: %r" % meth
+
+This approach works nicely when there are many methods and the
+``if...elif...else`` statement would be unworkably long.
+
+
+$Id: web-services.txt 25695 2004-11-30 20:53:44Z dbinger $
diff --git a/pypers/europython05/Quixote-2.0/doc/widgets.txt b/pypers/europython05/Quixote-2.0/doc/widgets.txt
new file mode 100755
index 0000000..0dc3597
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/widgets.txt
@@ -0,0 +1,524 @@
+Quixote Widget Classes
+======================
+
+[This is reference documentation. If you haven't yet read "Lesson 5:
+widgets" of demo.txt, you should go and do so now. This document also
+assumes you have a good understanding of HTML forms and form elements.
+If not, you could do worse than pick up a copy of *HTML: The Definitive
+Guide* by Chuck Musciano & Bill Kennedy (O'Reilly). I usually keep it
+within arm's reach.]
+
+Web forms are built out of form elements: string input, select lists,
+checkboxes, submit buttons, and so forth. Quixote provides a family of
+classes for handling these form elements, or widgets, in the
+quixote.form.widget module. The class hierarchy is::
+
+ Widget [A]
+ |
+ +--StringWidget
+ | |
+ | +--PasswordWidget
+ | |
+ | +--NumberWidget [*] [A]
+ | |
+ | +-FloatWidget [*]
+ | +-IntWidget [*]
+ |
+ +--TextWidget
+ |
+ +--CheckboxWidget
+ |
+ +--SelectWidget [A]
+ | |
+ | +--SingleSelectWidget
+ | | |
+ | | +-RadiobuttonsWidget
+ | | |
+ | | +-OptionSelectWidget [*]
+ | |
+ | +--MultipleSelectWidget
+ |
+ +--SubmitButtonWidget
+ |
+ +--HiddenWidget
+ |
+ +--ListWidget [*]
+
+ [*] Widget classes that do not correspond exactly with a particular
+ HTML form element
+ [A] Abstract classes
+
+
+Widget: the base class
+----------------------
+
+Widget is the abstract base class for the widget hierarchy. It provides
+the following facilities:
+
+* widget name (``name`` attribute, ``set_name()`` method)
+* widget value (``value`` attribute, ``set_value()`` and ``clear()`` methods)
+* ``__str__()`` and ``__repr__()`` methods
+* some facilities for writing composite widget classes
+
+The Widget constructor signature is::
+
+ Widget(name : string, value : any = None)
+
+``name``
+ the name of the widget. For non-compound widgets (ie. everything in
+ the above class hierarchy), this will be used as the "name"
+ attribute for the main HTML tag that defines the form element.
+
+``value``
+ the current value of the form element. The type of 'value' depends
+ on the widget class. Most widget classes support only a single
+ type, eg. StringWidget always deals with strings and IntWidget with
+ integers. The SelectWidget classes are different; see the
+ descriptions below for details.
+
+
+Common widget methods
+---------------------
+
+The Widget base class also provides a couple of useful
+methods:
+
+``set_name(name:string)``
+ use this to change the widget name supplied to the constructor.
+ Unless you know what you're doing, you should do this before
+ rendering or parsing the widget.
+
+``set_value(value:any)``
+ use this to set the widget value; this is the same as supplying
+ a value to the constructor (and the same type rules apply, ie.
+ the type of 'value' depends on the widget class).
+
+``clear()``
+ clear the widget's current value. Equivalent to
+ ``widget.set_value(None)``.
+
+The following two methods will be used on every widget object you
+create; if you write your own widget classes, you will almost certainly
+have to define both of these:
+
+``render(request:HTTPRequest)`` : ``string``
+ return a chunk of HTML that implements the form element
+ corresponding to this widget.
+
+``parse(request:HTTPRequest)`` : ``any``
+ extract the form value for this widget from ``request.form``, parse it
+ according to the rules for this widget class, and return the
+ resulting value. The return value depends on the widget class, and
+ will be of the same type as the value passed to the constructor
+ and/or ``set_value()``.
+
+
+StringWidget
+------------
+
+Used for short, single-line string input with no validation (ie. any
+string will be accepted.) Generates an ``<input type="text">`` form
+element.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ StringWidget(name : string,
+ value : string = None,
+ size : int = None,
+ maxlength : int = None)
+
+``size``
+ used as the ``size`` attribute of the generated ``<input>`` tag;
+ controls the physical size of the input field.
+
+``maxlength``
+ used as the ``maxlength`` attribute; controls the maximum amount
+ of input.
+
+Examples
+~~~~~~~~
+::
+
+ >>> StringWidget("foo", value="hello").render(request)
+ '<input name="foo" type="text" value="hello">'
+
+ >>> StringWidget("foo", size=10, maxlength=20).render(request)
+ '<input name="foo" type="text" size="10" maxlength="20">'
+
+
+PasswordWidget
+--------------
+
+PasswordWidget is identical to StringWidget except for the type of the
+HTML form element: ``password`` instead of ``text``.
+
+
+TextWidget
+----------
+
+Used for multi-line text input. The value is a single string with
+newlines right where the browser supplied them. (``\r\n``, if present,
+is converted to ``\n``.) Generates a ``<textarea>`` form element.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ TextWidget(name : string,
+ value : string = None,
+ cols : int = None,
+ rows : int = None,
+ wrap : string = "physical")
+
+``cols``, ``rows``
+ number of columns/rows in the textarea
+``wrap``
+ controls how the browser wraps text and includes newlines in the
+ submitted form value; consult an HTML book for details.
+
+
+CheckboxWidget
+--------------
+
+Used for single boolean (on/off) value. The value you supply can be
+anything, since Python has a boolean interpretation for all values; the
+value returned by ``parse()`` will always be 0 or 1 (but you shouldn't
+rely on that!). Generates an ``<input type="checkbox">`` form element.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ CheckboxWidget(name : string,
+ value : boolean = false)
+
+Examples
+~~~~~~~~
+::
+
+ >>> CheckboxWidget("foo", value=0).render(request)
+ '<input name="foo" type="checkbox" value="yes">'
+
+ >>> CheckboxWidget("foo", value="you bet").render(request)
+ '<input name="foo" type="checkbox" value="yes" checked>'
+
+
+RadiobuttonsWidget
+------------------
+
+Used for a *set* of related radiobuttons, ie. several ``<input
+type="radio">`` tags with the same name and different values. The set
+of values are supplied to the constructor as ``allowed_values``, which
+may be a list of any Python objects (not just strings). The current
+value must be either ``None`` (the default) or one of the values in
+``allowed_values``; if you supply a ``value`` not in ``allowed_values``,
+it will be ignored. ``parse()`` will return either ``None`` or one of
+the values in ``allowed_values``.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ RadiobuttonsWidget(name : string,
+ value : any = None,
+ allowed_values : [any] = None,
+ descriptions : [string] = map(str, allowed_values),
+ quote : boolean = true,
+ delim : string = "\n")
+
+``allowed_values``
+ specifies how many ``<input type="radio">`` tags to generate and the
+ values for each. Eg. ``allowed_values=["foo", "bar"]`` will result in
+ (roughly)::
+
+ <input type="radio" value="foo">
+ <input type="radio" value="bar">
+
+``descriptions``
+ the text that will actually be shown to the user in the web page
+ that includes this widget. Handy when the elements of
+ ``allowed_values`` are too terse, or don't have a meaningful
+ ``str()``, or you want to add some additional cues for the user. If
+ not supplied, ``map(str, allowed_values)`` is used, with the
+ exception that ``None`` in ``allowed_values`` becomes ``""`` (the
+ empty string) in ``descriptions``. If supplied, ``descriptions``
+ must be the same length as ``allowed_values``.
+
+``quote``
+ if true (the default), the elements of 'descriptions' will be
+ HTML-quoted (using ``quixote.html.html_quote()``) when the widget is
+ rendered. This is essential if you might have characters like
+ ``&`` or ``<`` in your descriptions. However, you'll want to set
+ ``quote`` to false if you are deliberately including HTML markup
+ in your descriptions.
+
+``delim``
+ the delimiter to separate the radiobuttons with when rendering
+ the whole widget. The default ensures that your HTML is readable
+ (by putting each ``<input>`` tag on a separate line), and that there
+ is horizontal whitespace between each radiobutton.
+
+Examples
+~~~~~~~~
+::
+
+ >>> colours = ["red", "green", "blue", "pink"]
+ >>> widget = RadiobuttonsWidget("foo", allowed_values=colours)
+ >>> print widget.render(request)
+ <input name="foo" type="radio" value="0">red</input>
+ <input name="foo" type="radio" value="1">green</input>
+ <input name="foo" type="radio" value="2">blue</input>
+ <input name="foo" type="radio" value="3">pink</input>
+
+(Note that the actual form values, ie. what the browser returns to the
+server, are always stringified indices into the 'allowed_values' list.
+This is irrelevant to you, since SingleSelectWidget takes care of
+converting ``"1"`` to ``1`` and looking up ``allowed_values[1]``.)
+
+::
+
+ >>> values = [val1, val2, val3]
+ >>> descs = ["thing <b>1</b>",
+ "thing <b>2</b>",
+ "thing <b>3</b>"]
+ >>> widget = RadiobuttonsWidget("bar",
+ allowed_values=values,
+ descriptions=descs,
+ value=val3,
+ delim="<br>\n",
+ quote=0)
+ >>> print widget.render(request)
+ <input name="bar" type="radio" value="0">thing <b>1</b></input><br>
+ <input name="bar" type="radio" value="1">thing <b>2</b></input><br>
+ <input name="bar" type="radio" value="2" checked="checked">thing <b>3</b></input>
+
+
+SingleSelectWidget
+------------------
+
+Used to select a single value from a list that's too long or ungainly
+for a set of radiobuttons. (Most browsers implement this as a scrolling
+list; UNIX versions of Netscape 4.x and earlier used a pop-up menu.)
+The value can be any Python object; ``parse()`` will return either
+``None`` or one of the values you supply to the constructor as
+``allowed_values``. Generates a ``<select>...</select>`` tag, with one
+``<option>`` tag for each element of ``allowed_values``.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ SingleSelectWidget(name : string,
+ value : any = None,
+ allowed_values : [any] = None,
+ descriptions : [string] = map(str, allowed_values),
+ quote : boolean = true,
+ size : int = None)
+
+``allowed_values``
+ determines the set of ``<option>`` tags that will go inside the
+ ``<select>`` tag; these can be any Python values (not just strings).
+ ``parse()`` will return either one of the ``allowed_values`` or ``None``.
+ If you supply a ``value`` that is not in ``allowed_values``, it
+ will be ignored.
+
+``descriptions``
+ (same as RadiobuttonsWidget above)
+
+``quote``
+ (same as RadiobuttonsWidget above)
+
+``size``
+ corresponds to the ``size`` attribute of the ``<select>`` tag: ask
+ the browser to show a select list with ``size`` items visible.
+ Not always respected by the browser; consult an HTML book.
+
+Examples
+~~~~~~~~
+::
+
+ >>> widget = SingleSelectWidget("foo",
+ allowed_values=["abc", 123, 5.5])
+ >>> print widget.render(request)
+ <select name="foo">
+ <option value="0">abc
+ <option value="1">123
+ <option value="2">5.5
+ </select>
+
+ >>> widget = SingleSelectWidget("bar",
+ value=val2,
+ allowed_values=[val1, val2, val3],
+ descriptions=["foo", "bar", "foo & bar"],
+ size=3)
+ >>> print widget.render(request)
+ <select name="bar" size="3">
+ <option value="0">foo
+ <option selected value="1">bar
+ <option value="2">foo &amp; bar
+ </select>
+
+
+MultipleSelectWidget
+--------------------
+
+Used to select multiple values from a list. Everything is just like
+SingleSelectWidget, except that ``value`` can be a list of objects
+selected from ``allowed_values`` (in which case every object in ``value``
+will initially be selected). Generates a ``<select multiple>...</select>``
+tag, with one ``<option>`` tag for each element of ``allowed_values``.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ MultipleSelectWidget(name : string,
+ value : any | [any] = None,
+ allowed_values : [any] = None,
+ descriptions : [string] = map(str, allowed_values),
+ quote : boolean = true,
+ size : int = None)
+
+
+SubmitButtonWidget
+------------------
+
+Used for generating submit buttons. Note that HTML submit buttons are
+rather weird, and Quixote preserves this weirdness -- the Widget classes
+are meant to be a fairly thin wrapper around HTML form elements, after
+all.
+
+In particular, the widget value for a submit button controls two things:
+what the user sees in their browser (the text in the button) and what
+the browser returns as the value for that form element. You can't
+control the two separately, as you can with radiobuttons or selection
+widgets.
+
+Also, SubmitButtonWidget is the only widget with an optional ``name``.
+In many simple forms, all you care about is the fact that the form was
+submitted -- which submit button the user used doesn't matter.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ SubmitButtonWidget(name : string = None,
+ value : string = None)
+
+``value``
+ the text that will be shown in the user's browser, *and* the
+ value that will be returned for this form element (widget)
+ if the user selects this submit button.
+
+Examples
+~~~~~~~~
+
+ >>> SubmitButtonWidget(value="Submit Form").render(request)
+ '<input type="submit" value="Submit Form">'
+
+
+HiddenWidget
+------------
+
+Used to generate HTML hidden widgets, which can be useful for carrying
+around non-sensitive application state. (The Quixote form framework
+uses hidden widgets for form tokens as a measure against cross-site
+request forgery [CSRF] attacks. So by "sensitive" I mean "information
+which should not be revealed", rather than "security-related". If you
+wouldn't put it in a cookie or in email, don't put it in a hidden form
+element.)
+
+Constructor
+~~~~~~~~~~~
+::
+
+ HiddenWidget(name : string,
+ value : string)
+
+Examples
+~~~~~~~~
+::
+
+ >>> HiddenWidget("form_id", "2452345135").render(request)
+ '<input type="hidden" name="form_id" value="2452345135">'
+
+
+IntWidget
+---------
+
+The first derived widget class: this is a subclass of StringWidget
+specifically for entering integer values. As such, this is the first
+widget class we've covered that can reject certain user input. (The
+selection widgets all have to validate their input in case of broken or
+malicious clients, but they just drop bogus values.) If the user enters
+a string that Python's built-in ``int()`` can't convert to an integer,
+IntWidget's ``parse()`` method raises FormValueError (also defined in
+the quixote.form.widget module). This exception is handled by Quixote's
+form framework, but if you're using widget objects on their own, you'll
+have to handle it yourself.
+
+``IntWidget.parse()`` always returns an integer or ``None``.
+
+Constructor
+~~~~~~~~~~~
+::
+
+ IntWidget(name : string,
+ value : int = None,
+ size : int = None,
+ maxlength : int = None)
+
+Constructor arguments are as for StringWidget, except that ``value``
+must be an integer (or ``None``). Note that ``size`` and
+``maxlength`` have exactly the same meaning: they control the size of
+the input widget and the maximum number of characters of input.
+
+[Examples]
+
+ >>> IntWidget("num", value=37, size=5).render(request)
+ '<input type="string" name="num" value="37" size="5">'
+
+
+FloatWidget
+-----------
+
+FloatWidget is identical to IntWidget, except:
+
+* ``value`` must be a float
+* ``parse()`` returns a float or ``None``
+* ``parse()`` raises FormValueError if the string entered by the
+ user cannot be converted by Python's built-in ``float()`` function
+
+
+OptionSelectWidget
+------------------
+
+OptionSelectWidget is simply a SingleSelectWidget that uses a bit of
+Javascript to automatically submit the current form as soon as the user
+selects a value. This is useful for very simple one-element forms where
+you don't want to bother with a submit button, or for very complex forms
+where you need to revamp the user interface based on a user's selection.
+Your form-processing code could then detect that style of form
+submission, and regenerate a slightly different form for the user. (Or
+you could treat it as a full-blown form submission, if the only widget
+of interest is the OptionSelectWidget.)
+
+For example, if you're asking a user for their address, some of the
+details will vary depending on which country they're in. You might make
+the country widget an OptionSelectWidget: if the user selects "Canada",
+you'll ask them for a province and a postal code; if they select "United
+States", you ask for a state and a zip code; and so forth. (I don't
+really recommend a user interface that works this way: you'll spend way
+too much time getting the details right ["How many states does Australia
+have again?"], and you're bound to get something wrong -- there are over
+200 countries in the world, after all.)
+
+Be warned that since OptionSelectWidget relies on Javascript to work,
+using it makes immediately makes your application less portable and more
+fragile. One thing to avoid: form elements with a name of ``submit``,
+since that masks the Javascript function called by OptionSelectWidget.
+
+
+$Id: widgets.txt 20217 2003-01-16 20:51:53Z akuchlin $
diff --git a/pypers/europython05/Quixote-2.0/doc/win32.txt b/pypers/europython05/Quixote-2.0/doc/win32.txt
new file mode 100755
index 0000000..9204f10
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/doc/win32.txt
@@ -0,0 +1,14 @@
+Compiling Quixote c extensions for Windows using the mingw compiler:
+
+The variable build_extensions in setup.py must be set to True (instead
+of "sys.platform != win32"). Then, Quixote can be installed through
+
+ python setup.py build_ext --compiler=mingw32 install
+
+The mingw compiler can be downloaded from
+http://www.bloodshed.net/dev/devcpp.html. Probably you need to list
+the bin directory (C:\Program files\Dev-Cpp\bin) in the PATH.
+
+To permit gcc to link against Python library, you also need a
+libpython2x.a file. Instructions for its creation are provided at
+http://sebsauvage.net/python/mingw.html.
diff --git a/pypers/europython05/Quixote-2.0/errors.py b/pypers/europython05/Quixote-2.0/errors.py
new file mode 100755
index 0000000..f33dc41
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/errors.py
@@ -0,0 +1,141 @@
+"""quixote.errors
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/errors.py $
+$Id: errors.py 26378 2005-03-17 14:04:45Z dbinger $
+
+Exception classes used by Quixote
+"""
+from quixote.html import htmltext, htmlescape
+
+__revision__ = "$Id: errors.py 26378 2005-03-17 14:04:45Z dbinger $"
+
+
+class PublishError(Exception):
+ """PublishError exceptions are raised due to some problem with the
+ data provided by the client and are raised during the publishing
+ process. Quixote will abort the current request and return an error
+ page to the client.
+
+ public_msg should be a user-readable message that reveals no
+ inner workings of your application; it will always be shown.
+
+ private_msg will only be shown if the config option DISPLAY_EXCEPTIONS is
+ true; Quixote uses this to give you more detail about why the error
+ occurred. You might want to use it for similar, application-specific
+ information. (DISPLAY_EXCEPTIONS should always be false in a production
+ environment, since these details about the inner workings of your
+ application could conceivably be useful to attackers.)
+
+ The formatting done by the Quixote versions of these exceptions is
+ very simple. Applications will probably wish to raise application
+ specific subclasses which do more sophisticated formatting or provide
+ a _q_except handler to format the exception.
+
+ """
+
+ status_code = 400 # bad request
+ title = "Publishing error"
+ description = "no description"
+
+ def __init__(self, public_msg=None, private_msg=None):
+ self.public_msg = public_msg
+ self.private_msg = private_msg # cleared if DISPLAY_EXCEPTIONS is false
+
+ def __str__(self):
+ return self.private_msg or self.public_msg or "???"
+
+ def format(self):
+ msg = htmlescape(self.title)
+ if self.public_msg:
+ msg = msg + ": " + self.public_msg
+ if self.private_msg:
+ msg = msg + ": " + self.private_msg
+ return msg
+
+
+class TraversalError(PublishError):
+ """
+ Raised when a client attempts to access a resource that does not
+ exist or is otherwise unavailable to them (eg. a Python function
+ not listed in its module's _q_exports list).
+
+ path should be the path to the requested resource; if not
+ supplied, the current request object will be fetched and its
+ get_path() method called.
+ """
+
+ status_code = 404 # not found
+ title = "Page not found"
+ description = ("The requested link does not exist on this site. If "
+ "you arrived here by following a link from an external "
+ "page, please inform that page's maintainer.")
+
+ def __init__(self, public_msg=None, private_msg=None, path=None):
+ PublishError.__init__(self, public_msg, private_msg)
+ if path is None:
+ import quixote
+ path = quixote.get_request().get_path()
+ self.path = path
+
+ def format(self):
+ msg = htmlescape(self.title) + ": " + self.path
+ if self.public_msg:
+ msg = msg + ": " + self.public_msg
+ if self.private_msg:
+ msg = msg + ": " + self.private_msg
+ return msg
+
+class RequestError(PublishError):
+ """
+ Raised when Quixote is unable to parse an HTTP request (or its CGI
+ representation). This is a lower-level error than QueryError -- it
+ either means that Quixote is not smart enough to handle the request
+ being passed to it, or the user-agent is broken and/or malicious.
+ """
+ status_code = 400
+ title = "Invalid request"
+ description = "Unable to parse HTTP request."
+
+
+class QueryError(PublishError):
+ """Should be raised if bad data was provided in the query part of a
+ URL or in the content of a POST request. What constitutes bad data is
+ solely application dependent (eg: letters in a form field when the
+ application expects a number).
+ """
+
+ status_code = 400
+ title = "Invalid query"
+ description = ("An error occurred while handling your request. The "
+ "query data provided as part of the request is invalid.")
+
+
+
+class AccessError(PublishError):
+ """Should be raised if the client does not have access to the
+ requested resource. Usually applications will raise this error from
+ an _q_access method.
+ """
+
+ status_code = 403
+ title = "Access denied"
+ description = ("An error occurred while handling your request. "
+ "Access to the requested resource was not permitted.")
+
+
+
+def format_publish_error(exc):
+ """(exc : PublishError) -> string
+
+ Format a PublishError exception as a web page.
+ """
+ return htmltext("""\
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
+ "http://www.w3.org/TR/REC-html40/strict.dtd">
+ <html>
+ <head><title>Error: %s</title></head>
+ <body>
+ <p>%s</p>
+ <p>%s</p>
+ </body>
+ </html>
+ """) % (exc.title, exc.description, exc.format())
diff --git a/pypers/europython05/Quixote-2.0/form/__init__.py b/pypers/europython05/Quixote-2.0/form/__init__.py
new file mode 100755
index 0000000..5685c8a
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form/__init__.py
@@ -0,0 +1,18 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form/__init__.py $
+$Id: __init__.py 26469 2005-04-05 10:26:03Z dbinger $
+
+The web interface framework, consisting of Form and Widget base classes
+(and a bunch of standard widget classes recognized by Form).
+Application developers will typically create a Form instance each
+form in their application; each form object will contain a number
+of widget objects. Custom widgets can be created by inheriting
+and/or composing the standard widget classes.
+"""
+
+from quixote.form.form import Form, FormTokenWidget
+from quixote.form.widget import Widget, StringWidget, FileWidget, \
+ PasswordWidget, TextWidget, CheckboxWidget, RadiobuttonsWidget, \
+ SingleSelectWidget, SelectWidget, OptionSelectWidget, \
+ MultipleSelectWidget, SubmitWidget, HiddenWidget, \
+ FloatWidget, IntWidget, subname, WidgetValueError, CompositeWidget, \
+ WidgetList, WidgetDict
diff --git a/pypers/europython05/Quixote-2.0/form/compatibility.py b/pypers/europython05/Quixote-2.0/form/compatibility.py
new file mode 100755
index 0000000..4ad0389
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form/compatibility.py
@@ -0,0 +1,101 @@
+'''$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form/compatibility.py $
+$Id: compatibility.py 26061 2005-02-11 02:48:16Z dbinger $
+
+A Form subclass that provides close to the same API as the old form
+class (useful for transitioning existing forms).
+'''
+
+from quixote import get_request, get_path, redirect
+from quixote.form import Form as _Form, Widget, StringWidget, FileWidget, \
+ PasswordWidget, TextWidget, CheckboxWidget, RadiobuttonsWidget, \
+ SingleSelectWidget, SelectWidget, OptionSelectWidget, \
+ MultipleSelectWidget, SubmitWidget, HiddenWidget, \
+ FloatWidget, IntWidget
+from quixote.html import url_quote
+
+_widget_names = {
+ "string" : StringWidget,
+ "file" : FileWidget,
+ "password" : PasswordWidget,
+ "text" : TextWidget,
+ "checkbox" : CheckboxWidget,
+ "single_select" : SingleSelectWidget,
+ "radiobuttons" : RadiobuttonsWidget,
+ "multiple_select" : MultipleSelectWidget,
+ "submit_button" : SubmitWidget,
+ "hidden" : HiddenWidget,
+ "float" : FloatWidget,
+ "int" : IntWidget,
+ "option_select" : OptionSelectWidget,
+}
+
+
+class Form(_Form):
+ def __init__(self, action_url=None, *args, **kwargs):
+ _Form.__init__(self, action=action_url, *args, **kwargs)
+ self.cancel_url = None
+ self.action_url = self.action
+
+ def add_widget(self, widget_class, name, value=None,
+ title=None, hint=None, required=False, **kwargs):
+ try:
+ widget_class = _widget_names[widget_class]
+ except KeyError:
+ pass
+ self.add(widget_class, name, value=value, title=title, hint=hint,
+ required=required, **kwargs)
+
+ def add_submit_button(self, name, value):
+ self.add_submit(name, value)
+
+ def add_cancel_button(self, caption, url):
+ self.add_submit("cancel", caption)
+ self.cancel_url = url
+
+ def get_action_url(self):
+ action_url = url_quote(get_path())
+ query = get_request().get_query()
+ if query:
+ action_url += "?" + query
+ return action_url
+
+ def render(self, action_url=None):
+ if action_url:
+ self.action_url = action_url
+ return _Form.render(self)
+
+ def process(self):
+ values = {}
+ request = get_request()
+ for name, widget in self._names.items():
+ values[name] = widget.parse()
+ return values
+
+ def action(self, submit, values):
+ raise NotImplementedError, "sub-classes must implement 'action()'"
+
+ def handle(self):
+ """handle() -> string
+
+ Master method for handling forms. It should be called after
+ initializing a form. Controls form action based on a request. You
+ probably should override 'process' and 'action' instead of
+ overriding this method.
+ """
+ request = get_request()
+ if not self.is_submitted():
+ return self.render(self.action_url)
+ submit = self.get_submit()
+ if submit == "cancel":
+ return redirect(self.cancel_url)
+ values = self.process()
+ if submit == True:
+ # The form was submitted by an unregistered submit button, assume
+ # that the submission was required to update the layout of the form.
+ self.clear_errors()
+ return self.render(self.action_url)
+
+ if self.has_errors():
+ return self.render(self.action_url)
+ else:
+ return self.action(submit, values)
diff --git a/pypers/europython05/Quixote-2.0/form/css.py b/pypers/europython05/Quixote-2.0/form/css.py
new file mode 100755
index 0000000..c2849f8
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form/css.py
@@ -0,0 +1,76 @@
+
+BASIC_FORM_CSS = """\
+form.quixote div.title {
+ font-weight: bold;
+}
+
+form.quixote br.submit,
+form.quixote br.widget,
+br.quixoteform {
+ clear: left;
+}
+
+form.quixote div.submit br.widget {
+ display: none;
+}
+
+form.quixote div.widget {
+ float: left;
+ padding: 4px;
+ padding-right: 1em;
+ margin-bottom: 6px;
+}
+
+/* pretty forms (attribute selector hides from broken browsers (e.g. IE) */
+form.quixote[action] {
+ float: left;
+}
+
+form.quixote[action] > div.widget {
+ float: none;
+}
+
+form.quixote[action] > br.widget {
+ display: none;
+}
+
+form.quixote div.widget div.widget {
+ padding: 0;
+ margin-bottom: 0;
+}
+
+form.quixote div.SubmitWidget {
+ float: left
+}
+
+form.quixote div.content {
+ margin-left: 0.6em; /* indent content */
+}
+
+form.quixote div.content div.content {
+ margin-left: 0; /* indent content only for top-level widgets */
+}
+
+form.quixote div.error {
+ color: #c00;
+ font-size: small;
+ margin-top: .1em;
+}
+
+form.quixote div.hint {
+ font-size: small;
+ font-style: italic;
+ margin-top: .1em;
+}
+
+form.quixote div.errornotice {
+ color: #c00;
+ padding: 0.5em;
+ margin: 0.5em;
+}
+
+form.quixote div.FormTokenWidget,
+form.quixote.div.HiddenWidget {
+ display: none;
+}
+"""
diff --git a/pypers/europython05/Quixote-2.0/form/form.py b/pypers/europython05/Quixote-2.0/form/form.py
new file mode 100755
index 0000000..fd91fd4
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form/form.py
@@ -0,0 +1,365 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form/form.py $
+$Id: form.py 26200 2005-02-18 22:58:30Z dbinger $
+
+Provides the Form class and related classes. Forms are a convenient
+way of building HTML forms that are composed of Widget objects.
+"""
+
+from quixote import get_request, get_session, get_publisher
+from quixote.html import htmltag, htmltext, TemplateIO
+from quixote.form.widget import HiddenWidget, StringWidget, TextWidget, \
+ CheckboxWidget, SingleSelectWidget, RadiobuttonsWidget, \
+ MultipleSelectWidget, ResetWidget, SubmitWidget, FloatWidget, \
+ IntWidget, PasswordWidget
+
+
+class FormTokenWidget(HiddenWidget):
+
+ def _parse(self, request):
+ token = request.form.get(self.name)
+ session = get_session()
+ if not session.has_form_token(token):
+ self.error = 'invalid' # this error does not get displayed
+ else:
+ session.remove_form_token(token)
+
+ def render_error(self, error):
+ return ''
+
+ def render(self):
+ self.value = get_session().create_form_token()
+ return HiddenWidget.render(self)
+
+
+class Form(object):
+ """
+ Provides a high-level mechanism for collecting and processing user
+ input that is based on HTML forms.
+
+ Instance attributes:
+ widgets : [Widget]
+ widgets that are not subclasses of SubmitWidget or HiddenWidget
+ submit_widgets : [SubmitWidget]
+ subclasses of SubmitWidget, normally rendered at the end of the
+ form
+ hidden_widgets : [HiddenWidget]
+ subclasses of HiddenWidget, normally rendered at the beginning
+ of the form
+ _names : { name:string : Widget }
+ names used in the form and the widgets associated with them
+ """
+
+ TOKEN_NAME = "_form_id" # name of hidden token widget
+
+ JAVASCRIPT_MARKUP = htmltext('<script type="text/javascript">\n'
+ '<!--\n'
+ '%s\n'
+ '// -->\n'
+ '</script>\n')
+
+ TOKEN_NOTICE = htmltext('<div class="errornotice">'
+ 'The form you have submitted is invalid. Most '
+ 'likely it has been successfully submitted once '
+ 'already. Please review the the form data '
+ 'and submit the form again.'
+ '</div>')
+
+ ERROR_NOTICE = htmltext('<div class="errornotice">'
+ 'There were errors processing your form. '
+ 'See below for details.'
+ '</div>')
+
+ def __init__(self,
+ method="post",
+ action=None,
+ enctype=None,
+ use_tokens=True,
+ **attrs):
+
+ if method not in ("post", "get"):
+ raise ValueError("Form method must be 'post' or 'get', "
+ "not %r" % method)
+ self.method = method
+ self.action = action or self._get_default_action()
+ if 'class' not in attrs:
+ attrs['class'] = 'quixote'
+ self.attrs = attrs
+ self.widgets = []
+ self.submit_widgets = []
+ self.hidden_widgets = []
+ self._names = {}
+
+ if enctype is not None and enctype not in (
+ "application/x-www-form-urlencoded", "multipart/form-data"):
+ raise ValueError, ("Form enctype must be "
+ "'application/x-www-form-urlencoded' or "
+ "'multipart/form-data', not %r" % enctype)
+ self.enctype = enctype
+
+ if use_tokens and self.method == "post":
+ config = get_publisher().config
+ if config.form_tokens:
+ # unique token for each form, this prevents many cross-site
+ # attacks and prevents a form from being submitted twice
+ self.add(FormTokenWidget, self.TOKEN_NAME, value=None)
+
+ def _get_default_action(self):
+ query = get_request().get_query()
+ if query:
+ return "?" + query
+ else:
+ return ""
+
+ # -- Form data access methods --------------------------------------
+
+ def __getitem__(self, name):
+ """(name:string) -> any
+ Return a widget's value. Raises KeyError if widget named 'name'
+ does not exist.
+ """
+ try:
+ return self._names[name].parse()
+ except KeyError:
+ raise KeyError, 'no widget named %r' % name
+
+ def has_key(self, name):
+ """Return true if the widget named 'name' is in the form."""
+ return self._names.has_key(name)
+
+ def get(self, name, default=None):
+ """(name:string, default=None) -> any
+ Return a widget's value. Returns 'default' if widget named 'name'
+ does not exist.
+ """
+ widget = self._names.get(name)
+ if widget is not None:
+ return widget.parse()
+ else:
+ return default
+
+ def get_widget(self, name):
+ """(name:string) -> Widget | None
+ Return the widget named 'name'. Returns None if the widget does
+ not exist.
+ """
+ return self._names.get(name)
+
+ def get_submit_widgets(self):
+ """() -> [SubmitWidget]
+ """
+ return self.submit_widgets
+
+ def get_all_widgets(self):
+ """() -> [Widget]
+ Return all the widgets that have been added to the form. Note that
+ this while this list includes submit widgets and hidden widgets, it
+ does not include sub-widgets (e.g. widgets that are part of
+ CompositeWidgets)
+ """
+ return self._names.values()
+
+ # -- Form processing and error checking ----------------------------
+
+ def is_submitted(self):
+ """() -> bool
+
+ Return true if a form was submitted. If the form method is 'POST'
+ and the page was not requested using 'POST', then the form is not
+ considered to be submitted. If the form method is 'GET' then the
+ form is considered submitted if there is any form data in the
+ request.
+ """
+ request = get_request()
+ if self.method == 'post':
+ if request.get_method() == 'POST':
+ return True
+ else:
+ return False
+ else:
+ return bool(request.form)
+
+ def has_errors(self):
+ """() -> bool
+
+ Ensure that all components of the form have parsed themselves. Return
+ true if any of them have errors.
+ """
+ request = get_request()
+ has_errors = False
+ if self.is_submitted():
+ for widget in self.get_all_widgets():
+ if widget.has_error(request=request):
+ has_errors = True
+ return has_errors
+
+ def clear_errors(self):
+ """Ensure that all components of the form have parsed themselves.
+ Clear any errors that might have occured during parsing.
+ """
+ request = get_request()
+ for widget in self.get_all_widgets():
+ widget.clear_error(request)
+
+ def get_submit(self):
+ """() -> string | bool
+
+ Get the name of the submit button that was used to submit the
+ current form. If the form is submitted but not by any known
+ SubmitWidget then return True. Otherwise, return False.
+ """
+ request = get_request()
+ for button in self.submit_widgets:
+ if button.parse(request):
+ return button.name
+ else:
+ if self.is_submitted():
+ return True
+ else:
+ return False
+
+ def set_error(self, name, error):
+ """(name : string, error : string)
+ Set the error attribute of the widget named 'name'.
+ """
+ widget = self._names.get(name)
+ if not widget:
+ raise KeyError, "unknown name %r" % name
+ widget.set_error(error)
+
+ # -- Form population methods ---------------------------------------
+
+ def add(self, widget_class, name, *args, **kwargs):
+ if self._names.has_key(name):
+ raise ValueError, "form already has '%s' widget" % name
+ widget = widget_class(name, *args, **kwargs)
+ self._names[name] = widget
+ if isinstance(widget, SubmitWidget):
+ self.submit_widgets.append(widget) # will be rendered at end
+ elif isinstance(widget, HiddenWidget):
+ self.hidden_widgets.append(widget) # will be render at beginning
+ else:
+ self.widgets.append(widget)
+
+ # convenience methods
+
+ def add_submit(self, name, value=None, **kwargs):
+ self.add(SubmitWidget, name, value, **kwargs)
+
+ def add_reset(self, name, value=None, **kwargs):
+ self.add(ResetWidget, name, value, **kwargs)
+
+ def add_hidden(self, name, value=None, **kwargs):
+ self.add(HiddenWidget, name, value, **kwargs)
+
+ def add_string(self, name, value=None, **kwargs):
+ self.add(StringWidget, name, value, **kwargs)
+
+ def add_text(self, name, value=None, **kwargs):
+ self.add(TextWidget, name, value, **kwargs)
+
+ def add_password(self, name, value=None, **kwargs):
+ self.add(PasswordWidget, name, value, **kwargs)
+
+ def add_checkbox(self, name, value=None, **kwargs):
+ self.add(CheckboxWidget, name, value, **kwargs)
+
+ def add_single_select(self, name, value=None, **kwargs):
+ self.add(SingleSelectWidget, name, value, **kwargs)
+
+ def add_multiple_select(self, name, value=None, **kwargs):
+ self.add(MultipleSelectWidget, name, value, **kwargs)
+
+ def add_radiobuttons(self, name, value=None, **kwargs):
+ self.add(RadiobuttonsWidget, name, value, **kwargs)
+
+ def add_float(self, name, value=None, **kwargs):
+ self.add(FloatWidget, name, value, **kwargs)
+
+ def add_int(self, name, value=None, **kwargs):
+ self.add(IntWidget, name, value, **kwargs)
+
+
+ # -- Layout (rendering) methods ------------------------------------
+
+ def render(self):
+ """() -> HTML text
+ Render a form as HTML.
+ """
+ r = TemplateIO(html=True)
+ r += self._render_start()
+ r += self._render_body()
+ r += self._render_finish()
+ return r.getvalue()
+
+ def _render_start(self):
+ r = TemplateIO(html=True)
+ r += htmltag('form', method=self.method,
+ enctype=self.enctype, action=self.action,
+ **self.attrs)
+ r += self._render_hidden_widgets()
+ return r.getvalue()
+
+ def _render_finish(self):
+ r = TemplateIO(html=True)
+ r += htmltext('</form><br class="quixoteform" />')
+ code = get_request().response.javascript_code
+ if code:
+ r += self._render_javascript(code)
+ return r.getvalue()
+
+ def _render_widgets(self):
+ r = TemplateIO(html=True)
+ for widget in self.widgets:
+ r += widget.render()
+ return r.getvalue()
+
+ def _render_hidden_widgets(self):
+ r = TemplateIO(html=True)
+ for widget in self.hidden_widgets:
+ r += widget.render()
+ return r.getvalue()
+
+ def _render_submit_widgets(self):
+ r = TemplateIO(html=True)
+ if self.submit_widgets:
+ r += htmltext('<div class="submit">')
+ for widget in self.submit_widgets:
+ r += widget.render()
+ r += htmltext('</div><br class="submit" />')
+ return r.getvalue()
+
+ def _render_error_notice(self):
+ token_widget = self.get_widget(self.TOKEN_NAME)
+ if token_widget is not None and token_widget.has_error():
+ # form tokens are enabled but the token data in the request
+ # does not match anything in the session. It could be an
+ # a cross-site attack but most likely the back button has
+ # be used
+ return self.TOKEN_NOTICE
+ else:
+ return self.ERROR_NOTICE
+
+ def _render_javascript(self, javascript_code):
+ """Render javacript code for the form. Insert code lexically
+ sorted by code_id.
+ """
+ form_code = []
+ code_ids = javascript_code.keys()
+ code_ids.sort()
+ for code_id in code_ids:
+ code = javascript_code[code_id]
+ if code:
+ form_code.append(code)
+ javascript_code[code_id] = ''
+ if form_code:
+ return self.JAVASCRIPT_MARKUP % htmltext(''.join(form_code))
+ else:
+ return ''
+
+ def _render_body(self):
+ r = TemplateIO(html=True)
+ if self.has_errors():
+ r += self._render_error_notice()
+ r += self._render_widgets()
+ r += self._render_submit_widgets()
+ return r.getvalue()
diff --git a/pypers/europython05/Quixote-2.0/form/widget.py b/pypers/europython05/Quixote-2.0/form/widget.py
new file mode 100755
index 0000000..b989766
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form/widget.py
@@ -0,0 +1,951 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form/widget.py $
+$Id: widget.py 26514 2005-04-07 13:29:50Z dbinger $
+
+Provides the basic web widget classes: Widget itself, plus StringWidget,
+TextWidget, CheckboxWidget, etc.
+"""
+
+import struct
+from quixote import get_request
+from quixote.html import htmltext, htmlescape, htmltag, TemplateIO, stringify
+from quixote.http_request import Upload
+
+def subname(prefix, name):
+ """Create a unique name for a sub-widget or sub-component."""
+ # $ is nice because it's valid as part of a Javascript identifier
+ return "%s$%s" % (prefix, name)
+
+
+def merge_attrs(base, overrides):
+ """({string: any}, {string: any}) -> {string: any}
+ """
+ items = []
+ if base:
+ items.extend(base.items())
+ if overrides:
+ items.extend(overrides.items())
+ attrs = {}
+ for name, val in items:
+ if name.endswith('_'):
+ name = name[:-1]
+ attrs[name] = val
+ return attrs
+
+
+class WidgetValueError(Exception):
+ """May be raised a widget has problems parsing its value."""
+
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return stringify(self.msg)
+
+
+
+class Widget(object):
+ """Abstract base class for web widgets.
+
+ Instance attributes:
+ name : string
+ value : any
+ error : string
+ title : string
+ hint : string
+ required : bool
+ attrs : {string: any}
+ _parsed : bool
+
+ Feel free to access these directly; to set them, use the 'set_*()'
+ modifier methods.
+ """
+
+ REQUIRED_ERROR = 'required'
+
+ def __init__(self, name, value=None, title="", hint="", required=False,
+ render_br=True, attrs=None, **kwattrs):
+ assert self.__class__ is not Widget, "abstract class"
+ self.name = name
+ self.value = value
+ self.error = None
+ self.title = title
+ self.hint = hint
+ self.required = required
+ self.render_br = render_br
+ self.attrs = merge_attrs(attrs, kwattrs)
+ self._parsed = False
+
+ def __repr__(self):
+ return "<%s at %x: %s>" % (self.__class__.__name__,
+ id(self),
+ self.name)
+
+ def __str__(self):
+ return "%s: %s" % (self.__class__.__name__, self.name)
+
+ def get_name(self):
+ return self.name
+
+ def set_value(self, value):
+ self.value = value
+
+ def set_error(self, error):
+ self.error = error
+
+ def get_error(self, request=None):
+ self.parse(request=request)
+ return self.error
+
+ def has_error(self, request=None):
+ return bool(self.get_error(request=request))
+
+ def clear_error(self, request=None):
+ self.parse(request=request)
+ self.error = None
+
+ def set_title(self, title):
+ self.title = title
+
+ def get_title(self):
+ return self.title
+
+ def set_hint(self, hint):
+ self.hint = hint
+
+ def get_hint(self):
+ return self.hint
+
+ def is_required(self):
+ return self.required
+
+ def parse(self, request=None):
+ if not self._parsed:
+ self._parsed = True
+ if request is None:
+ request = get_request()
+ if request.form or request.get_method() == 'POST':
+ try:
+ self._parse(request)
+ except WidgetValueError, exc:
+ self.set_error(stringify(exc))
+ if (self.required and self.value is None and
+ not self.has_error()):
+ self.set_error(self.REQUIRED_ERROR)
+ return self.value
+
+ def _parse(self, request):
+ # subclasses may override but this is not part of the public API
+ value = request.form.get(self.name)
+ if isinstance(value, basestring) and value.strip():
+ self.value = value
+ else:
+ self.value = None
+
+ def render_title(self, title):
+ if title:
+ if self.required:
+ title += htmltext('<span class="required">*</span>')
+ return htmltext('<div class="title">%s</div>') % title
+ else:
+ return ''
+
+ def render_hint(self, hint):
+ if hint:
+ return htmltext('<div class="hint">%s</div>') % hint
+ else:
+ return ''
+
+ def render_error(self, error):
+ if error:
+ return htmltext('<div class="error">%s</div>') % error
+ else:
+ return ''
+
+ def render(self):
+ r = TemplateIO(html=True)
+ classnames = '%s widget' % self.__class__.__name__
+ r += htmltext('<div class="%s">') % classnames
+ r += self.render_title(self.get_title())
+ r += htmltext('<div class="content">')
+ r += self.render_content()
+ r += self.render_hint(self.get_hint())
+ r += self.render_error(self.get_error())
+ r += htmltext('</div>')
+ r += htmltext('</div>')
+ if self.render_br:
+ r += htmltext('<br class="%s" />') % classnames
+ r += htmltext('\n')
+ return r.getvalue()
+
+ def render_content(self):
+ raise NotImplementedError
+
+# class Widget
+
+# -- Fundamental widget types ------------------------------------------
+# These correspond to the standard types of input tag in HTML:
+# text StringWidget
+# password PasswordWidget
+# radio RadiobuttonsWidget
+# checkbox CheckboxWidget
+#
+# and also to the other basic form elements:
+# <textarea> TextWidget
+# <select> SingleSelectWidget
+# <select multiple>
+# MultipleSelectWidget
+
+class StringWidget(Widget):
+ """Widget for entering a single string: corresponds to
+ '<input type="text">' in HTML.
+
+ Instance attributes:
+ value : string
+ """
+
+ # This lets PasswordWidget be a trivial subclass
+ HTML_TYPE = "text"
+
+ def render_content(self):
+ return htmltag("input", xml_end=True,
+ type=self.HTML_TYPE,
+ name=self.name,
+ value=self.value,
+ **self.attrs)
+
+
+class FileWidget(StringWidget):
+ """Subclass of StringWidget for uploading files.
+
+ Instance attributes: none
+ """
+
+ HTML_TYPE = "file"
+
+ def _parse(self, request):
+ parsed_value = request.form.get(self.name)
+ if isinstance(parsed_value, Upload):
+ self.value = parsed_value
+ else:
+ self.value = None
+
+
+class PasswordWidget(StringWidget):
+ """Trivial subclass of StringWidget for entering passwords (different
+ widget type because HTML does it that way).
+
+ Instance attributes: none
+ """
+
+ HTML_TYPE = "password"
+
+
+class TextWidget(Widget):
+ """Widget for entering a long, multi-line string; corresponds to
+ the HTML "<textarea>" tag.
+
+ Instance attributes:
+ value : string
+ """
+
+ def _parse(self, request):
+ Widget._parse(self, request)
+ if self.value and self.value.find("\r\n") >= 0:
+ self.value = self.value.replace("\r\n", "\n")
+
+ def render_content(self):
+ return (htmltag("textarea", name=self.name, **self.attrs) +
+ htmlescape(self.value or "") +
+ htmltext("</textarea>"))
+
+
+class CheckboxWidget(Widget):
+ """Widget for a single checkbox: corresponds to "<input
+ type=checkbox>". Do not put multiple CheckboxWidgets with the same
+ name in the same form.
+
+ Instance attributes:
+ value : boolean
+ """
+
+ def _parse(self, request):
+ self.value = request.form.has_key(self.name)
+
+ def render_content(self):
+ return htmltag("input", xml_end=True,
+ type="checkbox",
+ name=self.name,
+ value="yes",
+ checked=self.value and "checked" or None,
+ **self.attrs)
+
+
+
+class SelectWidget(Widget):
+ """Widget for single or multiple selection; corresponds to
+ <select name=...>
+ <option value="Foo">Foo</option>
+ ...
+ </select>
+
+ Instance attributes:
+ options : [ (value:any, description:any, key:string) ]
+ value : any
+ The value is None or an element of dict(options.values()).
+ """
+
+ SELECTION_ERROR = "invalid value selected"
+
+ def __init__(self, name, value=None, options=None, sort=False,
+ verify_selection=True, **kwargs):
+ assert self.__class__ is not SelectWidget, "abstract class"
+ Widget.__init__(self, name, value, **kwargs)
+ self.options = []
+ if not options:
+ raise ValueError, "a non-empty list of 'options' is required"
+ else:
+ self.set_options(options, sort)
+ self.verify_selection = verify_selection
+
+ def get_allowed_values(self):
+ return [item[0] for item in self.options]
+
+ def get_descriptions(self):
+ return [item[1] for item in self.options]
+
+ def set_value(self, value):
+ self.value = None
+ for object, description, key in self.options:
+ if value == object:
+ self.value = value
+ break
+
+ def _generate_keys(self, values, descriptions):
+ """Called if no keys were provided. Try to generate a set of keys
+ that will be consistent between rendering and parsing.
+ """
+ # try to use ZODB object IDs
+ keys = []
+ for value in values:
+ if value is None:
+ oid = ""
+ else:
+ oid = getattr(value, "_p_oid", None)
+ if not oid:
+ break
+ hi, lo = struct.unpack(">LL", oid)
+ oid = "%x" % ((hi << 32) | lo)
+ keys.append(oid)
+ else:
+ # found OID for every value
+ return keys
+ # can't use OIDs, try using descriptions
+ used_keys = {}
+ keys = map(str, descriptions)
+ for key in keys:
+ if used_keys.has_key(key):
+ raise ValueError, "duplicated descriptions (provide keys)"
+ used_keys[key] = 1
+ return keys
+
+ def set_options(self, options, sort=False):
+ """(options: [objects:any], sort=False)
+ or
+ (options: [(object:any, description:any)], sort=False)
+ or
+ (options: [(object:any, description:any, key:any)], sort=False)
+ """
+
+ """
+ Set the options list. The list of options can be a list of objects, in
+ which case the descriptions default to map(htmlescape, objects)
+ applying htmlescape() to each description and
+ key.
+ If keys are provided they must be distinct. If the sort keyword
+ argument is true, sort the options by case-insensitive lexicographic
+ order of descriptions, except that options with value None appear
+ before others.
+ """
+ if options:
+ first = options[0]
+ values = []
+ descriptions = []
+ keys = []
+ if isinstance(first, tuple):
+ if len(first) == 2:
+ for value, description in options:
+ values.append(value)
+ descriptions.append(description)
+ elif len(first) == 3:
+ for value, description, key in options:
+ values.append(value)
+ descriptions.append(description)
+ keys.append(stringify(key))
+ else:
+ raise ValueError, 'invalid options %r' % options
+ else:
+ values = descriptions = options
+
+ if not keys:
+ keys = self._generate_keys(values, descriptions)
+
+ options = zip(values, descriptions, keys)
+
+ if sort:
+ def make_sort_key(option):
+ value, description, key = option
+ if value is None:
+ return ('', option)
+ else:
+ return (stringify(description).lower(), option)
+ doptions = map(make_sort_key, options)
+ doptions.sort()
+ options = [item[1] for item in doptions]
+ self.options = options
+
+ def _parse_single_selection(self, parsed_key, default=None):
+ for value, description, key in self.options:
+ if key == parsed_key:
+ return value
+ else:
+ if self.verify_selection:
+ self.error = self.SELECTION_ERROR
+ return default
+ elif self.options:
+ return self.options[0][0]
+ else:
+ return default
+
+ def set_allowed_values(self, allowed_values, descriptions=None,
+ sort=False):
+ """(allowed_values:[any], descriptions:[any], sort:boolean=False)
+
+ Set the options for this widget. The allowed_values and descriptions
+ parameters must be sequences of the same length. The sort option
+ causes the options to be sorted using case-insensitive lexicographic
+ order of descriptions, except that options with value None appear
+ before others.
+ """
+ if descriptions is None:
+ self.set_options(allowed_values, sort)
+ else:
+ assert len(descriptions) == len(allowed_values)
+ self.set_options(zip(allowed_values, descriptions), sort)
+
+ def is_selected(self, value):
+ return value == self.value
+
+ def render_content(self):
+ tags = [htmltag("select", name=self.name, **self.attrs)]
+ for object, description, key in self.options:
+ if self.is_selected(object):
+ selected = 'selected'
+ else:
+ selected = None
+ if description is None:
+ description = ""
+ r = htmltag("option", value=key, selected=selected)
+ tags.append(r + htmlescape(description) + htmltext('</option>'))
+ tags.append(htmltext("</select>"))
+ return htmltext("\n").join(tags)
+
+
+class SingleSelectWidget(SelectWidget):
+ """Widget for single selection.
+ """
+
+ SELECT_TYPE = "single_select"
+ MULTIPLE_SELECTION_ERROR = "cannot select multiple values"
+
+ def _parse(self, request):
+ parsed_key = request.form.get(self.name)
+ if parsed_key:
+ if isinstance(parsed_key, list):
+ self.error = self.MULTIPLE_SELECTION_ERROR
+ else:
+ self.value = self._parse_single_selection(parsed_key)
+ else:
+ self.value = None
+
+
+class RadiobuttonsWidget(SingleSelectWidget):
+ """Widget for a *set* of related radiobuttons -- all have the
+ same name, but different values (and only one of those values
+ is returned by the whole group).
+
+ Instance attributes:
+ delim : string = None
+ string to emit between each radiobutton in the group. If
+ None, a single newline is emitted.
+ """
+
+ SELECT_TYPE = "radiobuttons"
+
+ def __init__(self, name, value=None, options=None, delim=None, **kwargs):
+ SingleSelectWidget.__init__(self, name, value, options=options,
+ **kwargs)
+ if delim is None:
+ self.delim = "\n"
+ else:
+ self.delim = delim
+
+ def render_content(self):
+ tags = []
+ for object, description, key in self.options:
+ if self.is_selected(object):
+ checked = 'checked'
+ else:
+ checked = None
+ r = htmltag("input", xml_end=True,
+ type="radio",
+ name=self.name,
+ value=key,
+ checked=checked,
+ **self.attrs)
+ tags.append(r + htmlescape(description))
+ return htmlescape(self.delim).join(tags)
+
+
+class MultipleSelectWidget(SelectWidget):
+ """Widget for multiple selection.
+
+ Instance attributes:
+ value : [any]
+ for multipe selects, the value is None or a list of
+ elements from dict(self.options).values()
+ """
+
+ SELECT_TYPE = "multiple_select"
+
+ def __init__(self, name, value=None, options=None, **kwargs):
+ SelectWidget.__init__(self, name, value, options=options,
+ multiple='multiple', **kwargs)
+
+ def set_value(self, value):
+ allowed_values = self.get_allowed_values()
+ if value in allowed_values:
+ self.value = [ value ]
+ elif isinstance(value, (list, tuple)):
+ self.value = [ element
+ for element in value
+ if element in allowed_values ] or None
+ else:
+ self.value = None
+
+ def is_selected(self, value):
+ if self.value is None:
+ return value is None
+ else:
+ return value in self.value
+
+ def _parse(self, request):
+ parsed_keys = request.form.get(self.name)
+ if parsed_keys:
+ if isinstance(parsed_keys, list):
+ self.value = [value
+ for value, description, key in self.options
+ if key in parsed_keys] or None
+ else:
+ _marker = []
+ value = self._parse_single_selection(parsed_keys, _marker)
+ if value is _marker:
+ self.value = None
+ else:
+ self.value = [value]
+ else:
+ self.value = None
+
+
+class ButtonWidget(Widget):
+ """
+ Instance attributes:
+ label : string
+ value : boolean
+ """
+
+ HTML_TYPE = "button"
+
+ def __init__(self, name, value=None, **kwargs):
+ Widget.__init__(self, name, value=None, **kwargs)
+ self.set_label(value)
+
+ def set_label(self, label):
+ self.label = label
+
+ def get_label(self):
+ return self.label
+
+ def render_content(self):
+ # slightly different behavior here, we always render the
+ # tag using the 'value' passed in as a parameter. 'self.value'
+ # is a boolean that is true if the button's name appears
+ # in the request.
+ value = (self.label and htmlescape(self.label) or None)
+ return htmltag("input", xml_end=True, type=self.HTML_TYPE,
+ name=self.name, value=value, **self.attrs)
+
+ def _parse(self, request):
+ self.value = request.form.has_key(self.name)
+
+
+class SubmitWidget(ButtonWidget):
+ HTML_TYPE = "submit"
+
+class ResetWidget(SubmitWidget):
+ HTML_TYPE = "reset"
+
+
+class HiddenWidget(Widget):
+ """
+ Instance attributes:
+ value : string
+ """
+
+ def set_error(self, error):
+ if error is not None:
+ raise TypeError, 'error not allowed on hidden widgets'
+
+ def render_content(self):
+ if self.value is None:
+ value = None
+ else:
+ value = htmlescape(self.value)
+ return htmltag("input", xml_end=True,
+ type="hidden",
+ name=self.name,
+ value=value,
+ **self.attrs)
+
+ def render(self):
+ return self.render_content() # Input elements of type hidden have no decoration.
+
+# -- Derived widget types ----------------------------------------------
+# (these don't correspond to fundamental widget types in HTML,
+# so they're separated)
+
+class NumberWidget(StringWidget):
+ """
+ Instance attributes: none
+ """
+
+ # Parameterize the number type (either float or int) through
+ # these class attributes:
+ TYPE_OBJECT = None # eg. int, float
+ TYPE_ERROR = None # human-readable error message
+
+ def __init__(self, name, value=None, **kwargs):
+ assert self.__class__ is not NumberWidget, "abstract class"
+ assert value is None or type(value) is self.TYPE_OBJECT, (
+ "form value '%s' not a %s: got %r" % (name,
+ self.TYPE_OBJECT,
+ value))
+ StringWidget.__init__(self, name, value, **kwargs)
+
+ def _parse(self, request):
+ StringWidget._parse(self, request)
+ if self.value is not None:
+ try:
+ self.value = self.TYPE_OBJECT(self.value)
+ except ValueError:
+ self.error = self.TYPE_ERROR
+
+
+class FloatWidget(NumberWidget):
+ """
+ Instance attributes:
+ value : float
+ """
+ TYPE_OBJECT = float
+ TYPE_ERROR = "must be a number"
+
+
+class IntWidget(NumberWidget):
+ """
+ Instance attributes:
+ value : int
+ """
+ TYPE_OBJECT = int
+ TYPE_ERROR = "must be an integer"
+
+
+class OptionSelectWidget(SingleSelectWidget):
+ """Widget for single selection with automatic submission. Parse
+ will always return a value from it's options, even if the form is
+ not submitted. This allows its value to be used to decide what
+ other widgets need to be created in a form. It's a powerful
+ feature but it can be hard to understand what's going on.
+
+ Instance attributes:
+ value : any
+ """
+
+ SELECT_TYPE = "option_select"
+
+ def __init__(self, name, value=None, options=None, **kwargs):
+ SingleSelectWidget.__init__(self, name, value, options=options,
+ onchange='submit()', **kwargs)
+
+ def parse(self, request=None):
+ if not self._parsed:
+ if request is None:
+ request = get_request()
+ self._parse(request)
+ self._parsed = True
+ return self.value
+
+ def _parse(self, request):
+ parsed_key = request.form.get(self.name)
+ if parsed_key:
+ if isinstance(parsed_key, list):
+ self.error = self.MULTIPLE_SELECTION_ERROR
+ else:
+ self.value = self._parse_single_selection(parsed_key)
+ elif self.value is None:
+ self.value = self.options[0][0]
+
+ def render_content(self):
+ return (SingleSelectWidget.render_content(self) +
+ htmltext('<noscript>'
+ '<input type="submit" name="" value="apply" />'
+ '</noscript>'))
+
+
+class CompositeWidget(Widget):
+ """
+ Instance attributes:
+ widgets : [Widget]
+ _names : {name:string : Widget}
+ """
+ def __init__(self, name, value=None, **kwargs):
+ Widget.__init__(self, name, value, **kwargs)
+ self.widgets = []
+ self._names = {}
+
+ def _parse(self, request):
+ for widget in self.widgets:
+ widget.parse(request)
+
+ def __getitem__(self, name):
+ return self._names[name].parse()
+
+ def get(self, name):
+ widget = self._names.get(name)
+ if widget:
+ return widget.parse()
+ return None
+
+ def get_widget(self, name):
+ return self._names.get(name)
+
+ def get_widgets(self):
+ return self.widgets
+
+ def clear_error(self, request=None):
+ Widget.clear_error(self, request)
+ for widget in self.widgets:
+ widget.clear_error(request)
+
+ def set_widget_error(self, name, error):
+ self._names[name].set_error(error)
+
+ def has_error(self, request=None):
+ has_error = False
+ if Widget.has_error(self, request=request):
+ has_error = True
+ for widget in self.widgets:
+ if widget.has_error(request=request):
+ has_error = True
+ return has_error
+
+ def add(self, widget_class, name, *args, **kwargs):
+ if self._names.has_key(name):
+ raise ValueError, 'the name %r is already used' % name
+ if self.attrs.get('disabled') and 'disabled' not in kwargs:
+ kwargs['disabled'] = True
+ widget = widget_class(subname(self.name, name), *args, **kwargs)
+ self._names[name] = widget
+ self.widgets.append(widget)
+
+ def render_content(self):
+ r = TemplateIO(html=True)
+ for widget in self.get_widgets():
+ r += widget.render()
+ return r.getvalue()
+
+
+class WidgetList(CompositeWidget):
+ """A variable length list of widgets. There is only one
+ title and hint but each element of the list can have its own
+ error. You can also set an error on the WidgetList itself (e.g. as a
+ result of higher-level processing).
+
+ Instance attributes:
+ element_names : [string]
+ """
+
+ def __init__(self, name, value=None,
+ element_type=StringWidget,
+ element_kwargs={},
+ add_element_label="Add row", **kwargs):
+ assert value is None or type(value) is list, (
+ "value '%s' not a list: got %r" % (name, value))
+ assert issubclass(element_type, Widget), (
+ "value '%s' element_type not a Widget: "
+ "got %r" % (name, element_type))
+ assert type(element_kwargs) is dict, (
+ "value '%s' element_kwargs not a dict: "
+ "got %r" % (name, element_kwargs))
+ assert type(add_element_label) in (str, htmltext), (
+ "value '%s'add_element_label not a string: "
+ "got %r" % (name, add_element_label))
+
+ CompositeWidget.__init__(self, name, value, **kwargs)
+ self.element_names = []
+
+ self.add(HiddenWidget, 'added_elements')
+ added_elements_widget = self.get_widget('added_elements')
+
+
+ def add_element(value=None):
+ name = "element%d" % len(self.element_names)
+ self.add(element_type, name, value=value, **element_kwargs)
+ self.element_names.append(name)
+
+ # Add element widgets for initial value
+ if value is not None:
+ for element_value in value:
+ add_element(value=element_value)
+
+ # Add at least one additional element widget
+ num_added = int(added_elements_widget.parse() or 1)
+ for i in range(num_added):
+ add_element()
+
+ # Add submit to add more element widgets
+ self.add(SubmitWidget, 'add_element', value=add_element_label)
+ if self.get('add_element'):
+ add_element()
+ num_added += 1
+ added_elements_widget.set_value(num_added)
+
+ def _parse(self, request):
+ values = []
+ for name in self.element_names:
+ value = self.get(name)
+ if value is not None:
+ values.append(value)
+ self.value = values or None
+
+ def render_content(self):
+ r = TemplateIO(html=True)
+ add_element_widget = self.get_widget('add_element')
+ for widget in self.get_widgets():
+ if widget is add_element_widget:
+ continue
+ r += widget.render()
+ r += add_element_widget.render()
+ return r.getvalue()
+
+ def render(self):
+ r = TemplateIO(html=True)
+ r += self.render_title(self.get_title())
+ add_element_widget = self.get_widget('add_element')
+ for widget in self.get_widgets():
+ if widget is add_element_widget:
+ continue
+ r += widget.render()
+ r += add_element_widget.render()
+ r += self.render_hint(self.get_hint())
+ return r.getvalue()
+
+
+class WidgetDict(CompositeWidget):
+ """A variable length dict of widgets. There is only one
+ title and hint but each element of the dict can have its own
+ error. You can also set an error on the WidgetDict itself (e.g. as a
+ result of higher-level processing).
+
+ Instance attributes:
+ element_names : [string]
+ """
+
+ def __init__(self, name, value=None, title='', hint='',
+ element_key_type=StringWidget,
+ element_value_type=StringWidget,
+ element_key_kwargs={},
+ element_value_kwargs={},
+ add_element_label='Add row', **kwargs):
+ assert value is None or type(value) is dict, (
+ 'value %r not a dict: got %r' % (name, value))
+ assert issubclass(element_key_type, Widget), (
+ "value '%s' element_key_type not a Widget: "
+ "got %r" % (name, element_key_type))
+ assert issubclass(element_value_type, Widget), (
+ "value '%s' element_value_type not a Widget: "
+ "got %r" % (name, element_value_type))
+ assert type(element_key_kwargs) is dict, (
+ "value '%s' element_key_kwargs not a dict: "
+ "got %r" % (name, element_key_kwargs))
+ assert type(element_value_kwargs) is dict, (
+ "value '%s' element_value_kwargs not a dict: "
+ "got %r" % (name, element_value_kwargs))
+ assert type(add_element_label) in (str, htmltext), (
+ 'value %r element_name not a string: '
+ 'got %r' % (name, add_element_label))
+
+ CompositeWidget.__init__(self, name, value, **kwargs)
+ self.element_names = []
+
+ self.add(HiddenWidget, 'added_elements')
+ added_elements_widget = self.get_widget('added_elements')
+
+ def add_element(key=None, value=None):
+ name = 'element%d' % len(self.element_names)
+ self.add(element_key_type, name + 'key',
+ value=key, render_br=False, **element_key_kwargs)
+ self.add(element_value_type, name + 'value',
+ value=value, **element_value_kwargs)
+ self.element_names.append(name)
+
+ # Add element widgets for initial value
+ if value is not None:
+ for key, element_value in value.items():
+ add_element(key=key, value=element_value)
+
+ # Add at least one additional element widget
+ num_added = int(added_elements_widget.parse() or 1)
+ for i in range(num_added):
+ add_element()
+
+ # Add submit to add more element widgets
+ self.add(SubmitWidget, 'add_element', value=add_element_label)
+ if self.get('add_element'):
+ add_element()
+ num_added += 1
+ added_elements_widget.set_value(num_added)
+
+ def _parse(self, request):
+ values = {}
+ for name in self.element_names:
+ key = self.get(name + 'key')
+ value = self.get(name + 'value')
+ if key and value:
+ values[key] = value
+ self.value = values or None
+
+ def render_content(self):
+ r = TemplateIO(html=True)
+ for name in self.element_names:
+ if name in ('add_element', 'added_elements'):
+ continue
+ key_widget = self.get_widget(name + 'key')
+ value_widget = self.get_widget(name + 'value')
+ r += htmltext('%s<div class="widget">: </div>%s') % (
+ key_widget.render(),
+ value_widget.render())
+ if self.render_br:
+ r += htmltext('<br clear="left" class="widget" />')
+ r += htmltext('\n')
+ r += self.get_widget('add_element').render()
+ r += self.get_widget('added_elements').render()
+ return r.getvalue()
diff --git a/pypers/europython05/Quixote-2.0/form1/__init__.py b/pypers/europython05/Quixote-2.0/form1/__init__.py
new file mode 100755
index 0000000..2989d6d
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form1/__init__.py
@@ -0,0 +1,34 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form1/__init__.py $
+$Id: __init__.py 25664 2004-11-22 20:35:07Z nascheme $
+
+The web interface framework, consisting of Form and Widget base classes
+(and a bunch of standard widget classes recognized by Form).
+Application developers will typically create a Form subclass for each
+form in their application; each form object will contain a number
+of widget objects. Custom widgets can be created by inheriting
+and/or composing the standard widget classes.
+"""
+
+from quixote.form1.form import Form, register_widget_class, FormTokenWidget
+from quixote.form1.widget import Widget, StringWidget, FileWidget, \
+ PasswordWidget, TextWidget, CheckboxWidget, RadiobuttonsWidget, \
+ SingleSelectWidget, SelectWidget, OptionSelectWidget, \
+ MultipleSelectWidget, ListWidget, SubmitButtonWidget, HiddenWidget, \
+ FloatWidget, IntWidget, CollapsibleListWidget, FormValueError
+
+# Register the standard widget classes
+register_widget_class(StringWidget)
+register_widget_class(FileWidget)
+register_widget_class(PasswordWidget)
+register_widget_class(TextWidget)
+register_widget_class(CheckboxWidget)
+register_widget_class(RadiobuttonsWidget)
+register_widget_class(SingleSelectWidget)
+register_widget_class(OptionSelectWidget)
+register_widget_class(MultipleSelectWidget)
+register_widget_class(ListWidget)
+register_widget_class(SubmitButtonWidget)
+register_widget_class(HiddenWidget)
+register_widget_class(FloatWidget)
+register_widget_class(IntWidget)
+register_widget_class(CollapsibleListWidget)
diff --git a/pypers/europython05/Quixote-2.0/form1/form.py b/pypers/europython05/Quixote-2.0/form1/form.py
new file mode 100755
index 0000000..c67598c
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form1/form.py
@@ -0,0 +1,534 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form1/form.py $
+$Id: form.py 25664 2004-11-22 20:35:07Z nascheme $
+
+Provides the Form class and bureaucracy for registering widget classes.
+(The standard widget classes are registered automatically.)
+"""
+
+from types import StringType
+from quixote import get_session, get_publisher, redirect
+from quixote.html import url_quote, htmltag, htmltext, nl2br, TemplateIO
+from quixote.form1.widget import FormValueError, HiddenWidget
+
+
+class FormTokenWidget (HiddenWidget):
+ def render(self, request):
+ self.value = get_session().create_form_token()
+ return HiddenWidget.render(self, request)
+
+
+JAVASCRIPT_MARKUP = htmltext('''\
+<script type="text/javascript">
+<!--
+%s
+// -->
+</script>
+''')
+
+class Form:
+ """
+ A form is the major element of an interactive web page. A form
+ consists of the following:
+ * widgets (input/interaction elements)
+ * text
+ * layout
+ * code to process the form
+
+ All four of these are the responsibility of Form classes.
+ Typically, you will create one Form subclass for each form in your
+ application. Thanks to the separation of responsibilities here,
+ it's not too hard to structure things so that a given form is
+ rendered and/or processed somewhat differently depending on context.
+ That separation is as follows:
+ * the constructor declares what widgets are in the form, and
+ any static text that is always associated with those widgets
+ (in particular, a widget title and "hint" text)
+ * the 'render()' method combines the widgets and their associated
+ text to create a (1-D) stream of HTML that represents the
+ (2-D) web page that will be presented to the user
+ * the 'process()' method parses the user input values from the form
+ and validates them
+ * the 'action()' method takes care of finishing whatever action
+ was requested by the user submitting the form -- commit
+ a database transaction, update session flags, redirect the
+ user to a new page, etc.
+
+ This class provides a default 'process()' method that just parses
+ each widget, storing any error messages for display on the next
+ 'render()', and returns the results (if the form parses
+ successfully) in a dictionary.
+
+ This class also provides a default 'render()' method that lays out
+ widgets and text in a 3-column table: the first column is the widget
+ title, the second column is the widget itself, and the third column is
+ any hint and/or error text associated with the widget. Also provided
+ are methods that can be used to construct this table a row at a time,
+ so you can use this layout for most widgets, but escape from it for
+ oddities.
+
+ Instance attributes:
+ widgets : { widget_name:string : widget:Widget }
+ dictionary of all widgets in the form
+ widget_order : [Widget]
+ same widgets as 'widgets', but ordered (because order matters)
+ submit_buttons : [SubmitButtonWidget]
+ the submit button widgets in the form
+
+ error : { widget_name:string : error_message:string }
+ hint : { widget_name:string : hint_text:string }
+ title : { widget_name:string : widget_title:string }
+ required : { widget_name:string : boolean }
+
+ """
+
+ TOKEN_NAME = "_form_id" # name of hidden token widget
+
+ def __init__(self, method="post", enctype=None, use_tokens=1):
+
+ if method not in ("post", "get"):
+ raise ValueError("Form method must be 'post' or 'get', "
+ "not %r" % method)
+ self.method = method
+
+ if enctype is not None and enctype not in (
+ "application/x-www-form-urlencoded", "multipart/form-data"):
+ raise ValueError, ("Form enctype must be "
+ "'application/x-www-form-urlencoded' or "
+ "'multipart/form-data', not %r" % enctype)
+ self.enctype = enctype
+
+ # The first major component of a form: its widgets. We want
+ # both easy access and order, so we have a dictionary and a list
+ # of the same objects. The dictionary is keyed on widget name.
+ # These are populated by the 'add_*_widget()' methods.
+ self.widgets = {}
+ self.widget_order = []
+ self.submit_buttons = []
+ self.cancel_url = None
+
+ # The second major component: text. It's up to the 'render()'
+ # method to figure out how to lay these out; the standard
+ # 'render()' does so in a fairly sensible way that should work
+ # for most of our forms. These are also populated by the
+ # 'add_*_widget()' methods.
+ self.error = {}
+ self.hint = {}
+ self.title = {}
+ self.required = {}
+
+ config = get_publisher().config
+ if self.method == "post" and use_tokens and config.form_tokens:
+ # unique token for each form, this prevents many cross-site
+ # attacks and prevents a form from being submitted twice
+ self.add_widget(FormTokenWidget, self.TOKEN_NAME)
+ self.use_form_tokens = 1
+ else:
+ self.use_form_tokens = 0
+
+ # Subclasses should override this method to specify the actual
+ # widgets in this form -- typically this consists of a series of
+ # calls to 'add_widget()', which updates the data structures we
+ # just defined.
+
+
+ # -- Layout (rendering) methods ------------------------------------
+
+ # The third major component of a web form is layout. These methods
+ # combine text and widgets in a 1-D stream of HTML, or in a 2-D web
+ # page (depending on your level of abstraction).
+
+ def render(self, request, action_url):
+ # render(request : HTTPRequest,
+ # action_url : string)
+ # -> HTML text
+ #
+ # Render a form as HTML.
+ assert type(action_url) in (StringType, htmltext)
+ r = TemplateIO(html=1)
+ r += self._render_start(request, action_url,
+ enctype=self.enctype, method=self.method)
+ r += self._render_body(request)
+ r += self._render_finish(request)
+ return r.getvalue()
+
+ def _render_start(self, request, action,
+ enctype=None, method='post', name=None):
+ r = TemplateIO(html=1)
+ r += htmltag('form', enctype=enctype, method=method,
+ action=action, name=name)
+ r += self._render_hidden_widgets(request)
+ return r.getvalue()
+
+ def _render_finish(self, request):
+ r = TemplateIO(html=1)
+ r += htmltext('</form>')
+ r += self._render_javascript(request)
+ return r.getvalue()
+
+ def _render_sep(self, text, line=1):
+ return htmltext('<tr><td colspan="3">%s<strong><big>%s'
+ '</big></strong></td></tr>') % \
+ (line and htmltext('<hr>') or '', text)
+
+ def _render_error(self, error):
+ if error:
+ return htmltext('<font color="red">%s</font><br />') % nl2br(error)
+ else:
+ return ''
+
+ def _render_hint(self, hint):
+ if hint:
+ return htmltext('<em>%s</em>') % hint
+ else:
+ return ''
+
+ def _render_widget_row(self, request, widget):
+ if widget.widget_type == 'hidden':
+ return ''
+ title = self.title[widget.name] or ''
+ if self.required.get(widget.name):
+ title = title + htmltext('&nbsp;*')
+ r = TemplateIO(html=1)
+ r += htmltext('<tr><th colspan="3" align="left">')
+ r += title
+ r += htmltext('</th></tr>'
+ '<tr><td>&nbsp;&nbsp;</td><td>')
+ r += widget.render(request)
+ r += htmltext('</td><td>')
+ r += self._render_error(self.error.get(widget.name))
+ r += self._render_hint(self.hint.get(widget.name))
+ r += htmltext('</td></tr>')
+ return r.getvalue()
+
+ def _render_hidden_widgets(self, request):
+ r = TemplateIO(html=1)
+ for widget in self.widget_order:
+ if widget.widget_type == 'hidden':
+ r += widget.render(request)
+ r += self._render_error(self.error.get(widget.name))
+ return r.getvalue()
+
+ def _render_submit_buttons(self, request, ncols=3):
+ r = TemplateIO(html=1)
+ r += htmltext('<tr><td colspan="%d">\n') % ncols
+ for button in self.submit_buttons:
+ r += button.render(request)
+ r += htmltext('</td></tr>')
+ return r.getvalue()
+
+ def _render_visible_widgets(self, request):
+ r = TemplateIO(html=1)
+ for widget in self.widget_order:
+ r += self._render_widget_row(request, widget)
+ return r.getvalue()
+
+ def _render_error_notice(self, request):
+ if self.error:
+ r = htmltext('<tr><td colspan="3">'
+ '<font color="red"><strong>Warning:</strong></font> '
+ 'there were errors processing your form. '
+ 'See below for details.'
+ '</td></tr>')
+ else:
+ r = ''
+ return r
+
+ def _render_required_notice(self, request):
+ if filter(None, self.required.values()):
+ r = htmltext('<tr><td colspan="3">'
+ '<b>*</b> = <em>required field</em>'
+ '</td></tr>')
+ else:
+ r = ''
+ return r
+
+ def _render_body(self, request):
+ r = TemplateIO(html=1)
+ r += htmltext('<table>')
+ r += self._render_error_notice(request)
+ r += self._render_required_notice(request)
+ r += self._render_visible_widgets(request)
+ r += self._render_submit_buttons(request)
+ r += htmltext('</table>')
+ return r.getvalue()
+
+ def _render_javascript(self, request):
+ """Render javacript code for the form, if any.
+ Insert code lexically sorted by code_id
+ """
+ javascript_code = request.response.javascript_code
+ if javascript_code:
+ form_code = []
+ code_ids = javascript_code.keys()
+ code_ids.sort()
+ for code_id in code_ids:
+ code = javascript_code[code_id]
+ if code:
+ form_code.append(code)
+ javascript_code[code_id] = ''
+ if form_code:
+ return JAVASCRIPT_MARKUP % htmltext(''.join(form_code))
+ return ''
+
+
+ # -- Processing methods --------------------------------------------
+
+ # The fourth and final major component: code to process the form.
+ # The standard 'process()' method just parses every widget and
+ # returns a { field_name : field_value } dictionary as 'values'.
+
+ def process(self, request):
+ """process(request : HTTPRequest) -> values : { string : any }
+
+ Process the form data, validating all input fields (widgets).
+ If any errors in input fields, adds error messages to the
+ 'error' attribute (so that future renderings of the form will
+ include the errors). Returns a dictionary mapping widget names to
+ parsed values.
+ """
+ self.error.clear()
+
+ values = {}
+ for widget in self.widget_order:
+ try:
+ val = widget.parse(request)
+ except FormValueError, exc:
+ self.error[widget.name] = exc.msg
+ else:
+ values[widget.name] = val
+
+ return values
+
+ def action(self, request, submit, values):
+ """action(request : HTTPRequest, submit : string,
+ values : { string : any }) -> string
+
+ Carry out the action required by a form submission. 'submit' is the
+ name of submit button used to submit the form. 'values' is the
+ dictionary of parsed values from 'process()'. Note that error
+ checking cannot be done here -- it must done in the 'process()'
+ method.
+ """
+ raise NotImplementedError, "sub-classes must implement 'action()'"
+
+ def handle(self, request):
+ """handle(request : HTTPRequest) -> string
+
+ Master method for handling forms. It should be called after
+ initializing a form. Controls form action based on a request. You
+ probably should override 'process' and 'action' instead of
+ overriding this method.
+ """
+ action_url = self.get_action_url(request)
+ if not self.form_submitted(request):
+ return self.render(request, action_url)
+ submit = self.get_submit_button(request)
+ if submit == "cancel":
+ return redirect(self.cancel_url)
+ values = self.process(request)
+ if submit == "":
+ # The form was submitted by unknown submit button, assume that
+ # the submission was required to update the layout of the form.
+ # Clear the errors and re-render the form.
+ self.error.clear()
+ return self.render(request, action_url)
+
+ if self.use_form_tokens:
+ # before calling action() ensure that there is a valid token
+ # present
+ token = values.get(self.TOKEN_NAME)
+ if not request.session.has_form_token(token):
+ if not self.error:
+ # if there are other errors then don't show the token
+ # error, the form needs to be resubmitted anyhow
+ self.error[self.TOKEN_NAME] = (
+ "The form you have submitted is invalid. It has "
+ "already been submitted or has expired. Please "
+ "review and resubmit the form.")
+ else:
+ request.session.remove_form_token(token)
+
+ if self.error:
+ return self.render(request, action_url)
+ else:
+ return self.action(request, submit, values)
+
+
+ # -- Convenience methods -------------------------------------------
+
+ def form_submitted(self, request):
+ """form_submitted(request : HTTPRequest) -> boolean
+
+ Return true if a form was submitted in the current request.
+ """
+ return len(request.form) > 0
+
+ def get_action_url(self, request):
+ action_url = url_quote(request.get_path())
+ query = request.get_environ("QUERY_STRING")
+ if query:
+ action_url += "?" + query
+ return action_url
+
+ def get_submit_button(self, request):
+ """get_submit_button(request : HTTPRequest) -> string | None
+
+ Get the name of the submit button that was used to submit the
+ current form. If the browser didn't include this information in
+ the request, use the first submit button registered.
+ """
+ for button in self.submit_buttons:
+ if request.form.has_key(button.name):
+ return button.name
+ else:
+ if request.form and self.submit_buttons:
+ return ""
+ else:
+ return None
+
+ def get_widget(self, widget_name):
+ return self.widgets.get(widget_name)
+
+ def parse_widget(self, name, request):
+ """parse_widget(name : string, request : HTTPRequest) -> any
+
+ Parse the value of named widget. If any parse errors, store the
+ error message (in self.error) for use in the next rendering of
+ the form and return None; otherwise, return the value parsed
+ from the widget (whose type depends on the widget type).
+ """
+ try:
+ return self.widgets[name].parse(request)
+ except FormValueError, exc:
+ self.error[name] = str(exc)
+ return None
+
+ def store_value(self, widget_name, request, target,
+ mode="modifier",
+ key=None,
+ missing_error=None):
+ """store_value(widget_name : string,
+ request : HTTPRequest,
+ target : instance | dict,
+ mode : string = "modifier",
+ key : string = widget_name,
+ missing_error : string = None)
+
+ Parse a widget and, if it parsed successfully, store its value
+ in 'target'. The value is stored in 'target' by name 'key';
+ if 'key' is not supplied, it defaults to 'widget_name'.
+ How the value is stored depends on 'mode':
+ * modifier: call a modifier method, eg. if 'key' is "foo",
+ call 'target.set_foo(value)'
+ * direct: direct attribute update, eg. if 'key' is
+ "foo" do "target.foo = value"
+ * dict: dictionary update, eg. if 'key' is "foo" do
+ "target['foo'] = value"
+
+ If 'missing_error' is supplied, use it as an error message if
+ the field doesn't have a value -- ie. supplying 'missing_error'
+ means this field is required.
+ """
+ value = self.parse_widget(widget_name, request)
+ if (value is None or value == "") and missing_error:
+ self.error[widget_name] = missing_error
+ return None
+
+ if key is None:
+ key = widget_name
+ if mode == "modifier":
+ # eg. turn "name" into "target.set_name", and
+ # call it like "target.set_name(value)"
+ mod = getattr(target, "set_" + key)
+ mod(value)
+ elif mode == "direct":
+ if not hasattr(target, key):
+ raise AttributeError, \
+ ("target object %s doesn't have attribute %s" %
+ (`target`, key))
+ setattr(target, key, value)
+ elif mode == "dict":
+ target[key] = value
+ else:
+ raise ValueError, "unknown update mode %s" % `mode`
+
+ def clear_widget(self, widget_name):
+ self.widgets[widget_name].clear()
+
+ def get_widget_value(self, widget_name):
+ return self.widgets[widget_name].value
+
+ def set_widget_value(self, widget_name, value):
+ self.widgets[widget_name].set_value(value)
+
+
+ # -- Form population methods ---------------------------------------
+
+ def add_widget(self, widget_type, name, value=None,
+ title=None, hint=None, required=0, **args):
+ """add_widget(widget_type : string | Widget,
+ name : string,
+ value : any = None,
+ title : string = None,
+ hint : string = None,
+ required : boolean = 0,
+ ...) -> Widget
+
+ Create a new Widget object and add it to the form. The widget
+ class used depends on 'widget_type', and the expected type of
+ 'value' also depends on the widget class. Any extra keyword
+ args are passed to the widget constructor.
+
+ Returns the new Widget.
+ """
+ if self.widgets.has_key(name):
+ raise ValueError, "form already has '%s' variable" % name
+ klass = get_widget_class(widget_type)
+ new_widget = apply(klass, (name, value), args)
+
+ self.widgets[name] = new_widget
+ self.widget_order.append(new_widget)
+ self.title[name] = title
+ self.hint[name] = hint
+ self.required[name] = required
+ return new_widget
+
+ def add_submit_button(self, name, value):
+ global _widget_class
+ if self.widgets.has_key(name):
+ raise ValueError, "form already has '%s' variable" % name
+ new_widget = _widget_class['submit_button'](name, value)
+
+ self.widgets[name] = new_widget
+ self.submit_buttons.append(new_widget)
+
+ def add_cancel_button(self, caption, url):
+ if not isinstance(url, (StringType, htmltext)):
+ raise TypeError, "url must be a string (got %r)" % url
+ self.add_submit_button("cancel", caption)
+ self.cancel_url = url
+
+# class Form
+
+
+_widget_class = {}
+
+def register_widget_class(klass, widget_type=None):
+ global _widget_class
+ if widget_type is None:
+ widget_type = klass.widget_type
+ assert widget_type is not None, "widget_type must be defined"
+ _widget_class[widget_type] = klass
+
+def get_widget_class(widget_type):
+ global _widget_class
+ if callable(widget_type):
+ # Presumably someone passed a widget class object to
+ # Widget.create_subwidget() or Form.add_widget() --
+ # don't bother with the widget class registry at all.
+ return widget_type
+ else:
+ try:
+ return _widget_class[widget_type]
+ except KeyError:
+ raise ValueError("unknown widget type %r" % widget_type)
diff --git a/pypers/europython05/Quixote-2.0/form1/widget.py b/pypers/europython05/Quixote-2.0/form1/widget.py
new file mode 100755
index 0000000..1ddc229
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/form1/widget.py
@@ -0,0 +1,842 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/form1/widget.py $
+$Id: widget.py 25664 2004-11-22 20:35:07Z nascheme $
+
+Provides the basic web widget classes: Widget itself, plus StringWidget,
+TextWidget, CheckboxWidget, etc.
+"""
+
+import struct
+from types import FloatType, IntType, ListType, StringType, TupleType
+from quixote import get_request
+from quixote.html import htmltext, htmlescape, htmltag
+from quixote.http_request import Upload
+
+
+class FormValueError (Exception):
+ """Raised whenever a widget has problems parsing its value."""
+
+ def __init__(self, msg):
+ self.msg = msg
+
+
+ def __str__(self):
+ return str(self.msg)
+
+
+class Widget:
+ """Abstract base class for web widgets. The key elements
+ of a web widget are:
+ - name
+ - widget type (how the widget looks/works in the browser)
+ - value
+
+ The name and value are instance attributes (because they're specific to
+ a particular widget in a particular context); widget type is a
+ class attributes.
+
+ Instance attributes:
+ name : string
+ value : any
+
+ Feel free to access these directly; to set them, use the 'set_*()'
+ modifier methods.
+ """
+
+ # Subclasses must define. 'widget_type' is just a string, e.g.
+ # "string", "text", "checkbox".
+ widget_type = None
+
+ def __init__(self, name, value=None):
+ assert self.__class__ is not Widget, "abstract class"
+ self.set_name(name)
+ self.set_value(value)
+
+
+ def __repr__(self):
+ return "<%s at %x: %s>" % (self.__class__.__name__,
+ id(self),
+ self.name)
+
+
+ def __str__(self):
+ return "%s: %s" % (self.widget_type, self.name)
+
+
+ def set_name(self, name):
+ self.name = name
+
+
+ def set_value(self, value):
+ self.value = value
+
+
+ def clear(self):
+ self.value = None
+
+ # -- Subclasses must implement these -------------------------------
+
+ def render(self, request):
+ """render(request) -> HTML text"""
+ raise NotImplementedError
+
+
+ def parse(self, request):
+ """parse(request) -> any"""
+ value = request.form.get(self.name)
+ if type(value) is StringType and value.strip():
+ self.value = value
+ else:
+ self.value = None
+
+ return self.value
+
+ # -- Convenience methods for subclasses ----------------------------
+
+ # This one's really only for composite widgets; lives here until
+ # we have a demonstrated need for a CompositeWidget class.
+ def get_subwidget_name(self, name):
+ return "%s$%s" % (self.name, name)
+
+
+ def create_subwidget(self, widget_type, widget_name, value=None, **args):
+ from quixote.form.form import get_widget_class
+ klass = get_widget_class(widget_type)
+ name = self.get_subwidget_name(widget_name)
+ return apply(klass, (name, value), args)
+
+# class Widget
+
+# -- Fundamental widget types ------------------------------------------
+# These correspond to the standard types of input tag in HTML:
+# text StringWidget
+# password PasswordWidget
+# radio RadiobuttonWidget
+# checkbox CheckboxWidget
+#
+# and also to the other basic form elements:
+# <textarea> TextWidget
+# <select> SingleSelectWidget
+# <select multiple>
+# MultipleSelectWidget
+
+class StringWidget (Widget):
+ """Widget for entering a single string: corresponds to
+ '<input type="text">' in HTML.
+
+ Instance attributes:
+ value : string
+ size : int
+ maxlength : int
+ """
+
+ widget_type = "string"
+
+ # This lets PasswordWidget be a trivial subclass
+ html_type = "text"
+
+ def __init__(self, name, value=None,
+ size=None, maxlength=None):
+ Widget.__init__(self, name, value)
+ self.size = size
+ self.maxlength = maxlength
+
+
+ def render(self, request, **attributes):
+ return htmltag("input", xml_end=1,
+ type=self.html_type,
+ name=self.name,
+ size=self.size,
+ maxlength=self.maxlength,
+ value=self.value,
+ **attributes)
+
+
+class FileWidget (StringWidget):
+ """Trivial subclass of StringWidget for uploading files.
+
+ Instance attributes: none
+ """
+ widget_type = "file"
+ html_type = "file"
+
+ def parse(self, request):
+ """parse(request) -> any"""
+ value = request.form.get(self.name)
+ if isinstance(value, Upload):
+ self.value = value
+ else:
+ self.value = None
+ return self.value
+
+
+class PasswordWidget (StringWidget):
+ """Trivial subclass of StringWidget for entering passwords (different
+ widget type because HTML does it that way).
+
+ Instance attributes: none
+ """
+
+ widget_type = "password"
+ html_type = "password"
+
+
+class TextWidget (Widget):
+ """Widget for entering a long, multi-line string; corresponds to
+ the HTML "<textarea>" tag.
+
+ Instance attributes:
+ value : string
+ cols : int
+ rows : int
+ wrap : string
+ (see an HTML book for details on text widget wrap options)
+ css_class : string
+ """
+
+ widget_type = "text"
+
+ def __init__(self, name, value=None, cols=None, rows=None, wrap=None,
+ css_class=None):
+ Widget.__init__(self, name, value)
+ self.cols = cols
+ self.rows = rows
+ self.wrap = wrap
+ self.css_class = css_class
+
+ def render(self, request):
+ return (htmltag("textarea", name=self.name,
+ cols=self.cols,
+ rows=self.rows,
+ wrap=self.wrap,
+ css_class=self.css_class) +
+ htmlescape(self.value or "") +
+ htmltext("</textarea>"))
+
+
+ def parse(self, request):
+ value = Widget.parse(self, request)
+ if value:
+ value = value.replace("\r\n", "\n")
+ self.value = value
+ return self.value
+
+
+class CheckboxWidget (Widget):
+ """Widget for a single checkbox: corresponds to "<input
+ type=checkbox>". Do not put multiple CheckboxWidgets with the same
+ name in the same form.
+
+ Instance attributes:
+ value : boolean
+ """
+
+ widget_type = "checkbox"
+
+ def render(self, request):
+ return htmltag("input", xml_end=1,
+ type="checkbox",
+ name=self.name,
+ value="yes",
+ checked=self.value and "checked" or None)
+
+
+ def parse(self, request):
+ self.value = request.form.has_key(self.name)
+ return self.value
+
+
+class SelectWidget (Widget):
+ """Widget for single or multiple selection; corresponds to
+ <select name=...>
+ <option value="Foo">Foo</option>
+ ...
+ </select>
+
+ Instance attributes:
+ options : [ (value:any, description:any, key:string) ]
+ value : any
+ The value is None or an element of dict(options.values()).
+ size : int
+ The number of options that should be presented without scrolling.
+ """
+
+ # NB. 'widget_type' not set here because this is an abstract class: it's
+ # set by subclasses SingleSelectWidget and MultipleSelectWidget.
+
+ def __init__(self, name, value=None,
+ allowed_values=None,
+ descriptions=None,
+ options=None,
+ size=None,
+ sort=0,
+ verify_selection=1):
+ assert self.__class__ is not SelectWidget, "abstract class"
+ self.options = []
+ # if options passed, cannot pass allowed_values or descriptions
+ if allowed_values is not None:
+ assert options is None, (
+ 'cannot pass both allowed_values and options')
+ assert allowed_values, (
+ 'cannot pass empty allowed_values list')
+ self.set_allowed_values(allowed_values, descriptions, sort)
+ elif options is not None:
+ assert descriptions is None, (
+ 'cannot pass both options and descriptions')
+ assert options, (
+ 'cannot pass empty options list')
+ self.set_options(options, sort)
+ self.set_name(name)
+ self.set_value(value)
+ self.size = size
+ self.verify_selection = verify_selection
+
+
+ def get_allowed_values(self):
+ return [item[0] for item in self.options]
+
+
+ def get_descriptions(self):
+ return [item[1] for item in self.options]
+
+
+ def set_value(self, value):
+ self.value = None
+ for object, description, key in self.options:
+ if value == object:
+ self.value = value
+ break
+
+
+ def _generate_keys(self, values, descriptions):
+ """Called if no keys were provided. Try to generate a set of keys
+ that will be consistent between rendering and parsing.
+ """
+ # try to use ZODB object IDs
+ keys = []
+ for value in values:
+ if value is None:
+ oid = ""
+ else:
+ oid = getattr(value, "_p_oid", None)
+ if not oid:
+ break
+ hi, lo = struct.unpack(">LL", oid)
+ oid = "%x" % ((hi << 32) | lo)
+ keys.append(oid)
+ else:
+ # found OID for every value
+ return keys
+ # can't use OIDs, try using descriptions
+ used_keys = {}
+ keys = map(str, descriptions)
+ for key in keys:
+ if used_keys.has_key(key):
+ raise ValueError, "duplicated descriptions (provide keys)"
+ used_keys[key] = 1
+ return keys
+
+
+ def set_options(self, options, sort=0):
+ """(options: [objects:any], sort=0)
+ or
+ (options: [(object:any, description:any)], sort=0)
+ or
+ (options: [(object:any, description:any, key:any)], sort=0)
+ """
+
+ """
+ Set the options list. The list of options can be a list of objects, in
+ which case the descriptions default to map(htmlescape, objects)
+ applying htmlescape() to each description and
+ key.
+ If keys are provided they must be distinct. If the sort keyword
+ argument is true, sort the options by case-insensitive lexicographic
+ order of descriptions, except that options with value None appear
+ before others.
+ """
+ if options:
+ first = options[0]
+ values = []
+ descriptions = []
+ keys = []
+ if type(first) is TupleType:
+ if len(first) == 2:
+ for value, description in options:
+ values.append(value)
+ descriptions.append(description)
+ elif len(first) == 3:
+ for value, description, key in options:
+ values.append(value)
+ descriptions.append(description)
+ keys.append(str(key))
+ else:
+ raise ValueError, 'invalid options %r' % options
+ else:
+ values = descriptions = options
+
+ if not keys:
+ keys = self._generate_keys(values, descriptions)
+
+ options = zip(values, descriptions, keys)
+
+ if sort:
+ def make_sort_key(option):
+ value, description, key = option
+ if value is None:
+ return ('', option)
+ else:
+ return (str(description).lower(), option)
+ doptions = map(make_sort_key, options)
+ doptions.sort()
+ options = [item[1] for item in doptions]
+ self.options = options
+
+
+ def parse_single_selection(self, parsed_key):
+ for value, description, key in self.options:
+ if key == parsed_key:
+ return value
+ else:
+ if self.verify_selection:
+ raise FormValueError, "invalid value selected"
+ else:
+ return self.options[0][0]
+
+
+ def set_allowed_values(self, allowed_values, descriptions=None, sort=0):
+ """(allowed_values:[any], descriptions:[any], sort:boolean=0)
+
+ Set the options for this widget. The allowed_values and descriptions
+ parameters must be sequences of the same length. The sort option
+ causes the options to be sorted using case-insensitive lexicographic
+ order of descriptions, except that options with value None appear
+ before others.
+ """
+ if descriptions is None:
+ self.set_options(allowed_values, sort)
+ else:
+ assert len(descriptions) == len(allowed_values)
+ self.set_options(zip(allowed_values, descriptions), sort)
+
+
+ def is_selected(self, value):
+ return value == self.value
+
+
+ def render(self, request):
+ if self.widget_type == "multiple_select":
+ multiple = "multiple"
+ else:
+ multiple = None
+ if self.widget_type == "option_select":
+ onchange = "submit()"
+ else:
+ onchange = None
+ tags = [htmltag("select", name=self.name,
+ multiple=multiple, onchange=onchange,
+ size=self.size)]
+ for object, description, key in self.options:
+ if self.is_selected(object):
+ selected = "selected"
+ else:
+ selected = None
+ if description is None:
+ description = ""
+ r = htmltag("option", value=key, selected=selected)
+ tags.append(r + htmlescape(description) + htmltext('</option>'))
+ tags.append(htmltext("</select>"))
+ return htmltext("\n").join(tags)
+
+
+class SingleSelectWidget (SelectWidget):
+ """Widget for single selection.
+ """
+
+ widget_type = "single_select"
+
+ def parse(self, request):
+ parsed_key = request.form.get(self.name)
+ self.value = None
+ if parsed_key:
+ if type(parsed_key) is ListType:
+ raise FormValueError, "cannot select multiple values"
+ self.value = self.parse_single_selection(parsed_key)
+ return self.value
+
+
+class RadiobuttonsWidget (SingleSelectWidget):
+ """Widget for a *set* of related radiobuttons -- all have the
+ same name, but different values (and only one of those values
+ is returned by the whole group).
+
+ Instance attributes:
+ delim : string = None
+ string to emit between each radiobutton in the group. If
+ None, a single newline is emitted.
+ """
+
+ widget_type = "radiobuttons"
+
+ def __init__(self, name, value=None,
+ allowed_values=None,
+ descriptions=None,
+ options=None,
+ delim=None):
+ SingleSelectWidget.__init__(self, name, value, allowed_values,
+ descriptions, options)
+ if delim is None:
+ self.delim = "\n"
+ else:
+ self.delim = delim
+
+
+ def render(self, request):
+ tags = []
+ for object, description, key in self.options:
+ if self.is_selected(object):
+ checked = "checked"
+ else:
+ checked = None
+ r = htmltag("input", xml_end=True,
+ type="radio",
+ name=self.name,
+ value=key,
+ checked=checked)
+ tags.append(r + htmlescape(description))
+ return htmlescape(self.delim).join(tags)
+
+
+class MultipleSelectWidget (SelectWidget):
+ """Widget for multiple selection.
+
+ Instance attributes:
+ value : [any]
+ for multipe selects, the value is None or a list of
+ elements from dict(self.options).values()
+ """
+
+ widget_type = "multiple_select"
+
+ def set_value(self, value):
+ allowed_values = self.get_allowed_values()
+ if value in allowed_values:
+ self.value = [ value ]
+ elif type(value) in (ListType, TupleType):
+ self.value = [ element
+ for element in value
+ if element in allowed_values ] or None
+ else:
+ self.value = None
+
+
+ def is_selected(self, value):
+ if self.value is None:
+ return value is None
+ else:
+ return value in self.value
+
+
+ def parse(self, request):
+ parsed_keys = request.form.get(self.name)
+ self.value = None
+ if parsed_keys:
+ if type(parsed_keys) is ListType:
+ self.value = [value
+ for value, description, key in self.options
+ if key in parsed_keys] or None
+ else:
+ self.value = [self.parse_single_selection(parsed_keys)]
+ return self.value
+
+
+class SubmitButtonWidget (Widget):
+ """
+ Instance attributes:
+ value : boolean
+ """
+
+ widget_type = "submit_button"
+
+ def __init__(self, name=None, value=None):
+ Widget.__init__(self, name, value)
+
+
+ def render(self, request):
+ value = (self.value and htmlescape(self.value) or None)
+ return htmltag("input", xml_end=1, type="submit",
+ name=self.name, value=value)
+
+
+ def parse(self, request):
+ return request.form.get(self.name)
+
+
+ def is_submitted(self):
+ return self.parse(get_request())
+
+
+class HiddenWidget (Widget):
+ """
+ Instance attributes:
+ value : string
+ """
+
+ widget_type = "hidden"
+
+ def render(self, request):
+ if self.value is None:
+ value = None
+ else:
+ value = htmlescape(self.value)
+ return htmltag("input", xml_end=1,
+ type="hidden",
+ name=self.name,
+ value=value)
+
+
+ def set_current_value(self, value):
+ self.value = value
+ request = get_request()
+ if request.form:
+ request.form[self.name] = value
+
+
+ def get_current_value(self):
+ request = get_request()
+ if request.form:
+ return self.parse(request)
+ else:
+ return self.value
+
+# -- Derived widget types ----------------------------------------------
+# (these don't correspond to fundamental widget types in HTML,
+# so they're separated)
+
+class NumberWidget (StringWidget):
+ """
+ Instance attributes: none
+ """
+
+ # Parameterize the number type (either float or int) through
+ # these class attributes:
+ type_object = None # eg. int, float
+ type_error = None # human-readable error message
+ type_converter = None # eg. int(), float()
+
+ def __init__(self, name,
+ value=None,
+ size=None, maxlength=None):
+ assert self.__class__ is not NumberWidget, "abstract class"
+ assert value is None or type(value) is self.type_object, (
+ "form value '%s' not a %s: got %r" % (name,
+ self.type_object,
+ value))
+ StringWidget.__init__(self, name, value, size, maxlength)
+
+
+ def parse(self, request):
+ value = StringWidget.parse(self, request)
+ if value:
+ try:
+ self.value = self.type_converter(value)
+ except ValueError:
+ raise FormValueError, self.type_error
+ return self.value
+
+
+class FloatWidget (NumberWidget):
+ """
+ Instance attributes:
+ value : float
+ """
+
+ widget_type = "float"
+ type_object = FloatType
+ type_converter = float
+ type_error = "must be a number"
+
+
+class IntWidget (NumberWidget):
+ """
+ Instance attributes:
+ value : int
+ """
+
+ widget_type = "int"
+ type_object = IntType
+ type_converter = int
+ type_error = "must be an integer"
+
+
+class OptionSelectWidget (SingleSelectWidget):
+ """Widget for single selection with automatic submission and early
+ parsing. This widget parses the request when it is created. This
+ allows its value to be used to decide what other widgets need to be
+ created in a form. It's a powerful feature but it can be hard to
+ understand what's going on.
+
+ Instance attributes:
+ value : any
+ """
+
+ widget_type = "option_select"
+
+ def __init__(self, *args, **kwargs):
+ SingleSelectWidget.__init__(self, *args, **kwargs)
+
+ request = get_request()
+ if request.form:
+ SingleSelectWidget.parse(self, request)
+ if self.value is None:
+ self.value = self.options[0][0]
+
+
+ def render(self, request):
+ return (SingleSelectWidget.render(self, request) +
+ htmltext('<noscript>'
+ '<input type="submit" name="" value="apply" />'
+ '</noscript>'))
+
+
+ def parse(self, request):
+ return self.value
+
+
+ def get_current_option(self):
+ return self.value
+
+
+class ListWidget (Widget):
+ """Widget for lists of objects.
+
+ Instance attributes:
+ value : [any]
+ """
+
+ widget_type = "list"
+
+ def __init__(self, name, value=None,
+ element_type=None,
+ element_name="row",
+ **args):
+ assert value is None or type(value) is ListType, (
+ "form value '%s' not a list: got %r" % (name, value))
+ assert type(element_name) in (StringType, htmltext), (
+ "form value '%s' element_name not a string: "
+ "got %r" % (name, element_name))
+
+ Widget.__init__(self, name, value)
+
+ if element_type is None:
+ self.element_type = "string"
+ else:
+ self.element_type = element_type
+ self.args = args
+
+ self.added_elements_widget = self.create_subwidget(
+ "hidden", "added_elements")
+
+ added_elements = int(self.added_elements_widget.get_current_value() or
+ '1')
+
+ self.add_button = self.create_subwidget(
+ "submit_button", "add_element",
+ value="Add %s" % element_name)
+
+ if self.add_button.is_submitted():
+ added_elements += 1
+ self.added_elements_widget.set_current_value(str(added_elements))
+
+ self.element_widgets = []
+ self.element_count = 0
+
+ if self.value is not None:
+ for element in self.value:
+ self.add_element(element)
+
+ for index in range(added_elements):
+ self.add_element()
+
+ def add_element(self, value=None):
+ self.element_widgets.append(
+ self.create_subwidget(self.element_type,
+ "element_%d" % self.element_count,
+ value=value,
+ **self.args))
+ self.element_count += 1
+
+ def render(self, request):
+ tags = []
+ for element_widget in self.element_widgets:
+ tags.append(element_widget.render(request))
+ tags.append(self.add_button.render(request))
+ tags.append(self.added_elements_widget.render(request))
+ return htmltext('<br />\n').join(tags)
+
+ def parse(self, request):
+ self.value = []
+ for element_widget in self.element_widgets:
+ value = element_widget.parse(request)
+ if value is not None:
+ self.value.append(value)
+ self.value = self.value or None
+ return self.value
+
+
+
+class CollapsibleListWidget (ListWidget):
+ """Widget for lists of objects with associated delete buttons.
+
+ CollapsibleListWidget behaves like ListWidget except that each element
+ is rendered with an associated delete button. Pressing the delete
+ button will cause the associated element name to be added to a hidden
+ widget that remembers all deletions until the form is submitted.
+ Only elements that are not marked as deleted will be rendered and
+ ultimately added to the value of the widget.
+
+ Instance attributes:
+ value : [any]
+ """
+
+ widget_type = "collapsible_list"
+
+ def __init__(self, name, value=None, element_name="row", **args):
+ self.name = name
+ self.element_name = element_name
+ self.deleted_elements_widget = self.create_subwidget(
+ "hidden", "deleted_elements")
+ self.element_delete_buttons = []
+ self.deleted_elements = (
+ self.deleted_elements_widget.get_current_value() or '')
+ ListWidget.__init__(self, name, value=value,
+ element_name=element_name,
+ **args)
+
+ def add_element(self, value=None):
+ element_widget_name = "element_%d" % self.element_count
+ if self.deleted_elements.find(element_widget_name) == -1:
+ delete_button = self.create_subwidget(
+ "submit_button", "delete_" + element_widget_name,
+ value="Delete %s" % self.element_name)
+ if delete_button.is_submitted():
+ self.element_count += 1
+ self.deleted_elements += element_widget_name
+ self.deleted_elements_widget.set_current_value(
+ self.deleted_elements)
+ else:
+ self.element_delete_buttons.append(delete_button)
+ ListWidget.add_element(self, value=value)
+ else:
+ self.element_count += 1
+
+ def render(self, request):
+ tags = []
+ for element_widget, element_delete_button in zip(
+ self.element_widgets, self.element_delete_buttons):
+ if self.deleted_elements.find(element_widget.name) == -1:
+ tags.append(element_widget.render(request) +
+ element_delete_button.render(request))
+ tags.append(self.add_button.render(request))
+ tags.append(self.added_elements_widget.render(request))
+ tags.append(self.deleted_elements_widget.render(request))
+ return htmltext('<br />\n').join(tags)
diff --git a/pypers/europython05/Quixote-2.0/html/__init__.py b/pypers/europython05/Quixote-2.0/html/__init__.py
new file mode 100755
index 0000000..563e78b
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/html/__init__.py
@@ -0,0 +1,106 @@
+"""Various functions for dealing with HTML.
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/html/__init__.py $
+$Id: __init__.py 26357 2005-03-16 14:56:23Z dbinger $
+
+These functions are fairly simple but it is critical that they be
+used correctly. Many security problems are caused by escaping errors
+(cross site scripting is one example). The HTML and XML standards on
+www.w3c.org and www.xml.com should be studied, especially the sections
+on character sets, entities, attribute and values.
+
+htmltext and htmlescape
+-----------------------
+
+This type and function are meant to be used with [html] PTL template type.
+The htmltext type designates data that does not need to be escaped and the
+htmlescape() function calls str() on the argment, escapes the resulting
+string and returns a htmltext instance. htmlescape() does nothing to
+htmltext instances.
+
+url_quote
+---------
+
+Use for quoting data to be included as part of a URL, for example:
+
+ input = "foo bar"
+ ...
+ '<a href="/search?keyword=%s">' % url_quote(input)
+
+Note that URLs are usually used as attribute values and might need to have
+HTML special characters escaped. As an example of incorrect usage:
+
+ url = 'http://example.com/?a=1&copy=0' # INCORRECT
+ url = 'http://example.com/?a=1&amp;copy=0' # CORRECT
+ ...
+ '<a href="%s">do something</a>' % url
+
+Old browsers would treat "&copy" as an entity reference and replace it with
+the copyright character. XML processors should treat it as an invalid entity
+reference.
+"""
+
+import urllib
+
+try:
+ # faster C implementation
+ from quixote.html._c_htmltext import htmltext, htmlescape, \
+ stringify, TemplateIO
+except ImportError:
+ from quixote.html._py_htmltext import htmltext, htmlescape, \
+ stringify, TemplateIO
+
+ValuelessAttr = object() # magic singleton object
+
+def htmltag(tag, xml_end=False, css_class=None, **attrs):
+ """Create a HTML tag.
+ """
+ r = ["<%s" % tag]
+ if css_class is not None:
+ attrs['class'] = css_class
+ for (attr, val) in attrs.items():
+ if val is ValuelessAttr:
+ val = attr
+ if val is not None:
+ r.append(' %s="%s"' % (attr, htmlescape(val)))
+ if xml_end:
+ r.append(" />")
+ else:
+ r.append(">")
+ return htmltext("".join(r))
+
+
+def href(url, text, title=None, **attrs):
+ return (htmltag("a", href=url, title=title, **attrs) +
+ htmlescape(text) +
+ htmltext("</a>"))
+
+def url_with_query(path, **attrs):
+ result = htmltext(url_quote(path))
+ if attrs:
+ result += "?" + "&".join([url_quote(key) + "=" + url_quote(value)
+ for key, value in attrs.items()])
+ return result
+
+def nl2br(value):
+ """nl2br(value : any) -> htmltext
+
+ Insert <br /> tags before newline characters.
+ """
+ text = htmlescape(value)
+ return htmltext(text.s.replace('\n', '<br />\n'))
+
+
+def url_quote(value, fallback=None):
+ """url_quote(value : any [, fallback : string]) -> string
+
+ Quotes 'value' for use in a URL; see urllib.quote(). If value is None,
+ then the behavior depends on the fallback argument. If it is not
+ supplied then an error is raised. Otherwise, the fallback value is
+ returned unquoted.
+ """
+ if value is None:
+ if fallback is None:
+ raise ValueError, "value is None and no fallback supplied"
+ else:
+ return fallback
+ return urllib.quote(stringify(value))
diff --git a/pypers/europython05/Quixote-2.0/html/_c_htmltext.c b/pypers/europython05/Quixote-2.0/html/_c_htmltext.c
new file mode 100755
index 0000000..1c6581a
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/html/_c_htmltext.c
@@ -0,0 +1,1019 @@
+/* htmltext type and the htmlescape function */
+
+#include "Python.h"
+#include "structmember.h"
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *s;
+} htmltextObject;
+
+static PyTypeObject htmltext_Type;
+
+#define htmltextObject_Check(v) ((v)->ob_type == &htmltext_Type)
+
+#define htmltext_STR(v) ((PyObject *)(((htmltextObject *)v)->s))
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *obj;
+} QuoteWrapperObject;
+
+static PyTypeObject QuoteWrapper_Type;
+
+#define QuoteWrapper_Check(v) ((v)->ob_type == &QuoteWrapper_Type)
+
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *data; /* PyList_Object */
+ int html;
+} TemplateIO_Object;
+
+static PyTypeObject TemplateIO_Type;
+
+#define TemplateIO_Check(v) ((v)->ob_type == &TemplateIO_Type)
+
+
+static PyObject *
+type_error(const char *msg)
+{
+ PyErr_SetString(PyExc_TypeError, msg);
+ return NULL;
+}
+
+static int
+string_check(PyObject *v)
+{
+ return PyUnicode_Check(v) || PyString_Check(v);
+}
+
+static PyObject *
+stringify(PyObject *obj)
+{
+ static PyObject *unicodestr = NULL;
+ PyObject *res, *func;
+ if (string_check(obj)) {
+ Py_INCREF(obj);
+ return obj;
+ }
+ if (unicodestr == NULL) {
+ unicodestr = PyString_InternFromString("__unicode__");
+ if (unicodestr == NULL)
+ return NULL;
+ }
+ func = PyObject_GetAttr(obj, unicodestr);
+ if (func != NULL) {
+ res = PyEval_CallObject(func, (PyObject *)NULL);
+ Py_DECREF(func);
+ }
+ else {
+ PyErr_Clear();
+ if (obj->ob_type->tp_str != NULL)
+ res = (*obj->ob_type->tp_str)(obj);
+ else
+ res = PyObject_Repr(obj);
+ }
+ if (res == NULL)
+ return NULL;
+ if (!string_check(res)) {
+ Py_DECREF(res);
+ return type_error("string object required");
+ }
+ return res;
+}
+
+static PyObject *
+escape_string(PyObject *obj)
+{
+ char *s;
+ PyObject *newobj;
+ size_t i, j, extra_space, size, new_size;
+ assert (PyString_Check(obj));
+ size = PyString_GET_SIZE(obj);
+ extra_space = 0;
+ for (i=0; i < size; i++) {
+ switch (PyString_AS_STRING(obj)[i]) {
+ case '&':
+ extra_space += 4;
+ break;
+ case '<':
+ case '>':
+ extra_space += 3;
+ break;
+ case '"':
+ extra_space += 5;
+ break;
+ }
+ }
+ if (extra_space == 0) {
+ Py_INCREF(obj);
+ return (PyObject *)obj;
+ }
+ new_size = size + extra_space;
+ newobj = PyString_FromStringAndSize(NULL, new_size);
+ if (newobj == NULL)
+ return NULL;
+ s = PyString_AS_STRING(newobj);
+ for (i=0, j=0; i < size; i++) {
+ switch (PyString_AS_STRING(obj)[i]) {
+ case '&':
+ s[j++] = '&';
+ s[j++] = 'a';
+ s[j++] = 'm';
+ s[j++] = 'p';
+ s[j++] = ';';
+ break;
+ case '<':
+ s[j++] = '&';
+ s[j++] = 'l';
+ s[j++] = 't';
+ s[j++] = ';';
+ break;
+ case '>':
+ s[j++] = '&';
+ s[j++] = 'g';
+ s[j++] = 't';
+ s[j++] = ';';
+ break;
+ case '"':
+ s[j++] = '&';
+ s[j++] = 'q';
+ s[j++] = 'u';
+ s[j++] = 'o';
+ s[j++] = 't';
+ s[j++] = ';';
+ break;
+ default:
+ s[j++] = PyString_AS_STRING(obj)[i];
+ break;
+ }
+ }
+ assert (j == new_size);
+ return (PyObject *)newobj;
+}
+
+static PyObject *
+escape_unicode(PyObject *obj)
+{
+ Py_UNICODE *u;
+ PyObject *newobj;
+ size_t i, j, extra_space, size, new_size;
+ assert (PyUnicode_Check(obj));
+ size = PyUnicode_GET_SIZE(obj);
+ extra_space = 0;
+ for (i=0; i < size; i++) {
+ switch (PyUnicode_AS_UNICODE(obj)[i]) {
+ case '&':
+ extra_space += 4;
+ break;
+ case '<':
+ case '>':
+ extra_space += 3;
+ break;
+ case '"':
+ extra_space += 5;
+ break;
+ }
+ }
+ if (extra_space == 0) {
+ Py_INCREF(obj);
+ return (PyObject *)obj;
+ }
+ new_size = size + extra_space;
+ newobj = PyUnicode_FromUnicode(NULL, new_size);
+ if (newobj == NULL) {
+ return NULL;
+ }
+ u = PyUnicode_AS_UNICODE(newobj);
+ for (i=0, j=0; i < size; i++) {
+ switch (PyUnicode_AS_UNICODE(obj)[i]) {
+ case '&':
+ u[j++] = '&';
+ u[j++] = 'a';
+ u[j++] = 'm';
+ u[j++] = 'p';
+ u[j++] = ';';
+ break;
+ case '<':
+ u[j++] = '&';
+ u[j++] = 'l';
+ u[j++] = 't';
+ u[j++] = ';';
+ break;
+ case '>':
+ u[j++] = '&';
+ u[j++] = 'g';
+ u[j++] = 't';
+ u[j++] = ';';
+ break;
+ case '"':
+ u[j++] = '&';
+ u[j++] = 'q';
+ u[j++] = 'u';
+ u[j++] = 'o';
+ u[j++] = 't';
+ u[j++] = ';';
+ break;
+ default:
+ u[j++] = PyUnicode_AS_UNICODE(obj)[i];
+ break;
+ }
+ }
+ assert (j == new_size);
+ return (PyObject *)newobj;
+}
+
+static PyObject *
+escape(PyObject *obj)
+{
+ if (PyString_Check(obj)) {
+ return escape_string(obj);
+ }
+ else if (PyUnicode_Check(obj)) {
+ return escape_unicode(obj);
+ }
+ else {
+ return type_error("string object required");
+ }
+}
+
+static PyObject *
+quote_wrapper_new(PyObject *o)
+{
+ QuoteWrapperObject *self;
+ if (htmltextObject_Check(o) ||
+ PyInt_Check(o) ||
+ PyFloat_Check(o) ||
+ PyLong_Check(o)) {
+ /* no need for wrapper */
+ Py_INCREF(o);
+ return o;
+ }
+ self = PyObject_New(QuoteWrapperObject, &QuoteWrapper_Type);
+ if (self == NULL)
+ return NULL;
+ Py_INCREF(o);
+ self->obj = o;
+ return (PyObject *)self;
+}
+
+static void
+quote_wrapper_dealloc(QuoteWrapperObject *self)
+{
+ Py_DECREF(self->obj);
+ PyObject_Del(self);
+}
+
+static PyObject *
+quote_wrapper_repr(QuoteWrapperObject *self)
+{
+ PyObject *qs;
+ PyObject *s = PyObject_Repr(self->obj);
+ if (s == NULL)
+ return NULL;
+ qs = escape(s);
+ Py_DECREF(s);
+ return qs;
+}
+
+static PyObject *
+quote_wrapper_str(QuoteWrapperObject *self)
+{
+ PyObject *qs;
+ PyObject *s = stringify(self->obj);
+ if (s == NULL)
+ return NULL;
+ qs = escape(s);
+ Py_DECREF(s);
+ return qs;
+}
+
+static PyObject *
+quote_wrapper_subscript(QuoteWrapperObject *self, PyObject *key)
+{
+ PyObject *v, *w;;
+ v = PyObject_GetItem(self->obj, key);
+ if (v == NULL) {
+ return NULL;
+ }
+ w = quote_wrapper_new(v);
+ Py_DECREF(v);
+ return w;
+}
+
+static PyObject *
+htmltext_from_string(PyObject *s)
+{
+ /* note, this steals a reference */
+ PyObject *self;
+ if (s == NULL)
+ return NULL;
+ assert (string_check(s));
+ self = PyType_GenericAlloc(&htmltext_Type, 0);
+ if (self == NULL) {
+ Py_DECREF(s);
+ return NULL;
+ }
+ ((htmltextObject *)self)->s = s;
+ return self;
+}
+
+static PyObject *
+htmltext_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ htmltextObject *self;
+ PyObject *s;
+ static char *kwlist[] = {"s", 0};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:htmltext", kwlist,
+ &s))
+ return NULL;
+ s = stringify(s);
+ if (s == NULL)
+ return NULL;
+ self = (htmltextObject *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ Py_DECREF(s);
+ return NULL;
+ }
+ self->s = s;
+ return (PyObject *)self;
+}
+
+/* htmltext methods */
+
+static void
+htmltext_dealloc(htmltextObject *self)
+{
+ Py_DECREF(self->s);
+ self->ob_type->tp_free((PyObject *)self);
+}
+
+static long
+htmltext_hash(PyObject *self)
+{
+ return PyObject_Hash(htmltext_STR(self));
+}
+
+static PyObject *
+htmltext_str(htmltextObject *self)
+{
+ Py_INCREF(self->s);
+ return (PyObject *)self->s;
+}
+
+static PyObject *
+htmltext_repr(htmltextObject *self)
+{
+ PyObject *sr, *rv;
+ sr = PyObject_Repr((PyObject *)self->s);
+ if (sr == NULL)
+ return NULL;
+ rv = PyString_FromFormat("<htmltext %s>", PyString_AsString(sr));
+ Py_DECREF(sr);
+ return rv;
+}
+
+static PyObject *
+htmltext_richcompare(PyObject *a, PyObject *b, int op)
+{
+ if (htmltextObject_Check(a)) {
+ a = htmltext_STR(a);
+ }
+ if (htmltextObject_Check(b)) {
+ b = htmltext_STR(b);
+ }
+ return PyObject_RichCompare(a, b, op);
+}
+
+static long
+htmltext_length(htmltextObject *self)
+{
+ return PyObject_Size(htmltext_STR(self));
+}
+
+
+static PyObject *
+wrap_arg(PyObject *arg)
+{
+ PyObject *warg;
+ if (htmltextObject_Check(arg)) {
+ /* don't bother with wrapper object */
+ warg = arg;
+ Py_INCREF(arg);
+ }
+ else {
+ warg = quote_wrapper_new(arg);
+ }
+ return warg;
+}
+
+
+static PyObject *
+htmltext_format(htmltextObject *self, PyObject *args)
+{
+ /* wrap the format arguments with QuoteWrapperObject */
+ int is_unicode;
+ PyObject *rv, *wargs;
+ if (PyUnicode_Check(self->s)) {
+ is_unicode = 1;
+ }
+ else {
+ is_unicode = 0;
+ assert (PyString_Check(self->s));
+ }
+ if (PyTuple_Check(args)) {
+ long i, n = PyTuple_GET_SIZE(args);
+ wargs = PyTuple_New(n);
+ for (i=0; i < n; i++) {
+ PyObject *wvalue = wrap_arg(PyTuple_GET_ITEM(args, i));
+ if (wvalue == NULL) {
+ Py_DECREF(wargs);
+ return NULL;
+ }
+ PyTuple_SetItem(wargs, i, wvalue);
+ }
+ }
+ else {
+ wargs = wrap_arg(args);
+ if (wargs == NULL)
+ return NULL;
+ }
+ if (is_unicode)
+ rv = PyUnicode_Format(self->s, wargs);
+ else
+ rv = PyString_Format(self->s, wargs);
+ Py_DECREF(wargs);
+ return htmltext_from_string(rv);
+}
+
+static PyObject *
+htmltext_add(PyObject *v, PyObject *w)
+{
+ PyObject *qv, *qw, *rv;
+ if (htmltextObject_Check(v) && htmltextObject_Check(w)) {
+ qv = htmltext_STR(v);
+ qw = htmltext_STR(w);
+ Py_INCREF(qv);
+ Py_INCREF(qw);
+ }
+ else if (string_check(w)) {
+ assert (htmltextObject_Check(v));
+ qv = htmltext_STR(v);
+ qw = escape(w);
+ if (qw == NULL)
+ return NULL;
+ Py_INCREF(qv);
+ }
+ else if (string_check(v)) {
+ assert (htmltextObject_Check(w));
+ qv = escape(v);
+ if (qv == NULL)
+ return NULL;
+ qw = htmltext_STR(w);
+ Py_INCREF(qw);
+ }
+ else {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ if (PyString_Check(qv)) {
+ PyString_ConcatAndDel(&qv, qw);
+ rv = qv;
+ }
+ else {
+ assert (PyUnicode_Check(qv));
+ rv = PyUnicode_Concat(qv, qw);
+ Py_DECREF(qv);
+ Py_DECREF(qw);
+ }
+ return htmltext_from_string(rv);
+}
+
+static PyObject *
+htmltext_repeat(htmltextObject *self, int n)
+{
+ PyObject *s = PySequence_Repeat(htmltext_STR(self), n);
+ if (s == NULL)
+ return NULL;
+ return htmltext_from_string(s);
+}
+
+static PyObject *
+htmltext_join(PyObject *self, PyObject *args)
+{
+ int i;
+ PyObject *quoted_args, *rv;
+
+ quoted_args = PySequence_List(args);
+ if (quoted_args == NULL)
+ return NULL;
+ for (i=0; i < PyList_Size(quoted_args); i++) {
+ PyObject *value, *qvalue;
+ value = PyList_GET_ITEM(quoted_args, i);
+ if (value == NULL) {
+ goto error;
+ }
+ if (htmltextObject_Check(value)) {
+ qvalue = htmltext_STR(value);
+ Py_INCREF(qvalue);
+ }
+ else {
+ if (!string_check(value)) {
+ type_error("join requires a list of strings");
+ goto error;
+ }
+ qvalue = escape(value);
+ if (qvalue == NULL)
+ goto error;
+ }
+ if (PyList_SetItem(quoted_args, i, qvalue) < 0) {
+ goto error;
+ }
+ }
+ if (PyUnicode_Check(htmltext_STR(self))) {
+ rv = PyUnicode_Join(htmltext_STR(self), quoted_args);
+ }
+ else {
+ rv = _PyString_Join(htmltext_STR(self), quoted_args);
+ }
+ Py_DECREF(quoted_args);
+ return htmltext_from_string(rv);
+
+error:
+ Py_DECREF(quoted_args);
+ return NULL;
+}
+
+static PyObject *
+quote_arg(PyObject *s)
+{
+ PyObject *ss;
+ if (string_check(s)) {
+ ss = escape(s);
+ if (ss == NULL)
+ return NULL;
+ }
+ else if (htmltextObject_Check(s)) {
+ ss = htmltext_STR(s);
+ Py_INCREF(ss);
+ }
+ else {
+ return type_error("string object required");
+ }
+ return ss;
+}
+
+static PyObject *
+htmltext_call_method1(PyObject *self, PyObject *s, char *method)
+{
+ PyObject *ss, *rv;
+ ss = quote_arg(s);
+ if (ss == NULL)
+ return NULL;
+ rv = PyObject_CallMethod(htmltext_STR(self), method, "O", ss);
+ Py_DECREF(ss);
+ return rv;
+}
+
+static PyObject *
+htmltext_startswith(PyObject *self, PyObject *s)
+{
+ return htmltext_call_method1(self, s, "startswith");
+}
+
+static PyObject *
+htmltext_endswith(PyObject *self, PyObject *s)
+{
+ return htmltext_call_method1(self, s, "endswith");
+}
+
+static PyObject *
+htmltext_replace(PyObject *self, PyObject *args)
+{
+ PyObject *old, *new, *q_old, *q_new, *rv;
+ int maxsplit = -1;
+ if (!PyArg_ParseTuple(args,"OO|i:replace", &old, &new, &maxsplit))
+ return NULL;
+ q_old = quote_arg(old);
+ if (q_old == NULL)
+ return NULL;
+ q_new = quote_arg(new);
+ if (q_new == NULL) {
+ Py_DECREF(q_old);
+ return NULL;
+ }
+ rv = PyObject_CallMethod(htmltext_STR(self), "replace", "OOi",
+ q_old, q_new, maxsplit);
+ Py_DECREF(q_old);
+ Py_DECREF(q_new);
+ return htmltext_from_string(rv);
+}
+
+
+static PyObject *
+htmltext_lower(PyObject *self)
+{
+ return htmltext_from_string(PyObject_CallMethod(htmltext_STR(self),
+ "lower", ""));
+}
+
+static PyObject *
+htmltext_upper(PyObject *self)
+{
+ return htmltext_from_string(PyObject_CallMethod(htmltext_STR(self),
+ "upper", ""));
+}
+
+static PyObject *
+htmltext_capitalize(PyObject *self)
+{
+ return htmltext_from_string(PyObject_CallMethod(htmltext_STR(self),
+ "capitalize", ""));
+}
+
+static PyObject *
+template_io_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ TemplateIO_Object *self;
+ int html = 0;
+ static char *kwlist[] = {"html", 0};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:TemplateIO",
+ kwlist, &html))
+ return NULL;
+ self = (TemplateIO_Object *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->data = PyList_New(0);
+ if (self->data == NULL) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ self->html = html != 0;
+ return (PyObject *)self;
+}
+
+static void
+template_io_dealloc(TemplateIO_Object *self)
+{
+ Py_DECREF(self->data);
+ self->ob_type->tp_free((PyObject *)self);
+}
+
+static PyObject *
+template_io_str(TemplateIO_Object *self)
+{
+ static PyObject *empty = NULL;
+ if (empty == NULL) {
+ empty = PyString_FromStringAndSize(NULL, 0);
+ if (empty == NULL)
+ return NULL;
+ }
+ return _PyString_Join(empty, self->data);
+}
+
+static PyObject *
+template_io_getvalue(TemplateIO_Object *self)
+{
+ if (self->html) {
+ return htmltext_from_string(template_io_str(self));
+ }
+ else {
+ return template_io_str(self);
+ }
+}
+
+static PyObject *
+template_io_iadd(TemplateIO_Object *self, PyObject *other)
+{
+ PyObject *s = NULL;
+ if (!TemplateIO_Check(self))
+ return type_error("TemplateIO object required");
+ if (other == Py_None) {
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ else if (htmltextObject_Check(other)) {
+ s = htmltext_STR(other);
+ Py_INCREF(s);
+ }
+ else {
+ if (self->html) {
+ PyObject *ss = stringify(other);
+ if (ss == NULL)
+ return NULL;
+ s = escape(ss);
+ Py_DECREF(ss);
+ }
+ else {
+ s = stringify(other);
+ }
+ if (s == NULL)
+ return NULL;
+ }
+ if (PyList_Append(self->data, s) != 0)
+ return NULL;
+ Py_DECREF(s);
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+static PyMethodDef htmltext_methods[] = {
+ {"join", (PyCFunction)htmltext_join, METH_O, ""},
+ {"startswith", (PyCFunction)htmltext_startswith, METH_O, ""},
+ {"endswith", (PyCFunction)htmltext_endswith, METH_O, ""},
+ {"replace", (PyCFunction)htmltext_replace, METH_VARARGS, ""},
+ {"lower", (PyCFunction)htmltext_lower, METH_NOARGS, ""},
+ {"upper", (PyCFunction)htmltext_upper, METH_NOARGS, ""},
+ {"capitalize", (PyCFunction)htmltext_capitalize, METH_NOARGS, ""},
+ {NULL, NULL}
+};
+
+static PyMemberDef htmltext_members[] = {
+ {"s", T_OBJECT, offsetof(htmltextObject, s), READONLY, "the string"},
+ {NULL},
+};
+
+static PySequenceMethods htmltext_as_sequence = {
+ (inquiry)htmltext_length, /*sq_length*/
+ 0, /*sq_concat*/
+ (intargfunc)htmltext_repeat, /*sq_repeat*/
+ 0, /*sq_item*/
+ 0, /*sq_slice*/
+ 0, /*sq_ass_item*/
+ 0, /*sq_ass_slice*/
+ 0, /*sq_contains*/
+};
+
+static PyNumberMethods htmltext_as_number = {
+ (binaryfunc)htmltext_add, /*nb_add*/
+ 0, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_divide*/
+ (binaryfunc)htmltext_format, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ 0, /*nb_coerce*/
+ 0, /*nb_int*/
+ 0, /*nb_long*/
+ 0, /*nb_float*/
+};
+
+static PyTypeObject htmltext_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "htmltext", /*tp_name*/
+ sizeof(htmltextObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)htmltext_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ (unaryfunc)htmltext_repr,/*tp_repr*/
+ &htmltext_as_number, /*tp_as_number*/
+ &htmltext_as_sequence, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ htmltext_hash, /*tp_hash*/
+ 0, /*tp_call*/
+ (unaryfunc)htmltext_str,/*tp_str*/
+ 0, /*tp_getattro set to PyObject_GenericGetAttr by module init*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE \
+ | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ htmltext_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ htmltext_methods, /*tp_methods*/
+ htmltext_members, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc set to PyType_GenericAlloc by module init*/
+ htmltext_new, /*tp_new*/
+ 0, /*tp_free set to _PyObject_Del by module init*/
+ 0, /*tp_is_gc*/
+};
+
+static PyNumberMethods quote_wrapper_as_number = {
+ 0, /*nb_add*/
+ 0, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_divide*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ 0, /*nb_coerce*/
+ 0, /*nb_int*/
+ 0, /*nb_long*/
+ 0, /*nb_float*/
+};
+
+static PyMappingMethods quote_wrapper_as_mapping = {
+ 0, /*mp_length*/
+ (binaryfunc)quote_wrapper_subscript, /*mp_subscript*/
+ 0, /*mp_ass_subscript*/
+};
+
+
+static PyTypeObject QuoteWrapper_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "QuoteWrapper", /*tp_name*/
+ sizeof(QuoteWrapperObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)quote_wrapper_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ (unaryfunc)quote_wrapper_repr,/*tp_repr*/
+ &quote_wrapper_as_number,/*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ &quote_wrapper_as_mapping,/*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ (unaryfunc)quote_wrapper_str, /*tp_str*/
+};
+
+static PyNumberMethods template_io_as_number = {
+ 0, /*nb_add*/
+ 0, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_divide*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ 0, /*nb_coerce*/
+ 0, /*nb_int*/
+ 0, /*nb_long*/
+ 0, /*nb_float*/
+ 0, /*nb_oct*/
+ 0, /*nb_hex*/
+ (binaryfunc)template_io_iadd, /*nb_inplace_add*/
+};
+
+static PyMethodDef template_io_methods[] = {
+ {"getvalue", (PyCFunction)template_io_getvalue, METH_NOARGS, ""},
+ {NULL, NULL}
+};
+
+static PyTypeObject TemplateIO_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "TemplateIO", /*tp_name*/
+ sizeof(TemplateIO_Object),/*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)template_io_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ &template_io_as_number, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ (unaryfunc)template_io_str,/*tp_str*/
+ 0, /*tp_getattro set to PyObject_GenericGetAttr by module init*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ template_io_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc set to PyType_GenericAlloc by module init*/
+ template_io_new, /*tp_new*/
+ 0, /*tp_free set to _PyObject_Del by module init*/
+ 0, /*tp_is_gc*/
+};
+
+/* --------------------------------------------------------------------- */
+
+static PyObject *
+html_escape(PyObject *self, PyObject *o)
+{
+ if (htmltextObject_Check(o)) {
+ Py_INCREF(o);
+ return o;
+ }
+ else {
+ PyObject *rv;
+ PyObject *s = stringify(o);
+ if (s == NULL)
+ return NULL;
+ rv = escape(s);
+ Py_DECREF(s);
+ return htmltext_from_string(rv);
+ }
+}
+
+static PyObject *
+py_escape_string(PyObject *self, PyObject *o)
+{
+ return escape(o);
+}
+
+static PyObject *
+py_stringify(PyObject *self, PyObject *o)
+{
+ return stringify(o);
+}
+
+/* List of functions defined in the module */
+
+static PyMethodDef htmltext_module_methods[] = {
+ {"htmlescape", (PyCFunction)html_escape, METH_O},
+ {"_escape_string", (PyCFunction)py_escape_string, METH_O},
+ {"stringify", (PyCFunction)py_stringify, METH_O},
+ {NULL, NULL}
+};
+
+static char module_doc[] = "htmltext string type";
+
+void
+init_c_htmltext(void)
+{
+ PyObject *m;
+
+ /* Initialize the type of the new type object here; doing it here
+ * is required for portability to Windows without requiring C++. */
+ htmltext_Type.ob_type = &PyType_Type;
+ QuoteWrapper_Type.ob_type = &PyType_Type;
+ TemplateIO_Type.ob_type = &PyType_Type;
+
+ /* Fix not constant element initialization */
+ htmltext_Type.tp_getattro = PyObject_GenericGetAttr;
+ htmltext_Type.tp_alloc = PyType_GenericAlloc;
+ htmltext_Type.tp_free = _PyObject_Del;
+ TemplateIO_Type.tp_getattro = PyObject_GenericGetAttr;
+ TemplateIO_Type.tp_alloc = PyType_GenericAlloc;
+ TemplateIO_Type.tp_free = _PyObject_Del;
+
+ /* Create the module and add the functions */
+ m = Py_InitModule4("_c_htmltext", htmltext_module_methods, module_doc,
+ NULL, PYTHON_API_VERSION);
+
+ Py_INCREF((PyObject *)&htmltext_Type);
+ Py_INCREF((PyObject *)&QuoteWrapper_Type);
+ Py_INCREF((PyObject *)&TemplateIO_Type);
+ PyModule_AddObject(m, "htmltext", (PyObject *)&htmltext_Type);
+ PyModule_AddObject(m, "TemplateIO", (PyObject *)&TemplateIO_Type);
+}
diff --git a/pypers/europython05/Quixote-2.0/html/_py_htmltext.py b/pypers/europython05/Quixote-2.0/html/_py_htmltext.py
new file mode 100755
index 0000000..798944d
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/html/_py_htmltext.py
@@ -0,0 +1,213 @@
+"""Python implementation of the htmltext type, the htmlescape function and
+TemplateIO.
+"""
+
+#$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/html/_py_htmltext.py $
+#$Id: _py_htmltext.py 26357 2005-03-16 14:56:23Z dbinger $
+
+def _escape_string(s):
+ if not isinstance(s, basestring):
+ raise TypeError, 'string object required'
+ s = s.replace("&", "&amp;")
+ s = s.replace("<", "&lt;")
+ s = s.replace(">", "&gt;")
+ s = s.replace('"', "&quot;")
+ return s
+
+def stringify(obj):
+ """Return 'obj' as a string or unicode object. Tries to prevent
+ turning strings into unicode objects.
+ """
+ tp = type(obj)
+ if issubclass(tp, basestring):
+ return obj
+ elif hasattr(tp, '__unicode__'):
+ s = tp.__unicode__(obj)
+ if not isinstance(s, basestring):
+ raise TypeError, '__unicode__ did not return a string'
+ return s
+ elif hasattr(tp, '__str__'):
+ s = tp.__str__(obj)
+ if not isinstance(s, basestring):
+ raise TypeError, '__str__ did not return a string'
+ return s
+ else:
+ return str(obj)
+
+class htmltext(object):
+ """The htmltext string-like type. This type serves as a tag
+ signifying that HTML special characters do not need to be escaped
+ using entities.
+ """
+
+ __slots__ = ['s']
+
+ def __init__(self, s):
+ self.s = stringify(s)
+
+ # XXX make read-only
+ #def __setattr__(self, name, value):
+ # raise AttributeError, 'immutable object'
+
+ def __getstate__(self):
+ raise ValueError, 'htmltext objects should not be pickled'
+
+ def __repr__(self):
+ return '<htmltext %r>' % self.s
+
+ def __str__(self):
+ return self.s
+
+ def __len__(self):
+ return len(self.s)
+
+ def __cmp__(self, other):
+ return cmp(self.s, other)
+
+ def __hash__(self):
+ return hash(self.s)
+
+ def __mod__(self, args):
+ if isinstance(args, tuple):
+ return htmltext(self.s % tuple(map(_wraparg, args)))
+ else:
+ return htmltext(self.s % _wraparg(args))
+
+ def __add__(self, other):
+ if isinstance(other, basestring):
+ return htmltext(self.s + _escape_string(other))
+ elif isinstance(other, htmltext):
+ return htmltext(self.s + other.s)
+ else:
+ return NotImplemented
+
+ def __radd__(self, other):
+ if isinstance(other, basestring):
+ return htmltext(_escape_string(other) + self.s)
+ else:
+ return NotImplemented
+
+ def __mul__(self, n):
+ return htmltext(self.s * n)
+
+ def join(self, items):
+ quoted_items = []
+ for item in items:
+ if isinstance(item, htmltext):
+ quoted_items.append(stringify(item))
+ elif isinstance(item, basestring):
+ quoted_items.append(_escape_string(item))
+ else:
+ raise TypeError(
+ 'join() requires string arguments (got %r)' % item)
+ return htmltext(self.s.join(quoted_items))
+
+ def startswith(self, s):
+ if isinstance(s, htmltext):
+ s = s.s
+ else:
+ s = _escape_string(s)
+ return self.s.startswith(s)
+
+ def endswith(self, s):
+ if isinstance(s, htmltext):
+ s = s.s
+ else:
+ s = _escape_string(s)
+ return self.s.endswith(s)
+
+ def replace(self, old, new, maxsplit=-1):
+ if isinstance(old, htmltext):
+ old = old.s
+ else:
+ old = _escape_string(old)
+ if isinstance(new, htmltext):
+ new = new.s
+ else:
+ new = _escape_string(new)
+ return htmltext(self.s.replace(old, new))
+
+ def lower(self):
+ return htmltext(self.s.lower())
+
+ def upper(self):
+ return htmltext(self.s.upper())
+
+ def capitalize(self):
+ return htmltext(self.s.capitalize())
+
+class _QuoteWrapper(object):
+ # helper for htmltext class __mod__
+
+ __slots__ = ['value']
+
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return _escape_string(stringify(self.value))
+
+ def __repr__(self):
+ return _escape_string(`self.value`)
+
+ def __getitem__(self, key):
+ return _wraparg(self.value[key])
+
+
+def _wraparg(arg):
+ if (isinstance(arg, htmltext) or
+ isinstance(arg, int) or
+ isinstance(arg, long) or
+ isinstance(arg, float)):
+ # ints, longs, floats, and htmltext are okay
+ return arg
+ else:
+ # everything is gets wrapped
+ return _QuoteWrapper(arg)
+
+def htmlescape(s):
+ """htmlescape(s) -> htmltext
+
+ Return an 'htmltext' object using the argument. If the argument is not
+ already a 'htmltext' object then the HTML markup characters \", <, >,
+ and & are first escaped.
+ """
+ if isinstance(s, htmltext):
+ return s
+ else:
+ s = stringify(s)
+ # inline _escape_string for speed
+ s = s.replace("&", "&amp;") # must be done first
+ s = s.replace("<", "&lt;")
+ s = s.replace(">", "&gt;")
+ s = s.replace('"', "&quot;")
+ return htmltext(s)
+
+
+class TemplateIO(object):
+ """Collect output for PTL scripts.
+ """
+
+ __slots__ = ['html', 'data']
+
+ def __init__(self, html=False):
+ self.html = html
+ self.data = []
+
+ def __iadd__(self, other):
+ if other is not None:
+ self.data.append(other)
+ return self
+
+ def __repr__(self):
+ return ("<%s at %x: %d chunks>" %
+ (self.__class__.__name__, id(self), len(self.data)))
+
+ def __str__(self):
+ return stringify(self.getvalue())
+
+ def getvalue(self):
+ if self.html:
+ return htmltext('').join(map(htmlescape, self.data))
+ else:
+ return ''.join(map(stringify, self.data))
diff --git a/pypers/europython05/Quixote-2.0/html/test/utest_html.py b/pypers/europython05/Quixote-2.0/html/test/utest_html.py
new file mode 100755
index 0000000..fa5450f
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/html/test/utest_html.py
@@ -0,0 +1,368 @@
+import sys
+from sancho.utest import UTest
+from quixote.html import _py_htmltext
+from quixote.html import href, url_with_query, url_quote, nl2br, htmltag
+
+markupchars = '<>&"'
+quotedchars = '&lt;&gt;&amp;&quot;'
+if sys.hexversion >= 0x20400a2:
+ unicodechars = u'\u1234'
+else:
+ unicodechars = 'x' # lie, Python <= 2.3 is broken
+
+class Wrapper:
+ def __init__(self, s):
+ self.s = s
+
+ def __repr__(self):
+ return self.s
+
+ def __str__(self):
+ return self.s
+
+class BrokenError(Exception):
+ pass
+
+class Broken:
+ def __str__(self):
+ raise BrokenError, 'eieee'
+
+ def __repr__(self):
+ raise BrokenError, 'eieee'
+
+htmltext = escape = htmlescape = TemplateIO = stringify = None
+
+class HTMLTest (UTest):
+
+ def check_href(self):
+ assert str(href('/foo/bar', 'bar')) == '<a href="/foo/bar">bar</a>'
+
+ def check_url_with_query(self):
+ assert str(url_with_query('/f/b', a='1')) == '/f/b?a=1'
+ assert str(url_with_query(
+ '/f/b', a='1', b='3 4')) == '/f/b?a=1&amp;b=3%204'
+
+ def check_nl2br(self):
+ assert str(nl2br('a\nb\nc')) == 'a<br />\nb<br />\nc'
+
+ def check_url_quote(self):
+ assert url_quote('abc') == 'abc'
+ assert url_quote('a b c') == 'a%20b%20c'
+ assert url_quote(None, fallback='abc') == 'abc'
+
+
+class HTMLTextTest (UTest):
+
+ def _pre(self):
+ global htmltext, escape, htmlescape, TemplateIO, stringify
+ htmltext = _py_htmltext.htmltext
+ escape = _py_htmltext._escape_string
+ stringify = _py_htmltext.stringify
+ htmlescape = _py_htmltext.htmlescape
+ TemplateIO = _py_htmltext.TemplateIO
+
+ def _post(self):
+ global htmltext, escape, htmlescape, TemplateIO, stringify
+ htmltext = escape = htmlescape = TemplateIO = stringify = None
+
+ def _check_init(self):
+ assert str(htmltext('foo')) == 'foo'
+ assert str(htmltext(markupchars)) == markupchars
+ assert unicode(htmltext(unicodechars)) == unicodechars
+ assert str(htmltext(unicode(markupchars))) == markupchars
+ assert str(htmltext(None)) == 'None'
+ assert str(htmltext(1)) == '1'
+ try:
+ htmltext(Broken())
+ assert 0
+ except BrokenError: pass
+
+ def check_stringify(self):
+ assert stringify(markupchars) is markupchars
+ assert stringify(unicodechars) is unicodechars
+ assert stringify(Wrapper(unicodechars)) is unicodechars
+ assert stringify(Wrapper(markupchars)) is markupchars
+ assert stringify(Wrapper) == str(Wrapper)
+ assert stringify(None) == str(None)
+
+ def check_escape(self):
+ assert htmlescape(markupchars) == quotedchars
+ assert isinstance(htmlescape(markupchars), htmltext)
+ assert escape(markupchars) == quotedchars
+ assert escape(unicodechars) == unicodechars
+ assert escape(unicode(markupchars)) == quotedchars
+ assert isinstance(escape(markupchars), basestring)
+ assert htmlescape(htmlescape(markupchars)) == quotedchars
+ try:
+ escape(1)
+ assert 0
+ except TypeError: pass
+
+ def check_cmp(self):
+ s = htmltext("foo")
+ assert s == 'foo'
+ assert s != 'bar'
+ assert s == htmltext('foo')
+ assert s != htmltext('bar')
+ assert htmltext(u'\u1234') == u'\u1234'
+ assert htmltext('1') != 1
+ assert 1 != s
+
+ def check_len(self):
+ assert len(htmltext('foo')) == 3
+ assert len(htmltext(markupchars)) == len(markupchars)
+ assert len(htmlescape(markupchars)) == len(quotedchars)
+
+ def check_hash(self):
+ assert hash(htmltext('foo')) == hash('foo')
+ assert hash(htmltext(markupchars)) == hash(markupchars)
+ assert hash(htmlescape(markupchars)) == hash(quotedchars)
+
+ def check_concat(self):
+ s = htmltext("foo")
+ assert s + 'bar' == "foobar"
+ assert 'bar' + s == "barfoo"
+ assert s + htmltext('bar') == "foobar"
+ assert s + markupchars == "foo" + quotedchars
+ assert isinstance(s + markupchars, htmltext)
+ assert markupchars + s == quotedchars + "foo"
+ assert isinstance(markupchars + s, htmltext)
+ assert markupchars + htmltext(u'') == quotedchars
+ try:
+ s + 1
+ assert 0
+ except TypeError: pass
+ try:
+ 1 + s
+ assert 0
+ except TypeError: pass
+ # mixing unicode and str
+ assert repr(htmltext('a') + htmltext('b')) == "<htmltext 'ab'>"
+ assert repr(htmltext(u'a') + htmltext('b')) == "<htmltext u'ab'>"
+ assert repr(htmltext('a') + htmltext(u'b')) == "<htmltext u'ab'>"
+
+ def check_repeat(self):
+ s = htmltext('a')
+ assert s * 3 == "aaa"
+ assert isinstance(s * 3, htmltext)
+ assert htmlescape(markupchars) * 3 == quotedchars * 3
+ try:
+ s * 'a'
+ assert 0
+ except TypeError: pass
+ try:
+ 'a' * s
+ assert 0
+ except TypeError: pass
+ try:
+ s * s
+ assert 0
+ except TypeError: pass
+
+ def check_format(self):
+ s_fmt = htmltext('%s')
+ u_fmt = htmltext(u'%s')
+ assert s_fmt % 'foo' == "foo"
+ assert u_fmt % 'foo' == u"foo"
+ assert isinstance(s_fmt % 'foo', htmltext)
+ assert isinstance(u_fmt % 'foo', htmltext)
+ assert s_fmt % markupchars == quotedchars
+ assert u_fmt % markupchars == quotedchars
+ assert s_fmt % None == "None"
+ assert u_fmt % None == "None"
+ assert u_fmt % unicodechars == unicodechars
+ assert htmltext('%r') % Wrapper(markupchars) == quotedchars
+ assert htmltext('%s%s') % ('foo', htmltext(markupchars)) \
+ == ("foo" + markupchars)
+ assert htmltext('%d') % 10 == "10"
+ assert htmltext('%.1f') % 10 == "10.0"
+ try:
+ s_fmt % Broken()
+ assert 0
+ except BrokenError: pass
+ try:
+ htmltext('%r') % Broken()
+ assert 0
+ except BrokenError: pass
+ try:
+ s_fmt % (1, 2)
+ assert 0
+ except TypeError: pass
+ assert htmltext('%d') % 12300000000000000000L == "12300000000000000000"
+
+ def check_dict_format(self):
+ args = {'a': 'foo&', 'b': htmltext('bar&')}
+ result = "foo&amp; 'foo&amp;' bar&"
+ assert htmltext('%(a)s %(a)r %(b)s') % args == result
+ assert htmltext('%(a)s') % {'a': 'foo&'} == "foo&amp;"
+ assert isinstance(htmltext('%(a)s') % {'a': 'a'}, htmltext)
+ assert htmltext('%s') % {'a': 'foo&'} == "{'a': 'foo&amp;'}"
+ try:
+ htmltext('%(a)s') % 1
+ assert 0
+ except TypeError: pass
+ try:
+ htmltext('%(a)s') % {}
+ assert 0
+ except KeyError: pass
+ assert htmltext('') % {} == ''
+ assert htmltext('%%') % {} == '%'
+
+ def check_join(self):
+ assert htmltext(' ').join(['foo', 'bar']) == "foo bar"
+ assert htmltext(' ').join(['foo', markupchars]) == \
+ "foo " + quotedchars
+ assert htmlescape(markupchars).join(['foo', 'bar']) == \
+ "foo" + quotedchars + "bar"
+ assert htmltext(' ').join([htmltext(markupchars), 'bar']) == \
+ markupchars + " bar"
+ assert isinstance(htmltext('').join([]), htmltext)
+ assert htmltext(u' ').join([unicodechars]) == unicodechars
+ assert htmltext(u' ').join(['']) == u''
+ try:
+ htmltext('').join(1)
+ assert 0
+ except TypeError: pass
+ try:
+ htmltext('').join([1])
+ assert 0
+ except TypeError: pass
+
+ def check_startswith(self):
+ assert htmltext('foo').startswith('fo')
+ assert htmlescape(markupchars).startswith(markupchars[:3])
+ assert htmltext(markupchars).startswith(htmltext(markupchars[:3]))
+ try:
+ htmltext('').startswith(1)
+ assert 0
+ except TypeError: pass
+
+ def check_endswith(self):
+ assert htmltext('foo').endswith('oo')
+ assert htmlescape(markupchars).endswith(markupchars[-3:])
+ assert htmltext(markupchars).endswith(htmltext(markupchars[-3:]))
+ try:
+ htmltext('').endswith(1)
+ assert 0
+ except TypeError: pass
+
+ def check_replace(self):
+ assert htmlescape('&').replace('&', 'foo') == "foo"
+ assert htmltext('&').replace(htmltext('&'), 'foo') == "foo"
+ assert htmltext('foo').replace('foo', htmltext('&')) == "&"
+ assert isinstance(htmltext('a').replace('a', 'b'), htmltext)
+ try:
+ htmltext('').replace(1, 'a')
+ assert 0
+ except TypeError: pass
+
+ def check_lower(self):
+ assert htmltext('aB').lower() == "ab"
+ assert isinstance(htmltext('a').lower(), htmltext)
+
+ def check_upper(self):
+ assert htmltext('aB').upper() == "AB"
+ assert isinstance(htmltext('a').upper(), htmltext)
+
+ def check_capitalize(self):
+ assert htmltext('aB').capitalize() == "Ab"
+ assert isinstance(htmltext('a').capitalize(), htmltext)
+
+class TemplateTest (UTest):
+
+ def _pre(self):
+ global TemplateIO
+ TemplateIO = _py_htmltext.TemplateIO
+
+ def _post(self):
+ global TemplateIO
+ TemplateIO = None
+
+ def check_init(self):
+ TemplateIO()
+ TemplateIO(html=True)
+ TemplateIO(html=False)
+
+ def check_text_iadd(self):
+ t = TemplateIO()
+ assert t.getvalue() == ''
+ t += "abcd"
+ assert t.getvalue() == 'abcd'
+ t += None
+ assert t.getvalue() == 'abcd'
+ t += 123
+ assert t.getvalue() == 'abcd123'
+ t += u'\u1234'
+ assert t.getvalue() == u'abcd123\u1234'
+ try:
+ t += Broken(); t.getvalue()
+ assert 0
+ except BrokenError: pass
+
+ def check_html_iadd(self):
+ t = TemplateIO(html=1)
+ assert t.getvalue() == ''
+ t += "abcd"
+ assert t.getvalue() == 'abcd'
+ t += None
+ assert t.getvalue() == 'abcd'
+ t += 123
+ assert t.getvalue() == 'abcd123'
+ try:
+ t += Broken(); t.getvalue()
+ assert 0
+ except BrokenError: pass
+ t = TemplateIO(html=1)
+ t += markupchars
+ assert t.getvalue() == quotedchars
+
+ def check_repr(self):
+ t = TemplateIO()
+ t += "abcd"
+ assert "TemplateIO" in repr(t)
+
+ def check_str(self):
+ t = TemplateIO()
+ t += "abcd"
+ assert str(t) == "abcd"
+
+
+
+try:
+ from quixote.html import _c_htmltext
+except ImportError:
+ _c_htmltext = None
+
+if _c_htmltext:
+ class CHTMLTest(HTMLTest):
+ def _pre(self):
+ # using globals like this is a bit of a hack since it assumes
+ # Sancho tests each class individually, oh well
+ global htmltext, escape, htmlescape, stringify
+ htmltext = _c_htmltext.htmltext
+ escape = _c_htmltext._escape_string
+ stringify = _py_htmltext.stringify
+ htmlescape = _c_htmltext.htmlescape
+
+ class CHTMLTextTest(HTMLTextTest):
+ def _pre(self):
+ global htmltext, escape, htmlescape, stringify
+ htmltext = _c_htmltext.htmltext
+ escape = _c_htmltext._escape_string
+ stringify = _py_htmltext.stringify
+ htmlescape = _c_htmltext.htmlescape
+
+ class CTemplateTest(TemplateTest):
+ def _pre(self):
+ global TemplateIO
+ TemplateIO = _c_htmltext.TemplateIO
+
+
+if __name__ == "__main__":
+ HTMLTest()
+ HTMLTextTest()
+ TemplateTest()
+ if _c_htmltext:
+ CHTMLTest()
+ CHTMLTextTest()
+ CTemplateTest()
diff --git a/pypers/europython05/Quixote-2.0/http_request.py b/pypers/europython05/Quixote-2.0/http_request.py
new file mode 100755
index 0000000..6a9602d
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/http_request.py
@@ -0,0 +1,759 @@
+"""quixote.http_request
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/http_request.py $
+$Id: http_request.py 26337 2005-03-11 17:06:05Z dbinger $
+
+Provides the HTTPRequest class and related code for parsing HTTP
+requests, such as the Upload class.
+"""
+
+import re
+import string
+import tempfile
+import urllib
+import rfc822
+from cStringIO import StringIO
+
+from quixote.http_response import HTTPResponse
+from quixote.errors import RequestError
+
+
+# Various regexes for parsing specific bits of HTTP, all from RFC 2616.
+
+# These are needed by 'get_encoding()', to parse the "Accept-Encoding"
+# header. LWS is linear whitespace; the latter two assume that LWS
+# has been removed.
+_http_lws_re = re.compile(r"(\r\n)?[ \t]+")
+_http_list_re = re.compile(r",+")
+_http_encoding_re = re.compile(r"([^;]+)(;q=([\d.]+))?$")
+
+# These are needed by 'guess_browser_version()', for parsing the
+# "User-Agent" header.
+# token = 1*<any CHAR except CTLs or separators>
+# CHAR = any 7-bit US ASCII character (0-127)
+# separators are ( ) < > @ , ; : \ " / [ ] ? = { }
+#
+# The user_agent RE is a simplification; it only looks for one "product",
+# possibly followed by a comment.
+_http_token_pat = r"[\w!#$%&'*+.^`|~-]+"
+_http_product_pat = r'(%s)(?:/(%s))?' % (_http_token_pat, _http_token_pat)
+_http_product_re = re.compile(_http_product_pat)
+_comment_delim_re = re.compile(r';\s*')
+
+
+def get_content_type(environ):
+ ctype = environ.get("CONTENT_TYPE")
+ if ctype:
+ return ctype.split(";")[0]
+ else:
+ return None
+
+def _decode_string(s, charset):
+ if charset == 'iso-8859-1':
+ return s
+ try:
+ return s.decode(charset)
+ except LookupError:
+ raise RequestError('unknown charset %r' % charset)
+ except UnicodeDecodeError:
+ raise RequestError('invalid %r encoded string' % charset)
+
+def parse_header(line):
+ """Parse a Content-type like header.
+
+ Return the main content-type and a dictionary of options.
+
+ """
+ plist = map(lambda x: x.strip(), line.split(';'))
+ key = plist.pop(0).lower()
+ pdict = {}
+ for p in plist:
+ i = p.find('=')
+ if i >= 0:
+ name = p[:i].strip().lower()
+ value = p[i+1:].strip()
+ if len(value) >= 2 and value[0] == value[-1] == '"':
+ value = value[1:-1]
+ pdict[name] = value
+ return key, pdict
+
+def parse_content_disposition(full_cdisp):
+ (cdisp, cdisp_params) = parse_header(full_cdisp)
+ name = cdisp_params.get('name')
+ if not (cdisp == 'form-data' and name):
+ raise RequestError('expected Content-Disposition: form-data '
+ 'with a "name" parameter: got %r' % full_cdisp)
+ return (name, cdisp_params.get('filename'))
+
+def parse_query(qs, charset):
+ """(qs: string) -> {key:string, string|[string]}
+
+ Parse a query given as a string argument and return a dictionary.
+ """
+ fields = {}
+ for chunk in filter(None, qs.split('&')):
+ if '=' not in chunk:
+ name = chunk
+ value = ''
+ else:
+ name, value = chunk.split('=', 1)
+ name = urllib.unquote(name.replace('+', ' '))
+ value = urllib.unquote(value.replace('+', ' '))
+ name = _decode_string(name, charset)
+ value = _decode_string(value, charset)
+ _add_field_value(fields, name, value)
+ return fields
+
+def _add_field_value(fields, name, value):
+ if name in fields:
+ values = fields[name]
+ if not isinstance(values, list):
+ fields[name] = values = [values]
+ values.append(value)
+ else:
+ fields[name] = value
+
+
+class HTTPRequest:
+ """
+ Model a single HTTP request and all associated data: environment
+ variables, form variables, cookies, etc.
+
+ To access environment variables associated with the request, use
+ get_environ(): eg. request.get_environ('SERVER_PORT', 80).
+
+ To access form variables, use get_field(), eg.
+ request.get_field("name").
+
+ To access cookies, use get_cookie().
+
+ Various bits and pieces of the requested URL can be accessed with
+ get_url(), get_path(), get_server()
+
+ The HTTPResponse object corresponding to this request is available
+ in the 'response' attribute. This is rarely needed: eg. to send an
+ error response, you should raise one of the exceptions in errors.py;
+ to send a redirect, you should use the request's redirect() method,
+ which lets you specify relative URLs. However, if you need to tweak
+ the response object in other ways, you can do so via 'response'.
+ Just keep in mind that Quixote discards the original response object
+ when handling an exception.
+ """
+
+ DEFAULT_CHARSET = 'iso-8859-1'
+
+ def __init__(self, stdin, environ):
+ self.stdin = stdin
+ self.environ = environ
+ self.form = {}
+ self.session = None
+ self.response = HTTPResponse()
+
+ # The strange treatment of SERVER_PORT_SECURE is because IIS
+ # sets this environment variable to "0" for non-SSL requests
+ # (most web servers -- well, Apache at least -- simply don't set
+ # it in that case).
+ if (environ.get('HTTPS', 'off').lower() == 'on' or
+ environ.get('SERVER_PORT_SECURE', '0') != '0'):
+ self.scheme = "https"
+ else:
+ self.scheme = "http"
+
+ k = self.environ.get('HTTP_COOKIE', '')
+ if k:
+ self.cookies = parse_cookies(k)
+ else:
+ self.cookies = {}
+
+ # IIS breaks PATH_INFO because it leaves in the path to
+ # the script, so SCRIPT_NAME is "/cgi-bin/q.py" and PATH_INFO
+ # is "/cgi-bin/q.py/foo/bar". The following code fixes
+ # PATH_INFO to the expected value "/foo/bar".
+ web_server = environ.get('SERVER_SOFTWARE', 'unknown')
+ if web_server.find('Microsoft-IIS') != -1:
+ script = environ['SCRIPT_NAME']
+ path = environ['PATH_INFO']
+ if path.startswith(script):
+ path = path[len(script):]
+ self.environ['PATH_INFO'] = path
+
+ def process_inputs(self):
+ query = self.get_query()
+ if query:
+ self.form.update(parse_query(query, self.DEFAULT_CHARSET))
+ length = self.environ.get('CONTENT_LENGTH') or "0"
+ try:
+ length = int(length)
+ except ValueError:
+ raise RequestError('invalid content-length header')
+ ctype = self.environ.get("CONTENT_TYPE")
+ if ctype:
+ ctype, ctype_params = parse_header(ctype)
+ if ctype == 'application/x-www-form-urlencoded':
+ self._process_urlencoded(length, ctype_params)
+ elif ctype == 'multipart/form-data':
+ self._process_multipart(length, ctype_params)
+
+ def _process_urlencoded(self, length, params):
+ query = self.stdin.read(length)
+ if len(query) != length:
+ raise RequestError('unexpected end of request body')
+ charset = params.get('charset', self.DEFAULT_CHARSET)
+ self.form.update(parse_query(query, charset))
+
+ def _process_multipart(self, length, params):
+ boundary = params.get('boundary')
+ if not boundary:
+ raise RequestError('multipart/form-data missing boundary')
+ charset = params.get('charset')
+ mimeinput = MIMEInput(self.stdin, boundary, length)
+ try:
+ for line in mimeinput.readpart():
+ pass # discard lines up to first boundary
+ while mimeinput.moreparts():
+ self._process_multipart_body(mimeinput, charset)
+ except EOFError:
+ raise RequestError('unexpected end of multipart/form-data')
+
+ def _process_multipart_body(self, mimeinput, charset):
+ headers = StringIO()
+ lines = mimeinput.readpart()
+ for line in lines:
+ headers.write(line)
+ if line == '\r\n':
+ break
+ headers.seek(0)
+ headers = rfc822.Message(headers)
+ ctype, ctype_params = parse_header(headers.get('content-type', ''))
+ if ctype and 'charset' in ctype_params:
+ charset = ctype_params['charset']
+ cdisp, cdisp_params = parse_header(headers.get('content-disposition',
+ ''))
+ if not cdisp:
+ raise RequestError('expected Content-Disposition header')
+ name = cdisp_params.get('name')
+ filename = cdisp_params.get('filename')
+ if not (cdisp == 'form-data' and name):
+ raise RequestError('expected Content-Disposition: form-data'
+ 'with a "name" parameter: got %r' %
+ headers.get('content-disposition', ''))
+ # FIXME: should really to handle Content-Transfer-Encoding and other
+ # MIME complexity here. See RFC2048 for the full horror story.
+ if filename:
+ # it might be large file upload so use a temporary file
+ upload = Upload(filename, ctype, charset)
+ upload.receive(lines)
+ _add_field_value(self.form, name, upload)
+ else:
+ value = _decode_string(''.join(lines),
+ charset or self.DEFAULT_CHARSET)
+ _add_field_value(self.form, name, value)
+
+ def get_header(self, name, default=None):
+ """get_header(name : string, default : string = None) -> string
+
+ Return the named HTTP header, or an optional default argument
+ (or None) if the header is not found. Note that both original
+ and CGI-ified header names are recognized, e.g. 'Content-Type',
+ 'CONTENT_TYPE' and 'HTTP_CONTENT_TYPE' should all return the
+ Content-Type header, if available.
+ """
+ environ = self.environ
+ name = name.replace("-", "_").upper()
+ val = environ.get(name)
+ if val is not None:
+ return val
+ if name[:5] != 'HTTP_':
+ name = 'HTTP_' + name
+ return environ.get(name, default)
+
+ def get_cookie(self, cookie_name, default=None):
+ return self.cookies.get(cookie_name, default)
+
+ def get_cookies(self):
+ return self.cookies
+
+ def get_field(self, name, default=None):
+ return self.form.get(name, default)
+
+ def get_fields(self):
+ return self.form
+
+ def get_method(self):
+ """Returns the HTTP method for this request
+ """
+ return self.environ.get('REQUEST_METHOD', 'GET')
+
+ def formiter(self):
+ return self.form.iteritems()
+
+ def get_scheme(self):
+ return self.scheme
+
+ # The following environment variables are useful for reconstructing
+ # the original URL, all of which are specified by CGI 1.1:
+ #
+ # SERVER_NAME "www.example.com"
+ # SCRIPT_NAME "/q"
+ # PATH_INFO "/debug/dump_sessions"
+ # QUERY_STRING "session_id=10.27.8.40...."
+
+ def get_server(self):
+ """get_server() -> string
+
+ Return the server name with an optional port number, eg.
+ "www.example.com" or "foo.bar.com:8000".
+ """
+ http_host = self.environ.get("HTTP_HOST")
+ if http_host:
+ return http_host
+ server_name = self.environ["SERVER_NAME"].strip()
+ server_port = self.environ.get("SERVER_PORT")
+ if (not server_port or
+ (self.get_scheme() == "http" and server_port == "80") or
+ (self.get_scheme() == "https" and server_port == "443")):
+ return server_name
+ else:
+ return server_name + ":" + server_port
+
+ def get_path(self, n=0):
+ """get_path(n : int = 0) -> string
+
+ Return the path of the current request, chopping off 'n' path
+ components from the right. Eg. if the path is "/bar/baz/qux",
+ n=0 would return "/bar/baz/qux" and n=2 would return "/bar".
+ Note that the query string, if any, is not included.
+
+ A path with a trailing slash should just be considered as having
+ an empty last component. Eg. if the path is "/bar/baz/", then:
+ get_path(0) == "/bar/baz/"
+ get_path(1) == "/bar/baz"
+ get_path(2) == "/bar"
+
+ If 'n' is negative, then components from the left of the path
+ are returned. Continuing the above example,
+ get_path(-1) = "/bar"
+ get_path(-2) = "/bar/baz"
+ get_path(-3) = "/bar/baz/"
+
+ Raises ValueError if absolute value of n is larger than the number of
+ path components."""
+
+ path_info = self.environ.get('PATH_INFO', '')
+ path = self.environ['SCRIPT_NAME'] + path_info
+ if n == 0:
+ return path
+ else:
+ path_comps = path.split('/')
+ if abs(n) > len(path_comps)-1:
+ raise ValueError, "n=%d too big for path '%s'" % (n, path)
+ if n > 0:
+ return '/'.join(path_comps[:-n])
+ elif n < 0:
+ return '/'.join(path_comps[:-n+1])
+ else:
+ assert 0, "Unexpected value for n (%s)" % n
+
+ def get_query(self):
+ """() -> string
+
+ Return the query component of the URL.
+ """
+ return self.environ.get('QUERY_STRING', '')
+
+ def get_url(self, n=0):
+ """get_url(n : int = 0) -> string
+
+ Return the URL of the current request, chopping off 'n' path
+ components from the right. Eg. if the URL is
+ "http://foo.com/bar/baz/qux", n=2 would return
+ "http://foo.com/bar". Does not include the query string (if
+ any).
+ """
+ return "%s://%s%s" % (self.get_scheme(), self.get_server(),
+ urllib.quote(self.get_path(n)))
+
+ def get_environ(self, key, default=None):
+ """get_environ(key : string) -> string
+
+ Fetch a CGI environment variable from the request environment.
+ See http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ for the variables specified by the CGI standard.
+ """
+ return self.environ.get(key, default)
+
+ def get_encoding(self, encodings):
+ """get_encoding(encodings : [string]) -> string
+
+ Parse the "Accept-encoding" header. 'encodings' is a list of
+ encodings supported by the server sorted in order of preference.
+ The return value is one of 'encodings' or None if the client
+ does not accept any of the encodings.
+ """
+ accept_encoding = self.get_header("accept-encoding") or ""
+ found_encodings = self._parse_pref_header(accept_encoding)
+ if found_encodings:
+ for encoding in encodings:
+ if found_encodings.has_key(encoding):
+ return encoding
+ return None
+
+ def get_accepted_types(self):
+ """get_accepted_types() : {string:float}
+ Return a dictionary mapping MIME types the client will accept
+ to the corresponding quality value (1.0 if no value was specified).
+ """
+ accept_types = self.environ.get('HTTP_ACCEPT', "")
+ return self._parse_pref_header(accept_types)
+
+
+ def _parse_pref_header(self, S):
+ """_parse_pref_header(S:string) : {string:float}
+ Parse a list of HTTP preferences (content types, encodings) and
+ return a dictionary mapping strings to the quality value.
+ """
+
+ found = {}
+ # remove all linear whitespace
+ S = _http_lws_re.sub("", S)
+ for coding in _http_list_re.split(S):
+ m = _http_encoding_re.match(coding)
+ if m:
+ encoding = m.group(1).lower()
+ q = m.group(3) or 1.0
+ try:
+ q = float(q)
+ except ValueError:
+ continue
+ if encoding == "*":
+ continue # stupid, ignore it
+ if q > 0:
+ found[encoding] = q
+ return found
+
+ def dump(self):
+ result=[]
+ row='%-15s %s'
+
+ result.append("Form:")
+ L = self.form.items() ; L.sort()
+ for k,v in L:
+ result.append(row % (k,v))
+
+ result.append("")
+ result.append("Cookies:")
+ L = self.cookies.items() ; L.sort()
+ for k,v in L:
+ result.append(row % (k,v))
+
+
+ result.append("")
+ result.append("Environment:")
+ L = self.environ.items() ; L.sort()
+ for k,v in L:
+ result.append(row % (k,v))
+ return "\n".join(result)
+
+ def guess_browser_version(self):
+ """guess_browser_version() -> (name : string, version : string)
+
+ Examine the User-agent request header to try to figure out what
+ the current browser is. Returns either (name, version) where
+ each element is a string, (None, None) if we couldn't parse the
+ User-agent header at all, or (name, None) if we got the name but
+ couldn't figure out the version.
+
+ Handles Microsoft's little joke of pretending to be Mozilla,
+ eg. if the "User-Agent" header is
+ Mozilla/5.0 (compatible; MSIE 5.5)
+ returns ("MSIE", "5.5"). Konqueror does the same thing, and
+ it's handled the same way.
+ """
+ ua = self.get_header('user-agent')
+ if ua is None:
+ return (None, None)
+
+ # The syntax for "User-Agent" in RFC 2616 is fairly simple:
+ #
+ # User-Agent = "User-Agent" ":" 1*( product | comment )
+ # product = token ["/" product-version ]
+ # product-version = token
+ # comment = "(" *( ctext | comment ) ")"
+ # ctext = <any TEXT excluding "(" and ")">
+ # token = 1*<any CHAR except CTLs or tspecials>
+ # tspecials = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" |
+ # "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" |
+ # "}" | SP | HT
+ #
+ # This function handles the most-commonly-used subset of this syntax,
+ # namely
+ # User-Agent = "User-Agent" ":" product 1*SP [comment]
+ # ie. one product string followed by an optional comment;
+ # anything after that first comment is ignored. This should be
+ # enough to distinguish Mozilla/Netscape, MSIE, Opera, and
+ # Konqueror.
+
+ m = _http_product_re.match(ua)
+ if not m:
+ import sys
+ sys.stderr.write("couldn't parse User-Agent header: %r\n" % ua)
+ return (None, None)
+
+ name, version = m.groups()
+ ua = ua[m.end():].lstrip()
+
+ if ua.startswith('('):
+ # we need to handle nested comments since MSIE uses them
+ depth = 1
+ chars = []
+ for c in ua[1:]:
+ if c == '(':
+ depth += 1
+ elif c == ')':
+ depth -= 1
+ if depth == 0:
+ break
+ elif depth == 1:
+ # nested comments are discarded
+ chars.append(c)
+ comment = ''.join(chars)
+ else:
+ comment = ''
+ if comment:
+ comment_chunks = _comment_delim_re.split(comment)
+ else:
+ comment_chunks = []
+
+ if ("compatible" in comment_chunks and
+ len(comment_chunks) > 1 and comment_chunks[1]):
+ # A-ha! Someone is kidding around, pretending to be what
+ # they are not. Most likely MSIE masquerading as Mozilla,
+ # but lots of other clients (eg. Konqueror) do the same.
+ real_ua = comment_chunks[1]
+ if "/" in real_ua:
+ (name, version) = real_ua.split("/", 1)
+ else:
+ if real_ua.startswith("MSIE") and ' ' in real_ua:
+ (name, version) = real_ua.split(" ", 1)
+ else:
+ name = real_ua
+ version = None
+ return (name, version)
+
+ # Either nobody is pulling our leg, or we didn't find anything
+ # that looks vaguely like a user agent in the comment. So use
+ # what we found outside the comment, ie. what the spec says we
+ # should use (sigh).
+ return (name, version)
+
+ # guess_browser_version ()
+
+
+# See RFC 2109 for details. Note that this parser is more liberal.
+_COOKIE_RE = re.compile(r"""
+ \s*
+ (?P<name>[^=;,\s]+)
+ \s*
+ (
+ =
+ \s*
+ (
+ (?P<qvalue> "(\\[\x00-\x7f] | [^"])*")
+ |
+ (?P<value> [^";,\s]*)
+ )
+ )?
+ \s*
+ [;,]?
+ """, re.VERBOSE)
+
+def parse_cookies(text):
+ result = {}
+ for m in _COOKIE_RE.finditer(text):
+ name = m.group('name')
+ if name[0] == '$':
+ # discard, we don't handle per cookie attributes (e.g. $Path)
+ continue
+ qvalue = m.group('qvalue')
+ if qvalue:
+ value = re.sub(r'\\(.)', r'\1', qvalue)[1:-1]
+ else:
+ value = m.group('value') or ''
+ result[name] = value
+ return result
+
+SAFE_CHARS = string.letters + string.digits + "-@&+=_., "
+_safe_trans = None
+
+def make_safe_filename(s):
+ global _safe_trans
+ if _safe_trans is None:
+ _safe_trans = ["_"] * 256
+ for c in SAFE_CHARS:
+ _safe_trans[ord(c)] = c
+ _safe_trans = "".join(_safe_trans)
+
+ return s.translate(_safe_trans)
+
+
+class Upload:
+ r"""
+ Represents a single uploaded file. Uploaded files live in the
+ filesystem, *not* in memory.
+
+ fp
+ an open file containing the content of the upload. The file pointer
+ points to the beginning of the file
+ orig_filename
+ the complete filename supplied by the user-agent in the
+ request that uploaded this file. Depending on the browser,
+ this might have the complete path of the original file
+ on the client system, in the client system's syntax -- eg.
+ "C:\foo\bar\upload_this" or "/foo/bar/upload_this" or
+ "foo:bar:upload_this".
+ base_filename
+ the base component of orig_filename, shorn of MS-DOS,
+ Mac OS, and Unix path components and with "unsafe"
+ characters neutralized (see make_safe_filename())
+ content_type
+ the content type provided by the user-agent in the request
+ that uploaded this file.
+ charset
+ the charset provide by the user-agent
+ """
+
+ def __init__(self, orig_filename, content_type=None, charset=None):
+ if orig_filename:
+ self.orig_filename = orig_filename
+ bspos = orig_filename.rfind("\\")
+ cpos = orig_filename.rfind(":")
+ spos = orig_filename.rfind("/")
+ if bspos != -1: # eg. "\foo\bar" or "D:\ding\dong"
+ filename = orig_filename[bspos+1:]
+ elif cpos != -1: # eg. "C:foo" or ":ding:dong:foo"
+ filename = orig_filename[cpos+1:]
+ elif spos != -1: # eg. "foo/bar/baz" or "/tmp/blah"
+ filename = orig_filename[spos+1:]
+ else:
+ filename = orig_filename
+
+ self.base_filename = make_safe_filename(filename)
+ else:
+ self.orig_filename = None
+ self.base_filename = None
+ self.content_type = content_type
+ self.charset = charset
+ self.fp = None
+
+ def receive(self, lines):
+ self.fp = tempfile.TemporaryFile("w+b")
+ for line in lines:
+ self.fp.write(line)
+ self.fp.seek(0)
+
+ def __str__(self):
+ return str(self.orig_filename)
+
+ def __repr__(self):
+ return "<%s at %x: %s>" % (self.__class__.__name__, id(self), self)
+
+ def read(self, n):
+ return self.fp.read(n)
+
+ def readline(self):
+ return self.fp.readlines()
+
+ def __iter__(self):
+ return iter(self.fp)
+
+ def close(self):
+ self.fp.close()
+
+
+class LineInput:
+ r"""
+ A wrapper for an input stream that has the following properties:
+
+ * lines are terminated by \r\n
+
+ * lines shorter than 'maxlength' are always returned unbroken
+
+ * lines longer than 'maxlength' are broken but the pair of
+ characters \r\n are never split
+
+ * no more than 'length' characters are read from the underlying
+ stream
+
+ * if the underlying stream does not produce at least 'length'
+ characters then EOFError is raised
+
+ """
+ def __init__(self, fp, length):
+ self.fp = fp
+ self.length = length
+ self.buf = ''
+
+ def readline(self, maxlength=4096):
+ # fill buffer
+ n = min(self.length, maxlength - len(self.buf))
+ if n > 0:
+ self.length -= n
+ assert self.length >= 0
+ chunk = self.fp.read(n)
+ if len(chunk) != n:
+ raise EOFError('unexpected end of input')
+ self.buf += chunk
+ # split into lines
+ buf = self.buf
+ i = buf.find('\r\n')
+ if i >= 0:
+ i += 2
+ self.buf = buf[i:]
+ return buf[:i]
+ elif buf.endswith('\r'):
+ # avoid splitting CR LF pairs
+ self.buf = '\r'
+ return buf[:-1]
+ else:
+ self.buf = ''
+ return buf
+
+class MIMEInput:
+ """
+ Split a MIME input stream into parts. Note that this class does not
+ handle headers, transfer encoding, etc.
+ """
+
+ def __init__(self, fp, boundary, length):
+ self.lineinput = LineInput(fp, length)
+ self.pat = re.compile(r'--%s(--)?[ \t]*\r\n' % re.escape(boundary))
+ self.done = False
+
+ def moreparts(self):
+ """Return true if there are more parts to be read."""
+ return not self.done
+
+ def readpart(self):
+ """Generate all the lines up to a MIME boundary. Note that you
+ must exhaust the generator before calling this function again."""
+ assert not self.done
+ last_line = ''
+ while 1:
+ line = self.lineinput.readline()
+ if not line:
+ # Hit EOF -- nothing more to read. This should *not* happen
+ # in a well-formed MIME message.
+ raise EOFError('MIME boundary not found (end of input)')
+ if last_line.endswith('\r\n') or last_line == '':
+ m = self.pat.match(line)
+ if m:
+ # If we hit the boundary line, return now. Forget
+ # the current line *and* the CRLF ending of the
+ # previous line.
+ if m.group(1):
+ # hit final boundary
+ self.done = True
+ yield last_line[:-2]
+ return
+ if last_line:
+ yield last_line
+ last_line = line
diff --git a/pypers/europython05/Quixote-2.0/http_response.py b/pypers/europython05/Quixote-2.0/http_response.py
new file mode 100755
index 0000000..435b9e8
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/http_response.py
@@ -0,0 +1,475 @@
+"""quixote.http_response
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/http_response.py $
+$Id: http_response.py 26251 2005-02-25 16:17:06Z dbinger $
+
+Provides the HTTPResponse class.
+"""
+
+import time
+from sets import Set
+try:
+ import zlib
+except ImportError:
+ pass
+import struct
+from rfc822 import formatdate
+from quixote.html import stringify
+
+status_reasons = {
+ 100: 'Continue',
+ 101: 'Switching Protocols',
+ 102: 'Processing',
+ 200: 'OK',
+ 201: 'Created',
+ 202: 'Accepted',
+ 203: 'Non-Authoritative Information',
+ 204: 'No Content',
+ 205: 'Reset Content',
+ 206: 'Partial Content',
+ 207: 'Multi-Status',
+ 300: 'Multiple Choices',
+ 301: 'Moved Permanently',
+ 302: 'Moved Temporarily',
+ 303: 'See Other',
+ 304: 'Not Modified',
+ 305: 'Use Proxy',
+ 307: 'Temporary Redirect',
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 402: 'Payment Required',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 405: 'Method Not Allowed',
+ 406: 'Not Acceptable',
+ 407: 'Proxy Authentication Required',
+ 408: 'Request Time-out',
+ 409: 'Conflict',
+ 410: 'Gone',
+ 411: 'Length Required',
+ 412: 'Precondition Failed',
+ 413: 'Request Entity Too Large',
+ 414: 'Request-URI Too Large',
+ 415: 'Unsupported Media Type',
+ 416: 'Requested range not satisfiable',
+ 417: 'Expectation Failed',
+ 422: 'Unprocessable Entity',
+ 423: 'Locked',
+ 424: 'Failed Dependency',
+ 500: 'Internal Server Error',
+ 501: 'Not Implemented',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Time-out',
+ 505: 'HTTP Version not supported',
+ 507: 'Insufficient Storage',
+}
+
+_GZIP_HEADER = ("\037\213" # magic
+ "\010" # compression method
+ "\000" # flags
+ "\000\000\000\000" # time, who cares?
+ "\002"
+ "\377")
+
+_GZIP_EXCLUDE = Set(["application/pdf",
+ "application/zip",
+ "audio/mpeg",
+ "image/gif",
+ "image/jpeg",
+ "image/png",
+ "video/mpeg",
+ "video/quicktime",
+ "video/x-msvideo",
+ ])
+
+class HTTPResponse:
+ """
+ An object representation of an HTTP response.
+
+ The Response type encapsulates all possible responses to HTTP
+ requests. Responses are normally created by the Quixote publisher
+ or by the HTTPRequest class (every request must have a response,
+ after all).
+
+ Instance attributes:
+ content_type : string
+ the MIME content type of the response (does not include extra params
+ like charset)
+ charset : string
+ the character encoding of the the response
+ status_code : int
+ HTTP response status code (integer between 100 and 599)
+ reason_phrase : string
+ the reason phrase that accompanies status_code (usually
+ set automatically by the set_status() method)
+ headers : { string : string }
+ most of the headers included with the response; every header set
+ by 'set_header()' goes here. Does not include "Status" or
+ "Set-Cookie" headers (unless someone uses set_header() to set
+ them, but that would be foolish).
+ body : str | Stream
+ the response body, None by default. Note that if the body is not a
+ stream then it is already encoded using 'charset'.
+ buffered : bool
+ if false, response data will be flushed as soon as it is
+ written (the default is true). This is most useful for
+ responses that use the Stream() protocol. Note that whether the
+ client actually receives the partial response data is highly
+ dependent on the web server
+ cookies : { name:string : { attrname : value } }
+ collection of cookies to set in this response; it is expected
+ that the user-agent will remember the cookies and send them on
+ future requests. The cookie value is stored as the "value"
+ attribute. The other attributes are as specified by RFC 2109.
+ cache : int | None
+ the number of seconds the response may be cached. The default is 0,
+ meaning don't cache at all. This variable is used to set the HTTP
+ expires header. If set to None then the expires header will not be
+ added.
+ javascript_code : { string : string }
+ a collection of snippets of JavaScript code to be included in
+ the response. The collection is built by calling add_javascript(),
+ but actually including the code in the HTML document is somebody
+ else's problem.
+ """
+
+ DEFAULT_CONTENT_TYPE = 'text/html'
+ DEFAULT_CHARSET = 'iso-8859-1'
+
+ def __init__(self, status=200, body=None, content_type=None, charset=None):
+ """
+ Creates a new HTTP response.
+ """
+ self.content_type = content_type or self.DEFAULT_CONTENT_TYPE
+ self.charset = charset or self.DEFAULT_CHARSET
+ self.set_status(status)
+ self.headers = {}
+
+ if body is not None:
+ self.set_body(body)
+ else:
+ self.body = None
+
+ self.cookies = {}
+ self.cache = 0
+ self.buffered = True
+ self.javascript_code = None
+
+ def set_content_type(self, content_type, charset='iso-8859-1'):
+ """(content_type : string, charset : string = 'iso-8859-1')
+
+ Set the content type of the response to the MIME type specified by
+ 'content_type'. Also sets the charset, defaulting to 'iso-8859-1'.
+ """
+ self.charset = charset
+ self.content_type = content_type
+
+ def set_charset(self, charset):
+ self.charset = str(charset).lower()
+
+ def set_status(self, status, reason=None):
+ """set_status(status : int, reason : string = None)
+
+ Sets the HTTP status code of the response. 'status' must be an
+ integer in the range 100 .. 599. 'reason' must be a string; if
+ not supplied, the default reason phrase for 'status' will be
+ used. If 'status' is a non-standard status code, the generic
+ reason phrase for its group of status codes will be used; eg.
+ if status == 493, the reason for status 400 will be used.
+ """
+ if not isinstance(status, int):
+ raise TypeError, "status must be an integer"
+ if not (100 <= status <= 599):
+ raise ValueError, "status must be between 100 and 599"
+
+ self.status_code = status
+ if reason is None:
+ if status_reasons.has_key(status):
+ reason = status_reasons[status]
+ else:
+ # Eg. for generic 4xx failures, use the reason
+ # associated with status 400.
+ reason = status_reasons[status - (status % 100)]
+ else:
+ reason = str(reason)
+
+ self.reason_phrase = reason
+
+ def set_header(self, name, value):
+ """set_header(name : string, value : string)
+
+ Sets an HTTP return header "name" with value "value", clearing
+ the previous value set for the header, if one exists.
+ """
+ self.headers[name.lower()] = value
+
+ def get_header(self, name, default=None):
+ """get_header(name : string, default=None) -> value : string
+
+ Gets an HTTP return header "name". If none exists then 'default' is
+ returned.
+ """
+ return self.headers.get(name.lower(), default)
+
+ def set_expires(self, seconds=0, minutes=0, hours=0, days=0):
+ if seconds is None:
+ self.cache = None # don't generate 'Expires' header
+ else:
+ self.cache = seconds + 60*(minutes + 60*(hours + 24*days))
+
+ def _encode_chunk(self, chunk):
+ """(chunk : str | unicode) -> str
+ """
+ if self.charset == 'iso-8859-1' and isinstance(chunk, str):
+ return chunk # non-ASCII chars are okay
+ else:
+ return chunk.encode(self.charset)
+
+ def _compress_body(self, body):
+ """(body: str) -> str
+ """
+ n = len(body)
+ co = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS,
+ zlib.DEF_MEM_LEVEL, 0)
+ chunks = [_GZIP_HEADER,
+ co.compress(body),
+ co.flush(),
+ struct.pack("<ll", zlib.crc32(body), n)]
+ compressed_body = "".join(chunks)
+ ratio = float(n) / len(compressed_body)
+ #print "gzip original size %d, ratio %.1f" % (n, ratio)
+ if ratio > 1.0:
+ self.set_header("Content-Encoding", "gzip")
+ return compressed_body
+ else:
+ return body
+
+ def set_body(self, body, compress=False):
+ """(body : any, compress : bool = False)
+
+ Sets the response body equal to the argument 'body'. If 'compress'
+ is true then the body may be compressed using 'gzip'.
+ """
+ if not isinstance(body, Stream):
+ body = self._encode_chunk(stringify(body))
+ if compress and self.content_type not in _GZIP_EXCLUDE:
+ body = self._compress_body(body)
+ self.body = body
+
+ def expire_cookie(self, name, **attrs):
+ """
+ Cause an HTTP cookie to be removed from the browser
+
+ The response will include an HTTP header that will remove the cookie
+ corresponding to "name" on the client, if one exists. This is
+ accomplished by sending a new cookie with an expiration date
+ that has already passed. Note that some clients require a path
+ to be specified - this path must exactly match the path given
+ when creating the cookie. The path can be specified as a keyword
+ argument.
+ """
+ dict = {'max_age': 0, 'expires': 'Thu, 01-Jan-1970 00:00:00 GMT'}
+ dict.update(attrs)
+ self.set_cookie(name, "deleted", **dict)
+
+ def set_cookie(self, name, value, **attrs):
+ """set_cookie(name : string, value : string, **attrs)
+
+ Set an HTTP cookie on the browser.
+
+ The response will include an HTTP header that sets a cookie on
+ cookie-enabled browsers with a key "name" and value "value".
+ Cookie attributes such as "expires" and "domains" may be
+ supplied as keyword arguments; see RFC 2109 for a full list.
+ (For the "secure" attribute, use any true value.)
+
+ This overrides any previous value for this cookie. Any
+ previously-set attributes for the cookie are preserved, unless
+ they are explicitly overridden with keyword arguments to this
+ call.
+ """
+ cookies = self.cookies
+ if cookies.has_key(name):
+ cookie = cookies[name]
+ else:
+ cookie = cookies[name] = {}
+ cookie.update(attrs)
+ cookie['value'] = value
+
+ def add_javascript(self, code_id, code):
+ """Add javascript code to be included in the response.
+
+ code_id is used to ensure that the same piece of code is not
+ included twice. The caller must be careful to avoid
+ unintentional code_id and javascript identifier collisions.
+ Note that the response object only provides a mechanism for
+ collecting code -- actually including it in the HTML document
+ that is the response body is somebody else's problem. (For
+ an example, see Form._render_javascript().)
+ """
+ if self.javascript_code is None:
+ self.javascript_code = {code_id: code}
+ elif not self.javascript_code.has_key(code_id):
+ self.javascript_code[code_id] = code
+
+ def redirect(self, location, permanent=False):
+ """Cause a redirection without raising an error"""
+ if not isinstance(location, str):
+ raise TypeError, "location must be a string (got %s)" % `location`
+ # Ensure that location is a full URL
+ if location.find('://') == -1:
+ raise ValueError, "URL must include the server name"
+ if permanent:
+ status = 301
+ else:
+ status = 302
+ self.set_status(status)
+ self.headers['location'] = location
+ self.set_content_type('text/plain')
+ return "Your browser should have redirected you to %s" % location
+
+ def get_status_code(self):
+ return self.status_code
+
+ def get_reason_phrase(self):
+ return self.reason_phrase
+
+ def get_content_type(self):
+ return self.content_type
+
+ def get_content_length(self):
+ if self.body is None:
+ return None
+ elif isinstance(self.body, Stream):
+ return self.body.length
+ else:
+ return len(self.body)
+
+ def _gen_cookie_headers(self):
+ """_gen_cookie_headers() -> [string]
+
+ Build a list of "Set-Cookie" headers based on all cookies
+ set with 'set_cookie()', and return that list.
+ """
+ cookie_headers = []
+ for name, attrs in self.cookies.items():
+ value = str(attrs['value'])
+ if '"' in value:
+ value = value.replace('"', '\\"')
+ chunks = ['%s="%s"' % (name, value)]
+ for name, val in attrs.items():
+ name = name.lower()
+ if val is None:
+ continue
+ if name in ('expires', 'domain', 'path', 'max_age', 'comment'):
+ name = name.replace('_', '-')
+ chunks.append('%s=%s' % (name, val))
+ elif name == 'secure' and val:
+ chunks.append("secure")
+ cookie_headers.append(("Set-Cookie", '; '.join(chunks)))
+ return cookie_headers
+
+ def generate_headers(self):
+ """generate_headers() -> [(name:string, value:string)]
+
+ Generate a list of headers to be returned as part of the response.
+ """
+ headers = []
+
+ for name, value in self.headers.items():
+ headers.append((name.title(), value))
+
+ # All the "Set-Cookie" headers.
+ if self.cookies:
+ headers.extend(self._gen_cookie_headers())
+
+ # Date header
+ now = time.time()
+ if "date" not in self.headers:
+ headers.append(("Date", formatdate(now)))
+
+ # Cache directives
+ if self.cache is None:
+ pass # don't mess with the expires header
+ elif "expires" not in self.headers:
+ if self.cache > 0:
+ expire_date = formatdate(now + self.cache)
+ else:
+ expire_date = "-1" # allowed by HTTP spec and may work better
+ # with some clients
+ headers.append(("Expires", expire_date))
+
+ # Content-type
+ if "content-type" not in self.headers:
+ headers.append(('Content-Type',
+ '%s; charset=%s' % (self.content_type,
+ self.charset)))
+
+ # Content-Length
+ if "content-length" not in self.headers:
+ length = self.get_content_length()
+ if length is not None:
+ headers.append(('Content-Length', length))
+
+ return headers
+
+ def generate_body_chunks(self):
+ """Return a sequence of body chunks, encoded using 'charset'.
+ """
+ if self.body is None:
+ pass
+ elif isinstance(self.body, Stream):
+ for chunk in self.body:
+ yield self._encode_chunk(chunk)
+ else:
+ yield self.body # already encoded
+
+ def write(self, output, include_status=True):
+ """(output : file, include_status : bool = True)
+
+ Write the HTTP response headers and body to 'output'. This is not
+ a complete HTTP response, as it doesn't start with a response
+ status line as specified by RFC 2616. By default, it does start
+ with a "Status" header as described by the CGI spec. It is expected
+ that this response is parsed by the web server and turned into a
+ complete HTTP response.
+ """
+ flush_output = not self.buffered and hasattr(output, 'flush')
+ if include_status:
+ # "Status" header must come first.
+ output.write("Status: %03d %s\r\n" % (self.status_code,
+ self.reason_phrase))
+ for name, value in self.generate_headers():
+ output.write("%s: %s\r\n" % (name, value))
+ output.write("\r\n")
+ if flush_output:
+ output.flush()
+ for chunk in self.generate_body_chunks():
+ output.write(chunk)
+ if flush_output:
+ output.flush()
+ if flush_output:
+ output.flush()
+
+
+class Stream:
+ """
+ A wrapper around response data that can be streamed. The 'iterable'
+ argument must support the iteration protocol. Items returned by 'next()'
+ must be strings. Beware that exceptions raised while writing the stream
+ will not be handled gracefully.
+
+ Instance attributes:
+ iterable : any
+ an object that supports the iteration protocol. The items produced
+ by the stream must be strings.
+ length: int | None
+ the number of bytes that will be produced by the stream, None
+ if it is not known. Used to set the Content-Length header.
+ """
+ def __init__(self, iterable, length=None):
+ self.iterable = iterable
+ self.length = length
+
+ def __iter__(self):
+ return iter(self.iterable)
diff --git a/pypers/europython05/Quixote-2.0/logger.py b/pypers/europython05/Quixote-2.0/logger.py
new file mode 100755
index 0000000..f631c57
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/logger.py
@@ -0,0 +1,92 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/logger.py $
+$Id: logger.py 25521 2004-11-04 18:16:18Z nascheme $
+"""
+import sys
+import os
+import time
+import socket
+from quixote.sendmail import sendmail
+
+class DefaultLogger:
+ """
+ This is the default logger object used by the Quixote publisher. It
+ controls access log and error log behavior. You may provide your own
+ object if you wish to have different behavior.
+
+ Instance attributes:
+
+ access_log : file | None
+ file to which every access will be logged. If None then access
+ is not logged.
+ error_log : file
+ file to which application errors (exceptions caught by Quixote,
+ as well as anything printed to stderr by application code) will
+ be logged. Set to sys.stderr by default.
+ error_email : string | None
+ if set then internal server errors will cause messages to be sent to
+ this address
+ """
+ def __init__(self, access_log=None, error_log=None, error_email=None):
+ if access_log:
+ self.access_log = open(access_log, 'a', 1)
+ else:
+ self.access_log = None
+ if error_log is None:
+ self.error_log = sys.stderr
+ else:
+ self.error_log = open(error_log, 'a', 1)
+ self.error_email = error_email
+ sys.stdout = self.error_log # print is handy for debugging
+
+ def log(self, msg):
+ """
+ Write an message to the error log with a time stamp.
+ """
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(time.time()))
+ self.error_log.write("[%s] %s\n" % (timestamp, msg))
+
+ def log_internal_error(self, error_summary, error_msg):
+ """(error_summary: str, error_msg: str)
+
+ error_summary is a single line summary of the internal error, suitable
+ for an email subject. error_msg is a multi-line plaintext message
+ describing the error in detail.
+ """
+ self.log("exception caught")
+ self.error_log.write(error_msg)
+ if self.error_email:
+ sendmail('Quixote Traceback (%s)' % error_summary,
+ error_msg, [self.error_email],
+ from_addr=(self.error_email, socket.gethostname()))
+
+ def log_request(self, request, start_time):
+ """Log a request in the access_log file.
+ """
+ if self.access_log is None:
+ return
+ if request.session:
+ user = request.session.user or "-"
+ else:
+ user = "-"
+ now = time.time()
+ seconds = now - start_time
+ timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now))
+
+ request_uri = request.get_path()
+ query = request.get_query()
+ if query:
+ request_uri += "?" + query
+ proto = request.get_environ('SERVER_PROTOCOL')
+ self.access_log.write('%s %s %s %d "%s %s %s" %s %r %0.2fsec\n' %
+ (request.get_environ('REMOTE_ADDR'),
+ user,
+ timestamp,
+ os.getpid(),
+ request.get_method(),
+ request_uri,
+ proto,
+ request.response.status_code,
+ request.get_environ('HTTP_USER_AGENT', ''),
+ seconds
+ ))
diff --git a/pypers/europython05/Quixote-2.0/ptl/__init__.py b/pypers/europython05/Quixote-2.0/ptl/__init__.py
new file mode 100755
index 0000000..3409414
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/__init__.py
@@ -0,0 +1,245 @@
+'''
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/ptl/__init__.py $
+$Id: __init__.py 26357 2005-03-16 14:56:23Z dbinger $
+
+PTL: Python Template Language
+=============================
+
+Introduction
+------------
+
+PTL is the templating language used by Quixote. Most web templating
+languages embed a real programming language in HTML, but PTL inverts
+this model by merely tweaking Python to make it easier to generate
+HTML pages (or other forms of text). In other words, PTL is basically
+Python with a novel way to specify function return values.
+
+Specifically, a PTL template is designated by inserting a ``[plain]``
+or ``[html]`` modifier after the function name. The value of
+expressions inside templates are kept, not discarded. If the type is
+``[html]`` then non-literal strings are passed through a function that
+escapes HTML special characters.
+
+
+Plain text templates
+--------------------
+
+Here's a sample plain text template::
+
+ def foo [plain] (x, y = 5):
+ "This is a chunk of static text."
+ greeting = "hello world" # statement, no PTL output
+ print 'Input values:', x, y
+ z = x + y
+ """You can plug in variables like x (%s)
+ in a variety of ways.""" % x
+
+ "\n\n"
+ "Whitespace is important in generated text.\n"
+ "z = "; z
+ ", but y is "
+ y
+ "."
+
+Obviously, templates can't have docstrings, but otherwise they follow
+Python's syntactic rules: indentation indicates scoping, single-quoted
+and triple-quoted strings can be used, the same rules for continuing
+lines apply, and so forth. PTL also follows all the expected semantics
+of normal Python code: so templates can have parameters, and the
+parameters can have default values, be treated as keyword arguments,
+etc.
+
+The difference between a template and a regular Python function is that
+inside a template the result of expressions are saved as the return
+value of that template. Look at the first part of the example again::
+
+ def foo [plain] (x, y = 5):
+ "This is a chunk of static text."
+ greeting = "hello world" # statement, no PTL output
+ print 'Input values:', x, y
+ z = x + y
+ """You can plug in variables like x (%s)
+ in a variety of ways.""" % x
+
+Calling this template with ``foo(1, 2)`` results in the following
+string::
+
+ This is a chunk of static text.You can plug in variables like x (1)
+ in a variety of ways.
+
+Normally when Python evaluates expressions inside functions, it just
+discards their values, but in a ``[plain]`` PTL template the value is
+converted to a string using ``str()`` and appended to the template's
+return value. There's a single exception to this rule: ``None`` is the
+only value that's ever ignored, adding nothing to the output. (If this
+weren't the case, calling methods or functions that return ``None``
+would require assigning their value to a variable. You'd have to write
+``dummy = list.sort()`` in PTL code, which would be strange and
+confusing.)
+
+The initial string in a template isn't treated as a docstring, but is
+just incorporated in the generated output; therefore, templates can't
+have docstrings. No whitespace is ever automatically added to the
+output, resulting in ``...text.You can ...`` from the example. You'd
+have to add an extra space to one of the string literals to correct
+this.
+
+The assignment to the ``greeting`` local variable is a statement, not an
+expression, so it doesn't return a value and produces no output. The
+output from the ``print`` statement will be printed as usual, but won't
+go into the string generated by the template. Quixote directs standard
+output into Quixote's debugging log; if you're using PTL on its own, you
+should consider doing something similar. ``print`` should never be used
+to generate output returned to the browser, only for adding debugging
+traces to a template.
+
+Inside templates, you can use all of Python's control-flow statements::
+
+ def numbers [plain] (n):
+ for i in range(n):
+ i
+ " " # PTL does not add any whitespace
+
+Calling ``numbers(5)`` will return the string ``"1 2 3 4 5 "``. You can
+also have conditional logic or exception blocks::
+
+ def international_hello [plain] (language):
+ if language == "english":
+ "hello"
+ elif language == "french":
+ "bonjour"
+ else:
+ raise ValueError, "I don't speak %s" % language
+
+
+HTML templates
+--------------
+
+Since PTL is usually used to generate HTML documents, an ``[html]``
+template type has been provided to make generating HTML easier.
+
+A common error when generating HTML is to grab data from the browser
+or from a database and incorporate the contents without escaping
+special characters such as '<' and '&'. This leads to a class of
+security bugs called "cross-site scripting" bugs, where a hostile user
+can insert arbitrary HTML in your site's output that can link to other
+sites or contain JavaScript code that does something nasty (say,
+popping up 10,000 browser windows).
+
+Such bugs occur because it's easy to forget to HTML-escape a string,
+and forgetting it in just one location is enough to open a hole. PTL
+offers a solution to this problem by being able to escape strings
+automatically when generating HTML output, at the cost of slightly
+diminished performance (a few percent).
+
+Here's how this feature works. PTL defines a class called
+``htmltext`` that represents a string that's already been HTML-escaped
+and can be safely sent to the client. The function ``htmlescape(string)``
+is used to escape data, and it always returns an ``htmltext``
+instance. It does nothing if the argument is already ``htmltext``.
+
+If a template function is declared ``[html]`` instead of ``[text]``
+then two things happen. First, all literal strings in the function
+become instances of ``htmltext`` instead of Python's ``str``. Second,
+the values of expressions are passed through ``htmlescape()`` instead
+of ``str()``.
+
+``htmltext`` type is like the ``str`` type except that operations
+combining strings and ``htmltext`` instances will result in the string
+being passed through ``htmlescape()``. For example::
+
+ >>> from quixote.html import htmltext
+ >>> htmltext('a') + 'b'
+ <htmltext 'ab'>
+ >>> 'a' + htmltext('b')
+ <htmltext 'ab'>
+ >>> htmltext('a%s') % 'b'
+ <htmltext 'ab'>
+ >>> response = 'green eggs & ham'
+ >>> htmltext('The response was: %s') % response
+ <htmltext 'The response was: green eggs &amp; ham'>
+
+Note that calling ``str()`` strips the ``htmltext`` type and should be
+avoided since it usually results in characters being escaped more than
+once. While ``htmltext`` behaves much like a regular string, it is
+sometimes necessary to insert a ``str()`` inside a template in order
+to obtain a genuine string. For example, the ``re`` module requires
+genuine strings. We have found that explicit calls to ``str()`` can
+often be avoided by splitting some code out of the template into a
+helper function written in regular Python.
+
+It is also recommended that the ``htmltext`` constructor be used as
+sparingly as possible. The reason is that when using the htmltext
+feature of PTL, explicit calls to ``htmltext`` become the most likely
+source of cross-site scripting holes. Calling ``htmltext`` is like
+saying "I am absolutely sure this piece of data cannot contain malicious
+HTML code injected by a user. Don't escape HTML special characters
+because I want them."
+
+Note that literal strings in template functions declared with
+``[html]`` are htmltext instances, and therefore won't be escaped.
+You'll only need to use ``htmltext`` when HTML markup comes from
+outside the template. For example, if you want to include a file
+containing HTML::
+
+ def output_file [html] ():
+ '<html><body>' # does not get escaped
+ htmltext(open("myfile.html").read())
+ '</body></html>'
+
+In the common case, templates won't be dealing with HTML markup from
+external sources, so you can write straightforward code. Consider
+this function to generate the contents of the ``HEAD`` element::
+
+ def meta_tags [html] (title, description):
+ '<title>%s</title>' % title
+ '<meta name="description" content="%s">\n' % description
+
+There are no calls to ``htmlescape()`` at all, but string literals
+such as ``<title>%s</title>`` have all be turned into ``htmltext``
+instances, so the string variables will be automatically escaped::
+
+ >>> t.meta_tags('Catalog', 'A catalog of our cool products')
+ <htmltext '<title>Catalog</title>
+ <meta name="description" content="A catalog of our cool products">\n'>
+ >>> t.meta_tags('Dissertation on <HEAD>',
+ ... 'Discusses the "LINK" and "META" tags')
+ <htmltext '<title>Dissertation on &lt;HEAD&gt;</title>
+ <meta name="description"
+ content="Discusses the &quot;LINK&quot; and &quot;META&quot; tags">\n'>
+ >>>
+
+Note how the title and description have had HTML-escaping applied to them.
+(The output has been manually pretty-printed to be more readable.)
+
+Once you start using ``htmltext`` in one of your templates, mixing
+plain and HTML templates is tricky because of ``htmltext``'s automatic
+escaping; plain templates that generate HTML tags will be
+double-escaped. One approach is to just use HTML templates throughout
+your application. Alternatively you can use ``str()`` to convert
+``htmltext`` instances to regular Python strings; just be sure the
+resulting string isn't HTML-escaped again.
+
+Two implementations of ``htmltext`` are provided, one written in pure
+Python and a second one implemented as a C extension. Both versions
+have seen production use.
+
+
+PTL modules
+-----------
+
+PTL templates are kept in files with the extension .ptl. Like Python
+files, they are byte-compiled on import, and the byte-code is written to
+a compiled file with the extension ``.pyc``. Since vanilla Python
+doesn't know anything about PTL, this package provides an import hook to let
+you import PTL files just like regular Python modules. The import
+hook is installed when you import *this* package.
+
+(Note: if you're using ZODB, always import ZODB *before* installing the
+PTL import hook. There's some interaction which causes importing the
+TimeStamp module to fail when the PTL import hook is installed; we
+haven't debugged the problem. A similar problem has been reported for
+BioPython and win32com.client imports.)
+'''
+
+
diff --git a/pypers/europython05/Quixote-2.0/ptl/cimport.c b/pypers/europython05/Quixote-2.0/ptl/cimport.c
new file mode 100755
index 0000000..6e37ca5
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/cimport.c
@@ -0,0 +1,483 @@
+/* Mostly stolen from Python/import.c. PSF license applies. */
+
+
+#include "Python.h"
+#include "osdefs.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Python function to find and load a module. */
+static PyObject *loader_hook;
+
+
+PyObject *
+call_find_load(char *fullname, char *subname, PyObject *path)
+{
+ PyObject *args, *m;
+
+ if (!(args = Py_BuildValue("(ssO)", fullname, subname,
+ path != NULL ? path : Py_None)))
+ return NULL;
+
+ m = PyEval_CallObject(loader_hook, args);
+
+ Py_DECREF(args);
+ return m;
+}
+
+
+/* Forward declarations for helper routines */
+static PyObject *get_parent(PyObject *globals, char *buf, int *p_buflen);
+static PyObject *load_next(PyObject *mod, PyObject *altmod,
+ char **p_name, char *buf, int *p_buflen);
+static int mark_miss(char *name);
+static int ensure_fromlist(PyObject *mod, PyObject *fromlist,
+ char *buf, int buflen, int recursive);
+static PyObject * import_submodule(PyObject *mod, char *name, char *fullname);
+
+
+static PyObject *
+import_module(char *name, PyObject *globals, PyObject *locals,
+ PyObject *fromlist)
+{
+ char buf[MAXPATHLEN+1];
+ int buflen = 0;
+ PyObject *parent, *head, *next, *tail;
+
+ parent = get_parent(globals, buf, &buflen);
+ if (parent == NULL)
+ return NULL;
+
+ head = load_next(parent, Py_None, &name, buf, &buflen);
+ if (head == NULL)
+ return NULL;
+
+ tail = head;
+ Py_INCREF(tail);
+ while (name) {
+ next = load_next(tail, tail, &name, buf, &buflen);
+ Py_DECREF(tail);
+ if (next == NULL) {
+ Py_DECREF(head);
+ return NULL;
+ }
+ tail = next;
+ }
+
+ if (fromlist != NULL) {
+ if (fromlist == Py_None || !PyObject_IsTrue(fromlist))
+ fromlist = NULL;
+ }
+
+ if (fromlist == NULL) {
+ Py_DECREF(tail);
+ return head;
+ }
+
+ Py_DECREF(head);
+ if (!ensure_fromlist(tail, fromlist, buf, buflen, 0)) {
+ Py_DECREF(tail);
+ return NULL;
+ }
+
+ return tail;
+}
+
+static PyObject *
+get_parent(PyObject *globals, char *buf, int *p_buflen)
+{
+ static PyObject *namestr = NULL;
+ static PyObject *pathstr = NULL;
+ PyObject *modname, *modpath, *modules, *parent;
+
+ if (globals == NULL || !PyDict_Check(globals))
+ return Py_None;
+
+ if (namestr == NULL) {
+ namestr = PyString_InternFromString("__name__");
+ if (namestr == NULL)
+ return NULL;
+ }
+ if (pathstr == NULL) {
+ pathstr = PyString_InternFromString("__path__");
+ if (pathstr == NULL)
+ return NULL;
+ }
+
+ *buf = '\0';
+ *p_buflen = 0;
+ modname = PyDict_GetItem(globals, namestr);
+ if (modname == NULL || !PyString_Check(modname))
+ return Py_None;
+
+ modpath = PyDict_GetItem(globals, pathstr);
+ if (modpath != NULL) {
+ int len = PyString_GET_SIZE(modname);
+ if (len > MAXPATHLEN) {
+ PyErr_SetString(PyExc_ValueError,
+ "Module name too long");
+ return NULL;
+ }
+ strcpy(buf, PyString_AS_STRING(modname));
+ *p_buflen = len;
+ }
+ else {
+ char *start = PyString_AS_STRING(modname);
+ char *lastdot = strrchr(start, '.');
+ size_t len;
+ if (lastdot == NULL)
+ return Py_None;
+ len = lastdot - start;
+ if (len >= MAXPATHLEN) {
+ PyErr_SetString(PyExc_ValueError,
+ "Module name too long");
+ return NULL;
+ }
+ strncpy(buf, start, len);
+ buf[len] = '\0';
+ *p_buflen = len;
+ }
+
+ modules = PyImport_GetModuleDict();
+ parent = PyDict_GetItemString(modules, buf);
+ if (parent == NULL)
+ parent = Py_None;
+ return parent;
+ /* We expect, but can't guarantee, if parent != None, that:
+ - parent.__name__ == buf
+ - parent.__dict__ is globals
+ If this is violated... Who cares? */
+}
+
+/* altmod is either None or same as mod */
+static PyObject *
+load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf,
+ int *p_buflen)
+{
+ char *name = *p_name;
+ char *dot = strchr(name, '.');
+ size_t len;
+ char *p;
+ PyObject *result;
+
+ if (dot == NULL) {
+ *p_name = NULL;
+ len = strlen(name);
+ }
+ else {
+ *p_name = dot+1;
+ len = dot-name;
+ }
+ if (len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "Empty module name");
+ return NULL;
+ }
+
+ p = buf + *p_buflen;
+ if (p != buf)
+ *p++ = '.';
+ if (p+len-buf >= MAXPATHLEN) {
+ PyErr_SetString(PyExc_ValueError,
+ "Module name too long");
+ return NULL;
+ }
+ strncpy(p, name, len);
+ p[len] = '\0';
+ *p_buflen = p+len-buf;
+
+ result = import_submodule(mod, p, buf);
+ if (result == Py_None && altmod != mod) {
+ Py_DECREF(result);
+ /* Here, altmod must be None and mod must not be None */
+ result = import_submodule(altmod, p, p);
+ if (result != NULL && result != Py_None) {
+ if (mark_miss(buf) != 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ strncpy(buf, name, len);
+ buf[len] = '\0';
+ *p_buflen = len;
+ }
+ }
+ if (result == NULL)
+ return NULL;
+
+ if (result == Py_None) {
+ Py_DECREF(result);
+ PyErr_Format(PyExc_ImportError,
+ "No module named %.200s", name);
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+mark_miss(char *name)
+{
+ PyObject *modules = PyImport_GetModuleDict();
+ return PyDict_SetItemString(modules, name, Py_None);
+}
+
+static int
+ensure_fromlist(PyObject *mod, PyObject *fromlist, char *buf, int buflen,
+ int recursive)
+{
+ int i;
+
+ if (!PyObject_HasAttrString(mod, "__path__"))
+ return 1;
+
+ for (i = 0; ; i++) {
+ PyObject *item = PySequence_GetItem(fromlist, i);
+ int hasit;
+ if (item == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_IndexError)) {
+ PyErr_Clear();
+ return 1;
+ }
+ return 0;
+ }
+ if (!PyString_Check(item)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Item in ``from list'' not a string");
+ Py_DECREF(item);
+ return 0;
+ }
+ if (PyString_AS_STRING(item)[0] == '*') {
+ PyObject *all;
+ Py_DECREF(item);
+ /* See if the package defines __all__ */
+ if (recursive)
+ continue; /* Avoid endless recursion */
+ all = PyObject_GetAttrString(mod, "__all__");
+ if (all == NULL)
+ PyErr_Clear();
+ else {
+ if (!ensure_fromlist(mod, all, buf, buflen, 1))
+ return 0;
+ Py_DECREF(all);
+ }
+ continue;
+ }
+ hasit = PyObject_HasAttr(mod, item);
+ if (!hasit) {
+ char *subname = PyString_AS_STRING(item);
+ PyObject *submod;
+ char *p;
+ if (buflen + strlen(subname) >= MAXPATHLEN) {
+ PyErr_SetString(PyExc_ValueError,
+ "Module name too long");
+ Py_DECREF(item);
+ return 0;
+ }
+ p = buf + buflen;
+ *p++ = '.';
+ strcpy(p, subname);
+ submod = import_submodule(mod, subname, buf);
+ Py_XDECREF(submod);
+ if (submod == NULL) {
+ Py_DECREF(item);
+ return 0;
+ }
+ }
+ Py_DECREF(item);
+ }
+
+ /* NOTREACHED */
+}
+
+static PyObject *
+import_submodule(PyObject *mod, char *subname, char *fullname)
+{
+ PyObject *modules = PyImport_GetModuleDict();
+ PyObject *m;
+
+ /* Require:
+ if mod == None: subname == fullname
+ else: mod.__name__ + "." + subname == fullname
+ */
+
+ if ((m = PyDict_GetItemString(modules, fullname)) != NULL) {
+ Py_INCREF(m);
+ }
+ else {
+ PyObject *path;
+
+ if (mod == Py_None)
+ path = NULL;
+ else {
+ path = PyObject_GetAttrString(mod, "__path__");
+ if (path == NULL) {
+ PyErr_Clear();
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ }
+
+ m = call_find_load(fullname, subname, path);
+
+ if (m != NULL && mod != Py_None) {
+ if (PyObject_SetAttrString(mod, subname, m) < 0) {
+ Py_DECREF(m);
+ m = NULL;
+ }
+ }
+ }
+
+ return m;
+}
+
+
+PyObject *
+reload_module(PyObject *m)
+{
+ PyObject *modules = PyImport_GetModuleDict();
+ PyObject *path = NULL;
+ char *name, *subname;
+
+ if (m == NULL || !PyModule_Check(m)) {
+ PyErr_SetString(PyExc_TypeError,
+ "reload_module() argument must be module");
+ return NULL;
+ }
+ name = PyModule_GetName(m);
+ if (name == NULL)
+ return NULL;
+ if (m != PyDict_GetItemString(modules, name)) {
+ PyErr_Format(PyExc_ImportError,
+ "reload(): module %.200s not in sys.modules",
+ name);
+ return NULL;
+ }
+ subname = strrchr(name, '.');
+ if (subname == NULL)
+ subname = name;
+ else {
+ PyObject *parentname, *parent;
+ parentname = PyString_FromStringAndSize(name, (subname-name));
+ if (parentname == NULL)
+ return NULL;
+ parent = PyDict_GetItem(modules, parentname);
+ Py_DECREF(parentname);
+ if (parent == NULL) {
+ PyErr_Format(PyExc_ImportError,
+ "reload(): parent %.200s not in sys.modules",
+ name);
+ return NULL;
+ }
+ subname++;
+ path = PyObject_GetAttrString(parent, "__path__");
+ if (path == NULL)
+ PyErr_Clear();
+ }
+ m = call_find_load(name, subname, path);
+ Py_XDECREF(path);
+ return m;
+}
+
+
+static PyObject *
+cimport_import_module(PyObject *self, PyObject *args)
+{
+ char *name;
+ PyObject *globals = NULL;
+ PyObject *locals = NULL;
+ PyObject *fromlist = NULL;
+
+ if (!PyArg_ParseTuple(args, "s|OOO:import_module", &name, &globals,
+ &locals, &fromlist))
+ return NULL;
+ return import_module(name, globals, locals, fromlist);
+}
+
+static PyObject *
+cimport_reload_module(PyObject *self, PyObject *args)
+{
+ PyObject *m;
+ if (!PyArg_ParseTuple(args, "O:reload_module", &m))
+ return NULL;
+ return reload_module(m);
+}
+
+static char doc_reload_module[] =
+"reload(module) -> module\n\
+\n\
+Reload the module. The module must have been successfully imported before.";
+
+static PyObject *
+cimport_set_loader(PyObject *self, PyObject *args)
+{
+ PyObject *l = NULL;
+ if (!PyArg_ParseTuple(args, "O:set_loader", &l))
+ return NULL;
+ if (!PyCallable_Check(l)) {
+ PyErr_SetString(PyExc_TypeError, "callable object needed");
+ return NULL;
+ }
+ Py_XDECREF(loader_hook);
+ loader_hook = l;
+ Py_INCREF(loader_hook);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+static char doc_set_loader[] = "\
+Set the function that will be used to import modules.\n\
+\n\
+The function should should have the signature:\n\
+\n\
+ loader(fullname : str, subname : str, path : [str] | None) -> module | None\n\
+\n\
+It should return the initialized module or None if it is not found.\n\
+";
+
+
+static PyObject *
+cimport_get_loader(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":get_loader"))
+ return NULL;
+ Py_INCREF(loader_hook);
+ return loader_hook;
+}
+
+static char doc_get_loader[] = "\
+Get the function that will be used to import modules.\n\
+";
+
+static char doc_import_module[] = "\
+import_module(name, globals, locals, fromlist) -> module\n\
+\n\
+Import a module. The globals are only used to determine the context;\n\
+they are not modified. The locals are currently unused. The fromlist\n\
+should be a list of names to emulate ``from name import ...'', or an\n\
+empty list to emulate ``import name''.\n\
+\n\
+When importing a module from a package, note that import_module('A.B', ...)\n\
+returns package A when fromlist is empty, but its submodule B when\n\
+fromlist is not empty.\n\
+";
+
+
+static PyMethodDef cimport_methods[] = {
+ {"import_module", cimport_import_module, 1, doc_import_module},
+ {"reload_module", cimport_reload_module, 1, doc_reload_module},
+ {"get_loader", cimport_get_loader, 1, doc_get_loader},
+ {"set_loader", cimport_set_loader, 1, doc_set_loader},
+ {NULL, NULL} /* sentinel */
+};
+
+void
+initcimport(void)
+{
+ PyObject *m, *d;
+
+ m = Py_InitModule4("cimport", cimport_methods, "",
+ NULL, PYTHON_API_VERSION);
+ d = PyModule_GetDict(m);
+
+}
diff --git a/pypers/europython05/Quixote-2.0/ptl/install.py b/pypers/europython05/Quixote-2.0/ptl/install.py
new file mode 100755
index 0000000..642c69b
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/install.py
@@ -0,0 +1,2 @@
+import quixote.ptl.ptl_import
+quixote.ptl.ptl_import.install()
diff --git a/pypers/europython05/Quixote-2.0/ptl/ptl_compile.py b/pypers/europython05/Quixote-2.0/ptl/ptl_compile.py
new file mode 100755
index 0000000..47c0e32
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/ptl_compile.py
@@ -0,0 +1,314 @@
+#!/www/python/bin/python
+"""
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/ptl/ptl_compile.py $
+$Id: ptl_compile.py 26357 2005-03-16 14:56:23Z dbinger $
+
+Compile a PTL template.
+
+First template function names are mangled, noting the template type.
+Next, the file is parsed into a parse tree. This tree is converted into
+a modified AST. It is during this state that the semantics are modified
+by adding extra nodes to the tree. Finally bytecode is generated using
+the compiler package.
+"""
+
+import sys
+import os
+import stat
+import symbol
+import token
+import re
+import imp
+import stat
+import marshal
+import struct
+
+assert sys.hexversion >= 0x20300b1, 'PTL requires Python 2.3 or newer'
+
+from compiler import pycodegen, transformer
+from compiler import ast
+from compiler.consts import OP_ASSIGN
+from compiler import misc, syntax
+
+HTML_TEMPLATE_PREFIX = "_q_html_template_"
+PLAIN_TEMPLATE_PREFIX = "_q_plain_template_"
+
+class TemplateTransformer(transformer.Transformer):
+
+ def __init__(self, *args, **kwargs):
+ transformer.Transformer.__init__(self, *args, **kwargs)
+ # __template_type is a stack whose values are
+ # "html", "plain", or None
+ self.__template_type = []
+
+ def _get_template_type(self):
+ """Return the type of the function being compiled (
+ "html", "plain", or None)
+ """
+ if self.__template_type:
+ return self.__template_type[-1]
+ else:
+ return None
+
+ def file_input(self, nodelist):
+ doc = None # self.get_docstring(nodelist, symbol.file_input)
+
+ html_imp = ast.From('quixote.html', [('TemplateIO', '_q_TemplateIO'),
+ ('htmltext', '_q_htmltext')])
+ vars_imp = ast.From("__builtin__", [("vars", "_q_vars")])
+ stmts = [ vars_imp, html_imp ]
+
+ for node in nodelist:
+ if node[0] != token.ENDMARKER and node[0] != token.NEWLINE:
+ self.com_append_stmt(stmts, node)
+
+ return ast.Module(doc, ast.Stmt(stmts))
+
+ def funcdef(self, nodelist):
+ if len(nodelist) == 6:
+ assert nodelist[0][0] == symbol.decorators
+ decorators = self.decorators(nodelist[0][1:])
+ else:
+ assert len(nodelist) == 5
+ decorators = None
+
+ lineno = nodelist[-4][2]
+ name = nodelist[-4][1]
+ args = nodelist[-3][2]
+
+ if not re.match('_q_(html|plain)_(dollar_)?template_', name):
+ # just a normal function, let base class handle it
+ self.__template_type.append(None)
+ n = transformer.Transformer.funcdef(self, nodelist)
+ else:
+ if name.startswith(PLAIN_TEMPLATE_PREFIX):
+ name = name[len(PLAIN_TEMPLATE_PREFIX):]
+ template_type = "plain"
+ elif name.startswith(HTML_TEMPLATE_PREFIX):
+ name = name[len(HTML_TEMPLATE_PREFIX):]
+ template_type = "html"
+ else:
+ raise RuntimeError, 'unknown prefix on %s' % name
+
+ self.__template_type.append(template_type)
+
+ if args[0] == symbol.varargslist:
+ names, defaults, flags = self.com_arglist(args[1:])
+ else:
+ names = defaults = ()
+ flags = 0
+ doc = None # self.get_docstring(nodelist[-1])
+
+ # code for function
+ code = self.com_node(nodelist[-1])
+
+ # _q_output = _q_TemplateIO()
+ klass = ast.Name('_q_TemplateIO')
+ args = [ast.Const(template_type == "html")]
+ instance = ast.CallFunc(klass, args)
+ assign_name = ast.AssName('_q_output', OP_ASSIGN)
+ assign = ast.Assign([assign_name], instance)
+
+ # return _q_output.getvalue()
+ func = ast.Getattr(ast.Name('_q_output'), "getvalue")
+ ret = ast.Return(ast.CallFunc(func, []))
+
+ # wrap original function code
+ code = ast.Stmt([assign, code, ret])
+
+ if sys.hexversion >= 0x20400a2:
+ n = ast.Function(decorators, name, names, defaults, flags, doc,
+ code)
+ else:
+ n = ast.Function(name, names, defaults, flags, doc, code)
+ n.lineno = lineno
+
+ self.__template_type.pop()
+ return n
+
+ def expr_stmt(self, nodelist):
+ if self._get_template_type() is None:
+ return transformer.Transformer.expr_stmt(self, nodelist)
+
+ # Instead of discarding objects on the stack, call
+ # "_q_output += obj".
+ exprNode = self.com_node(nodelist[-1])
+ if len(nodelist) == 1:
+ lval = ast.Name('_q_output')
+ n = ast.AugAssign(lval, '+=', exprNode)
+ if hasattr(exprNode, 'lineno'):
+ n.lineno = exprNode.lineno
+ elif nodelist[1][0] == token.EQUAL:
+ nodes = [ ]
+ for i in range(0, len(nodelist) - 2, 2):
+ nodes.append(self.com_assign(nodelist[i], OP_ASSIGN))
+ n = ast.Assign(nodes, exprNode)
+ n.lineno = nodelist[1][2]
+ else:
+ lval = self.com_augassign(nodelist[0])
+ op = self.com_augassign_op(nodelist[1])
+ n = ast.AugAssign(lval, op[1], exprNode)
+ n.lineno = op[2]
+ return n
+
+ def atom_string(self, nodelist):
+ k = ''
+ for node in nodelist:
+ k = k + eval(node[1])
+ lineno = node[2]
+ return self._get_text_node(k)
+
+ def _get_text_node(self, k):
+ if self._get_template_type() == "html":
+ return ast.CallFunc(ast.Name('_q_htmltext'), [ast.Const(k)])
+ else:
+ return ast.Const(k)
+
+_template_re = re.compile(
+ r"^(?P<indent>[ \t]*) def (?:[ \t]+)"
+ r" (?P<name>[a-zA-Z_][a-zA-Z_0-9]*)"
+ r" (?:[ \t]*) \[(?P<type>plain|html)\] (?:[ \t]*)"
+ r" (?:[ \t]*[\(\\])",
+ re.MULTILINE|re.VERBOSE)
+
+def translate_tokens(buf):
+ """
+ Since we can't modify the parser in the builtin parser module we
+ must do token translation here. Luckily it does not affect line
+ numbers.
+
+ def foo [plain] (...): -> def _q_plain_template__foo(...):
+
+ def foo [html] (...): -> def _q_html_template__foo(...):
+
+ XXX This parser is too stupid. For example, it doesn't understand
+ triple quoted strings.
+ """
+ def replacement(match):
+ template_type = match.group('type')
+ return '%sdef _q_%s_template_%s(' % (match.group('indent'),
+ template_type,
+ match.group('name'))
+ return _template_re.sub(replacement, buf)
+
+def parse(buf, filename='<string>'):
+ buf = translate_tokens(buf)
+ try:
+ return TemplateTransformer().parsesuite(buf)
+ except SyntaxError, e:
+ # set the filename attribute
+ raise SyntaxError(str(e), (filename, e.lineno, e.offset, e.text))
+
+
+PTL_EXT = ".ptl"
+
+class Template(pycodegen.Module):
+
+ def _get_tree(self):
+ tree = parse(self.source, self.filename)
+ misc.set_filename(self.filename, tree)
+ syntax.check(tree)
+ return tree
+
+ def dump(self, fp):
+ mtime = os.stat(self.filename)[stat.ST_MTIME]
+ fp.write('\0\0\0\0')
+ fp.write(struct.pack('<I', mtime))
+ marshal.dump(self.code, fp)
+ fp.flush()
+ fp.seek(0)
+ fp.write(imp.get_magic())
+
+
+def compile_template(input, filename, output=None):
+ """(input, filename, output=None) -> code
+
+ Compile an open file.
+ If output is not None then the code is written to output.
+ The code object is returned.
+ """
+ buf = input.read()
+ template = Template(buf, filename)
+ template.compile()
+ if output is not None:
+ template.dump(output)
+ return template.code
+
+def compile(inputname, outputname):
+ """(inputname, outputname)
+
+ Compile a template file. The new template is written to outputname.
+ """
+ input = open(inputname)
+ output = open(outputname, "wb")
+ try:
+ compile_template(input, inputname, output)
+ except:
+ # don't leave a corrupt .pyc file around
+ output.close()
+ os.unlink(outputname)
+ raise
+
+def compile_dir(dir, maxlevels=10, force=0):
+ """Byte-compile all PTL modules in the given directory tree.
+ (Adapted from compile_dir in Python module: compileall.py)
+
+ Arguments (only dir is required):
+
+ dir: the directory to byte-compile
+ maxlevels: maximum recursion level (default 10)
+ force: if true, force compilation, even if timestamps are up-to-date
+ """
+ print 'Listing', dir, '...'
+ try:
+ names = os.listdir(dir)
+ except os.error:
+ print "Can't list", dir
+ names = []
+ names.sort()
+ success = 1
+ for name in names:
+ fullname = os.path.join(dir, name)
+ if os.path.isfile(fullname):
+ head, tail = name[:-4], name[-4:]
+ if tail == PTL_EXT:
+ cfile = fullname[:-4] + '.pyc'
+ ftime = os.stat(fullname)[stat.ST_MTIME]
+ try:
+ ctime = os.stat(cfile)[stat.ST_MTIME]
+ except os.error: ctime = 0
+ if (ctime > ftime) and not force:
+ continue
+ print 'Compiling', fullname, '...'
+ try:
+ ok = compile(fullname, cfile)
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except:
+ # XXX compile catches SyntaxErrors
+ if type(sys.exc_type) == type(''):
+ exc_type_name = sys.exc_type
+ else: exc_type_name = sys.exc_type.__name__
+ print 'Sorry:', exc_type_name + ':',
+ print sys.exc_value
+ success = 0
+ else:
+ if ok == 0:
+ success = 0
+ elif (maxlevels > 0 and name != os.curdir and name != os.pardir and
+ os.path.isdir(fullname) and not os.path.islink(fullname)):
+ if not compile_dir(fullname, maxlevels - 1, force):
+ success = 0
+ return success
+
+def main():
+ args = sys.argv[1:]
+ if not args:
+ print "no files to compile"
+ else:
+ for filename in args:
+ path, ext = os.path.splitext(filename)
+ compile(filename, path + ".pyc")
+
+if __name__ == "__main__":
+ main()
diff --git a/pypers/europython05/Quixote-2.0/ptl/ptl_import.py b/pypers/europython05/Quixote-2.0/ptl/ptl_import.py
new file mode 100755
index 0000000..d6ac2a0
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/ptl_import.py
@@ -0,0 +1,148 @@
+"""
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/ptl/ptl_import.py $
+$Id: ptl_import.py 26357 2005-03-16 14:56:23Z dbinger $
+
+Import hooks; when installed, these hooks allow importing .ptl files
+as if they were Python modules.
+
+Note: there's some unpleasant incompatibility between ZODB's import
+trickery and the import hooks here. Bottom line: if you're using ZODB,
+import it *before* installing the PTL import hooks.
+"""
+
+import sys
+import os.path
+import imp, ihooks, new
+import struct
+import marshal
+import __builtin__
+
+from ptl_compile import compile_template, PTL_EXT
+
+assert sys.hexversion >= 0x20000b1, "need Python 2.0b1 or later"
+
+def _exec_module_code(code, name, filename):
+ if sys.modules.has_key(name):
+ mod = sys.modules[name] # necessary for reload()
+ else:
+ mod = new.module(name)
+ sys.modules[name] = mod
+ mod.__name__ = name
+ mod.__file__ = filename
+ exec code in mod.__dict__
+ return mod
+
+def _timestamp(filename):
+ try:
+ s = os.stat(filename)
+ except OSError:
+ return None
+ return s.st_mtime
+
+def _load_pyc(name, filename, pyc_filename):
+ try:
+ fp = open(pyc_filename, "rb")
+ except IOError:
+ return None
+ if fp.read(4) == imp.get_magic():
+ mtime = struct.unpack('<I', fp.read(4))[0]
+ ptl_mtime = _timestamp(filename)
+ if ptl_mtime is not None and mtime >= ptl_mtime:
+ code = marshal.load(fp)
+ return _exec_module_code(code, name, filename)
+ return None
+
+def _load_ptl(name, filename, file=None):
+ if not file:
+ try:
+ file = open(filename, "rb")
+ except IOError:
+ return None
+ path, ext = os.path.splitext(filename)
+ pyc_filename = path + ".pyc"
+ module = _load_pyc(name, filename, pyc_filename)
+ if module is not None:
+ return module
+ try:
+ output = open(pyc_filename, "wb")
+ except IOError:
+ output = None
+ try:
+ code = compile_template(file, filename, output)
+ except:
+ if output:
+ output.close()
+ os.unlink(pyc_filename)
+ raise
+ else:
+ if output:
+ output.close()
+ return _exec_module_code(code, name, filename)
+
+
+# Constant used to signal a PTL files
+PTL_FILE = object()
+
+class PTLHooks(ihooks.Hooks):
+
+ def get_suffixes(self):
+ # add our suffixes
+ return [(PTL_EXT, 'r', PTL_FILE)] + imp.get_suffixes()
+
+class PTLLoader(ihooks.ModuleLoader):
+
+ def load_module(self, name, stuff):
+ file, filename, info = stuff
+ (suff, mode, type) = info
+
+ # If it's a PTL file, load it specially.
+ if type is PTL_FILE:
+ return _load_ptl(name, filename, file)
+
+ else:
+ # Otherwise, use the default handler for loading
+ return ihooks.ModuleLoader.load_module(self, name, stuff)
+
+try:
+ import cimport
+except ImportError:
+ cimport = None
+
+class cModuleImporter(ihooks.ModuleImporter):
+ def __init__(self, loader=None):
+ self.loader = loader or ihooks.ModuleLoader()
+ cimport.set_loader(self.find_import_module)
+
+ def find_import_module(self, fullname, subname, path):
+ stuff = self.loader.find_module(subname, path)
+ if not stuff:
+ return None
+ return self.loader.load_module(fullname, stuff)
+
+ def install(self):
+ self.save_import_module = __builtin__.__import__
+ self.save_reload = __builtin__.reload
+ if not hasattr(__builtin__, 'unload'):
+ __builtin__.unload = None
+ self.save_unload = __builtin__.unload
+ __builtin__.__import__ = cimport.import_module
+ __builtin__.reload = cimport.reload_module
+ __builtin__.unload = self.unload
+
+_installed = False
+
+def install():
+ global _installed
+ if not _installed:
+ hooks = PTLHooks()
+ loader = PTLLoader(hooks)
+ if cimport is not None:
+ importer = cModuleImporter(loader)
+ else:
+ importer = ihooks.ModuleImporter(loader)
+ ihooks.install(importer)
+ _installed = True
+
+
+if __name__ == '__main__':
+ install()
diff --git a/pypers/europython05/Quixote-2.0/ptl/ptlrun.py b/pypers/europython05/Quixote-2.0/ptl/ptlrun.py
new file mode 100755
index 0000000..490188a
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/ptlrun.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+import sys
+from quixote.ptl.ptl_compile import compile_template
+exec compile_template(open(sys.argv[1]), sys.argv[1])
+
diff --git a/pypers/europython05/Quixote-2.0/ptl/qx_distutils.py b/pypers/europython05/Quixote-2.0/ptl/qx_distutils.py
new file mode 100755
index 0000000..163545a
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/qx_distutils.py
@@ -0,0 +1,47 @@
+"""
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/ptl/qx_distutils.py $
+$Id: qx_distutils.py 26357 2005-03-16 14:56:23Z dbinger $
+
+Provides a version of the Distutils "build_py" command that knows about
+PTL files.
+"""
+
+import os, string
+from glob import glob
+from types import StringType, ListType, TupleType
+from distutils.command.build_py import build_py
+
+class qx_build_py(build_py):
+
+ def find_package_modules(self, package, package_dir):
+ self.check_package(package, package_dir)
+ module_files = (glob(os.path.join(package_dir, "*.py")) +
+ glob(os.path.join(package_dir, "*.ptl")))
+ modules = []
+ setup_script = os.path.abspath(self.distribution.script_name)
+
+ for f in module_files:
+ abs_f = os.path.abspath(f)
+ if abs_f != setup_script:
+ module = os.path.splitext(os.path.basename(f))[0]
+ modules.append((package, module, f))
+ else:
+ self.debug_print("excluding %s" % setup_script)
+ return modules
+
+ def build_module(self, module, module_file, package):
+ if type(package) is StringType:
+ package = string.split(package, '.')
+ elif type(package) not in (ListType, TupleType):
+ raise TypeError, \
+ "'package' must be a string (dot-separated), list, or tuple"
+
+ # Now put the module source file into the "build" area -- this is
+ # easy, we just copy it somewhere under self.build_lib (the build
+ # directory for Python source).
+ outfile = self.get_module_outfile(self.build_lib, package, module)
+ if module_file.endswith(".ptl"): # XXX hack for PTL
+ outfile = outfile[0:outfile.rfind('.')] + ".ptl"
+ dir = os.path.dirname(outfile)
+ self.mkpath(dir)
+ return self.copy_file(module_file, outfile, preserve_mode=0)
diff --git a/pypers/europython05/Quixote-2.0/ptl/test/utest_ptl.py b/pypers/europython05/Quixote-2.0/ptl/test/utest_ptl.py
new file mode 100755
index 0000000..91b96ba
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/ptl/test/utest_ptl.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+from sancho.utest import UTest
+from quixote.ptl.ptl_compile import compile_template
+from cStringIO import StringIO
+from quixote.html import TemplateIO, htmltext
+
+def run_ptl(*source):
+ """
+ Compile the given lines of source code using the ptl compiler
+ and run the resulting compiled code.
+ """
+ # When the ptl compiler compiles a module, it places _q_TemplateIO
+ # and _q_htmltext into the globals of the module. Here, we don't
+ # have a module, but we provide these same globals for eval.
+ eval(compile_template(StringIO('\n'.join(source)), 'test'),
+ dict(_q_TemplateIO=TemplateIO, _q_htmltext=htmltext))
+
+class Test (UTest):
+
+ def check_html(self):
+ run_ptl(
+ 'from quixote.html import htmltext',
+ 'def f [html] (a):',
+ ' "&"',
+ ' a',
+ 'assert type(f(1)) == htmltext',
+ 'assert f("") == "&"',
+ 'assert f("&") == "&&amp;"',
+ 'assert f(htmltext("&")) == "&&"')
+
+ def check_plain(self):
+ run_ptl(
+ 'from quixote.html import htmltext',
+ 'def f [plain] (a):',
+ ' "&"',
+ ' a',
+ 'assert type(f(1)) == str',
+ 'assert f("") == "&"',
+ 'assert f("&") == "&&"',
+ 'assert f(htmltext("&")) == "&&"',
+ 'assert type(f(htmltext("&"))) == str')
+
+ def check_syntax(self):
+ run_ptl('def f(a):\n a')
+ try:
+ run_ptl('def f [] (a):\n a')
+ assert 0
+ except SyntaxError, e:
+ assert e.lineno == 1
+ try:
+ run_ptl('def f [HTML] (a):\n a')
+ assert 0
+ except SyntaxError, e:
+ assert e.lineno == 1
+
+if __name__ == "__main__":
+ Test()
+
diff --git a/pypers/europython05/Quixote-2.0/publish.py b/pypers/europython05/Quixote-2.0/publish.py
new file mode 100755
index 0000000..058875b
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/publish.py
@@ -0,0 +1,336 @@
+"""$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/publish.py $
+$Id: publish.py 26333 2005-03-11 01:15:40Z dbinger $
+
+Logic for publishing modules and objects on the Web.
+"""
+
+import sys, traceback, StringIO
+import time
+import urlparse
+import cgitb
+
+from quixote.errors import PublishError, format_publish_error
+from quixote import util
+from quixote.config import Config
+from quixote.http_response import HTTPResponse
+from quixote.logger import DefaultLogger
+
+# Error message to dispay when DISPLAY_EXCEPTIONS in config file is not
+# true. Note that SERVER_ADMIN must be fetched from the environment and
+# plugged in here -- we can't do it now because the environment isn't
+# really setup for us yet if running as a FastCGI script.
+INTERNAL_ERROR_MESSAGE = """\
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
+ "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html>
+<head><title>Internal Server Error</title></head>
+<body>
+<h1>Internal Server Error</h1>
+<p>An internal error occurred while handling your request.</p>
+
+<p>The server administrator should have been notified of the problem.
+You may wish to contact the server administrator (%s) and inform them of
+the time the error occurred, and anything you might have done to trigger
+the error.</p>
+
+<p>If you are the server administrator, more information may be
+available in either the server's error log or Quixote's error log.</p>
+</body>
+</html>
+"""
+
+class Publisher:
+ """
+ The core of Quixote and of any Quixote application. This class is
+ responsible for converting each HTTP request into a traversal of the
+ application's directory tree and, ultimately, a call of a Python
+ function/method/callable object.
+
+ Each invocation of a driver script should have one Publisher
+ instance that lives for as long as the driver script itself. Eg. if
+ your driver script is plain CGI, each Publisher instance will handle
+ exactly one HTTP request; if you have a FastCGI driver, then each
+ Publisher will handle every HTTP request handed to that driver
+ script process.
+
+ Instance attributes:
+ root_directory : Directory
+ the root directory that will be searched for objects to fulfill
+ each request. This can be any object with a _q_traverse method
+ that acts like Directory._q_traverse.
+ logger : DefaultLogger
+ controls access log and error log behavior
+ session_manager : NullSessionManager
+ keeps track of sessions
+ config : Config
+ holds all configuration info for this application. If the
+ application doesn't provide values then default values
+ from the quixote.config module are used.
+ _request : HTTPRequest
+ the HTTP request currently being processed.
+ """
+
+ def __init__(self, root_directory, logger=None, session_manager=None,
+ config=None, **kwargs):
+ global _publisher
+ if config is None:
+ self.config = Config(**kwargs)
+ else:
+ if kwargs:
+ raise ValueError("cannot provide both 'config' object and"
+ " config arguments")
+ self.config = config
+ if logger is None:
+ self.logger = DefaultLogger(error_log=self.config.error_log,
+ access_log=self.config.access_log,
+ error_email=self.config.error_email)
+ else:
+ self.logger = logger
+ if session_manager is not None:
+ self.session_manager = session_manager
+ else:
+ from quixote.session import NullSessionManager
+ self.session_manager = NullSessionManager()
+
+ if _publisher is not None:
+ raise RuntimeError, "only one instance of Publisher allowed"
+ _publisher = self
+
+ if not callable(getattr(root_directory, '_q_traverse')):
+ raise TypeError(
+ 'Expected something with a _q_traverse method, got %r' %
+ root_directory)
+ self.root_directory = root_directory
+ self._request = None
+
+ def set_session_manager(self, session_manager):
+ self.session_manager = session_manager
+
+ def log(self, msg):
+ self.logger.log(msg)
+
+ def parse_request(self, request):
+ """Parse the request information waiting in 'request'.
+ """
+ request.process_inputs()
+
+ def start_request(self):
+ """Called at the start of each request.
+ """
+ self.session_manager.start_request()
+
+ def _set_request(self, request):
+ """Set the current request object.
+ """
+ self._request = request
+
+ def _clear_request(self):
+ """Unset the current request object.
+ """
+ self._request = None
+
+ def get_request(self):
+ """Return the current request object.
+ """
+ return self._request
+
+ def finish_successful_request(self):
+ """Called at the end of a successful request.
+ """
+ self.session_manager.finish_successful_request()
+
+ def format_publish_error(self, exc):
+ return format_publish_error(exc)
+
+ def finish_interrupted_request(self, exc):
+ """
+ Called at the end of an interrupted request. Requests are
+ interrupted by raising a PublishError exception. This method
+ should return a string object which will be used as the result of
+ the request.
+ """
+ if not self.config.display_exceptions and exc.private_msg:
+ exc.private_msg = None # hide it
+ request = get_request()
+ request.response = HTTPResponse(status=exc.status_code)
+ output = self.format_publish_error(exc)
+ self.session_manager.finish_successful_request()
+ return output
+
+ def finish_failed_request(self):
+ """
+ Called at the end of an failed request. Any exception (other
+ than PublishError) causes a request to fail. This method should
+ return a string object which will be used as the result of the
+ request.
+ """
+ # build new response to be safe
+ request = get_request()
+ original_response = request.response
+ request.response = HTTPResponse()
+ #self.log("caught an error (%s), reporting it." %
+ # sys.exc_info()[1])
+
+ (exc_type, exc_value, tb) = sys.exc_info()
+ error_summary = traceback.format_exception_only(exc_type, exc_value)
+ error_summary = error_summary[0][0:-1] # de-listify and strip newline
+
+ plain_error_msg = self._generate_plaintext_error(request,
+ original_response,
+ exc_type, exc_value,
+ tb)
+
+ if not self.config.display_exceptions:
+ # DISPLAY_EXCEPTIONS is false, so return the most
+ # secure (and cryptic) page.
+ request.response.set_header("Content-Type", "text/html")
+ user_error_msg = self._generate_internal_error(request)
+ elif self.config.display_exceptions == 'html':
+ # Generate a spiffy HTML display using cgitb
+ request.response.set_header("Content-Type", "text/html")
+ user_error_msg = self._generate_cgitb_error(request,
+ original_response,
+ exc_type, exc_value,
+ tb)
+ else:
+ # Generate a plaintext page containing the traceback
+ request.response.set_header("Content-Type", "text/plain")
+ user_error_msg = plain_error_msg
+
+ self.logger.log_internal_error(error_summary, plain_error_msg)
+ request.response.set_status(500)
+ self.session_manager.finish_failed_request()
+ return user_error_msg
+
+
+ def _generate_internal_error(self, request):
+ admin = request.get_environ('SERVER_ADMIN',
+ "<i>email address unknown</i>")
+ return INTERNAL_ERROR_MESSAGE % admin
+
+
+ def _generate_plaintext_error(self, request, original_response,
+ exc_type, exc_value, tb):
+ error_file = StringIO.StringIO()
+
+ # format the traceback
+ traceback.print_exception(exc_type, exc_value, tb, file=error_file)
+
+ # include request and response dumps
+ error_file.write('\n')
+ error_file.write(request.dump())
+ error_file.write('\n')
+
+ return error_file.getvalue()
+
+
+ def _generate_cgitb_error(self, request, original_response,
+ exc_type, exc_value, tb):
+ error_file = StringIO.StringIO()
+ hook = cgitb.Hook(file=error_file)
+ hook(exc_type, exc_value, tb)
+ error_file.write('<h2>Original Request</h2>')
+ error_file.write(str(util.dump_request(request)))
+ error_file.write('<h2>Original Response</h2><pre>')
+ original_response.write(error_file)
+ error_file.write('</pre>')
+ return error_file.getvalue()
+
+
+ def try_publish(self, request):
+ """(request : HTTPRequest) -> object
+
+ The master method that does all the work for a single request.
+ Exceptions are handled by the caller.
+ """
+ self.start_request()
+ path = request.get_environ('PATH_INFO', '')
+ assert path[:1] == '/'
+ # split path into components
+ path = path[1:].split('/')
+ output = self.root_directory._q_traverse(path)
+ # The callable ran OK, commit any changes to the session
+ self.finish_successful_request()
+ return output
+
+ def filter_output(self, request, output):
+ """Hook for post processing the output. Subclasses may wish to
+ override (e.g. check HTML syntax).
+ """
+ return output
+
+ def process_request(self, request):
+ """(request : HTTPRequest) -> HTTPResponse
+
+ Process a single request, given an HTTPRequest object. The
+ try_publish() method will be called to do the work and
+ exceptions will be handled here.
+ """
+ self._set_request(request)
+ start_time = time.time()
+ try:
+ self.parse_request(request)
+ output = self.try_publish(request)
+ except PublishError, exc:
+ # Exit the publishing loop and return a result right away.
+ output = self.finish_interrupted_request(exc)
+ except:
+ # Some other exception, generate error messages to the logs, etc.
+ output = self.finish_failed_request()
+ output = self.filter_output(request, output)
+ self.logger.log_request(request, start_time)
+ if output:
+ if self.config.compress_pages and request.get_encoding(["gzip"]):
+ compress = True
+ else:
+ compress = False
+ request.response.set_body(output, compress)
+ self._clear_request()
+ return request.response
+
+
+# Publisher singleton, only one of these per process.
+_publisher = None
+
+def get_publisher():
+ return _publisher
+
+def get_request():
+ return _publisher.get_request()
+
+def get_response():
+ return _publisher.get_request().response
+
+def get_field(name, default=None):
+ return _publisher.get_request().get_field(name, default)
+
+def get_cookie(name, default=None):
+ return _publisher.get_request().get_cookie(name, default)
+
+def get_path(n=0):
+ return _publisher.get_request().get_path(n)
+
+def redirect(location, permanent=False):
+ """(location : string, permanent : boolean = false) -> string
+
+ Create a redirection response. If the location is relative, then it
+ will automatically be made absolute. The return value is an HTML
+ document indicating the new URL (useful if the client browser does
+ not honor the redirect).
+ """
+ request = _publisher.get_request()
+ location = urlparse.urljoin(request.get_url(), str(location))
+ return request.response.redirect(location, permanent)
+
+def get_session():
+ return _publisher.get_request().session
+
+def get_session_manager():
+ return _publisher.session_manager
+
+def get_user():
+ session = _publisher.get_request().session
+ if session is None:
+ return None
+ else:
+ return session.user
diff --git a/pypers/europython05/Quixote-2.0/publish1.py b/pypers/europython05/Quixote-2.0/publish1.py
new file mode 100755
index 0000000..93bfaf3
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/publish1.py
@@ -0,0 +1,270 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/publish1.py $
+$Id: publish1.py 25664 2004-11-22 20:35:07Z nascheme $
+
+Provides a publisher object that behaves like the Quixote 1 Publisher.
+Specifically, arbitrary namespaces may be exported and the HTTPRequest
+object is passed as the first argument to exported functions. Also,
+the _q_lookup(), _q_resolve(), and _q_access() methods work as they did
+in Quixote 1.
+"""
+
+import sys
+import re
+import types
+import warnings
+from quixote import errors, get_request, redirect
+from quixote.publish import Publisher as _Publisher
+from quixote.directory import Directory
+from quixote.html import htmltext
+
+
+class Publisher(_Publisher):
+ """
+ Instance attributes:
+ namespace_stack : [ module | instance | class ]
+ """
+
+ def __init__(self, root_namespace, config=None):
+ from quixote.config import Config
+ if type(root_namespace) is types.StringType:
+ root_namespace = _get_module(root_namespace)
+ self.namespace_stack = [root_namespace]
+ if config is None:
+ config = Config()
+ directory = RootDirectory(root_namespace, self.namespace_stack)
+ _Publisher.__init__(self, directory, config=config)
+
+ def debug(self, msg):
+ self.log(msg)
+
+ def get_namespace_stack(self):
+ """get_namespace_stack() -> [ module | instance | class ]
+ """
+ return self.namespace_stack
+
+
+class RootDirectory(Directory):
+ def __init__(self, root_namespace, namespace_stack):
+ self.root_namespace = root_namespace
+ self.namespace_stack = namespace_stack
+
+ def _q_traverse(self, path):
+ # Initialize the publisher's namespace_stack
+ del self.namespace_stack[:]
+
+ request = get_request()
+
+ # Traverse package to a (hopefully-) callable object
+ object = _traverse_url(self.root_namespace, path, request,
+ self.namespace_stack)
+
+ # None means no output -- traverse_url() just issued a redirect.
+ if object is None:
+ return None
+
+ # Anything else must be either a string...
+ if isstring(object):
+ output = object
+
+ # ...or a callable.
+ elif callable(object):
+ output = object(request)
+ if output is None:
+ raise RuntimeError, 'callable %s returned None' % repr(object)
+
+ # Uh-oh: 'object' is neither a string nor a callable.
+ else:
+ raise RuntimeError(
+ "object is neither callable nor a string: %s" % repr(object))
+
+ return output
+
+
+def _get_module(name):
+ """Get a module object by name."""
+ __import__(name)
+ module = sys.modules[name]
+ return module
+
+
+_slash_pat = re.compile("//*")
+
+def _traverse_url(root_namespace, path_components, request, namespace_stack):
+ """(root_namespace : any, path_components : [string],
+ request : HTTPRequest, namespace_stack : list) -> (object : any)
+
+ Perform traversal based on the provided path, starting at the root
+ object. It returns the script name and path info values for
+ the arrived-at object, along with the object itself and
+ a list of the namespaces traversed to get there.
+
+ It's expected that the final object is something callable like a
+ function or a method; intermediate objects along the way will
+ usually be packages or modules.
+
+ To prevent crackers from writing URLs that traverse private
+ objects, every package, module, or object along the way must have
+ a _q_exports attribute containing a list of publicly visible
+ names. Not having a _q_exports attribute is an error, though
+ having _q_exports be an empty list is OK. If a component of the path
+ isn't in _q_exports, that also produces an error.
+
+ Modifies the namespace_stack as it traverses the url, so that
+ any exceptions encountered along the way can be handled by the
+ nearest handler.
+ """
+
+ path = '/' + '/'.join(path_components)
+
+ # If someone accesses a Quixote driver script without a trailing
+ # slash, we'll wind up here with an empty path. This won't
+ # work; relative references in the page generated by the root
+ # namespace's _q_index() will be off. Fix it by redirecting the
+ # user to the right URL; when the client follows the redirect,
+ # we'll wind up here again with path == '/'.
+ if not path:
+ return redirect(request.environ['SCRIPT_NAME'] + '/' , permanent=1)
+
+ # Traverse starting at the root
+ object = root_namespace
+ namespace_stack.append(object)
+
+ # Loop over the components of the path
+ for component in path_components:
+ if component == "":
+ # "/q/foo/" == "/q/foo/_q_index"
+ component = "_q_index"
+ object = _get_component(object, component, request, namespace_stack)
+
+ if not (isstring(object) or callable(object)):
+ # We went through all the components of the path and ended up at
+ # something which isn't callable, like a module or an instance
+ # without a __call__ method.
+ if path[-1] != '/':
+ if not request.form:
+ # This is for the convenience of users who type in paths.
+ # Repair the path and redirect. This should not happen for
+ # URLs within the site.
+ return redirect(request.get_path() + "/", permanent=1)
+
+ else:
+ # Automatic redirects disabled or there is form data. If
+ # there is form data then the programmer is using the
+ # wrong path. A redirect won't work if the form data came
+ # from a POST anyhow.
+ raise errors.TraversalError(
+ "object is neither callable nor string "
+ "(missing trailing slash?)",
+ private_msg=repr(object),
+ path=path)
+ else:
+ raise errors.TraversalError(
+ "object is neither callable nor string",
+ private_msg=repr(object),
+ path=path)
+
+ return object
+
+
+def _get_component(container, component, request, namespace_stack):
+ """Get one component of a path from a namespace.
+ """
+ # First security check: if the container doesn't even have an
+ # _q_exports list, fail now: all Quixote-traversable namespaces
+ # (modules, packages, instances) must have an export list!
+ if not hasattr(container, '_q_exports'):
+ raise errors.TraversalError(
+ private_msg="%r has no _q_exports list" % container)
+
+ # Second security check: call _q_access function if it's present.
+ if hasattr(container, '_q_access'):
+ # will raise AccessError if access failed
+ container._q_access(request)
+
+ # Third security check: make sure the current name component
+ # is in the export list or is '_q_index'. If neither
+ # condition is true, check for a _q_lookup() and call it.
+ # '_q_lookup()' translates an arbitrary string into an object
+ # that we continue traversing. (This is very handy; it lets
+ # you put user-space objects into your URL-space, eliminating
+ # the need for digging ID strings out of a query, or checking
+ # PATHINFO after Quixote's done with it. But it is a
+ # compromise to security: it opens up the traversal algorithm
+ # to arbitrary names not listed in _q_exports!) If
+ # _q_lookup() doesn't exist or is None, a TraversalError is
+ # raised.
+
+ # Check if component is in _q_exports. The elements in
+ # _q_exports can be strings or 2-tuples mapping external names
+ # to internal names.
+ if component in container._q_exports or component == '_q_index':
+ internal_name = component
+ else:
+ # check for an explicit external to internal mapping
+ for value in container._q_exports:
+ if type(value) is types.TupleType:
+ if value[0] == component:
+ internal_name = value[1]
+ break
+ else:
+ internal_name = None
+
+ if internal_name is None:
+ # Component is not in exports list.
+ object = None
+ if hasattr(container, "_q_lookup"):
+ object = container._q_lookup(request, component)
+ elif hasattr(container, "_q_getname"):
+ warnings.warn("_q_getname() on %s used; should "
+ "be replaced by _q_lookup()" % type(container))
+ object = container._q_getname(request, component)
+ if object is None:
+ raise errors.TraversalError(
+ private_msg="object %r has no attribute %r" % (
+ container,
+ component))
+
+ # From here on, you can assume that the internal_name is not None
+ elif hasattr(container, internal_name):
+ # attribute is in _q_exports and exists
+ object = getattr(container, internal_name)
+
+ elif internal_name == '_q_index':
+ if hasattr(container, "_q_lookup"):
+ object = container._q_lookup(request, "")
+ else:
+ raise errors.AccessError(
+ private_msg=("_q_index not found in %r" % container))
+
+ elif hasattr(container, "_q_resolve"):
+ object = container._q_resolve(internal_name)
+ if object is None:
+ raise RuntimeError, ("component listed in _q_exports, "
+ "but not returned by _q_resolve(%r)"
+ % internal_name)
+ else:
+ # Set the object, so _q_resolve won't need to be called again.
+ setattr(container, internal_name, object)
+
+ elif type(container) is types.ModuleType:
+ # try importing it as a sub-module. If we get an ImportError
+ # here we don't catch it. It means that something that
+ # doesn't exist was exported or an exception was raised from
+ # deeper in the code.
+ mod_name = container.__name__ + '.' + internal_name
+ object = _get_module(mod_name)
+
+ else:
+ # a non-existent attribute is in _q_exports,
+ # and the container is not a module. Give up.
+ raise errors.TraversalError(
+ private_msg=("%r in _q_exports list, "
+ "but not found in %r" % (component,
+ container)))
+
+ namespace_stack.append(object)
+ return object
+
+
+def isstring(x):
+ return isinstance(x, (str, unicode, htmltext))
diff --git a/pypers/europython05/Quixote-2.0/sendmail.py b/pypers/europython05/Quixote-2.0/sendmail.py
new file mode 100755
index 0000000..0a13884
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/sendmail.py
@@ -0,0 +1,273 @@
+"""quixote.sendmail
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/sendmail.py $
+$Id: sendmail.py 25234 2004-09-30 17:36:19Z nascheme $
+
+Tools for sending mail from Quixote applications.
+"""
+
+# created 2001/08/27, Greg Ward (with a long and complicated back-story)
+
+__revision__ = "$Id: sendmail.py 25234 2004-09-30 17:36:19Z nascheme $"
+
+import re
+from types import ListType, TupleType, StringType
+from smtplib import SMTP
+
+rfc822_specials_re = re.compile(r'[\(\)\<\>\@\,\;\:\\\"\.\[\]]')
+
+class RFC822Mailbox:
+ """
+ In RFC 822, a "mailbox" is either a bare e-mail address or a bare
+ e-mail address coupled with a chunk of text, most often someone's
+ name. Eg. the following are all "mailboxes" in the RFC 822 grammar:
+ luser@example.com
+ Joe Luser <luser@example.com>
+ Paddy O'Reilly <paddy@example.ie>
+ "Smith, John" <smith@example.com>
+ Dick & Jane <dickjane@example.net>
+ "Tom, Dick, & Harry" <tdh@example.org>
+
+ This class represents an (addr_spec, real_name) pair and takes care
+ of quoting the real_name according to RFC 822's rules for you.
+ Just use the format() method and it will spit out a properly-
+ quoted RFC 822 "mailbox".
+ """
+
+ def __init__(self, *args):
+ """RFC822Mailbox(addr_spec : string, name : string)
+ RFC822Mailbox(addr_spec : string)
+ RFC822Mailbox((addr_spec : string, name : string))
+ RFC822Mailbox((addr_spec : string))
+
+ Create a new RFC822Mailbox instance. The variety of call
+ signatures is purely for your convenience.
+ """
+ if (len(args) == 1 and type(args[0]) is TupleType):
+ args = args[0]
+
+ if len(args) == 1:
+ addr_spec = args[0]
+ real_name = None
+ elif len(args) == 2:
+ (addr_spec, real_name) = args
+ else:
+ raise TypeError(
+ "invalid number of arguments: "
+ "expected 1 or 2 strings or "
+ "a tuple of 1 or 2 strings")
+
+ self.addr_spec = addr_spec
+ self.real_name = real_name
+
+ def __str__(self):
+ return self.addr_spec
+
+ def __repr__(self):
+ return "<%s at %x: %s>" % (self.__class__.__name__, id(self), self)
+
+ def format(self):
+ if self.real_name and rfc822_specials_re.search(self.real_name):
+ return '"%s" <%s>' % (self.real_name.replace('"', '\\"'),
+ self.addr_spec)
+ elif self.real_name:
+ return '%s <%s>' % (self.real_name, self.addr_spec)
+
+ else:
+ return self.addr_spec
+
+
+def _ensure_mailbox(s):
+ """_ensure_mailbox(s : string |
+ (string,) |
+ (string, string) |
+ RFC822Mailbox |
+ None)
+ -> RFC822Mailbox | None
+
+ If s is a string, or a tuple of 1 or 2 strings, returns an
+ RFC822Mailbox encapsulating them as an addr_spec and real_name. If
+ s is already an RFC822Mailbox, returns s. If s is None, returns
+ None.
+ """
+ if s is None or isinstance(s, RFC822Mailbox):
+ return s
+ else:
+ return RFC822Mailbox(s)
+
+
+# Maximum number of recipients that will be explicitly listed in
+# any single message header. Eg. if MAX_HEADER_RECIPIENTS is 10,
+# there could be up to 10 "To" recipients and 10 "CC" recipients
+# explicitly listed in the message headers.
+MAX_HEADER_RECIPIENTS = 10
+
+def _add_recip_headers(headers, field_name, addrs):
+ if not addrs:
+ return
+ addrs = [addr.format() for addr in addrs]
+
+ if len(addrs) == 1:
+ headers.append("%s: %s" % (field_name, addrs[0]))
+ elif len(addrs) <= MAX_HEADER_RECIPIENTS:
+ headers.append("%s: %s," % (field_name, addrs[0]))
+ for addr in addrs[1:-1]:
+ headers.append(" %s," % addr)
+ headers.append(" %s" % addrs[-1])
+ else:
+ headers.append("%s: (long recipient list suppressed) : ;" % field_name)
+
+
+def sendmail(subject, msg_body, to_addrs,
+ from_addr=None, cc_addrs=None,
+ extra_headers=None,
+ smtp_sender=None, smtp_recipients=None,
+ config=None):
+ """sendmail(subject : string,
+ msg_body : string,
+ to_addrs : [email_address],
+ from_addr : email_address = config.MAIL_SENDER,
+ cc_addrs : [email_address] = None,
+ extra_headers : [string] = None,
+ smtp_sender : email_address = (derived from from_addr)
+ smtp_recipients : [email_address] = (derived from to_addrs),
+ config : quixote.config.Config = (current publisher's config)):
+
+ Send an email message to a list of recipients via a local SMTP
+ server. In normal use, you supply a list of primary recipient
+ e-mail addresses in 'to_addrs', an optional list of secondary
+ recipient addresses in 'cc_addrs', and a sender address in
+ 'from_addr'. sendmail() then constructs a message using those
+ addresses, 'subject', and 'msg_body', and mails the message to every
+ recipient address. (Specifically, it connects to the mail server
+ named in the MAIL_SERVER config variable -- default "localhost" --
+ and instructs the server to send the message to every recipient
+ address in 'to_addrs' and 'cc_addrs'.)
+
+ 'from_addr' is optional because web applications often have a common
+ e-mail sender address, such as "webmaster@example.com". Just set
+ the Quixote config variable MAIL_FROM, and it will be used as the
+ default sender (both header and envelope) for all e-mail sent by
+ sendmail().
+
+ E-mail addresses can be specified a number of ways. The most
+ efficient is to supply instances of RFC822Mailbox, which bundles a
+ bare e-mail address (aka "addr_spec" from the RFC 822 grammar) and
+ real name together in a readily-formattable object. You can also
+ supply an (addr_spec, real_name) tuple, or an addr_spec on its own.
+ The latter two are converted into RFC822Mailbox objects for
+ formatting, which is why it may be more efficient to construct
+ RFC822Mailbox objects yourself.
+
+ Thus, the following are all equivalent in terms of who gets the
+ message:
+ sendmail(to_addrs=["joe@example.com"], ...)
+ sendmail(to_addrs=[("joe@example.com", "Joe User")], ...)
+ sendmail(to_addrs=[RFC822Mailbox("joe@example.com", "Joe User")], ...)
+ ...although the "To" header will be slightly different. In the
+ first case, it will be
+ To: joe@example.com
+ while in the other two, it will be:
+ To: Joe User <joe@example.com>
+ which is a little more user-friendly.
+
+ In more advanced usage, you might wish to specify the SMTP sender
+ and recipient addresses separately. For example, if you want your
+ application to send mail to users that looks like it comes from a
+ real human being, but you don't want that human being to get the
+ bounce messages from the mailing, you might do this:
+ sendmail(to_addrs=user_list,
+ ...,
+ from_addr=("realuser@example.com", "A Real User"),
+ smtp_sender="postmaster@example.com")
+
+ End users will see mail from "A Real User <realuser@example.com>" in
+ their inbox, but bounces will go to postmaster@example.com.
+
+ One use of different header and envelope recipients is for
+ testing/debugging. If you want to test that your application is
+ sending the right mail to bigboss@example.com without filling
+ bigboss' inbox with dross, you might do this:
+ sendmail(to_addrs=["bigboss@example.com"],
+ ...,
+ smtp_recipients=["developers@example.com"])
+
+ This is so useful that it's a Quixote configuration option: just set
+ MAIL_DEBUG_ADDR to (eg.) "developers@example.com", and every message
+ that sendmail() would send out is diverted to the debug address.
+
+ Generally raises an exception on any SMTP errors; see smtplib (in
+ the standard library documentation) for details.
+ """
+ if config is None:
+ from quixote import get_publisher
+ config = get_publisher().config
+
+ if not isinstance(to_addrs, ListType):
+ raise TypeError("'to_addrs' must be a list")
+ if not (cc_addrs is None or isinstance(cc_addrs, ListType)):
+ raise TypeError("'cc_addrs' must be a list or None")
+
+ # Make sure we have a "From" address
+ if from_addr is None:
+ from_addr = config.mail_from
+ if from_addr is None:
+ raise RuntimeError(
+ "no from_addr supplied, and MAIL_FROM not set in config file")
+
+ # Ensure all of our addresses are really RFC822Mailbox objects.
+ from_addr = _ensure_mailbox(from_addr)
+ to_addrs = map(_ensure_mailbox, to_addrs)
+ if cc_addrs:
+ cc_addrs = map(_ensure_mailbox, cc_addrs)
+
+ # Start building the message headers.
+ headers = ["From: %s" % from_addr.format(),
+ "Subject: %s" % subject]
+ _add_recip_headers(headers, "To", to_addrs)
+
+ if cc_addrs:
+ _add_recip_headers(headers, "Cc", cc_addrs)
+
+ if extra_headers:
+ headers.extend(extra_headers)
+
+ if config.mail_debug_addr:
+ debug1 = ("[debug mode, message actually sent to %s]\n"
+ % config.mail_debug_addr)
+ if smtp_recipients:
+ debug2 = ("[original SMTP recipients: %s]\n"
+ % ", ".join(smtp_recipients))
+ else:
+ debug2 = ""
+
+ sep = ("-"*72) + "\n"
+ msg_body = debug1 + debug2 + sep + msg_body
+
+ smtp_recipients = [config.mail_debug_addr]
+
+ if smtp_sender is None:
+ smtp_sender = from_addr.addr_spec
+ else:
+ smtp_sender = _ensure_mailbox(smtp_sender).addr_spec
+
+ if smtp_recipients is None:
+ smtp_recipients = [addr.addr_spec for addr in to_addrs]
+ if cc_addrs:
+ smtp_recipients.extend([addr.addr_spec for addr in cc_addrs])
+ else:
+ smtp_recipients = [_ensure_mailbox(recip).addr_spec
+ for recip in smtp_recipients]
+
+ message = "\n".join(headers) + "\n\n" + msg_body
+
+ # Sanity checks
+ assert type(smtp_sender) is StringType, \
+ "smtp_sender not a string: %r" % (smtp_sender,)
+ assert (type(smtp_recipients) is ListType and
+ map(type, smtp_recipients) == [StringType]*len(smtp_recipients)), \
+ "smtp_recipients not a list of strings: %r" % (smtp_recipients,)
+ smtp = SMTP(config.mail_server)
+ smtp.sendmail(smtp_sender, smtp_recipients, message)
+ smtp.quit()
+
+# sendmail ()
diff --git a/pypers/europython05/Quixote-2.0/server/__init__.py b/pypers/europython05/Quixote-2.0/server/__init__.py
new file mode 100755
index 0000000..6947382
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/__init__.py
@@ -0,0 +1,5 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/__init__.py $
+$Id: __init__.py 25579 2004-11-11 20:56:32Z nascheme $
+
+This package is for Quixote to server glue.
+"""
diff --git a/pypers/europython05/Quixote-2.0/server/_fcgi.py b/pypers/europython05/Quixote-2.0/server/_fcgi.py
new file mode 100755
index 0000000..7ac41d2
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/_fcgi.py
@@ -0,0 +1,466 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/_fcgi.py $
+$Id: _fcgi.py 25688 2004-11-30 20:05:33Z dbinger $
+Derived from Robin Dunn's FastCGI module,
+available at http://alldunn.com/python/#fcgi.
+"""
+#------------------------------------------------------------------------
+# Copyright (c) 1998 by Total Control Software
+# All Rights Reserved
+#------------------------------------------------------------------------
+#
+# Module Name: fcgi.py
+#
+# Description: Handles communication with the FastCGI module of the
+# web server without using the FastCGI developers kit, but
+# will also work in a non-FastCGI environment, (straight CGI.)
+# This module was originally fetched from someplace on the
+# Net (I don't remember where and I can't find it now...) and
+# has been significantly modified to fix several bugs, be more
+# readable, more robust at handling large CGI data and return
+# document sizes, and also to fit the model that we had previously
+# used for FastCGI.
+#
+# WARNING: If you don't know what you are doing, don't tinker with this
+# module!
+#
+# Creation Date: 1/30/98 2:59:04PM
+#
+# License: This is free software. You may use this software for any
+# purpose including modification/redistribution, so long as
+# this header remains intact and that you do not claim any
+# rights of ownership or authorship of this software. This
+# software has been tested, but no warranty is expressed or
+# implied.
+#
+#------------------------------------------------------------------------
+
+__revision__ = "$Id: _fcgi.py 25688 2004-11-30 20:05:33Z dbinger $"
+
+
+import os, sys, string, socket, errno, struct
+from cStringIO import StringIO
+import cgi
+
+#---------------------------------------------------------------------------
+
+# Set various FastCGI constants
+# Maximum number of requests that can be handled
+FCGI_MAX_REQS=1
+FCGI_MAX_CONNS = 1
+
+# Supported version of the FastCGI protocol
+FCGI_VERSION_1 = 1
+
+# Boolean: can this application multiplex connections?
+FCGI_MPXS_CONNS=0
+
+# Record types
+FCGI_BEGIN_REQUEST = 1 ; FCGI_ABORT_REQUEST = 2 ; FCGI_END_REQUEST = 3
+FCGI_PARAMS = 4 ; FCGI_STDIN = 5 ; FCGI_STDOUT = 6
+FCGI_STDERR = 7 ; FCGI_DATA = 8 ; FCGI_GET_VALUES = 9
+FCGI_GET_VALUES_RESULT = 10
+FCGI_UNKNOWN_TYPE = 11
+FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
+
+# Types of management records
+ManagementTypes = [FCGI_GET_VALUES]
+
+FCGI_NULL_REQUEST_ID = 0
+
+# Masks for flags component of FCGI_BEGIN_REQUEST
+FCGI_KEEP_CONN = 1
+
+# Values for role component of FCGI_BEGIN_REQUEST
+FCGI_RESPONDER = 1 ; FCGI_AUTHORIZER = 2 ; FCGI_FILTER = 3
+
+# Values for protocolStatus component of FCGI_END_REQUEST
+FCGI_REQUEST_COMPLETE = 0 # Request completed nicely
+FCGI_CANT_MPX_CONN = 1 # This app can't multiplex
+FCGI_OVERLOADED = 2 # New request rejected; too busy
+FCGI_UNKNOWN_ROLE = 3 # Role value not known
+
+
+error = 'fcgi.error'
+
+
+#---------------------------------------------------------------------------
+
+# The following function is used during debugging; it isn't called
+# anywhere at the moment
+
+def error(msg):
+ "Append a string to /tmp/err"
+ errf = open('/tmp/err', 'a+')
+ errf.write(msg+'\n')
+ errf.close()
+
+#---------------------------------------------------------------------------
+
+class record:
+ "Class representing FastCGI records"
+ def __init__(self):
+ self.version = FCGI_VERSION_1
+ self.recType = FCGI_UNKNOWN_TYPE
+ self.reqId = FCGI_NULL_REQUEST_ID
+ self.content = ""
+
+ #----------------------------------------
+ def readRecord(self, sock, unpack=struct.unpack):
+ (self.version, self.recType, self.reqId, contentLength,
+ paddingLength) = unpack(">BBHHBx", sock.recv(8))
+
+ content = ""
+ while len(content) < contentLength:
+ content = content + sock.recv(contentLength - len(content))
+ self.content = content
+
+ if paddingLength != 0:
+ padding = sock.recv(paddingLength)
+
+ # Parse the content information
+ if self.recType == FCGI_BEGIN_REQUEST:
+ (self.role, self.flags) = unpack(">HB", content[:3])
+
+ elif self.recType == FCGI_UNKNOWN_TYPE:
+ self.unknownType = ord(content[0])
+
+ elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
+ self.values = {}
+ pos = 0
+ while pos < len(content):
+ name, value, pos = readPair(content, pos)
+ self.values[name] = value
+
+ elif self.recType == FCGI_END_REQUEST:
+ (self.appStatus, self.protocolStatus) = unpack(">IB", content[0:5])
+
+ #----------------------------------------
+ def writeRecord(self, sock, pack=struct.pack):
+ content = self.content
+ if self.recType == FCGI_BEGIN_REQUEST:
+ content = pack(">HBxxxxx", self.role, self.flags)
+
+ elif self.recType == FCGI_UNKNOWN_TYPE:
+ content = pack(">Bxxxxxx", self.unknownType)
+
+ elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
+ content = ""
+ for i in self.values.keys():
+ content = content + writePair(i, self.values[i])
+
+ elif self.recType == FCGI_END_REQUEST:
+ content = pack(">IBxxx", self.appStatus, self.protocolStatus)
+
+ cLen = len(content)
+ eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary
+ padLen = eLen - cLen
+
+ hdr = pack(">BBHHBx", self.version, self.recType, self.reqId, cLen,
+ padLen)
+
+ ##debug.write('Sending fcgi record: %s\n' % repr(content[:50]) )
+ sock.send(hdr + content + padLen*'\000')
+
+#---------------------------------------------------------------------------
+
+_lowbits = ~(1L << 31) # everything but the 31st bit
+
+def readPair(s, pos):
+ nameLen = ord(s[pos]) ; pos = pos+1
+ if nameLen & 128:
+ pos = pos + 3
+ nameLen = int(struct.unpack(">I", s[pos-4:pos])[0] & _lowbits)
+ valueLen = ord(s[pos]) ; pos = pos+1
+ if valueLen & 128:
+ pos = pos + 3
+ valueLen = int(struct.unpack(">I", s[pos-4:pos])[0] & _lowbits)
+ return ( s[pos:pos+nameLen], s[pos+nameLen:pos+nameLen+valueLen],
+ pos+nameLen+valueLen )
+
+#---------------------------------------------------------------------------
+
+_highbit = (1L << 31)
+
+def writePair(name, value):
+ l = len(name)
+ if l < 128:
+ s = chr(l)
+ else:
+ s = struct.pack(">I", l | _highbit)
+ l = len(value)
+ if l < 128:
+ s = s + chr(l)
+ else:
+ s = s + struct.pack(">I", l | _highbit)
+ return s + name + value
+
+#---------------------------------------------------------------------------
+
+def HandleManTypes(r, conn):
+ if r.recType == FCGI_GET_VALUES:
+ r.recType = FCGI_GET_VALUES_RESULT
+ v = {}
+ vars = {'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
+ 'FCGI_MAX_REQS' : FCGI_MAX_REQS,
+ 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS}
+ for i in r.values.keys():
+ if vars.has_key(i): v[i] = vars[i]
+ r.values = vars
+ r.writeRecord(conn)
+
+#---------------------------------------------------------------------------
+#---------------------------------------------------------------------------
+
+
+_isFCGI = 1 # assume it is until we find out for sure
+
+def isFCGI():
+ return _isFCGI
+
+
+
+#---------------------------------------------------------------------------
+
+
+_init = None
+_sock = None
+
+class FCGI:
+ def __init__(self):
+ self.haveFinished = 0
+ if _init == None:
+ _startup()
+ if not _isFCGI:
+ self.haveFinished = 1
+ self.inp = sys.__stdin__
+ self.out = sys.__stdout__
+ self.err = sys.__stderr__
+ self.env = os.environ
+ return
+
+ if os.environ.has_key('FCGI_WEB_SERVER_ADDRS'):
+ good_addrs = string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',')
+ good_addrs = map(string.strip, good_addrs) # Remove whitespace
+ else:
+ good_addrs = None
+
+ self.conn, addr = _sock.accept()
+ stdin, data = "", ""
+ self.env = {}
+ self.requestId = 0
+ remaining = 1
+
+ # Check if the connection is from a legal address
+ if good_addrs != None and addr not in good_addrs:
+ raise error, 'Connection from invalid server!'
+
+ while remaining:
+ r = record()
+ r.readRecord(self.conn)
+
+ if r.recType in ManagementTypes:
+ HandleManTypes(r, self.conn)
+
+ elif r.reqId == 0:
+ # Oh, poopy. It's a management record of an unknown
+ # type. Signal the error.
+ r2 = record()
+ r2.recType = FCGI_UNKNOWN_TYPE
+ r2.unknownType = r.recType
+ r2.writeRecord(self.conn)
+ continue # Charge onwards
+
+ # Ignore requests that aren't active
+ elif r.reqId != self.requestId and r.recType != FCGI_BEGIN_REQUEST:
+ continue
+
+ # If we're already doing a request, ignore further BEGIN_REQUESTs
+ elif r.recType == FCGI_BEGIN_REQUEST and self.requestId != 0:
+ continue
+
+ # Begin a new request
+ if r.recType == FCGI_BEGIN_REQUEST:
+ self.requestId = r.reqId
+ if r.role == FCGI_AUTHORIZER: remaining = 1
+ elif r.role == FCGI_RESPONDER: remaining = 2
+ elif r.role == FCGI_FILTER: remaining = 3
+
+ elif r.recType == FCGI_PARAMS:
+ if r.content == "":
+ remaining = remaining-1
+ else:
+ for i in r.values.keys():
+ self.env[i] = r.values[i]
+
+ elif r.recType == FCGI_STDIN:
+ if r.content == "":
+ remaining = remaining-1
+ else:
+ stdin = stdin+r.content
+
+ elif r.recType == FCGI_DATA:
+ if r.content == "":
+ remaining = remaining-1
+ else:
+ data = data+r.content
+ # end of while remaining:
+
+ self.inp = StringIO(stdin)
+ self.err = StringIO()
+ self.out = StringIO()
+ self.data = StringIO(data)
+
+ def __del__(self):
+ self.Finish()
+
+ def Finish(self, status=0):
+ if not self.haveFinished:
+ self.haveFinished = 1
+
+ self.err.seek(0,0)
+ self.out.seek(0,0)
+
+ ##global debug
+ ##debug = open("/tmp/quixote-debug.log", "a+")
+ ##debug.write("fcgi.FCGI.Finish():\n")
+
+ r = record()
+ r.recType = FCGI_STDERR
+ r.reqId = self.requestId
+ data = self.err.read()
+ ##debug.write(" sending stderr (%s)\n" % `self.err`)
+ ##debug.write(" data = %s\n" % `data`)
+ while data:
+ chunk, data = self.getNextChunk(data)
+ ##debug.write(" chunk, data = %s, %s\n" % (`chunk`, `data`))
+ r.content = chunk
+ r.writeRecord(self.conn)
+ r.content = ""
+ r.writeRecord(self.conn) # Terminate stream
+
+ r.recType = FCGI_STDOUT
+ data = self.out.read()
+ ##debug.write(" sending stdout (%s)\n" % `self.out`)
+ ##debug.write(" data = %s\n" % `data`)
+ while data:
+ chunk, data = self.getNextChunk(data)
+ r.content = chunk
+ r.writeRecord(self.conn)
+ r.content = ""
+ r.writeRecord(self.conn) # Terminate stream
+
+ r = record()
+ r.recType = FCGI_END_REQUEST
+ r.reqId = self.requestId
+ r.appStatus = status
+ r.protocolStatus = FCGI_REQUEST_COMPLETE
+ r.writeRecord(self.conn)
+ self.conn.close()
+
+ #debug.close()
+
+
+ def getFieldStorage(self):
+ method = 'GET'
+ if self.env.has_key('REQUEST_METHOD'):
+ method = string.upper(self.env['REQUEST_METHOD'])
+ if method == 'GET':
+ return cgi.FieldStorage(environ=self.env, keep_blank_values=1)
+ else:
+ return cgi.FieldStorage(fp=self.inp,
+ environ=self.env,
+ keep_blank_values=1)
+
+ def getNextChunk(self, data):
+ chunk = data[:8192]
+ data = data[8192:]
+ return chunk, data
+
+
+Accept = FCGI # alias for backwards compatibility
+#---------------------------------------------------------------------------
+
+def _startup():
+ global _isFCGI, _init, _sock
+ # This function won't work on Windows at all.
+ if sys.platform[:3] == 'win':
+ _isFCGI = 0
+ return
+
+ _init = 1
+ try:
+ s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET,
+ socket.SOCK_STREAM)
+ s.getpeername()
+ except socket.error, (err, errmsg):
+ if err != errno.ENOTCONN: # must be a non-fastCGI environment
+ _isFCGI = 0
+ return
+
+ _sock = s
+
+
+#---------------------------------------------------------------------------
+
+def _test():
+ counter = 0
+ try:
+ while isFCGI():
+ req = Accept()
+ counter = counter+1
+
+ try:
+ fs = req.getFieldStorage()
+ size = string.atoi(fs['size'].value)
+ doc = ['*' * size]
+ except:
+ doc = ['<HTML><HEAD>'
+ '<TITLE>FCGI TestApp</TITLE>'
+ '</HEAD>\n<BODY>\n']
+ doc.append('<H2>FCGI TestApp</H2><P>')
+ doc.append('<b>request count</b> = %d<br>' % counter)
+ doc.append('<b>pid</b> = %s<br>' % os.getpid())
+ if req.env.has_key('CONTENT_LENGTH'):
+ cl = string.atoi(req.env['CONTENT_LENGTH'])
+ doc.append('<br><b>POST data (%s):</b><br><pre>' % cl)
+ keys = fs.keys()
+ keys.sort()
+ for k in keys:
+ val = fs[k]
+ if type(val) == type([]):
+ doc.append(' <b>%-15s :</b> %s\n'
+ % (k, val))
+ else:
+ doc.append(' <b>%-15s :</b> %s\n'
+ % (k, val.value))
+ doc.append('</pre>')
+
+
+ doc.append('<P><HR><P><pre>')
+ keys = req.env.keys()
+ keys.sort()
+ for k in keys:
+ doc.append('<b>%-20s :</b> %s\n' % (k, req.env[k]))
+ doc.append('\n</pre><P><HR>\n')
+ doc.append('</BODY></HTML>\n')
+
+
+ doc = string.join(doc, '')
+ req.out.write('Content-length: %s\r\n'
+ 'Content-type: text/html\r\n'
+ 'Cache-Control: no-cache\r\n'
+ '\r\n'
+ % len(doc))
+ req.out.write(doc)
+
+ req.Finish()
+ except:
+ import traceback
+ f = open('traceback', 'w')
+ traceback.print_exc( file = f )
+# f.write('%s' % doc)
+
+if __name__ == '__main__':
+ #import pdb
+ #pdb.run('_test()')
+ _test()
diff --git a/pypers/europython05/Quixote-2.0/server/cgi_server.py b/pypers/europython05/Quixote-2.0/server/cgi_server.py
new file mode 100755
index 0000000..c4e8ea2
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/cgi_server.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/cgi_server.py $
+$Id: cgi_server.py 25476 2004-10-27 21:54:59Z nascheme $
+"""
+
+import sys
+import os
+from quixote.http_request import HTTPRequest
+
+def run(create_publisher):
+ if sys.platform == "win32":
+ # on Windows, stdin and stdout are in text mode by default
+ import msvcrt
+ msvcrt.setmode(sys.__stdin__.fileno(), os.O_BINARY)
+ msvcrt.setmode(sys.__stdout__.fileno(), os.O_BINARY)
+ publisher = create_publisher()
+ request = HTTPRequest(sys.__stdin__, os.environ)
+ response = publisher.process_request(request)
+ try:
+ response.write(sys.__stdout__)
+ except IOError, err:
+ publisher.log("IOError while sending response ignored: %s" % err)
+
+
+if __name__ == '__main__':
+ from quixote.demo import create_publisher
+ run(create_publisher)
diff --git a/pypers/europython05/Quixote-2.0/server/fastcgi_server.py b/pypers/europython05/Quixote-2.0/server/fastcgi_server.py
new file mode 100755
index 0000000..4ba7530
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/fastcgi_server.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/fastcgi_server.py $
+$Id: fastcgi_server.py 25476 2004-10-27 21:54:59Z nascheme $
+
+Server for Quixote applications that use FastCGI. It should work
+for CGI too but the cgi_server module is preferred as it is more
+portable.
+"""
+
+from quixote.server import _fcgi
+from quixote.http_request import HTTPRequest
+
+def run(create_publisher):
+ publisher = create_publisher()
+ while _fcgi.isFCGI():
+ f = _fcgi.FCGI()
+ request = HTTPRequest(f.inp, f.env)
+ response = publisher.process_request(request)
+ try:
+ response.write(f.out)
+ except IOError, err:
+ publisher.log("IOError while sending response ignored: %s" % err)
+ f.Finish()
+
+
+if __name__ == '__main__':
+ from quixote.demo import create_publisher
+ run(create_publisher)
diff --git a/pypers/europython05/Quixote-2.0/server/medusa_server.py b/pypers/europython05/Quixote-2.0/server/medusa_server.py
new file mode 100755
index 0000000..239d95e
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/medusa_server.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/medusa_server.py $
+$Id: medusa_server.py 25579 2004-11-11 20:56:32Z nascheme $
+
+An HTTP handler for Medusa that publishes a Quixote application.
+"""
+
+import asyncore, rfc822, socket, urllib
+from StringIO import StringIO
+from medusa import http_server, xmlrpc_handler
+import quixote
+from quixote.http_request import HTTPRequest
+
+
+class StreamProducer:
+ def __init__(self, chunks):
+ self.chunks = chunks # a generator
+
+ def more(self):
+ try:
+ return self.chunks.next()
+ except StopIteration:
+ return ''
+
+
+class QuixoteHandler:
+ def __init__(self, publisher, server):
+ self.publisher = publisher
+ self.server = server
+
+ def match(self, request):
+ # Always match, since this is the only handler there is.
+ return True
+
+ def handle_request(self, request):
+ msg = rfc822.Message(StringIO('\n'.join(request.header)))
+ length = int(msg.get('Content-Length', '0'))
+ if length:
+ request.collector = xmlrpc_handler.collector(self, request)
+ else:
+ self.continue_request('', request)
+
+ def continue_request(self, data, request):
+ msg = rfc822.Message(StringIO('\n'.join(request.header)))
+ remote_addr, remote_port = request.channel.addr
+ if '#' in request.uri:
+ # MSIE is buggy and sometimes includes fragments in URLs
+ [request.uri, fragment] = request.uri.split('#', 1)
+ if '?' in request.uri:
+ [path, query_string] = request.uri.split('?', 1)
+ else:
+ path = request.uri
+ query_string = ''
+
+ path = urllib.unquote(path)
+ server_port = str(self.server.port)
+ http_host = msg.get("Host")
+ if http_host:
+ if ":" in http_host:
+ server_name, server_port = http_host.split(":", 1)
+ else:
+ server_name = http_host
+ else:
+ server_name = (self.server.ip or
+ socket.gethostbyaddr(socket.gethostname())[0])
+
+ environ = {'REQUEST_METHOD': request.command,
+ 'ACCEPT_ENCODING': msg.get('Accept-encoding', ''),
+ 'CONTENT_TYPE': msg.get('Content-type', ''),
+ 'CONTENT_LENGTH': len(data),
+ "GATEWAY_INTERFACE": "CGI/1.1",
+ 'PATH_INFO': path,
+ 'QUERY_STRING': query_string,
+ 'REMOTE_ADDR': remote_addr,
+ 'REMOTE_PORT': str(remote_port),
+ 'REQUEST_URI': request.uri,
+ 'SCRIPT_NAME': '',
+ "SCRIPT_FILENAME": '',
+ 'SERVER_NAME': server_name,
+ 'SERVER_PORT': server_port,
+ 'SERVER_PROTOCOL': 'HTTP/1.1',
+ 'SERVER_SOFTWARE': 'Quixote/%s' % quixote.__version__,
+ }
+ for title, header in msg.items():
+ envname = 'HTTP_' + title.replace('-', '_').upper()
+ environ[envname] = header
+
+ stdin = StringIO(data)
+ qrequest = HTTPRequest(stdin, environ)
+ qresponse = self.publisher.process_request(qrequest)
+
+ # Copy headers from Quixote's HTTP response
+ for name, value in qresponse.generate_headers():
+ # XXX Medusa's HTTP request is buggy, and only allows unique
+ # headers.
+ request[name] = value
+
+ request.response(qresponse.status_code)
+ request.push(StreamProducer(qresponse.generate_body_chunks()))
+ request.done()
+
+
+def run(create_publisher, host='', port=80):
+ """Runs a Medusa HTTP server that publishes a Quixote
+ application.
+ """
+ server = http_server.http_server(host, port)
+ publisher = create_publisher()
+ handler = QuixoteHandler(publisher, server)
+ server.install_handler(handler)
+ asyncore.loop()
+
+
+if __name__ == '__main__':
+ from quixote.server.util import main
+ main(run)
diff --git a/pypers/europython05/Quixote-2.0/server/mod_python_handler.py b/pypers/europython05/Quixote-2.0/server/mod_python_handler.py
new file mode 100755
index 0000000..17f32ec
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/mod_python_handler.py
@@ -0,0 +1,106 @@
+"""
+$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/mod_python_handler.py $
+$Id: mod_python_handler.py 25893 2005-01-19 14:26:25Z dbinger $
+
+This needs testing.
+
+mod_python configuration
+------------------------
+
+mod_python is an Apache module for embedding a Python interpreter into
+the Apache server. To use mod_python as the interface layer between
+Apache and Quixote, add something like this to your httpd.conf::
+
+ LoadModule python_module /usr/lib/apache/1.3/mod_python.so
+ <LocationMatch "^/qdemo(/|$)">
+ SetHandler python-program
+ PythonHandler quixote.server.mod_python_handler
+ PythonOption quixote-publisher-factory quixote.demo.create_publisher
+ PythonInterpreter quixote.demo
+ PythonDebug On
+ </LocationMatch>
+
+This will attach URLs starting with ``/qdemo`` to the Quixote demo.
+When you use mod_python, there's no need for rewrite rules (because of
+the pattern in the ``LocationMatch`` directive), and no need for a
+driver script.
+
+mod_python support was contributed to Quixote (1) by Erno Kuusela
+<erno@iki.fi> and the Quixote 2 port comes from Clint.
+"""
+
+import sys
+from mod_python import apache
+from quixote import enable_ptl
+from quixote.publish import Publisher
+from quixote.config import Config
+from quixote.util import import_object
+
+class ErrorLog:
+ def __init__(self, publisher):
+ self.publisher = publisher
+
+ def write(self, msg):
+ self.publisher.log(msg)
+
+ def close(self):
+ pass
+
+class ModPythonPublisher(Publisher):
+ def __init__(self, package, **kwargs):
+ Publisher.__init__(self, package, **kwargs)
+ # may be overwritten
+ self.logger.error_log = self.__error_log = ErrorLog(self)
+ self.__apache_request = None
+
+ def log(self, msg):
+ if self.logger.error_log is self.__error_log:
+ try:
+ self.__apache_request.log_error(msg)
+ except AttributeError:
+ apache.log_error(msg)
+ else:
+ Publisher.log(self, msg)
+
+ def publish_modpython(self, req):
+ """publish_modpython() -> None
+
+ Entry point from mod_python.
+ """
+ self.__apache_request = req
+ try:
+ self.publish(apache.CGIStdin(req),
+ apache.CGIStdout(req),
+ sys.stderr,
+ apache.build_cgi_env(req))
+
+ return apache.OK
+ finally:
+ self.__apache_request = None
+
+name2publisher = {}
+
+def run(publisher, req):
+ from quixote.http_request import HTTPRequest
+ request = HTTPRequest(apache.CGIStdin(req), apache.build_cgi_env(req))
+ response = publisher.process_request(request)
+ try:
+ response.write(apache.CGIStdout(req))
+ except IOError, err:
+ publisher.log("IOError while sending response ignored: %s" % err)
+ return apache.OK
+
+def handler(req):
+ opts = req.get_options()
+ try:
+ factory = opts['quixote-publisher-factory']
+ except KeyError:
+ apache.log_error('quixote-publisher-factory setting required')
+ return apache.HTTP_INTERNAL_SERVER_ERROR
+ pub = name2publisher.get(factory)
+ if pub is None:
+ factory_fcn = import_object(factory)
+ pub = factory_fcn()
+ name2publisher[factory] = pub
+ return run(pub, req)
+
diff --git a/pypers/europython05/Quixote-2.0/server/scgi_server.py b/pypers/europython05/Quixote-2.0/server/scgi_server.py
new file mode 100755
index 0000000..1f99ede
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/scgi_server.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/scgi_server.py $
+$Id: scgi_server.py 25579 2004-11-11 20:56:32Z nascheme $
+
+A SCGI server that uses Quixote to publish dynamic content.
+"""
+
+from scgi import scgi_server
+from quixote.http_request import HTTPRequest
+
+class QuixoteHandler(scgi_server.SCGIHandler):
+ def __init__(self, parent_fd, create_publisher, script_name=None):
+ scgi_server.SCGIHandler.__init__(self, parent_fd)
+ self.publisher = create_publisher()
+ self.script_name = script_name
+
+ def handle_connection(self, conn):
+ input = conn.makefile("r")
+ output = conn.makefile("w")
+ env = self.read_env(input)
+
+ if self.script_name is not None:
+ # mod_scgi doesn't know SCRIPT_NAME :-(
+ prefix = self.script_name
+ path = env['SCRIPT_NAME']
+ assert path[:len(prefix)] == prefix, (
+ "path %r doesn't start with script_name %r" % (path, prefix))
+ env['SCRIPT_NAME'] = prefix
+ env['PATH_INFO'] = path[len(prefix):] + env.get('PATH_INFO', '')
+
+ request = HTTPRequest(input, env)
+ response = self.publisher.process_request(request)
+ try:
+ response.write(output)
+ input.close()
+ output.close()
+ conn.close()
+ except IOError, err:
+ self.publisher.log("IOError while sending response "
+ "ignored: %s" % err)
+
+
+def run(create_publisher, host='', port=3000, script_name=None, max_children=5):
+ def create_handler(parent_fd):
+ return QuixoteHandler(parent_fd, create_publisher, script_name)
+ s = scgi_server.SCGIServer(create_handler, host=host, port=port,
+ max_children=max_children)
+ s.serve()
+
+
+def main():
+ from optparse import OptionParser
+ from quixote.util import import_object
+ parser = OptionParser()
+ parser.set_description(run.__doc__)
+ default_host = 'localhost'
+ parser.add_option(
+ '--host', dest="host", default=default_host, type="string",
+ help="Host interface to listen on. (default=%s)" % default_host)
+ default_port = 3000
+ parser.add_option(
+ '--port', dest="port", default=default_port, type="int",
+ help="Port to listen on. (default=%s)" % default_port)
+ default_maxchild = 5
+ parser.add_option(
+ '--max-children', dest="maxchild", default=default_maxchild,
+ type="string",
+ help="Maximum number of children to spawn. (default=%s)" %
+ default_maxchild)
+ parser.add_option(
+ '--script-name', dest="script_name", default=None, type="string",
+ help="Value of SCRIPT_NAME (only needed if using mod_scgi)")
+ default_factory = 'quixote.demo.create_publisher'
+ parser.add_option(
+ '--factory', dest="factory",
+ default=default_factory,
+ help="Path to factory function to create the site Publisher. "
+ "(default=%s)" % default_factory)
+ (options, args) = parser.parse_args()
+ run(import_object(options.factory), host=options.host, port=options.port,
+ script_name=options.script_name, max_children=options.maxchild)
+
+if __name__ == '__main__':
+ main()
diff --git a/pypers/europython05/Quixote-2.0/server/simple_server.py b/pypers/europython05/Quixote-2.0/server/simple_server.py
new file mode 100755
index 0000000..08d5149
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/simple_server.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/simple_server.py $
+$Id: simple_server.py 26472 2005-04-05 12:40:24Z dbinger $
+
+A simple, single threaded, synchronous HTTP server.
+"""
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+import urllib
+import quixote
+from quixote import get_publisher
+from quixote.http_request import HTTPRequest
+from quixote.util import import_object
+
+class HTTPRequestHandler(BaseHTTPRequestHandler):
+
+ required_cgi_environment = {}
+
+ def get_cgi_env(self, method):
+ env = dict(
+ SERVER_SOFTWARE="Quixote/%s" % quixote.__version__,
+ SERVER_NAME=self.server.server_name,
+ GATEWAY_INTERFACE='CGI/1.1',
+ SERVER_PROTOCOL=self.protocol_version,
+ SERVER_PORT=str(self.server.server_port),
+ REQUEST_METHOD=method,
+ REMOTE_ADDR=self.client_address[0],
+ SCRIPT_NAME='')
+ if '?' in self.path:
+ env['PATH_INFO'], env['QUERY_STRING'] = self.path.split('?')
+ else:
+ env['PATH_INFO'] = self.path
+ env['PATH_INFO'] = urllib.unquote(env['PATH_INFO'])
+ if self.headers.typeheader is None:
+ env['CONTENT_TYPE'] = self.headers.type
+ else:
+ env['CONTENT_TYPE'] = self.headers.typeheader
+ env['CONTENT_LENGTH'] = self.headers.getheader('content-length') or "0"
+ for name, value in self.headers.items():
+ header_name = 'HTTP_' + name.upper().replace('-', '_')
+ env[header_name] = value
+ accept = []
+ for line in self.headers.getallmatchingheaders('accept'):
+ if line[:1] in "\t\n\r ":
+ accept.append(line.strip())
+ else:
+ accept = accept + line[7:].split(',')
+ env['HTTP_ACCEPT'] = ','.join(accept)
+ co = filter(None, self.headers.getheaders('cookie'))
+ if co:
+ env['HTTP_COOKIE'] = ', '.join(co)
+ env.update(self.required_cgi_environment)
+ return env
+
+ def process(self, env):
+ request = HTTPRequest(self.rfile, env)
+ response = get_publisher().process_request(request)
+ try:
+ self.send_response(response.get_status_code(),
+ response.get_reason_phrase())
+ response.write(self.wfile, include_status=False)
+ except IOError, err:
+ print "IOError while sending response ignored: %s" % err
+
+ def do_POST(self):
+ return self.process(self.get_cgi_env('POST'))
+
+ def do_GET(self):
+ return self.process(self.get_cgi_env('GET'))
+
+
+def run(create_publisher, host='', port=80, https=False):
+ """Runs a simple, single threaded, synchronous HTTP server that
+ publishes a Quixote application.
+ """
+ if https:
+ HTTPRequestHandler.required_cgi_environment['HTTPS'] = 'on'
+ httpd = HTTPServer((host, port), HTTPRequestHandler)
+ publisher = create_publisher()
+ httpd.serve_forever()
+
+
+if __name__ == '__main__':
+ from quixote.server.util import get_server_parser
+ parser = get_server_parser(run.__doc__)
+ parser.add_option(
+ '--https', dest="https", default=False, action="store_true",
+ help=("Force the scheme for all requests to be https. "
+ "Not that this is for running the simple server "
+ "through a proxy or tunnel that provides real SSL "
+ "support. The simple server itself does not. "))
+ (options, args) = parser.parse_args()
+ run(import_object(options.factory), host=options.host, port=options.port,
+ https=options.https)
diff --git a/pypers/europython05/Quixote-2.0/server/twisted_server.py b/pypers/europython05/Quixote-2.0/server/twisted_server.py
new file mode 100755
index 0000000..911ec50
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/twisted_server.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/twisted_server.py $
+$Id: twisted_server.py 25711 2004-12-07 22:52:41Z nascheme $
+
+An HTTP server for Twisted that publishes a Quixote application.
+"""
+
+import urllib
+from twisted.protocols import http
+from twisted.web import server
+from twisted.python import threadable
+from twisted.internet import reactor
+from quixote.http_request import HTTPRequest
+
+
+class QuixoteFactory(http.HTTPFactory):
+ def __init__(self, publisher):
+ self.publisher = publisher
+ http.HTTPFactory.__init__(self, None)
+
+ def buildProtocol(self, addr):
+ protocol = http.HTTPFactory.buildProtocol(self, addr)
+ protocol.requestFactory = QuixoteRequest
+ return protocol
+
+
+class QuixoteRequest(server.Request):
+ def process(self):
+ environ = self.create_environment()
+ # this seek is important, it doesn't work without it (it doesn't
+ # matter for GETs, but POSTs will not work properly without it.)
+ self.content.seek(0, 0)
+ qxrequest = HTTPRequest(self.content, environ)
+ qxresponse = self.channel.factory.publisher.process_request(qxrequest)
+ self.setResponseCode(qxresponse.status_code)
+ for name, value in qxresponse.generate_headers():
+ if name != 'Set-Cookie':
+ self.setHeader(name, value)
+ # Cookies get special treatment since it seems Twisted cannot handle
+ # multiple Set-Cookie headers.
+ for name, attrs in qxresponse.cookies.items():
+ attrs = attrs.copy()
+ value = attrs.pop('value')
+ self.addCookie(name, value, **attrs)
+ QuixoteProducer(qxresponse, self)
+
+ def create_environment(self):
+ """
+ Borrowed heavily from twisted.web.twcgi
+ """
+ # Twisted doesn't decode the path for us, so let's do it here.
+ if '%' in self.path:
+ self.path = urllib.unquote(self.path)
+
+ serverName = self.getRequestHostname().split(':')[0]
+ env = {"SERVER_SOFTWARE": server.version,
+ "SERVER_NAME": serverName,
+ "GATEWAY_INTERFACE": "CGI/1.1",
+ "SERVER_PROTOCOL": self.clientproto,
+ "SERVER_PORT": str(self.getHost()[2]),
+ "REQUEST_METHOD": self.method,
+ "SCRIPT_NAME": '',
+ "SCRIPT_FILENAME": '',
+ "REQUEST_URI": self.uri,
+ "HTTPS": (self.isSecure() and 'on') or 'off',
+ 'SERVER_PROTOCOL': 'HTTP/1.1',
+ }
+
+ for env_var, header in [('ACCEPT_ENCODING', 'Accept-encoding'),
+ ('CONTENT_TYPE', 'Content-type'),
+ ('HTTP_COOKIE', 'Cookie'),
+ ('HTTP_REFERER', 'Referer'),
+ ('HTTP_USER_AGENT', 'User-agent')]:
+ value = self.getHeader(header)
+ if value is not None:
+ env[env_var] = value
+
+ client = self.getClient()
+ if client is not None:
+ env['REMOTE_HOST'] = client
+ ip = self.getClientIP()
+ if ip is not None:
+ env['REMOTE_ADDR'] = ip
+ _, _, remote_port = self.transport.getPeer()
+ env['REMOTE_PORT'] = remote_port
+ env["PATH_INFO"] = self.path
+
+ qindex = self.uri.find('?')
+ if qindex != -1:
+ env['QUERY_STRING'] = self.uri[qindex+1:]
+ else:
+ env['QUERY_STRING'] = ''
+
+ # Propogate HTTP headers
+ for title, header in self.getAllHeaders().items():
+ envname = title.replace('-', '_').upper()
+ if title not in ('content-type', 'content-length'):
+ envname = "HTTP_" + envname
+ env[envname] = header
+
+ return env
+
+
+class QuixoteProducer:
+ """
+ Produce the Quixote response for twisted.
+ """
+ def __init__(self, qxresponse, request):
+ self.request = request
+ self.size = qxresponse.get_content_length()
+ self.stream = qxresponse.generate_body_chunks()
+ request.registerProducer(self, 0)
+
+ def resumeProducing(self):
+ if self.request:
+ try:
+ chunk = self.stream.next()
+ except StopIteration:
+ self.request.unregisterProducer()
+ self.request.finish()
+ self.request = None
+ else:
+ self.request.write(chunk)
+
+ def pauseProducing(self):
+ pass
+
+ def stopProducing(self):
+ self.request = None
+
+ synchronized = ['resumeProducing', 'stopProducing']
+
+threadable.synchronize(QuixoteProducer)
+
+
+def run(create_publisher, host='', port=80):
+ """Runs a Twisted HTTP server server that publishes a Quixote
+ application."""
+ publisher = create_publisher()
+ factory = QuixoteFactory(publisher)
+ reactor.listenTCP(port, factory, interface=host)
+ reactor.run()
+
+
+if __name__ == '__main__':
+ from quixote.server.util import main
+ main(run)
diff --git a/pypers/europython05/Quixote-2.0/server/util.py b/pypers/europython05/Quixote-2.0/server/util.py
new file mode 100755
index 0000000..69ed675
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/server/util.py
@@ -0,0 +1,32 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/util.py $
+$Id: util.py 26427 2005-03-30 18:03:32Z dbinger $
+
+Miscellaneous utility functions shared by servers.
+"""
+
+from optparse import OptionParser
+from quixote.util import import_object
+
+def get_server_parser(doc):
+ parser = OptionParser()
+ parser.set_description(doc)
+ default_host = 'localhost'
+ parser.add_option(
+ '--host', dest="host", default=default_host, type="string",
+ help="Host interface to listen on. (default=%s)" % default_host)
+ default_port = 8080
+ parser.add_option(
+ '--port', dest="port", default=default_port, type="int",
+ help="Port to listen on. (default=%s)" % default_port)
+ default_factory = 'quixote.demo.create_publisher'
+ parser.add_option(
+ '--factory', dest="factory",
+ default=default_factory,
+ help="Path to factory function to create the site Publisher. "
+ "(default=%s)" % default_factory)
+ return parser
+
+def main(run):
+ parser = get_server_parser(run.__doc__)
+ (options, args) = parser.parse_args()
+ run(import_object(options.factory), host=options.host, port=options.port)
diff --git a/pypers/europython05/Quixote-2.0/session.py b/pypers/europython05/Quixote-2.0/session.py
new file mode 100755
index 0000000..0241b7f
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/session.py
@@ -0,0 +1,567 @@
+"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/session.py $
+$Id: session.py 26524 2005-04-08 10:22:34Z dbinger $
+
+Quixote session management. There are two levels to Quixote's
+session management system:
+ - SessionManager
+ - Session
+
+A SessionManager is responsible for creating sessions, setting and reading
+session cookies, maintaining the collection of all sessions, and so forth.
+There is one SessionManager instance per Quixote process.
+
+A Session is the umbrella object for a single session (notionally, a (user,
+host, browser_process) triple). Simple applications can probably get away
+with putting all session data into a Session object (or, better, into an
+application-specific subclass of Session).
+
+The default implementation provided here is not persistent: when the
+Quixote process shuts down, all session data is lost. See
+doc/session-mgmt.txt for information on session persistence.
+"""
+
+from time import time, localtime, strftime
+
+from quixote import get_publisher, get_cookie, get_response, get_request, \
+ get_session
+from quixote.util import randbytes
+
+class NullSessionManager:
+ """A session manager that does nothing. It is the default session manager.
+ """
+
+ def start_request(self):
+ """
+ Called near the beginning of each request: after the HTTPRequest
+ object has been built, but before we traverse the URL or call the
+ callable object found by URL traversal.
+ """
+
+ def finish_successful_request(self):
+ """Called near the end of each successful request. Not called if
+ there were any errors processing the request.
+ """
+
+ def finish_failed_request(self):
+ """Called near the end of a failed request (i.e. a exception that was
+ not a PublisherError was raised.
+ """
+
+
+class SessionManager:
+ """
+ SessionManager acts as a dictionary of all sessions, mapping session
+ ID strings to individual session objects. Session objects are
+ instances of Session (or a custom subclass for your application).
+ SessionManager is also responsible for creating and destroying
+ sessions, for generating and interpreting session cookies, and for
+ session persistence (if any -- this implementation is not
+ persistent).
+
+ Most applications can just use this class directly; sessions will
+ be kept in memory-based dictionaries, and will be lost when the
+ Quixote process dies. Alternatively an application can subclass
+ SessionManager to implement specific behaviour, such as persistence.
+
+ Instance attributes:
+ session_class : class
+ the class that is instantiated to create new session objects
+ (in new_session())
+ sessions : mapping { session_id:string : Session }
+ the collection of sessions managed by this SessionManager
+ """
+
+ ACCESS_TIME_RESOLUTION = 1 # in seconds
+
+
+ def __init__(self, session_class=None, session_mapping=None):
+ """(session_class : class = Session, session_mapping : mapping = None)
+
+ Create a new session manager. There should be one session
+ manager per publisher, ie. one per process
+
+ session_class is used by the new_session() method -- it returns
+ an instance of session_class.
+ """
+ self.sessions = {}
+ if session_class is None:
+ self.session_class = Session
+ else:
+ self.session_class = session_class
+ if session_mapping is None:
+ self.sessions = {}
+ else:
+ self.sessions = session_mapping
+
+ def __repr__(self):
+ return "<%s at %x>" % (self.__class__.__name__, id(self))
+
+
+ # -- Mapping interface ---------------------------------------------
+ # (subclasses shouldn't need to override any of this, unless
+ # your application passes in a session_mapping object that
+ # doesn't provide all of the mapping methods needed here)
+
+ def keys(self):
+ """() -> [string]
+
+ Return the list of session IDs of sessions in this session manager.
+ """
+ return self.sessions.keys()
+
+ def sorted_keys(self):
+ """() -> [string]
+
+ Return the same list as keys(), but sorted.
+ """
+ keys = self.keys()
+ keys.sort()
+ return keys
+
+ def values(self):
+ """() -> [Session]
+
+ Return the list of sessions in this session manager.
+ """
+ return self.sessions.values()
+
+ def items(self):
+ """() -> [(string, Session)]
+
+ Return the list of (session_id, session) pairs in this session
+ manager.
+ """
+ return self.sessions.items()
+
+ def get(self, session_id, default=None):
+ """(session_id : string, default : any = None) -> Session
+
+ Return the session object identified by 'session_id', or None if
+ no such session.
+ """
+ return self.sessions.get(session_id, default)
+
+ def __getitem__(self, session_id):
+ """(session_id : string) -> Session
+
+ Return the session object identified by 'session_id'. Raise KeyError
+ if no such session.
+ """
+ return self.sessions[session_id]
+
+ def has_key(self, session_id):
+ """(session_id : string) -> boolean
+
+ Return true if a session identified by 'session_id' exists in
+ the session manager.
+ """
+ return self.sessions.has_key(session_id)
+
+ # has_session() is a synonym for has_key() -- if you override
+ # has_key(), be sure to repeat this alias!
+ has_session = has_key
+
+ def __setitem__(self, session_id, session):
+ """(session_id : string, session : Session)
+
+ Store 'session' in the session manager under 'session_id'.
+ """
+ if not isinstance(session, self.session_class):
+ raise TypeError("session not an instance of %r: %r"
+ % (self.session_class, session))
+ assert session.id is not None, "session ID not set"
+ assert session_id == session.id, "session ID mismatch"
+ self.sessions[session_id] = session
+
+ def __delitem__(self, session_id):
+ """(session_id : string) -> Session
+
+ Remove the session object identified by 'session_id' from the session
+ manager. Raise KeyError if no such session.
+ """
+ del self.sessions[session_id]
+
+ # -- Transactional interface ---------------------------------------
+ # Useful for applications that provide a transaction-oriented
+ # persistence mechanism. You'll still need to provide a mapping
+ # object that works with your persistence mechanism; these two
+ # methods let you hook into your transaction machinery after a
+ # request is finished processing.
+
+ def abort_changes(self, session):
+ """(session : Session)
+
+ Placeholder for subclasses that implement transactional
+ persistence: forget about saving changes to the current
+ session. Called by the publisher when a request fails,
+ ie. when it catches an exception other than PublishError.
+ """
+ pass
+
+ def commit_changes(self, session):
+ """(session : Session)
+
+ Placeholder for subclasses that implement transactional
+ persistence: commit changes to the current session. Called by
+ the publisher when a request completes successfully, or is
+ interrupted by a PublishError exception.
+ """
+ pass
+
+
+ # -- Session management --------------------------------------------
+ # these build on the storage mechanism implemented by the
+ # above mapping methods, and are concerned with all the high-
+ # level details of managing web sessions
+
+ def new_session(self, id):
+ """(id : string) -> Session
+
+ Return a new session object, ie. an instance of the session_class
+ class passed to the constructor (defaults to Session).
+ """
+ return self.session_class(id)
+
+ def _get_session_id(self, config):
+ """() -> string
+
+ Find the ID of the current session by looking for the session
+ cookie in the request. Return None if no such cookie or the
+ cookie has been expired, otherwise return the cookie's value.
+ """
+ id = get_cookie(config.session_cookie_name)
+ if id == "" or id == "*del*":
+ return None
+ else:
+ return id
+
+ def _make_session_id(self):
+ # Generate a session ID, which is just the value of the session
+ # cookie we are about to drop on the user. (It's also the key
+ # used with the session manager mapping interface.)
+ id = None
+ while id is None or self.has_session(id):
+ id = randbytes(8) # 64-bit random number
+ return id
+
+ def _create_session(self):
+ # Create a new session object, with no ID for now - one will
+ # be assigned later if we save the session.
+ return self.new_session(None)
+
+ def get_session(self):
+ """() -> Session
+
+ Fetch or create a session object for the current session, and
+ return it. If a session cookie is found in the HTTP request
+ object, use it to look up and return an existing session object.
+ If no session cookie is found, create a new session.
+
+ Note that this method does *not* cause the new session to be
+ stored in the session manager, nor does it drop a session cookie
+ on the user. Those are both the responsibility of
+ maintain_session(), called at the end of a request.
+ """
+ config = get_publisher().config
+ id = self._get_session_id(config)
+ session = self.get(id) or self._create_session()
+ session._set_access_time(self.ACCESS_TIME_RESOLUTION)
+ return session
+
+ def maintain_session(self, session):
+ """(session : Session)
+
+ Maintain session information. This method is called after servicing
+ an HTTP request, just before the response is returned. If a session
+ contains information it is saved and a cookie dropped on the client.
+ If not, the session is discarded and the client will be instructed
+ to delete the session cookie (if any).
+ """
+ if not session.has_info():
+ # Session has no useful info -- forget it. If it previously
+ # had useful information and no longer does, we have to
+ # explicitly forget it.
+ if session.id and self.has_session(session.id):
+ del self[session.id]
+ self.revoke_session_cookie()
+ return
+
+ if session.id is None:
+ # This is the first time this session has had useful
+ # info -- store it and set the session cookie.
+ session.id = self._make_session_id()
+ self[session.id] = session
+ self.set_session_cookie(session.id)
+
+ elif session.is_dirty():
+ # We have already stored this session, but it's dirty
+ # and needs to be stored again. This will never happen
+ # with the default Session class, but it's there for
+ # applications using a persistence mechanism that requires
+ # repeatedly storing the same object in the same mapping.
+ self[session.id] = session
+
+ def _set_cookie(self, value, **attrs):
+ config = get_publisher().config
+ name = config.session_cookie_name
+ if config.session_cookie_path:
+ path = config.session_cookie_path
+ else:
+ path = get_request().get_environ('SCRIPT_NAME')
+ if not path.endswith("/"):
+ path += "/"
+ domain = config.session_cookie_domain
+ get_response().set_cookie(name, value, domain=domain,
+ path=path, **attrs)
+ return name
+
+ def set_session_cookie(self, session_id):
+ """(session_id : string)
+
+ Ensure that a session cookie with value 'session_id' will be
+ returned to the client via the response object.
+ """
+ self._set_cookie(session_id)
+
+ def revoke_session_cookie(self):
+ """
+ Remove the session cookie from the remote user's session by
+ resetting the value and maximum age in the response object. Also
+ remove the cookie from the request so that further processing of
+ this request does not see the cookie's revoked value.
+ """
+ cookie_name = self._set_cookie("", max_age=0)
+ if get_cookie(cookie_name) is not None:
+ del get_request().cookies[cookie_name]
+
+ def expire_session(self):
+ """
+ Expire the current session, ie. revoke the session cookie from
+ the client and remove the session object from the session
+ manager and from the current request.
+ """
+ self.revoke_session_cookie()
+ request = get_request()
+ try:
+ del self[request.session.id]
+ except KeyError:
+ # This can happen if the current session hasn't been saved
+ # yet, eg. if someone tries to leave a session with no
+ # interesting data. That's not a big deal, so ignore it.
+ pass
+ request.session = None
+
+ def has_session_cookie(self, must_exist=False):
+ """(must_exist : boolean = false) -> bool
+
+ Return true if the request already has a cookie identifying a
+ session object. If 'must_exist' is true, the cookie must
+ correspond to a currently existing session; otherwise (the
+ default), we just check for the existence of the session cookie
+ and don't inspect its content at all.
+ """
+ config = get_publisher().config
+ id = get_cookie(config.session_cookie_name)
+ if id is None:
+ return False
+ if must_exist:
+ return self.has_session(id)
+ else:
+ return True
+
+ # -- Hooks into the Quixote main loop ------------------------------
+
+ def start_request(self):
+ """
+ Called near the beginning of each request: after the HTTPRequest
+ object has been built, but before we traverse the URL or call the
+ callable object found by URL traversal.
+ """
+ session = self.get_session()
+ get_request().session = session
+ session.start_request()
+
+ def finish_successful_request(self):
+ """Called near the end of each successful request. Not called if
+ there were any errors processing the request.
+ """
+ session = get_session()
+ if session is not None:
+ self.maintain_session(session)
+ self.commit_changes(session)
+
+ def finish_failed_request(self):
+ """Called near the end of a failed request (i.e. a exception that was
+ not a PublisherError was raised.
+ """
+ self.abort_changes(get_session())
+
+
+class Session:
+ """
+ Holds information about the current session. The only information
+ that is likely to be useful to applications is the 'user' attribute,
+ which applications can use as they please.
+
+ Instance attributes:
+ id : string
+ the session ID (generated by SessionManager and used as the
+ value of the session cookie)
+ user : any
+ an object to identify the human being on the other end of the
+ line. It's up to you whether to store just a string in 'user',
+ or some more complex data structure or object.
+ _remote_address : string
+ IP address of user owning this session (only set when the
+ session is created)
+ _creation_time : float
+ _access_time : float
+ two ways of keeping track of the "age" of the session.
+ Note that '__access_time' is maintained by the SessionManager that
+ owns this session, using _set_access_time().
+ _form_tokens : [string]
+ outstanding form tokens. This is used as a queue that can grow
+ up to MAX_FORM_TOKENS. Tokens are removed when forms are submitted.
+
+ Feel free to access 'id' and 'user' directly, but do not modify
+ 'id'. The preferred way to set 'user' is with the set_user() method
+ (which you might want to override for type-checking).
+ """
+
+ MAX_FORM_TOKENS = 16 # maximum number of outstanding form tokens
+
+ def __init__(self, id):
+ self.id = id
+ self.user = None
+ self._remote_address = get_request().get_environ("REMOTE_ADDR")
+ self._creation_time = self._access_time = time()
+ self._form_tokens = [] # queue
+
+ def __repr__(self):
+ return "<%s at %x: %s>" % (self.__class__.__name__, id(self), self.id)
+
+ def __str__(self):
+ if self.user:
+ return "session %s (user %s)" % (self.id, self.user)
+ else:
+ return "session %s (no user)" % self.id
+
+ def has_info(self):
+ """() -> boolean
+
+ Return true if this session contains any information that must
+ be saved.
+ """
+ return self.user or self._form_tokens
+
+ def is_dirty(self):
+ """() -> boolean
+
+ Return true if this session has changed since it was last saved
+ such that it needs to be saved again.
+
+ Default implementation always returns false since the default
+ storage mechanism is an in-memory dictionary, and you don't have
+ to put the same object into the same slot of a dictionary twice.
+ If sessions are stored to, eg., files in a directory or slots in
+ a hash file, is_dirty() should probably be an alias or wrapper
+ for has_info(). See doc/session-mgmt.txt.
+ """
+ return False
+
+ def dump(self, file=None, header=True, deep=True):
+ time_fmt = "%Y-%m-%d %H:%M:%S"
+ ctime = strftime(time_fmt, localtime(self._creation_time))
+ atime = strftime(time_fmt, localtime(self._access_time))
+
+ if header:
+ file.write('session %s:' % self.id)
+ file.write(' user %s' % self.user)
+ file.write(' _remote_address: %s' % self._remote_address)
+ file.write(' created %s, last accessed %s' % (ctime, atime))
+ file.write(' _form_tokens: %s\n' % self._form_tokens)
+
+ def start_request(self):
+ """
+ Called near the beginning of each request: after the HTTPRequest
+ object has been built, but before we traverse the URL or call the
+ callable object found by URL traversal.
+ """
+ if self.user is not None:
+ get_request().environ['REMOTE_USER'] = str(self.user)
+
+ # -- Simple accessors and modifiers --------------------------------
+
+ def set_user(self, user):
+ self.user = user
+
+ def get_user(self):
+ return self.user
+
+ def get_remote_address(self):
+ """Return the IP address (dotted-quad string) that made the
+ initial request in this session.
+ """
+ return self._remote_address
+
+ def get_creation_time(self):
+ """Return the time that this session was created (seconds
+ since epoch).
+ """
+ return self._creation_time
+
+ def get_access_time(self):
+ """Return the time that this session was last accessed (seconds
+ since epoch).
+ """
+ return self._access_time
+
+ def get_creation_age(self, _now=None):
+ """Return the number of seconds since session was created."""
+ # _now arg is not strictly necessary, but there for consistency
+ # with get_access_age()
+ return (_now or time()) - self._creation_time
+
+ def get_access_age(self, _now=None):
+ """Return the number of seconds since session was last accessed."""
+ # _now arg is for SessionManager's use
+ return (_now or time()) - self._access_time
+
+
+ # -- Methods for SessionManager only -------------------------------
+
+ def _set_access_time(self, resolution):
+ now = time()
+ if now - self._access_time > resolution:
+ self._access_time = now
+
+
+ # -- Form token methods --------------------------------------------
+
+ def create_form_token(self):
+ """() -> string
+
+ Create a new form token and add it to a queue of outstanding form
+ tokens for this session. A maximum of MAX_FORM_TOKENS are saved.
+ The new token is returned.
+ """
+ token = randbytes(8)
+ self._form_tokens.append(token)
+ extra = len(self._form_tokens) - self.MAX_FORM_TOKENS
+ if extra > 0:
+ del self._form_tokens[:extra]
+ return token
+
+ def has_form_token(self, token):
+ """(token : string) -> boolean
+
+ Return true if 'token' is in the queue of outstanding tokens.
+ """
+ return token in self._form_tokens
+
+ def remove_form_token(self, token):
+ """(token : string)
+
+ Remove 'token' from the queue of outstanding tokens.
+ """
+ self._form_tokens.remove(token)
diff --git a/pypers/europython05/Quixote-2.0/setup.py b/pypers/europython05/Quixote-2.0/setup.py
new file mode 100755
index 0000000..7498982
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/setup.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/setup.py $
+#$Id: setup.py 26539 2005-04-11 15:47:33Z dbinger $
+
+# Setup script for Quixote
+
+__revision__ = "$Id: setup.py 26539 2005-04-11 15:47:33Z dbinger $"
+
+import sys, os
+from distutils import core
+from distutils.extension import Extension
+from ptl.qx_distutils import qx_build_py
+
+# a fast htmltext type
+htmltext = Extension(name="quixote.html._c_htmltext",
+ sources=["html/_c_htmltext.c"])
+
+# faster import hook for PTL modules
+cimport = Extension(name="quixote.ptl.cimport",
+ sources=["ptl/cimport.c"])
+
+kw = {'name': "Quixote",
+ 'version': "2.0",
+ 'description': "A highly Pythonic Web application framework",
+ 'author': "MEMS Exchange",
+ 'author_email': "quixote@mems-exchange.org",
+ 'url': "http://www.mems-exchange.org/software/quixote/",
+ 'license': "CNRI Open Source License (see LICENSE.txt)",
+
+ 'package_dir': {'quixote':os.curdir},
+ 'packages': ['quixote', 'quixote.demo', 'quixote.form',
+ 'quixote.html', 'quixote.ptl',
+ 'quixote.server'],
+
+ 'ext_modules': [],
+
+ 'cmdclass': {'build_py': qx_build_py},
+ }
+
+
+build_extensions = sys.platform != 'win32'
+
+if build_extensions:
+ # The _c_htmltext module requires Python 2.2 features.
+ if sys.hexversion >= 0x20200a1:
+ kw['ext_modules'].append(htmltext)
+ kw['ext_modules'].append(cimport)
+
+# If we're running Python 2.3, add extra information
+if hasattr(core, 'setup_keywords'):
+ if 'classifiers' in core.setup_keywords:
+ kw['classifiers'] = ['Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'License :: OSI Approved :: Python License (CNRI Python License)',
+ 'Intended Audience :: Developers',
+ 'Operating System :: Unix',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+ ]
+ if 'download_url' in core.setup_keywords:
+ kw['download_url'] = ('http://www.mems-exchange.org/software/files'
+ '/quixote/Quixote-%s.tar.gz' % kw['version'])
+
+core.setup(**kw)
diff --git a/pypers/europython05/Quixote-2.0/test/__init__.py b/pypers/europython05/Quixote-2.0/test/__init__.py
new file mode 100755
index 0000000..bcc196b
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/test/__init__.py
@@ -0,0 +1,2 @@
+
+# Empty file to make this directory a package
diff --git a/pypers/europython05/Quixote-2.0/test/ua_test.py b/pypers/europython05/Quixote-2.0/test/ua_test.py
new file mode 100755
index 0000000..d1de207
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/test/ua_test.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+# Test Quixote's ability to parse the "User-Agent" header, ie.
+# the 'guess_browser_version()' method of HTTPRequest.
+#
+# Reads User-Agent strings on stdin, and writes Quixote's interpretation
+# of each on stdout. This is *not* an automated test!
+
+import sys, os
+from copy import copy
+from quixote.http_request import HTTPRequest
+
+env = copy(os.environ)
+file = sys.stdin
+while 1:
+ line = file.readline()
+ if not line:
+ break
+ if line[-1] == "\n":
+ line = line[:-1]
+
+ env["HTTP_USER_AGENT"] = line
+ req = HTTPRequest(None, env)
+ (name, version) = req.guess_browser_version()
+ if name is None:
+ print "%s -> ???" % line
+ else:
+ print "%s -> (%s, %s)" % (line, name, version)
diff --git a/pypers/europython05/Quixote-2.0/test/utest_request.py b/pypers/europython05/Quixote-2.0/test/utest_request.py
new file mode 100755
index 0000000..ba3f053
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/test/utest_request.py
@@ -0,0 +1,43 @@
+from sancho.utest import UTest
+from quixote.http_request import parse_cookies
+
+
+class ParseCookiesTest (UTest):
+
+ def check_basic(self):
+ assert parse_cookies('a') == {'a': ''}
+ assert parse_cookies('a = ') == {'a': ''}
+ assert parse_cookies('a = ""') == {'a': ''}
+ assert parse_cookies(r'a = "\""') == {'a': '"'}
+ assert parse_cookies('a, b; c') == {'a': '', 'b': '', 'c': ''}
+ assert parse_cookies('a, b=1') == {'a': '', 'b': '1'}
+ assert parse_cookies('a = ";, \t";') == {'a': ';, \t'}
+
+ def check_rfc2109_example(self):
+ s = ('$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; '
+ 'Part_Number="Rocket_Launcher_0001"; $Path="/acme"')
+ result = {'Customer': 'WILE_E_COYOTE',
+ 'Part_Number': 'Rocket_Launcher_0001',
+ }
+ assert parse_cookies(s) == result
+
+ def check_other(self):
+ s = 'PREF=ID=0a06b1:TM=108:LM=1069:C2COFF=1:S=ETXrcU'
+ result = {'PREF': 'ID=0a06b1:TM=108:LM=1069:C2COFF=1:S=ETXrcU'}
+ assert parse_cookies(s) == result
+ s = 'pageColor=White; pageWidth=990; fontSize=12; fontFace=1; E=E'
+ assert parse_cookies(s) == {'pageColor': 'White',
+ 'pageWidth': '990',
+ 'fontSize': '12',
+ 'fontFace': '1',
+ 'E': 'E'}
+ s = 'userid="joe"; QX_session="58a3ced39dcd0d"'
+ assert parse_cookies(s) == {'userid': 'joe',
+ 'QX_session': '58a3ced39dcd0d'}
+
+ def check_invalid(self):
+ parse_cookies('a="123')
+ parse_cookies('a=123"')
+
+if __name__ == "__main__":
+ ParseCookiesTest()
diff --git a/pypers/europython05/Quixote-2.0/util.py b/pypers/europython05/Quixote-2.0/util.py
new file mode 100755
index 0000000..1835be3
--- /dev/null
+++ b/pypers/europython05/Quixote-2.0/util.py
@@ -0,0 +1,390 @@
+"""quixote.util
+$HeadURL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/util.py $
+$Id: util.py 26523 2005-04-08 10:20:19Z dbinger $
+
+Contains various useful functions and classes:
+
+ xmlrpc(request, func) : Processes the body of an XML-RPC request, and calls
+ 'func' with the method name and parameters.
+ StaticFile : Wraps a file from a filesystem as a
+ Quixote resource.
+ StaticDirectory : Wraps a directory containing static files as
+ a Quixote directory.
+
+StaticFile and StaticDirectory were contributed by Hamish Lawson.
+See doc/static-files.txt for examples of their use.
+"""
+
+import sys
+import os
+import time
+import binascii
+import mimetypes
+import urllib
+import xmlrpclib
+from rfc822 import formatdate
+import quixote
+from quixote import errors
+from quixote.directory import Directory
+from quixote.html import htmltext, TemplateIO
+from quixote.http_response import Stream
+
+if hasattr(os, 'urandom'):
+ # available in Python 2.4 and also works on win32
+ def randbytes(bytes):
+ """Return bits of random data as a hex string."""
+ return binascii.hexlify(os.urandom(bytes))
+
+elif os.path.exists('/dev/urandom'):
+ # /dev/urandom is just as good as /dev/random for cookies (assuming
+ # SHA-1 is secure) and it never blocks.
+ def randbytes(bytes):
+ """Return bits of random data as a hex string."""
+ return binascii.hexlify(open("/dev/urandom").read(bytes))
+
+else:
+ # this is much less secure than the above function
+ import sha
+ class _PRNG:
+ def __init__(self):
+ self.state = sha.new(str(time.time() + time.clock()))
+ self.count = 0
+
+ def _get_bytes(self):
+ self.state.update('%s %d' % (time.time() + time.clock(),
+ self.count))
+ self.count += 1
+ return self.state.hexdigest()
+
+ def randbytes(self, bytes):
+ """Return bits of random data as a hex string."""
+ s = ""
+ chars = 2*bytes
+ while len(s) < chars:
+ s += self._get_bytes()
+ return s[:chars]
+
+ randbytes = _PRNG().randbytes
+
+
+def import_object(name):
+ i = name.rfind('.')
+ if i != -1:
+ module_name = name[:i]
+ object_name = name[i+1:]
+ __import__(module_name)
+ return getattr(sys.modules[module_name], object_name)
+ else:
+ __import__(name)
+ return sys.modules[name]
+
+def xmlrpc(request, func):
+ """xmlrpc(request:Request, func:callable) : string
+
+ Processes the body of an XML-RPC request, and calls 'func' with
+ two arguments, a string containing the method name and a tuple of
+ parameters.
+ """
+
+ # Get contents of POST body
+ if request.get_method() != 'POST':
+ request.response.set_status(405, "Only the POST method is accepted")
+ return "XML-RPC handlers only accept the POST method."
+
+ length = int(request.environ['CONTENT_LENGTH'])
+ data = request.stdin.read(length)
+
+ # Parse arguments
+ params, method = xmlrpclib.loads(data)
+
+ try:
+ result = func(method, params)
+ except xmlrpclib.Fault, exc:
+ result = exc
+ except:
+ # report exception back to client
+ result = xmlrpclib.dumps(
+ xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
+ )
+ else:
+ result = (result,)
+ result = xmlrpclib.dumps(result, methodresponse=1)
+
+ request.response.set_content_type('text/xml')
+ return result
+
+
+class FileStream(Stream):
+
+ CHUNK_SIZE = 20000
+
+ def __init__(self, fp, size=None):
+ self.fp = fp
+ self.length = size
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ chunk = self.fp.read(self.CHUNK_SIZE)
+ if not chunk:
+ raise StopIteration
+ return chunk
+
+
+class StaticFile:
+
+ """
+ Wrapper for a static file on the filesystem.
+ """
+
+ def __init__(self, path, follow_symlinks=False,
+ mime_type=None, encoding=None, cache_time=None):
+ """StaticFile(path:string, follow_symlinks:bool)
+
+ Initialize instance with the absolute path to the file. If
+ 'follow_symlinks' is true, symbolic links will be followed.
+ 'mime_type' specifies the MIME type, and 'encoding' the
+ encoding; if omitted, the MIME type will be guessed,
+ defaulting to text/plain.
+
+ Optional cache_time parameter indicates the number of
+ seconds a response is considered to be valid, and will
+ be used to set the Expires header in the response when
+ quixote gets to that part. If the value is None then
+ the Expires header will not be set.
+ """
+
+ # Check that the supplied path is absolute and (if a symbolic link) may
+ # be followed
+ self.path = path
+ if not os.path.isabs(path):
+ raise ValueError, "Path %r is not absolute" % path
+ # Decide the Content-Type of the file
+ guess_mime, guess_enc = mimetypes.guess_type(os.path.basename(path),
+ strict=False)
+ self.mime_type = mime_type or guess_mime or 'text/plain'
+ self.encoding = encoding or guess_enc or None
+ self.cache_time = cache_time
+ self.follow_symlinks = follow_symlinks
+
+ def __call__(self):
+ if not self.follow_symlinks and os.path.islink(self.path):
+ raise errors.TraversalError(private_msg="Path %r is a symlink"
+ % self.path)
+ request = quixote.get_request()
+ response = quixote.get_response()
+
+ if self.cache_time is None:
+ response.set_expires(None) # don't set the Expires header
+ else:
+ # explicitly allow client to cache page by setting the Expires
+ # header, this is even more efficient than the using
+ # Last-Modified/If-Modified-Since since the browser does not need
+ # to contact the server
+ response.set_expires(seconds=self.cache_time)
+
+ stat = os.stat(self.path)
+ last_modified = formatdate(stat.st_mtime)
+ if last_modified == request.get_header('If-Modified-Since'):
+ # handle exact match of If-Modified-Since header
+ response.set_status(304)
+ return ''
+
+ # Set the Content-Type for the response and return the file's contents.
+ response.set_content_type(self.mime_type)
+ if self.encoding:
+ response.set_header("Content-Encoding", self.encoding)
+
+ response.set_header('Last-Modified', last_modified)
+
+ return FileStream(open(self.path, 'rb'), stat.st_size)
+
+
+class StaticDirectory(Directory):
+
+ """
+ Wrap a filesystem directory containing static files as a Quixote directory.
+ """
+
+ _q_exports = ['']
+
+ FILE_CLASS = StaticFile
+
+ def __init__(self, path, use_cache=False, list_directory=False,
+ follow_symlinks=False, cache_time=None, file_class=None,
+ index_filenames=None):
+ """(path:string, use_cache:bool, list_directory:bool,
+ follow_symlinks:bool, cache_time:int,
+ file_class=None, index_filenames:[string])
+
+ Initialize instance with the absolute path to the file.
+ If 'use_cache' is true, StaticFile instances will be cached in memory.
+ If 'list_directory' is true, users can request a directory listing.
+ If 'follow_symlinks' is true, symbolic links will be followed.
+
+ Optional parameter cache_time allows setting of Expires header in
+ response object (see note for StaticFile for more detail).
+
+ Optional parameter 'index_filenames' specifies a list of
+ filenames to be used as index files in the directory. First
+ file found searching left to right is returned.
+ """
+
+ # Check that the supplied path is absolute
+ self.path = path
+ if not os.path.isabs(path):
+ raise ValueError, "Path %r is not absolute" % path
+
+ self.use_cache = use_cache
+ self.cache = {}
+ self.list_directory = list_directory
+ self.follow_symlinks = follow_symlinks
+ self.cache_time = cache_time
+ if file_class is not None:
+ self.file_class = file_class
+ else:
+ self.file_class = self.FILE_CLASS
+ self.index_filenames = index_filenames
+
+ def _render_header(self, title):
+ r = TemplateIO(html=True)
+ r += htmltext('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 '
+ 'Transitional//EN" '
+ '"http://www.w3.org/TR/REC-html40/loose.dtd">')
+ r += htmltext('<html>')
+ r += htmltext('<head><title>%s</title></head>') % title
+ r += htmltext('<body>')
+ r += htmltext("<h1>%s</h1>") % title
+ return r.getvalue()
+
+ def _render_footer(self):
+ return htmltext('</body></html>')
+
+ def _q_index(self):
+ """
+ If directory listings are allowed, generate a simple HTML
+ listing of the directory's contents with each item hyperlinked;
+ if the item is a subdirectory, place a '/' after it. If not allowed,
+ return a page to that effect.
+ """
+ if self.index_filenames:
+ for name in self.index_filenames:
+ try:
+ obj = self._q_lookup(name)
+ except errors.TraversalError:
+ continue
+ if not isinstance(obj, StaticDirectory) and callable(obj):
+ return obj()
+ r = TemplateIO(html=True)
+ if self.list_directory:
+ r += self._render_header('Index of %s' % quixote.get_path())
+ template = htmltext('<a href="%s">%s</a>%s\n')
+ r += htmltext('<pre>')
+ r += template % ('..', '..', '')
+ files = os.listdir(self.path)
+ files.sort()
+ for filename in files:
+ filepath = os.path.join(self.path, filename)
+ marker = os.path.isdir(filepath) and "/" or ""
+ r += template % (urllib.quote(filename), filename, marker)
+ r += htmltext('</pre>')
+ r += self._render_footer()
+ else:
+ r += self._render_header('Directory listing denied')
+ r += htmltext('<p>This directory does not allow its contents '
+ 'to be listed.</p>')
+ r += self._render_footer()
+ return r.getvalue()
+
+ def _q_lookup(self, name):
+ """
+ Get a file from the filesystem directory and return the StaticFile
+ or StaticDirectory wrapper of it; use caching if that is in use.
+ """
+ if name in ('.', '..'):
+ raise errors.TraversalError(private_msg="Attempt to use '.', '..'")
+ if self.cache.has_key(name):
+ # Get item from cache
+ item = self.cache[name]
+ else:
+ # Get item from filesystem; cache it if caching is in use.
+ item_filepath = os.path.join(self.path, name)
+ while os.path.islink(item_filepath):
+ if not self.follow_symlinks:
+ raise errors.TraversalError
+ else:
+ dest = os.readlink(item_filepath)
+ item_filepath = os.path.join(self.path, dest)
+
+ if os.path.isdir(item_filepath):
+ item = self.__class__(item_filepath, self.use_cache,
+ self.list_directory,
+ self.follow_symlinks, self.cache_time,
+ self.file_class, self.index_filenames)
+
+ elif os.path.isfile(item_filepath):
+ item = self.file_class(item_filepath, self.follow_symlinks,
+ cache_time=self.cache_time)
+ else:
+ raise errors.TraversalError
+ if self.use_cache:
+ self.cache[name] = item
+ return item
+
+
+class Redirector:
+ """
+ A simple class that can be used from inside _q_lookup() to redirect
+ requests.
+ """
+
+ _q_exports = []
+
+ def __init__(self, location, permanent=False):
+ self.location = location
+ self.permanent = permanent
+
+ def _q_lookup(self, component):
+ return self
+
+ def __call__(self):
+ return quixote.redirect(self.location, self.permanent)
+
+
+def dump_request(request=None):
+ if request is None:
+ request = quixote.get_request()
+ """Dump an HTTPRequest object as HTML."""
+ row_fmt = htmltext('<tr><th>%s</th><td>%s</td></tr>')
+ r = TemplateIO(html=True)
+ r += htmltext('<h3>form</h3>'
+ '<table>')
+ for k, v in request.form.items():
+ r += row_fmt % (k, v)
+ r += htmltext('</table>'
+ '<h3>cookies</h3>'
+ '<table>')
+ for k, v in request.cookies.items():
+ r += row_fmt % (k, v)
+ r += htmltext('</table>'
+ '<h3>environ</h3>'
+ '<table>')
+ for k, v in request.environ.items():
+ r += row_fmt % (k, v)
+ r += htmltext('</table>')
+ return r.getvalue()
+
+def get_directory_path():
+ """() -> [object]
+ Return the list of traversed instances.
+ """
+ path = []
+ frame = sys._getframe()
+ while frame:
+ if frame.f_code.co_name == '_q_traverse':
+ self = frame.f_locals.get('self', None)
+ if path[:1] != [self]:
+ path.insert(0, self)
+ frame = frame.f_back
+ return path
diff --git a/pypers/europython05/easytwill.py b/pypers/europython05/easytwill.py
new file mode 100755
index 0000000..9e8028c
--- /dev/null
+++ b/pypers/europython05/easytwill.py
@@ -0,0 +1,4 @@
+
+from twill import TwillCommandLoop
+
+TwillCommandLoop().cmdloop()
diff --git a/pypers/europython05/fig.html b/pypers/europython05/fig.html
new file mode 100755
index 0000000..4138d50
--- /dev/null
+++ b/pypers/europython05/fig.html
@@ -0,0 +1,15 @@
+<?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></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<div class="image"><img alt="python.png" src="python.png" /></div>
+</div>
+</body>
+</html>
diff --git a/pypers/europython05/fig.txt b/pypers/europython05/fig.txt
new file mode 100755
index 0000000..4a2ff92
--- /dev/null
+++ b/pypers/europython05/fig.txt
@@ -0,0 +1 @@
+.. image:: python.png
diff --git a/pypers/europython05/hello.py b/pypers/europython05/hello.py
new file mode 100755
index 0000000..c8f176f
--- /dev/null
+++ b/pypers/europython05/hello.py
@@ -0,0 +1,7 @@
+from ms.quixote_utils_exp import Website, htmlpage
+
+@htmlpage()
+def hello():
+ return "hello world"
+
+Website(hello).publisher().run_show(page="hello",port=7080)
diff --git a/pypers/europython05/lightening_talk.py b/pypers/europython05/lightening_talk.py
new file mode 100755
index 0000000..0bd1fac
--- /dev/null
+++ b/pypers/europython05/lightening_talk.py
@@ -0,0 +1,57 @@
+# standard way
+
+def decorator_trace(f):
+ def newf(*args, **kw):
+ print "calling %s with args %s, %s" % (f.__name__, args, kw)
+ return f(*args, **kw)
+ newf.__name__ = f.__name__
+ newf.__dict__ = f.__dict__
+ newf.__doc__ = f.__doc__
+ return newf
+
+@decorator_trace
+def f1(x):
+ pass
+
+
+from decorator import decorator
+
+def trace(f, *args, **kw):
+ print "calling %s with args %s, %s" % (f.func_name, args, kw)
+ return f(*args, **kw)
+
+@decorator(trace)
+def f1(x):
+ pass
+
+@decorator(trace)
+def f(x, y=1, z=2, *args, **kw):
+ pass
+
+f(0, 3)
+
+print getargspec(f)
+(['x', 'y', 'z'], 'args', 'kw', (1, 2))
+
+@decorator(trace)
+def exotic_signature((x, y)=(1,2)):
+ return x+y
+
+print getargspec(exotic_signature)
+
+#################################################
+
+import threading
+
+def delayed(nsec):
+ def call(proc, *args, **kw):
+ thread = threading.Timer(nsec, proc, args, kw)
+ thread.start()
+ return thread
+ return decorator(call)
+
+@delayed(4)
+def print_hello():
+ print "hello"
+
+print_hello()
diff --git a/pypers/europython05/table.html b/pypers/europython05/table.html
new file mode 100755
index 0000000..d995b09
--- /dev/null
+++ b/pypers/europython05/table.html
@@ -0,0 +1,44 @@
+<?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>Example table</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="example-table">
+<h1 class="title">Example table</h1>
+<blockquote>
+<table border="1" class="docutils">
+<colgroup>
+<col width="36%" />
+<col width="32%" />
+<col width="32%" />
+</colgroup>
+<tbody valign="top">
+<tr><td>Item</td>
+<td>400 x 3</td>
+<td>1200E</td>
+</tr>
+<tr><td>Tax</td>
+<td>20%</td>
+<td>240E</td>
+</tr>
+<tr><td>Other</td>
+<td>150 x 3</td>
+<td>450E</td>
+</tr>
+<tr><td colspan="2">Special</td>
+<td>60E</td>
+</tr>
+<tr><td colspan="2">Total</td>
+<td>1950E</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+</div>
+</body>
+</html>
diff --git a/pypers/europython05/table.tex b/pypers/europython05/table.tex
new file mode 100755
index 0000000..6c47294
--- /dev/null
+++ b/pypers/europython05/table.tex
@@ -0,0 +1,115 @@
+\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{Example table}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Example table}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+\begin{quote}
+
+\begin{longtable}[c]{|p{0.11\locallinewidth}|p{0.10\locallinewidth}|p{0.10\locallinewidth}|}
+\hline
+
+Item
+ &
+400 x 3
+ &
+1200E
+ \\
+\hline
+
+Tax
+ &
+20{\%}
+ &
+240E
+ \\
+\hline
+
+Other
+ &
+150 x 3
+ &
+450E
+ \\
+\hline
+\multicolumn{2}{|l|}{
+Special
+} &
+60E
+ \\
+\hline
+\multicolumn{2}{|l|}{
+Total
+} &
+1950E
+ \\
+\hline
+\end{longtable}
+\end{quote}
+
+\end{document}
+
diff --git a/pypers/europython05/table.txt b/pypers/europython05/table.txt
new file mode 100755
index 0000000..bc8b0cd
--- /dev/null
+++ b/pypers/europython05/table.txt
@@ -0,0 +1,14 @@
+Example table
+--------------
+
+ ======== ======= =======
+ Item 400 x 3 1200E
+ -------- ------- -------
+ Tax 20% 240E
+ -------- ------- -------
+ Other 150 x 3 450E
+ -------- ------- -------
+ Special 60E
+ ---------------- -------
+ Total 1950E
+ ================ =======
diff --git a/pypers/europython05/taste-python.html b/pypers/europython05/taste-python.html
new file mode 100755
index 0000000..8df2c65
--- /dev/null
+++ b/pypers/europython05/taste-python.html
@@ -0,0 +1,47 @@
+<?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>Taste Python (EuroPython 2005)</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="taste-python-europython-2005">
+<h1 class="title">Taste Python (EuroPython 2005)</h1>
+<p>Hi Harald,</p>
+<p>I was looking at your suggestions again:</p>
+<pre class="literal-block">
+&quot;&quot;&quot;
+a) some real &quot;hello world&quot; stuff
+b) tricks and powers of the interactive console
+c) dynamic modules taken from
+- web scraping with python (beautifull soup, urllib..)
+- database access (WITHOUT sqlobject, which is presented seperately)
+- some basic system Administration Stuff
+- do a PDF
+&quot;&quot;&quot;
+</pre>
+<p>I think we should decide how to split this stuff between us and
+also have an idea of the time needed. I could do</p>
+<ol class="arabic simple">
+<li>A superfast toor of IPython (10 minutes)</li>
+<li>reStructuredText, how to make Web pages and PDFs (15 minutes)</li>
+<li>Twill, i.e. how to interact with the Web the easy way (10 minutes)</li>
+</ol>
+<p>The time are indicatives i.e. they could easily become longer (but not
+much shorter, unless we kill something). According to the interests of people
+times and topics could change at run time, but I think its is best if we have
+prepared at least a provisional program. BTW, if you don't know about Twill,
+here is a reference:</p>
+<p><a class="reference" href="http://darcs.idyll.org/~t/projects/twill/README.html">http://darcs.idyll.org/~t/projects/twill/README.html</a></p>
+<p>It is so easy to use that it makes a good candidate even for a
+total beginner course and on the other hand it is useful even to
+the expert Web developer. I am writing a paper on it for O'Reilly,
+so I am already prepared ;)</p>
+<blockquote>
+Michele Simionato</blockquote>
+</div>
+</body>
+</html>
diff --git a/pypers/europython05/taste-python.txt b/pypers/europython05/taste-python.txt
new file mode 100755
index 0000000..7bc4501
--- /dev/null
+++ b/pypers/europython05/taste-python.txt
@@ -0,0 +1,40 @@
+Taste Python (EuroPython 2005)
+==========================================
+
+Hi Harald,
+
+I was looking at your suggestions again:
+
+ ::
+
+ """
+ a) some real "hello world" stuff
+ b) tricks and powers of the interactive console
+ c) dynamic modules taken from
+ - web scraping with python (beautifull soup, urllib..)
+ - database access (WITHOUT sqlobject, which is presented seperately)
+ - some basic system Administration Stuff
+ - do a PDF
+ """
+
+I think we should decide how to split this stuff between us and
+also have an idea of the time needed. I could do
+
+1. A superfast toor of IPython (10 minutes)
+2. reStructuredText, how to make Web pages and PDFs (15 minutes)
+3. Twill, i.e. how to interact with the Web the easy way (10 minutes)
+
+The time are indicatives i.e. they could easily become longer (but not
+much shorter, unless we kill something). According to the interests of people
+times and topics could change at run time, but I think its is best if we have
+prepared at least a provisional program. BTW, if you don't know about Twill,
+here is a reference:
+
+http://darcs.idyll.org/~t/projects/twill/README.html
+
+It is so easy to use that it makes a good candidate even for a
+total beginner course and on the other hand it is useful even to
+the expert Web developer. I am writing a paper on it for O'Reilly,
+so I am already prepared ;)
+
+ Michele Simionato
diff --git a/pypers/europython05/test1.py b/pypers/europython05/test1.py
new file mode 100755
index 0000000..c5f55b6
--- /dev/null
+++ b/pypers/europython05/test1.py
@@ -0,0 +1 @@
+assert 1 == 1
diff --git a/pypers/europython06/cherrypy_ex.py b/pypers/europython06/cherrypy_ex.py
new file mode 100644
index 0000000..9b87389
--- /dev/null
+++ b/pypers/europython06/cherrypy_ex.py
@@ -0,0 +1,10 @@
+import cherrypy as cp
+
+class Root(object):
+ @cp.expose
+ def index(self):
+ return 'This is the index page'
+
+if __name__ == '__main__':
+ cp.root = Root()
+ cp.server.start()
diff --git a/pypers/europython06/dt_eval.py b/pypers/europython06/dt_eval.py
new file mode 100644
index 0000000..5e9f509
--- /dev/null
+++ b/pypers/europython06/dt_eval.py
@@ -0,0 +1,11 @@
+"""
+Doc tester evaluator
+"""
+
+def evaldoctest():
+ code = ''.join(line[4:] for line in raw_input().splitlines())
+ print code
+
+if __name__ == '__main__':
+ while True:
+ evaldoctest()
diff --git a/pypers/europython06/ipython-ask.txt b/pypers/europython06/ipython-ask.txt
new file mode 100644
index 0000000..0105a9f
--- /dev/null
+++ b/pypers/europython06/ipython-ask.txt
@@ -0,0 +1,20 @@
+Can I cut and paste a doctest into IPython?
+I mean, if I have the doctest
+
+>>> 1+1
+2
+
+and I cut and past it into IPython I obviously get
+
+File "<console>", line 1
+ >>> 1+1
+ ^
+SyntaxError: invalid syntax
+
+but I guess there is an automagic option to avoid this. Am I right?
+
+Alternatively, I would like leading spaces to be stripped, so that
+I could cut and paste example from a reStructuredText document.
+
+
+ Michele Simionato
diff --git a/pypers/europython06/talk.html b/pypers/europython06/talk.html
new file mode 100644
index 0000000..23db191
--- /dev/null
+++ b/pypers/europython06/talk.html
@@ -0,0 +1,629 @@
+<?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.4: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title></title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+<h2>EuroPython 2006 - 4 July 2006</h2>
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+
+</div>
+<div class="slide" id="using-decorators">
+<h1>Using decorators</h1>
+<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">talk given at:</th><td class="field-body">EuroPython 2006</td>
+</tr>
+<tr class="field"><th class="field-name">by:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">2006-07-04</td>
+</tr>
+</tbody>
+</table>
+<p>An introduction to Python 2.4 decorators.</p>
+<!-- Definitions of interpreted text roles (classes) for S5/HTML data. -->
+<!-- This data file has been placed in the public domain. -->
+<!-- Colours
+======= -->
+<!-- Text Sizes
+========== -->
+<!-- Display in Slides (Presentation Mode) Only
+========================================== -->
+<!-- Display in Outline Mode Only
+============================ -->
+<!-- Display in Print Only
+===================== -->
+<!-- Incremental Display
+=================== -->
+</div>
+<div class="slide" id="let-s-begin-with-a-poll">
+<h1>Let's begin with a poll ...</h1>
+<ol class="incremental arabic simple">
+<li>how many of you know what decorators are?</li>
+<li>how many of you have used built-in decorators?</li>
+</ol>
+<blockquote class="incremental">
+<pre class="literal-block">
+# example of built-in decorator
+&#64;classmethod
+def makeinstance(cls):
+ return cls()
+</pre>
+</blockquote>
+<ol class="incremental arabic simple" start="3">
+<li>how many of you are using custom decorators?</li>
+</ol>
+</div>
+<div class="slide" id="decorators-are-out-there">
+<h1>Decorators are out there</h1>
+<p>There already libraries out there that provide custom decorators.
+I will show two examples of many.</p>
+<ol class="incremental arabic simple">
+<li>cherrypy: exposing methods</li>
+<li>standard library: making templates for the <tt class="docutils literal"><span class="pre">with</span></tt> statement</li>
+</ol>
+</div>
+<div class="slide" id="cherrypy-exposing-web-methods">
+<h1>CherryPy (exposing web methods)</h1>
+<pre class="literal-block">
+import cherrypy as cp
+
+class Root(object):
+ &#64;cp.expose
+ def index(self):
+ return 'This is the index page'
+
+if __name__ == '__main__':
+ cp.root = Root()
+ cp.server.start()
+</pre>
+</div>
+<div class="slide" id="how-does-it-work">
+<h1>How does it work</h1>
+<pre class="literal-block">
+def expose(func):
+ &quot;Expose a function&quot;
+ func.exposed = True
+ return func
+
+ &#64;expose
+ def foo(): pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; foo.exposed
+True
+</pre>
+</div>
+<div class="slide" id="decorators-as-syntactic-sugar">
+<h1>Decorators as syntactic sugar</h1>
+<p>In other words</p>
+<blockquote>
+<pre class="literal-block">
+&#64;decor
+def foo(): pass
+</pre>
+</blockquote>
+<p>is (essentially) a shortcut for</p>
+<blockquote>
+<pre class="literal-block">
+def foo(): pass
+foo = decor(foo)
+</pre>
+</blockquote>
+</div>
+<div class="slide" id="only-syntactic-sugar">
+<h1>Only syntactic sugar?</h1>
+<p><span class="incremental">all Turing-complete languages are equivalent up to syntactic sugar</span></p>
+<blockquote>
+<span class="incremental">=&gt;</span></blockquote>
+<p><span class="incremental">syntactic sugar is the most important thing ever!!</span></p>
+<blockquote>
+<span class="incremental">;-)</span></blockquote>
+<p><span class="incremental">*Decorators changed the way we think about functions*</span></p>
+</div>
+<div class="slide" id="python-2-5-contextmanager">
+<h1>Python 2.5 contextmanager</h1>
+<pre class="literal-block">
+from __future__ import with_statement
+from contextlib import contextmanager
+&#64;contextmanager
+def cursor(conn):
+ curs = conn.cursor()
+ try:
+ yield curs
+ except:
+ conn.rollback(); raise
+ finally:
+ curs.close()
+ conn.commit()
+</pre>
+</div>
+<div class="slide" id="python-2-5-transactions">
+<h1>Python 2.5 transactions</h1>
+<pre class="literal-block">
+import psycopg
+from contextlib import closing
+
+with closing(psycopg.connect('')) as conn:
+ with cursor(conn) as c:
+ c.execute('create table example (name char(3))')
+ c.execute(&quot;insert into example values ('foo')&quot;)
+ c.execute(&quot;insert into example values ('bar')&quot;)
+ c.execute('select * from example')
+ print c.fetchall()
+</pre>
+</div>
+<div class="slide" id="writing-your-own-decorators">
+<h1>Writing your own decorators</h1>
+<p>Naive implementation:</p>
+<pre class="literal-block">
+def traced(func):
+ def newfunc(*args,**kw):
+ print 'Calling %s with arguments %s, %s' % (
+ func.__name__, args, kw)
+ return func(*args, **kw)
+ return newfunc
+
+&#64;traced
+def square(x):
+ return x*x
+</pre>
+</div>
+<div class="slide" id="example">
+<h1>Example</h1>
+<pre class="doctest-block">
+&gt;&gt;&gt; square(2)
+Calling square with arguments (2,), {}
+4
+</pre>
+<p>However the naive implementation breaks introspection:</p>
+<pre class="literal-block">
+&gt;&gt;&gt; help(square)
+Help on function newfunction in module __main__:
+
+newfunction(*args, **kw)
+</pre>
+</div>
+<div class="slide" id="a-non-solution">
+<h1>A non-solution</h1>
+<pre class="incremental doctest-block">
+&gt;&gt;&gt; def traced(func):
+... def newfunc(*args,**kw):
+... print 'Calling %s with arguments %s, %s' % (
+... func.__name__, args, kw)
+... return func(*args, **kw)
+... newfunc.__name__ = func.__name__
+... newfunc.__doc__ = func.__doc__
+... newfunc.__module__ = func.__module__
+... newfunc.__dict__ = func.__dict__
+... return newfunc
+</pre>
+<p class="incremental">but the signature is still broken :-(</p>
+</div>
+<div class="slide" id="enter-the-decorator-module">
+<h1>Enter the <em>decorator</em> module</h1>
+<pre class="literal-block">
+from decorator import decorator
+
+&#64;decorator
+def traced(func, *args, **kw):
+ print 'Calling %s with arguments %s, %s' % (
+ func.__name__, args, kw)
+ return func(*args, **kw)
+
+&#64;traced
+def square(x):
+ return x*x
+</pre>
+</div>
+<div class="slide" id="look-ma-it-works">
+<h1>Look ma, it works!</h1>
+<pre class="literal-block">
+&gt;&gt;&gt; square(2)
+Calling square with arguments (2,), {}
+4
+&gt;&gt;&gt; help(square)
+Help on function square in module __main__:
+
+square(x)
+
+&gt;&gt;&gt; isinstance(traced, decorator)
+True
+</pre>
+</div>
+<div class="slide" id="nontrivial-decorators">
+<h1>Nontrivial decorators</h1>
+<ul class="incremental">
+<li><p class="first">timing:</p>
+<pre class="literal-block">
+&#64;time
+def mycomputation(): pass
+</pre>
+</li>
+<li><p class="first">logging:</p>
+<pre class="literal-block">
+&#64;log
+def myprocedure(): pass
+</pre>
+</li>
+</ul>
+</div>
+<div class="slide" id="other-applications">
+<h1>Other applications ...</h1>
+<ul>
+<li><p class="first">caching:</p>
+<pre class="literal-block">
+&#64;cached
+def mylongcomputation(): pass
+</pre>
+</li>
+</ul>
+<ul class="incremental">
+<li><p class="first">access control:</p>
+<pre class="literal-block">
+&#64;admin
+def showpage(): pass
+
+&#64;user
+def showpage(): pass
+</pre>
+</li>
+</ul>
+</div>
+<div class="slide" id="id1">
+<h1>Other applications</h1>
+<ul class="simple">
+<li>provide nice syntax in frameworks</li>
+</ul>
+<ul class="incremental">
+<li><p class="first">remove boilerplate in locking, transactions, ...</p>
+</li>
+<li><p class="first">mind boggling exercises:</p>
+<pre class="literal-block">
+&#64;tail_recursive
+def fact(n, acc=1): return fact(n-1, acc*n)
+</pre>
+</li>
+<li><p class="first">etc. etc.</p>
+</li>
+</ul>
+</div>
+<div class="slide" id="caveats">
+<h1>Caveats</h1>
+<ol class="incremental arabic simple">
+<li>you may have performance issues</li>
+<li>your traceback will become longer</li>
+<li>you may end up being too clever :-(</li>
+</ol>
+</div>
+<div class="slide" id="references">
+<h1>References</h1>
+<ul class="simple">
+<li>history of decorators
+<a class="reference" href="http://wiki.python.org/moin/PythonDecorators">http://wiki.python.org/moin/PythonDecorators</a></li>
+<li>the decorators PEP:
+<a class="reference" href="http://www.python.org/dev/peps/pep-0318/">http://www.python.org/dev/peps/pep-0318/</a></li>
+<li><a class="reference" href="http://wiki.python.org/moin/PythonDecoratorLibrary">http://wiki.python.org/moin/PythonDecoratorLibrary</a></li>
+<li>David Mertz's on IBMDeveloperWorks</li>
+<li><a class="reference" href="http://www.phyast.pitt.edu/~micheles/python/documentation.txt">http://www.phyast.pitt.edu/~micheles/python/documentation.txt</a></li>
+</ul>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/europython06/talk.txt b/pypers/europython06/talk.txt
new file mode 100644
index 0000000..52450d3
--- /dev/null
+++ b/pypers/europython06/talk.txt
@@ -0,0 +1,288 @@
+Using decorators
+----------------
+
+:talk given at: EuroPython 2006
+:by: Michele Simionato
+:date: 2006-07-04
+
+An introduction to Python 2.4 decorators.
+
+.. include:: <s5defs.txt>
+.. footer:: EuroPython 2006 - 4 July 2006
+
+Let's begin with a poll ...
+---------------------------
+
+.. class:: incremental
+
+ 1. how many of you know what decorators are?
+
+ 2. how many of you have used built-in decorators?
+
+ ::
+
+ # example of built-in decorator
+ @classmethod
+ def makeinstance(cls):
+ return cls()
+
+ 3. how many of you are using custom decorators?
+
+Decorators are out there
+-------------------------------------------
+
+There already libraries out there that provide custom decorators.
+I will show two examples of many.
+
+.. class:: incremental
+
+1. cherrypy: exposing methods
+2. standard library: making templates for the ``with`` statement
+
+
+CherryPy (exposing web methods)
+---------------------------------
+
+::
+
+ import cherrypy as cp
+
+ class Root(object):
+ @cp.expose
+ def index(self):
+ return 'This is the index page'
+
+ if __name__ == '__main__':
+ cp.root = Root()
+ cp.server.start()
+
+How does it work
+---------------------------------------------
+
+::
+
+ def expose(func):
+ "Expose a function"
+ func.exposed = True
+ return func
+
+ @expose
+ def foo(): pass
+
+>>> foo.exposed
+True
+
+Decorators as syntactic sugar
+-----------------------------
+
+In other words
+
+ ::
+
+ @decor
+ def foo(): pass
+
+is (essentially) a shortcut for
+
+ ::
+
+ def foo(): pass
+ foo = decor(foo)
+
+Only syntactic sugar?
+--------------------------------------
+
+`all Turing-complete languages are equivalent up to syntactic sugar`
+
+ `=>`
+
+`syntactic sugar is the most important thing ever!!`
+
+ `;-)`
+
+`*Decorators changed the way we think about functions*`
+
+Python 2.5 contextmanager
+------------------------------------------
+
+::
+
+ from __future__ import with_statement
+ from contextlib import contextmanager
+ @contextmanager
+ def cursor(conn):
+ curs = conn.cursor()
+ try:
+ yield curs
+ except:
+ conn.rollback(); raise
+ finally:
+ curs.close()
+ conn.commit()
+
+
+Python 2.5 transactions
+---------------------------------------------------
+
+::
+
+ import psycopg
+ from contextlib import closing
+
+ with closing(psycopg.connect('')) as conn:
+ with cursor(conn) as c:
+ c.execute('create table example (name char(3))')
+ c.execute("insert into example values ('foo')")
+ c.execute("insert into example values ('bar')")
+ c.execute('select * from example')
+ print c.fetchall()
+
+Writing your own decorators
+--------------------------------------
+
+Naive implementation::
+
+ def traced(func):
+ def newfunc(*args,**kw):
+ print 'Calling %s with arguments %s, %s' % (
+ func.__name__, args, kw)
+ return func(*args, **kw)
+ return newfunc
+
+ @traced
+ def square(x):
+ return x*x
+
+Example
+------------------------------------------
+
+>>> square(2)
+Calling square with arguments (2,), {}
+4
+
+However the naive implementation breaks introspection::
+
+
+ >>> help(square)
+ Help on function newfunction in module __main__:
+
+ newfunction(*args, **kw)
+
+A non-solution
+---------------------------------
+
+.. class:: incremental
+
+ >>> def traced(func):
+ ... def newfunc(*args,**kw):
+ ... print 'Calling %s with arguments %s, %s' % (
+ ... func.__name__, args, kw)
+ ... return func(*args, **kw)
+ ... newfunc.__name__ = func.__name__
+ ... newfunc.__doc__ = func.__doc__
+ ... newfunc.__module__ = func.__module__
+ ... newfunc.__dict__ = func.__dict__
+ ... return newfunc
+
+ but the signature is still broken :-(
+
+Enter the *decorator* module
+------------------------------------------
+
+::
+
+ from decorator import decorator
+
+ @decorator
+ def traced(func, *args, **kw):
+ print 'Calling %s with arguments %s, %s' % (
+ func.__name__, args, kw)
+ return func(*args, **kw)
+
+ @traced
+ def square(x):
+ return x*x
+
+Look ma, it works!
+------------------------------------------
+
+::
+
+ >>> square(2)
+ Calling square with arguments (2,), {}
+ 4
+ >>> help(square)
+ Help on function square in module __main__:
+
+ square(x)
+
+ >>> isinstance(traced, decorator)
+ True
+
+Nontrivial decorators
+----------------------------------------
+
+.. class:: incremental
+
+- timing::
+
+ @time
+ def mycomputation(): pass
+
+- logging::
+
+ @log
+ def myprocedure(): pass
+
+Other applications ...
+-------------------------------------------
+
+- caching::
+
+ @cached
+ def mylongcomputation(): pass
+
+.. class:: incremental
+
+- access control::
+
+ @admin
+ def showpage(): pass
+
+ @user
+ def showpage(): pass
+
+Other applications
+-------------------------------------------
+
+- provide nice syntax in frameworks
+
+.. class:: incremental
+
+- remove boilerplate in locking, transactions, ...
+- mind boggling exercises::
+
+ @tail_recursive
+ def fact(n, acc=1): return fact(n-1, acc*n)
+
+- etc. etc.
+
+Caveats
+---------------------------------
+
+.. class:: incremental
+
+1. you may have performance issues
+2. your traceback will become longer
+3. you may end up being too clever :-(
+
+References
+-------------------------------------
+
+- history of decorators
+ http://wiki.python.org/moin/PythonDecorators
+- the decorators PEP:
+ http://www.python.org/dev/peps/pep-0318/
+- http://wiki.python.org/moin/PythonDecoratorLibrary
+- David Mertz's on IBMDeveloperWorks
+- http://www.phyast.pitt.edu/~micheles/python/documentation.txt
diff --git a/pypers/europython06/transac.py b/pypers/europython06/transac.py
new file mode 100644
index 0000000..df7a32b
--- /dev/null
+++ b/pypers/europython06/transac.py
@@ -0,0 +1,26 @@
+from __future__ import with_statement
+from contextlib import contextmanager, closing
+
+@contextmanager
+def cursor(conn, action='commit'):
+ assert action in ('commit', 'rollback')
+ curs = conn.cursor()
+ try:
+ yield curs
+ except:
+ conn.rollback()
+ raise
+ finally:
+ curs.close()
+ getattr(conn, action)() # commit or rollback
+
+if __name__ == '__main__':
+ import psycopg
+ with closing(psycopg.connect('')) as conn:
+ with cursor(conn, 'rollback') as curs:
+ curs.execute('create table example (name varchar(32))')
+ curs.execute("insert into example values ('pippo')")
+ curs.execute("insert into example values ('lippo')")
+ curs.execute('select * from example')
+ print curs.fetchall()
+
diff --git a/pypers/europython06/ui/default/blank.gif b/pypers/europython06/ui/default/blank.gif
new file mode 100644
index 0000000..75b945d
--- /dev/null
+++ b/pypers/europython06/ui/default/blank.gif
Binary files differ
diff --git a/pypers/europython06/ui/default/slides.js b/pypers/europython06/ui/default/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/europython06/ui/default/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/europython07/Scotch_Whisky_(aka).png b/pypers/europython07/Scotch_Whisky_(aka).png
new file mode 100644
index 0000000..91eeabc
--- /dev/null
+++ b/pypers/europython07/Scotch_Whisky_(aka).png
Binary files differ
diff --git a/pypers/europython07/abstract-en.txt b/pypers/europython07/abstract-en.txt
new file mode 100644
index 0000000..844956c
--- /dev/null
+++ b/pypers/europython07/abstract-en.txt
@@ -0,0 +1,12 @@
+As the old saying goes, Python is the only language with more Web frameworks than
+keywords. This is sometimes an advantage, but more often than not, it is an issue.
+In order to improve the interoperability between different frameworks, Phillip
+J. Eby proposed in 2003 a specification, the WSGI or Web Server Gateway
+Interface, a.k.a Whiskey. In my talk I will discuss how you can achieve portability
+of your application by following the WSGI protocol. I will give practical
+examples of how to use the WSGI reference implementation which is part of the
+standard library (wsgiref), of how to supplement it with other WSGI-compliant
+libraries (notably Paste by Ian Bicking) and of how to integrate your WSGI
+application in different frameworks including Zope, Twisted, TurboGears et al.
+The talk is intended as a tutorial and requires only elementary knowledge of
+Web programming, at the level of simple CGI.
diff --git a/pypers/europython07/bio.txt b/pypers/europython07/bio.txt
new file mode 100644
index 0000000..3adf82e
--- /dev/null
+++ b/pypers/europython07/bio.txt
@@ -0,0 +1,14 @@
+Nota biografica
+---------------
+
+Michele Simionato e' conosciuto nella comunita' internazionale per la sua
+attivita' nei newsgroups di Python, per la sua partecipazione a conferenze,
+per i suoi articoli divulgativi, come autore di ricette pubblicate nel Python
+cookbook e per un modulo open source sui decoratori. Michele e' anche socio
+fondatore dell'associazione Python Italia.
+In una vita precedente Michele si e' occupato di Fisica Teorica, lavorando per
+vari anni in Francia e negli Stati Uniti come ricercatore post-doc. Adesso
+Michele vive e lavora a Milano per StatPro Italia, occupandosi di software
+finanziario e programmazione Web. Nel suo tempo libero, quando non e' impegnato
+con la visione di vecchi cartoni animati giapponesi, Michele cerca sempre di
+imparare qualche cosa di nuovo.
diff --git a/pypers/europython07/evalexception_ex.py b/pypers/europython07/evalexception_ex.py
new file mode 100644
index 0000000..cd6bf45
--- /dev/null
+++ b/pypers/europython07/evalexception_ex.py
@@ -0,0 +1,11 @@
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ yield str(a/b)
+
+if __name__ == '__main__':
+ make_server('', 9090, EvalException(app)).serve_forever()
diff --git a/pypers/europython07/hello.py b/pypers/europython07/hello.py
new file mode 100644
index 0000000..caa7f62
--- /dev/null
+++ b/pypers/europython07/hello.py
@@ -0,0 +1,10 @@
+
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
diff --git a/pypers/europython07/objectpublisher.py b/pypers/europython07/objectpublisher.py
new file mode 100644
index 0000000..10a48a5
--- /dev/null
+++ b/pypers/europython07/objectpublisher.py
@@ -0,0 +1,43 @@
+from wsgiref import util, simple_server
+
+# page is any callable object returning HTML in chunks
+class WSGIObjectPublisher(object):
+ def __init__(self, root):
+ self.root = root
+ def __call__(self, env, resp):
+ return self.getsubpage(self.root, env, resp)()
+ def getsubpage(self, root, env, resp):
+ script_name = util.shift_path_info(env)
+ if not script_name: # We've arrived!
+ resp('200 OK', [('content-type', 'text/html')])
+ return root
+ try:
+ page = getattr(root, script_name)
+ except AttributeError:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['missing page %r' % script_name]
+ exposed = getattr(page, 'exposed', False)
+ if not exposed:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['%r is not exposed!' % script_name]
+ return self.getsubpage(page, env, resp)
+
+if __name__ == '__main__':
+ class Example(object):
+ def __init__(self, sitename):
+ self.sitename = sitename
+ def __call__(self):
+ yield '<h1>%s: index page</h1>' % self.sitename
+ yield 'goto <a href="./page1">page1</a><br/>'
+ yield 'goto <a href="./page2">page2</a><br/>'
+ yield 'goto <a href="subsite">subsite</a><br/>'
+ def page1(self):
+ yield 'page1'
+ def page2(self):
+ yield 'page2'
+ page1.exposed = page2.exposed = True
+ root = Example('Example Site')
+ root.subsite = Example('Example Subsite')
+ root.subsite.exposed = True
+ app = WSGIObjectPublisher(root)
+ simple_server.make_server('', 8000, app).serve_forever()
diff --git a/pypers/europython07/simpleplotter.py b/pypers/europython07/simpleplotter.py
new file mode 100644
index 0000000..7006ef3
--- /dev/null
+++ b/pypers/europython07/simpleplotter.py
@@ -0,0 +1,25 @@
+import os, sys
+from ms.plot_utils import GnuPlotter
+DATADIR = os.path.expanduser('~/sp/equities-histories')
+TEMPL = '''\
+plot '-' using 1:2 with lines
+$data
+e'''
+
+def make_graph(code, batch):
+ gp = GnuPlotter(TEMPL, batch)
+ lines = list(file(os.path.join(DATADIR, code)))[-500:]
+ data = ''.join(lines)
+ png_file = '/tmp/%s.png' % code
+ gp.plot(locals())
+ return png_file
+
+if __name__ == '__main__':
+ L = len(sys.argv) - 1
+ if L == 1:
+ batch = False
+ elif L == 2:
+ batch = bool(int(sys.argv[2])) # 0 or 1
+ else:
+ sys.exit('Examples: $ python simpleplotter.py "fri-gb;AVE"')
+ make_graph(sys.argv[1], batch)
diff --git a/pypers/europython07/talk.html b/pypers/europython07/talk.html
new file mode 100644
index 0000000..aa9f605
--- /dev/null
+++ b/pypers/europython07/talk.html
@@ -0,0 +1,669 @@
+<?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.4.1: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title>An Introduction to Web Programming with WSGI</title>
+<meta name="organization" content="StatPro Italy" />
+<meta name="date" content="2007-07-11" />
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+<h1>An Introduction to Web Programming with WSGI</h1>
+<h2>EuroPython 2007 - 11 June 2007</h2>
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+<h1 class="title">An Introduction to Web Programming with WSGI</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Talk given at:</th><td class="field-body">EuroPython 2007</td>
+</tr>
+<tr class="field"><th class="docinfo-name">By:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr><th class="docinfo-name">Organization:</th>
+<td>StatPro Italy</td></tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>2007-07-11</td></tr>
+</tbody>
+</table>
+<!-- Definitions of interpreted text roles (classes) for S5/HTML data. -->
+<!-- This data file has been placed in the public domain. -->
+<!-- Colours
+======= -->
+<!-- Text Sizes
+========== -->
+<!-- Display in Slides (Presentation Mode) Only
+========================================== -->
+<!-- Display in Outline Mode Only
+============================ -->
+<!-- Display in Print Only
+===================== -->
+<!-- Incremental Display
+=================== -->
+
+</div>
+<div class="slide" id="introduction">
+<h1>Introduction</h1>
+<p>This is as a talk for beginners, only knowledge of
+CGI and a bit of HTTP protocol is expected
+<span class="incremental">(non-beginners -&gt; &quot;Zope on a Paste&quot;)</span></p>
+<ul class="incremental simple">
+<li>Have you ever heard of WSGI?</li>
+<li>Have you ever used WSGI?</li>
+<li>Have you used non WSGI-based Web frameworks?</li>
+</ul>
+</div>
+<div class="slide" id="ok-now-about-me">
+<h1>Ok, now about me</h1>
+<ul class="incremental simple">
+<li>I started to do Web programming with Zope and Plone</li>
+<li>(never liked them)</li>
+<li>I flirted with Quixote, Twisted, CherryPy, mod_python</li>
+<li>(which are ok but ...)</li>
+<li>I got in love with WSGI at last EuroPython</li>
+<li>(I am still in love with it ;)</li>
+</ul>
+</div>
+<div class="slide" id="what-i-have-done">
+<h1>What I have done</h1>
+<ul class="incremental simple">
+<li>written various helpers to simplify the usage of WSGI;</li>
+<li>debug with WSGI, deploy with Zope strategy</li>
+<li>written various simple Web tools for internal usage at <a class="reference" href="http://www.statpro.com">StatPro</a></li>
+<li>now we are going to start a major project with WSGI</li>
+</ul>
+</div>
+<div class="slide" id="wsgi">
+<h1>WSGI</h1>
+<img alt="Scotch_Whisky_(aka).png" src="Scotch_Whisky_(aka).png" />
+</div>
+<div class="slide" id="short-history-of-wsgi">
+<h1><em>Short</em> history of WSGI</h1>
+<ul class="incremental simple">
+<li>WSGI = Web Server Gateway Interface (<em>Whisky</em> for friends)</li>
+<li>the brainchild of Python guru Phillip J. Eby</li>
+<li>also input from Ian Bicking (<tt class="docutils literal"><span class="pre">paste</span></tt>) and others</li>
+<li>starting from Python 2.5, we have a WSGI web server in the standard
+library (<tt class="docutils literal"><span class="pre">wsgiref</span></tt>)</li>
+<li>there are plenty of simple and useful add-ons for WSGI applications
+out there (<tt class="docutils literal"><span class="pre">pylons</span> <span class="pre">...</span></tt>)</li>
+<li><a class="reference" href="http://video.google.com/videoplay?docid=-8502904076440714866">Guido</a> likes it!</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-key-concepts">
+<h1>WSGI key concepts</h1>
+<ol class="incremental arabic">
+<li><p class="first">WSGI application:</p>
+<p>(env, resp) -&gt; chunks of text</p>
+<p>env = environment dictionary of the server;
+resp = function sending to the client the HTTP headers</p>
+</li>
+<li><p class="first">WSGI middleware:</p>
+<p>WSGI app -&gt; enhanced WSGI app</p>
+</li>
+</ol>
+</div>
+<div class="slide" id="hello-world">
+<h1>Hello World</h1>
+<pre class="literal-block">
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['&lt;h1&gt;Hello, World!&lt;/h1&gt;']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
+</pre>
+</div>
+<div class="slide" id="a-real-life-example">
+<h1>A real-life example</h1>
+<p class="incremental">Let me show a real problem we had at <a class="reference" href="http://www.statpro.com">StatPro</a></p>
+<img alt="badpricehistory.png" class="incremental" src="badpricehistory.png" />
+</div>
+<div class="slide" id="the-history-plotter">
+<h1>The history plotter</h1>
+<p>It was easy to write a simple command line history plotter</p>
+<ul class="incremental simple">
+<li>show live example</li>
+<li>but we were not happy with it</li>
+<li>because of installation issues, etc</li>
+<li>so we wanted to go on the Web</li>
+</ul>
+</div>
+<div class="slide" id="going-on-the-web">
+<h1>Going on the Web</h1>
+<ul class="incremental simple">
+<li>tool for internal usage on our intranet</li>
+<li>convenient to integrate with other Web tools</li>
+<li>usable also for non-techical users</li>
+<li>avoid installing and mantaining on every machine</li>
+<li>possibly we may open it to our other offices in the world</li>
+<li>we like the browser interface</li>
+</ul>
+</div>
+<div class="slide" id="without-a-framework">
+<h1>Without a framework</h1>
+<ul class="incremental simple">
+<li>KISS</li>
+<li>no security concerns</li>
+<li>no scalability concerns</li>
+<li>no nice-looking concerns</li>
+<li>it must be <em>EASY</em> to change</li>
+<li>we want minimal learning curve</li>
+<li>we want no installation/configuration hassle</li>
+<li>we want no dependencies</li>
+<li>we want something even simpler than CGI, if possible!</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-is-the-answer">
+<h1>WSGI is the answer!</h1>
+<p>The web plotter</p>
+<pre class="literal-block">
+$ python webplotter.py
+</pre>
+<p><a class="reference" href="http://localhost:8000">Click here for the live demonstration</a></p>
+</div>
+<div class="slide" id="some-code-i">
+<h1>Some code (I)</h1>
+<pre class="literal-block">
+def app(env, resp):
+ form = getformdict(env)
+ if form.get('submitted'):
+ try:
+ fname = make_graph(form.get('code'), batch=True)
+ except Exception, e:
+ resp('500 ERR', [('Content-type', 'text/plain')])
+ return [traceback.format_exc()]
+ else:
+ resp('200 OK', [('Content-type', 'image/png')])
+ return file(fname)
+</pre>
+</div>
+<div class="slide" id="some-code-ii">
+<h1>Some code (II)</h1>
+<pre class="literal-block">
+else:
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [
+ 'Try values such as &lt;pre&gt;fri-gb;AVE&lt;/pre&gt;',
+ '&lt;pre&gt;fri-gb;TSCO&lt;/pre&gt; &lt;pre&gt;fri-us;DELL&lt;/pre&gt;',
+ '&lt;form&gt;', 'insert code ',
+ '&lt;input type=&quot;text&quot; name=&quot;code&quot;/&gt;',
+ '&lt;input type=&quot;submit&quot;, name=&quot;submitted&quot;,'
+ ' value=&quot;submit&quot; /&gt;',
+ '&lt;/form&gt;']
+</pre>
+</div>
+<div class="slide" id="some-code-iii">
+<h1>Some code (III)</h1>
+<pre class="literal-block">
+def getformdict(env):
+ qs = env.get('QUERY_STRING')
+ if qs:
+ return dict((k, v[0])
+ for k, v in cgi.parse_qsl(qs))
+ else:
+ return {}
+</pre>
+</div>
+<div class="slide" id="wsgi-vs-cgi">
+<h1>WSGI vs. CGI</h1>
+<ul class="incremental simple">
+<li>WSGI is simpler than CGI<ul>
+<li><span class="incremental">using wsgiref you don't require an external server</span></li>
+<li><span class="incremental">you can keep sessions in memory</span></li>
+</ul>
+</li>
+<li>WSGI scales better than CGI<ul>
+<li><span class="incremental">there is a large choice of wsgi servers (mod_wsgi, Twisted ...)</span></li>
+<li><span class="incremental">there is a large choice of third party middleware</span></li>
+<li><span class="incremental">it is relatively easy to turn a toy application into a serious one</span></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="slide" id="the-missing-link">
+<h1>The missing link</h1>
+<p>WSGI is a the missing link between the low-level mechanics of
+the Web and the high-level intricacies of the Web frameworks.</p>
+<img alt="Australopithecus.jpg" src="Australopithecus.jpg" />
+</div>
+<div class="slide" id="a-common-objection">
+<h1>A common objection</h1>
+<ul class="incremental simple">
+<li>Python has too many Web frameworks because it is too easy to build
+a Web framework in Python</li>
+<li>WSGI makes building a Web framework even easier, the number of framework
+will increase</li>
+<li>=&gt; WSGI fails its goal!</li>
+<li><strong>NOT TRUE</strong></li>
+<li><em>integration</em> is the key word</li>
+</ul>
+</div>
+<div class="slide" id="object-publishing-i">
+<h1>Object publishing (I)</h1>
+<pre class="literal-block">
+class Example(object):
+ def __init__(self, sitename):
+ self.sitename = sitename
+ def __call__(self):
+ yield '&lt;h1&gt;%s: index page&lt;/h1&gt;' % self.sitename
+ yield 'goto &lt;a href=&quot;./page1&quot;&gt;page1&lt;/a&gt;&lt;br/&gt;'
+ yield 'goto &lt;a href=&quot;./page2&quot;&gt;page2&lt;/a&gt;&lt;br/&gt;'
+ yield 'goto &lt;a href=&quot;subsite&quot;&gt;subsite&lt;/a&gt;&lt;br/&gt;'
+ def page1(self): yield 'page1'
+ def page2(self): yield 'page2'
+ page1.exposed = page2.exposed = True
+</pre>
+</div>
+<div class="slide" id="object-publishing-ii">
+<h1>Object publishing (II)</h1>
+<pre class="literal-block">
+class WSGIObjectPublisher(object):
+ def __init__(self, root):
+ self.root = root
+ def __call__(self, env, resp):
+ return self.getsubpage(self.root,env,resp)()
+ def getsubpage(self, root, env, resp):
+ script_name = util.shift_path_info(env)
+ if not script_name: # We've arrived!
+ resp('200 OK',[('content-type','text/html')])
+ return root
+ ...
+</pre>
+</div>
+<div class="slide" id="object-publishing-iii">
+<h1>Object publishing (III)</h1>
+<pre class="literal-block">
+try:
+ page = getattr(root, script_name)
+except AttributeError:
+ resp('404 Not Found',[('content-type','text/plain')])
+ return lambda:['missing page %r'%script_name]
+exposed = getattr(page, 'exposed', False)
+if not exposed:
+ resp('404 Not Found',[('content-type','text/plain')])
+ return lambda : [
+ '%r is not exposed!' % script_name]
+return self.getsubpage(page, env, resp)
+</pre>
+</div>
+<div class="slide" id="wsgi-vs-frameworks">
+<h1>WSGI vs. frameworks</h1>
+<p>Pro:</p>
+<ul class="simple">
+<li><span class="incremental">if you liked playing with Lego, you will be happy</span></li>
+<li><span class="incremental">you have much more control and you are not forced to marry a technology</span></li>
+<li><span class="incremental">you can learn a lot</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="id2">
+<h1>WSGI vs. frameworks</h1>
+<p>Contra:</p>
+<ul class="simple">
+<li><span class="incremental">you can build your own framework with WSGI, but you have to debug it</span></li>
+<li><span class="incremental">the existing WSGI frameworks are newer, there is less experience with them</span></li>
+<li><span class="incremental">WSGI is not particularly Twisted-friendly</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="and-now-middleware">
+<h1>And now middleware</h1>
+<p>No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware:</p>
+<pre class="literal-block">
+from paste.auth.basic import AuthBasicHandler
+
+def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+</pre>
+</div>
+<div class="slide" id="debugging-wsgi-apps">
+<h1>Debugging WSGI apps</h1>
+<pre class="literal-block">
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK',[('Content-type','text/html')])
+ return [str(a/b)]
+
+make_server('',9090,EvalException(app)
+ ).serve_forever()
+</pre>
+<p>Show <a class="reference" href="http://localhost:9090">evalexception</a></p>
+</div>
+<div class="slide" id="references">
+<h1>References</h1>
+<p>That's all, folks!</p>
+<ul class="simple">
+<li><a class="reference" href="http://wsgi.org/wsgi">http://wsgi.org/wsgi</a></li>
+<li><a class="reference" href="http://www.python.org/dev/peps/pep-0333">http://www.python.org/dev/peps/pep-0333</a></li>
+<li><a class="reference" href="http://pythonpaste.org/do-it-yourself-framework.html">http://pythonpaste.org/do-it-yourself-framework.html</a></li>
+<li><a class="reference" href="http://pylonshq.com/">http://pylonshq.com/</a></li>
+</ul>
+<p class="incremental"><strong>(P.S. at StatPro, we are hiring! ;)</strong></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/europython07/talk.txt b/pypers/europython07/talk.txt
new file mode 100644
index 0000000..ba352c5
--- /dev/null
+++ b/pypers/europython07/talk.txt
@@ -0,0 +1,374 @@
+An Introduction to Web Programming with WSGI
+===============================================
+
+:Talk given at: EuroPython 2007
+:By: Michele Simionato
+:organization: StatPro Italy
+:date: 2007-07-11
+
+.. include:: <s5defs.txt>
+.. footer:: EuroPython 2007 - 11 June 2007
+
+Introduction
+------------------------------------------------
+
+This is as a talk for beginners, only knowledge of
+CGI and a bit of HTTP protocol is expected
+`(non-beginners -> "Zope on a Paste")`
+
+.. class:: incremental
+
+ - Have you ever heard of WSGI?
+ - Have you ever used WSGI?
+ - Have you used non WSGI-based Web frameworks?
+
+
+Ok, now about me
+----------------------------
+
+.. class:: incremental
+
+- I started to do Web programming with Zope and Plone
+- (never liked them)
+- I flirted with Quixote, Twisted, CherryPy, mod_python
+- (which are ok but ...)
+- I got in love with WSGI at last EuroPython
+- (I am still in love with it ;)
+
+What I have done
+--------------------------------------
+
+.. class:: incremental
+
+- written various helpers to simplify the usage of WSGI;
+- debug with WSGI, deploy with Zope strategy
+- written various simple Web tools for internal usage at StatPro_
+- now we are going to start a major project with WSGI
+
+.. _StatPro: http://www.statpro.com
+
+WSGI
+----------------------------------
+
+.. image:: Scotch_Whisky_(aka).png
+
+
+*Short* history of WSGI
+-------------------------
+
+.. class:: incremental
+
+- WSGI = Web Server Gateway Interface (*Whisky* for friends)
+- the brainchild of Python guru Phillip J. Eby
+- also input from Ian Bicking (``paste``) and others
+- starting from Python 2.5, we have a WSGI web server in the standard
+ library (``wsgiref``)
+- there are plenty of simple and useful add-ons for WSGI applications
+ out there (``pylons ...``)
+- Guido_ likes it!
+
+.. _Guido: http://video.google.com/videoplay?docid=-8502904076440714866
+
+WSGI key concepts
+------------------------------------------------
+
+.. class:: incremental
+
+1. WSGI application:
+
+ (env, resp) -> chunks of text
+
+ env = environment dictionary of the server;
+ resp = function sending to the client the HTTP headers
+
+2. WSGI middleware:
+
+ WSGI app -> enhanced WSGI app
+
+Hello World
+-------------------------------
+
+::
+
+ from wsgiref import simple_server
+
+ def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+ server=simple_server.make_server('', 8000, app)
+ server.serve_forever()
+
+A real-life example
+--------------------------------------
+
+.. class:: incremental
+
+ Let me show a real problem we had at StatPro_
+
+ .. _StatPro: http://www.statpro.com
+
+ .. image:: badpricehistory.png
+
+The history plotter
+------------------------------------
+
+It was easy to write a simple command line history plotter
+
+.. class:: incremental
+
+- show live example
+- but we were not happy with it
+- because of installation issues, etc
+- so we wanted to go on the Web
+
+Going on the Web
+-----------------------------------
+
+.. class:: incremental
+
+- tool for internal usage on our intranet
+- convenient to integrate with other Web tools
+- usable also for non-techical users
+- avoid installing and mantaining on every machine
+- possibly we may open it to our other offices in the world
+- we like the browser interface
+
+Without a framework
+---------------------------------------------
+
+.. class:: incremental
+
+- KISS
+- no security concerns
+- no scalability concerns
+- no nice-looking concerns
+
+- it must be *EASY* to change
+- we want minimal learning curve
+- we want no installation/configuration hassle
+
+- we want no dependencies
+- we want something even simpler than CGI, if possible!
+
+WSGI is the answer!
+----------------------------------------------------
+
+The web plotter
+
+::
+
+ $ python webplotter.py
+
+`Click here for the live demonstration`_
+
+.. _`Click here for the live demonstration`: http://localhost:8000
+
+
+Some code (I)
+-------------------------------------------------
+
+::
+
+ def app(env, resp):
+ form = getformdict(env)
+ if form.get('submitted'):
+ try:
+ fname = make_graph(form.get('code'), batch=True)
+ except Exception, e:
+ resp('500 ERR', [('Content-type', 'text/plain')])
+ return [traceback.format_exc()]
+ else:
+ resp('200 OK', [('Content-type', 'image/png')])
+ return file(fname)
+
+
+Some code (II)
+-------------------------------------------------
+
+::
+
+ else:
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [
+ 'Try values such as <pre>fri-gb;AVE</pre>',
+ '<pre>fri-gb;TSCO</pre> <pre>fri-us;DELL</pre>',
+ '<form>', 'insert code ',
+ '<input type="text" name="code"/>',
+ '<input type="submit", name="submitted",'
+ ' value="submit" />',
+ '</form>']
+
+
+Some code (III)
+-------------------------------------------------
+
+::
+
+ def getformdict(env):
+ qs = env.get('QUERY_STRING')
+ if qs:
+ return dict((k, v[0])
+ for k, v in cgi.parse_qsl(qs))
+ else:
+ return {}
+
+
+WSGI vs. CGI
+--------------------------------------------
+
+.. class:: incremental
+
+- WSGI is simpler than CGI
+
+ + `using wsgiref you don't require an external server`
+ + `you can keep sessions in memory`
+
+- WSGI scales better than CGI
+
+ + `there is a large choice of wsgi servers (mod_wsgi, Twisted ...)`
+ + `there is a large choice of third party middleware`
+ + `it is relatively easy to turn a toy application into a serious one`
+
+
+The missing link
+-----------------------------------------
+
+WSGI is a the missing link between the low-level mechanics of
+the Web and the high-level intricacies of the Web frameworks.
+
+.. image:: Australopithecus.jpg
+
+A common objection
+-----------------------------------------
+
+.. class:: incremental
+
+- Python has too many Web frameworks because it is too easy to build
+ a Web framework in Python
+- WSGI makes building a Web framework even easier, the number of framework
+ will increase
+- => WSGI fails its goal!
+- **NOT TRUE**
+- *integration* is the key word
+
+Object publishing (I)
+------------------------------
+
+::
+
+ class Example(object):
+ def __init__(self, sitename):
+ self.sitename = sitename
+ def __call__(self):
+ yield '<h1>%s: index page</h1>' % self.sitename
+ yield 'goto <a href="./page1">page1</a><br/>'
+ yield 'goto <a href="./page2">page2</a><br/>'
+ yield 'goto <a href="subsite">subsite</a><br/>'
+ def page1(self): yield 'page1'
+ def page2(self): yield 'page2'
+ page1.exposed = page2.exposed = True
+
+Object publishing (II)
+------------------------------
+
+::
+
+ class WSGIObjectPublisher(object):
+ def __init__(self, root):
+ self.root = root
+ def __call__(self, env, resp):
+ return self.getsubpage(self.root,env,resp)()
+ def getsubpage(self, root, env, resp):
+ script_name = util.shift_path_info(env)
+ if not script_name: # We've arrived!
+ resp('200 OK',[('content-type','text/html')])
+ return root
+ ...
+
+Object publishing (III)
+------------------------------
+
+::
+
+ try:
+ page = getattr(root, script_name)
+ except AttributeError:
+ resp('404 Not Found',[('content-type','text/plain')])
+ return lambda:['missing page %r'%script_name]
+ exposed = getattr(page, 'exposed', False)
+ if not exposed:
+ resp('404 Not Found',[('content-type','text/plain')])
+ return lambda : [
+ '%r is not exposed!' % script_name]
+ return self.getsubpage(page, env, resp)
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Pro:
+
+- `if you liked playing with Lego, you will be happy`
+- `you have much more control and you are not forced to marry a technology`
+- `you can learn a lot`
+- `others ...`
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Contra:
+
+- `you can build your own framework with WSGI, but you have to debug it`
+- `the existing WSGI frameworks are newer, there is less experience with them`
+- `WSGI is not particularly Twisted-friendly`
+- `others ...`
+
+And now middleware
+-----------------------------
+
+No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware::
+
+ from paste.auth.basic import AuthBasicHandler
+
+ def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+ auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+
+Debugging WSGI
+-----------------------------------------------
+
+::
+
+ from wsgiref.simple_server import make_server
+ from paste.evalexception import EvalException
+
+ a, b = 1,0
+
+ def app(env, resp):
+ resp('200 OK',[('Content-type','text/html')])
+ return [str(a/b)]
+
+ make_server('',9090,EvalException(app)
+ ).serve_forever()
+
+Show evalexception_
+
+.. _evalexception: http://localhost:9090
+
+References
+-----------
+
+That's all, folks!
+
+- http://wsgi.org/wsgi
+- http://www.python.org/dev/peps/pep-0333
+- http://pythonpaste.org/do-it-yourself-framework.html
+- http://pylonshq.com/
+
+.. class:: incremental
+
+ **(P.S. at StatPro, we are hiring! ;)**
diff --git a/pypers/europython07/ui/default/blank.gif b/pypers/europython07/ui/default/blank.gif
new file mode 100644
index 0000000..75b945d
--- /dev/null
+++ b/pypers/europython07/ui/default/blank.gif
Binary files differ
diff --git a/pypers/europython07/ui/default/ex.html b/pypers/europython07/ui/default/ex.html
new file mode 100644
index 0000000..798e3cc
--- /dev/null
+++ b/pypers/europython07/ui/default/ex.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style type="text/css">
+body
+{
+background: url(statpro_logo.gif) no-repeat top
+}
+</style>
+</head>
+<body>
+Prova Background CSS
+</body>
+</html>
diff --git a/pypers/europython07/ui/default/slides.js b/pypers/europython07/ui/default/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/europython07/ui/default/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/europython07/ui/default/statpro_logo.gif b/pypers/europython07/ui/default/statpro_logo.gif
new file mode 100644
index 0000000..86923e4
--- /dev/null
+++ b/pypers/europython07/ui/default/statpro_logo.gif
Binary files differ
diff --git a/pypers/europython07/webplotter.py b/pypers/europython07/webplotter.py
new file mode 100644
index 0000000..e524f9f
--- /dev/null
+++ b/pypers/europython07/webplotter.py
@@ -0,0 +1,37 @@
+import os, cgi, traceback
+from wsgiref import simple_server
+from simpleplotter import make_graph
+
+def getformdict(env):
+ qs = env.get('QUERY_STRING')
+ if qs:
+ return dict((k, v[0]) for k, v in cgi.parse_qsl(qs))
+ else:
+ return {}
+
+def app(env, resp):
+ form = getformdict(env)
+ if form.get('submitted'):
+ try:
+ fname = make_graph(form.get('code'), batch=True)
+ except Exception, e:
+ resp('500 ERR', [('Content-type', 'text/plain')])
+ return [traceback.format_exc()]
+ else:
+ resp('200 OK', [('Content-type', 'image/png')])
+ return file(fname)
+ else:
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [
+ 'Try values such as <pre>fri-gb;AVE</pre>',
+ '<pre>fri-gb;TSCO</pre> <pre>fri-us;DELL</pre>',
+ '<form>', 'insert code ',
+ '<input type="text" name="code"/>',
+ '<input type="submit", name="submitted", value="submit" />',
+ '</form>']
+
+if __name__ == '__main__':
+ #from paste.auth.basic import AuthBasicHandler
+ #app = AuthBasicHandler(
+ # app, 'plotter realm', lambda e, u, p: u=='pippo' and p=='lippo')
+ simple_server.make_server('', 8000, app).serve_forever()
diff --git a/pypers/europython07/what-to-see.txt b/pypers/europython07/what-to-see.txt
new file mode 100644
index 0000000..b8054a9
--- /dev/null
+++ b/pypers/europython07/what-to-see.txt
@@ -0,0 +1,39 @@
+9 July
+------
+
+Morning
+
+50 Extending Python with EasyExtend
+50 Parsing Languages with mxTextTools
+300 Googled Python
+
+60 Case study of a Pylons project
+60 KSS, Ajax development with style
+60 Using FormEncode for web data validation
+
+Afternoon
+
+300 PyPy 1.0 and Beyond
+300 py.test
+300 unittest is Broken
+
+10 July
+-------
+
+Morning
+
+100 RPython: Need for speed aka C and C# considered harmful
+100 How our Python trading platform got 40 times faster by switching to RPython
+100 Stackless
+
+Afternoon
+
+50 Taking advantage of multiple CPUs for games - simply.
+50 Integrating Python and TeX: MathTran and beyond
+
+
+10 July
+-------
+
+100 What Zope did wrong (and what to do instead)
+50 Measuring Python Performance
diff --git a/pypers/final.py b/pypers/final.py
new file mode 100755
index 0000000..811040c
--- /dev/null
+++ b/pypers/final.py
@@ -0,0 +1,15 @@
+from oopp import *
+class Final(Singleton,type):
+ "Inherits the 'instance' attribute from Singleton (default None)"
+ def __new__(meta,name,bases,dic):
+ if meta.counter==0: # first call
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else:
+ raise NonDerivableError("I cannot derive from %s" % bases)
+
+class C: __metaclass__=Final
+try:
+ class D(C): pass
+except NonDerivableError,e:
+ print e
+
diff --git a/pypers/first.txt b/pypers/first.txt
new file mode 100755
index 0000000..487ecc7
--- /dev/null
+++ b/pypers/first.txt
@@ -0,0 +1,963 @@
+FIRST THINGS, FIRST
+==============================================================================
+
+This is an introductory chapter, with the main purpose of fixing the
+terminology used in the sequel. In particular, I give the definitions
+of objects, classes, attributes and methods. I discuss a few examples
+and I show some of the most elementary Python introspection features.
+
+What's an object?
+----------------------------------------------------------------------------
+
+ .. line-block::
+
+ *So Everything Is An object.
+ I'm sure the Smalltalkers are very happy :)*
+
+ -- Michael Hudson on comp.lang.python
+
+"What's an object" is the obvious question raised by anybody starting
+to learn Object Oriented Programming. The answer is simple: in Python,
+everything in an object!
+
+An operative definition is the following: an *object*
+is everything that can be labelled with an *object reference*.
+
+In practical terms, the object reference is implemented as
+the object memory address, that is an integer number which uniquely
+specify the object. There is a simple way to retrieve the object reference:
+to use the builtin ``id`` function. Informations on ``id`` can be retrieved
+via the ``help`` function [#]_:
+
+ >>> help(id)
+ Help on built-in function id:
+ id(...)
+ id(object) -> integer
+ Return the identity of an object. This is guaranteed to be unique among
+ simultaneously existing objects. (Hint: it's the object's memory address.)
+
+The reader is strongly encouraged to try the help function on everything
+(including help(help) ;-). This is the best way to learn how Python works,
+even *better* than reading the standard documentation, since the on-line
+help is often more update.
+
+Suppose for instance we wonder if the number ``1`` is an object:
+it is easy enough to ask Python for the answer:
+
+ >>> id(1)
+ 135383880
+
+Therefore the number 1 is a Python object and it is stored at the memory
+address 135383880, at least in my computer and during the current session.
+Notice that the object reference is a dynamic thing; nevertheless it
+is guaranteed to be unique and constant for a given object during its
+lifetime (two objects whose lifetimes are disjunct may have the same id()
+value, though).
+
+Here there are other examples of built-in objects:
+
+ >>> id(1L) # long
+ 1074483312
+ >>> id(1.0) #float
+ 135682468
+ >>> id(1j) # complex
+ 135623440
+ >>> id('1') #string
+ 1074398272
+ >>> id([1]) #list
+ 1074376588
+ >>> id((1,)) #tuple
+ 1074348844
+ >>> id({1:1}) # dict
+ 1074338100
+
+Even functions are objects:
+
+ >>> def f(x): return x #user-defined function
+ >>> id(f)
+ 1074292020
+ >>> g=lambda x: x #another way to define functions
+ >>> id(g)
+ 1074292468
+ >>> id(id) #id itself is a built-in function
+ 1074278668
+
+Modules are objects, too:
+
+ >>> import math
+ >>> id(math) #module of the standard library
+ 1074239068
+ >>> id(math.sqrt) #function of the standard library
+ 1074469420
+
+``help`` itself is an object:
+
+ >>> id(help)
+ 1074373452
+
+Finally, we may notice that the reserved keywords are not objects:
+
+ >>> id(print) #error
+ File "<string>", line 1
+ id(print) ^
+ SyntaxError: invalid syntax
+
+The operative definition is convenient since it gives a practical way
+to check if something is an object and, more importantly, if two
+objects are the same or not:
+
+ .. doctest
+
+ >>> s1='spam'
+ >>> s2='spam'
+ >>> s1==s2
+ True
+ >>> id(s1)==id(s2)
+ True
+
+A more elegant way of spelling ``id(obj1)==id(obj2)`` is to use the
+keyword ``is``:
+
+ >>> s1 is s2
+ True
+
+However, I should warn the reader that sometimes ``is`` can be surprising:
+
+ >>> id([]) == id([])
+ True
+ >>> [] is []
+ False
+
+This is happening because writing ``id([])`` dynamically creates an unique
+object (a list) which goes away when you're finished with it. So when an
+expression needs both at the same time (``[] is []``), two unique objects
+are created, but when an expression doesn't need both at the same time
+(``id([]) == id([])``), an object gets created with an ID, is destroyed,
+and then a second object is created with the same ID (since the last one
+just got reclaimed) and their IDs compare equal. In other words, "the
+ID is guaranteed to be unique *only* among simultaneously existing objects".
+
+Another surprise is the following:
+
+ >>> a=1
+ >>> b=1
+ >>> a is b
+ True
+ >>> a=556
+ >>> b=556
+ >>> a is b
+ False
+
+The reason is that integers between 0 and 99 are pre-instantiated by the
+interpreter, whereas larger integers are recreated each time.
+
+Notice the difference between '==' and 'is':
+
+ >>> 1L==1
+ True
+
+but
+
+ >>> 1L is 1
+ False
+
+since they are different objects:
+
+ >>> id(1L) # long 1
+ 135625536
+ >>> id(1) # int 1
+ 135286080
+
+
+The disadvantage of the operative definition is that it gives little
+understanding of what an object can be used for. To this aim, I must
+introduce the concept of *class*.
+
+.. [#] Actually ``help`` is not a function but a callable object. The
+ difference will be discussed in a following chapter.
+
+Objects and classes
+---------------------------------------------------------------------------
+
+It is convenient to think of an object as an element of a set.
+
+It you think a bit, this is the most general definition that actually
+grasps what we mean by object in the common language.
+For instance, consider this book, "Object Oriented Programming in Python":
+this book is an object, in the sense that it is a specific representative
+of the *class* of all possible books.
+According to this definition, objects are strictly related to classes, and
+actually we say that objects are *instances* of classes.
+
+Classes are nested: for
+instance this book belongs to the class of books about programming
+language, which is a subset of the class of all possible books;
+moreover we may further specify this book as a Python book; moreover
+we may specify this book as a Python 2.2+ book. There is no limit
+to the restrictions we may impose to our classes.
+On the other hand. it is convenient to have a "mother" class,
+such that any object belongs to it. All strongly Object Oriented
+Language have such a class [#]_; in Python it is called *object*.
+
+The relation between objects and classes in Python can be investigated
+trough the built-in function ``type`` [#]_ that gives the class of any
+Python object.
+
+Let me give some example:
+
+1. Integers numbers are instances of the class ``int`` or ``long``:
+
+ >>> type(1)
+ <type 'int'>
+ >>> type(1L)
+ <type 'long'>
+
+2. Floating point numbers are instances of the class ``float``:
+
+ >>> type(1.0)
+ <type 'float'>
+
+
+3. Complex numbers are instances of the class ``complex``:
+
+ >>> type(1.0+1.0j)
+ <type 'complex'>
+
+4. Strings are instances of the class ``str``:
+
+ >>> type('1')
+ <type 'str'>
+
+
+5. List, tuples and dictionaries are instances of ``list``, ``tuple`` and
+ ``dict`` respectively:
+
+ >>> type('1')
+ <type 'str'>
+ >>> type([1])
+ <type 'list'>
+ >>> type((1,))
+ <type 'tuple'>
+ >>> type({1:1})
+ <type 'dict'>
+
+6. User defined functions are instances of the ``function`` built-in type
+
+ >>> type(f)
+ <type 'function'>
+ >>> type(g)
+ <type 'function'>
+
+All the previous types are subclasses of object:
+
+ >>> for cl in int,long,float,str,list,tuple,dict: issubclass(cl,object)
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+
+However, Python is not a 100% pure Object
+Oriented Programming language and its object model has still some minor
+warts, due to historical accidents.
+
+Paraphrasing George Orwell, we may say that in Python 2.2-2.3,
+all objects are equal, but some objects are more equal than others.
+Actually, we may distinguish Python objects in new style objects,
+or rich man objects, and old style objects, or poor man objects.
+New style objects are instances of new style classes whereas old
+style objects are instances of old style classes.
+The difference is that new style classes are subclasses of object whereas
+old style classes are not.
+
+Old style classes are there for sake of compatibility with previous
+releases of Python, but starting from Python 2.2 practically all built-in
+classes are new style classes.
+
+Instance of old style classes are called old style objects. I will give
+few examples of old style objects in the future.
+
+In this tutorial with the term
+object *tout court* we will mean new style objects, unless the contrary
+is explicitely stated.
+
+
+.. [#] one may notice that C++ does not have such a class, but C++
+ is *not* a strongly object oriented language ;-)
+
+.. [#] Actually ``type`` is not a function, but a metaclass; nevertheless,
+ since this is an advanced concept, discussed in the fourth chapter;
+ for the time being it is better to think of ``type`` as a built-in
+ function analogous to ``id``.
+
+Objects have attributes
+----------------------------------------------------------------------------
+
+All objects have attributes describing their characteristics, that may
+be accessed via the dot notation
+
+ ::
+
+ objectname.objectattribute
+
+The dot notation is common to most Object Oriented programming languages,
+therefore the reader with a little of experience should find it not surprising
+at all (Python strongly believes in the Principle of Least Surprise). However,
+Python objects also have special attributes denoted by the double-double
+underscore notation
+
+ ::
+
+ objectname.__specialattribute__
+
+with the aim of helping the wonderful Python introspection features, that
+does not have correspondence in all OOP language.
+
+Consider for example the string literal "spam". We may discover its
+class by looking at its special attribute *__class__*:
+
+ >>> 'spam'.__class__
+ <type 'str'>
+
+
+Using the ``__class__`` attribute is not always equivalent to using the
+``type`` function, but it works for all built-in types. Consider for instance
+the number *1*: we may extract its class as follows:
+
+ >>> (1).__class__
+ <type 'int'>
+
+Notice that the parenthesis are needed to avoid confusion between the integer
+1 and the float (1.).
+
+The non-equivalence type/class is the key to distinguish new style objects from
+old style, since for old style objects ``type(obj)<>obj.__class__``.
+We may use this knowledge to make and utility function that discovers
+if an object is a "real" object (i.e. new style) or a poor man object:
+
+ ::
+
+ #<oopp.py>
+
+ def isnewstyle(obj):
+ try: #some objects may lack a __class__ attribute
+ obj.__class__
+ except AttributeError:
+ return False
+ else: #look if there is unification type/class
+ return type(obj) is obj.__class__
+ #</oopp.py>
+
+Let us check this with various examples:
+
+ >>> from oopp import isnewstyle
+ >>> isnewstyle(1)
+ True
+ >>> isnewstyle(lambda x:x)
+ True
+ >>> isnewstyle(id)
+ True
+ >>> isnewstyle(type)
+ True
+ >>> isnewstyle(isnewstyle)
+ True
+ >>> import math
+ >>> isnewstyle(math)
+ True
+ >>> isnewstyle(math.sqrt)
+ True
+ >>> isnewstyle('hello')
+ True
+
+It is not obvious to find something which is not a real object,
+between the built-in objects, however it is possible. For instance,
+the ``help`` "function" is an old style object:
+
+ >>> isnewstyle(help)
+ False
+
+since
+
+ >>> help.__class__
+ <class site._Helper at 0x8127c94>
+
+is different from
+
+ >>> type(help)
+ <type 'instance'>
+
+Regular expression objects are even poorer objects with no ``__class__``
+attribute:
+
+ >>> import re
+ >>> reobj=re.compile('somestring')
+ >>> isnewstyle(reobj)
+ False
+ >>> type(reobj)
+ <type '_sre.SRE_Pattern'>
+ >>> reobj.__class__ #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: __class__
+
+There other special attributes other than ``__class__``; a particularly useful
+one is ``__doc__``, that contains informations on the class it
+refers to. Consider for instance the ``str`` class: by looking at its
+``__doc__`` attribute we can get information on the usage of this class:
+
+ >>> str.__doc__
+ str(object) -> string
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+From that docstring we learn how to convert generic objects in strings;
+for instance we may convert numbers, lists, tuples and dictionaries:
+
+ >>> str(1)
+ '1'
+ >>> str([1])
+ '[1]'
+ >>> str((1,))
+ (1,)'
+ >>> str({1:1})
+ '{1: 1}'
+
+``str`` is implicitely called each time we use the ``print`` statement, since
+``print obj`` is actually syntactic sugar for ``print str(obj)``.
+
+Classes and modules have another interesting special attribute, the
+``__dict__`` attribute that gives the content of the class/module.
+For instance, the contents of the standard ``math`` module can be retrieved
+as follows:
+
+ >>> import math
+ >>> for key in math.__dict__: print key,
+ ...
+ fmod atan pow __file__ cosh ldexp hypot sinh __name__ tan ceil asin cos
+ e log fabs floor tanh sqrt __doc__ frexp atan2 modf exp acos pi log10 sin
+
+Alternatively, one can use the built-in function ``vars``:
+
+ >>> vars(math) is math.__dict__
+ True
+
+This identity is true for any object with a ``__dict__`` attribute.
+Two others interesting special attributes are ``__doc__``
+
+ >>> print math.__doc__
+ This module is always available. It provides access to the
+ mathematical functions defined by the C standard.
+
+and ``__file__``:
+
+ >>> math.__file__ #gives the file associated with the module
+ '/usr/lib/python2.2/lib-dynload/mathmodule.so'
+
+Objects have methods
+----------------------------------------------------------------------------
+
+In addition to attributes, objects also have *methods*, i.e.
+functions attached to their classes [#]_.
+Methods are also invoked with the dot notation, but
+they can be distinguished by attributes because they are typically
+called with parenthesis (this is a little simplistic, but it is enough for
+an introductory chapter). As a simple example, let me show the
+invocation of the ``split`` method for a string object:
+
+ >>> s='hello world!'
+ >>> s.split()
+ ['hello', 'world!']
+
+In this example ``s.split`` is called a *bount method*, since it is
+applied to the string object ``s``:
+
+ >>> s.split
+ <built-in method split of str object at 0x81572b8>
+
+An *unbound method*, instead, is applied to the class: in this case the
+unbound version of ``split`` is applied to the ``str`` class:
+
+ >>> str.split
+ <method 'split' of 'str' objects>
+
+A bound method is obtained from its corresponding unbound
+method by providing the object to the unbound method: for instance
+by providing ``s`` to ``str.split`` we obtain the same effect of `s.split()`:
+
+ >>> str.split(s)
+ ['hello', 'world!']
+
+This operation is called *binding* in the Python literature: when write
+``str.split(s)`` we bind the unbound method ``str.split`` to the object ``s``.
+It is interesting to recognize that the bound and unbound methods are
+*different* objects:
+
+ >>> id(str.split) # unbound method reference
+ 135414364
+ >>> id(s.split) # this is a different object!
+ 135611408
+
+The unbound method (and therefore the bound method) has a ``__doc__``
+attribute explaining how it works:
+
+ >>> print str.split.__doc__
+ S.split([sep [,maxsplit]]) -> list of strings
+ Return a list of the words in the string S, using sep as the
+ delimiter string. If maxsplit is given, at most maxsplit
+ splits are done. If sep is not specified or is None, any
+ whitespace string is a separator.
+
+
+.. [#] A precise definition will be given in chapter 5 that introduces the
+ concept of attribute descriptors. There are subtle
+ differences between functions and methods.
+
+Summing objects
+--------------------------------------------------------------------------
+
+In a pure object-oriented world, there are no functions and everything is
+done trough methods. Python is not a pure OOP language, however quite a
+lot is done trough methods. For instance, it is quite interesting to analyze
+what happens when an apparently trivial statement such as
+
+ >>> 1+1
+ 2
+
+is executed in an object-oriented world.
+
+The key to understand, is to notice that the number 1 is an object, specifically
+an instance of class ``int``: this means that that 1 inherits all the methods
+of the ``int`` class. In particular it inherits a special method called
+``__add__``: this means 1+1 is actually syntactic sugar for
+
+ >>> (1).__add__(1)
+ 2
+
+which in turns is syntactic sugar for
+
+ >>> int.__add__(1,1)
+ 2
+
+The same is true for subtraction, multiplication, division and other
+binary operations.
+
+ >>> 'hello'*2
+ 'hellohello'
+ >>> (2).__mul__('hello')
+ 'hellohello'
+ >>> str.__mul__('hello',2)
+ 'hellohello'
+
+However, notice that
+
+ >>> str.__mul__(2,'hello') #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: descriptor '__mul__' requires a 'str' object but received a 'int'
+
+The fact that operators are implemented as methods, is the key to
+*operator overloading*: in Python (as well as in other OOP languages)
+the user can redefine the operators. This is already done by default
+for some operators: for instance the operator ``+`` is overloaded
+and works both for integers, floats, complex numbers and for strings.
+
+Inspecting objects
+---------------------------------------------------------------------------
+
+In Python it is possible to retrieve most of the attributes and methods
+of an object by using the built-in function ``dir()``
+(try ``help(dir)`` for more information).
+
+Let me consider the simplest case of a generic object:
+
+ >>> obj=object()
+ >>> dir(obj)
+ ['__class__', '__delattr__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__']
+
+As we see, there are plenty of attributes available
+even to a do nothing object; many of them are special attributes
+providing introspection capabilities which are not
+common to all programming languages. We have already discussed the
+meaning of some of the more obvious special attributes.
+The meaning of some of the others is quite non-obvious, however.
+The docstring is invaluable in providing some clue.
+
+Notice that there are special *hidden* attributes that cannot be retrieved
+with ``dir()``. For instance the ``__name__`` attribute, returning the
+name of the object (defined for classes, modules and functions)
+and the ``__subclasses__`` method, defined for classes and returning the
+list of immediate subclasses of a class:
+
+ >>> str.__name__
+ 'str'
+ >>> str.__subclasses__.__doc__
+ '__subclasses__() -> list of immediate subclasses'
+ >>> str.__subclasses__() # no subclasses of 'str' are currently defined
+ []
+
+For instance by doing
+
+ >>> obj.__getattribute__.__doc__
+ "x.__getattribute__('name') <==> x.name"
+
+we discover that the expression ``x.name`` is syntactic sugar for
+
+ ``x.__getattribute__('name')``
+
+Another equivalent form which is more often used is
+
+ ``getattr(x,'name')``
+
+We may use this trick to make a function that retrieves all the
+attributes of an object except the special ones:
+
+ ::
+
+ #<oopp.py>
+
+ def special(name): return name.startswith('__') and name.endswith('__')
+
+ def attributes(obj,condition=lambda n,v: not special(n)):
+ """Returns a dictionary containing the accessible attributes of
+ an object. By default, returns the non-special attributes only."""
+ dic={}
+ for attr in dir(obj):
+ try: v=getattr(obj,attr)
+ except: continue #attr is not accessible
+ if condition(attr,v): dic[attr]=v
+ return dic
+
+ getall = lambda n,v: True
+
+ #</oopp.py>
+
+Notice that certain attributes may be unaccessible (we will see how
+to make attributes unaccessible in a following chapter)
+and in this case they are simply ignored.
+For instance you may retrieve the regular (i.e. non special)
+attributes of the built-in functions:
+
+ >>> from oopp import attributes
+ >>> attributes(f).keys()
+ ['func_closure', 'func_dict', 'func_defaults', 'func_name',
+ 'func_code', 'func_doc', 'func_globals']
+
+In the same vein of the ``getattr`` function, there is a built-in
+``setattr`` function (that actually calls the ``__setattr__`` built-in
+method), that allows the user to change the attributes and methods of
+and object. Informations on ``setattr`` can be retrieved from the help
+function:
+
+ ::
+
+ >>> help(setattr)
+ Help on built-in function setattr:
+ setattr(...)
+ setattr(object, name, value)
+ Set a named attribute on an object; setattr(x, 'y', v) is equivalent to
+ ``x.y = v''.
+
+``setattr`` can be used to add attributes to an object:
+
+ ::
+
+ #<oopp.py>
+
+ import sys
+
+ def customize(obj,errfile=None,**kw):
+ """Adds attributes to an object, if possible. If not, writes an error
+ message on 'errfile'. If errfile is None, skips the exception."""
+ for k in kw:
+ try:
+ setattr(obj,k,kw[k])
+ except: # setting error
+ if errfile:
+ print >> errfile,"Error: %s cannot be set" % k
+
+ #</oopp.py>
+
+The attributes of built-in objects cannot be set, however:
+
+ >>> from oopp import customize,sys
+ >>> customize(object(),errfile=sys.stdout,newattr='hello!') #error
+ AttributeError: newattr cannot be set
+
+On the other hand, the attributes of modules can be set:
+
+ >>> import time
+ >>> customize(time,newattr='hello!')
+ >>> time.newattr
+ 'hello!'
+
+Notice that this means we may enhances modules at run-time, but adding
+new routines, not only new data attributes.
+
+The ``attributes`` and ``customize`` functions work for any kind of objects;
+in particular, since classes are a special kind of objects, they work
+for classes, too. Here are the attributes of the ``str``, ``list`` and
+``dict`` built-in types:
+
+ >>> from oopp import attributes
+ >>> attributes(str).keys()
+ ['startswith', 'rjust', 'lstrip', 'swapcase', 'replace','encode',
+ 'endswith', 'splitlines', 'rfind', 'strip', 'isdigit', 'ljust',
+ 'capitalize', 'find', 'count', 'index', 'lower', 'translate','join',
+ 'center', 'isalnum','title', 'rindex', 'expandtabs', 'isspace',
+ 'decode', 'isalpha', 'split', 'rstrip', 'islower', 'isupper',
+ 'istitle', 'upper']
+ >>> attributes(list).keys()
+ ['append', 'count', 'extend', 'index', 'insert', 'pop',
+ 'remove', 'reverse', 'sort']
+ >>> attributes(dict).keys()
+ ['clear','copy','fromkeys', 'get', 'has_key', 'items','iteritems',
+ 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault',
+ 'update', 'values']
+
+Classes and modules have a special attribute ``__dict__`` giving the
+dictionary of their attributes. Since it is often a quite large dictionary,
+it is convenient to define an utility function printing this dictionary in a
+nice form:
+
+ ::
+
+ #<oopp.py>
+
+ def pretty(dic):
+ "Returns a nice string representation for the dictionary"
+ keys=dic.keys(); keys.sort() # sorts the keys
+ return '\n'.join(['%s = %s' % (k,dic[k]) for k in keys])
+
+ #</oopp.py>
+
+I encourage the use of this function in order to retrieve more
+information about the modules of the standard library:
+
+ >>> from oopp import pretty
+ >>> import time #look at the 'time' standard library module
+ >>> print pretty(vars(time))
+ __doc__ = This module provides various functions to manipulate time values.
+ There are two standard representations of time. One is the number
+ of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer
+ or a floating point number (to represent fractions of seconds).
+ The Epoch is system-defined; on Unix, it is generally January 1st, 1970.
+ The actual value can be retrieved by calling gmtime(0).
+ The other representation is a tuple of 9 integers giving local time.
+ The tuple items are:
+ year (four digits, e.g. 1998)
+ month (1-12)
+ day (1-31)
+ hours (0-23)
+ minutes (0-59)
+ seconds (0-59)
+ weekday (0-6, Monday is 0)
+ Julian day (day in the year, 1-366)
+ DST (Daylight Savings Time) flag (-1, 0 or 1)
+ If the DST flag is 0, the time is given in the regular time zone;
+ if it is 1, the time is given in the DST time zone;
+ if it is -1, mktime() should guess based on the date and time.
+ Variables:
+ timezone -- difference in seconds between UTC and local standard time
+ altzone -- difference in seconds between UTC and local DST time
+ daylight -- whether local time should reflect DST
+ tzname -- tuple of (standard time zone name, DST time zone name)
+ Functions:
+ time() -- return current time in seconds since the Epoch as a float
+ clock() -- return CPU time since process start as a float
+ sleep() -- delay for a number of seconds given as a float
+ gmtime() -- convert seconds since Epoch to UTC tuple
+ localtime() -- convert seconds since Epoch to local time tuple
+ asctime() -- convert time tuple to string
+ ctime() -- convert time in seconds to string
+ mktime() -- convert local time tuple to seconds since Epoch
+ strftime() -- convert time tuple to string according to format specification
+ strptime() -- parse string to time tuple according to format specification
+ __file__ = /usr/local/lib/python2.3/lib-dynload/time.so
+ __name__ = time
+ accept2dyear = 1
+ altzone = 14400
+ asctime = <built-in function asctime>
+ clock = <built-in function clock>
+ ctime = <built-in function ctime>
+ daylight = 1
+ gmtime = <built-in function gmtime>
+ localtime = <built-in function localtime>
+ mktime = <built-in function mktime>
+ newattr = hello!
+ sleep = <built-in function sleep>
+ strftime = <built-in function strftime>
+ strptime = <built-in function strptime>
+ struct_time = <type 'time.struct_time'>
+ time = <built-in function time>
+ timezone = 18000
+ tzname = ('EST', 'EDT')
+
+The list of the built-in Python types can be found in the ``types`` module:
+
+ >>> import types
+ >>> t_dict=dict([(k,v) for (k,v) in vars(types).iteritems()
+ ... if k.endswith('Type')])
+ >>> for t in t_dict: print t,
+ ...
+ DictType IntType TypeType FileType CodeType XRangeType EllipsisType
+ SliceType BooleanType ListType MethodType TupleType ModuleType FrameType
+ StringType LongType BuiltinMethodType BufferType FloatType ClassType
+ DictionaryType BuiltinFunctionType UnboundMethodType UnicodeType
+ LambdaType DictProxyType ComplexType GeneratorType ObjectType
+ FunctionType InstanceType NoneType TracebackType
+
+For a pedagogical account of the most elementary
+Python introspection features,
+Patrick O' Brien:
+http://www-106.ibm.com/developerworks/linux/library/l-pyint.html
+
+Built-in objects: iterators and generators
+---------------------------------------------------------------------------
+
+At the end of the last section , I have used the ``iteritems`` method
+of the dictionary, which returns an iterator:
+
+ >>> dict.iteritems.__doc__
+ 'D.iteritems() -> an iterator over the (key, value) items of D'
+
+Iterators (and generators) are new features of Python 2.2 and could not be
+familiar to all readers. However, since they are unrelated to OOP, they
+are outside the scope of this book and will not be discussed here in detail.
+Nevertheless, I will give a typical example of use of a generator, since
+this construct will be used in future chapters.
+
+At the syntactical level, a generator is a "function" with (at least one)
+``yield`` statement (notice that in Python 2.2 the ``yield`` statement is
+enabled trough the ``from __future__ import generators`` syntax):
+
+
+ ::
+
+ #<oopp.py>
+
+ import re
+
+ def generateblocks(regexp,text):
+ "Generator splitting text in blocks according to regexp"
+ start=0
+ for MO in regexp.finditer(text):
+ beg,end=MO.span()
+ yield text[start:beg] # actual text
+ yield text[beg:end] # separator
+ start=end
+ lastblock=text[start:]
+ if lastblock: yield lastblock; yield ''
+
+ #</oopp.py>
+
+In order to understand this example, the reader my want to refresh his/her
+understanding of regular expressions; since this is not a subject for
+this book, I simply remind the meaning of ``finditer``:
+
+ >>> import re
+ >>> help(re.finditer)
+ finditer(pattern, string)
+ Return an iterator over all non-overlapping matches in the
+ string. For each match, the iterator returns a match object.
+ Empty matches are included in the result.
+
+Generators can be thought of as resumable functions that stop at the
+``yield`` statement and resume from the point where they left.
+
+ >>> from oopp import generateblocks
+ >>> text='Python_Rules!'
+ >>> g=generateblocks(re.compile('_'),text)
+ >>> g
+ <generator object at 0x401b140c>
+ >>> dir(g)
+ ['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
+ '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__',
+ '__repr__', '__setattr__', '__str__', 'gi_frame', 'gi_running', 'next']
+
+Generator objects can be used as iterators in a ``for`` loop.
+In this example the generator takes a text and a regular expression
+describing a fixed delimiter; then it splits the text in blocks
+according to the delimiter. For instance, if the delimiter is
+'_', the text 'Python Rules!' is splitted as 'Python', '_' and 'Rules!':
+
+ >>> for n, block in enumerate(g): print n, block
+ ...
+ 0 Python
+ 1
+ 2 Rules!
+ 3
+
+This example also show the usage of the new Python 2.3 built-in ``enumerate``.
+
+Under the hood the ``for`` loop is calling the generator via its
+``next`` method, until the ``StopIteration`` exception is raised.
+For this reason a new call to the ``for`` loop will have no effect:
+
+ >>> for n, block in enumerate(g): print n, block
+ ...
+
+The point is that the generator has already yield its last element:
+
+ >>> g.next() # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ StopIteration
+
+``generateblocks`` always returns an even number of blocks; odd blocks
+are delimiters whereas even blocks are the intertwining text; there may be
+empty blocks, corresponding to the null string ''.
+
+It must be remarked the difference with the 'str.split' method
+
+ >>> 'Python_Rules!'.split('_')
+ ['Python', 'Rules!']
+
+and the regular expression split method:
+
+ >>> re.compile('_').split('Python_Rules!')
+ ['Python', 'Rules!']
+
+both returns lists with an odd number of elements and both miss the separator.
+The regular expression split method can catch the separator, if wanted,
+
+ >>> re.compile('(_)').split('Python_Rules!')
+ ['Python', '_', 'Rules!']
+
+but still is different from the generator, since it returns a list. The
+difference is relevant if we want to split a very large text, since
+the generator avoids to build a very large list and thus it is much more
+memory efficient (it is faster, too). Moreover, ``generateblocks``
+works differently in the case of multiple groups:
+
+ >>> delim=re.compile('(_)|(!)') #delimiter is space or exclamation mark
+ >>> for n, block in enumerate(generateblocks(delim,text)):
+ ... print n, block
+ 0 Python
+ 1 _
+ 2 Rules
+ 3 !
+
+whereas
+
+ >>> delim.split(text)
+ ['Python', '_', None, 'Rules', None, '!', '']
+
+gives various unwanted ``None`` (which could be skipped with
+``[x for x in delim.split(text) if x is not None]``); notice, that
+there are no differences (apart from the fact that ``delim.split(text)``
+has an odd number of elements) when one uses a single group regular expression:
+
+ >>> delim=re.compile('(_|!)')
+ >>> delim.split(text)
+ ['Python', '_', 'Rules', '!', '']
+
+The reader unfamiliar with iterators and generators is encouraged
+to look at the standard documentation and other
+references. For instance, there are Alex Martelli's notes on iterators at
+http://www.strakt.com/dev_talks.html
+and there is a good article on generators by David Mertz
+http://www-106.ibm.com/developerworks/linux/library/l-pycon.html
diff --git a/pypers/frozen.py b/pypers/frozen.py
new file mode 100755
index 0000000..ea2fcfd
--- /dev/null
+++ b/pypers/frozen.py
@@ -0,0 +1,11 @@
+from oopp import *
+class C(Frozen):
+ c=1
+ def __init__(self):
+ #self.x=5 # won't work anymore, __new__ will be okay
+ pass
+class D(C):
+ d=2
+
+C.c=2
+print D().d
diff --git a/pypers/functions.txt b/pypers/functions.txt
new file mode 100755
index 0000000..79d7cf5
--- /dev/null
+++ b/pypers/functions.txt
@@ -0,0 +1,1142 @@
+THE CONVENIENCE OF FUNCTIONS
+============================================================================
+
+Functions are the most basic Python objects. They are also the simplest
+objects where one can apply the metaprogramming techniques that are
+the subject of this book. The tricks used in this chapter and the utility
+functions defined here will be used over all the book. Therefore this
+is an *essential* chapter.
+
+Since it is intended to be a gentle introduction, the tone will be
+informal.
+
+Introduction
+-------------
+
+One could be surprised that a text on OOP begins with a chapter on the
+well known old-fashioned functions. In some sense, this is also
+against the spirit of an important trend in OOP, which tries to
+shift the focus from functions to data. In pure OOP languages,
+there are no more functions, only methods. [#]_
+
+However, there are good reasons for that:
+
+1. In Python, functions *are* objects. And particularly useful ones.
+2. Python functions are pretty powerful and all their secrets are probably
+ *not* well known to the average Python programmer.
+3. In the solutions of many problems, you don't need the full apparatus
+ of OOP: good old functions can be enough.
+
+Moreover, I am a believer in the multiparadigm approach to programming,
+in which you choose your tools according to your problem.
+With a bazooka you can kill a mosquito, yes, but this does not mean
+that you must use the bazooka *always*.
+In certain languages, you have no choice, and you must define
+a class (involving a lot of boiler plate code) even for the most trivial
+application. Python's philosophy is to keep simple things simple, but
+having the capability of doing even difficult things with a reasonable
+amount of effort. The message of this chapter will be: "use functions when
+you don't need classes". Functions are good because:
+
+1. They are easy to write (no boiler plate);
+2. They are easy to understand;
+3. They can be reused in your code;
+4. Functions are an essential building block in the construction of objects.
+
+Even if I think that OOP is an extremely effective strategy, with
+enormous advantages on design, maintanibility and reusability of code,
+nevertheless this book is *not* intended to be a panegyric of OOP. There
+are cases in which you don't need OOP. I think the critical parameter is
+the size of the program. These are the rules I follows usually (to be
+taken as indicative):
+
+1. If I have to write a short script of 20-30 lines, that copies two or
+ three files and prints some message, I use fast and dirty spaghetti-code;
+ there is no use for OOP.
+2. If your script grows to one-hundred lines or more, I structure
+ it write a few routines and a main program: but still I can live
+ without OOP.
+3. If the script goes beyond the two hundred lines, I start
+ collecting my routines in few classes.
+4. If the script goes beyond the five hundred lines, I split the program
+ in various files and modules and convert it to a package.
+5. I never write a function longer than 50 lines, since 50 lines is more
+ or less the size of a page in my editor, and I need to be able to
+ see the entire function in a page.
+
+Of course your taste could be different and you could prefer to write a
+monolitic program of five thousand lines; however the average size of
+the modules in the Python standard library is of 111 lines.
+I think this is a *strong* suggestion towards
+a modular style of programming, which
+is *very* well supported in Python.
+
+The point is that OOP is especially useful for *large* programs: if you
+only use Python for short system administration scripts you may well
+live without OOP. Unfortunaly, as everybody knows, short scripts have
+an evil tendency to become medium size scripts, and medium size scripts
+have the even more evil tendency to become large scripts and possible
+even full featured applications ! For this reason it is very probable
+that at a certain moment you will feel the need for OOP.
+
+I remember my first big program, a long time ago: I wrote a program
+to draw mathematical functions in AmigaBasic. It was good and nice
+until it had size of few hundred lines; but when it passed a thousand
+of lines, it became rapidly unmanageable and unmaintenable. There where
+three problems:
+
+1. I could not split the program in modules, as I wanted, due to the
+ limitations of AmigaBasic;
+
+2. I was missing OOP to keep the logic of the program all together, but
+ at the time I didn't know that;
+
+3. I was missing effective debugging techniques.
+
+4. I was missing effective refactoring tools.
+
+I am sure anybody who has ever written a large program has run in these
+limitations: and the biggest help of OOP is in overcoming these limitations.
+Obviously, miracles are impossible, and even object oriented programs can
+grow to a size where they become unmaintanable: the point is that the
+critical limit is much higher than the thousand lines of structured programs.
+I haven't yet reached the limit of unmanageability with Python. The fact
+that the standard library is 66492 lines long (as result from the total
+number of lines in ``/usr/local/lib/python2.2/``), but it is still manageable,
+give me an hope ;-)
+
+ .. [#] However, one could argue that having functions distinguished from
+ methods is the best thing to do, even in a strongly object-oriented
+ world. For instance, generic functions can be used to implement
+ multimethods. See for instance Lisp, Dylan and MultiJava. This latter
+ is forced to introduce the concept of function outside a class,
+ foreign to traditional Java, just to implement multimethods.
+
+A few useful functions
+------------------------------------------------------------------------------
+
+It is always a good idea to have a set of useful function collected in
+a user defined module. The first function we want to have in our module
+is the ``do_nothing`` function:
+
+ ::
+
+ #<oopp.py>
+
+ def do_nothing(*args,**kw): pass
+
+ #</oopp.py>
+
+This function accept a variable number of arguments and keywords (I
+defer the reader to the standard documentation if she is unfamiliar
+with these concept; this is *not* another Python tutorial ;-) and
+return ``None``. It is very useful for debugging purposes, when in a
+complex program you may want concentrate your attention to few crucial
+functions and set the non-relevant functions to ``do_nothing`` functions.
+
+A second function which is useful in developing programs is a timer
+function. Very ofter indeed, we may want to determine the bottleneck
+parts of a program, we are interested in profiling them and in seeing
+if we can improve the speed by improving the algorithm, or by using
+a Python "compiler" such as Psyco, or if really we need to write a C
+extension. In my experience, I never needed to write a C extension,
+since Python is fast enough. Nevertheless, to profile a program is
+always a good idea and Python provides a profiler module in the
+stardard library with this aim. Still, it is convenient to have
+a set of user defined functions to test the execution speed of
+few selected routines (whereas the standard profiler profiles everything).
+
+We see from the standard library documentation that
+the current time can be retrieved from the ``time`` module: [#]_
+
+ >>> import time
+ >>> time.asctime()
+ 'Wed Jan 15 12:46:03 2003'
+
+Since we are not interested in the date but only in the time, we need
+a function to extract it. This is easily implemented:
+
+ ::
+
+ #<oopp.py>
+
+ import time
+
+ def get_time():
+ "Return the time of the system in the format HH:MM:SS"
+ return time.asctime().split()[3]
+
+ #</oopp.py>
+
+ >>> from oopp import get_time
+ >>> get_time()
+ '13:03:49'
+
+Suppose, for instance, we want to know how much it takes to Python
+to write a Gigabyte of data. This can be a quite useful benchmark
+to have an idea of the I/O bottlenecks in our system. Since to take in memory
+a file of a Gigabyte can be quite problematic, let me compute the
+time spent in writing 1024 files of one Megabyte each. To this
+aim we need a ``writefile`` function
+
+ ::
+
+ #<oopp.py>
+
+ def writefile(fname,data):
+ f=file(fname,'w')
+ f.write(data)
+ f.close()
+
+ #</oopp.py>
+
+and timing function. The idea is to wrap the ``writefile`` function in
+a ``with_clock`` function as follows:
+
+ ::
+
+ #<oopp.py>
+
+ def with_clock(func,n=1):
+ def _(*args,**kw): # this is a closure
+ print "Process started on",get_time()
+ print ' .. please wait ..'
+ for i in range(n): func(*args,**kw)
+ print "Process ended on",get_time()
+ return _
+
+ #</oopp.py>
+
+The wrapper function ``with_clock`` has converted the function ``writefile``
+in a function ``with_clock(writefile)`` which has the same arguments
+of ``writefile``, but contains additional features: in this case
+timing capabilities. Technically speaking, the internal function ``_``
+is called a *closure*. Closures are very common in functional languages
+and can be used in Python too, with very little effort [#]_.
+
+I will use closures very often in the following, and I will use
+the convention of denoting with "_" the inner
+function in the closure, since there is no reason of giving to it a
+descriptive name (the name 'with_clock' in the outer function
+is descriptive enough). For the same, reason I do not use a
+docstring for "_". If Python would allow multistatement lambda
+functions, "_" would be a good candidate for an anonymous function.
+
+Here is an example of usage:
+
+ >>> from oopp import *
+ >>> data='*'*1024*1024 #one megabyte
+ >>> with_clock(writefile,n=1024)('datafile',data) #.
+ Process started on 21:20:01
+ .. please wait ..
+ Process ended on 21:20:57
+
+This example shows that Python has written one Gigabyte of data (splitted in
+1024 chunks of one Megabyte each) in less than a minute. However,the
+result depends very much on the filesystem. I always suggest people
+to profile their programs, since one *always* find surprises.
+For instance, I have checked the performance of my laptop,
+a dual machine Windows 98 SE/ Red Hat Linux 7.3.
+The results are collected in the following table:
+
+ ================= ===================== ========================
+ Laptop
+ Linux ext-3 FAT under Linux FAT under Windows 98
+ ================= ===================== ========================
+ 24-25 s 56-58 s 86-88 s
+ ================= ===================== ========================
+
+
+We see that Linux is *much* faster: more than three times faster than
+Windows, using the same machine! Notice that the FAT filesystem under
+Linux (where it is *not* native) is remarkably faster than the FAT
+under Windows 98, where it is native !! I think that now my readers
+can begin to understand why this book has been written under Linux
+and why I *never* use Windows for programming (actually I use it only
+to see the DVD's ;-).
+
+I leave as an exercise for the reader to check the results on this
+script on their machine. Since my laptop is quite old, you will probably
+have much better performances (for instance on my linux desktop I can
+write a Gigabyte in less than 12 seconds!). However, there are *always*
+surprises: my desktop is a dual Windows 2000 machine with three different
+filesystems, Linux ext-2, FAT and NTFS. Surprisingly enough, the NT
+filesystem is the more inefficient for writing, *ten times slower*
+than Linux!
+
+ ================= ===================== ========================
+ Desktop
+ Linux ext-2 FAT under Win2000 NTFS under Win2000
+ ================= ===================== ========================
+ 11-12 s 95-97 s 117-120 s
+ ================= ===================== ========================
+
+.. [#] Users of Python 2.3 can give a look to the new ``datetime`` module,
+ if they are looking for a sophisticated clock/calendar.
+
+.. [#] There are good references on functional programming in Python;
+ I suggest the Python Cookbook and the articles by David Mertz
+ www.IBM.dW.
+
+
+Functions are objects
+---------------------------------------------------------------------------
+
+As we said in the first chapter, objects have attributes accessible with the
+dot notation. This is not surprising at all. However, it could be
+surprising to realize that since Python functions are objects, they
+can have attributes, too. This could be surprising since this feature is quite
+uncommon: typically or i) the language is
+not object-oriented, and therefore functions are not objects, or ii)
+the language is strongly object-oriented and does not have functions, only
+methods. Python is a multiparadigm language (which I prefer to the
+term "hybrid" language), therefore it has functions that are objects,
+as in Lisp and other functional languages.
+Consider for instance the ``get_time`` function.
+That function has at least an useful attribute, its doctring:
+
+ >>> from oopp import get_time
+ >>> print get_time.func_doc
+ Return the time of the system in the format HH:MM:SS
+
+The docstring can also be obtained with the ``help`` function:
+
+ >>> help(get_time)
+ Help on function get_time in module oopp:
+ get_time()
+ Return the time of the system in the format HH:MM:SS
+
+Therefore ``help`` works on user-defined functions, too, not only on
+built-in functions. Notice that ``help`` also returns the argument list of
+the function. For instance, this is
+the help message on the ``round`` function that we will use in the
+following:
+
+ >>> help(round)
+ Help on built-in function round:
+ round(...)
+ round(number[, ndigits]) -> floating point number
+ Round a number to a given precision in decimal digits (default 0
+ digits).This always returns a floating point number. Precision may
+ be negative.
+
+I strongly recommend Python programmers to use docstrings, not
+only for clarity sake during the development, but especially because
+it is possible to automatically generate nice HTML documentation from
+the docstrings, by using the standard tool "pydoc".
+
+One can easily add attributes to a function. For instance:
+
+ >>> get_time.more_doc='get_time invokes the function time.asctime'
+ >>> print get_time.more_doc
+ get_time invokes the function time.asctime
+
+Attributes can be functions, too:
+
+ >>> def IamAfunction(): print "I am a function attached to a function"
+ >>> get_time.f=IamAfunction
+ >>> get_time.f()
+ I am a function attached to a function
+
+This is a quite impressive potentiality of Python functions, which has
+no direct equivalent in most other languages.
+
+One possible application is to fake C "static" variables. Suppose
+for instance we need a function remembering how may times it is
+called: we can simply use
+
+ ::
+
+ #<double.py>
+
+ def double(x):
+ try: #look if double.counter is defined
+ double.counter
+ except AttributeError:
+ double.counter=0 #first call
+ double.counter+=1
+ return 2*x
+
+ double(double(2))
+ print "double has been called %s times" % double.counter
+
+ #</double.py>
+
+with output ``double has been called 2 times``.
+A more elegant approach involves closures. A closure can enhance an
+ordinary function, providing to it the capability of remembering
+the results of its previous calls and avoiding the duplication of
+computations:
+
+::
+
+ #<oopp.py>
+
+ def withmemory(f):
+ """This closure invokes the callable object f only if need there is"""
+ argskw=[]; result=[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ i=argskw.index(akw)
+ except ValueError: # there is no previously stored result
+ res=f(*args,**kw) # returns the new result
+ argskw.append(akw) # update argskw
+ result.append(res) # update result
+ return res
+ else:
+ return result[i]
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+ def memoize(f):
+ """This closure remembers all f invocations"""
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+ #</oopp.py>
+
+Now, if we call the wrapped function ``f`` twice with the same arguments,
+Python can give the result without repeating the (possibly very long)
+computation.
+
+ >>> def f(x):
+ ... print 'called f'
+ ... return x*x
+ >>> wrapped_f=withmemory(f)
+ >>> wrapped_f(2) #first call with the argument 2; executes the computation
+ called f
+ 4
+ >>> wrapped_f(2) #does not repeat the computation
+ 4
+ >>> wrapped_f.result
+ [4]
+ >>> wrapped_f.argskw
+ [((2,), {})]
+
+Profiling functions
+---------------------------------------------------------------------------
+
+The ``with_clock`` function provided before was intended to be
+pedagogical; as such it is a quite poor solution to the
+problem of profiling a Python routine. A better solution involves
+using two others functions in the time library, ``time.time()``
+that gives that time in seconds elapsed from a given date, and
+``time.clock()`` that gives the time spent by the CPU in a given
+computation. Notice that ``time.clock()`` has not an infinite
+precision (the precision depends on the system) and one
+should expect relatively big errors if the function runs in
+a very short time. That's the reason why it is convenient
+to execute multiple times short functions and divide the total
+time by the number of repetitions. Moreover, one should subtract the
+overhead do to the looping. This can be computed with the following
+routine:
+
+ ::
+
+ #<oopp.py>
+
+ def loop_overhead(N):
+ "Computes the time spent in empty loop of N iterations"
+ t0=time.clock()
+ for i in xrange(N): pass
+ return time.clock()-t0
+
+ #</oopp.py>
+
+For instance, on my laptop an empty loop of one million of iterations
+is performed in 1.3 seconds. Typically the loop overhead is negligible,
+whereas the real problem is the function overhead.
+
+Using the attribute trick discussed above, we may
+define a ``with_timer`` function that enhances quite a bit
+``with_clock``:
+
+ ::
+
+ #<oopp.py>
+
+ def with_timer(func, modulename='__main__', n=1, logfile=sys.stdout):
+ """Wraps the function func and executes it n times (default n=1).
+ The average time spent in one iteration, express in milliseconds,
+ is stored in the attributes func.time and func.CPUtime, and saved
+ in a log file which defaults to the standard output.
+ """
+ def _(*args,**kw): # anonymous function
+ time1=time.time()
+ CPUtime1=time.clock()
+ print 'Executing %s.%s ...' % (modulename,func.__name__),
+ for i in xrange(n): res=func(*args,**kw) # executes func n times
+ time2=time.time()
+ CPUtime2=time.clock()
+ func.time=1000*(time2-time1)/n
+ func.CPUtime=1000*(CPUtime2-CPUtime1-loop_overhead(n))/n
+ if func.CPUtime<10: r=3 #better rounding
+ else: r=1 #default rounding
+ print >> logfile, 'Real time: %s ms' % round(func.time,r),
+ print >> logfile, ' CPU time: %s ms' % round(func.CPUtime,r)
+ return res
+ return _
+
+ #</oopp.py>
+
+Here it is an example of application:
+
+ >>> from oopp import with_timer,writefile
+ >>> data='*'*1024*1024 #one megabyte
+ >>> with_timer(writefile,n=1024)('datafile',data) #.
+ Executing writefile ... Real time: 60.0 ms CPU time: 42.2 ms
+
+The CPU time can be quite different from the real time,
+as you can see in the following example:
+
+ >>> import time
+ >>> def sleep(): time.sleep(1)
+ ...
+ >>> with_timer(sleep)() #.
+ Executing sleep ... Real time: 999.7 ms CPU time: 0.0 ms
+
+We see that Python has run for 999.7 ms (i.e. 1 second, up to
+approximation errors in the system clock) during which the CPU has
+worked for 0.0 ms (i.e. the CPU took a rest ;-).
+The CPU time is the relevant time to use with the purpose of
+benchmarking Python speed.
+
+I should notice that the approach pursued in ``with_timer`` is still
+quite simple. A better approach would be to
+plot the time versus the number of iteration, do a linear interpolation
+and extract the typical time for iteration from that. This allows
+to check visually that the machine is not doing something strange
+during the execution time and it is what
+I do in my personal benchmark routine; doing something similar is
+left as an exercise for the reader ;-).
+
+Another approach is to use the ``timeit.py`` module (new in Python 2.3,
+but works also with Python 2.2):
+
+ ::
+
+ #<oopp.py>
+
+ import timeit,__main__,warnings
+
+ warnings.filterwarnings('ignore',
+ 'import \* only allowed at module level',SyntaxWarning)
+
+ def timeit_(stmt,setup='from __main__ import *',n=1000):
+ t=timeit.Timer(stmt,setup)
+ try: print t.repeat(number=n) # class timeit 3 times
+ except: t.print_exc()
+
+ #</oopp.py>
+
+It is often stated that Python is slow and quite ineffective
+in application involving hard computations. This is generally speaking
+true, but how bad is the situation ? To test the (in)efficiency of
+Python on number crunching, let me give a function to compute the
+Mandelbrot set, which I have found in the Python Frequently Asked
+Question (FAQ 4.15. *Is it possible to write obfuscated one-liners
+in Python?*).
+This function is due to Ulf Bartelt and you should ask him to know how
+does it work ;-)
+
+ ::
+
+ #<oopp.py>
+
+ def mandelbrot(row,col):
+ "Computes the Mandelbrot set in one line"
+ return (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(
+ lambda x,y:x+y,map(lambda y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=
+ lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, Sx=Sx,Sy=Sy:reduce(
+ lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, i=i,
+ Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)
+ or (x*x+y*y>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):
+ f(xc,yc,x,y,k,f):chr(64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),
+ range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(
+ -2.1, 0.7, -1.2, 1.2, 30, col, row)
+ # \___ ___/ \___ ___/ | | |_ lines on screen
+ # V V | |______ columns on screen
+ # | | |__________ maximum of "iterations"
+ # | |_________________ range on y axis
+ # |____________________________ range on x axis
+
+ #</oopp.py>
+
+Here there is the benchmark on my laptop:
+
+ >>> from oopp import mandelbrot,with_timer
+ >>> row,col=24,75
+ >>> output=with_timer(mandelbrot,n=1)(row,col)
+ Executing __main__.mandelbrot ... Real time: 427.9 ms CPU time: 410.0 ms
+ >>> for r in range(row): print output[r*col:(r+1)*col]
+ ...
+ BBBBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC
+ BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+ BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+ BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+ BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+ BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+ BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+ BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+ BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+ BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+ BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+ BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+ BBB_____________________________________________________________OKIHGFFEEEE
+ BBBDEEEEEEFGGGGHHHJPNQP^___________________________________________IGFFEEEE
+ BBBCDEEEEEEEEFFFFFFHHHML___________PQ_______________________________TGFEEEE
+ BBBBDDDDEEEEEEEEFFFFGHIKPVPMNU_QMJJKKZ_____________________________PIGFEEED
+ BBBBCDDDDDDDDDEEEEEFFGJKHHHHHHHHHHHHIKN[__________________________MJKGFEEDD
+ BBBBBCDDDDDDDDDDDDDDDEEEFFFFFFFFFGGGGHJNM________________________XLHGFFEEDD
+ BBBBBBCDDDDDDDDDDDDDDDDDDEEEEEFFFFFFGGGHMQ__T________________QLOUP[OGFEDDDD
+ BBBBBBBCCDDDDDDDDDDDDDDDDDDDDEEEEEEEFFFGHILIIIJJKMS_____PLJJIHGGGHJFEEDDDDD
+ BBBBBBBBCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGGGHIK_______LHGFFFFFEEEEDDDDDD
+ BBBBBBBBBCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFGHJJR^QLIHGFFEEEEEEDDDDDDCC
+ BBBBBBBBBBCCCCCCCDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFGIKNJLLGEEEEEEDDDDDDCCCCC
+ BBBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDEEEEEEFGYLFFFEEEEEDDDDDCCCCCCCCC
+
+I am willing to concede that this code is not typical Python code and
+actually it could be an example of *bad* code, but I wanted a nice ASCII
+picture on my book ... :) Also, this prove that Python is not necessarily
+readable and easy to understand ;-)
+I leave for the courageous reader to convert the previous algorithm to C and
+measure the difference in speed ;-)
+
+
+About Python speed
+---------------------------------------------------
+
+The best way to improved the speed is to improve the algorithm; in
+this sense Python is an ideal language since it allows you to test
+many algorithms in an incredibly short time: in other words, the time you
+would spend fighting with the compiler in other languages, in Python
+can be used to improve the algorithm.
+However in some cases, there is little to do: for instance, in many
+problems one has to run lots of loops, and Python loops are horribly
+inefficients as compared to C loops. In this case the simplest possibility
+is to use Psyco. Psyco is a specialing Python compiler written by Armin
+Rigo. It works for 386 based processors and allows Python to run loops at
+C speed. Installing Psyco requires $0.00 and ten minutes of your time:
+nine minutes to find the program, download it, and install it; one
+minute to understand how to use it.
+
+The following script explains both the usage and the advantages of Psyco:
+
+ ::
+
+ #<psyco1.py>
+
+ import oopp,sys
+
+ def measure_loop_overhead(n):
+ without=oopp.loop_overhead(n)
+ print "Without Psyco:",without
+
+ psyco.bind(oopp.loop_overhead) #compile the empty_loop
+
+ with=oopp.loop_overhead(n)
+ print "With Psyco:",with
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+ try:
+ import psyco
+ measure_loop_overhead(1000000)
+ except ImportError:
+ print "Psyco is not installed, sorry."
+
+ #</psyco1.py>
+
+The output is impressive:
+
+ ::
+
+ Without Psyco: 1.3
+ With Psyco: 0.02
+ Speedup = 65.0x
+
+
+Notice that repeating the test, you will obtain different speedups.
+On my laptop, the speedup for an empty loop of 10,000,000 of
+iteration is of the order of 70x, which is the same speed of a C loop,
+actually (I checked it). On my desktop, I have even found a speedup of
+94x !
+
+However, I must say that Psyco has some limitations. The problem is
+the function call overhead. Psyco enhances the overhead and in some
+programs it can even *worsen* the performance (this is way you should
+*never* use the ``psyco.jit()`` function that wraps all the functions of
+your program: you should only wrap the bottleneck loops). Generally
+speaking, you should expect a much more modest improvement, a
+factor of 2 or 3 is what I obtain usually in my programs.
+
+Look at this second example, which essentially measure the function
+call overhead by invoking the ``do_nothing`` function:
+
+ ::
+
+ #<psyco2.py>
+
+ import oopp
+
+ def measure2(n):
+
+ def do_nothing_loop(n):
+ for i in xrange(n): oopp.do_nothing()
+
+ print "Without Psyco:"
+ oopp.with_timer(do_nothing_loop,n=5)(n) #50,000 times
+ print
+
+ without=do_nothing_loop.CPUtime
+
+ psyco.bind(do_nothing_loop)
+
+ print "With Psyco:"
+ oopp.with_timer(do_nothing_loop,n=5)(n) #50,000 times
+
+ with=do_nothing_loop.CPUtime
+
+ print 'Speedup = %sx' % round(without/with,1)
+
+ try:
+ import psyco
+ measure2(10000)
+ except ImportError:
+ print "Psyco is not installed, sorry."
+
+ #</psyco2.py>
+
+The output is less incredible:
+
+ ::
+
+ Without Psyco:
+ Executing do_nothing_loop ... Real time: 138.2 ms CPU time: 130.0 ms
+ With Psyco:
+ Executing do_nothing_loop ... Real time: 70.0 ms CPU time: 68.0 ms
+ Speedup = 1.9x
+
+
+However, this is still impressive, if you think that you can double
+the speed of your program by adding *a line* of code! Moreover this
+example is not fair since Psyco cannot improve very much the performance
+for loops invoking functions with a variable number of arguments. On the
+other hand, it can do quite a lot for loops invoking functions with
+a fixed number of arguments. I have checked that you can easily reach
+speedups of 20x (!). The only disadvantage is that a program invoking
+Psyco takes much more memory, than a normal Python program, but this
+is not a problem for most applications in nowadays computers.
+Therefore, often Psyco
+can save you the effort of going trough a C extension. In some cases,
+however, there is no hope: I leave as an exercise for the reader
+to check (at least the version 0.4.1 I am using now) is unable to
+improve the performance on the Mandelbrot set example. This proves
+that in the case bad code, there is no point in using a compiler:
+you have to improve the algorithm first !
+
+ #<erf.py>
+
+ import math
+ import psyco
+ psyco.full()
+
+ def erfc(x):
+ exp = math.exp
+
+ p = 0.3275911
+ a1 = 0.254829592
+ a2 = -0.284496736
+ a3 = 1.421413741
+ a4 = -1.453152027
+ a5 = 1.061405429
+
+ t = 1.0 / (1.0 + p*x)
+ erfcx = ( (a1 + (a2 + (a3 +
+ (a4 + a5*t)*t)*t)*t)*t ) * exp(-x*x)
+ return erfcx
+
+ def main():
+ erg = 0.0
+
+ for i in xrange(1000000):
+ erg += erfc(0.456)
+
+ print "%f" % erg
+
+ main()
+
+ #</erf.py>
+
+By the way, if you really want to go trough a C extension with a minimal
+departure from Python, you can use Pyrex by Greg Ewing. A Pyrex program
+is essentially a Python program with variable declarations that is
+automatically converted to C code. Alternatively, you can inline
+C functions is Python with ``weave`` of ...
+Finally, if you want to access C/C++ libraries, there tools
+like Swig, Booster and others.
+
+Tracing functions
+---------------------------------------------------------------------------
+
+Typically, a script contains many functions that call themselves each
+other when some conditions are satisfied. Also, typically during
+debugging things do not work the way we would like and it is not
+clear which functions are called, in which order they are called,
+and which parameters are passed. The best way to know all these
+informations, is to trace the functions in our script, and to write
+all the relevant informations in a log file. In order to keep the
+distinction between the traced functions and the original one, it
+is convenient to collect all the wrapped functions in a separate dictionary.
+The tracing of a single function can be done with a closure
+like this:
+
+::
+
+ #<oopp.py>
+
+ def with_tracer(function,namespace='__main__',output=sys.stdout, indent=[0]):
+ """Closure returning traced functions. It is typically invoked
+ trough an auxiliary function fixing the parameters of with_tracer."""
+ def _(*args,**kw):
+ name=function.__name__
+ i=' '*indent[0]; indent[0]+=4 # increases indentation
+ output.write("%s[%s] Calling '%s' with arguments\n" %
+ (i,namespace,name))
+ output.write("%s %s ...\n" % (i,str(args)+str(kw)))
+ res=function(*args,**kw)
+ output.write("%s[%s.%s] called with result: %s\n"
+ % (i,namespace,name,str(res)))
+ indent[0]-=4 # restores indentation
+ return res
+ return _ # the traced function
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> from oopp import with_tracer
+ >>> def fact(n): # factorial function
+ ... if n==1: return 1
+ ... else: return n*fact(n-1)
+ >>> fact=with_tracer(fact)
+ >>> fact(3)
+ [__main__] Calling 'fact' with arguments
+ (3,){} ...
+ [__main__] Calling 'fact' with arguments
+ (2,){} ...
+ [__main__] Calling 'fact' with arguments
+ (1,){} ...
+ [__main__.fact] called with result: 1
+ [__main__.fact] called with result: 2
+ [__main__.fact] called with result: 6
+ 6
+
+The logic behind ``with_tracer`` should be clear; the only trick is the
+usage of a default list as a way to store a global indentation parameter.
+Since ``indent`` is mutable, the value of ``indent[0]`` changes at any
+recursive call of the traced function, resulting in a nested display.
+
+Typically, one wants to trace all the functions in a given module;
+this can be done trough the following function:
+
+ ::
+
+ #<oopp.py>
+
+ from types import *
+
+ isfunction=lambda f: isinstance(f,(FunctionType,BuiltinFunctionType))
+
+ def wrapfunctions(obj,wrapper,err=None,**options):
+ "Traces the callable objects in an object with a dictionary"
+ namespace=options.get('namespace',getattr(obj,'__name__',''))
+ output=options.get('output',sys.stdout)
+ dic=dict([(k,wrapper(v,namespace,output))
+ for k,v in attributes(obj).items() if isfunction(v)])
+ customize(obj,err,**dic)
+
+ #</oopp.py>
+
+Notice that 'wrapfunctions' accepts as first argument an object with
+a ``__dict__`` attribute (such as a module or a class) or with some
+explicit attributes (such as a simple object) and modifies it. One can
+trace a module as in this example:
+
+ ::
+
+ #<tracemodule.py>
+
+ import oopp,random
+
+ oopp.wrapfunctions(random,oopp.with_tracer)
+
+ random.random()
+
+ #</tracemodule.py>
+
+with output
+
+ ::
+
+ [random] Calling 'random' with arguments
+ (){} ...
+ -> 'random.random' called with result: 0.175450439202
+
+The beauty of the present approach is its generality: 'wrap' can be
+used to add any kind of capabilities to a pre-existing module.
+For instance, we could time the functions in a module, with the
+purpose of looking at the bottlenecks. To this aim, it is enough
+to use a 'timer' nested closure:
+
+An example of calling is ``wrapfunction(obj,timer,iterations=1)``.
+
+We may also compose our closures; for instance one could define a
+``with_timer_and_tracer`` closure:
+
+ >>> with_timer_and_tracer=lambda f: with_timer(with_tracer(f))
+
+It should be noticed that Python comes with a standard profiler
+(in my system it is located in ``/usr/local/lib/python2.2/profile.py``)
+that allows to profile a script or a module (try
+python /usr/local/lib/python2.2/profile.py oopp.py)
+
+or
+
+ >>> import profile; help(profile)
+
+and see the on-line documentation.
+
+Tracing objects
+----------------------------------------------------------------------
+
+In this section, I will give a more sophisticated example, in which
+one can easily understand why the Python ability of changing methods and
+attributes during run-time, is so useful.
+As a preparation to the real example, let me
+first introduce an utility routine that allows the user
+to add tracing capabilities to a given object.
+Needless to say, this feature can be invaluable during debugging, or in trying
+to understand the behaviour of a program written by others.
+
+This routine is a little complex and needs some explanation.
+
+1. The routine looks in the attributes of the object and try to access them.
+
+2. If the access is possible, the routines looks for methods (methods
+ are recognized trough the ``inspect.isroutine`` function in the
+ standard library) and ignores regular attributes;
+
+3. The routine try to override the original methods with improved ones,
+ that possess tracing capabilities;
+
+4. the traced method is obtained with the wrapping trick discussed before.
+
+I give now the real life example that I have anticipated before.
+Improvements and elaborations of this example can be useful to the
+professional programmer, too. Suppose you have an XML text you want
+to parse. Python provides excellent support for this kind of operation
+and various standard modules. One of the most common is the ``expat``
+module (see the standard library documentation for more).
+
+If you are just starting using the module, it is certainly useful
+to have a way of tracing its behaviour; this is especially true if
+you you find some unexpected error during the parsing of a document
+(and this may happens even if you are an experience programmer ;-).
+
+The tracing routine just defined can be used to trace the parser, as
+it is exemplified in the following short script:
+
+ ::
+
+ #<expat.py>
+
+ import oopp, xml.parsers.expat, sys
+
+ # text to be parsed
+ text_xml="""\
+ <?xml version="1.0"?>
+ <parent id="dad">
+ <child name="kid">Text goes here</child>
+ </parent>"""
+
+ # a few do nothing functions
+ def start(*args): pass
+ def end(*args): pass
+ def handler(*args): pass
+
+ # a parser object
+ p = xml.parsers.expat.ParserCreate()
+
+ p.StartElementHandler = start
+ p.EndElementHandler = end
+ p.CharacterDataHandler = handler
+
+ #adds tracing capabilities to p
+ oopp.wrapfunctions(p,oopp.with_tracer, err=sys.stdout)
+
+ p.Parse(text_xml)
+
+ #</expat.py>
+
+The output is:
+
+ ::
+
+ Error: SetBase cannot be set
+ Error: Parse cannot be set
+ Error: ParseFile cannot be set
+ Error: GetBase cannot be set
+ Error: SetParamEntityParsing cannot be set
+ Error: ExternalEntityParserCreate cannot be set
+ Error: GetInputContext cannot be set
+ [] Calling 'start' with arguments
+ (u'parent', {u'id': u'dad'}){} ...
+ [.start] called with result: None
+ [] Calling 'handler' with arguments
+ (u'\n',){} ...
+ [.handler] called with result: None
+ [] Calling 'start' with arguments
+ (u'child', {u'name': u'kid'}){} ...
+ [.start] called with result: None
+ [] Calling 'handler' with arguments
+ (u'Text goes here',){} ...
+ [.handler] called with result: None
+ [] Calling 'end' with arguments
+ (u'child',){} ...
+ [.end] called with result: None
+ [] Calling 'handler' with arguments
+ (u'\n',){} ...
+ [.handler] called with result: None
+ [] Calling 'end' with arguments
+ (u'parent',){} ...
+ [.end] called with result: None
+
+
+This is a case where certain methods cannot be managed with
+``getattr/setattr``, because they are internally coded in C: this
+explain the error messages at the beginning. I leave as an exercise
+for the reader to understand the rest ;-)
+
+Inspecting functions
+----------------------------------------------------------------------
+
+Python wonderful introspection features are really impressive when applied
+to functions. It is possible to extract a big deal of informations
+from a Python function, by looking at its associated *code object*.
+For instance, let me consider my, ``do_nothing`` function: its associated
+code object can be extracted from the ``func_code`` attribute:
+
+ >>> from oopp import *
+ >>> co=do_nothing.func_code # extracts the code object
+ >>> co
+ <code object do_nothing at 0x402c5d20, file "oopp.py", line 48>
+ >>> type(co)
+ <type 'code'>
+
+The code object is far being trivial: the docstring says it all:
+
+ >>> print type(co).__doc__
+ code(argcount, nlocals, stacksize, flags, codestring, constants, names,
+ varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])
+ Create a code object. Not for the faint of heart.
+
+In the case of my ``do_nothing`` function, the code object
+possesses the following attributes:
+
+ >>> print pretty(attributes(co))
+ co_argcount = 0
+ co_cellvars = ()
+ co_code = dS
+ co_consts = (None,)
+ co_filename = oopp.py
+ co_firstlineno = 48
+ co_flags = 15
+ co_freevars = ()
+ co_lnotab =
+ co_name = do_nothing
+ co_names = ()
+ co_nlocals = 2
+ co_stacksize = 1
+ co_varnames = ('args', 'kw')
+
+Some of these arguments are pretty technical and implementation dependent;
+however, some of these are pretty clear and useful:
+
+ - co_argcount is the total number of arguments
+ - co_filename is the name of the file where the function is defined
+ - co_firstlineno is the line number where the function is defined
+ - co_name is the name of the function
+ - co_varnames are the names
+
+The programmer that it is not a "faint of heart" can study
+the built-in documentation on code objects; s/he should try
+
+ ::
+
+ for k,v in attributes(co).iteritems(): print k,':',v.__doc__,'\n'
+
+ ::
+
+ add=[lambda x,i=i: x+i for i in range(10)]
+
+ >>> def f(y):
+ ... return lambda x: x+y
+ ...
+ >>> f(1).func_closure #closure cell object
+ (<cell at 0x402b56bc: int object at 0x811d6c8>,)
+
+func.defaults, closure, etc.
+
+#how to extract (non-default) arguments as help does.
+
+print (lambda:None).func_code.co_filename
+
+One cannot change the name of a function:
+
+ >>> def f(): pass
+ ...
+ >>> f.__name__='ciao' # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: readonly attribute
+
+However, one can create a copy with a different name:
+
+ ::
+
+ #<oopp.py>
+
+ def copyfunc(f,newname=None): # works under Python 2.3
+ if newname is None: newname=f.func_name # same name
+ return FunctionType(f.func_code, globals(), newname,
+ f.func_defaults, f.func_closure)
+
+ #</oopp.py>
+
+ >>> copyfunc(f,newname='f2')
+ <function f2 at 0x403e233c>
+
+Notice that the ``copy`` module would not do the job:
+
+ >>> import copy
+ >>> copy.copy(f) # error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "/usr/local/lib/python2.3/copy.py", line 84, in copy
+ y = _reconstruct(x, reductor(), 0)
+ File "/usr/local/lib/python2.3/copy_reg.py", line 57, in _reduce
+ raise TypeError, "can't pickle %s objects" % base.__name__
+ TypeError: can't pickle function objects
diff --git a/pypers/last.txt b/pypers/last.txt
new file mode 100755
index 0000000..f165aa9
--- /dev/null
+++ b/pypers/last.txt
@@ -0,0 +1,3 @@
+-------------------------------------------------------------------
+LAST, BUT NOT LEAST
+-------------------------------------------------------------------
diff --git a/pypers/magic.txt b/pypers/magic.txt
new file mode 100755
index 0000000..979586d
--- /dev/null
+++ b/pypers/magic.txt
@@ -0,0 +1,1039 @@
+THE MAGIC OF METACLASSES - PART 2
+===========================================================================
+
+Metaclasses are so powerful that a single chapter is not enough to make
+justice to them ;) In this second chapter on metaclasses I will
+unravel their deepest secrets, covering topics such as meta-metaclasses,
+anonymous inner metaclasses, global metaclasses and advanced class factories.
+
+Moreover, I will give various magical applications of metaclasses,
+in the realm of enhancing the Python language itself. Actually, this is
+probably the most idiomatic application of metaclasses (Guido's examples
+on the metaclass usage are all in this area). I will show
+how metaclasses can be used to enhance the ``super`` cooperatice call
+mechanism.
+
+This is not a chapter for the faint of heart.
+
+The secrets of the ``__metaclass__`` hook
+------------------------------------------------------------------------
+
+In the previous chapter we have seen how the ``__metaclass__`` hook can
+be used as a way of metaclass enhancing pre-existing classes
+with a minimal change of the sourcecode.
+
+But it has much deeper secrets.
+
+The first and simplest of them,
+is the fact that the hook can be used it can also be defined
+at the module level, *outside* the class. This allows a number of neat
+tricks, since in presence of a ``__metaclass__`` hook at the module
+level *all* the old style classes in the module (including nested ones!)
+acquire that hook. A first application is to rejuvenate old style classes
+to new style classes.
+
+I remind that old style classes are retained with compability with old
+code, but they are a pain in the back, if you want to use features
+intended for new style classes only (for instance properties etc.).
+Naively, one would expect the conversion from old style classes
+to new style to be long and error prone: suppose you have a very large
+application with hundreds of old style classes defined in dozens of modules.
+Suppose you want to update your application to Python 2.2+ classes in order
+to take advantage of the new features I have discussed extensively in this
+book: the naive way to go would be to go trough the source, look for
+all classes definitions and change
+
+ ::
+
+ Classname: --> Classname(object)
+
+One could solve this problem with a regular expression search and replace
+in all modules, but this would require to change *all* the source.
+This is againt the spirit of OOP, we must *reuse* old code.
+
+Metaclasses are particularly handy to solve this problem: actually it is
+enough to add to your modules the following line as first line:
+
+ ::
+
+ __metaclass__ = type
+
+Then, all your old style classes will have 'type' as their metaclass: this
+is akin to say that all the old style classes are *automagically* rejuvenate
+to new style classes! And this also works for *nested* classes!!
+
+ ::
+
+ #<rejuvenate.py>
+
+ __metaclass__ = type # this rejuvanate all the class in the module
+
+ class C:
+ class D: pass
+
+ print dir(C) # both C and C.D
+ print dir(C.D) # are now new style classes
+
+ #</rejuvenate.py>
+
+This very first example add consistence (if needed) to the
+widespread belief that metaclasses have a well deserved reputation of magic.
+
+The explanation is that defining a global metaclass called ``__metaclass__``
+automatically makes all old style classes (new style class simply ignore
+the existence of the global ``__metaclass__``) defined in you module
+instances of the given metaclass; this automatically converts them to
+new style classes.
+
+Anonymous inner metaclasses
+---------------------------------------------------------------------------
+
+A second, deeper secret of the ``__metaclass__`` hook is that it can be
+used to define anonymous *inner metaclasses*. The following example
+explain what I mean:
+
+ ::
+
+ #<oopp.py>
+
+ def totuple(arg):
+ "Converts the argument to a tuple, if need there is"
+ if isinstance(arg,tuple): return arg # do nothing
+ else: return (arg,) # convert to tuple
+
+ class BracketCallable(object):
+ """Any subclass C(BracketCallable) can be called with the syntax C[t],
+ where t is a tuple of arguments stored in bracket_args; returns the
+ class or an instance of it, depending on the flag 'returnclass'."""
+
+ returnclass=True
+ class __metaclass__(type): # anonymous inner metaclass
+ def __getitem__(cls,args): # non cooperative metamethod
+ if cls.returnclass:
+ c=type(cls.__name__,(cls,),{'bracket_args':totuple(args)})
+ return c # a customized copy of the original class
+ else:
+ self=cls(); self.bracket_args=totuple(args)
+ return self
+
+ #</oopp.py>
+
+In this code 'BracketCallable.__metaclass__' is the anonymous (actually
+it has a special name, ``__metaclass__``) inner metaclass of 'BracketCallable'.
+
+The effect of 'BracketCallable.__metaclass__' is the following: it makes
+'BracketCallable' and its descendants callable with brackets. Since
+the 'returnclass' flag is set, ``__getitem__`` returns the class
+with an attribute 'bracket_args' containing the tuple of the passed
+arguments (otherwise it returns an instance of the class).
+This works since when Python encounters an expression of kind
+``cls[arg]`` it interprets it as ``type(cls).__getitem__(cls,arg)``.
+Therefore, if ``cls`` is a subclass of 'BracketCallable', this means that
+
+ ::
+
+ cls[arg] <=> BracketCallable.__metaclass__.__getitem__(cls,arg)
+
+Let me give few examples:
+
+ >>> from oopp import BracketCallable
+ >>> type(BracketCallable)
+ <class 'oopp.__metaclass__'>
+ >>> print type(BracketCallable).__name__ # not really anonymous
+ __metaclass__
+ >>> print BracketCallable['a1'].bracket_args
+ ('a1',)
+ >>> print BracketCallable['a1','a2'].bracket_args
+ ('a1', 'a2')
+
+This syntactical feature is an example of a thing that can be done
+*trough metaclasses only*: it cannot be emulated by functions.
+
+
+Anonymous inner metaclasses are the least verbose manner
+of defining metamethods. Moreover, they are a neat trick to define
+mix-in classes that, when inherited, can metamagically enhance
+an entire multiple inheritance hierarchy.
+
+In the previous example ``__getitem__`` is noncooperative, but nothing
+forbids anonymous inner metaclasses from being made cooperative. However,
+there is some subtlety one must be aware of.
+Let me give an example. My 'WithCounter' class counts how many instances
+of 'WithCounter' and its subclasses are generated. However, it does not
+distinguishes bewteen different subclasses.
+This was correct in the pizza shop example, simple only the total
+number of produced pizzas mattered, however, in other situations,
+one may want to reset the counter each time a new subclass is created.
+This can be done automagically by a cooperative inner metaclass:
+
+ ::
+
+ class WithMultiCounter(WithCounter):
+ """Each time a new subclass is derived, the counter is reset"""
+ class __metaclass__(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.__this,cls).__init__(*args)
+ reflective(__metaclass__)
+
+Notice that the order of execution of this code is subtle:
+
+1) first, the fact that WithMulticounter has a non-trivial metaclass is
+ registered, but nothing else is done;
+2) then, the line ``reflective(__metaclass__)`` is executed: this means
+ that the inner metaclass (and therefore its instances) get an
+ attribute ``._metaclass__this`` containing a reference to the
+ inner metaclass;
+3) then, the outer class is passed to its inner metaclass and created
+ by the inherited metaclass' ``__new__`` method;
+4) at this point ``cls`` exists and ``cls.__this`` is inherited from
+ ``__metaclass__._metaclass__this``; this means that the expression
+ ``super(cls.__this,cls).__init__(*args)`` is correctly recognized and
+ 'WithMultiCounter' can be initialized;
+5) only after that, the name 'WithMultiCounter' enters in the global
+ namespace and can be recognized.
+
+Notice in particular that inside ``super``, we could also
+use ``cls.__metaclass__`` instead of ``cls.__this``, but this
+would not work inside ``__new__``, whereas ``__this`` would be
+recognized even in ``__new__``. Moreover, ``cls.__metaclass__``
+can be overridden in subclasses (they may have a submetaclass
+of ``__metaclass__``) therefore the usage is risky.
+
+Notice also that ``X.__class__`` can be different from
+``X.__metaclass__`` if the metaclass ``__new__`` method
+is overridden or in various other circumstances.
+
+ >>> from oopp import *
+ >>> print MRO(WithMultiCounter)
+ 1 - WithMultiCounter(WithCounter)[__metaclass__]
+ 2 - WithCounter(object)
+ 3 - object()
+
+For sake of readability, often it is convenient
+to give a name even to inner classes:
+
+::
+
+ #<oopp.py>
+
+ class WithMultiCounter(WithCounter):
+ """Each time a new subclass is derived, the counter is reset"""
+ class ResetsCounter(type):
+ def __init__(cls,*args):
+ cls.counter=0
+ super(cls.ResetsCounter,cls).__init__(*args)
+ __metaclass__=ResetsCounter
+
+ #</oopp.py>
+
+Notice that inside super we used the expression ``cls.ResetsCounter`` and
+not ``WithMultiCounter.ResetsCounter``: doing that would generate a
+``NameError: global name 'WithMultiCounter' is not defined`` since at the
+time when ``ResetsCounter.__init__`` is called for the first time,
+the class ``WithMultiCounter`` exists but is has not yet entered the global
+namespace: this will happens only after the initialization in the
+``ResetsCounter`` metaclass, as we said before.
+
+Without the metaclass one can reset the counter by hand each time, or
+can reset the counter on all the classes of the hierarchy with a
+convenient function (akin to the 'traceH' routine defined in chapter 6).
+
+Example:
+
+ >>> from oopp import *
+ >>> class GrandFather(WithMultiCounter): pass
+ >>> class Father(GrandFather): pass
+ >>> class Child(Father): pass
+ >>> GrandFather()
+ <__main__.GrandFather object at 0x402f7f6c> # first GrandFather instance
+ >>> Father()
+ <__main__.Father object at 0x402f79ec> # first Father instance
+ >>> Father()
+ <__main__.Father object at 0x402f7d4c> # second Father instance
+ >>> Child.counter # zero instances
+ 0
+ >>> Father.counter # two instances
+ 2
+ >>> GrandFather.counter # one instance
+ 1
+
+I leave as an exercise for the reader to show that the original 'WithCounter'
+would fail to count correctly the different subclasses and would put the
+total number of instances in 'Child'.
+
+Passing parameters to (meta) classes
+-------------------------------------------------------------------------
+
+Calling a class with brackets is a way of passing parameters to it (or
+to its instances, if the 'returnclass' flag is not set).
+There additional ways for of doing that.
+One can control the instantiation syntax of classes by redefining the
+``__call__`` method of the metaclass. The point is that when we instantiate
+an object with the syntax ``c=C()``, Python looks
+at the ``__call__`` method of the metaclass of 'C'; the default behaviour
+it is to call ``C.__new__`` and ``C.__init__`` in succession, however, that
+behavior can be overridden. Let me give an example without using
+anonymous metaclasses (for sake of clarity only).
+
+ ::
+
+ #<metacall.py>
+
+ class M(type): # this is C metaclass
+ def __call__(cls):
+ return "Called M.__call__"
+
+ C=M('C',(),{}) # calls type(M).__call__
+ c=C() # calls type(C).__call__
+ # attention: c is a string!
+ print c #=> Called M.__call__
+
+ #</metacall.py>
+
+In this example, ``M.__call__`` simply
+returns the string ``Called M.__call__``, and the class
+'C' is *not* instantiated. Overriding the metaclass
+``__call__ `` method therefore provides another way to implement
+the ``Singleton`` pattern. However, savage overridings as the one in
+this example, are not a good idea, since it will confuse everybody.
+This is an example where metaclasses change the semantics: whereas
+usually the notation ``C()`` means "creates a C instance", the
+metaclass can give to the syntax ``C()`` any meaning we want.
+Here there is both the power and the danger of metaclasses: they
+allows to make both miracles and disasters. Nevertheless, used with
+a grain of salt, they provide a pretty nice convenience.
+
+Anyway, overriding the '__call__' method of the metaclass can be
+confusing, since parenthesis are usually reserved to mean instantion,
+therefore I will prefere to pass arguments trough brackets.
+
+The beauty and the magic of metaclasses stays in the fact that this mechanism
+is completely general: since metaclasses themselves are classes, we can
+'CallableWithBrackets' to pass arguments to a metaclass, i.e.
+'CallableWithBrackets' can also be used as a meta-metaclass!
+
+I leave as an exercise for the reader to figure out
+how to define meta-meta-metaclasses, meta-meta-meta-metaclasses, etc.
+etc. (there is no limit to the abstraction level you can reach with
+metaclasses;-)
+
+Let me show an example: a magical way of making methods cooperative.
+This can be done trough a 'Cooperative' metaclass that inherits from
+'BracketCallable' and therefore has 'BracketCallable.__metaclass__'
+as (meta)metaclass:
+
+ ::
+
+ #<oopp.py>
+
+ class Cooperative(BracketCallable,type):
+ """Bracket-callable metaclass implementing cooperative methods. Works
+ well for plain methods returning None, such as __init__"""
+ def __init__(cls,*args):
+ methods=cls.bracket_args
+ for meth in methods:
+ setattr(cls,meth,cls.coop_method(meth,vars(cls).get(meth)))
+ def coop_method(cls,name,method): # method can be None
+ """Calls both the superclass method and the class method (if the
+ class has an explicit method). Implemented via a closure"""
+ def _(self,*args,**kw):
+ getattr(super(cls,self),name)(*args,**kw) # call the supermethod
+ if method: method(self,*args,**kw) # call the method
+ return _
+
+ #</oopp.py>
+
+The code above works for methods returing ``None``, such as ``__init__``.
+Here I give a first example of application: a hierarchy where the ``__init__``
+methods are automatically called (similar to automatic initialization
+in Java).
+
+ ::
+
+ #<cooperative.py>
+
+ from oopp import Cooperative
+
+ class B(object):
+ """Cooperative base class; all its descendants will automagically
+ invoke their ancestors __init__ methods in chain."""
+ __metaclass__=Cooperative['__init__']
+ def __init__(self,*args,**kw):
+ print "This is B.__init__"
+
+ class C(B):
+ "Has not explicit __init__"
+
+ class D(C):
+ """The metaclass makes D.__init__ to call C.__init__ and
+ therefore B.__init__"""
+ def __init__(self,*args,**kw):
+ print "This is D.__init__"
+
+ d=D()
+
+ print "The metaclass of B is",type(B)
+ print "The meta-metaclass of B is", type(type(B))
+
+ #</cooperative.py>
+
+Output:
+
+ ::
+
+ This is B.__init__
+ This is D.__init__
+ The metaclass of B is <class 'oopp.Cooperative'>
+ The meta-metaclass of B is <class 'oopp.__metaclass__'>
+
+A second example, is the following, an alternative way of
+making the paleoanthropological hierarchy of chapter 4 cooperative:
+
+ ::
+
+ #<paleo.py>
+
+ from oopp import Cooperative,Homo
+
+ class HomoHabilis(Homo):
+ __metaclass__=Cooperative['can']
+ def can(self):
+ print " - make tools"
+
+ class HomoSapiens(HomoHabilis):
+ def can(self):
+ print " - make abstractions"
+
+ class HomoSapiensSapiens(HomoSapiens):
+ def can(self):
+ print " - make art"
+
+ HomoSapiensSapiens().can()
+
+ # Output:
+
+ # <HomoSapiensSapiens> can:
+ # - make tools
+ # - make abstractions
+ # - make art
+
+ #</paleo.py>
+
+Metaclasses can be used to violate the old good rule "explicit is
+better than implicit". Looking at the source code for 'HomoSapiens'
+and 'HomoSapiensSapiens' one would never imagine the ``can`` is
+somewhat special. That is why in the following I will prefer to
+use the anonymous super call mechanism, which is explicit, instead
+of the implicit cooperative mechanism.
+
+Meta-functions
+---------------------------------------------------------------------
+
+The third and deepest secret of the ``__metaclass__`` hook is that, even if
+it is typically used in conjunction with metaclasses, actually the hook
+can refer to generic class factories callable with the signature
+``(name,bases,dic)``. Let me show a few examples
+where ``__metaclass__`` is a function or a generic callable object
+instead of being a metaclass:
+
+ ::
+
+ #<metafun.py>
+
+ from oopp import kwdict
+
+ class Callable(object):
+ def __call__(self,name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+ callableobj=Callable()
+
+ class C: __metaclass__=callableobj
+
+ print "type of C:",C.__class__
+
+ def f(name,bases,dic):
+ print name,bases,'\n',kwdict(dic)
+ return type(name,bases,dic)
+
+ class D: __metaclass__=f
+
+ print "type of D:",D.__class__
+
+ class B(object):
+ def __metaclass__(name,bases,dic):
+ """In this form, the __metaclass__ attribute is a function.
+ In practice, it works as a special static method analogous
+ to __new__"""
+ print "name: ", name
+ print "bases:", bases
+ print "dic:\n",kwdict(dic)
+ return type(name,bases,dic)
+
+ class E(B): pass
+
+ print "type of E:",E.__class__
+ print "Non-called E.__metaclass__:", E.__metaclass__
+
+ #</metafun.py>
+
+With output
+
+ ::
+
+ C ()
+ __metaclass__ = <Callable object at 0x401c964c>
+ __module__ = __builtin__
+ type of C: <type 'type'>
+ D ()
+ __metaclass__ = <function f at 0x401c4994>
+ __module__ = __builtin__
+ type of D: <type 'type'>
+ name: B
+ bases: (<type 'object'>,)
+ dic:
+ __metaclass__ = <function __metaclass__ at 0x401c4a3c>
+ __module__ = __builtin__
+ type of E: <type 'type'>
+ Non-called E.__metaclass__: <unbound method E.__metaclass__>
+
+The advantage/disadvantage of this solution is that the ``__metaclass__``
+hook is called only once, i.e. it is not called again if a new class
+is derived from the original one. For instance in this example 'E' is
+derived from 'B', but the function ``B.__metaclass__`` is *not* called
+during the creation of 'E'.
+
+Metafunctions can also be used when one does not want to transmit the
+metaclass contraint. Therefore they usage is convenient in exactly
+the opposite situation of a cooperative metaclass.
+
+Anonymous cooperative super calls
+-----------------------------------------------------------------------
+
+As I noticed in the previous chapters, the ``super``
+mechanism has an annoying
+problem: one needs to pass explicitely the name of the base class. Typically,
+this is simply an
+inelegance since it is annoying to be forced to retype the name of the base
+class. However, in particular
+cases, it can be a problem. This happens for instance if we try to
+pass the class's methods to a different class: one cannot do that,
+since the methods contains an explicit reference to the original class
+and would not work with the new one. Moreover, having named super calls
+is annoying in view of refactoring. Consider for
+instance the previous ``supernew.py`` script: in the ``__new__`` method
+defined inside the class 'B', we called ``Super`` with the syntax
+``Super(B,cls)`` by repeating the name of the class 'B'. Now,
+if in the following I decide to give to 'B' a more descriptive
+name, I have to go trough the source, search all the ``super``
+calls, and change them accordingly to the new name. It would be
+nice having Python do the job for me. A first solution is to call
+``super`` (or ``Super``) with the syntax ``super(self.__this,obj)``,
+where the special name ``__this`` is explicitly replaced by the name
+of the class where the call is defined by the 'reflective' function
+of last chapter. This approach has the disadvantage that each time we
+derive a new class, we need to invoke *explicitely* the routine
+``reflective``. It would be marvelous to instruct Python to invoke
+``reflective`` automatically at each class creation. Actually, this
+seems to be deep magic and indeed it is: fortunately, a custom metaclass
+can perform this deep magic in few lines:
+
+ ::
+
+ #<oopp.py>
+
+ class Reflective(type):
+ """Cooperative metaclass that defines the private variable __this in
+ its instances. __this contains a reference to the class, therefore
+ it allows anonymous cooperative super calls in the class."""
+ def __init__(cls,*args):
+ super(Reflective,cls).__init__(*args)
+ reflective(cls)
+
+ #</oopp.py>
+
+Now, let me show how 'Reflective' can be used in a practical example.
+
+By deriving new metaclasses from 'Reflective', one can easily
+create powerful class factories that generate reflective classes.
+
+Suppose I want to define a handy class
+factory with the abilitity of counting the number of its instances.
+
+This can be done by noticing that metaclasses are just classes, therefore
+they can be composed with regular classes in multiple inheritance. In
+particular one can derive a 'Logged' metaclass from 'WithLogger': in
+this way we send a message to a log file each time a new class is created.
+This can be done by composing 'WithLogger' with
+'WithMultiCounter.__metaclass__' and with 'Reflective':
+
+ ::
+
+ #<oopp.py>
+
+ class Logged(WithLogger,Reflective):
+ """Metaclass that reuses the features provided by WithLogger.
+ In particular the classes created by Logged are Reflective,
+ PrettyPrinted and Customizable."""
+ #WithLogger provides logfile and verboselog
+ def __init__(cls,*args,**kw):
+ super(Logged,cls).__init__(*args,**kw)
+ bases=','.join([c.__name__ for c in cls.__bases__])
+ print >> cls.logfile, "%s is a child of %s" % (cls,bases)
+ print >> cls.logfile,'and an instance of %s' % type(cls).__name__
+
+ #</oopp.py>
+
+The MRO is
+
+ >>> print MRO(Logged)
+ MRO of Logged:
+ 0 - Logged(WithLogger,Reflective)
+ 1 - WithLogger(WithCounter,PrettyPrinted)
+ 2 - WithCounter(object)
+ 3 - PrettyPrinted(object)
+ 4 - Reflective(type)
+ 5 - type(object)
+ 6 - object()
+
+and the inheritance graph can be drawn as follows:
+
+ ::
+
+
+ _____________________ object 6 ___
+ / / \
+ 2 WithCounter 3 PrettyPrinted type 5
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ \ / /
+ 1 WithLogger Reflective 4
+ \ /
+ \ /
+ \ /
+ \ /
+ Logged 0
+ :
+ :
+ C1
+
+'WithCounter' acts now as a metaclass, since WithCounter.__new__ invokes
+type.__new__. Since ``type.__new__`` is non-cooperative,
+in the composition of a metaclass with a regular class, the metaclass
+should be put first: this guarantees that ``__new__`` derives from
+``type.__new__``, thus avoiding the error message.
+
+ >>> Logged.verboselog=True
+ >>> C1=Logged('C1',(),{})
+ *****************************************************************************
+ Tue Apr 22 18:47:05 2003
+ 1. Created 'C1'
+ with accessibile non-special attributes:
+ _C1__this = 'C1'
+ 'C1' is a child of object
+ and an instance of Logged
+
+Notice that any instance of 'WithCounterReflective' inherits the 'WithCounter'
+attribute ``counter``, that counts the number of classes that have been
+instantiated (however it is not retrieved by ``dir``; moreover the
+instances of 'WithCounterReflective' instances have no ``counter`` attribute).
+
+ >>> C1.counter
+ 1
+
+More on metaclasses as class factories
+----------------------------------------------------------------------------
+
+A slight disadvantage of the approach just described,
+is that 'Logged' cooperatively invokes the ``type.__new__``
+static method, therefore, when we invoke the metaclass, we must explicitly
+provide a name, a tuple of base classes and a dictionary, since the
+``type.__new__`` staticmethod requires that signature. Actually,
+the expression
+
+ ::
+
+ C=Logged(name,bases,dic)
+
+is roughly syntactic sugar for
+
+ ::
+
+ C=Logged.__new__(Logged,name,bases,dic)
+ assert isinstance(C,Logged)
+ Logged.__init__(C,name,bases,dic)
+
+If a different interface is desired, the best way is to use a class
+factory 'ClsFactory' analogous to the object factory 'Makeobj'
+defined in chapter 4. It is convenient to make 'ClsFactory'
+bracket-callable.
+
+ ::
+
+ #<oopp.py>
+
+ class ClsFactory(BracketCallable):
+ """Bracket callable non-cooperative class acting as
+ a factory of class factories.
+
+ ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+ . They automatically converts functions to static methods
+ if the input object is not a class. If an explicit name is not passed
+ the name of the created class is obtained by adding an underscore to
+ the name of the original object."""
+
+ returnclass=False # ClsFactory[X] returns an *instance* of ClsFactory
+
+ def __call__(self, *args):
+ """Generates a new class using self.meta and avoiding conflicts.
+ The first metaobject can be a dictionary, an object with a
+ dictionary (except a class), or a simple name."""
+
+ # default attributes
+ self.name="CreatedWithClsFactory"
+ self.bases=()
+ self.dic={}
+ self.metas=self.bracket_args
+
+ if len(args)==1:
+ arg=args[0]
+ if isinstance(arg,str): # is a name
+ self.name=arg
+ elif hasattr(arg,'__name__'): # has a name
+ self.name=arg.__name__+'_'
+ self.setbasesdic(arg)
+ elif len(args)==2:
+ self.name=args[0]
+ assert isinstance(self.name,str) # must be a name
+ self.setbasesdic(args[1])
+ elif len(args)==3: # must be name,bases,dic
+ self.name=args[0]
+ self.bases+=args[1]
+ self.dic.update(args[2])
+ if len(args)<3 and not self.bases: # creating class from a non-class
+ for k,v in self.dic.iteritems():
+ if isfunction(v): self.dic[k]=staticmethod(v)
+ #return child(*self.bases,**vars(self))
+ return makecls(*self.metas)(self.name,self.bases,self.dic)
+
+ def setbasesdic(self,obj):
+ if isinstance(obj,tuple): # is a tuple
+ self.bases+=obj
+ elif hasattr(obj,'__bases__'): # is a class
+ self.bases+=obj.__bases__
+ if isinstance(obj,dict): # is a dict
+ self.dic.update(obj)
+ elif hasattr(obj,"__dict__"): # has a dict
+ self.dic.update(obj.__dict__)
+
+ #</oopp.py>
+
+'ClsFactory[X]' where 'X' is a metaclass returns callable objects acting as
+class factories. For instance
+
+ ::
+
+ #<oopp.py>
+
+ Class=ClsFactory[type] # generates non-conflicting classes
+ Mixin=ClsFactory[Reflective] # generates reflective classes
+
+ #</oopp.py>
+
+can be used as a class factories that automatically provides a default name,
+base classes and dictionary, and avoids meta-type conflicts.
+'Mixin' generates reflective classes that can be used as mixin in multiple
+inheritance hierarchies. Here I give few example of usage of 'Class':
+
+ >>> from oopp import *
+ >>> C1,C2,C3=[Class('C'+str(i+1)) for i in range(3)]
+ >>> C1
+ <class 'oopp.C1'>
+ >>> C2
+ <class 'oopp.C2'>
+ >>> C3
+ <class 'oopp.C3'>]
+
+ >>> Clock=Class('Clock',{'get_time':get_time})
+ >>> Clock
+ <class 'oopp.Clock'>
+ >>> Clock.get_time()
+ 16:01:02
+
+Another typical usage of 'Class' is the conversion of a module in a class:
+for instance
+
+ >>> time_=Class(time)
+ >>> time_
+ <class 'oopp.time_'>
+
+Notice the convention of adding an underscore to the name of the class
+generated from the 'time' module.
+
+ >>> time_.asctime()
+ 'Mon Jan 20 16:33:21 2003'
+
+Notice that all the functions in the module ``time`` has been magically
+converted in staticmethods of the class ``time_``. An advantage of this
+approach is that now the module is a class and can be enhanced with
+metaclasses: for instance we could add tracing capabilities, debugging
+features, etc.
+
+By design, 'Class' and 'Reflective' also works when the first argument
+is a class or a tuple of base classes:
+
+ >>> ClsFactory_=Class(ClsFactory)
+ >>> type(ClsFactory_)
+ <class 'oopp.__metaclass__'>
+ >>> ClsFactory_=Mixin(ClsFactory)
+ >>> type(ClsFactory_) # automagically generated metaclass
+ <class 'oopp._Reflective__metaclass__'>
+
+Programming with metaclasses
+--------------------------------------------------------------------------
+In order to how a non-trivial application of metaclasses in real life,
+let me come back to the pizza shop example discussed in chapter 4 and 6.
+
+ ::
+
+ #<oopp.py>
+
+ def Pizza(toppings,**dic):
+ """This function produces classes inheriting from GenericPizza and
+ WithLogger, using a metaclass inferred from Logged"""
+ toppinglist=toppings.split()
+ name='Pizza'+''.join([n.capitalize() for n in toppinglist])
+ dic['toppinglist']=toppinglist
+ return ClsFactory[Logged](name,
+ (GenericPizza,WithLogger,WithMultiCounter),dic)
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> Margherita=Pizza('tomato mozzarella',verboselog=True)
+ *****************************************************************************
+ Tue May 13 14:42:17 2003
+ 1. Created 'PizzaTomatoMozzarella'
+ with accessibile non-special attributes:
+ ResetsCounter = <class 'oopp.ResetsCounter'>
+ _GenericPizza__this = <class 'oopp.GenericPizza'>
+ _WithCounter__this = <class 'oopp.WithCounter'>
+ _WithLogger__this = <class 'oopp.WithLogger'>
+ baseprice = 1
+ counter = 0
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <unbound method PizzaTomatoMozzarella.price>
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato', 'mozzarella']
+ toppings_price = <unbound method PizzaTomatoMozzarella.toppings_price>
+ verboselog = True
+ 'PizzaTomatoMozzarella' is a child of GenericPizza,WithLogger,
+ WithMultiCounter and an instance of _LoggedResetsCounter
+
+Notice the *deep* magic: ``Pizza`` invokes ``ClsFactory[Logged]`` which in
+turns calls the class factory ``child`` that creates 'Margherita' from
+'GenericPizza', 'WithLogger' and 'WithMultiCounter' by using the
+metaclass 'Logged': however, since 'WithMultiCounter', has the internal
+metaclass 'ResetsCounter' , there is a metatype conflict:
+``child`` *automagically* solves the conflict by creating the metaclass
+'_LoggedResetsCounter' that inherits both from 'Logged' and 'ResetsCounter'.
+At this point, 'Margherita' can be safely created
+by '_LoggedResetsCounter'. As such, the creation of 'Margherita'
+will be registered in the log file and 'Margherita' (with all its
+children) will continue to be able to recognize the special identifier
+``this``.
+
+ >>> print Margherita('large')
+ *****************************************************************************
+ Tue May 13 14:47:03 2003
+ 1. Created large pizza with tomato,mozzarella, cost $ 6.0
+ with accessibile non-special attributes:
+ ResetsCounter = <class 'oopp.ResetsCounter'>
+ _GenericPizza__this = <class 'oopp.GenericPizza'>
+ _WithCounter__this = <class 'oopp.WithCounter'>
+ _WithLogger__this = <class 'oopp.WithLogger'>
+ baseprice = 1
+ counter = 1
+ formatstring = %s
+ logfile = <open file '<stdout>', mode 'w' at 0x402c2058>
+ price = <bound method PizzaTomatoMozzarella.price of
+ <oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+ size = large
+ sizefactor = {'small': 1, 'large': 3, 'medium': 2}
+ topping_unit_price = 0.5
+ toppinglist = ['tomato', 'mozzarella']
+ toppings_price = <bound method PizzaTomatoMozzarella.toppings_price of
+ <oopp.PizzaTomatoMozzarella object at 0x4032764c>>
+ verboselog = True
+ large pizza with tomato,mozzarella, cost $ 6.0
+ >>> print MRO(Margherita)
+ MRO of PizzaTomatoMozzarella:
+ 0 - PizzaTomatoMozzarella(GenericPizza,WithLogger)[_LoggedResetsCounter]
+ 1 - GenericPizza(object)
+ 2 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 3 - WithMultiCounter(WithCounter)[ResetsCounter]
+ 4 - WithCounter(object)
+ 5 - PrettyPrinted(object)
+ 6 - object()
+
+Notice that
+
+ >>> print Margherita
+ 'PizzaTomatoMozzarella'
+
+The power of inheritance in this example is quite impressive, since
+I have reused the same class 'WithLogger' (and its children) both in the
+metaclass hierarchy and in the regular hierarchy: this means that I have added
+logging capabilities both to classes and their instances in a
+strike! And there is no confusion between the two. For instance,
+there is a ``counter`` attribute for the metaclass 'Logged'
+and many independent ``counter`` attributes for any generated class,
+i.e. for any kind of pizza.
+
+ It is interesting to notice that '' itself is an instance of
+ its inner metaclass, as ``type()`` would show. This technique
+ avoids the need for inventing a new name for the metaclass. The inner
+ metaclass is automatically inherited by classes inheriting from the outer
+ class.
+
+Metaclass-aided operator overloading
+---------------------------------------------------------------------------
+
+As we discussed in chapter 4, inheriting from built-in types is generally
+painful. The problem is that if P is a primitive class, i.e. a
+Python built-in type, and D=D(P) is a derived class, then the
+primitive methods returning P-objects have to be modified (wrapped) in
+such a way to return D-objects.
+
+The problem is expecially clear in the context of operator overloading.
+
+Consider for instance the problem of defining a 'Vector' class in the
+mathematical sense. Mathematically-speaking, vectors are defined as objects
+that can be summed each other and multiplied by numbers; they can be
+represented by (finite or infinite) sequences. In the case of finite
+sequences, vectors can be represented with lists and a vector class can
+be naturally implemented by subclassing ``list``:
+
+ ::
+
+ #<vector.py>
+
+ class Vector(list):
+ """Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)"""
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [el*scalar for el in self]
+ def __rmul__(self,scalar):
+ return [scalar*el for el in self]
+
+ v=Vector([1,0])
+ w=Vector([0,1])
+
+ print v+w, type(v+w)
+ print 2*v, type(2*v)
+ print v*2, type(v*2)
+
+ #</vector.py>
+
+With output
+
+ ::
+
+ [1, 1] <type 'list'>
+ [2, 0] <type 'list'>
+ [2, 0] <type 'list'>
+
+The problem is that the overloaded methods must be wrapped in such a way
+to return ``Vector`` object and not ``list`` object; moreover, if
+``Vector`` is subclassed (for instance by defining a ``NumericVector``),
+the overloaded methods must return instances of the subclass. There is
+only one way of doing that automatically: trough the magic of metaclasses.
+
+Here is the solution, involving an ``autowrappedmethod`` descriptor class,
+that wraps the overloaded operators and is automatically invoked by
+the metaclass ``AutoWrapped``.
+
+ ::
+
+ #<oopp.py>
+
+ class autowrappedmethod(wrappedmethod):
+ """Makes the method returning cls instances, by wrapping its
+ output with cls"""
+ klass=None # has to be fixed dynamically from outside
+ def __init__(self,meth):
+ super(autowrappedmethod,self).__init__(meth) # cooperative
+ self.klass=self.klass # class variable -> instance variable
+ def wrapper(self): # closure
+ return lambda *args,**kw: self.klass(self.func(*args,**kw))
+
+ class AutoWrapped(type):
+ """Metaclass that looks at the methods declared in the attributes
+ builtinlist and wraplist of its instances and wraps them with
+ autowrappedmethod."""
+ def __init__(cls,name,bases,dic):
+ super(AutoWrapped,cls).__init__(name,bases,dic) # cooperative
+ cls.builtinlist=getattr(cls,'builtinlist',[])
+ if not hasattr(cls,'diclist') : # true only at the first call
+ cls.diclist=[(a,vars(bases[0])[a]) for a in cls.builtinlist]
+ if dic.has_key('wraplist'): # can be true at any call
+ cls.diclist+=[(a,dic[a]) for a in cls.wraplist]
+ wrapper=autowrappedmethod.With(klass=cls)
+ d=dict([(a,wrapper(v)) for a,v in cls.diclist])
+ customize(cls,**d)
+
+ #</oopp.py>
+
+Now the ``Vector`` class can be written as
+
+ ::
+
+ #<oopp.py>
+
+ class Vector(list):
+ """Implements finite dimensional vectors as lists. Can be instantiated
+ as Vector([a,b,c,..]) or as Vector(a,b,c ..)"""
+ __metaclass__=AutoWrapped
+ wraplist='__add__ __radd__ __mul__ __rmul__'.split()
+ def __add__(self,other):
+ return [el+other[i] for i,el in enumerate(self)]
+ __radd__=__add__
+ def __mul__(self,scalar):
+ return [scalar*el for el in self]
+ def __rmul__(self,scalar):
+ return [el*scalar for el in self]
+
+ #</oopp.py>
+
+Here the ``AutoWrapped`` metaclass wraps the output of ``__add__,
+__radd__, __mul__, __rmul__``, guaranteeing that they returns ``Vector``
+instances or instances of some subclass of ``Vector``, if ``Vector`` is
+subclassed. This is an example of usage:
+
+ .. doctest
+
+ >>> from oopp import Vector
+ >>> v=Vector([1,0])
+ >>> v
+ <oopp.Vector object at 0x4032858c>
+ >>> w=Vector([0,1])
+ >>> v+2*w
+ <oopp.Vector object at 0x403190ac>
+ >>> print v+2*w
+ [1, 2]
+
+It should be clear by now that metaclasses are the natural framework where
+to discuss operator overloading
+(at least in languages that have metaclasses ;-). After all, operator
+overloading is another kind of (very nice) syntactic sugar and we know
+already that metaclasses are very good when we need syntactic sugar.
diff --git a/pypers/marelli/corso_py.txt b/pypers/marelli/corso_py.txt
new file mode 100755
index 0000000..be5e092
--- /dev/null
+++ b/pypers/marelli/corso_py.txt
@@ -0,0 +1,144 @@
+- modulo string non è deprecated, ma il 90% di quello che contiene e' inutile
+- le classi normalmente iniziano con la 1a lettera maiuscola
+- nota sul coding dei caratteri
+- dalla 2.2.1 esistono True e False (1 o 0)
+- triple virgolette permettono di andare a capo
+- le costanti sono tutte maiuscole (come in C)
+- GRAFICA: librerie tk sono standard in tutte le distribuzioni
+- ipython è interessante: chiamando la funzione con due ?? ritorna il codice sorgente
+- nella normale distribuzione: import inspect, poi print inspect.getsource(nomefunzione)
+- esistono dei decompilatori che permettono di ritrovare il sorgente py partendo dal pyc
+- libro: python in a nutshell di Alex Martelli (come manuale di riferimento)
+- modulo __future__ per importare le caratteristiche future del linguaggio
+- __file__ restituisce il nome del file python in esecuzione
+- %s sostituisce il parametro nella stringa di testo (ciao)
+- %r sostituisce invece la rappresentazione della stringa ('ciao')
+- non far iniziare i files .py con un numero (i programmi), sempre con un carattere.
+ Però è possibile eseguire un programma che inizia con un numero facendo __import__("nomeprogramma").
+- IDLE non è consigliabile usarlo aperto 2 volte
+- l'attributo __name__ può essere 2 cose: il nome del modulo, se importato, oppure __main__
+- la funzione import esegue il file py, quindi se ci sono inizializzazioni ecc QUESTE VERRANNO ESEGUITE.
+ Per evitare ciò occorre (ed è bene) mettere if __name__ == '--main__' prima della parte di codice da eseguire
+ in caso quel file venga lanciato da solo (come main).
+ Questa cosa è fondamentale anche per il pydoc (documentazione).
+- nuovo modulo "decimal" per calcoli con numeri decimali (i calcoli sono ESATTI non c'e' errore di arrotondamento)
+- IMPORT: se si è modificato il modulo: reload(nomemodulo). Rifare l'import non funziona. Si può importare
+ anche con un altro nome "import pippo as pluto"; A PARITà DI NOME, L'ULTIMO IMPORT VINCE.
+ Non fare import metodo from modulo, meglio importare il modulo (si evita la confusione in caso di
+ metodi omonimi). PER FARE LA LISTA: import sys: sys.modules.
+- la prima stringa che segue la definizione di funzione rappresenta il doc help.
+- pydoc nomefunzione restiruisce la doc string della funzione o modulo
+- pydoc -g in grafico
+- pydoc -w nomefile -> scrive nomefile.html, colorato.
+- per chiamare pydoc: python -m pydoc (è un modulo).
+- attenzione a usare il backslash perchè è il codice di escape
+- quando una funzione non ritorna niente, in realtà ritorna un None.
+- usare os.path.join(dir1,dir2,ecc) per creare correttamente i nomi del path.
+- valori immutabili: numeri, stringhe e tuple. IMPORTANTE DA RICORDARE.
+- ls.sort ordina velocemente, ma cambia la lista; sorted invece no.
+- tuple (1,2,3) => usate in genere stile record database.
+- liste [1,2,3] => mutabili, hanno più metodi per lavorarci sopra.
+- modulo operator molto comodo (metodo itemgetter per ordinare).
+- potente istruzione set (nuovo tipo di dato), per fare la lista ORDINATA degli elementi.
+ Builtin solo nella 2.4, nelle precedenti occorre prendere il modulo sets dalla distro 2.3 e importarlo:
+ try: set: except Nameerror: from sets import Set as set (vedi esempio).
+- comoda ma strana libreria tkMessageBox per le finestre grafiche [non e' strana ;-)]
+- libreria urllib per leggere le pagine web
+- libreria webbrowser per aprire il browser sulla macchina e visualizzare la pagina.
+- isinstance per verificare il tipo di oggetto
+- installazione di matplotlib: indicare a mano di usare numarray (matplotlibrc) e riavviare python.
+- from pylab import *; plot(x,y) due vettori; show() per vedere il plot.
+- strumenti di sviluppo: wingide, boa x wxpython, eric (sotto linux gratis, a pagamento sotto Windows),
+ iron python x dot net, ctypes x accedere alle librerie windows.
+- è preferibile non scrivere righe di codice python più lunghe di 80 caratteri. Per andare a capo si usa semplicemente il "\".
+- Guido Var Rossum è il creatore, durante le vacanze di Natale 1989.
+- ATTENZIONE: se una funzione esce con return, è una funzione. Se esce con yeld allora è un generatore di iterazioni.
+ L'iteratore va chiamato con un ciclo, in modo da farsi restituire gli elementi, ma non ha bisogno di trappare
+ le eccezioni sulla fine del ciclo (vedi immagini2.py).
+- quando parte python viene eseguito il file pythonrc.
+- per modificare una libreria conviene fare la sovrascrittura della funzione incriminata e sovrascriverla dopo il caricamento.
+- in caso di applicazioni grafiche, conviene fare prima il kernel non grafico e validarlo, poi aggiungere la grafica.
+- per chiamare programmi esterni la 2.4 ha subprocess.call, che è più sicura di os.system.
+ Inserita a mano funziona nella 2.3; eventualmente verificare sulla 2.2. E' diverso da os.system, che chiama una nuova shell.
+- come navigatore di classi, IDLE è meglio del Pwin (?).
+- Michele ha copiato dalle FAQ una funzione per killare i processi windows (non esiste nella distro standard).
+- newsgroup: it.comp.lang.python (anche su google newsgroup: http://groups.google.it/group/it.comp.lang.python).
+- IMPORTANTE: non esistono variabili globali, ma solo relative al modulo. Però è possibile modificare __builtin__
+ in qualsiasi momento per aggiungere o cambiare qualsiasi metodo o parametro. E' ALTAMENTE SCONSIGLIATO.
+- threads: nuovo modulo "threading"
+- in generale è meglio usare i processi, invece dei thread (si maneggiano meglio). E poi i processi si possono spammare
+ su altri computer in caso di bisogno. Però sotto WIN i processi consumano molte risorse.
+- i thread non si possono uccidere, a differenza dei processi, se non usando ctrl-break.
+ L'uso di try:finally è consigliabile perchè si può trappare i ctrl-c ed eseguire le varie operazioni di abort.
+ Ma usando i thread l'unica soluzione rimane ctrl-break, e quindi non è possibile eseguire correttamente le operazioni di chiusura.
+ ATTENZIONE!!!
+- interessante trucco killme di Michele per chiudere il programma principale lanciato.
+- dir(oggetto) restituisce le proprietà di un oggetto (in pratica, qualsiasi cosa di python).
+- CLASSI: usare (object), nuova definizione per le classi "class Nomeclasse(object):"
+- modulo itertools per le iterazioni.
+- per lanciare IDLE: pythonw c:\programmi\Utils\python24\Lib\idlelib\idle.pyw
+- debugger a riga di comando: python -m pdb nomedelfile.py
+- il debugger praticamente non serve quando si usa il python (testimonianze dirette dei guru).
+- exc_debugger è comodo per il debug post mortem.
+- Classi.oggetti privati: iniziarli con "_".
+- esiste l'ereditarietà multipla, ma non è consigliato usarla. EVITARE.
+- zope3 è stata appena rifatta praticamente senza ereditarieta' multipla.
+- non usare exec, perchè restituisce poco in caso di errore; usare getattr e setattr. ATTENZIONE: in python non si è mai sicuri
+ della conformazione della classe, visto che può essere dinamicamente ridefinita -> istanze comprese!!!
+- usare Classe.mro per vedere il method resolution order.
+- pydoc nomedelmodulo(senza .py) restituisce le doc strings ordinate.
+- pydoc -w nomedelmodulo(senza .py) restituisce un file nomedelmodulo.html (colorato).
+- doctester.py (creato da Michele, chiama il doctest) permette di eseguire codice scritto all'interno della documentazione ascii.
+ GALATTICO!
+- wrapper di rst2html.py fatto da Michele per produrre sia html che pdf, partendo dalla sintassi ascii "restructured text".
+ Il pdf lo fa Latex, non c'è sotto windows. BISOGNA INSTALLARE ANCHE docutils: partendo dalla dir della distro: python setup.py install.
+Riga di comando: python rst2html.py < f:\doctest\esempio.txt >f:\doctest\esempio.html (meglio fare un batch).
+- unit test: vengono verificati solo i metodi che iniziano per "test". Si usa il modulo "unittest".
+- e' possibile convertire un doctest in unit test!!!! Si usa doctest.DocFileSuite. GALATTICO!
+- non è possibile testare un'interfaccia grafica, se non c'è la possibilità offerta dall'interfaccia stessa.
+- la classe cmd permette di costruire un interprete di comandi; per accedere ai metodi occorre che inizino per "do".
+- PACKAGE: collezione di cose che possono servire. E' una directory che contiene un file "__init__.py".
+ Va messa nel posto giusto: .\Lib\site-packages, dentro un file .pth. Si mette la dir di livello superiore.
+ Si possono indicare anche files .zip (GAGLIARDO, ma solo dalla 2.3).
+- EXCEPTION: è molto comodo specificare un messaggio. NON USARE raise "frasettina", non verrà più supportato.
+ Si può inoltre specificare una classe personale per le eccezioni, frasette incluse. Vedi esempio.
+- attualmente il metodo super non funziona con le eccezioni (sono vecchio stile).
+- gli errori non devono mai passare sotto silenzio, sempre che non siano trappati correttamente.
+ OCCORRE TRAPPARE UNA COSA ALLA VOLTA!!!
+- usando i threads in caso eccezione imprevista il programma non si ferma. ATTENZIONE.
+- "la cosa giusta è non usare i thread".... by Michele Simionato :)
+- classe queue per comunicare tra i thread. Classe deque (doppia coda).
+ USARE TRY EXCEPT per verificare la coda, non usare if. INSERIRE SEMPRE UN TRY: FINALLY:
+ -> ATTENZIONE: IL CTRL-BREAK AMMAZZA TUTTO E NON è TRAPPABILE. (Diverso il discorso tra linea
+ di comando e interfaccia grafica).
+- PEP: python enhancement proposal
+- generator conphrention: un loop entro parentesi tonde non sporca la variabile indice nel workspace.
+ La list conphrention esisteva già [parentesi quadre].
+- invece di ritornare un parametro in caso di errore conviene sollevare e gestire un'eccezione.
+- in caso di liste è molto più conveniente usare un append (o extend) invece che +=.
+- anche in caso di stringhe è meglio usare join invece di +.
+- import this -> zen di python
+- IDE: Wingide gode di buona fama; Eric/QT Designer è molto carino per le interfacce grafiche; Komodo;
+ Boa Contructor... sono a pagamento. MA I PIù GRANDI SVILUPPATORI USANO VI, EMACS...
+- INFORMAZIONI: usare il newsgroup è caldamente consigliato! Cookbook per esempi di codice ben fatto.
+- il carattere "_" è una variabile dummy.
+- il del non libera la memoria, solo le referenze. NON bisogna fidarsi ciecamente del garbage collector!!!
+ Infatti, in caso di utilizzo di debugger, rimangono dei riferimenti collegati.
+- file HISTORY.txt che spiega l'evoluzione del modulo. Ordine cronologico inverso.
+- EXEC serve solo per i guru... (doctest per esempio). Meglio usare import per eseguire altri files, oppure execfile
+ (che alimenta un dizionario), così si riesce a fare il debug.
+- revision control systems: Darts (a linea di comando).
+- DOC STRING: se manca il modulo già puzza...
+- tutti i progetti seri hanno il loro test!!!
+- ASSERT: utile per verificare una variabile rispetto uno o più valori noti.
+ Se è diversa viene alzata un'eccezione "AssertionError".
+- www.edgewall.com, trac, version control manager, Roberto Lupi (Fano) <- contatto da Michele Simionato.
+- utilizzo di __init__ per eseguire operazioni di default tipo directory.
+- ATTENZIONE: gli oggetti che hanno un wrapper c (o altro) bisogna cancellarli esplicitamente,
+ altrimente non saranno distrutti.
+- im_func per estrarre un metodo da una classe e usarlo come funzione (o metterlo dentro un'altra classe).
+ Si può cancellare un metodo da una classe usando del. COMUNQUE SONO COSE DA EVITARE!
+- il doppio underscore davanti al metodo impedisce la sovrascrittura da parte di una classe ereditata
+ che ridefinisce lo stesso metodo.
+- from __future__ import generators
+- pyserial per gestire le seriali.
diff --git a/pypers/marelli/deleting.py b/pypers/marelli/deleting.py
new file mode 100755
index 0000000..3f4ddc2
--- /dev/null
+++ b/pypers/marelli/deleting.py
@@ -0,0 +1,4 @@
+class C(object):
+ pass
+
+ls = [C() for _ in range(1000000)]
diff --git a/pypers/marelli/ex_thread.py b/pypers/marelli/ex_thread.py
new file mode 100755
index 0000000..a949d34
--- /dev/null
+++ b/pypers/marelli/ex_thread.py
@@ -0,0 +1,20 @@
+import threading, time
+
+def print_hello():
+ for i in range(10):
+ print "hello"
+ time.sleep(1)
+ if i == 5:
+ import pdb; pdb.set_trace()
+ raise RuntimeError("Ahia!")
+
+
+def print_world():
+ for i in range(10):
+ print "world"
+ time.sleep(1)
+
+threading.Thread(target=print_hello).start()
+
+threading.Thread(target=print_world).start()
+
diff --git a/pypers/marelli/frontpage.html b/pypers/marelli/frontpage.html
new file mode 100755
index 0000000..2886d8d
--- /dev/null
+++ b/pypers/marelli/frontpage.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<STYLE TYPE="text/css">
+ body { font-size: 160%; }
+</STYLE>
+</head>
+<body bgcolor="lightblue">
+<center>
+<h1>Corso di Python Avanzato</h1>
+Michele Simionato<br/><br/>
+michele.simionato@gmail.com<br/><br/>
+Corso tenuto alla Magneti Marelli<br/><br/>
+Bologna, 19-23 Settembre 2005
+</center>
+</body>
+</html>
diff --git a/pypers/marelli/mail/corso-python.tex b/pypers/marelli/mail/corso-python.tex
new file mode 100755
index 0000000..2ea6c4e
--- /dev/null
+++ b/pypers/marelli/mail/corso-python.tex
@@ -0,0 +1,164 @@
+\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{Corso di Python avanzato per la Magneti Marelli}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Corso di Python avanzato per la Magneti Marelli}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+
+Daniele Matteucci mi ha chiesto di stilare una breve nota per una proposta
+di un corso di Python avanzato da tenere presso la Magneti Marelli nel
+Settembre 2005.
+
+Il corso si svolgerebbe in 3 giorni di full immersion. Il mio compenso
+ordinario per questo tipologia di corsi è di 400 Euro al giorno più le
+spese (diciamo 150 Euro al giorno per vitto e alloggio). La cifra precisa
+andrà poi definita in base al programma da svolgere. Un programma indicativo
+potrebbe essere il seguente.
+\begin{itemize}
+\item {}
+Primo giorno: testing automatico di applicazioni in Python.
+
+\end{itemize}
+
+Discuterò i frameworks più usati (doctest, py.test, unittest) con
+esercitazioni pratiche.
+\begin{itemize}
+\item {}
+Secondo giorno: refactoring a buone pratiche di programmazione.
+
+\end{itemize}
+
+Esempi pratici di come convertire cattivo codice in buon codice e
+discussione dei più tipici errori di programmazione.
+\begin{itemize}
+\item {}
+Terzo giorno: argomenti specifici di programmazione avanzata.
+
+\end{itemize}
+
+Questa parte del programma è da concordare: argomenti trattabili
+potrebbero essere la programmazione multithreading, programmazione
+ad oggetti avanzata, iteratori e generatori, etc.
+
+Ho già tenuto corsi di Python di questo tipo (quest'anno a Bolzano
+e ad Oxford); il programma del corso di Oxford si può trovare a
+qui:
+
+\href{https://www.accu.org/conference/events_2005_04_19.html\#55}{https://www.accu.org/conference/events{\_}2005{\_}04{\_}19.html{\#}55}
+
+Preventivo indicativo per tre giorni:
+\begin{quote}
+
+\begin{longtable}[c]{|p{0.11\locallinewidth}|p{0.10\locallinewidth}|p{0.10\locallinewidth}|}
+\hline
+
+Compenso
+ &
+400 x 3
+ &
+1200E
+ \\
+\hline
+
+IVA
+ &
+20{\%}
+ &
+240E
+ \\
+\hline
+
+Spese
+ &
+150 x 3
+ &
+450E
+ \\
+\hline
+
+Viaggio
+ & &
+60E
+ \\
+\hline
+\multicolumn{2}{|l|}{
+Totale
+} &
+1950E
+ \\
+\hline
+\end{longtable}
+\end{quote}
+
+Roma, 23 giugno 2005
+\begin{quote}
+
+\emph{Michele Simionato}, \href{mailto:michele.simionato@gmail.com}{michele.simionato@gmail.com}
+\end{quote}
+
+\end{document}
+
diff --git a/pypers/marelli/mail/corso-python.txt b/pypers/marelli/mail/corso-python.txt
new file mode 100755
index 0000000..6561e05
--- /dev/null
+++ b/pypers/marelli/mail/corso-python.txt
@@ -0,0 +1,57 @@
+Corso di Python avanzato per la Magneti Marelli
+-------------------------------------------------------------------------
+
+
+
+Daniele Matteucci mi ha chiesto di stilare una breve nota per una proposta
+di un corso di Python avanzato da tenere presso la Magneti Marelli nel
+Settembre 2005.
+
+Il corso si svolgerebbe in 3 giorni di full immersion. Il mio compenso
+ordinario per questo tipologia di corsi è di 400 Euro al giorno più le
+spese (diciamo 150 Euro al giorno per vitto e alloggio). La cifra precisa
+andrà poi definita in base al programma da svolgere. Un programma indicativo
+potrebbe essere il seguente.
+
+- Primo giorno: testing automatico di applicazioni in Python.
+
+Discuterò i frameworks più usati (doctest, py.test, unittest) con
+esercitazioni pratiche.
+
+- Secondo giorno: refactoring a buone pratiche di programmazione.
+
+Esempi pratici di come convertire cattivo codice in buon codice e
+discussione dei più tipici errori di programmazione.
+
+- Terzo giorno: argomenti specifici di programmazione avanzata.
+
+Questa parte del programma è da concordare: argomenti trattabili
+potrebbero essere la programmazione multithreading, programmazione
+ad oggetti avanzata, iteratori e generatori, etc.
+
+Ho già tenuto corsi di Python di questo tipo (quest'anno a Bolzano
+e ad Oxford); il programma del corso di Oxford si può trovare a
+qui:
+
+https://www.accu.org/conference/events_2005_04_19.html#55
+
+
+Preventivo indicativo per tre giorni:
+
+ ======== ======= =======
+ Compenso 400 x 3 1200E
+ -------- ------- -------
+ IVA 20% 240E
+ -------- ------- -------
+ Spese 150 x 3 450E
+ -------- ------- -------
+ Viaggio 60E
+ -------- ------- -------
+ Totale 1950E
+ ================ =======
+
+Roma, 23 giugno 2005
+
+ *Michele Simionato*, michele.simionato@gmail.com
+
+
diff --git a/pypers/marelli/mail/corso-python2.txt b/pypers/marelli/mail/corso-python2.txt
new file mode 100755
index 0000000..6b1c1b8
--- /dev/null
+++ b/pypers/marelli/mail/corso-python2.txt
@@ -0,0 +1,55 @@
+Corso di Python avanzato per la Magneti Marelli
+-------------------------------------------------------------------------
+
+Daniele Matteucci mi ha chiesto di stilare una breve nota per una proposta
+di un corso di Python avanzato da tenere presso la Magneti Marelli nel
+Settembre 2005.
+
+Il corso si svolgerebbe in 3 giorni di full immersion. Il mio compenso
+ordinario per questo tipologia di corsi è di 400 Euro al giorno più le
+spese (diciamo 150 Euro al giorno per vitto e alloggio). La cifra precisa
+andrà poi definita in base al programma da svolgere. Un programma indicativo
+potrebbe essere il seguente.
+
+- Primo giorno: testing automatico di applicazioni in Python.
+
+Discuterò i frameworks più usati (doctest, py.test, unittest) con
+esercitazioni pratiche.
+
+- Secondo giorno: refactoring a buone pratiche di programmazione.
+
+Esempi pratici di come convertire cattivo codice in buon codice e
+discussione dei più tipici errori di programmazione.
+
+- Terzo giorno: argomenti specifici di programmazione avanzata.
+
+Questa parte del programma è da concordare: argomenti trattabili
+potrebbero essere la programmazione multithreading, programmazione
+ad oggetti avanzata, iteratori e generatori, etc.
+
+Ho già tenuto corsi di Python di questo tipo (quest'anno a Bolzano
+e ad Oxford); il programma del corso di Oxford si può trovare a
+qui:
+
+https://www.accu.org/conference/events_2005_04_19.html#55
+
+
+Preventivo indicativo per tre giorni:
+
+ ======== ======= =======
+ Compenso 400 x 3 1200E
+ -------- ------- -------
+ IVA 20% 240E
+ -------- ------- -------
+ Spese 150 x 3 450E
+ -------- ------- -------
+ Viaggio 60E
+ -------- ------- -------
+ Totale 1950E
+ ================ =======
+
+Roma, 23 giugno 2005
+
+ *Michele Simionato*, michele.simionato@gmail.com
+
+
diff --git a/pypers/marelli/mail/polizza.txt b/pypers/marelli/mail/polizza.txt
new file mode 100755
index 0000000..e88a8b1
--- /dev/null
+++ b/pypers/marelli/mail/polizza.txt
@@ -0,0 +1,28 @@
+Un paio di comunicazioni.
+
+1. Ho stipulato una polizza di responsabilita' civile terzi per i giorni
+19-23 Settembre, quindi adesso non ci dovrebbero essere piu' problemi.
+
+2. Stavo riguardando il programma da portare e mi sono reso conto che
+resta un po' di tempo libero (diciamo una mezza giornata). In questa
+mezza giornata potrei parlare di varie cose, ditemi voi cosa vi
+interessa di piu'. Possibilita' sono:
+
+- numeric & matplotlib (praticamente l'equivalente di Matlab ma fatto
+ da Python)
+
+- programmazione orientata agli oggetti;
+
+- un'introduzione alla programmazione con i threads;
+
+- un'introduzione alla programmazione di network (client/server, magari
+ qualche cenno su Twisted);
+
+- altro a richiesta.
+
+Le possibilita' sono esclusive perche' non si puo' far tutto!
+
+A risentirci,
+
+
+ Michele Simionato
diff --git a/pypers/marelli/mail/preventivo.txt b/pypers/marelli/mail/preventivo.txt
new file mode 100755
index 0000000..e1e5239
--- /dev/null
+++ b/pypers/marelli/mail/preventivo.txt
@@ -0,0 +1,40 @@
+Stavo preparando il preventivo per il corso di Settembre.
+Ho ridotto la stima delle spese a 120 Euro al giorno per
+5 giorni. Il compenso resta a 400 Euro al giorno per 5
+giorni. Tuttavia c'è da dire che:
+
+1. ho speso un'intera giornata lunedì scorso (4 Luglio) dalle
+ 10.30 alle 16:00 (piu' 4 ore di viaggio) per discutere il corso
+ e farmi un'idea degli strumenti di sviluppo utilizzati;
+
+2. si è deciso di organizzare un corso molto su misura, con argomenti
+ specifici; inoltre si è deciso di preparare due questionari, uno di inizio
+ corso per verificare la preparazione generale (che ho già spedito a
+ Daniele Matteucci) ed uno di fine corso per verificare l'apprendimento
+ (che preparerò in seguito).
+
+Stimando il tempo occupato in queste attività in una giornata lavorativa
+(in effetti è un pò di più) mi pare il caso di aggiungere 400 Euro al
+preventivo. Ecco qui il sommario:
+
+ ============ ======= =======
+ Preparazione 400E
+ ------------ ------- -------
+ Compenso 400 x 5 2000E
+ ------------ ------- -------
+ Spese 120 x 5 600E
+ ------------ ------- -------
+ Viaggio 60E
+ ------------ ------- -------
+ Totale 3060E
+ ============ ======= =======
+
+A questo c'è da aggiungere l'IVA. In passato ho tenuto dei corsi per
+la provincia la quale pagava direttamente le spese di viaggio e vitto e
+alloggio. Come funziona in questo caso invece? Va aggiunta solo l'IVA
+al 20% sul compenso oppure va aggiunto anche qualcosa per l'albergo (mi pare
+il 10%) e per il viaggio?
+Infine, fevo ancora informarmi per quanto riguarda la copertura assicurativa,
+ma mi riservo di aggiungere qualcosina per coprire tali costi aggiuntivi.
+
+ Michele Simionato
diff --git a/pypers/marelli/mail/programma.txt b/pypers/marelli/mail/programma.txt
new file mode 100755
index 0000000..ec36352
--- /dev/null
+++ b/pypers/marelli/mail/programma.txt
@@ -0,0 +1,96 @@
+Corso Python Magneti Marelli
+======================================================
+
+Data: 19-23 Settembre
+Docente: Michele Simionato
+
+
+Programma
+---------------------------------------
+
+Si tratta di un corso di 20 ore suddiviso in 5 moduli di 4 ore ciascuno.
+
+Modulo 1: Strumenti di sviluppo, debugging e introspezione.
+
+In questo modulo discutero' gli strumenti da me utilizzati per sviluppare
+in Python sotto Windows. Parlero' di Cygwin come ambiente di lavoro,
+di Python e di IPython come interpreti interattivi, di Idle e di PythonWin
+come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discutero'
+alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot,
+etc.). Se rimane del tempo e c'e' interesse accennero' anche
+a Revision Control Systems come Subversion e Darcs.
+
+Modulo 2: Programmazione di base in Python.
+
+In questo modulo si ripasseranno molto brevemente le basi di Python e
+si discuteranno le soluzioni al questionario di ammissione. Lo scopo
+piu' che altro e' quello di conoscersi e di chiarire il livello medio
+dei partecipanti e coprire eventuali buchi nella preparazione di base.
+
+Modulo 3. Tipici errori di programmazione e gestione delle eccezioni.
+
+Si discuteranno buoni e cattivi esempi di programmazione presi da software
+reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche
+per interpretare i tracebacks di Python e per identificare l'origine dei
+problemi.
+
+Modulo 4. Sviluppo orientato ai test
+
+Come scrivere software con tecnologie agili, con lo scopo di ridurre e
+tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest.
+Esempi di programmazione test driven.
+
+Modulo 5: Design, documentazione e manutenzione di librarie
+
+Pratiche di programmazione "in the large". Moduli, packages, strumenti di
+documentazione e di installazione. Applicazioni pratiche di principi generali
+quali disaccoppiamento, modularità, non duplicazione del codice.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Preventivo
+----------------------
+
+ ============ ======= =======
+ Preparazione 400E
+ ------------ ------- -------
+ Compenso 400 x 5 2000E
+ ------------ ------- -------
+ IVA 20% 240E
+ ------------ ------- -------
+ Spese 120 x 5 600E
+ ------------ ------- -------
+ Viaggio 60E
+ ------------ ------- -------
+ Totale 3300E
+ ============ ======= =======
+
+
+Coordinate bancarie per il pagamento
+-------------------------------------
+
+ Michele Simionato
+ Cassa di Risparmio di Venezia
+ Filiale di Pianiga
+ Via Roma 1 (ITALIA)
+ ABI: 06345 CAB: 36230
+ C/C: 0700075870H
+ Code swift/bic: CVCEIT2V
+ IBAN: IT47 C063 4536 2300 7400 0758 70H
diff --git a/pypers/marelli/materiale/README.txt b/pypers/marelli/materiale/README.txt
new file mode 100755
index 0000000..fcf7e29
--- /dev/null
+++ b/pypers/marelli/materiale/README.txt
@@ -0,0 +1,14 @@
+Questa directory contiene il materiale relativo al corso di Python avanzato
+tenuto presso la Magneti Marelli dal 19 al 23 settembre 2005.
+
+- corso.pdf (dispense in formato PDF)
+- corso.html (dispense in formato HTML)
+- corso.txt (dispense in formato RST)
+- *.py (alcuni scripts di esempio)
+
+Materiale integrativo:
+
+- doctest-talk (seminario da me tenuto su doctest)
+
+I curiosi possono anche guardare alle mie lezioni di Oxford
+(http://www.phyast.pitt.edu/~micheles/oxford-lectures.zip)
diff --git a/pypers/marelli/materiale/corso.html b/pypers/marelli/materiale/corso.html
new file mode 100755
index 0000000..8a0de82
--- /dev/null
+++ b/pypers/marelli/materiale/corso.html
@@ -0,0 +1,1850 @@
+<?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>Corso Python Magneti Marelli</title>
+</head>
+<body>
+<div class="document" id="corso-python-magneti-marelli">
+<h1 class="title">Corso Python Magneti Marelli</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Tenuto:</th><td class="field-body">19-23 Settembre 2005</td>
+</tr>
+<tr class="field"><th class="docinfo-name">Autore:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
+</tr>
+</tbody>
+</table>
+<p><em>Queste dispense sono un compendio informale e molto sintetico di quanto
+svolto durante il corso. Esse non si pongono in nessun modo come un testo
+sistematico di programmazione in Python. Il loro scopo principale è quello
+di tenere traccia, per quanto possibile, di quanto si è detto. Lo
+scopo secondario è quello di invogliare i partecipanti e tutti
+gli eventuali lettori ad affrontare gli argomenti qui trattati per forza
+di cose in maniera abbozzata, in maniera più sistematica andando a
+consultare le fonti più adatte alla bisogna, siano essi libri di testo,
+la documentazione ufficiale, newsgroups, siti Web dedicati e, in ultima
+instanza, il codice sorgente stesso, l'unico riferimento definitivo.</em></p>
+<div class="contents topic" id="contents">
+<p class="topic-title first"><a name="contents">Indice</a></p>
+<ul class="simple">
+<li><a class="reference" href="#i-messaggi-fondamentali-del-corso" id="id1" name="id1">I messaggi fondamentali del corso</a><ul>
+<li><a class="reference" href="#studiare-paga" id="id2" name="id2"><em>Studiare paga</em></a></li>
+<li><a class="reference" href="#disaccoppiamento-e-kiss" id="id3" name="id3"><em>Disaccoppiamento</em> e <em>KISS</em></a></li>
+<li><a class="reference" href="#non-nascondete-gli-errori-sotto-il-tappeto" id="id4" name="id4"><em>Non nascondete gli errori sotto il tappeto</em></a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-1-strumenti-di-introspezione-sviluppo-e-debugging" id="id5" name="id5">Modulo 1: Strumenti di introspezione, sviluppo e debugging</a><ul>
+<li><a class="reference" href="#strumenti-di-introspezione-e-come-ottenere-aiuto" id="id6" name="id6">Strumenti di introspezione e come ottenere aiuto</a></li>
+<li><a class="reference" href="#ambienti-di-sviluppo" id="id7" name="id7">Ambienti di sviluppo</a></li>
+<li><a class="reference" href="#strumenti-di-debugging" id="id8" name="id8">Strumenti di debugging</a></li>
+<li><a class="reference" href="#strumenti-utili-per-l-utenza-scientifica-ingegneristica" id="id9" name="id9">Strumenti utili per l'utenza scientifica/ingegneristica</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-2-programmazione-di-base-in-python" id="id10" name="id10">Modulo 2: Programmazione di base in Python</a><ul>
+<li><a class="reference" href="#encoding" id="id11" name="id11">Encoding</a></li>
+<li><a class="reference" href="#pathnames" id="id12" name="id12">Pathnames</a></li>
+<li><a class="reference" href="#insiemi" id="id13" name="id13">Insiemi</a></li>
+<li><a class="reference" href="#differenza-tra-mutabili-e-immutabili" id="id14" name="id14">Differenza tra mutabili e immutabili</a></li>
+<li><a class="reference" href="#getattr-e-setattr" id="id15" name="id15">getattr e setattr</a></li>
+<li><a class="reference" href="#gestione-dei-processi" id="id16" name="id16">Gestione dei processi</a></li>
+<li><a class="reference" href="#iteratori-e-generatori" id="id17" name="id17">Iteratori e generatori</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni" id="id18" name="id18">Modulo 3. Tipici errori di programmazione e gestione delle eccezioni</a><ul>
+<li><a class="reference" href="#l-errore-pi-tipico-con-le-eccezioni" id="id19" name="id19">L'errore più tipico con le eccezioni</a></li>
+<li><a class="reference" href="#il-try-finally-una-grande-idea" id="id20" name="id20">Il try .. finally è una grande idea</a></li>
+<li><a class="reference" href="#uso-di-assert" id="id21" name="id21">Uso di assert</a></li>
+<li><a class="reference" href="#non-usate-exec" id="id22" name="id22">Non usate exec</a></li>
+<li><a class="reference" href="#come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate" id="id23" name="id23">Come far partire pdb automaticamente in caso di eccezioni inaspettate</a></li>
+<li><a class="reference" href="#eccezioni-e-threads" id="id24" name="id24">Eccezioni e threads</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-4-sviluppo-orientato-ai-test" id="id25" name="id25">Modulo 4. Sviluppo orientato ai test</a><ul>
+<li><a class="reference" href="#consigli-di-carattere-generale" id="id26" name="id26">Consigli di carattere generale</a></li>
+<li><a class="reference" href="#usare-unittest" id="id27" name="id27">Usare unittest</a></li>
+<li><a class="reference" href="#usare-doctest" id="id28" name="id28">Usare doctest</a></li>
+<li><a class="reference" href="#un-esempio-di-programma-sviluppato-in-maniera-incrementale" id="id29" name="id29">Un esempio di programma sviluppato in maniera incrementale</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-5-design-documentazione-e-manutenzione-di-librarie" id="id30" name="id30">Modulo 5: Design, documentazione e manutenzione di librarie</a><ul>
+<li><a class="reference" href="#la-filosofia-del-python" id="id31" name="id31">La filosofia del Python</a></li>
+<li><a class="reference" href="#principio-del-disaccoppiamento" id="id32" name="id32">Principio del disaccoppiamento</a></li>
+<li><a class="reference" href="#principio-del-kiss" id="id33" name="id33">Principio del KISS</a></li>
+<li><a class="reference" href="#importanza-di-avere-un-prototipo" id="id34" name="id34">Importanza di avere un prototipo</a></li>
+<li><a class="reference" href="#moduli-e-packages" id="id35" name="id35">Moduli e packages</a></li>
+<li><a class="reference" href="#come-si-documenta-una-libreria-python" id="id36" name="id36">Come si documenta una libreria Python</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#modulo-6-domande-estemporanee" id="id37" name="id37">Modulo 6: Domande estemporanee</a><ul>
+<li><a class="reference" href="#come-funziona-import" id="id38" name="id38">Come funziona 'import'</a></li>
+<li><a class="reference" href="#come-funziona-del" id="id39" name="id39">Come funziona '__del__'</a></li>
+<li><a class="reference" href="#che-differenza-c-fra-variabili-di-classe-e-di-istanza" id="id40" name="id40">Che differenza c'è fra variabili di classe e di istanza</a></li>
+<li><a class="reference" href="#che-cosa-sono-i-metodi-che-iniziano-con" id="id41" name="id41">Che cosa sono i metodi che iniziano con &quot;__&quot;</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#soluzioni-al-questionario-di-ammissione" id="id42" name="id42">Soluzioni al questionario di ammissione</a><ul>
+<li><a class="reference" href="#scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero" id="id43" name="id43">Scrivere un programma che testa se una stringa rappresenta un numero</a></li>
+<li><a class="reference" href="#scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente" id="id44" name="id44">Scrivere un programma che lista tutti i files nella directory corrente</a></li>
+<li><a class="reference" href="#listare-tutti-i-files-nelle-sottodirectories-ricorsivamente" id="id45" name="id45">Listare tutti i files nelle sottodirectories ricorsivamente</a></li>
+<li><a class="reference" href="#calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory" id="id46" name="id46">Calcolare lo spazio occupato da tutti i files di tipo .txt in una directory</a></li>
+<li><a class="reference" href="#listare-i-files-a-seconda-delle-dimensioni" id="id47" name="id47">Listare i files a seconda delle dimensioni</a></li>
+<li><a class="reference" href="#scrivere-un-test-per-verificate-che-una-directory-sia-vuota" id="id48" name="id48">Scrivere un test per verificate che una directory sia vuota</a></li>
+<li><a class="reference" href="#aprire-una-finestrella-contenente-la-scritta-hello-world" id="id49" name="id49">Aprire una finestrella contenente la scritta &quot;hello, world!&quot;</a></li>
+<li><a class="reference" href="#scaricare-la-pagina-web-http-www-example-com-da-internet" id="id50" name="id50">Scaricare la pagina Web http://www.example.com da Internet</a></li>
+<li><a class="reference" href="#stampare-a-schermo-una-tavola-delle-moltiplicazioni" id="id51" name="id51">Stampare a schermo una tavola delle moltiplicazioni</a></li>
+<li><a class="reference" href="#trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto" id="id52" name="id52">Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="i-messaggi-fondamentali-del-corso">
+<h1><a class="toc-backref" href="#id1" name="i-messaggi-fondamentali-del-corso">I messaggi fondamentali del corso</a></h1>
+<div class="section" id="studiare-paga">
+<h2><a class="toc-backref" href="#id2" name="studiare-paga"><em>Studiare paga</em></a></h2>
+<p>Grazie alla sua macchina del tempo, Guido ha risolto i problemi
+che vi stanno affliggendo ora dieci anni fa, e sono già nel linguaggio
+e nella libreria standard. Per esempìo avete scoperto di avere a
+disposizione:</p>
+<ul class="simple">
+<li>le stringhe di documentazione per darvi la documentazione automatica
+del codice tramite strumenti quali pydoc ed altri;</li>
+<li>il try ... finally per garantirvi un &quot;gracefull exit&quot; del vostro
+programma, anche in presenza di eccezioni inaspettate;</li>
+<li>i metodi setUp e tearDown di unittest.TestCase, che vi permettono
+di inizializzare e di pulire propriamente l'ambiente per ogni test;</li>
+<li>sia unittest che doctest vi permetto di gestire le eccezioni (nel
+senso di gestire le eccezioni &quot;buone&quot;, quelle che vi aspettate);</li>
+<li>call e Popen in subprocess per aprire processi, kill per ucciderli
+(usando win32api.TerminateProcess);</li>
+<li>twisted vi gestisce la comunicazione tra processi e tutte le eccezioni
+senza problemi</li>
+</ul>
+<p>Una settimana di studio oggi può risparmiarvi mesi di frustrazioni
+domani. Sfruttare le risorse disponibili, quali tutorial, howto,
+libri, documentazione, siti Web (come il Python cookbook) e soprattutto
+i newsgroups. Ma prima di postare su di un newsgroup leggetevi &quot;How to
+ask smart questions&quot; e ricordatevi sempre che &quot;Google is you friend&quot;.</p>
+</div>
+<div class="section" id="disaccoppiamento-e-kiss">
+<h2><a class="toc-backref" href="#id3" name="disaccoppiamento-e-kiss"><em>Disaccoppiamento</em> e <em>KISS</em></a></h2>
+<p>Se potere, scomponete l'applicazione in componenti separati e testateli
+separatamente. Scegliete architetture che vi permettono di disaccoppiare
+i problemi. Se potere fare le cose semplici, fatele semplici.
+Abbiate un core minimale che faccia poco, ma che siate sicuri che
+funzioni. Non complicatevi la vita.</p>
+</div>
+<div class="section" id="non-nascondete-gli-errori-sotto-il-tappeto">
+<h2><a class="toc-backref" href="#id4" name="non-nascondete-gli-errori-sotto-il-tappeto"><em>Non nascondete gli errori sotto il tappeto</em></a></h2>
+<p>Non trappate l'inaspettato, è una ricetta sicura per avere rogne.
+Usate lo <em>sviluppo incrementale</em>, cioè verificate il funzionamento del
+programma SUBITO, e fissate l'errore SUBITO. Non debuggate mai
+a posteriori, se potete evitarlo, ma scrivete codice testato fin da subito.
+Abbiate una bella suite di test automatici di installazione, per accorgervi
+da subito se c'è un problema quando installate il software su di un'altra
+macchina.</p>
+</div>
+</div>
+<div class="section" id="modulo-1-strumenti-di-introspezione-sviluppo-e-debugging">
+<h1><a class="toc-backref" href="#id5" name="modulo-1-strumenti-di-introspezione-sviluppo-e-debugging">Modulo 1: Strumenti di introspezione, sviluppo e debugging</a></h1>
+<p><em>In questo modulo discuterò gli strumenti da me utilizzati per sviluppare
+in Python sotto Windows. Parlerò di Cygwin come ambiente di lavoro,
+di Python e di IPython come interpreti interattivi, di Idle e di PythonWin
+come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discuterò
+alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot,
+etc.).</em></p>
+<div class="section" id="strumenti-di-introspezione-e-come-ottenere-aiuto">
+<h2><a class="toc-backref" href="#id6" name="strumenti-di-introspezione-e-come-ottenere-aiuto">Strumenti di introspezione e come ottenere aiuto</a></h2>
+<ul class="simple">
+<li><strong>Help in linea:</strong>
+Ottimo</li>
+<li><strong>Pydoc:</strong>
+Standard, può essere usato dalla riga di comando.
+<tt class="docutils literal"><span class="pre">pydoc</span> <span class="pre">-g</span></tt> oppure <tt class="docutils literal"><span class="pre">python</span> <span class="pre">-mpydoc</span> <span class="pre">-g</span></tt>
+vi dà la versione grafica, con funzionalità di search.</li>
+<li><strong>Help di ActiveState:</strong>
+Eccezionale, contiene anche libri di testo, FAQs e how-tos.</li>
+<li><strong>Google:</strong>
+Di tutto, di più.</li>
+<li><strong>Newsgroups:</strong>
+Risolvono i vostri problemi per voi, e gratis.</li>
+</ul>
+</div>
+<div class="section" id="ambienti-di-sviluppo">
+<h2><a class="toc-backref" href="#id7" name="ambienti-di-sviluppo">Ambienti di sviluppo</a></h2>
+<ul class="simple">
+<li><strong>Cygwin:</strong>
+Emulatore Unix sotto Windows. Vi permette di lavorare dalla riga di
+comando comodamente. Evitate di perdere tempo a cliccare a destra e
+a manca e avete una stabilità maggiore di quella di un ambiente
+grafico.</li>
+<li><strong>Idle:</strong>
+Ambiente di sviluppo per Python che viene con la distribuzione standard.
+Un pò povero, con qualche difetto, ma semplice e portabile ovunque.</li>
+<li><strong>PythonWin:</strong>
+Ambiente di sviluppo per Python che viene con la distribuzione ActiveState
+per Windows. Più sofisticato di Idle e più integrato con Windows. Ha dei
+pro e dei contro.</li>
+<li><strong>WingIDE, Eric/Qt Designer, Komodo, Boa Constructor:</strong>
+Ambienti di sviluppo di cui esiste una versione commerciale. In generale
+hanno un look più professionale e sono utili se uno deve dare interfacce
+grafiche. WingIDE ha il debugger migliore, a quanto ho sentito.</li>
+<li><strong>Emacs o Vi:</strong>
+Un Real Programmer (TM) vi dirà che al mondo esistono due soli editor:
+Emacs e Vi. Tutto il resto è spazzatura. Emacs e Vi girano bene sotto
+Windows, ma funzionano al meglio sotto Unix.</li>
+</ul>
+</div>
+<div class="section" id="strumenti-di-debugging">
+<h2><a class="toc-backref" href="#id8" name="strumenti-di-debugging">Strumenti di debugging</a></h2>
+<ul class="simple">
+<li><strong>print:</strong> è la soluzione usata dai migliori programmatori Python;</li>
+<li><strong>pdb:</strong> sembra il debugger dei poveri, ma è standard e funziona;</li>
+<li><strong>mille altri debuggers</strong>, compreso un nuovissimo winpdb dall'autore
+del pdb, da valutare;</li>
+<li><strong>programmazione test-driven:</strong> con questa metodologia la stragrande
+maggioranza dei bugs vengono individuati subito, non appena il
+codice viene scritto, e vi troverete ad usare il debuggere dieci
+volte di meno di quanto fate ora.</li>
+</ul>
+</div>
+<div class="section" id="strumenti-utili-per-l-utenza-scientifica-ingegneristica">
+<h2><a class="toc-backref" href="#id9" name="strumenti-utili-per-l-utenza-scientifica-ingegneristica">Strumenti utili per l'utenza scientifica/ingegneristica</a></h2>
+<ul class="simple">
+<li><strong>Numeric e/o Numarray:</strong>
+Tutto quello che serve per far conti con le matrici. Numarray è il
+successore di Numeric, con compatibilità al 99%.</li>
+<li><strong>matplotlib:</strong>
+Il meglio per plottare grafici 2D. Richiede Numeric/Numarray.</li>
+<li><strong>ipython:</strong>
+Il miglior interprete interattivo per Python. Eccezionali capacità
+di introspezione e di debugging (è integrato con il Python debugger
+pdb della libreria standard).</li>
+</ul>
+</div>
+</div>
+<div class="section" id="modulo-2-programmazione-di-base-in-python">
+<h1><a class="toc-backref" href="#id10" name="modulo-2-programmazione-di-base-in-python">Modulo 2: Programmazione di base in Python</a></h1>
+<p><em>In questo modulo si ripasseranno molto brevemente le basi di Python e
+si discuteranno le soluzioni al questionario di ammissione. Lo scopo
+più che altro è quello di conoscersi e di chiarire il livello medio
+dei partecipanti e coprire eventuali buchi nella preparazione di base.</em></p>
+<p>Le soluzioni agli esercizi sono riportate nell'ultimo capitolo. Durante le
+correzioni mi sono accordi di vari buchi nella programmazione
+di base in Python che si è cercato di riempire.</p>
+<div class="section" id="encoding">
+<h2><a class="toc-backref" href="#id11" name="encoding">Encoding</a></h2>
+<p>[Illustrato al corso 1] In versioni recenti di Python (dalla 2.3)
+se il vostro script contiene dei caratteri accentati (anche nei commenti)
+ottenete un warning tipo questo:</p>
+<pre class="literal-block">
+sys:1: DeprecationWarning: Non-ASCII character '\xe0' in file x.py
+on line 1, but no encoding declared; see
+http://www.python.org/peps/pep-0263.html for details
+</pre>
+<p>La soluzione e' aggiungere in testa al vostro programma una dichiarazione
+tipo questa:</p>
+<pre class="literal-block">
+#-*- encoding: latin-1 -*-
+</pre>
+<p>(usate latin-15 se il vostro programma contiene il simbolo dell'Euro).</p>
+</div>
+<div class="section" id="pathnames">
+<h2><a class="toc-backref" href="#id12" name="pathnames">Pathnames</a></h2>
+<p>Sfortunatamente Windows usa la backslash come separatore dei pathnames;
+la backslash e' anche il carattere di escaping, quindi ci sono
+problemi:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print 'documenti\nonna' # \n interpretato come newline
+documenti
+onna
+</pre>
+<p>La soluzione e' usare raw strings:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print r'documenti\nonna'
+documenti\nonna
+</pre>
+<p>Alternativamente, in versioni recenti di Windows, avreste potuto usare
+anche &quot;/&quot; come separatore:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import os
+&gt;&gt;&gt; os.path.exists(r'C:\Python24\python.exe')
+True
+&gt;&gt;&gt; os.path.exists(r'C:/Python24/python.exe')
+True
+</pre>
+</div>
+<div class="section" id="insiemi">
+<h2><a class="toc-backref" href="#id13" name="insiemi">Insiemi</a></h2>
+<p>In Python 2.3 gli insiemi sono stati aggiunti come un nuovo tipo di dati
+nel modulo sets (by Alex Martelli); in Python 2.4 gli insiemi sono diventati
+un tipo builtin.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; s = set('pippo')
+&gt;&gt;&gt; s
+set(['i', 'p', 'o'])
+&gt;&gt;&gt; 'i' in s
+True
+&gt;&gt;&gt; 'p' in s
+True
+&gt;&gt;&gt; 'o' in s
+True
+&gt;&gt;&gt; 'z' in s
+False
+</pre>
+<p>E' possibile calcolare l'unione e l'intersezione di insiemi:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; t = set('aglio')
+&gt;&gt;&gt; s | t
+set(['a', 'g', 'i', 'l', 'o', 'p'])
+&gt;&gt;&gt; s &amp; t
+set(['i', 'o'])
+</pre>
+<p>Il modo compatibile con il passato per usare i sets nello stesso modo nella
+2.3 e nella 2.4 è il seguente:</p>
+<pre class="literal-block">
+try:
+ set:
+except NameError:
+ from sets import Set as set
+</pre>
+<p>Guardare la documentazione standard per saperne di più.</p>
+</div>
+<div class="section" id="differenza-tra-mutabili-e-immutabili">
+<h2><a class="toc-backref" href="#id14" name="differenza-tra-mutabili-e-immutabili">Differenza tra mutabili e immutabili</a></h2>
+<p>Questa è una causa comune di confusione in Python:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a = 1 # i numeri sono immutabili
+&gt;&gt;&gt; b = a
+&gt;&gt;&gt; a += 1
+&gt;&gt;&gt; a
+2
+&gt;&gt;&gt; b
+1
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; a = [1] # le liste sono mutabili
+&gt;&gt;&gt; b = a
+&gt;&gt;&gt; a += [1]
+&gt;&gt;&gt; a
+[1, 1]
+&gt;&gt;&gt; b
+[1, 1]
+</pre>
+</div>
+<div class="section" id="getattr-e-setattr">
+<h2><a class="toc-backref" href="#id15" name="getattr-e-setattr">getattr e setattr</a></h2>
+<p>[Svolto al corso 1] <tt class="docutils literal"><span class="pre">getattr</span></tt> e <tt class="docutils literal"><span class="pre">setattr</span></tt> servono per richiamare e
+settare metodi il cui nome viene determinato dinamicamente:</p>
+<pre class="literal-block">
+#&lt;getattr_ex.py&gt;
+
+class C(object):
+ def m1(self):
+ print &quot;chiamato m1&quot;
+ def m2(self):
+ print &quot;chiamato m2&quot;
+
+if __name__ == &quot;__main__&quot;:
+ c = C()
+ method = raw_input(&quot;Che metodo devo chiamare? [m1 o m2] &quot;)
+ getattr(c, method)()
+
+#&lt;/getattr_ex.py&gt;
+</pre>
+<p><em>Non usate exec quando getattr basterebbe!</em></p>
+<pre class="doctest-block">
+&gt;&gt;&gt; method = 'm1'
+&gt;&gt;&gt; exec 'c.%s()' % method # funziona ma è brutto
+chiamato m1
+&gt;&gt;&gt; getattr(c, method)() # il modo giusto
+chiamato m1
+</pre>
+<p>Per <tt class="docutils literal"><span class="pre">setattr</span></tt> vedere la documentazione.</p>
+</div>
+<div class="section" id="gestione-dei-processi">
+<h2><a class="toc-backref" href="#id16" name="gestione-dei-processi">Gestione dei processi</a></h2>
+<p>Come far partire un processo in parallelo:</p>
+<pre class="literal-block">
+import subprocess
+
+PLAYER =&quot;mplay32&quot;
+
+def play_song(song):
+ subprocess.Popen([PLAYER, &quot;/play&quot;, &quot;/close&quot;, song]) # NON BLOCCA!
+ print &quot;Partito&quot;
+
+
+if __name__ == &quot;__main__&quot;:
+ play_song(&quot;c:/Documents and Settings/micheles/Desktop/Music/1. &quot;
+ &quot;Theme from Harry's Game.mp3&quot;)
+</pre>
+<p><tt class="docutils literal"><span class="pre">subprocess.call</span></tt> fa partire il processo e blocca il programma fintanto
+che il processo non è terminato. Ho anche fatto vedere cosa succede
+se uno dei processi solleva qualche eccezione inaspettata ma viene chiuso
+correttamente grazie al try .. finally:</p>
+<pre class="literal-block">
+#&lt;main.py&gt;
+
+&quot;Chiama due processi proc1a.py e proc1b.py&quot;
+
+import subprocess
+
+CMD_a = [&quot;python&quot;, &quot;-c&quot;, &quot;import proc1a; proc1a.main()&quot;]
+CMD_b = [&quot;python&quot;, &quot;-c&quot;, &quot;import proc1b; proc1b.main()&quot;]
+
+if __name__ == &quot;__main__&quot;:
+ p_a = subprocess.Popen(CMD_a)
+ p_b = subprocess.Popen(CMD_b)
+
+#&lt;/main.py&gt;
+</pre>
+<p>Processo 1a:</p>
+<pre class="literal-block">
+#&lt;proc1a.py&gt;
+
+import time
+
+def main():
+ for i in range(10):
+ print &quot;hello&quot;
+ time.sleep(1)
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc1a.py&gt;
+</pre>
+<p>Processo 1b:</p>
+<pre class="literal-block">
+#&lt;proc1b.py&gt;
+
+#-*- encoding: latin-1 -*-
+import time, sys
+
+def main():
+ try:
+ f = file(&quot;proc1b.py&quot;)
+ for i in range(10):
+ print &quot;world&quot;
+ if i == 5:
+ raise RuntimeError(&quot;Ahia!&quot;)
+ time.sleep(1)
+ finally:
+ f.close()
+ print &quot;Il file è stato chiuso correttamente.&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc1b.py&gt;
+</pre>
+<p>Ho anche illustrato brevemente come si possono gestire i processi da
+Twisted:</p>
+<pre class="literal-block">
+#&lt;proc2a.py&gt;
+
+&quot;Un processo che genera numeri casuali e li salva nel file data.txt&quot;
+
+import random
+
+def main():
+ ro = random.Random()
+ out = file(&quot;data.txt&quot;, &quot;w&quot;)
+ for number in ro.sample(range(1000), 100):
+ print &gt;&gt; out, number
+ out.close()
+ print &quot;Dati salvati sul file 'data.txt'&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc2a.py&gt;
+
+#&lt;proc2b.py&gt;
+
+&quot;Un processo che genera l'istogramma histo.png dai dati in data.txt&quot;
+
+from pylab import hist, savefig
+
+def main():
+ hist([int(n) for n in file(&quot;dat.txt&quot;)], 10)
+ savefig(&quot;histo.png&quot;)
+ print &quot;Istogramma salvato sul file 'histo.png'&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc2b.py&gt;
+
+#&lt;twisted_main.py&gt;
+
+&quot;Il main che chiama proc2a.py e proc2b.py nell'ordine e gestisce gli errori&quot;
+
+import webbrowser, sys
+if sys.platform == &quot;win32&quot;:
+ from twisted.internet import win32eventreactor
+ win32eventreactor.install()
+
+from twisted.internet.utils import getProcessOutput
+from twisted.internet import reactor
+
+def scrivi_messaggio(err):
+ print err.getErrorMessage()
+ reactor.stop()
+ import pdb; pdb.set_trace() # fa partire il debugger in caso di errore
+
+def visualizza_histo(out_di_genera_histo):
+ print out_di_genera_histo
+ webbrowser.open(&quot;histo.png&quot;)
+
+def genera_histo(out_di_genera_dati):
+ print out_di_genera_dati
+ getProcessOutput(sys.executable, (r&quot;c:\corso\processi\proc2b.py&quot;,)) \
+ .addCallback(visualizza_histo) \
+ .addErrback(scrivi_messaggio)
+
+def genera_dati():
+ getProcessOutput(sys.executable, (r&quot;c:\corso\processi\proc2a.py&quot;,)) \
+ .addCallback(genera_histo) \
+ .addErrback(scrivi_messaggio)
+
+if __name__ == &quot;__main__&quot;:
+ reactor.callLater(0, genera_dati) # call &quot;genera_dati&quot; after 0 seconds
+ reactor.run()
+
+#&lt;/twisted_main.py&gt;
+</pre>
+<p>In questo esempio ho usato <tt class="docutils literal"><span class="pre">sys.executable</span></tt>, che contiene il nome
+completo dell'eseguibile Python (per esempio <tt class="docutils literal"><span class="pre">C:\Python24\python.exe</span></tt>)
+con cui il programma principale è stato lanciato. Questo assicura che
+i processi secondari vengano lanciati con quella versione di Python
+(utile se avete installato contemporaneamente piu' versioni di Python
+e ci possono essere dei dubbi, oppure se il path non e' settato
+correttamente e l'eseguibile Python non viene trovato).</p>
+<p>A volte, nonostante tutta la buona volontà, i processi vanno fuori
+controllo. E' possibile ammazzarli brutalmente, con una funzione
+<tt class="docutils literal"><span class="pre">kill</span></tt> come la seguente:</p>
+<pre class="literal-block">
+import os
+
+try: # we are on Windows
+ import win32api
+ def kill(pid, *unix_compatibility_dummy_args):
+ handle = win32api.OpenProcess(1, False, pid) # open handle to kill
+ win32api.TerminateProcess(handle, -1)
+ win32api.CloseHandle(handle)
+ os.kill = kill # fix os
+except ImportError: # we are on Unix
+ pass
+</pre>
+<p>In questo modo di fare, il modulo 'os' della libreria standard viene
+fissato automaticamente, aggiungendogli una funzione 'kill' che è
+mancante nell'ambiente Windows ma che può facilmente essere implementata
+usando le win32api (che non vengono con la distribuzione standard ma sono
+incluse con la distribuzione dell'ActiveState).</p>
+<p>Naturalmente cambiare moduli della libreria standard al volo NON È
+CONSIGLIATO, ma è sempre meglio che modificare a mano il codice
+sorgente e mantenere una propria versione modificata.</p>
+</div>
+<div class="section" id="iteratori-e-generatori">
+<h2><a class="toc-backref" href="#id17" name="iteratori-e-generatori">Iteratori e generatori</a></h2>
+<p>Un iterabile è un qualunque oggetto su cui si può iterare con un
+ciclo &quot;for&quot;; un iteratore è un oggetto con un metodo .next().
+Il modo più comune per definire iteratori è tramite un generatore,
+cioè una &quot;funzione&quot; con uno &quot;yield&quot;:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def gen123():
+... 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):
+ File '&lt;stdin&gt;', line 1, in ?
+StopIteration
+</pre>
+<p>Un ciclo &quot;for&quot; internamente converte l'iterabile in un iteratore,
+chiama il metodo &quot;.next()&quot; successivamente e trappa l'eccezione StopIteration,
+uscendo dal loop quando non c'è più nulla su cui iterare:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = gen123()
+&gt;&gt;&gt; for i in it: print i
+...
+1
+2
+3
+</pre>
+</div>
+</div>
+<div class="section" id="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni">
+<h1><a class="toc-backref" href="#id18" name="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni">Modulo 3. Tipici errori di programmazione e gestione delle eccezioni</a></h1>
+<p><em>Si discuteranno buoni e cattivi esempi di programmazione presi da software
+reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche
+per interpretare i tracebacks di Python e per identificare l'origine dei
+problemi.</em></p>
+<div class="section" id="l-errore-pi-tipico-con-le-eccezioni">
+<h2><a class="toc-backref" href="#id19" name="l-errore-pi-tipico-con-le-eccezioni">L'errore più tipico con le eccezioni</a></h2>
+<p>Bisogna assolutamente evitare codice come il seguente:</p>
+<pre class="literal-block">
+try:
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+except:
+ bla-bla
+</pre>
+<p>Nel blocco <tt class="docutils literal"><span class="pre">try</span></tt> dev'esserci la cosa più semplice possibile, per
+limitare i tipi di eccezione che possono nascere. Inoltre l'except nudo
+e' orribile perchè trappa qualunque cosa, anche quello che non vorreste.
+La cosa giusta da fare è del tipo:</p>
+<pre class="literal-block">
+try:
+ bla-bla
+except MyException, e: # sempre specificare l'eccezione aspettata
+ print e
+except OtherException,e:
+ print e
+...
+</pre>
+<p>Non trappate l'inaspettato, altrimenti non capirete mai qual è stata
+l'origine di un problema.</p>
+</div>
+<div class="section" id="il-try-finally-una-grande-idea">
+<h2><a class="toc-backref" href="#id20" name="il-try-finally-una-grande-idea">Il try .. finally è una grande idea</a></h2>
+<p>Molto spesso quello che volete non è tanto il try .. except, quanto il
+try .. finally: il vantaggio del try .. finally è che
+<em>non vi nasconde l'eccezione</em> e nello stesso tempo vi <em>garantisce che quello
+che deve essere chiuso correttamente venga chiuso correttamente</em> in ogni caso.
+C'è un eccezione a questa regola: se ammazzate un processo di brutto con un
+kill, il try .. finally non può salvarvi. Il try .. finally vi
+salva per tutte le eccezioni Python, compreso il CTRL-C (KeyboardInterrupt)
+e il sys.exit() (SystemExit).</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; try:
+... raise RuntimeError(&quot;Ahia&quot;)
+... finally:
+... print &quot;Io vengo eseguito SEMPRE, anche se c'è un'eccezione!&quot;
+...
+Io vengo eseguito SEMPRE, anche se c'e' un'eccezione!
+Traceback (most recent call last):
+ File '&lt;stdin&gt;', line 2, in ?
+RuntimeError: Ahia
+</pre>
+</div>
+<div class="section" id="uso-di-assert">
+<h2><a class="toc-backref" href="#id21" name="uso-di-assert">Uso di assert</a></h2>
+<p>Per asserire che una condizione è verificata con certezza:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def div2(x):
+... assert isinstance(x, int), '%s non è un numero intero' % x
+... return x/2
+...
+&gt;&gt;&gt; div2(14)
+7
+&gt;&gt;&gt; div2(14.0)
+Traceback (most recent call last):
+ File '&lt;stdin&gt;', line 1, in ?
+ File '&lt;stdin&gt;', line 2, in div2
+AssertionError: 14.0 non è un numero intero
+</pre>
+<p>Tipicamente si usa in &quot;sanity checks&quot;, per essere sicuri che un parametro
+sia esattamente quello che ci si aspetta, in casi di eccezioni gravi; se
+l'assert non è rispettato, tutto il programma deve bloccarsi.</p>
+</div>
+<div class="section" id="non-usate-exec">
+<h2><a class="toc-backref" href="#id22" name="non-usate-exec">Non usate exec</a></h2>
+<p>'exec' è un costrutto pericolosissimo che va riservato solo a chi sa
+cosa sta facendo. Spesso e volentieri si usa soltanto per ignoranza
+dell'esistenza di una soluzione migliore.</p>
+<p>Esempio:</p>
+<pre class="literal-block">
+exec file(&quot;myscript.py&quot;).read()
+</pre>
+<p>è UN ERRORE GRAVE per vari motivi.</p>
+<p>In primo luogo, in caso di eccezioni sollevate in 'myscript.py'
+perdete informazione su dove si trova l'errore (cioè nel file &quot;myscript.py&quot;)
+e rendete impossibile la vita al debugger; in secondo luogo, sporcate il
+namespace del vostro programma in maniera potenzialmente pericolosa. La cosa
+giusta da fare è:</p>
+<pre class="literal-block">
+dic = {}
+execfile(&quot;myscript.py&quot;, dic)
+</pre>
+<p>che non perde informazioni e non sporca il namespace, perchè i nomi
+definiti in myscript.py verranno confinati nel dizionario. Leggetevi
+la documentazione di 'execfilè per saperne di più.</p>
+</div>
+<div class="section" id="come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate">
+<h2><a class="toc-backref" href="#id23" name="come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate">Come far partire pdb automaticamente in caso di eccezioni inaspettate</a></h2>
+<p>[Illustrato al corso 1]</p>
+<pre class="literal-block">
+#&lt;exc_debug.py&gt;
+
+# recipe in the Python cookbook first edition, chapter 14.5
+
+import sys
+
+def info(type, value, tb):
+ if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \
+ type == SyntaxError:
+ # we are in interactive mode or we don't have a tty-like
+ # device, so we call the default hook
+ sys.__excepthook__(type, value, tb)
+ else:
+ import traceback, pdb
+ # we are NOT in interactive mode, print the exception...
+ traceback.print_exception(type, value, tb)
+ print
+ # ...then start the debugger in post-mortem mode.
+ pdb.pm()
+
+sys.excepthook = info
+
+#&lt;/exc_debug.py&gt;
+</pre>
+<p>Se un programma importa <tt class="docutils literal"><span class="pre">exc_debug</span></tt>, il Python debugger partirà
+automaticamente in caso di eccezioni non trappate. Per esempio eseguire</p>
+<pre class="literal-block">
+#&lt;example.py&gt;
+import exc_debug
+a = 1
+b = 0
+a/b
+#&lt;/example.py&gt;
+</pre>
+<p>fa partire il debugger:</p>
+<pre class="literal-block">
+$ python example.py
+Traceback (most recent call last):
+ File &quot;example.py&quot;, line 4, in ?
+ a/b
+ZeroDivisionError: integer division or modulo by zero
+
+&gt; /mnt/hda2/cygwin/home/micheles/md/pypers/marelli/materiale/example.py(4)?()
+-&gt; a/b
+(Pdb) print a
+1
+(Pdb) print b
+0
+Pdb) !b = 1 # cambia il valore di b
+(Pdb) print a/b
+1
+</pre>
+<p>Date <tt class="docutils literal"><span class="pre">help</span> <span class="pre">pdb</span></tt> dall'interno del debugger per avere informazioni sul suo
+funzionamento.</p>
+</div>
+<div class="section" id="eccezioni-e-threads">
+<h2><a class="toc-backref" href="#id24" name="eccezioni-e-threads">Eccezioni e threads</a></h2>
+<p>Un'eccezione non trappata blocca soltanto il thread in cui si è verificata,
+NON tutto il programma:</p>
+<pre class="literal-block">
+#&lt;esempio1.py&gt;
+
+import threading, time, sys
+
+def print_hello():
+ for i in range(10):
+ print &quot;hello&quot;
+ if i == 5:
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+ time.sleep(1)
+
+def print_world():
+ for i in range(10):
+ print &quot;world&quot;
+ time.sleep(1)
+
+threading.Thread(target=print_hello).start()
+threading.Thread(target=print_world).start()
+
+#&lt;/esempio1.py&gt;
+</pre>
+<p>dà come output:</p>
+<pre class="literal-block">
+$ python esempio1.py
+hello
+world
+hello
+world
+hello
+world
+hello
+world
+hello
+world
+hello
+world
+Exception in thread Thread-1:
+Traceback (most recent call last):
+ File &quot;/usr/lib/python2.4/threading.py&quot;, line 442, in __bootstrap
+ self.run()
+ File &quot;/usr/lib/python2.4/threading.py&quot;, line 422, in run
+ self.__target(*self.__args, **self.__kwargs)
+ File &quot;esempio1.py&quot;, line 7, in print_hello
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+RuntimeError: Problema a runtime
+
+world
+world
+world
+world
+</pre>
+<p>Quindi uno è costretto a implementare un meccanismo di controllo, tipo
+il seguente:</p>
+<pre class="literal-block">
+import threading, time, sys
+
+END = False
+
+def print_hello():
+ global END
+ i = 0
+ while not END:
+ i += 1
+ print &quot;hello&quot;
+ if i == 5:
+ try:
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+ except RuntimeError, e:
+ END = True
+ time.sleep(1)
+
+def print_world():
+ i = 0
+ while not END:
+ print &quot;world&quot;
+ time.sleep(1)
+
+threading.Thread(target=print_hello).start()
+threading.Thread(target=print_world).start()
+</pre>
+<p>Questa è una soluzione artigianale, che usa una variabile globale, sfruttando
+il fatto che le variabili globali sono condivise fra tutti i thread (questo
+può anche causare danni, se non si sta attenti).</p>
+<p>La strada consigliata per comunicare fra threads è quella di usare una coda
+[esempio svolto al corso 1]:</p>
+<pre class="literal-block">
+&quot;Esempio di due threads comunicanti tramite una queue.&quot;
+
+import time, threading, Tkinter, Queue
+
+queue = Queue.Queue()
+
+def print_hello():
+ for i in range(10):
+ try:
+ messaggio = queue.get_nowait()
+ except Queue.Empty:
+ pass
+ else:
+ if messaggio == &quot;terminate&quot;: break
+ print &quot;%s, hello&quot; % i
+ time.sleep(1)
+
+def print_world():
+ for i in range(10):
+ print &quot;%s, world&quot; % i
+ if i == 5:
+ queue.put(&quot;terminate&quot;) # manda il messaggio di terminazione
+ root.quit()
+ raise RuntimeError(&quot;Errore nel thread print_world!&quot;)
+ time.sleep(1)
+
+root = Tkinter.Tk()
+
+for func in print_hello, print_world:
+ th = threading.Thread(target=func)
+ th.start()
+
+root.mainloop()
+</pre>
+<p>Questo esempio fa anche vedere che chiudere la finestrella grafica NON
+uccide i threads che stanno girando.</p>
+<p>I meccanismi di controllo per bloccare i thread sono notoriamente fragili
+e piccoli errori possono farvi grossi danni.</p>
+<p>Debuggare i threads è notoriamente un macello.</p>
+<p>L'unica soluzione vera è evitare i threads quando è possibile.</p>
+</div>
+</div>
+<div class="section" id="modulo-4-sviluppo-orientato-ai-test">
+<h1><a class="toc-backref" href="#id25" name="modulo-4-sviluppo-orientato-ai-test">Modulo 4. Sviluppo orientato ai test</a></h1>
+<p><em>Come scrivere software con tecnologie agili, con lo scopo di ridurre e
+tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest.
+Esempi di programmazione test driven.</em></p>
+<div class="section" id="consigli-di-carattere-generale">
+<h2><a class="toc-backref" href="#id26" name="consigli-di-carattere-generale">Consigli di carattere generale</a></h2>
+<ul class="simple">
+<li>testate SUBITO, non debuggate dopo (ad ogni una riga di codice che si
+scrive, si testa che funzioni e che non distrugga quello che funzionava
+prima).</li>
+<li>scrivete software in maniera che sia testabile (per esempio create
+sempre anche una versione non-grafica di un programma grafico, perchè
+la versione testuale si testa <strong>molto</strong> più facilmente).</li>
+<li>non abbiate paura a scrivervi un vostro ambiente di test personalizzato.</li>
+<li>tenete conto che unittest e doctest esistono e possono aiutarvi infinitamente
+nel gestire i vostri test.</li>
+</ul>
+</div>
+<div class="section" id="usare-unittest">
+<h2><a class="toc-backref" href="#id27" name="usare-unittest">Usare unittest</a></h2>
+<p>Tipicamente con unittest si divide la libreria da testare:</p>
+<pre class="literal-block">
+#&lt;isnumber.py&gt;
+
+def is_number(arg):
+ &quot;Verifica se la stringa arg è un numero valido&quot;
+ try:
+ float(arg)
+ except ValueError:
+ return False
+ else:
+ return True
+
+#&lt;/isnumber.py&gt;
+</pre>
+<p>dal file di test, che convenzionalmente ha un nome che inizia per &quot;test&quot;:</p>
+<pre class="literal-block">
+#&lt;test_isnumber.py&gt;
+
+import unittest
+
+from isnumber import is_number
+
+class TestIsNumber(unittest.TestCase):
+
+ def setUp(self):
+ print &quot;sto inizializzando&quot;
+
+ # test positivi
+ def test_1(self):
+ &quot;Testa che '1' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;1&quot;))
+ def test_2(self):
+ &quot;Testa che '1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;1.3&quot;))
+ def test_3(self):
+ &quot;Testa che '+1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;+1.3&quot;))
+ def test_4(self):
+ &quot;Testa che '-1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;-1.3&quot;))
+
+ # test negativi
+ def test_5(self):
+ &quot;Testa che '1-.3' non è un numero buono.&quot;
+ self.assertFalse(is_number(&quot;1-.3&quot;))
+ def test_6(self):
+ &quot;Testa che 'à non è un numero buono.&quot;
+ self.assertFalse(is_number(&quot;a&quot;))
+ def test_7(self):
+ &quot;Testa che '42' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;42&quot;))
+
+ def tearDown(self):
+ print &quot;Sto chiudendo quello che c'è da chiudere&quot;
+
+if __name__ == &quot;__main__&quot;:
+ unittest.main()
+
+#&lt;/test_isnumber.py&gt;
+</pre>
+<p>Eseguire i tests con l'opzione verbose da:</p>
+<pre class="literal-block">
+$ python test_isnumber.py -v
+Testa che '1' è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che '1.3' è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che '+1.3' è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che '-1.3' è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che '1-.3' non è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che 'à non è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+Testa che '42' è un numero buono. ... sto inizializzando
+Sto chiudendo quello che c'è da chiudere
+ok
+
+----------------------------------------------------------------------
+Ran 7 tests in 0.001s
+
+OK
+</pre>
+<p>Questo mostra che i metodi <tt class="docutils literal"><span class="pre">setUp</span></tt> e <tt class="docutils literal"><span class="pre">tearDown</span></tt> vengono chiamati
+<em>per ogni test</em>, quindi tutti i test si svolgono in un ambiente pulito.</p>
+<p>E' normale avere un file di test più lungo della libreria da testare.</p>
+<p>E' possibile specificare le eccezioni aspettate:</p>
+<pre class="literal-block">
+#&lt;test_exc.py&gt;
+
+import unittest
+
+def divide(a, b):
+ return a/b
+
+class TestIsNumber(unittest.TestCase):
+ def test_1(self):
+ &quot;Divide 4/2&quot;
+ self.assertEqual(divide(4,2), 2)
+ def test_2(self):
+ &quot;Divide 4/0&quot;
+ self.assertRaises(ZeroDivisionError, divide, 4, 0)
+
+
+if __name__ == &quot;__main__&quot;:
+ unittest.main()
+
+#&lt;/test_exc.py&gt;
+
+$ python test_exc.py -v
+Divide 4/2 ... ok
+Divide 4/0 ... ok
+
+----------------------------------------------------------------------
+Ran 2 tests in 0.001s
+
+OK
+</pre>
+</div>
+<div class="section" id="usare-doctest">
+<h2><a class="toc-backref" href="#id28" name="usare-doctest">Usare doctest</a></h2>
+<p>L'uso più semplice, eseguire i doctest che si trovano nelle stringhe
+di documentazione di un modulo:</p>
+<pre class="literal-block">
+#&lt;esempio_banale.py&gt;
+
+def sum12():
+ &quot;&quot;&quot;Questa funzione ritorna la somma di 1 + 2::
+ &gt;&gt;&gt; sum12()
+ 3&quot;&quot;&quot;
+ return 1+2
+
+if __name__ == &quot;__main__&quot;:
+ import doctest; doctest.testmod()
+
+#&lt;/esempio_banale.py&gt;
+</pre>
+<p>Ecco come eseguire i tests con l'opzione verbose:</p>
+<pre class="literal-block">
+$ python esempio_banale.py -v
+Trying:
+ sum12()
+Expecting:
+ 3
+ok
+1 items had no tests:
+ __main__
+1 items passed all tests:
+ 1 tests in __main__.sum12
+1 tests in 2 items.
+1 passed and 0 failed.
+Test passed.
+</pre>
+<p>Eseguire i doctest che si trovano in un file di testo separato (nuova
+funzionalità di Python 2.4):</p>
+<pre class="literal-block">
+#&lt;doctest_runner.py&gt;
+
+import doctest
+
+if __name__== &quot;__main__&quot;:
+ doctest.testfile(&quot;test_isnumber.txt&quot;)
+
+#&lt;/doctest_runner.py&gt;
+</pre>
+<p>Contenuto di 'test_isnumber.txt':</p>
+<pre class="literal-block">
+Questa è la documentazione della funzione isnumber
+====================================================
+
+Esempi di uso:
+
+&gt;&gt;&gt; from isnumber import is_number
+&gt;&gt;&gt; is_number(&quot;1&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;+1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;-1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;1-.3&quot;)
+False
+&gt;&gt;&gt; is_number(&quot;a&quot;)
+False
+&gt;&gt;&gt; is_number(&quot;42&quot;)
+True
+&gt;&gt;&gt; 1/0
+Traceback (most recent call last):
+ kkkkdjjfkf
+ZeroDivisionError: integer division or modulo by zero
+</pre>
+<p>Eseguire i tests:</p>
+<pre class="literal-block">
+$ python doctest_runner.py -v
+Trying:
+ from isnumber import is_number
+Expecting nothing
+ok
+Trying:
+ is_number(&quot;1&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;+1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;-1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;1-.3&quot;)
+Expecting:
+ False
+ok
+Trying:
+ is_number(&quot;a&quot;)
+Expecting:
+ False
+ok
+Trying:
+ is_number(&quot;42&quot;)
+Expecting:
+ True
+ok
+Trying:
+ 1/0
+Expecting:
+ Traceback (most recent call last):
+ kkkkdjjfkf
+ ZeroDivisionError: integer division or modulo by zero
+ok
+1 items passed all tests:
+ 9 tests in test_isnumber.txt
+9 tests in 1 items.
+9 passed and 0 failed.
+Test passed.
+</pre>
+<p>Confrontare la leggibilità di <tt class="docutils literal"><span class="pre">test_isnumber.txt</span></tt> con la leggibilità
+di <tt class="docutils literal"><span class="pre">test_isnumber.py</span></tt>, basato su unittest. Leggetevi il mio seminario su
+doctest (in allegato) per convincervi che doctest è il migliore. Anche perchè
+i doctest possono essere convertiti in unittest automaticamente, a partire
+da Python 2.4</p>
+<p>Per convertire i test contenuti in 'mymodulè da doctest a unittest:</p>
+<pre class="literal-block">
+import doctest, unittest, mymodule
+
+if __name__== &quot;__main__&quot;:
+ suite = doctest.DocTestSuite(mymodule)
+ unittest.TextTestRunner(verbosity=2).run(suite)
+</pre>
+<p>E' anche possibile contenere i tests contenuti in un file di
+tipo testo da doctest a unittest, vedere la documentazione.</p>
+<p>Correntemente, doctest non ha un meccanismo predefinito corrispondente
+ai metodi <tt class="docutils literal"><span class="pre">setUp</span></tt> e <tt class="docutils literal"><span class="pre">tearDown</span></tt> di unittest, ma potete impostarlo a mano.</p>
+</div>
+<div class="section" id="un-esempio-di-programma-sviluppato-in-maniera-incrementale">
+<h2><a class="toc-backref" href="#id29" name="un-esempio-di-programma-sviluppato-in-maniera-incrementale">Un esempio di programma sviluppato in maniera incrementale</a></h2>
+<p>Il seguente esempio svolto al corso intendeva dimostrare:</p>
+<ol class="arabic simple">
+<li>Come si sviluppa un programma in maniera incrementale (ad ogni nuova riga di
+codice si fa una verifica immediata del funzionamento dell'insieme);</li>
+<li>Come si fanno scelte architetturali in maniera tale da assicurare la
+testabilità del prodotto finale in maniera semplice ed automatica;</li>
+<li>Come sfruttare la libreria standard di Python al meglio, usando il
+modulo cmd;</li>
+<li>Come dividere il codice in metodi pubblici, metodi di utilità e
+metodi di debugging;</li>
+<li>Come assicurarsi che il programma venga chiuso propriamente, anche
+nel caso di eccezioni impreviste e imprevedibili;</li>
+</ol>
+<pre class="literal-block">
+# -*- encoding: latin-1 -*-
+import os, cmd, subprocess, lib, time # lib fissa os.kill
+from pywintypes import error as WindowsProcessDidNotStartCorrectly
+
+MUSICDIR = &quot;C:/Documents and Settings/micheles/Desktop/Music&quot;
+
+def play_song(name):
+ po = subprocess.Popen([&quot;mplay32&quot;, &quot;/play&quot;, &quot;/close&quot;, name])
+ return po.pid
+
+class InterpreteDiComandi(cmd.Cmd):
+ cwd = MUSICDIR
+ prompt = &quot;Player&gt; &quot;
+ def preloop(self):
+ self.process_list = []
+ os.chdir(MUSICDIR)
+
+ def do_play(self, arg):
+ &quot;Suona una canzone&quot;
+ if not arg:
+ print &quot;Per favore scrivi il nome di una canzone!&quot;
+ else:
+ self.process_list.append(play_song(arg))
+
+ def do_quit(self, dummy):
+ &quot;Esce dall'interprete.&quot;
+ return True
+
+ def safe_kill(self, pid):
+ try:
+ os.kill(pid)
+ except WindowsProcessDidNotStartCorrectly, e:
+ print e
+
+ def do_kill(self, arg):
+ &quot;Uccide il player&quot;
+ try:
+ pid = self.process_list.pop()
+ except IndexError:
+ print &quot;Hai già ucciso tutti i processi!&quot;
+ else:
+ self.safe_kill(pid)
+
+ def postloop(self):
+ for pid in self.process_list:
+ self.safe_kill(pid)
+ print &quot;Ho ucciso tutto!&quot;
+ os.chdir(os.path.dirname(os.path.abspath(__file__)))
+
+ # metodi che possono essere utili per il debugging
+ def do_print_dir(self, arg):
+ &quot;Comando utile per il debugging&quot;
+ print self.cwd
+
+ def do_raise_exc(self, dummy):
+ raise RuntimeError(&quot;Tanto per vedere che succede&quot;)
+
+ def do_sleep(self, arg):
+ &quot;utile per vedere quello che i test automatici stanno facendo&quot;
+ time.sleep(int(arg))
+
+i = InterpreteDiComandi()
+
+try:
+ i.cmdloop()
+finally: # assicura la chiusura anche in caso di eccezione
+ i.postloop()
+</pre>
+<p>Nel caso regolare (senza eccezioni impreviste) .postloop è chiamato 2 volte,
+ma questo non fa danno.</p>
+<p>Questo potrebbe essere il codice corrispondente ad un test automatico:</p>
+<pre class="literal-block">
+$ more test_player.cmd
+play
+sleep 2
+play 2. Croi Croga.mp
+sleep 2
+play 2. Croi Croga.mp3
+sleep 2
+kill
+sleep 2
+quit
+</pre>
+<p>(la prima canzone non esiste, in modo da poter vedere come viene segnalato
+l'errore) che verrebbe eseguito così:</p>
+<pre class="literal-block">
+$ python player.py &lt; test_player.cmd
+</pre>
+</div>
+</div>
+<div class="section" id="modulo-5-design-documentazione-e-manutenzione-di-librarie">
+<h1><a class="toc-backref" href="#id30" name="modulo-5-design-documentazione-e-manutenzione-di-librarie">Modulo 5: Design, documentazione e manutenzione di librarie</a></h1>
+<p><em>Pratiche di programmazione &quot;in the large&quot;. Moduli, packages, strumenti di
+documentazione e di installazione. Applicazioni pratiche di principi generali
+quali disaccoppiamento, modularità, non duplicazione del codice.</em></p>
+<div class="section" id="la-filosofia-del-python">
+<h2><a class="toc-backref" href="#id31" name="la-filosofia-del-python">La filosofia del Python</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; import this
+The Zen of Python, by Tim Peters
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it.
+Although that way may not be obvious at first unless you are Dutch.
+Now is better than never.
+Although never is often better than *right* now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!
+</pre>
+</div>
+<div class="section" id="principio-del-disaccoppiamento">
+<h2><a class="toc-backref" href="#id32" name="principio-del-disaccoppiamento">Principio del disaccoppiamento</a></h2>
+<p>Questo è il principio guida di tutta la programmazione e non solo, tutto
+il resto nasce di conseguenza.</p>
+<p>Il principio dice: <strong>cercate di disaccoppiare il più possibile
+le componenti del vostro sistema</strong>.</p>
+<p><em>Un componente si dice disaccoppiato se
+può essere rimosso o sostituito senza danneggiare il resto del sistema.</em></p>
+<p>Per esempio:</p>
+<ul class="simple">
+<li>disaccoppiare l'ambiente di sviluppo dall'ambiente di esecuzione (è più
+sicuro lanciare un programma dalla riga di comando che dall' IDE; un
+debugger disaccoppiato come pdb è più sicuro di un debugger integrato
+come quello di PythoWin, un editor vero è più sicuro dell'editor dell'
+IDE, etc.)</li>
+<li>disaccoppiare l'interfaccia grafica: il programma deve poter funzionare
+senza di essa, o sostituendo il GUI toolkit con un altro.</li>
+<li>i threads vi accoppiano tutto, se uno va male può bloccare tutti gli
+altri: evitateli se potete.</li>
+<li>l'ereditarietà vi accoppia il parente al figlio (per sapere quello che
+fa il figlio bisogna andare a vedere quello che fanno il padre, il nonno,
+il bisnonno, il trisavolo, etc.). Evitatela se potete.</li>
+<li>la modularità è un modo concreto per applicare il principio del
+disaccoppiamento al codice: funzioni che logicamente vanno insieme,
+vanno messe in un modulo comune, funzioni separate vanno separate.
+Se c'è un problema nel modulo X, questo non deve interferire con
+il modulo Y (almeno in un mondo ideale). La modularità rende anche
+più semplice rimpiazzare un modulo sbagliato o vecchio con uno
+corretto o più recente.</li>
+<li>la non-duplicazione del codice è una conseguenza della modularità:
+avendo raggruppato le funzioni di uso generale in un modulo comune,
+si acquista in concisione, semplicità, leggibilità, debuggabilità,
+si evita di correggere lo stesso errore più volte, e in generale tutto
+diviene più facile da mantenere.</li>
+</ul>
+</div>
+<div class="section" id="principio-del-kiss">
+<h2><a class="toc-backref" href="#id33" name="principio-del-kiss">Principio del KISS</a></h2>
+<p><em>Keep it simple, stupid</em>. In Italiano: <em>non facciamoci del male</em>.</p>
+<p>Se potete fare le cose in maniera semplice, fatele in maniera semplice.</p>
+<p>Chiedetevi sempre se una cosa vi serve veramente oppure no.</p>
+</div>
+<div class="section" id="importanza-di-avere-un-prototipo">
+<h2><a class="toc-backref" href="#id34" name="importanza-di-avere-un-prototipo">Importanza di avere un prototipo</a></h2>
+<p>Non c'è nulla di più sbagliato che partire scrivendosi l'architettura di
+un progetto complesso sulla carta. Si parte sempre scrivendo un prototipo,
+<em>testato sul campo di battaglia</em>, che magari ha l'1% delle funzionalità
+che vi interessano, ma che <em>funziona</em>. A quel punto è possibile decidere
+l'architettura e il prototipo diventerà il core e la parte più testata
+e sicura della vostra applicazione.</p>
+<p>Il prototipo/core deve <em>rimuovere tutto l'inessenziale</em>. Più povero è,
+meglio è.</p>
+</div>
+<div class="section" id="moduli-e-packages">
+<h2><a class="toc-backref" href="#id35" name="moduli-e-packages">Moduli e packages</a></h2>
+<p>Qualunque file Python può essere visto come un modulo. Un package invece,
+è una directory contente un file <tt class="docutils literal"><span class="pre">__init__.py</span></tt> che contiene il codice di
+inizializzazione (<tt class="docutils literal"><span class="pre">__init__.py</span></tt> può benissimo essere vuoto, oppure può
+consistere solo di 'import' dei moduli di quel package).</p>
+<p>Importare un package non importa automaticamente tutti i suoi moduli,
+a meno che questo non sia detto esplicitamente nell' <tt class="docutils literal"><span class="pre">__init__.py</span></tt>.</p>
+<p>I package possono essere nidificati senza limite.</p>
+<p>Le variabili globali di un modulo sono locali a quel modulo, quindi
+non c'è mai il rischio di fare danni (a meno che un modulo non importi
+esplicitamente una variabile corrispondente ad un oggetto mutabile e
+la cambi esplicitamente).</p>
+<p>E' possibile mettere un package nel Python path automaticamente per
+tutti gli utenti listando il nome del package in un file <tt class="docutils literal"><span class="pre">.pth</span></tt> nella
+directory site-packages (vedere la documentazione di distutils).</p>
+</div>
+<div class="section" id="come-si-documenta-una-libreria-python">
+<h2><a class="toc-backref" href="#id36" name="come-si-documenta-una-libreria-python">Come si documenta una libreria Python</a></h2>
+<p>Python è un linguaggio &quot;self-documenting&quot; grazie alle doctrings.
+Usatele. In più guardate come è documentato un progetto Python
+tipico (io suggerisco di guardare a <em>docutils</em>) e copiate le convenzioni
+usate lì. Ci sono dei file standard come il README.txt, l'HISTORY.txt,
+il BUGS.txt, una directory docs, una directory test, un file setup.py,
+etc.</p>
+<p>Tenete presente che grazie a <em>doctest</em> potete inserire dei test automatici
+all'interno della vostra documentazione.</p>
+</div>
+</div>
+<div class="section" id="modulo-6-domande-estemporanee">
+<h1><a class="toc-backref" href="#id37" name="modulo-6-domande-estemporanee">Modulo 6: Domande estemporanee</a></h1>
+<p><em>Risponderò alle domande dell'audience, anche al di fuori dal programma,
+se di interesse generale</em>.</p>
+<div class="section" id="come-funziona-import">
+<h2><a class="toc-backref" href="#id38" name="come-funziona-import">Come funziona 'import'</a></h2>
+<p>Se un modulo è già stato importato e chiamate 'import' una seconda
+volta, il modulo NON viene importato due volte. Questo può essere
+un problema nell'interprete interattivo. Se importate un modulo,
+poi lo modificate e lo importate di nuovo, vi resterà in memoria
+la copia <em>vecchia</em>, quella non modificata. La soluzione è usare
+'reload', che vi importerà veramente la nuova versione.</p>
+</div>
+<div class="section" id="come-funziona-del">
+<h2><a class="toc-backref" href="#id39" name="come-funziona-del">Come funziona '__del__'</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; a = 1
+&gt;&gt;&gt; del a
+</pre>
+<p>vi cancella il nome dal namespace:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+NameError: name 'a' is not defined
+</pre>
+<p>Tuttavia l'oggetto cui si riferisce <em>non viene cancellato immediatamente</em>.
+Verrà cancellato dal garbage collector <em>quando serve</em>, ma soltanto
+<em>se non vi sono altre referenze</em> all'oggetto da qualche altra parte.
+Inoltre per oggetti C, dovrete definirvi un metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> custom
+che ha accesso all'oggetto a livello C. Tenete anche conto che tipicamente
+il debugger tiene referenze agli oggetti aggiuntive, quindi in modalità
+di debugger i problemi con oggetti non cancellati peggiorano.</p>
+<p>Ecco un esempio di un metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> custom piuttosto banale:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; del c
+Hai usato del
+</pre>
+<p>Il metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> viene chiamato automaticamente all'uscita
+dall'interprete:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; &lt;CTRL-Z&gt; per uscire dall'interprete
+Hai usato del
+</pre>
+<p>In principio, all'uscita del programma Python
+<em>dovrebbe chiamare ``__del__`` automaticamente anche se vi sono eccezioni</em>:</p>
+<pre class="literal-block">
+#&lt;del_with_exc.py&gt;
+
+class C(object):
+ def __del__(self):
+ print &quot;Hai chiamato del&quot;
+
+c = C()
+raise RuntimeError(&quot;Ahi ahi!&quot;)
+
+#&lt;/del_with_exc.py&gt;
+
+$ python del_with_exc.py
+Traceback (most recent call last):
+ File &quot;del_with_exc.py&quot;, line 6, in ?
+ raise RuntimeError(&quot;Ahi ahi!&quot;)
+RuntimeError: Ahi ahi!
+Hai chiamato del
+</pre>
+<p>Tuttavia per oggetti <tt class="docutils literal"><span class="pre">C</span></tt> ed eccezioni le cose possono essere delicate ed
+è sempre meglio chiamare <tt class="docutils literal"><span class="pre">del</span></tt> <em>esplicitamente</em>, magari in un
+<tt class="docutils literal"><span class="pre">try</span> <span class="pre">..</span> <span class="pre">finally</span></tt>.</p>
+</div>
+<div class="section" id="che-differenza-c-fra-variabili-di-classe-e-di-istanza">
+<h2><a class="toc-backref" href="#id40" name="che-differenza-c-fra-variabili-di-classe-e-di-istanza">Che differenza c'è fra variabili di classe e di istanza</a></h2>
+<p>Questo esempio dovrebbe chiarire la differenza:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... pippo = 'sono una variabile di classe'
+... def __init__(self):
+... self.poppi = 'sono una variabile di istanza'
+...
+</pre>
+<p>Le variabili di classe sono le stesse per tutte le istanze:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c1 = C()
+&gt;&gt;&gt; c2 = C()
+&gt;&gt;&gt; c1.pippo
+'sono una variabile di classe'
+&gt;&gt;&gt; c2.pippo
+'sono una variabile di classe'
+</pre>
+<p>Se cambio una variabile di classe, cambia per tutte le istanze, anche
+per quelle che sono state create <em>prima</em> del cambiamento:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.pippo = 'adesso la cambio'
+&gt;&gt;&gt; c1.pippo
+'adesso la cambio'
+&gt;&gt;&gt; c2.pippo
+'adesso la cambio'
+</pre>
+<p>Lo stesso vale per i metodi, che non sono altro che variabili di classe.</p>
+<p>Le variabili di istanza invece sono indipendenti:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c1.poppi
+'sono una variabile di instanza'
+&gt;&gt;&gt; c2.poppi
+'sono una variabile di instanza'
+&gt;&gt;&gt; c1.poppi = 'cambio la variabile dell'istanza c1'
+&gt;&gt;&gt; c1.poppi
+'cambio la variabile dell'istanza c1'
+&gt;&gt;&gt; c2.poppi # questa rimane quella che era prima
+'sono una variabile di instanza'
+</pre>
+</div>
+<div class="section" id="che-cosa-sono-i-metodi-che-iniziano-con">
+<h2><a class="toc-backref" href="#id41" name="che-cosa-sono-i-metodi-che-iniziano-con">Che cosa sono i metodi che iniziano con &quot;__&quot;</a></h2>
+<p>Sono metodi protetti. Sono utili per evitare rogne con l'ereditarietà.
+Per capire il problema dò un esempio semplice qui di seguito.</p>
+<p>Normalmente l'ereditarietà accoppia il padre con il figlio, nel senso che
+il figlio può sovrascrivere i metodi del padre, e i metodi del padre andranno
+automaticamente a chiamare i metodi del figlio:</p>
+<pre class="literal-block">
+class B(object): # padre
+ def __init__(self):
+ self.hello()
+ def hello(self):
+ print &quot;hello!&quot;
+
+class C(B): # figlio
+ def hello(self):
+ print &quot;cucu!&quot;
+
+b = B() # stampa 'hello!'
+c = C() # stampa 'cucu!'
+</pre>
+<p>In questo esempio l'__init__ del padre chiama l'hello del figlio. Tuttavia,
+a volte uno vuole essere sicuro che l'__init__ del padre chiami l'hello
+del padre, e non quello del figlio (sempre per via del principio del
+disaccoppiamento). Per questo ci sono i <em>metodi protetti</em>:</p>
+<pre class="literal-block">
+class B(object):
+ def __init__(self): # call the '__hello' method in THIS class
+ self.__hello()
+ def __hello(self):
+ print &quot;hello!&quot;
+
+class C(B):
+ def __hello(self): # won't be called by B.__init__
+ print &quot;cucu!&quot;
+
+b = B() # stampa 'hello!'
+c = C() # stampe 'hello!'
+</pre>
+<p>I metodi protetti di tipo <tt class="docutils literal"><span class="pre">__&lt;nome</span> <span class="pre">metodo&gt;</span></tt> non vanno confusi con i metodi
+privati di tipo <tt class="docutils literal"><span class="pre">_&lt;nome</span> <span class="pre">metodo&gt;</span></tt> (un solo underscore):</p>
+<ol class="arabic">
+<li><p class="first">i metodi privati non andrebbero chiamati mai, se non sapendo al 100% quello
+che si sta facendo, e solo in caso di errori nel design della libreria che
+si sta utilizzando;</p>
+</li>
+<li><p class="first">i metodi protetti possono essere chiamati con tranquillità. Proprio
+perchè sono protetti, c'è la garanzia che sovrascrivendoli nella
+sottoclasse non si va ad alterare il comportamento ereditato dal padre
+(nel nostro esempio l'<tt class="docutils literal"><span class="pre">__init__</span></tt> del padre continuerà a chiamare il
+proprio <tt class="docutils literal"><span class="pre">__hello</span></tt>, non l'<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio). D'altra parte i
+metodi definiti nel figlio vedranno solo l'<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio e
+non quello del padre.</p>
+</li>
+<li><p class="first">i metodi protetti non possono essere chiamati accidentalmente, perchè
+per esempio <tt class="docutils literal"><span class="pre">c.__hello()</span></tt> non funziona. Tuttavia è possibile chiamarli,
+quindi non sono veramente privati: nel nostro esempio è possibile
+chiamare</p>
+<p><tt class="docutils literal"><span class="pre">c._B__hello</span></tt> (<tt class="docutils literal"><span class="pre">__hello</span></tt> del padre) oppure
+<tt class="docutils literal"><span class="pre">c._C__hello</span></tt> (<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio)</p>
+<p>Siccome bisogna specificare il nome della classe dove il metodo è definito
+non c'è il rischio di sbagliarsi (a meno di non essere masochisti e di
+non dare lo stesso nome al padre e al figlio).</p>
+</li>
+</ol>
+<p>I metodi protetti vengono usati raramente. Vedere &quot;Python in a Nutshell&quot;
+per maggiori informazioni sul loro uso.</p>
+</div>
+</div>
+<div class="section" id="soluzioni-al-questionario-di-ammissione">
+<h1><a class="toc-backref" href="#id42" name="soluzioni-al-questionario-di-ammissione">Soluzioni al questionario di ammissione</a></h1>
+<div class="section" id="scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero">
+<h2><a class="toc-backref" href="#id43" name="scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero">Scrivere un programma che testa se una stringa rappresenta un numero</a></h2>
+<p>Soluzione standard (vedere anche il tutorial di Python):</p>
+<pre class="literal-block">
+try:
+ int(x)
+except ValueError:
+ print &quot;This is not a number!&quot;
+</pre>
+<p>Soluzione usando tkSimpleDialog:</p>
+<pre class="literal-block">
+from tkSimpleDialog import askfloat
+askfloat(&quot;Enter a number&quot;, &quot;Number:&quot;)
+</pre>
+</div>
+<div class="section" id="scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente">
+<h2><a class="toc-backref" href="#id44" name="scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente">Scrivere un programma che lista tutti i files nella directory corrente</a></h2>
+<pre class="literal-block">
+for f in os.listdir(&quot;.&quot;):
+ print f
+</pre>
+</div>
+<div class="section" id="listare-tutti-i-files-nelle-sottodirectories-ricorsivamente">
+<h2><a class="toc-backref" href="#id45" name="listare-tutti-i-files-nelle-sottodirectories-ricorsivamente">Listare tutti i files nelle sottodirectories ricorsivamente</a></h2>
+<pre class="literal-block">
+for cwd, dirs, files in os.walk(&quot;.&quot;):
+ for f in files:
+ print f
+</pre>
+</div>
+<div class="section" id="calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory">
+<h2><a class="toc-backref" href="#id46" name="calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory">Calcolare lo spazio occupato da tutti i files di tipo .txt in una directory</a></h2>
+<p>[Soluzione svolta al corso 1]</p>
+<pre class="literal-block">
+import os
+
+def get_text_files(d):
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ if f.lower().endswith(&quot;.txt&quot;):
+ fullname = os.path.join(cwd, f)
+ size = os.path.getsize(fullname)
+ yield fullname, size
+
+from operator import itemgetter
+print sum(map(itemgetter(1), get_text_files(&quot;.&quot;)))
+</pre>
+</div>
+<div class="section" id="listare-i-files-a-seconda-delle-dimensioni">
+<h2><a class="toc-backref" href="#id47" name="listare-i-files-a-seconda-delle-dimensioni">Listare i files a seconda delle dimensioni</a></h2>
+<pre class="literal-block">
+print sorted(get_text_files(&quot;.&quot;), key=itemgetter(1))
+</pre>
+</div>
+<div class="section" id="scrivere-un-test-per-verificate-che-una-directory-sia-vuota">
+<h2><a class="toc-backref" href="#id48" name="scrivere-un-test-per-verificate-che-una-directory-sia-vuota">Scrivere un test per verificate che una directory sia vuota</a></h2>
+<pre class="literal-block">
+def is_empty(d):
+ if os.listdir(d): return False
+ else: return True
+</pre>
+<p>Sarebbe ridondante scrivere</p>
+<pre class="literal-block">
+if os.listdir(d) != []:
+</pre>
+<p>perchè la lista vuota ha già di per sè un valore booleano &quot;False&quot;.</p>
+</div>
+<div class="section" id="aprire-una-finestrella-contenente-la-scritta-hello-world">
+<h2><a class="toc-backref" href="#id49" name="aprire-una-finestrella-contenente-la-scritta-hello-world">Aprire una finestrella contenente la scritta &quot;hello, world!&quot;</a></h2>
+<p>[In maniera portabile]</p>
+<p>Soluzione più semplice:</p>
+<pre class="literal-block">
+# hellotk.pyw
+from tkMessageBox import showinfo
+showinfo(message=&quot;hello&quot;)
+</pre>
+<p>Soluzione alternativa:</p>
+<pre class="literal-block">
+# hellotk.pyw
+import Tkinter as t
+root = t.Tk()
+l = t.Label(text=&quot;hello&quot;)
+l.pack()
+root.mainloop()
+</pre>
+<p>Guardatevi sull'help di ActiveState la differenza tra l'estensione <tt class="docutils literal"><span class="pre">.py</span></tt>
+e l'estensione <tt class="docutils literal"><span class="pre">.pyw</span></tt> (dovrebbe essere nelle FAQ).</p>
+</div>
+<div class="section" id="scaricare-la-pagina-web-http-www-example-com-da-internet">
+<h2><a class="toc-backref" href="#id50" name="scaricare-la-pagina-web-http-www-example-com-da-internet">Scaricare la pagina Web <a class="reference" href="http://www.example.com">http://www.example.com</a> da Internet</a></h2>
+<pre class="doctest-block">
+&gt;&gt;&gt; from urllib2 import urlopen
+&gt;&gt;&gt; print urlopen('http://www.example.com').read()
+</pre>
+</div>
+<div class="section" id="stampare-a-schermo-una-tavola-delle-moltiplicazioni">
+<h2><a class="toc-backref" href="#id51" name="stampare-a-schermo-una-tavola-delle-moltiplicazioni">Stampare a schermo una tavola delle moltiplicazioni</a></h2>
+<p>Soluzione senza generatori:</p>
+<pre class="literal-block">
+#&lt;maketable.py&gt;
+
+# non graphic
+N = 10
+for i in range(1, N+1):
+ for j in range(1, N+1):
+ print &quot;%4d&quot; % (i*j),
+ print
+
+# HTML
+def maketable(iterable, N):
+ iterable = iter(iterable)
+ print &quot;&lt;table border='1'&gt;&quot;
+ stop = False
+ while not stop:
+ print &quot;&lt;tr&gt;&quot;
+ for j in range(1, N+1):
+ try:
+ print &quot;&lt;td&gt;%s&lt;/td&gt;&quot; % iterable.next(),
+ except StopIteration:
+ print &quot;&lt;td&gt;&lt;/td&gt;&quot;
+ stop = True
+ print &quot;&lt;/tr&gt;&quot;
+ print &quot;&lt;/table&gt;&quot;
+
+import tempfile, webbrowser, os, sys
+
+def showtable(iterable, N):
+ stdout = sys.stdout # shows how to redirect stdout correctly
+ fd, name = tempfile.mkstemp(suffix=&quot;.html&quot;)
+ sys.stdout = os.fdopen(fd, &quot;w&quot;)
+ maketable(iterable, N)
+ webbrowser.open(name)
+ sys.stdout = stdout
+
+showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N)
+
+#&lt;/maketable.py&gt;
+</pre>
+<p>Soluzione usando i generatori, non HTML:</p>
+<pre class="literal-block">
+#&lt;gentable.py&gt;
+
+def gentable(N):
+ for i in range(1, N+1):
+ for j in range(1, N+1):
+ yield i*j
+
+def printtable(lst, N):
+ for i, el in enumerate(lst):
+ print &quot;%4d&quot; % el,
+ if (i+1) % 10 == 0:
+ print
+
+if __name__ == &quot;__main__&quot;:
+ printtable(gentable(10), 10)
+
+#&lt;/gentable.py&gt;
+</pre>
+</div>
+<div class="section" id="trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto">
+<h2><a class="toc-backref" href="#id52" name="trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto">Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto</a></h2>
+<p>Soluzione svolta al corso 1:</p>
+<pre class="literal-block">
+import os, webbrowser
+
+def gentableHTML(iterable, N):
+ iterator = iter(iterable)
+ yield &quot;&lt;HTML&gt;&quot;
+ yield &quot;&lt;BODY&gt;&quot;
+ yield &quot;&lt;TABLE border='1'&gt;&quot;
+ for i in range(N):
+ yield &quot;&lt;TR&gt;&quot;
+ for j in range(N):
+ yield '&lt;TD&gt;&lt;img src=&quot;%s&quot; width=&quot;50&quot;, height=&quot;50&quot;&gt;&lt;/TD&gt;\n' % \
+ iterator.next()
+ yield &quot;&lt;/TR&gt;\n&quot;
+ yield &quot;&lt;/TABLE&gt;&quot;
+ yield &quot;&lt;/BODY&gt;&quot;
+ yield &quot;&lt;/HTML&gt;&quot;
+
+# generatore di file names
+def get_files_with_ext(ext_set, d):
+ &quot;&quot;&quot;
+ ext_set is a set of valid extensions.
+ &quot;&quot;&quot;
+ assert isinstance(ext_set, set), &quot;%r is not a set!&quot; % ext_set
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ fullname = os.path.join(cwd, f)
+ if ext.lower() in ext_set and os.path.getsize(fullname):
+ yield fullname
+
+if __name__ == &quot;__main__&quot;:
+ # genera il file e lo mostra
+ images = file(&quot;images.html&quot;, &quot;w&quot;)
+ for el in gentableHTML(get_files_with_ext(set([&quot;.png&quot;]),
+ &quot;C:\\Documents and Settings&quot;), 15):
+ print &gt;&gt; images, el,
+ images.close()
+ webbrowser.open(&quot;images.html&quot;)
+</pre>
+<p>Soluzione più sofisticata, per darvi qualcosa su cui pensare:</p>
+<pre class="literal-block">
+#&lt;maketable.py&gt;
+
+def get_files_with_ext(ext, d):
+ &quot;&quot;&quot;
+ ext can be a string or a set of strings; for instance
+ get_files_with_ext(&quot;.png&quot;) or get_files_with_ext(set(&quot;.png&quot;, &quot;.gif&quot;))
+ &quot;&quot;&quot;
+ if not isinstance(ext, set):
+ ext_set = set([ext])
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ if ext.lower() in ext_set:
+ yield os.path.join(cwd, f)
+
+class Picture(object):
+ &quot;&quot;&quot;An example of the utility of __str__&quot;&quot;&quot;
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return &quot;&lt;img src=%r width=100&gt;&quot; % self.pathname
+
+if sys.platform == 'win32':
+ drive = &quot;C:\\&quot;
+else:
+ drive = &quot;/&quot;
+showtable(map(Picture, get_files_with_ext(&quot;.jpg&quot;, drive)), N)
+
+#&lt;/maketable.py&gt;
+</pre>
+</div>
+</div>
+</div>
+</body>
+</html>
+
diff --git a/pypers/marelli/materiale/corso.txt b/pypers/marelli/materiale/corso.txt
new file mode 100755
index 0000000..e8c07dd
--- /dev/null
+++ b/pypers/marelli/materiale/corso.txt
@@ -0,0 +1,1851 @@
+Corso Python Magneti Marelli
+===================================
+
+:Tenuto: 19-23 Settembre 2005
+
+:Autore: Michele Simionato
+
+:E-mail: michele.simionato@gmail.com
+
+*Queste dispense sono un compendio informale e molto sintetico di quanto
+svolto durante il corso. Esse non si pongono in nessun modo come un testo
+sistematico di programmazione in Python. Il loro scopo principale è quello
+di tenere traccia, per quanto possibile, di quanto si è detto. Lo
+scopo secondario è quello di invogliare i partecipanti e tutti
+gli eventuali lettori ad affrontare gli argomenti qui trattati per forza
+di cose in maniera abbozzata, in maniera più sistematica andando a
+consultare le fonti più adatte alla bisogna, siano essi libri di testo,
+la documentazione ufficiale, newsgroups, siti Web dedicati e, in ultima
+instanza, il codice sorgente stesso, l'unico riferimento definitivo.*
+
+.. contents::
+
+I messaggi fondamentali del corso
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Studiare paga*
+---------------
+
+Grazie alla sua macchina del tempo, Guido ha risolto i problemi
+che vi stanno affliggendo ora dieci anni fa, e sono già nel linguaggio
+e nella libreria standard. Per esempìo avete scoperto di avere a
+disposizione:
+
+- le stringhe di documentazione per darvi la documentazione automatica
+ del codice tramite strumenti quali pydoc ed altri;
+- il try ... finally per garantirvi un "gracefull exit" del vostro
+ programma, anche in presenza di eccezioni inaspettate;
+- i metodi setUp e tearDown di unittest.TestCase, che vi permettono
+ di inizializzare e di pulire propriamente l'ambiente per ogni test;
+- sia unittest che doctest vi permetto di gestire le eccezioni (nel
+ senso di gestire le eccezioni "buone", quelle che vi aspettate);
+- call e Popen in subprocess per aprire processi, kill per ucciderli
+ (usando win32api.TerminateProcess);
+- twisted vi gestisce la comunicazione tra processi e tutte le eccezioni
+ senza problemi
+
+Una settimana di studio oggi può risparmiarvi mesi di frustrazioni
+domani. Sfruttare le risorse disponibili, quali tutorial, howto,
+libri, documentazione, siti Web (come il Python cookbook) e soprattutto
+i newsgroups. Ma prima di postare su di un newsgroup leggetevi "How to
+ask smart questions" e ricordatevi sempre che "Google is you friend".
+
+*Disaccoppiamento* e *KISS*
+----------------------------
+
+Se potere, scomponete l'applicazione in componenti separati e testateli
+separatamente. Scegliete architetture che vi permettono di disaccoppiare
+i problemi. Se potere fare le cose semplici, fatele semplici.
+Abbiate un core minimale che faccia poco, ma che siate sicuri che
+funzioni. Non complicatevi la vita.
+
+*Non nascondete gli errori sotto il tappeto*
+--------------------------------------------
+
+Non trappate l'inaspettato, è una ricetta sicura per avere rogne.
+Usate lo *sviluppo incrementale*, cioè verificate il funzionamento del
+programma SUBITO, e fissate l'errore SUBITO. Non debuggate mai
+a posteriori, se potete evitarlo, ma scrivete codice testato fin da subito.
+Abbiate una bella suite di test automatici di installazione, per accorgervi
+da subito se c'è un problema quando installate il software su di un'altra
+macchina.
+
+
+Modulo 1: Strumenti di introspezione, sviluppo e debugging
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*In questo modulo discuterò gli strumenti da me utilizzati per sviluppare
+in Python sotto Windows. Parlerò di Cygwin come ambiente di lavoro,
+di Python e di IPython come interpreti interattivi, di Idle e di PythonWin
+come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discuterò
+alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot,
+etc.).*
+
+Strumenti di introspezione e come ottenere aiuto
+-------------------------------------------------
+
++ **Help in linea:**
+ Ottimo
+
++ **Pydoc:**
+ Standard, può essere usato dalla riga di comando.
+ ``pydoc -g`` oppure ``python -mpydoc -g``
+ vi dà la versione grafica, con funzionalità di search.
+
++ **Help di ActiveState:**
+ Eccezionale, contiene anche libri di testo, FAQs e how-tos.
+
++ **Google:**
+ Di tutto, di più.
+
++ **Newsgroups:**
+ Risolvono i vostri problemi per voi, e gratis.
+
+Ambienti di sviluppo
+--------------------
+
++ **Cygwin:**
+ Emulatore Unix sotto Windows. Vi permette di lavorare dalla riga di
+ comando comodamente. Evitate di perdere tempo a cliccare a destra e
+ a manca e avete una stabilità maggiore di quella di un ambiente
+ grafico.
+
++ **Idle:**
+ Ambiente di sviluppo per Python che viene con la distribuzione standard.
+ Un pò povero, con qualche difetto, ma semplice e portabile ovunque.
+
++ **PythonWin:**
+ Ambiente di sviluppo per Python che viene con la distribuzione ActiveState
+ per Windows. Più sofisticato di Idle e più integrato con Windows. Ha dei
+ pro e dei contro.
+
++ **WingIDE, Eric/Qt Designer, Komodo, Boa Constructor:**
+ Ambienti di sviluppo di cui esiste una versione commerciale. In generale
+ hanno un look più professionale e sono utili se uno deve dare interfacce
+ grafiche. WingIDE ha il debugger migliore, a quanto ho sentito.
+
++ **Emacs o Vi:**
+ Un Real Programmer (TM) vi dirà che al mondo esistono due soli editor:
+ Emacs e Vi. Tutto il resto è spazzatura. Emacs e Vi girano bene sotto
+ Windows, ma funzionano al meglio sotto Unix.
+
+Strumenti di debugging
+------------------------------------------------
+
+- **print:** è la soluzione usata dai migliori programmatori Python;
+- **pdb:** sembra il debugger dei poveri, ma è standard e funziona;
+- **mille altri debuggers**, compreso un nuovissimo winpdb dall'autore
+ del pdb, da valutare;
+- **programmazione test-driven:** con questa metodologia la stragrande
+ maggioranza dei bugs vengono individuati subito, non appena il
+ codice viene scritto, e vi troverete ad usare il debuggere dieci
+ volte di meno di quanto fate ora.
+
+Strumenti utili per l'utenza scientifica/ingegneristica
+------------------------------------------------------------------
+
+- **Numeric e/o Numarray:**
+ Tutto quello che serve per far conti con le matrici. Numarray è il
+ successore di Numeric, con compatibilità al 99%.
+
+- **matplotlib:**
+ Il meglio per plottare grafici 2D. Richiede Numeric/Numarray.
+
+- **ipython:**
+ Il miglior interprete interattivo per Python. Eccezionali capacità
+ di introspezione e di debugging (è integrato con il Python debugger
+ pdb della libreria standard).
+
+Modulo 2: Programmazione di base in Python
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*In questo modulo si ripasseranno molto brevemente le basi di Python e
+si discuteranno le soluzioni al questionario di ammissione. Lo scopo
+più che altro è quello di conoscersi e di chiarire il livello medio
+dei partecipanti e coprire eventuali buchi nella preparazione di base.*
+
+Le soluzioni agli esercizi sono riportate nell'ultimo capitolo. Durante le
+correzioni mi sono accordi di vari buchi nella programmazione
+di base in Python che si è cercato di riempire.
+
+Encoding
+----------------------------------
+
+[Illustrato al corso 1] In versioni recenti di Python (dalla 2.3)
+se il vostro script contiene dei caratteri accentati (anche nei commenti)
+ottenete un warning tipo questo::
+
+ sys:1: DeprecationWarning: Non-ASCII character '\xe0' in file x.py
+ on line 1, but no encoding declared; see
+ http://www.python.org/peps/pep-0263.html for details
+
+La soluzione e' aggiungere in testa al vostro programma una dichiarazione
+tipo questa::
+
+ #-*- encoding: latin-1 -*-
+
+(usate latin-15 se il vostro programma contiene il simbolo dell'Euro).
+
+
+Pathnames
+-----------------------------
+
+Sfortunatamente Windows usa la backslash come separatore dei pathnames;
+la backslash e' anche il carattere di escaping, quindi ci sono
+problemi:
+
+>>> print 'documenti\nonna' # \n interpretato come newline
+documenti
+onna
+
+La soluzione e' usare raw strings:
+
+>>> print r'documenti\nonna'
+documenti\nonna
+
+Alternativamente, in versioni recenti di Windows, avreste potuto usare
+anche "/" come separatore:
+
+>>> import os
+>>> os.path.exists(r'C:\Python24\python.exe')
+True
+>>> os.path.exists(r'C:/Python24/python.exe')
+True
+
+Insiemi
+-----------------------------------------
+
+In Python 2.3 gli insiemi sono stati aggiunti come un nuovo tipo di dati
+nel modulo sets (by Alex Martelli); in Python 2.4 gli insiemi sono diventati
+un tipo builtin.
+
+>>> s = set('pippo')
+>>> s
+set(['i', 'p', 'o'])
+>>> 'i' in s
+True
+>>> 'p' in s
+True
+>>> 'o' in s
+True
+>>> 'z' in s
+False
+
+E' possibile calcolare l'unione e l'intersezione di insiemi:
+
+>>> t = set('aglio')
+>>> s | t
+set(['a', 'g', 'i', 'l', 'o', 'p'])
+>>> s & t
+set(['i', 'o'])
+
+Il modo compatibile con il passato per usare i sets nello stesso modo nella
+2.3 e nella 2.4 è il seguente::
+
+ try:
+ set:
+ except NameError:
+ from sets import Set as set
+
+Guardare la documentazione standard per saperne di più.
+
+Differenza tra mutabili e immutabili
+---------------------------------------
+
+Questa è una causa comune di confusione in Python:
+
+>>> a = 1 # i numeri sono immutabili
+>>> b = a
+>>> a += 1
+>>> a
+2
+>>> b
+1
+
+>>> a = [1] # le liste sono mutabili
+>>> b = a
+>>> a += [1]
+>>> a
+[1, 1]
+>>> b
+[1, 1]
+
+getattr e setattr
+-------------------------------------------
+
+[Svolto al corso 1] ``getattr`` e ``setattr`` servono per richiamare e
+settare metodi il cui nome viene determinato dinamicamente::
+
+ #<getattr_ex.py>
+
+ class C(object):
+ def m1(self):
+ print "chiamato m1"
+ def m2(self):
+ print "chiamato m2"
+
+ if __name__ == "__main__":
+ c = C()
+ method = raw_input("Che metodo devo chiamare? [m1 o m2] ")
+ getattr(c, method)()
+
+ #</getattr_ex.py>
+
+*Non usate exec quando getattr basterebbe!*
+
+>>> method = 'm1'
+>>> exec 'c.%s()' % method # funziona ma è brutto
+chiamato m1
+>>> getattr(c, method)() # il modo giusto
+chiamato m1
+
+Per ``setattr`` vedere la documentazione.
+
+Gestione dei processi
+-----------------------
+
+Come far partire un processo in parallelo::
+
+ import subprocess
+
+ PLAYER ="mplay32"
+
+ def play_song(song):
+ subprocess.Popen([PLAYER, "/play", "/close", song]) # NON BLOCCA!
+ print "Partito"
+
+
+ if __name__ == "__main__":
+ play_song("c:/Documents and Settings/micheles/Desktop/Music/1. "
+ "Theme from Harry's Game.mp3")
+
+
+``subprocess.call`` fa partire il processo e blocca il programma fintanto
+che il processo non è terminato. Ho anche fatto vedere cosa succede
+se uno dei processi solleva qualche eccezione inaspettata ma viene chiuso
+correttamente grazie al try .. finally::
+
+ #<main.py>
+
+ "Chiama due processi proc1a.py e proc1b.py"
+
+ import subprocess
+
+ CMD_a = ["python", "-c", "import proc1a; proc1a.main()"]
+ CMD_b = ["python", "-c", "import proc1b; proc1b.main()"]
+
+ if __name__ == "__main__":
+ p_a = subprocess.Popen(CMD_a)
+ p_b = subprocess.Popen(CMD_b)
+
+ #</main.py>
+
+Processo 1a::
+
+ #<proc1a.py>
+
+ import time
+
+ def main():
+ for i in range(10):
+ print "hello"
+ time.sleep(1)
+
+ if __name__ == "__main__":
+ main()
+
+ #</proc1a.py>
+
+Processo 1b::
+
+ #<proc1b.py>
+
+ #-*- encoding: latin-1 -*-
+ import time, sys
+
+ def main():
+ try:
+ f = file("proc1b.py")
+ for i in range(10):
+ print "world"
+ if i == 5:
+ raise RuntimeError("Ahia!")
+ time.sleep(1)
+ finally:
+ f.close()
+ print "Il file è stato chiuso correttamente."
+
+ if __name__ == "__main__":
+ main()
+
+ #</proc1b.py>
+
+Ho anche illustrato brevemente come si possono gestire i processi da
+Twisted::
+
+ #<proc2a.py>
+
+ "Un processo che genera numeri casuali e li salva nel file data.txt"
+
+ import random
+
+ def main():
+ ro = random.Random()
+ out = file("data.txt", "w")
+ for number in ro.sample(range(1000), 100):
+ print >> out, number
+ out.close()
+ print "Dati salvati sul file 'data.txt'"
+
+ if __name__ == "__main__":
+ main()
+
+ #</proc2a.py>
+
+ #<proc2b.py>
+
+ "Un processo che genera l'istogramma histo.png dai dati in data.txt"
+
+ from pylab import hist, savefig
+
+ def main():
+ hist([int(n) for n in file("dat.txt")], 10)
+ savefig("histo.png")
+ print "Istogramma salvato sul file 'histo.png'"
+
+ if __name__ == "__main__":
+ main()
+
+ #</proc2b.py>
+
+ #<twisted_main.py>
+
+ "Il main che chiama proc2a.py e proc2b.py nell'ordine e gestisce gli errori"
+
+ import webbrowser, sys
+ if sys.platform == "win32":
+ from twisted.internet import win32eventreactor
+ win32eventreactor.install()
+
+ from twisted.internet.utils import getProcessOutput
+ from twisted.internet import reactor
+
+ def scrivi_messaggio(err):
+ print err.getErrorMessage()
+ reactor.stop()
+ import pdb; pdb.set_trace() # fa partire il debugger in caso di errore
+
+ def visualizza_histo(out_di_genera_histo):
+ print out_di_genera_histo
+ webbrowser.open("histo.png")
+
+ def genera_histo(out_di_genera_dati):
+ print out_di_genera_dati
+ getProcessOutput(sys.executable, (r"c:\corso\processi\proc2b.py",)) \
+ .addCallback(visualizza_histo) \
+ .addErrback(scrivi_messaggio)
+
+ def genera_dati():
+ getProcessOutput(sys.executable, (r"c:\corso\processi\proc2a.py",)) \
+ .addCallback(genera_histo) \
+ .addErrback(scrivi_messaggio)
+
+ if __name__ == "__main__":
+ reactor.callLater(0, genera_dati) # call "genera_dati" after 0 seconds
+ reactor.run()
+
+ #</twisted_main.py>
+
+In questo esempio ho usato ``sys.executable``, che contiene il nome
+completo dell'eseguibile Python (per esempio ``C:\Python24\python.exe``)
+con cui il programma principale è stato lanciato. Questo assicura che
+i processi secondari vengano lanciati con quella versione di Python
+(utile se avete installato contemporaneamente piu' versioni di Python
+e ci possono essere dei dubbi, oppure se il path non e' settato
+correttamente e l'eseguibile Python non viene trovato).
+
+A volte, nonostante tutta la buona volontà, i processi vanno fuori
+controllo. E' possibile ammazzarli brutalmente, con una funzione
+``kill`` come la seguente::
+
+ import os
+
+ try: # we are on Windows
+ import win32api
+ def kill(pid, *unix_compatibility_dummy_args):
+ handle = win32api.OpenProcess(1, False, pid) # open handle to kill
+ win32api.TerminateProcess(handle, -1)
+ win32api.CloseHandle(handle)
+ os.kill = kill # fix os
+ except ImportError: # we are on Unix
+ pass
+
+In questo modo di fare, il modulo 'os' della libreria standard viene
+fissato automaticamente, aggiungendogli una funzione 'kill' che è
+mancante nell'ambiente Windows ma che può facilmente essere implementata
+usando le win32api (che non vengono con la distribuzione standard ma sono
+incluse con la distribuzione dell'ActiveState).
+
+Naturalmente cambiare moduli della libreria standard al volo NON È
+CONSIGLIATO, ma è sempre meglio che modificare a mano il codice
+sorgente e mantenere una propria versione modificata.
+
+Iteratori e generatori
+----------------------------
+
+Un iterabile è un qualunque oggetto su cui si può iterare con un
+ciclo "for"; un iteratore è un oggetto con un metodo .next().
+Il modo più comune per definire iteratori è tramite un generatore,
+cioè una "funzione" con uno "yield":
+
+>>> def gen123():
+... yield 1
+... yield 2
+... yield 3
+...
+>>> it = gen123()
+>>> it.next()
+1
+>>> it.next()
+2
+>>> it.next()
+3
+>>> it.next()
+Traceback (most recent call last):
+ File '<stdin>', line 1, in ?
+StopIteration
+
+Un ciclo "for" internamente converte l'iterabile in un iteratore,
+chiama il metodo ".next()" successivamente e trappa l'eccezione StopIteration,
+uscendo dal loop quando non c'è più nulla su cui iterare:
+
+>>> it = gen123()
+>>> for i in it: print i
+...
+1
+2
+3
+
+Modulo 3. Tipici errori di programmazione e gestione delle eccezioni
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Si discuteranno buoni e cattivi esempi di programmazione presi da software
+reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche
+per interpretare i tracebacks di Python e per identificare l'origine dei
+problemi.*
+
+L'errore più tipico con le eccezioni
+--------------------------------------
+
+Bisogna assolutamente evitare codice come il seguente::
+
+ try:
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ bla-bla
+ except:
+ bla-bla
+
+Nel blocco ``try`` dev'esserci la cosa più semplice possibile, per
+limitare i tipi di eccezione che possono nascere. Inoltre l'except nudo
+e' orribile perchè trappa qualunque cosa, anche quello che non vorreste.
+La cosa giusta da fare è del tipo::
+
+ try:
+ bla-bla
+ except MyException, e: # sempre specificare l'eccezione aspettata
+ print e
+ except OtherException,e:
+ print e
+ ...
+
+Non trappate l'inaspettato, altrimenti non capirete mai qual è stata
+l'origine di un problema.
+
+Il try .. finally è una grande idea
+----------------------------------------------------------------
+
+Molto spesso quello che volete non è tanto il try .. except, quanto il
+try .. finally: il vantaggio del try .. finally è che
+*non vi nasconde l'eccezione* e nello stesso tempo vi *garantisce che quello
+che deve essere chiuso correttamente venga chiuso correttamente* in ogni caso.
+C'è un eccezione a questa regola: se ammazzate un processo di brutto con un
+kill, il try .. finally non può salvarvi. Il try .. finally vi
+salva per tutte le eccezioni Python, compreso il CTRL-C (KeyboardInterrupt)
+e il sys.exit() (SystemExit).
+
+>>> try:
+... raise RuntimeError("Ahia")
+... finally:
+... print "Io vengo eseguito SEMPRE, anche se c'è un'eccezione!"
+...
+Io vengo eseguito SEMPRE, anche se c'e' un'eccezione!
+Traceback (most recent call last):
+ File '<stdin>', line 2, in ?
+RuntimeError: Ahia
+
+Uso di assert
+--------------
+
+Per asserire che una condizione è verificata con certezza:
+
+>>> def div2(x):
+... assert isinstance(x, int), '%s non è un numero intero' % x
+... return x/2
+...
+>>> div2(14)
+7
+>>> div2(14.0)
+Traceback (most recent call last):
+ File '<stdin>', line 1, in ?
+ File '<stdin>', line 2, in div2
+AssertionError: 14.0 non è un numero intero
+
+Tipicamente si usa in "sanity checks", per essere sicuri che un parametro
+sia esattamente quello che ci si aspetta, in casi di eccezioni gravi; se
+l'assert non è rispettato, tutto il programma deve bloccarsi.
+
+Non usate exec
+----------------------------
+
+'exec' è un costrutto pericolosissimo che va riservato solo a chi sa
+cosa sta facendo. Spesso e volentieri si usa soltanto per ignoranza
+dell'esistenza di una soluzione migliore.
+
+Esempio::
+
+ exec file("myscript.py").read()
+
+è UN ERRORE GRAVE per vari motivi.
+
+In primo luogo, in caso di eccezioni sollevate in 'myscript.py'
+perdete informazione su dove si trova l'errore (cioè nel file "myscript.py")
+e rendete impossibile la vita al debugger; in secondo luogo, sporcate il
+namespace del vostro programma in maniera potenzialmente pericolosa. La cosa
+giusta da fare è::
+
+ dic = {}
+ execfile("myscript.py", dic)
+
+che non perde informazioni e non sporca il namespace, perchè i nomi
+definiti in myscript.py verranno confinati nel dizionario. Leggetevi
+la documentazione di 'execfilè per saperne di più.
+
+Come far partire pdb automaticamente in caso di eccezioni inaspettate
+-----------------------------------------------------------------------
+
+[Illustrato al corso 1]
+
+::
+
+ #<exc_debug.py>
+
+ # recipe in the Python cookbook first edition, chapter 14.5
+
+ import sys
+
+ def info(type, value, tb):
+ if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \
+ type == SyntaxError:
+ # we are in interactive mode or we don't have a tty-like
+ # device, so we call the default hook
+ sys.__excepthook__(type, value, tb)
+ else:
+ import traceback, pdb
+ # we are NOT in interactive mode, print the exception...
+ traceback.print_exception(type, value, tb)
+ print
+ # ...then start the debugger in post-mortem mode.
+ pdb.pm()
+
+ sys.excepthook = info
+
+ #</exc_debug.py>
+
+Se un programma importa ``exc_debug``, il Python debugger partirà
+automaticamente in caso di eccezioni non trappate. Per esempio eseguire
+
+::
+
+ #<example.py>
+ import exc_debug
+ a = 1
+ b = 0
+ a/b
+ #</example.py>
+
+fa partire il debugger::
+
+ $ python example.py
+ Traceback (most recent call last):
+ File "example.py", line 4, in ?
+ a/b
+ ZeroDivisionError: integer division or modulo by zero
+
+ > /mnt/hda2/cygwin/home/micheles/md/pypers/marelli/materiale/example.py(4)?()
+ -> a/b
+ (Pdb) print a
+ 1
+ (Pdb) print b
+ 0
+ Pdb) !b = 1 # cambia il valore di b
+ (Pdb) print a/b
+ 1
+
+Date ``help pdb`` dall'interno del debugger per avere informazioni sul suo
+funzionamento.
+
+Eccezioni e threads
+-------------------------
+
+Un'eccezione non trappata blocca soltanto il thread in cui si è verificata,
+NON tutto il programma::
+
+ #<esempio1.py>
+
+ import threading, time, sys
+
+ def print_hello():
+ for i in range(10):
+ print "hello"
+ if i == 5:
+ raise RuntimeError("Problema a runtime")
+ time.sleep(1)
+
+ def print_world():
+ for i in range(10):
+ print "world"
+ time.sleep(1)
+
+ threading.Thread(target=print_hello).start()
+ threading.Thread(target=print_world).start()
+
+ #</esempio1.py>
+
+dà come output::
+
+ $ python esempio1.py
+ hello
+ world
+ hello
+ world
+ hello
+ world
+ hello
+ world
+ hello
+ world
+ hello
+ world
+ Exception in thread Thread-1:
+ Traceback (most recent call last):
+ File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap
+ self.run()
+ File "/usr/lib/python2.4/threading.py", line 422, in run
+ self.__target(*self.__args, **self.__kwargs)
+ File "esempio1.py", line 7, in print_hello
+ raise RuntimeError("Problema a runtime")
+ RuntimeError: Problema a runtime
+
+ world
+ world
+ world
+ world
+
+Quindi uno è costretto a implementare un meccanismo di controllo, tipo
+il seguente::
+
+ import threading, time, sys
+
+ END = False
+
+ def print_hello():
+ global END
+ i = 0
+ while not END:
+ i += 1
+ print "hello"
+ if i == 5:
+ try:
+ raise RuntimeError("Problema a runtime")
+ except RuntimeError, e:
+ END = True
+ time.sleep(1)
+
+ def print_world():
+ i = 0
+ while not END:
+ print "world"
+ time.sleep(1)
+
+ threading.Thread(target=print_hello).start()
+ threading.Thread(target=print_world).start()
+
+Questa è una soluzione artigianale, che usa una variabile globale, sfruttando
+il fatto che le variabili globali sono condivise fra tutti i thread (questo
+può anche causare danni, se non si sta attenti).
+
+La strada consigliata per comunicare fra threads è quella di usare una coda
+[esempio svolto al corso 1]::
+
+ "Esempio di due threads comunicanti tramite una queue."
+
+ import time, threading, Tkinter, Queue
+
+ queue = Queue.Queue()
+
+ def print_hello():
+ for i in range(10):
+ try:
+ messaggio = queue.get_nowait()
+ except Queue.Empty:
+ pass
+ else:
+ if messaggio == "terminate": break
+ print "%s, hello" % i
+ time.sleep(1)
+
+ def print_world():
+ for i in range(10):
+ print "%s, world" % i
+ if i == 5:
+ queue.put("terminate") # manda il messaggio di terminazione
+ root.quit()
+ raise RuntimeError("Errore nel thread print_world!")
+ time.sleep(1)
+
+ root = Tkinter.Tk()
+
+ for func in print_hello, print_world:
+ th = threading.Thread(target=func)
+ th.start()
+
+ root.mainloop()
+
+Questo esempio fa anche vedere che chiudere la finestrella grafica NON
+uccide i threads che stanno girando.
+
+I meccanismi di controllo per bloccare i thread sono notoriamente fragili
+e piccoli errori possono farvi grossi danni.
+
+Debuggare i threads è notoriamente un macello.
+
+L'unica soluzione vera è evitare i threads quando è possibile.
+
+Modulo 4. Sviluppo orientato ai test
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Come scrivere software con tecnologie agili, con lo scopo di ridurre e
+tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest.
+Esempi di programmazione test driven.*
+
+Consigli di carattere generale
+-------------------------------
+
+- testate SUBITO, non debuggate dopo (ad ogni una riga di codice che si
+ scrive, si testa che funzioni e che non distrugga quello che funzionava
+ prima).
+
+- scrivete software in maniera che sia testabile (per esempio create
+ sempre anche una versione non-grafica di un programma grafico, perchè
+ la versione testuale si testa **molto** più facilmente).
+
+- non abbiate paura a scrivervi un vostro ambiente di test personalizzato.
+
+- tenete conto che unittest e doctest esistono e possono aiutarvi infinitamente
+ nel gestire i vostri test.
+
+Usare unittest
+------------------------------------------------------
+
+Tipicamente con unittest si divide la libreria da testare::
+
+ #<isnumber.py>
+
+ def is_number(arg):
+ "Verifica se la stringa arg è un numero valido"
+ try:
+ float(arg)
+ except ValueError:
+ return False
+ else:
+ return True
+
+ #</isnumber.py>
+
+dal file di test, che convenzionalmente ha un nome che inizia per "test"::
+
+ #<test_isnumber.py>
+
+ import unittest
+
+ from isnumber import is_number
+
+ class TestIsNumber(unittest.TestCase):
+
+ def setUp(self):
+ print "sto inizializzando"
+
+ # test positivi
+ def test_1(self):
+ "Testa che '1' è un numero buono."
+ self.assertTrue(is_number("1"))
+ def test_2(self):
+ "Testa che '1.3' è un numero buono."
+ self.assertTrue(is_number("1.3"))
+ def test_3(self):
+ "Testa che '+1.3' è un numero buono."
+ self.assertTrue(is_number("+1.3"))
+ def test_4(self):
+ "Testa che '-1.3' è un numero buono."
+ self.assertTrue(is_number("-1.3"))
+
+ # test negativi
+ def test_5(self):
+ "Testa che '1-.3' non è un numero buono."
+ self.assertFalse(is_number("1-.3"))
+ def test_6(self):
+ "Testa che 'à non è un numero buono."
+ self.assertFalse(is_number("a"))
+ def test_7(self):
+ "Testa che '42' è un numero buono."
+ self.assertTrue(is_number("42"))
+
+ def tearDown(self):
+ print "Sto chiudendo quello che c'è da chiudere"
+
+ if __name__ == "__main__":
+ unittest.main()
+
+ #</test_isnumber.py>
+
+Eseguire i tests con l'opzione verbose da::
+
+ $ python test_isnumber.py -v
+ Testa che '1' è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che '1.3' è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che '+1.3' è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che '-1.3' è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che '1-.3' non è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che 'à non è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+ Testa che '42' è un numero buono. ... sto inizializzando
+ Sto chiudendo quello che c'è da chiudere
+ ok
+
+ ----------------------------------------------------------------------
+ Ran 7 tests in 0.001s
+
+ OK
+
+Questo mostra che i metodi ``setUp`` e ``tearDown`` vengono chiamati
+*per ogni test*, quindi tutti i test si svolgono in un ambiente pulito.
+
+E' normale avere un file di test più lungo della libreria da testare.
+
+E' possibile specificare le eccezioni aspettate::
+
+ #<test_exc.py>
+
+ import unittest
+
+ def divide(a, b):
+ return a/b
+
+ class TestIsNumber(unittest.TestCase):
+ def test_1(self):
+ "Divide 4/2"
+ self.assertEqual(divide(4,2), 2)
+ def test_2(self):
+ "Divide 4/0"
+ self.assertRaises(ZeroDivisionError, divide, 4, 0)
+
+
+ if __name__ == "__main__":
+ unittest.main()
+
+ #</test_exc.py>
+
+ $ python test_exc.py -v
+ Divide 4/2 ... ok
+ Divide 4/0 ... ok
+
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.001s
+
+ OK
+
+Usare doctest
+-----------------------------------
+
+L'uso più semplice, eseguire i doctest che si trovano nelle stringhe
+di documentazione di un modulo::
+
+ #<esempio_banale.py>
+
+ def sum12():
+ """Questa funzione ritorna la somma di 1 + 2::
+ >>> sum12()
+ 3"""
+ return 1+2
+
+ if __name__ == "__main__":
+ import doctest; doctest.testmod()
+
+ #</esempio_banale.py>
+
+Ecco come eseguire i tests con l'opzione verbose::
+
+ $ python esempio_banale.py -v
+ Trying:
+ sum12()
+ Expecting:
+ 3
+ ok
+ 1 items had no tests:
+ __main__
+ 1 items passed all tests:
+ 1 tests in __main__.sum12
+ 1 tests in 2 items.
+ 1 passed and 0 failed.
+ Test passed.
+
+Eseguire i doctest che si trovano in un file di testo separato (nuova
+funzionalità di Python 2.4)::
+
+ #<doctest_runner.py>
+
+ import doctest
+
+ if __name__== "__main__":
+ doctest.testfile("test_isnumber.txt")
+
+ #</doctest_runner.py>
+
+
+Contenuto di 'test_isnumber.txt'::
+
+ Questa è la documentazione della funzione isnumber
+ ====================================================
+
+ Esempi di uso:
+
+ >>> from isnumber import is_number
+ >>> is_number("1")
+ True
+ >>> is_number("1.3")
+ True
+ >>> is_number("+1.3")
+ True
+ >>> is_number("-1.3")
+ True
+ >>> is_number("1-.3")
+ False
+ >>> is_number("a")
+ False
+ >>> is_number("42")
+ True
+ >>> 1/0
+ Traceback (most recent call last):
+ kkkkdjjfkf
+ ZeroDivisionError: integer division or modulo by zero
+
+Eseguire i tests::
+
+ $ python doctest_runner.py -v
+ Trying:
+ from isnumber import is_number
+ Expecting nothing
+ ok
+ Trying:
+ is_number("1")
+ Expecting:
+ True
+ ok
+ Trying:
+ is_number("1.3")
+ Expecting:
+ True
+ ok
+ Trying:
+ is_number("+1.3")
+ Expecting:
+ True
+ ok
+ Trying:
+ is_number("-1.3")
+ Expecting:
+ True
+ ok
+ Trying:
+ is_number("1-.3")
+ Expecting:
+ False
+ ok
+ Trying:
+ is_number("a")
+ Expecting:
+ False
+ ok
+ Trying:
+ is_number("42")
+ Expecting:
+ True
+ ok
+ Trying:
+ 1/0
+ Expecting:
+ Traceback (most recent call last):
+ kkkkdjjfkf
+ ZeroDivisionError: integer division or modulo by zero
+ ok
+ 1 items passed all tests:
+ 9 tests in test_isnumber.txt
+ 9 tests in 1 items.
+ 9 passed and 0 failed.
+ Test passed.
+
+Confrontare la leggibilità di ``test_isnumber.txt`` con la leggibilità
+di ``test_isnumber.py``, basato su unittest. Leggetevi il mio seminario su
+doctest (in allegato) per convincervi che doctest è il migliore. Anche perchè
+i doctest possono essere convertiti in unittest automaticamente, a partire
+da Python 2.4
+
+Per convertire i test contenuti in 'mymodulè da doctest a unittest::
+
+ import doctest, unittest, mymodule
+
+ if __name__== "__main__":
+ suite = doctest.DocTestSuite(mymodule)
+ unittest.TextTestRunner(verbosity=2).run(suite)
+
+E' anche possibile contenere i tests contenuti in un file di
+tipo testo da doctest a unittest, vedere la documentazione.
+
+Correntemente, doctest non ha un meccanismo predefinito corrispondente
+ai metodi ``setUp`` e ``tearDown`` di unittest, ma potete impostarlo a mano.
+
+
+Un esempio di programma sviluppato in maniera incrementale
+----------------------------------------------------------
+
+Il seguente esempio svolto al corso intendeva dimostrare:
+
+1. Come si sviluppa un programma in maniera incrementale (ad ogni nuova riga di
+ codice si fa una verifica immediata del funzionamento dell'insieme);
+2. Come si fanno scelte architetturali in maniera tale da assicurare la
+ testabilità del prodotto finale in maniera semplice ed automatica;
+3. Come sfruttare la libreria standard di Python al meglio, usando il
+ modulo cmd;
+4. Come dividere il codice in metodi pubblici, metodi di utilità e
+ metodi di debugging;
+5. Come assicurarsi che il programma venga chiuso propriamente, anche
+ nel caso di eccezioni impreviste e imprevedibili;
+
+::
+
+ # -*- encoding: latin-1 -*-
+ import os, cmd, subprocess, lib, time # lib fissa os.kill
+ from pywintypes import error as WindowsProcessDidNotStartCorrectly
+
+ MUSICDIR = "C:/Documents and Settings/micheles/Desktop/Music"
+
+ def play_song(name):
+ po = subprocess.Popen(["mplay32", "/play", "/close", name])
+ return po.pid
+
+ class InterpreteDiComandi(cmd.Cmd):
+ cwd = MUSICDIR
+ prompt = "Player> "
+ def preloop(self):
+ self.process_list = []
+ os.chdir(MUSICDIR)
+
+ def do_play(self, arg):
+ "Suona una canzone"
+ if not arg:
+ print "Per favore scrivi il nome di una canzone!"
+ else:
+ self.process_list.append(play_song(arg))
+
+ def do_quit(self, dummy):
+ "Esce dall'interprete."
+ return True
+
+ def safe_kill(self, pid):
+ try:
+ os.kill(pid)
+ except WindowsProcessDidNotStartCorrectly, e:
+ print e
+
+ def do_kill(self, arg):
+ "Uccide il player"
+ try:
+ pid = self.process_list.pop()
+ except IndexError:
+ print "Hai già ucciso tutti i processi!"
+ else:
+ self.safe_kill(pid)
+
+ def postloop(self):
+ for pid in self.process_list:
+ self.safe_kill(pid)
+ print "Ho ucciso tutto!"
+ os.chdir(os.path.dirname(os.path.abspath(__file__)))
+
+ # metodi che possono essere utili per il debugging
+ def do_print_dir(self, arg):
+ "Comando utile per il debugging"
+ print self.cwd
+
+ def do_raise_exc(self, dummy):
+ raise RuntimeError("Tanto per vedere che succede")
+
+ def do_sleep(self, arg):
+ "utile per vedere quello che i test automatici stanno facendo"
+ time.sleep(int(arg))
+
+ i = InterpreteDiComandi()
+
+ try:
+ i.cmdloop()
+ finally: # assicura la chiusura anche in caso di eccezione
+ i.postloop()
+
+Nel caso regolare (senza eccezioni impreviste) .postloop è chiamato 2 volte,
+ma questo non fa danno.
+
+Questo potrebbe essere il codice corrispondente ad un test automatico::
+
+ $ more test_player.cmd
+ play
+ sleep 2
+ play 2. Croi Croga.mp
+ sleep 2
+ play 2. Croi Croga.mp3
+ sleep 2
+ kill
+ sleep 2
+ quit
+
+(la prima canzone non esiste, in modo da poter vedere come viene segnalato
+l'errore) che verrebbe eseguito così::
+
+ $ python player.py < test_player.cmd
+
+Modulo 5: Design, documentazione e manutenzione di librarie
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Pratiche di programmazione "in the large". Moduli, packages, strumenti di
+documentazione e di installazione. Applicazioni pratiche di principi generali
+quali disaccoppiamento, modularità, non duplicazione del codice.*
+
+La filosofia del Python
+---------------------------------------
+
+>>> import this
+The Zen of Python, by Tim Peters
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it.
+Although that way may not be obvious at first unless you are Dutch.
+Now is better than never.
+Although never is often better than *right* now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!
+
+Principio del disaccoppiamento
+-------------------------------------
+
+Questo è il principio guida di tutta la programmazione e non solo, tutto
+il resto nasce di conseguenza.
+
+Il principio dice: **cercate di disaccoppiare il più possibile
+le componenti del vostro sistema**.
+
+*Un componente si dice disaccoppiato se
+può essere rimosso o sostituito senza danneggiare il resto del sistema.*
+
+Per esempio:
+
+- disaccoppiare l'ambiente di sviluppo dall'ambiente di esecuzione (è più
+ sicuro lanciare un programma dalla riga di comando che dall' IDE; un
+ debugger disaccoppiato come pdb è più sicuro di un debugger integrato
+ come quello di PythoWin, un editor vero è più sicuro dell'editor dell'
+ IDE, etc.)
+
+- disaccoppiare l'interfaccia grafica: il programma deve poter funzionare
+ senza di essa, o sostituendo il GUI toolkit con un altro.
+
+- i threads vi accoppiano tutto, se uno va male può bloccare tutti gli
+ altri: evitateli se potete.
+
+- l'ereditarietà vi accoppia il parente al figlio (per sapere quello che
+ fa il figlio bisogna andare a vedere quello che fanno il padre, il nonno,
+ il bisnonno, il trisavolo, etc.). Evitatela se potete.
+
+- la modularità è un modo concreto per applicare il principio del
+ disaccoppiamento al codice: funzioni che logicamente vanno insieme,
+ vanno messe in un modulo comune, funzioni separate vanno separate.
+ Se c'è un problema nel modulo X, questo non deve interferire con
+ il modulo Y (almeno in un mondo ideale). La modularità rende anche
+ più semplice rimpiazzare un modulo sbagliato o vecchio con uno
+ corretto o più recente.
+
+- la non-duplicazione del codice è una conseguenza della modularità:
+ avendo raggruppato le funzioni di uso generale in un modulo comune,
+ si acquista in concisione, semplicità, leggibilità, debuggabilità,
+ si evita di correggere lo stesso errore più volte, e in generale tutto
+ diviene più facile da mantenere.
+
+Principio del KISS
+--------------------------------------
+
+*Keep it simple, stupid*. In Italiano: *non facciamoci del male*.
+
+Se potete fare le cose in maniera semplice, fatele in maniera semplice.
+
+Chiedetevi sempre se una cosa vi serve veramente oppure no.
+
+Importanza di avere un prototipo
+------------------------------------------
+
+Non c'è nulla di più sbagliato che partire scrivendosi l'architettura di
+un progetto complesso sulla carta. Si parte sempre scrivendo un prototipo,
+*testato sul campo di battaglia*, che magari ha l'1% delle funzionalità
+che vi interessano, ma che *funziona*. A quel punto è possibile decidere
+l'architettura e il prototipo diventerà il core e la parte più testata
+e sicura della vostra applicazione.
+
+Il prototipo/core deve *rimuovere tutto l'inessenziale*. Più povero è,
+meglio è.
+
+
+Moduli e packages
+-------------------------------------------
+
+Qualunque file Python può essere visto come un modulo. Un package invece,
+è una directory contente un file ``__init__.py`` che contiene il codice di
+inizializzazione (``__init__.py`` può benissimo essere vuoto, oppure può
+consistere solo di 'import' dei moduli di quel package).
+
+Importare un package non importa automaticamente tutti i suoi moduli,
+a meno che questo non sia detto esplicitamente nell' ``__init__.py``.
+
+I package possono essere nidificati senza limite.
+
+Le variabili globali di un modulo sono locali a quel modulo, quindi
+non c'è mai il rischio di fare danni (a meno che un modulo non importi
+esplicitamente una variabile corrispondente ad un oggetto mutabile e
+la cambi esplicitamente).
+
+E' possibile mettere un package nel Python path automaticamente per
+tutti gli utenti listando il nome del package in un file ``.pth`` nella
+directory site-packages (vedere la documentazione di distutils).
+
+
+Come si documenta una libreria Python
+-------------------------------------------------
+
+Python è un linguaggio "self-documenting" grazie alle doctrings.
+Usatele. In più guardate come è documentato un progetto Python
+tipico (io suggerisco di guardare a *docutils*) e copiate le convenzioni
+usate lì. Ci sono dei file standard come il README.txt, l'HISTORY.txt,
+il BUGS.txt, una directory docs, una directory test, un file setup.py,
+etc.
+
+Tenete presente che grazie a *doctest* potete inserire dei test automatici
+all'interno della vostra documentazione.
+
+Modulo 6: Domande estemporanee
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Risponderò alle domande dell'audience, anche al di fuori dal programma,
+se di interesse generale*.
+
+Come funziona 'import'
+---------------------------------
+
+Se un modulo è già stato importato e chiamate 'import' una seconda
+volta, il modulo NON viene importato due volte. Questo può essere
+un problema nell'interprete interattivo. Se importate un modulo,
+poi lo modificate e lo importate di nuovo, vi resterà in memoria
+la copia *vecchia*, quella non modificata. La soluzione è usare
+'reload', che vi importerà veramente la nuova versione.
+
+Come funziona '__del__'
+---------------------------------------------
+
+>>> a = 1
+>>> del a
+
+vi cancella il nome dal namespace:
+
+>>> a
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+NameError: name 'a' is not defined
+
+Tuttavia l'oggetto cui si riferisce *non viene cancellato immediatamente*.
+Verrà cancellato dal garbage collector *quando serve*, ma soltanto
+*se non vi sono altre referenze* all'oggetto da qualche altra parte.
+Inoltre per oggetti C, dovrete definirvi un metodo ``__del__`` custom
+che ha accesso all'oggetto a livello C. Tenete anche conto che tipicamente
+il debugger tiene referenze agli oggetti aggiuntive, quindi in modalità
+di debugger i problemi con oggetti non cancellati peggiorano.
+
+Ecco un esempio di un metodo ``__del__`` custom piuttosto banale:
+
+>>> class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+>>> c = C()
+>>> del c
+Hai usato del
+
+Il metodo ``__del__`` viene chiamato automaticamente all'uscita
+dall'interprete:
+
+>>> class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+>>> c = C()
+>>> <CTRL-Z> per uscire dall'interprete
+Hai usato del
+
+In principio, all'uscita del programma Python
+*dovrebbe chiamare ``__del__`` automaticamente anche se vi sono eccezioni*::
+
+ #<del_with_exc.py>
+
+ class C(object):
+ def __del__(self):
+ print "Hai chiamato del"
+
+ c = C()
+ raise RuntimeError("Ahi ahi!")
+
+ #</del_with_exc.py>
+
+ $ python del_with_exc.py
+ Traceback (most recent call last):
+ File "del_with_exc.py", line 6, in ?
+ raise RuntimeError("Ahi ahi!")
+ RuntimeError: Ahi ahi!
+ Hai chiamato del
+
+Tuttavia per oggetti ``C`` ed eccezioni le cose possono essere delicate ed
+è sempre meglio chiamare ``del`` *esplicitamente*, magari in un
+``try .. finally``.
+
+Che differenza c'è fra variabili di classe e di istanza
+---------------------------------------------------------
+
+Questo esempio dovrebbe chiarire la differenza:
+
+>>> class C(object):
+... pippo = 'sono una variabile di classe'
+... def __init__(self):
+... self.poppi = 'sono una variabile di istanza'
+...
+
+Le variabili di classe sono le stesse per tutte le istanze:
+
+>>> c1 = C()
+>>> c2 = C()
+>>> c1.pippo
+'sono una variabile di classe'
+>>> c2.pippo
+'sono una variabile di classe'
+
+Se cambio una variabile di classe, cambia per tutte le istanze, anche
+per quelle che sono state create *prima* del cambiamento:
+
+>>> C.pippo = 'adesso la cambio'
+>>> c1.pippo
+'adesso la cambio'
+>>> c2.pippo
+'adesso la cambio'
+
+Lo stesso vale per i metodi, che non sono altro che variabili di classe.
+
+Le variabili di istanza invece sono indipendenti:
+
+>>> c1.poppi
+'sono una variabile di instanza'
+>>> c2.poppi
+'sono una variabile di instanza'
+>>> c1.poppi = 'cambio la variabile dell'istanza c1'
+>>> c1.poppi
+'cambio la variabile dell'istanza c1'
+>>> c2.poppi # questa rimane quella che era prima
+'sono una variabile di instanza'
+
+Che cosa sono i metodi che iniziano con "__"
+------------------------------------------------
+
+Sono metodi protetti. Sono utili per evitare rogne con l'ereditarietà.
+Per capire il problema dò un esempio semplice qui di seguito.
+
+Normalmente l'ereditarietà accoppia il padre con il figlio, nel senso che
+il figlio può sovrascrivere i metodi del padre, e i metodi del padre andranno
+automaticamente a chiamare i metodi del figlio::
+
+ class B(object): # padre
+ def __init__(self):
+ self.hello()
+ def hello(self):
+ print "hello!"
+
+ class C(B): # figlio
+ def hello(self):
+ print "cucu!"
+
+ b = B() # stampa 'hello!'
+ c = C() # stampa 'cucu!'
+
+In questo esempio l'__init__ del padre chiama l'hello del figlio. Tuttavia,
+a volte uno vuole essere sicuro che l'__init__ del padre chiami l'hello
+del padre, e non quello del figlio (sempre per via del principio del
+disaccoppiamento). Per questo ci sono i *metodi protetti*::
+
+ class B(object):
+ def __init__(self): # call the '__hello' method in THIS class
+ self.__hello()
+ def __hello(self):
+ print "hello!"
+
+ class C(B):
+ def __hello(self): # won't be called by B.__init__
+ print "cucu!"
+
+ b = B() # stampa 'hello!'
+ c = C() # stampe 'hello!'
+
+
+I metodi protetti di tipo ``__<nome metodo>`` non vanno confusi con i metodi
+privati di tipo ``_<nome metodo>`` (un solo underscore):
+
+1. i metodi privati non andrebbero chiamati mai, se non sapendo al 100% quello
+ che si sta facendo, e solo in caso di errori nel design della libreria che
+ si sta utilizzando;
+
+2. i metodi protetti possono essere chiamati con tranquillità. Proprio
+ perchè sono protetti, c'è la garanzia che sovrascrivendoli nella
+ sottoclasse non si va ad alterare il comportamento ereditato dal padre
+ (nel nostro esempio l'``__init__`` del padre continuerà a chiamare il
+ proprio ``__hello``, non l'``__hello`` del figlio). D'altra parte i
+ metodi definiti nel figlio vedranno solo l'``__hello`` del figlio e
+ non quello del padre.
+
+3. i metodi protetti non possono essere chiamati accidentalmente, perchè
+ per esempio ``c.__hello()`` non funziona. Tuttavia è possibile chiamarli,
+ quindi non sono veramente privati: nel nostro esempio è possibile
+ chiamare
+
+ ``c._B__hello`` (``__hello`` del padre) oppure
+ ``c._C__hello`` (``__hello`` del figlio)
+
+ Siccome bisogna specificare il nome della classe dove il metodo è definito
+ non c'è il rischio di sbagliarsi (a meno di non essere masochisti e di
+ non dare lo stesso nome al padre e al figlio).
+
+I metodi protetti vengono usati raramente. Vedere "Python in a Nutshell"
+per maggiori informazioni sul loro uso.
+
+Soluzioni al questionario di ammissione
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Scrivere un programma che testa se una stringa rappresenta un numero
+---------------------------------------------------------------------
+
+Soluzione standard (vedere anche il tutorial di Python)::
+
+ try:
+ int(x)
+ except ValueError:
+ print "This is not a number!"
+
+Soluzione usando tkSimpleDialog::
+
+ from tkSimpleDialog import askfloat
+ askfloat("Enter a number", "Number:")
+
+Scrivere un programma che lista tutti i files nella directory corrente
+------------------------------------------------------------------------
+
+::
+
+ for f in os.listdir("."):
+ print f
+
+Listare tutti i files nelle sottodirectories ricorsivamente
+-------------------------------------------------------------
+
+::
+
+ for cwd, dirs, files in os.walk("."):
+ for f in files:
+ print f
+
+Calcolare lo spazio occupato da tutti i files di tipo .txt in una directory
+----------------------------------------------------------------------------
+
+[Soluzione svolta al corso 1]
+
+::
+
+ import os
+
+ def get_text_files(d):
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ if f.lower().endswith(".txt"):
+ fullname = os.path.join(cwd, f)
+ size = os.path.getsize(fullname)
+ yield fullname, size
+
+ from operator import itemgetter
+ print sum(map(itemgetter(1), get_text_files(".")))
+
+Listare i files a seconda delle dimensioni
+-------------------------------------------
+
+::
+
+ print sorted(get_text_files("."), key=itemgetter(1))
+
+Scrivere un test per verificate che una directory sia vuota
+--------------------------------------------------------------
+
+::
+
+ def is_empty(d):
+ if os.listdir(d): return False
+ else: return True
+
+Sarebbe ridondante scrivere
+
+::
+
+ if os.listdir(d) != []:
+
+
+perchè la lista vuota ha già di per sè un valore booleano "False".
+
+Aprire una finestrella contenente la scritta "hello, world!"
+-------------------------------------------------------------
+
+[In maniera portabile]
+
+Soluzione più semplice::
+
+ # hellotk.pyw
+ from tkMessageBox import showinfo
+ showinfo(message="hello")
+
+Soluzione alternativa::
+
+ # hellotk.pyw
+ import Tkinter as t
+ root = t.Tk()
+ l = t.Label(text="hello")
+ l.pack()
+ root.mainloop()
+
+Guardatevi sull'help di ActiveState la differenza tra l'estensione ``.py``
+e l'estensione ``.pyw`` (dovrebbe essere nelle FAQ).
+
+Scaricare la pagina Web http://www.example.com da Internet
+----------------------------------------------------------------
+
+>>> from urllib2 import urlopen
+>>> print urlopen('http://www.example.com').read()
+
+Stampare a schermo una tavola delle moltiplicazioni
+---------------------------------------------------------
+
+Soluzione senza generatori::
+
+ #<maketable.py>
+
+ # non graphic
+ N = 10
+ for i in range(1, N+1):
+ for j in range(1, N+1):
+ print "%4d" % (i*j),
+ print
+
+ # HTML
+ def maketable(iterable, N):
+ iterable = iter(iterable)
+ print "<table border='1'>"
+ stop = False
+ while not stop:
+ print "<tr>"
+ for j in range(1, N+1):
+ try:
+ print "<td>%s</td>" % iterable.next(),
+ except StopIteration:
+ print "<td></td>"
+ stop = True
+ print "</tr>"
+ print "</table>"
+
+ import tempfile, webbrowser, os, sys
+
+ def showtable(iterable, N):
+ stdout = sys.stdout # shows how to redirect stdout correctly
+ fd, name = tempfile.mkstemp(suffix=".html")
+ sys.stdout = os.fdopen(fd, "w")
+ maketable(iterable, N)
+ webbrowser.open(name)
+ sys.stdout = stdout
+
+ showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N)
+
+ #</maketable.py>
+
+Soluzione usando i generatori, non HTML::
+
+ #<gentable.py>
+
+ def gentable(N):
+ for i in range(1, N+1):
+ for j in range(1, N+1):
+ yield i*j
+
+ def printtable(lst, N):
+ for i, el in enumerate(lst):
+ print "%4d" % el,
+ if (i+1) % 10 == 0:
+ print
+
+ if __name__ == "__main__":
+ printtable(gentable(10), 10)
+
+ #</gentable.py>
+
+Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto
+--------------------------------------------------------------------------------------------
+
+Soluzione svolta al corso 1::
+
+ import os, webbrowser
+
+ def gentableHTML(iterable, N):
+ iterator = iter(iterable)
+ yield "<HTML>"
+ yield "<BODY>"
+ yield "<TABLE border='1'>"
+ for i in range(N):
+ yield "<TR>"
+ for j in range(N):
+ yield '<TD><img src="%s" width="50", height="50"></TD>\n' % \
+ iterator.next()
+ yield "</TR>\n"
+ yield "</TABLE>"
+ yield "</BODY>"
+ yield "</HTML>"
+
+ # generatore di file names
+ def get_files_with_ext(ext_set, d):
+ """
+ ext_set is a set of valid extensions.
+ """
+ assert isinstance(ext_set, set), "%r is not a set!" % ext_set
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ fullname = os.path.join(cwd, f)
+ if ext.lower() in ext_set and os.path.getsize(fullname):
+ yield fullname
+
+ if __name__ == "__main__":
+ # genera il file e lo mostra
+ images = file("images.html", "w")
+ for el in gentableHTML(get_files_with_ext(set([".png"]),
+ "C:\\Documents and Settings"), 15):
+ print >> images, el,
+ images.close()
+ webbrowser.open("images.html")
+
+Soluzione più sofisticata, per darvi qualcosa su cui pensare::
+
+ #<maketable.py>
+
+ def get_files_with_ext(ext, d):
+ """
+ ext can be a string or a set of strings; for instance
+ get_files_with_ext(".png") or get_files_with_ext(set(".png", ".gif"))
+ """
+ if not isinstance(ext, set):
+ ext_set = set([ext])
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ if ext.lower() in ext_set:
+ yield os.path.join(cwd, f)
+
+ class Picture(object):
+ """An example of the utility of __str__"""
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return "<img src=%r width=100>" % self.pathname
+
+ if sys.platform == 'win32':
+ drive = "C:\\"
+ else:
+ drive = "/"
+ showtable(map(Picture, get_files_with_ext(".jpg", drive)), N)
+
+ #</maketable.py>
+
diff --git a/pypers/marelli/materiale/del_with_exc.py b/pypers/marelli/materiale/del_with_exc.py
new file mode 100755
index 0000000..ee09bf1
--- /dev/null
+++ b/pypers/marelli/materiale/del_with_exc.py
@@ -0,0 +1,10 @@
+# del_with_exc.py
+
+class C(object):
+ def __del__(self):
+ print "Hai chiamato del"
+
+c = C()
+raise RuntimeError("Ahi ahi!")
+
+
diff --git a/pypers/marelli/materiale/doctest_runner.py b/pypers/marelli/materiale/doctest_runner.py
new file mode 100755
index 0000000..cd528c9
--- /dev/null
+++ b/pypers/marelli/materiale/doctest_runner.py
@@ -0,0 +1,8 @@
+# doctest_runner.py
+
+import doctest
+
+if __name__== "__main__":
+ doctest.testfile("test_isnumber.txt")
+
+
diff --git a/pypers/marelli/materiale/doctest_talk/Makefile b/pypers/marelli/materiale/doctest_talk/Makefile
new file mode 100755
index 0000000..77a6cc0
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P01.html b/pypers/marelli/materiale/doctest_talk/P01.html
new file mode 100755
index 0000000..a3a9e4d
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P02.html b/pypers/marelli/materiale/doctest_talk/P02.html
new file mode 100755
index 0000000..b187e50
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P03.html b/pypers/marelli/materiale/doctest_talk/P03.html
new file mode 100755
index 0000000..dbfc5f8
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P04.html b/pypers/marelli/materiale/doctest_talk/P04.html
new file mode 100755
index 0000000..b05ff1a
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P05.html b/pypers/marelli/materiale/doctest_talk/P05.html
new file mode 100755
index 0000000..62b628a
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P06.html b/pypers/marelli/materiale/doctest_talk/P06.html
new file mode 100755
index 0000000..f4a18cb
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P07.html b/pypers/marelli/materiale/doctest_talk/P07.html
new file mode 100755
index 0000000..b6c31ae
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P08.html b/pypers/marelli/materiale/doctest_talk/P08.html
new file mode 100755
index 0000000..6c6c43e
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P09.html b/pypers/marelli/materiale/doctest_talk/P09.html
new file mode 100755
index 0000000..0e92531
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P10.html b/pypers/marelli/materiale/doctest_talk/P10.html
new file mode 100755
index 0000000..19c6e36
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P11.html b/pypers/marelli/materiale/doctest_talk/P11.html
new file mode 100755
index 0000000..e838716
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P12.html b/pypers/marelli/materiale/doctest_talk/P12.html
new file mode 100755
index 0000000..230eb27
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P13.html b/pypers/marelli/materiale/doctest_talk/P13.html
new file mode 100755
index 0000000..b2fe415
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P14.html b/pypers/marelli/materiale/doctest_talk/P14.html
new file mode 100755
index 0000000..5c16abf
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P15.html b/pypers/marelli/materiale/doctest_talk/P15.html
new file mode 100755
index 0000000..a6843e3
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P16.html b/pypers/marelli/materiale/doctest_talk/P16.html
new file mode 100755
index 0000000..d5b4541
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P17.html b/pypers/marelli/materiale/doctest_talk/P17.html
new file mode 100755
index 0000000..da93de5
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P18.html b/pypers/marelli/materiale/doctest_talk/P18.html
new file mode 100755
index 0000000..0acc512
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P19.html b/pypers/marelli/materiale/doctest_talk/P19.html
new file mode 100755
index 0000000..744a43b
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P20.html b/pypers/marelli/materiale/doctest_talk/P20.html
new file mode 100755
index 0000000..8aea60d
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P21.html b/pypers/marelli/materiale/doctest_talk/P21.html
new file mode 100755
index 0000000..34a3aa5
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P22.html b/pypers/marelli/materiale/doctest_talk/P22.html
new file mode 100755
index 0000000..929bbb5
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P23.html b/pypers/marelli/materiale/doctest_talk/P23.html
new file mode 100755
index 0000000..4ab3427
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P24.html b/pypers/marelli/materiale/doctest_talk/P24.html
new file mode 100755
index 0000000..276299f
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P25.html b/pypers/marelli/materiale/doctest_talk/P25.html
new file mode 100755
index 0000000..daae306
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/P26.html b/pypers/marelli/materiale/doctest_talk/P26.html
new file mode 100755
index 0000000..cb376d4
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/abstract.txt b/pypers/marelli/materiale/doctest_talk/abstract.txt
new file mode 100755
index 0000000..e671aae
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/doct_pkg.py b/pypers/marelli/materiale/doctest_talk/doct_pkg.py
new file mode 100755
index 0000000..331fcca
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/doctester_frontend.py b/pypers/marelli/materiale/doctest_talk/doctester_frontend.py
new file mode 100755
index 0000000..0a1acc7
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/doctester_frontend.txt b/pypers/marelli/materiale/doctest_talk/doctester_frontend.txt
new file mode 100755
index 0000000..72233d4
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/ex24.py b/pypers/marelli/materiale/doctest_talk/ex24.py
new file mode 100755
index 0000000..ce45cf8
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/ex_inner.py b/pypers/marelli/materiale/doctest_talk/ex_inner.py
new file mode 100755
index 0000000..b59dc3d
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/index.html b/pypers/marelli/materiale/doctest_talk/index.html
new file mode 100755
index 0000000..870e229
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/index.txt b/pypers/marelli/materiale/doctest_talk/index.txt
new file mode 100755
index 0000000..8988e3e
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/maketalk.py b/pypers/marelli/materiale/doctest_talk/maketalk.py
new file mode 100755
index 0000000..abd3394
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/more.txt b/pypers/marelli/materiale/doctest_talk/more.txt
new file mode 100755
index 0000000..0e0b7fc
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/refresh.txt b/pypers/marelli/materiale/doctest_talk/refresh.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/marelli/materiale/doctest_talk/refresh.txt
diff --git a/pypers/marelli/materiale/doctest_talk/split-failure.txt b/pypers/marelli/materiale/doctest_talk/split-failure.txt
new file mode 100755
index 0000000..0bce3e2
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/split-failure_txt.py b/pypers/marelli/materiale/doctest_talk/split-failure_txt.py
new file mode 100755
index 0000000..6819fb5
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/split.html b/pypers/marelli/materiale/doctest_talk/split.html
new file mode 100755
index 0000000..3cc867a
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/split.py b/pypers/marelli/materiale/doctest_talk/split.py
new file mode 100755
index 0000000..fda986c
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/split.txt b/pypers/marelli/materiale/doctest_talk/split.txt
new file mode 100755
index 0000000..8e3e8fe
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/talk.txt b/pypers/marelli/materiale/doctest_talk/talk.txt
new file mode 100755
index 0000000..c3b2991
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/test_pkg/__init__.py b/pypers/marelli/materiale/doctest_talk/test_pkg/__init__.py
new file mode 100755
index 0000000..3963e63
--- /dev/null
+++ b/pypers/marelli/materiale/doctest_talk/test_pkg/__init__.py
@@ -0,0 +1,6 @@
+"""Trying to doctest a package.
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/marelli/materiale/doctest_talk/test_pkg/a.py b/pypers/marelli/materiale/doctest_talk/test_pkg/a.py
new file mode 100755
index 0000000..ef2abf0
--- /dev/null
+++ b/pypers/marelli/materiale/doctest_talk/test_pkg/a.py
@@ -0,0 +1,7 @@
+"""
+Module a
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/marelli/materiale/doctest_talk/test_pkg/b.py b/pypers/marelli/materiale/doctest_talk/test_pkg/b.py
new file mode 100755
index 0000000..f17cd98
--- /dev/null
+++ b/pypers/marelli/materiale/doctest_talk/test_pkg/b.py
@@ -0,0 +1,7 @@
+"""
+Module b
+
+>>> 1 + 1 == 2
+True
+
+"""
diff --git a/pypers/marelli/materiale/doctest_talk/testfile_ex.py b/pypers/marelli/materiale/doctest_talk/testfile_ex.py
new file mode 100755
index 0000000..3d85fcc
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctest_talk/the_story.txt b/pypers/marelli/materiale/doctest_talk/the_story.txt
new file mode 100755
index 0000000..ccdbdbd
--- /dev/null
+++ b/pypers/marelli/materiale/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/marelli/materiale/doctester.py b/pypers/marelli/materiale/doctester.py
new file mode 100755
index 0000000..e759e5b
--- /dev/null
+++ b/pypers/marelli/materiale/doctester.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2.4
+# Author: michele.simionato@gmail.com
+"""\
+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)
+
+class file_(file):
+ """This is a file class which treats specially the filename "-",
+ returning stdin or stdout according to the mode."""
+ def __new__(cls, name, mode="r", buffering=1):
+ if name == "-" and "w" in mode or "a" in mode:
+ return sys.stdout
+ elif name == "-" and "r" in mode:
+ return sys.stdin
+ return super(file_, cls).__new__(cls, name, mode, buffering)
+
+# 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(fname, 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
+ return scriptdict
+
+# based on a clever trick: converts the original text into the docstring of
+# the _main module; works both for Python 2.3 and 2.4; threads work properly
+# too.
+
+def runtests(fname, txt, verbose=False):
+ if "_main.py" in savescripts(fname, txt):
+ _main = __import__("_main") # real module
+ else: # dynamic module
+ _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
+ try: fname = sys.argv[1]
+ except IndexError: sys.exit(__doc__)
+ valid_options = set("-v -h".split())
+ options = set(sys.argv[2:])
+ assert options < valid_options, "Unrecognized option"
+ if "-h" in options: # print usage message and exit
+ sys.exit(__doc__)
+ runtests(fname, file_(fname).read(), "-v" in options)
diff --git a/pypers/marelli/materiale/esempio1.py b/pypers/marelli/materiale/esempio1.py
new file mode 100755
index 0000000..61eb4b0
--- /dev/null
+++ b/pypers/marelli/materiale/esempio1.py
@@ -0,0 +1,20 @@
+# esempio1.py
+
+import threading, time, sys
+
+def print_hello():
+ for i in range(10):
+ print "hello"
+ if i == 5:
+ raise RuntimeError("Problema a runtime")
+ time.sleep(1)
+
+def print_world():
+ for i in range(10):
+ print "world"
+ time.sleep(1)
+
+threading.Thread(target=print_hello).start()
+threading.Thread(target=print_world).start()
+
+
diff --git a/pypers/marelli/materiale/esempio_banale.py b/pypers/marelli/materiale/esempio_banale.py
new file mode 100755
index 0000000..bdc14d1
--- /dev/null
+++ b/pypers/marelli/materiale/esempio_banale.py
@@ -0,0 +1,12 @@
+# esempio_banale.py
+
+def sum12():
+ """Questa funzione ritorna la somma di 1 + 2::
+ >>> sum12()
+ 3"""
+ return 1+2
+
+if __name__ == "__main__":
+ import doctest; doctest.testmod()
+
+
diff --git a/pypers/marelli/materiale/example.py b/pypers/marelli/materiale/example.py
new file mode 100755
index 0000000..10da18f
--- /dev/null
+++ b/pypers/marelli/materiale/example.py
@@ -0,0 +1,4 @@
+import exc_debug
+a = 1
+b = 0
+a/b
diff --git a/pypers/marelli/materiale/exc_debug.py b/pypers/marelli/materiale/exc_debug.py
new file mode 100755
index 0000000..32d507c
--- /dev/null
+++ b/pypers/marelli/materiale/exc_debug.py
@@ -0,0 +1,19 @@
+# recipe in the Python cookbook first edition, chapter 14.5
+
+import sys
+
+def info(type, value, tb):
+ if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \
+ type == SyntaxError:
+ # we are in interactive mode or we don't have a tty-like
+ # device, so we call the default hook
+ sys.__excepthook__(type, value, tb)
+ else:
+ import traceback, pdb
+ # we are NOT in interactive mode, print the exception...
+ traceback.print_exception(type, value, tb)
+ print
+ # ...then start the debugger in post-mortem mode.
+ pdb.pm()
+
+sys.excepthook = info
diff --git a/pypers/marelli/materiale/gentable.py b/pypers/marelli/materiale/gentable.py
new file mode 100755
index 0000000..cf1c3cf
--- /dev/null
+++ b/pypers/marelli/materiale/gentable.py
@@ -0,0 +1,17 @@
+# gentable.py
+
+def gentable(N):
+ for i in range(1, N+1):
+ for j in range(1, N+1):
+ yield i*j
+
+def printtable(lst, N):
+ for i, el in enumerate(lst):
+ print "%4d" % el,
+ if (i+1) % 10 == 0:
+ print
+
+if __name__ == "__main__":
+ printtable(gentable(10), 10)
+
+
diff --git a/pypers/marelli/materiale/getattr_ex.py b/pypers/marelli/materiale/getattr_ex.py
new file mode 100755
index 0000000..0140ea4
--- /dev/null
+++ b/pypers/marelli/materiale/getattr_ex.py
@@ -0,0 +1,14 @@
+# getattr_ex.py
+
+class C(object):
+ def m1(self):
+ print "chiamato m1"
+ def m2(self):
+ print "chiamato m2"
+
+if __name__ == "__main__":
+ c = C()
+ method = raw_input("Che metodo devo chiamare? [m1 o m2] ")
+ getattr(c, method)()
+
+
diff --git a/pypers/marelli/materiale/isnumber.py b/pypers/marelli/materiale/isnumber.py
new file mode 100755
index 0000000..024665c
--- /dev/null
+++ b/pypers/marelli/materiale/isnumber.py
@@ -0,0 +1,12 @@
+# isnumber.py
+
+def is_number(arg):
+ "Verifica se la stringa arg è un numero valido"
+ try:
+ float(arg)
+ except ValueError:
+ return False
+ else:
+ return True
+
+
diff --git a/pypers/marelli/materiale/main.py b/pypers/marelli/materiale/main.py
new file mode 100755
index 0000000..c1495a5
--- /dev/null
+++ b/pypers/marelli/materiale/main.py
@@ -0,0 +1,14 @@
+# main.py
+
+"Chiama due processi proc1a.py e proc1b.py"
+
+import subprocess
+
+CMD_a = ["python", "-c", "import proc1a; proc1a.main()"]
+CMD_b = ["python", "-c", "import proc1b; proc1b.main()"]
+
+if __name__ == "__main__":
+ p_a = subprocess.Popen(CMD_a)
+ p_b = subprocess.Popen(CMD_b)
+
+
diff --git a/pypers/marelli/materiale/maketable.py b/pypers/marelli/materiale/maketable.py
new file mode 100755
index 0000000..f950293
--- /dev/null
+++ b/pypers/marelli/materiale/maketable.py
@@ -0,0 +1,67 @@
+# maketable.py
+
+# non graphic
+N = 10
+for i in range(1, N+1):
+ for j in range(1, N+1):
+ print "%4d" % (i*j),
+ print
+
+# HTML
+def maketable(iterable, N):
+ iterable = iter(iterable)
+ print "<table border='1'>"
+ stop = False
+ while not stop:
+ print "<tr>"
+ for j in range(1, N+1):
+ try:
+ print "<td>%s</td>" % iterable.next(),
+ except StopIteration:
+ print "<td></td>"
+ stop = True
+ print "</tr>"
+ print "</table>"
+
+import tempfile, webbrowser, os, sys
+
+def showtable(iterable, N):
+ stdout = sys.stdout # shows how to redirect stdout correctly
+ fd, name = tempfile.mkstemp(suffix=".html")
+ sys.stdout = os.fdopen(fd, "w")
+ maketable(iterable, N)
+ webbrowser.open(name)
+ sys.stdout = stdout
+
+showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N)
+
+
+
+def get_files_with_ext(ext, d):
+ """
+ ext can be a string or a set of strings; for instance
+ get_files_with_ext(".png") or get_files_with_ext(set(".png", ".gif"))
+ """
+ if not isinstance(ext, set):
+ ext_set = set([ext])
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ if ext.lower() in ext_set:
+ yield os.path.join(cwd, f)
+
+class Picture(object):
+ """An example of the utility of __str__"""
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return "<img src=%r width=100>" % self.pathname
+
+if sys.platform == 'win32':
+ drive = "C:\\"
+else:
+ drive = "/"
+showtable(map(Picture, get_files_with_ext(".jpg", drive)), N)
+
+
diff --git a/pypers/marelli/materiale/proc1a.py b/pypers/marelli/materiale/proc1a.py
new file mode 100755
index 0000000..5402c7d
--- /dev/null
+++ b/pypers/marelli/materiale/proc1a.py
@@ -0,0 +1,13 @@
+# proc1a.py
+
+import time
+
+def main():
+ for i in range(10):
+ print "hello"
+ time.sleep(1)
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/pypers/marelli/materiale/proc1b.py b/pypers/marelli/materiale/proc1b.py
new file mode 100755
index 0000000..5d290d2
--- /dev/null
+++ b/pypers/marelli/materiale/proc1b.py
@@ -0,0 +1,20 @@
+# proc1b.py
+
+import time, sys
+
+def main():
+ try:
+ f = file("proc1b.py")
+ for i in range(10):
+ print "world"
+ if i == 5:
+ raise RuntimeError("Ahia!")
+ time.sleep(1)
+ finally:
+ f.close()
+ print "Il file è stato chiuso correttamente."
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/pypers/marelli/materiale/proc2a.py b/pypers/marelli/materiale/proc2a.py
new file mode 100755
index 0000000..565b1b1
--- /dev/null
+++ b/pypers/marelli/materiale/proc2a.py
@@ -0,0 +1,17 @@
+# proc2a.py
+"Un processo che genera numeri casuali e li salva nel file data.txt"
+
+import random
+
+def main():
+ ro = random.Random()
+ out = file("data.txt", "w")
+ for number in ro.sample(range(1000), 100):
+ print >> out, number
+ out.close()
+ print "Dati salvati sul file 'data.txt'"
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/pypers/marelli/materiale/proc2b.py b/pypers/marelli/materiale/proc2b.py
new file mode 100755
index 0000000..19ae87c
--- /dev/null
+++ b/pypers/marelli/materiale/proc2b.py
@@ -0,0 +1,15 @@
+# proc2b.py
+
+"Un processo che genera l'istogramma histo.png dai dati in data.txt"
+
+from pylab import hist,savefig
+
+def main():
+ hist([int(n) for n in file("dat.txt")], 10)
+ savefig("histo.png")
+ print "Istogramma salvato sul file 'histo.png'"
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/pypers/marelli/materiale/test_exc.py b/pypers/marelli/materiale/test_exc.py
new file mode 100755
index 0000000..1a71452
--- /dev/null
+++ b/pypers/marelli/materiale/test_exc.py
@@ -0,0 +1,20 @@
+# test_exc.py
+
+import unittest
+
+def divide(a, b):
+ return a/b
+
+class TestIsNumber(unittest.TestCase):
+ def test_1(self):
+ "Divide 4/2"
+ self.assertEqual(divide(4,2), 2)
+ def test_2(self):
+ "Divide 4/0"
+ self.assertRaises(ZeroDivisionError, divide, 4, 0)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+
diff --git a/pypers/marelli/materiale/test_isnumber.py b/pypers/marelli/materiale/test_isnumber.py
new file mode 100755
index 0000000..f5acbf2
--- /dev/null
+++ b/pypers/marelli/materiale/test_isnumber.py
@@ -0,0 +1,43 @@
+# test_isnumber.py
+
+import unittest
+
+from isnumber import is_number
+
+class TestIsNumber(unittest.TestCase):
+
+ def setUp(self):
+ print "sto inizializzando"
+
+ # test positivi
+ def test_1(self):
+ "Testa che '1' è un numero buono."
+ self.assertTrue(is_number("1"))
+ def test_2(self):
+ "Testa che '1.3' è un numero buono."
+ self.assertTrue(is_number("1.3"))
+ def test_3(self):
+ "Testa che '+1.3' è un numero buono."
+ self.assertTrue(is_number("+1.3"))
+ def test_4(self):
+ "Testa che '-1.3' è un numero buono."
+ self.assertTrue(is_number("-1.3"))
+
+ # test negativi
+ def test_5(self):
+ "Testa che '1-.3' non è un numero buono."
+ self.assertFalse(is_number("1-.3"))
+ def test_6(self):
+ "Testa che 'à non è un numero buono."
+ self.assertFalse(is_number("a"))
+ def test_7(self):
+ "Testa che '42' è un numero buono."
+ self.assertTrue(is_number("42"))
+
+ def tearDown(self):
+ print "Sto chiudendo quello che c'è da chiudere"
+
+if __name__ == "__main__":
+ unittest.main()
+
+
diff --git a/pypers/marelli/materiale/twisted_main.py b/pypers/marelli/materiale/twisted_main.py
new file mode 100755
index 0000000..116852f
--- /dev/null
+++ b/pypers/marelli/materiale/twisted_main.py
@@ -0,0 +1,37 @@
+# twisted_main.py
+
+"Il main che chiama proc2a.py e proc2b.py nell'ordine e gestisce gli errori"
+
+import webbrowser, sys
+if sys.platform == "win32":
+ from twisted.internet import win32eventreactor
+ win32eventreactor.install()
+
+from twisted.internet.utils import getProcessOutput
+from twisted.internet import reactor
+
+def scrivi_messaggio(err):
+ print err.getErrorMessage()
+ reactor.stop()
+ import pdb; pdb.set_trace() # fa partire il debugger in caso di errore
+
+def visualizza_histo(out_di_genera_histo):
+ print out_di_genera_histo
+ webbrowser.open("histo.png")
+
+def genera_histo(out_di_genera_dati):
+ print out_di_genera_dati
+ getProcessOutput(sys.executable, (r"c:\corso\processi\proc2b.py",)) \
+ .addCallback(visualizza_histo) \
+ .addErrback(scrivi_messaggio)
+
+def genera_dati():
+ getProcessOutput(sys.executable, (r"c:\corso\processi\proc2a.py",)) \
+ .addCallback(genera_histo) \
+ .addErrback(scrivi_messaggio)
+
+if __name__ == "__main__":
+ reactor.callLater(0, genera_dati) # call "genera_dati" after 0 seconds
+ reactor.run()
+
+
diff --git a/pypers/marelli/modulo1/debug_me.py b/pypers/marelli/modulo1/debug_me.py
new file mode 100755
index 0000000..d3e0e81
--- /dev/null
+++ b/pypers/marelli/modulo1/debug_me.py
@@ -0,0 +1,9 @@
+def f():
+ print "step 1"
+ print "step 2"
+ print "step 3"
+
+f()
+print "step 4"
+print "step 5"
+print "step 6"
diff --git a/pypers/marelli/modulo1/try_finally.py b/pypers/marelli/modulo1/try_finally.py
new file mode 100755
index 0000000..ca512a4
--- /dev/null
+++ b/pypers/marelli/modulo1/try_finally.py
@@ -0,0 +1,17 @@
+"""
+How try .. finally works:
+CTRL-C is caught, CTRL-Break is NOT
+"""
+import time
+F = "x.txt"
+
+f = file(F, "w")
+try:
+ for i in range(10):
+ print >> f, "line %s" % (i + 1)
+ time.sleep(1)
+finally:
+ f.close()
+ print "File %r was closed correctly. Current content:" % F
+ for line in file(F): print line,
+
diff --git a/pypers/marelli/modulo1/x.txt b/pypers/marelli/modulo1/x.txt
new file mode 100755
index 0000000..fc287e2
--- /dev/null
+++ b/pypers/marelli/modulo1/x.txt
@@ -0,0 +1,7 @@
+line 1
+line 2
+line 3
+line 4
+line 5
+line 6
+line 7
diff --git a/pypers/marelli/modulo2/TestLauncher.py b/pypers/marelli/modulo2/TestLauncher.py
new file mode 100755
index 0000000..f11a6b4
--- /dev/null
+++ b/pypers/marelli/modulo2/TestLauncher.py
@@ -0,0 +1,2902 @@
+#############################################################################################################
+# FILE: TestLauncher.py
+# AUTHOR: Alberto Mantovani
+# DATE: 16.07.2003
+# DESCRIPTION: Interfaccia grafica di lancio test
+# - individua i file descrittori dei TD
+# - crea l'interfaccia grafica relativa alle informazioni trovate
+# - lancia gli script che a loro volta lanciano i TD
+# - visualizza il risultato dei singoli TC
+#
+# REQUIREMENTS: file .txt descrittori dei TD
+# Ultimo Identificativo per pulsanti usato=156 (devono essere esclusi gli identificativi terminanti con 0 1 2)
+#
+# MODIFY
+# AUTHOR: Alberto Mantovani
+# DATE: 12.08.2003
+# DESCRIPTION: Al lancio dell'eseguibile viene creato un file path.py che contiene la costante LauncherPath
+# con il percorso appunto del launcher.
+# DATE: 03.10.2003
+# DESCRIPTION:
+# - I descrittori di files lay_descr.txt sono aperti solo in lettura e non anche in scrittura
+# - Nel combo del project non vengono visualizzati i files, e le cartelle del report e trash
+# - Pulsante per cancellare i moduli importati
+# - Pulsante per settare il path di lavoro
+# - Pulsante per cambiare gli stimoli
+# DATE: 22.12.2003
+# DESCRIPTION:
+# - Viene esteso il Launcher anche all'NSI Tester e predisposto per lo SPIL. Per fare ciò viene preso in considerazione e
+# anche il tag *INSTRUMENT presente nel lay_descr.txt. Le TD relative all'NSI saranno visualizzate in BLU
+# mentre rimarranno nere quelle del HIL. Quelle per lo SPIL sono in bianco.
+# Questo ha portato a modificare il file led_status.txt in quanto oltre alla lista dei LED vi è anche un flag
+# che segnala la fine della sessione per un certo strumento.
+# - E' stato aggiunto un pulsantino che permette di aprire il log. Rimane verde se non ci sono stati
+# problemi d'esecuzione, rosso altrimenti.
+# - La dimensione X del radiobox è stata fissata anzichè ottenerla automaticamente. In questo modo Test Case
+# dal nome lungo non deformano la grafica.
+# - Si fa un controllo sulla lunghezza del titolo della TD e se troppo lungo si diminuisce il font e si va a capo
+# - Messa icona di APOLLO13 nella barra del frame e compilando con l'opzione python setup.py py2exe --icon launcher.ico
+# si ottiene icona per l'eseguibile. (solo se si compila da NT o Win2000)
+# DATE: 22.12.2003
+# DESCRIPTION:
+# - E' cambiata la gestione del colore del pulsante di log. Per farlo diventare rosso si fa un confronto tra
+# lo stato dei led e i test selezionati. In questo modo il pulsante di log diventa rosso anche per i test di HIL
+# DATE: 28.03.2004
+# DESCRIPTION:
+# - Correzione baco : in modalità debug lancio il primo TD selezionato e non il primo assoluto
+# DATE: 06.04.2004
+# DESCRIPTION:
+# - Per i test NSI riconosco che c'è errore di compilazione se vedo diverso da 0 a sx di error(s) e non error
+# in quanto vi possono essere casi di ambiguità
+# - Se vi è un errore di sistema che fa colorare il log di rosso scrivo nel log TEST FALLITO
+#4.0.3 :
+# Modifica gestione errori in caso di errata lettura del file load
+# Correzione nome zip
+# Modifica nome cartella report (messo i campi di gg hh mm ss tra parentesi)
+#4.0.4 :
+# Passo alla setvar anche il path del progetto
+#4.0.5 :
+# Gestisco OL e CL passandolo alla MatStop
+# Creato pulsante nella finestra delle utility SPIL che converte gli output SPIL in output Matlab
+#4.0.6 :
+# Correzione Baco sulla selezione librerie di progetto
+#4.0.7 :
+# Settaggio variabile d'ambiente LIB x NSI in modalità RUN
+# Aggiunto path del launcher nel file command
+#4.0.8 :
+# Possibilità di scegliere l'opzione coverage
+# Lancio dello stimulus editor x SPIL
+# Correzione baco su scelta progetto nel caso delle utility SPIL
+# Creazione della cartella DD nel caso non esista
+#4.0.9 :
+# Nella modalità debug mancava un \n alla fine dell'IssStrtup.cmm
+#4.0.10 :
+# Gestione degli errori nella fase di pre-processing post-processing e SPIL
+#4.0.11 :
+# Anche in modalità debug scrivo nel report la parte di SETUP
+#4.0.12 :
+#-Aggiunta chekbox nel dSPACE che cancella i file .pyc se nella stessa cartella vi sono file omonimi
+#ma con estensione .py. La ricerca vieve effettuata nelle librerie, nei TD e nella cartella Work
+#-Messo nella configurazione dell'HIL il checkbox per ottenere la lista del NS di riferimento
+#-Aggiunta possibilità di ripetere la sequenza di test selezionati
+#-Correzione baco nel caso non vi sia il file degli IR
+#-Aggiunto pulsante SPIL x convertire pattern da STB a SDT
+#4.0.13 :
+#-Correzioni baco x l'iterzioni dei test
+#4.0.14 :
+#-Correzioni baco x il coverage quando non si sceglie il primo TC
+#4.0.15:
+#-Creazione del menù File in cui c'è l'opzione Exit
+#-Correzione path assoluto nello SPIL per il caricamento del load.cmm
+#4.0.16:
+#-Messa opzione Debug IDLE x dspace
+#4.0.17:
+#-Gestione diagnostica errori nel MatStrtp solo in fase RUN
+#4.0.18:
+#-Correzione baco nella gestione della ripetizione dei test (in realtà non funzionava). Il for è sulla RUN_TEST
+#anzichè essere su ogni strumento. La fine del test si ha quando sono finiti tutti gli strumenti e anche il
+#ciclo è finito. L'inizializzazione del file led_status.txt è fatto ad ogni ripetizione e quindi è stato sposta-
+#to nella RUN_TEST. Tra le altre modifiche la ripetizione non è abilitata in debug mode
+#-Ci si svincola completamente dal percorso M:\work
+#-Il browser dello stimulus editor dello SPIL diventa browser di file
+#-Il change stimulus dell'HIL si colloca nella directory del progetto
+#-Nel MatStrtp viene aggiunto il parametro CompareList nella IOCdiag
+#-Correzione scrittura nel log di test eseguiti o falliti x NSI
+#-
+############################################################################################################
+
+
+#################################################
+# INCLUDED FILES (libraries)
+#################################################
+import sys
+from wxPython.wx import*
+import threading
+import string
+import glob
+import os
+import time
+import traceback
+import shutil
+import stat
+import time
+import zipfile
+import win32api
+############################################################################################################
+# FUNCTION NAME : GetSpilTdData
+# FUNCTION DESCRIPTION:
+# Specificatamente per lo SPIL vengono creati automaticamente i campi di *MAIN,*TD e *TC prendendo le informazioni
+# dai nomi presenti nel TD
+#
+# INPUT PARAMETERS:
+# 1) TD: è il path della TD in cui si vanno a ricercare i files.
+#
+# OUTPUT PARAMETERS:
+# 1) TD aggiornato con i campi *MAIN *TD e *TC
+# es:['M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM08_4TV', 'SpilMainDummy', ['SM08','SM08_TP_01_in', 'SM08_TP_02_in']]
+############################################################################################################
+def GetSpilTdData(TD):
+ FileSearch=TD[0]+'\\*_TP_??_in.mat'
+ TClist=glob.glob(FileSearch)
+ tmp=[]
+ TDname=[]
+ for TCpath in TClist:
+ tmp.append(os.path.splitext(os.path.basename(TCpath))[0])
+ if tmp!=[]:
+ tmp.sort()
+ TDnamestp=string.find(tmp[-1],"_TP_")
+ TDname=[tmp[-1][0:TDnamestp]]
+ tmp=TDname+tmp
+ TD[1]=('SpilMainDummy')
+ TD.append(tmp)
+ else:
+ TD=[]
+ return TD
+
+
+
+############################################################################################################
+# FUNCTION NAME : TdParse
+# FUNCTION DESCRIPTION:
+# Vengono ricercati tutti i file che rispondono al path passato come argomento e analizzato il loro contenuto.
+# Questi files devono contenere le caratteristiche della TD per il layout grafico e il nome del file main della TD
+# Affinchè il TD sia trovato dal launcher, in ogni cartella TD deve esserci un file descrittore di TD es:lay_descr.txt
+#
+# INPUT PARAMETERS:
+# 1) file_path: è il path in cui si vanno a ricercare i files. (* è il simbolo jolly) es: "M:\Work\*\*\*\lay_descr.txt"
+#
+# OUTPUT PARAMETERS:
+# 1) TD_DATA: è la lista dei dati raccolti dal parse in generale è:
+# TD_DATA=[['TD1path','TD1main',["TD1 NAME","TC1 NAME","TC2 NAME",..,"TCn NAME"]],...,['TDmpath','TDmmain',["TDm NAME","TC1 NAME",...,"TCz NAME"]]]
+# -TDxpath è il path della TD
+# -TDxmain è il file main della TD
+# -TDx NAME è la label del TD
+# -TCx NAME è la label dei TC
+# raccoglie i dati di tutti gli strumenti presenti nell'ordine HIL,NSI,SPIL
+# 2) [0,RangeHIL,RangeNSI,RangeSPIL] poichè il TD_DATA non ha nessun riferimento a quali sono
+# i test relativi ad uno strumento piuttosto che ad un'altro occorre passare questo vettore che
+# indica per ogni strumento quali TD fanno capo. Se per es ci fossero 3 TD x HIL,2 TD x NSI,0 TD x SPIL
+# il vettore sarebbe [0,3,5,5]
+############################################################################################################
+
+def TdParse(file_path):
+ #main_list è la lista dei file descrittori trovati con il criterio del file_path (ogni cartella TD deve averne uno solo)
+ main_list=glob.glob(file_path)
+ #LEGGO IL FILE .TXT DI OGNI TD
+ TD_DATA=[] #raccoglitore globale sul quale viene costruito il layout
+ TD_DATA_HILdSPACE=[] #raccoglitore dei test su dspace
+ TD_DATA_NSI=[] #raccoglitore test su NSI
+ TD_DATA_SPIL=[] #raccoglitore test su SPIL
+ for mainpy in main_list:
+ #check flag che viene settato a 0 se il descrittore non è compilato correttamente per quanto riguarda le informazioni grafiche
+ check=1
+ #path della TD
+ path=os.path.dirname(mainpy)
+ f=open(mainpy,'r')
+ f.seek(0)
+ testo=f.read()
+ f.close()
+ TD=[path]
+
+ #Leggo il campo MAIN (file che viene lanciato dal launcher)
+ funzione_start=string.find(testo,"*MAIN=")
+ if funzione_start>=0 :
+ line_start=string.find(testo,"=",funzione_start)+1
+ line_stop=string.find(testo,"\n",line_start)
+ line=testo[line_start:line_stop]
+ TD=TD+[line]
+ else:
+ check=0
+
+ #Leggo il campo TD (label nome della TD)
+ TC=[]
+ td_start=string.find(testo,"*TD=")
+ if td_start>=0 :
+ line_start=string.find(testo,"=",td_start)+1
+ line_stop=string.find(testo,"\n",line_start)
+ line=testo[line_start:line_stop]
+ TC=TC+[line]
+ else:
+ check=0
+
+ #Leggo il campo TC (label nome della TC)
+ tc_start=0
+ while tc_start>=0:
+ tc_start=string.find(testo,"*TC=",tc_start+1)
+ if tc_start>=0 :
+ line_start=string.find(testo,"=",tc_start)+1
+ line_stop=string.find(testo,"\n",line_start)
+ line=testo[line_start:line_stop]
+ TC=TC+[line]
+ if len(TC)<=1:
+ check=0
+ TD.append(TC)
+
+ #Leggo il campo INSTRUMENT e inserisco i dati raccolti nella lista del relativo strumento
+# if check==1:
+ funzione_start=string.find(testo,"*INSTRUMENT=")
+ if funzione_start>=0 :
+ line_start=string.find(testo,"=",funzione_start)+1
+ line_stop=string.find(testo,"\n",line_start)
+ line=testo[line_start:line_stop]
+ if check==1 or line=="SPIL":
+ if line=="HILdSPACE":
+ TD_DATA_HILdSPACE.append(TD)
+ elif line=="NSI":
+ TD_DATA_NSI.append(TD)
+ elif line=="SPIL":
+ TD=GetSpilTdData(TD)
+ if TD!=[]:
+ TD_DATA_SPIL.append(TD)
+ else:
+ print "TD ",path,"non importata in quanto strumento non riconosciuto \n(correggere campo *INSTRUMENT nel lay_descr.txt)"
+ else:
+ print "TD ",path,"non importata in quanto non trovati i campi *MAIN e/o *TD e/o *TC nel lay_descr.txt"
+ else:
+ print "TD ",path,"non importata in quanto campo INSTRUMENT nel lay_descr.txt non trovato"
+
+ #Metto in ordine i TD per strumento: prima HIL, poi NSI, poi SPIL
+ if len(TD_DATA_HILdSPACE)!=0:
+ TD_DATA=TD_DATA+TD_DATA_HILdSPACE
+ if len(TD_DATA_NSI)!=0:
+ TD_DATA=TD_DATA+TD_DATA_NSI
+ if len(TD_DATA_SPIL)!=0:
+ TD_DATA=TD_DATA+TD_DATA_SPIL
+ #ritorno il TD_DATA e anche una lista nella quale sono indicati i range dei TD strumento x strumento
+ RangeHIL=len(TD_DATA_HILdSPACE)
+ RangeNSI=RangeHIL+len(TD_DATA_NSI)
+ RangeSPIL=RangeNSI+len(TD_DATA_SPIL)
+ return TD_DATA,[0,RangeHIL,RangeNSI,RangeSPIL]
+
+
+
+############################################################################################################
+# FUNCTION NAME : SelectorFrame
+# FUNCTION DESCRIPTION: Creazione della finestra contenente i TD, i TC e i led
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def SelectorFrame(self):
+
+ #self.scmain.sc --> finestra con lo scroll
+ self.scmain.sc=wxScrolledWindow(self.scmain, -1,pos = (200,0), size = (self.ScreenWidth*1.3-200,self.ScreenHeight*1.3-20), style = wxHSCROLL | wxVSCROLL, name = "scrolledWindow")
+ self.scmain.sc.SetScrollbars(10, 10, 1000, 1000, xPos = 0, yPos = 0, noRefresh = FALSE)
+
+ #Caratteristiche grafiche
+ TDW=200 #larghezza TD*
+ #TCH=40 #altezza TC
+ LEDW=20 #larghezza led*
+ LEDH=20 #altezza led*
+ DXLED=165 #delta x led*
+ DYLED=15 #delta y led*
+ #DXTD=197 #delta x TD*
+ DXTD=200 #delta x TD*
+ DYTC=50 #delta y TC*
+ DXTC=5 #delta x TC*
+ SXTD=20 #start x TD*
+ SYTD=10 #start y TD*
+ TCALLW=50 #larghezza TCALL
+ TCALLH=20 #altezza TCALL
+ DYTCALL=60 #delta y TCALL
+ TCdefvalue=2 #posizione di default del radio
+ DYWIN=85 #spazio al di sotto dei TCALL
+ LEDdefvalue=0 #posizione di default del radio
+ sampleList = ['NO ', 'OL ', 'CL ']
+ font = wxFont(18, wxSWISS, wxNORMAL, wxNORMAL, False, "Arial")
+
+
+ #creo TD
+ posx=SXTD
+ posy=SYTD
+ for indexTD in range(len(self.TD_DATA)):
+ posy=SYTD
+ #self.scmain.sc.TDx --> finestra in cui x è il numero della TD considerataa
+ TDH=(len(self.TD_DATA[indexTD][2])-1)*DYTC+TCALLH+DYWIN
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+" = wxWindow(self.scmain.sc, -1, (posx,posy), (TDW,TDH), wxSUNKEN_BORDER)"
+ #es: self.scmain.sc.TD1 = wxWindow(self.scmain.sc, -1, (posx,posy), (TDW,TDH), wxSUNKEN_BORDER)
+ exec(str2exe)
+ #Se la lunghezza del nome della TD è> 15 allora rimpicciollisci e va a capo
+ if len(self.TD_DATA[indexTD][2][0])>14:
+ self.TD_DATA[indexTD][2][0]=self.TD_DATA[indexTD][2][0][:17]+"\\n"+self.TD_DATA[indexTD][2][0][17:]
+ font = wxFont(14, wxSWISS, wxNORMAL, wxNORMAL, False, "Arial")
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".textTD"+str(indexTD+1)+"=wxStaticText(self.scmain.sc.TD"+str(indexTD+1)+", -1, '"+self.TD_DATA[indexTD][2][0]+"', wxDefaultPosition, wxDefaultSize)"
+ #es: self.scmain.sc.TD1.textTD1 = =wxStaticText(self.scmain.sc.TD1, -1, "TD GLAM", wxDefaultPosition, wxDefaultSize)
+ exec(str2exe)
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".textTD"+str(indexTD+1)+".SetFont(font)"
+ #es: self.scmain.sc.TD1.textTD1.SetFont(font)
+ exec(str2exe)
+ #In base al tipo di strumento cambio il colore nella visualizzazione
+ #colore BLU per NSI
+ if self.TD_NumTdXInstr[1]<=indexTD<self.TD_NumTdXInstr[2]:
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".textTD"+str(indexTD+1)+".SetForegroundColour(wxBLUE)"
+ exec(str2exe)
+ #colore BIANCO per SPIL
+ if self.TD_NumTdXInstr[2]<=indexTD<self.TD_NumTdXInstr[3]:
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".textTD"+str(indexTD+1)+".SetForegroundColour(wxWHITE)"
+ exec(str2exe)
+
+
+ #creo radio TC e LED
+ for indexTC in range(len(self.TD_DATA[indexTD][2])-1):
+ posy=posy+DYTC
+ #self.scmain.sc.TDx.rby_x --> radiobutton dove x è il numero della TD y del TC considerato,
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".rb"+str(indexTC+1)+"_"+str(indexTD+1)+" = wxRadioBox(self.scmain.sc.TD"+str(indexTD+1)+", -1, '"+self.TD_DATA[indexTD][2][indexTC+1]+"', wxDefaultPosition, (155,-1),sampleList)"
+ #es: self.scmain.sc.TD1.rb1_1 = wxRadioBox(self.scmain.sc.TD1, -1, "tc name", wxDefaultPosition, wxDefaultSize,sampleList)
+ exec(str2exe)
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".rb"+str(indexTC+1)+"_"+str(indexTD+1)+".SetPosition((DXTC,posy))"
+ #es: self.scmain.sc.TD1.rb1_1.SetPosition((DXTC,posy))
+ exec(str2exe)
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".rb"+str(indexTC+1)+"_"+str(indexTD+1)+".SetSelection(2)"
+ #es: self.scmain.sc.TD1.rb1_1.SetSelection(2)
+ exec(str2exe)
+ #self.scmain.sc.TDx.ledy_x --> led dove x è il numero della TD y del TC considerato,
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".led"+str(indexTC+1)+"_"+str(indexTD+1)+" = wxWindow(self.scmain.sc.TD"+str(indexTD+1)+", -1, (DXLED,posy+DYLED), (LEDW,LEDH), wxSUNKEN_BORDER)"
+ #es: self.scmain.sc.TD1.led1_1 = wxWindow(self.scmain.sc.TD1, -1, (DXLED,posy+DYLED), (LEDW,LEDH), wxSUNKEN_BORDER)
+ exec(str2exe)
+ #colore BLU per NSI
+ if self.TD_NumTdXInstr[1]<=indexTD<self.TD_NumTdXInstr[2]:
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".rb"+str(indexTC+1)+"_"+str(indexTD+1)+".SetForegroundColour(wxBLUE)"
+ exec(str2exe)
+ #colore BIANCO per SPIL
+ if self.TD_NumTdXInstr[2]<=indexTD<self.TD_NumTdXInstr[3]:
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".rb"+str(indexTC+1)+"_"+str(indexTD+1)+".SetForegroundColour(wxWHITE)"
+ exec(str2exe)
+
+
+ #nox=xy --> dove x TD considerata e y è la funzione associata:0=NO, 1=OL, 2=CL
+ str2exe="no"+str(indexTD+1)+"="+str(indexTD+1)+"0"
+ #es: no1=10
+ exec(str2exe)
+ str2exe="ol"+str(indexTD+1)+"="+str(indexTD+1)+"1"
+ #es: ol1=11
+ exec(str2exe)
+ str2exe="cl"+str(indexTD+1)+"="+str(indexTD+1)+"2"
+ #es: cl1=12
+ exec(str2exe)
+
+ #creo pulsanti e relativi eventi per la selezione rapida nel TD
+ #NO
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".no"+str(indexTD+1)+"=wxButton(self.scmain.sc.TD"+str(indexTD+1)+", no"+str(indexTD+1)+",'NO',(DXTC,posy+DYTCALL),size=(TCALLW,TCALLH))"
+ #es: self.scmain.sc.TD1.no1=wxButton(self.scmain.sc.TD1, no1,'NO',(DXTC,posy+DYTCALL),size=(TCALLW,TCALLH))
+ exec(str2exe)
+ str2exe="EVT_BUTTON(self, no"+str(indexTD+1)+", self.EvtTcAll)"
+ #es: EVT_BUTTON(self, no1, self.EvtTcAll)
+ exec(str2exe)
+ #OL
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".ol"+str(indexTD+1)+"=wxButton(self.scmain.sc.TD"+str(indexTD+1)+", ol"+str(indexTD+1)+",'OL',(DXTC+TCALLW,posy+DYTCALL),size=(TCALLW,TCALLH))"
+ #es: self.scmain.sc.TD1.ol1 = wxButton(self.scmain.sc.TD1, ol1,'OL',(DXTC+TCALLW,posy+DYTCALL),size=(TCALLW,TCALLH))
+ exec(str2exe)
+ str2exe="EVT_BUTTON(self, ol"+str(indexTD+1)+", self.EvtTcAll)"
+ #es: EVT_BUTTON(self, ol1, self.EvtTcAll)
+ exec(str2exe)
+ #CL
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".cl"+str(indexTD+1)+"=wxButton(self.scmain.sc.TD"+str(indexTD+1)+", cl"+str(indexTD+1)+",'CL',(DXTC+2*TCALLW,posy+DYTCALL),size=(TCALLW,TCALLH))"
+ #es: self.scmain.sc.TD1.cl1 = wxButton(self.scmain.sc.TD1, cl1,'CL',(DXTC+2*TCALLW,posy+DYTCALL),size=(TCALLW,TCALLH))
+ exec(str2exe)
+ str2exe="EVT_BUTTON(self, cl"+str(indexTD+1)+", self.EvtTcAll)"
+ #es: EVT_BUTTON(self, cl1, self.EvtTcAll)
+ exec(str2exe)
+
+ posx=posx+DXTD
+
+
+
+############################################################################################################
+# FUNCTION NAME : TdBrowse
+# FUNCTION DESCRIPTION: Creazione di un box contenente la lista dei TD scaricati
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+#
+############################################################################################################
+def TdBrowse(self):
+ #compongo la lista prendendo le label dei descrittori di TD
+ self.TDlist=[]
+ for indexTD in self.TD_DATA:
+ self.TDlist.append(indexTD[2][0])
+ self.wnd.launcher.ch = wxListBox(self.wnd.launcher, 5, (5, 220),(150,100), choices = self.TDlist)
+ #setto evento della lista
+ EVT_LISTBOX(self, 5, self.EvtChoice)
+
+
+
+############################################################################################################
+# FUNCTION NAME : LauncherFrame
+# FUNCTION DESCRIPTION: Creazione di una finestra contenente:pulsante di GO, selezione globale dei TC, TdBrowse, selezione tipo ecu
+# Creazione pulsante GO
+# Creazione pulsanti NO OL CL
+# Creazione radiobox tipo di ecu
+# Creazione radiobox per modalità di lancio
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def LauncherFrame(self):
+ #Creazione finestra launcher
+ self.wnd.launcher = wxWindow(self.wnd, -1, (10,10), (170,400), wxSUNKEN_BORDER)
+ self.wnd.lbl=wxStaticText(self.wnd.launcher, -1," LAUNCHER", wxDefaultPosition, wxDefaultSize)
+ font = wxFont(18, wxSWISS, wxNORMAL, wxNORMAL, False, "Arial")
+ self.wnd.lbl.SetFont(font)
+ #Creazione radiobox per selezione tipo ecu
+ self.wnd.launcher.ECU = wxRadioBox(self.wnd.launcher, -1,"", (5,30), wxDefaultSize,["Ecu Svil.","Ecu Prod."])
+ #creazione pulsante GO
+ self.wnd.GO=wxButton(self.wnd.launcher, 3,'GO',pos=(5,80),size=(150,70))
+ EVT_BUTTON(self,3,self.EvtGO)
+ #Creazione SpinButton x ripetizione test
+ self.wnd.RptTestTxt = wxTextCtrl(self.wnd.launcher, 148, "1", wxPoint(65, 190), wxSize(30, 23))
+ self.wnd.RptTestSpn = wxSpinButton(self.wnd.launcher, 149, wxPoint(98, 190), wxSize(20, 22), wxSP_VERTICAL)
+ self.wnd.RptTestLbl = wxStaticText(self.wnd.launcher, -1, "Repeat for", pos=(5,195), size=(55,20),style=wxALIGN_LEFT)
+ self.wnd.RptTestLbl = wxStaticText(self.wnd.launcher, -1, "times", pos=(122,195), size=(25,20),style=wxALIGN_RIGHT)
+ self.wnd.RptTestSpn.SetRange(1, 100)
+ self.wnd.RptTestSpn.SetValue(1)
+ EVT_SPIN(self.wnd.RptTestSpn, 149, self.RptTest)
+ #creazione pulsante NO
+ self.wnd.launcher.NO=wxButton(self.wnd.launcher, 0,'NO',pos=(5,150),size=(50,30))
+ EVT_BUTTON(self,0,self.EvtTcAll)
+ #creazione pulsante OL
+ self.wnd.launcher.OL=wxButton(self.wnd.launcher, 1,'OL',pos=(55,150),size=(50,30))
+ EVT_BUTTON(self,1,self.EvtTcAll)
+ #creazione pulsante CL
+ self.wnd.launcher.CL=wxButton(self.wnd.launcher, 2,'CL',pos=(105,150),size=(50,30))
+ EVT_BUTTON(self,2,self.EvtTcAll)
+ #Creazione selettore per modalità di lancio run o debug
+ self.wnd.launcher.run = wxRadioBox(self.wnd.launcher, -1,"Launch Modality", (5,325), wxDefaultSize,["Run ","Debug "])
+ #Creazione del pulsanti di log
+ self.wnd.launcher.log=wxButton(self.wnd.launcher, 116,'Log',pos=(5,375),size=(25,15))
+ EVT_BUTTON(self,116,self.EvtLog)
+
+
+############################################################################################################
+# FUNCTION NAME : ReloadButton
+# FUNCTION DESCRIPTION: Creazione pulsante per il rinfresco della finestra del TD Selector
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def ReloadButton(self):
+ #RELOAD
+ self.wnd.reload=wxButton(self.wnd, 6,'RELOAD',pos=(5,470),size=(180,40))
+ EVT_BUTTON(self,6,self.EvtReload)
+
+############################################################################################################
+# FUNCTION NAME : MenuBar
+# FUNCTION DESCRIPTION: Creazione Menu per la configurazione dell'ambiente di lavoro
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def MenuBar(self):
+ menuBar = wxMenuBar()
+ # menu0 = File menu per Exit
+ menu0 = wxMenu()
+ menu0.Append(156, "&Exit Alt+F4", "")
+ # menu1 = General Set per settare il path dei progetti e dei files descrittori di TD
+ menu1 = wxMenu()
+ menu1.Append(108, "&Td Set...", "")
+ # menu2 = Instruments Set per settare i path dello strumento
+ menu2 = wxMenu()
+ menu2.Append(105, "&dSPACE HIL...", "")
+ menu2.Append(106, "&NSI...", "")
+ menu2.Append(107, "&SPIL...", "")
+ # Add menu to the menu bar
+ menuBar.Append(menu0, "&File")
+ menuBar.Append(menu1, "&General Set")
+ menuBar.Append(menu2, "&Instruments Set")
+ self.SetMenuBar(menuBar)
+ #setto eventi
+ EVT_MENU(self, 105, self.Configuration)
+ EVT_MENU(self, 108, self.Configuration)
+ EVT_MENU(self, 106, self.Configuration)
+ EVT_MENU(self, 107, self.Configuration)
+ EVT_MENU(self, 156, self.Configuration)
+############################################################################################################
+# FUNCTION NAME : PyConfig
+# FUNCTION DESCRIPTION: Creazione delle finestre di settaggio configurazione del python
+# -Python.exe
+# -Pythonwin.exe
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def PyConfig(self):
+ #creazione frame generale
+ self.PyConfigFrame=wxMiniFrame(None, -1, title="Hil Configuration", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.PyConfigFrame.Panel = wxPanel(self.PyConfigFrame, -1,pos=(0,0), size=(500,300))
+ #creazione text control e browse di Python.exe
+ self.PyConfigFrame.Panel.PythonLabel = wxStaticText(self.PyConfigFrame.Panel, -1, "Python.exe:", pos=(5,7), size=(95,20),style=wxALIGN_RIGHT)
+ self.PyConfigFrame.Panel.PythonText = wxTextCtrl(self.PyConfigFrame.Panel, -1, self.PythonPath,pos=(100,5), size=(300, 20))
+ self.PyConfigFrame.Panel.PythonBrowse =wxButton(self.PyConfigFrame.Panel, 1, "...",pos=(405,5),size=(18,20))
+ EVT_BUTTON(self.PyConfigFrame.Panel.PythonBrowse, 1, self.OnBrowse)
+ #creazione text control e browse di Pythonwin.exe
+ self.PyConfigFrame.Panel.PythonwinLabel = wxStaticText(self.PyConfigFrame.Panel, -1, "Pythonwin.exe:\nidle.pyw:", pos=(5,40), size=(95,50),style=wxALIGN_CENTRE)
+ self.PyConfigFrame.Panel.PythonwinText = wxTextCtrl(self.PyConfigFrame.Panel, -1, self.PythonwinPath,pos=(100,45), size=(300, 20))
+ self.PyConfigFrame.Panel.PythonwinBrowse =wxButton(self.PyConfigFrame.Panel, 2, "...",pos=(405,45),size=(18,20))
+ EVT_BUTTON(self.PyConfigFrame.Panel.PythonwinBrowse, 2, self.OnBrowse)
+ #creazione pulsante per creazione del ClearNS
+ self.PyConfigFrame.Panel.CreateNSBox = wxCheckBox(self.PyConfigFrame.Panel, 147, "Generate Pythonwin NS reference (PythonWin must be down)", wxPoint(100, 80), wxSize(380, 30), wxNO_BORDER)
+ self.PyConfigFrame.Panel.CreateNSBox.SetValue(False)
+ #self.wnd.Develop.AddPage(self.wnd.Develop.Python, "dSPACE")
+
+
+ #self.PyConfigFrame.Panel.CreateNS=wxButton(self.PyConfigFrame.Panel, 147,'Create NS',pos=(150,100),size=(200,40))
+ #EVT_BUTTON(self.PyConfigFrame.Panel.CreateNS,147,self.CreateNS)
+ #creazione pulsante salvataggio
+ self.PyConfigFrame.Panel.SavePyConfig=wxButton(self.PyConfigFrame.Panel, 4,'Save Config',pos=(150,200),size=(200,40))
+ EVT_BUTTON(self.PyConfigFrame.Panel.SavePyConfig,4,self.SaveConfig)
+ self.PyConfigFrame.Show(1)
+
+
+
+############################################################################################################
+# FUNCTION NAME : GenConfig
+# FUNCTION DESCRIPTION: Creazione delle finestre di settaggio configurazione generale del launcher
+# - lay_descr.txt
+# - Project Dir
+# -PythonLib
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def GenConfig(self):
+ #creazione frame generale
+ self.GenConfigFrame=wxMiniFrame(None, -1, title="General Configuration", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.GenConfigFrame.Panel = wxPanel(self.GenConfigFrame, -1,pos=(0,0), size=(500,300))
+ #creazione text control e browse di lay_descr.txt
+ self.GenConfigFrame.Panel.DescrLabel = wxStaticText(self.GenConfigFrame.Panel, -1, "lay_descr.txt:", pos=(5,87), size=(95,20),style=wxALIGN_RIGHT)
+ self.GenConfigFrame.Panel.DescrText = wxTextCtrl(self.GenConfigFrame.Panel, -1, self.DescrPath,pos=(100,85), size=(300, 20))
+ self.GenConfigFrame.Panel.DescrBrowse =wxButton(self.GenConfigFrame.Panel, 5, "...",pos=(405,85),size=(18,20))
+ EVT_BUTTON(self.GenConfigFrame.Panel.DescrBrowse, 5, self.OnBrowse)
+ #creazione text control e browse di Project Dir
+ self.GenConfigFrame.Panel.ProjLabel = wxStaticText(self.GenConfigFrame.Panel, -1, "Project Dir:", pos=(5,47), size=(95,20),style=wxALIGN_RIGHT)
+ self.GenConfigFrame.Panel.ProjText = wxTextCtrl(self.GenConfigFrame.Panel, -1, self.ProjPath,pos=(100,45), size=(300, 20))
+ self.GenConfigFrame.Panel.ProjBrowse =wxButton(self.GenConfigFrame.Panel, 7, "...",pos=(405,45),size=(18,20))
+ EVT_BUTTON(self.GenConfigFrame.Panel.ProjBrowse, 7, self.OnBrowse)
+ #creazione text control e browse di PythonLib
+ self.GenConfigFrame.Panel.PylibLabel = wxStaticText(self.GenConfigFrame.Panel, -1, "LibPath:", pos=(5,127), size=(95,20),style=wxALIGN_RIGHT)
+ self.GenConfigFrame.Panel.PylibText = wxTextCtrl(self.GenConfigFrame.Panel, -1, self.PylibPath,pos=(100,125), size=(300, 20))
+ self.GenConfigFrame.Panel.PylibBrowse =wxButton(self.GenConfigFrame.Panel, 3, "...",pos=(405,125),size=(18,20))
+ EVT_BUTTON(self.GenConfigFrame.Panel.PylibBrowse, 3, self.OnBrowse)
+ #creazione pulsante salvataggio
+ self.GenConfigFrame.Panel.SaveGenConfig=wxButton(self.GenConfigFrame.Panel, 6,'Save Config',pos=(150,200),size=(200,40))
+ EVT_BUTTON(self.GenConfigFrame.Panel.SaveGenConfig,6,self.SaveConfig)
+ self.GenConfigFrame.Show(1)
+
+############################################################################################################
+# FUNCTION NAME : NSIConfig
+# FUNCTION DESCRIPTION: Creazione delle finestre di settaggio configurazione dello strumento NSI
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def NSIConfig(self):
+ #creazione frame generale
+ self.NSIConfigFrame=wxMiniFrame(None, -1, title="NSI Configuration", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.NSIConfigFrame.Panel = wxPanel(self.NSIConfigFrame, -1,pos=(0,0), size=(500,300))
+ #creazione text control e browse di MSDevDir
+ self.NSIConfigFrame.Panel.MSDevDirLabel = wxStaticText(self.NSIConfigFrame.Panel, -1, "MSDevDir:", pos=(5,37), size=(95,20),style=wxALIGN_RIGHT)
+ self.NSIConfigFrame.Panel.MSDevDirText = wxTextCtrl(self.NSIConfigFrame.Panel, -1, self.NSIMSDevDir,pos=(100,35), size=(300, 20))
+ self.NSIConfigFrame.Panel.MSDevDirBrowse =wxButton(self.NSIConfigFrame.Panel, 117, "...",pos=(405,35),size=(18,20))
+ EVT_BUTTON(self.NSIConfigFrame.Panel.MSDevDirBrowse, 117, self.OnBrowse)
+ #creazione text control e browse di MSVCDir
+ self.NSIConfigFrame.Panel.MSVCDirLabel = wxStaticText(self.NSIConfigFrame.Panel, -1, "MSVCDir:", pos=(5,87), size=(95,20),style=wxALIGN_RIGHT)
+ self.NSIConfigFrame.Panel.MSVCDirText = wxTextCtrl(self.NSIConfigFrame.Panel, -1, self.NSIMSVCDir,pos=(100,85), size=(300, 20))
+ self.NSIConfigFrame.Panel.MSVCDirBrowse =wxButton(self.NSIConfigFrame.Panel, 118, "...",pos=(405,85),size=(18,20))
+ EVT_BUTTON(self.NSIConfigFrame.Panel.MSVCDirBrowse, 118, self.OnBrowse)
+ #creazione text control e browse di NSIDir
+ self.NSIConfigFrame.Panel.NSIDirLabel = wxStaticText(self.NSIConfigFrame.Panel, -1, "NSIDir:", pos=(5,137), size=(95,20),style=wxALIGN_RIGHT)
+ self.NSIConfigFrame.Panel.NSIDirText = wxTextCtrl(self.NSIConfigFrame.Panel, -1, self.NSIDir,pos=(100,135), size=(300, 20))
+ self.NSIConfigFrame.Panel.NSIDirBrowse =wxButton(self.NSIConfigFrame.Panel, 123, "...",pos=(405,135),size=(18,20))
+ EVT_BUTTON(self.NSIConfigFrame.Panel.NSIDirBrowse, 123, self.OnBrowse)
+ #creazione pulsante salvataggio
+ self.NSIConfigFrame.Panel.SaveNSIConfigFrame=wxButton(self.NSIConfigFrame.Panel, 119,'Save Config',pos=(150,200),size=(200,40))
+ EVT_BUTTON(self.NSIConfigFrame.Panel.SaveNSIConfigFrame,119,self.SaveConfig)
+ self.NSIConfigFrame.Show(1)
+
+
+############################################################################################################
+# FUNCTION NAME : SPILConfig
+# FUNCTION DESCRIPTION: Creazione delle finestre di settaggio configurazione dello strumento SPIL
+# MatLabInstallation=r'C:\Programmi\matlab65p1\bin\win32\matlab.exe' #da configurazione
+# StartUpMatlabPath=r'C:\Programmi\matlab65p1\toolbox\local\startup' #da configurazione
+# IssInstallation=r'C:\Programmi\T32\T32M166.EXE' #da configurazione
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def SPILConfig(self):
+ #creazione frame generale
+ self.SPILConfigFrame=wxMiniFrame(None, -1, title="SPIL Configuration", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.SPILConfigFrame.Panel = wxPanel(self.SPILConfigFrame, -1,pos=(0,0), size=(500,300))
+ #creazione text control e browse di MatlabInstallation
+ self.SPILConfigFrame.Panel.MtlbInstallLabel = wxStaticText(self.SPILConfigFrame.Panel, -1, "Matlab.exe:", pos=(5,37), size=(95,20),style=wxALIGN_RIGHT)
+ self.SPILConfigFrame.Panel.MtlbInstallText = wxTextCtrl(self.SPILConfigFrame.Panel, -1, self.MtlbInstall,pos=(100,35), size=(300, 20))
+ self.SPILConfigFrame.Panel.MtlbInstallBrowse =wxButton(self.SPILConfigFrame.Panel, 124, "...",pos=(405,35),size=(18,20))
+ EVT_BUTTON(self.SPILConfigFrame.Panel.MtlbInstallBrowse, 124, self.OnBrowse)
+ #creazione text control e browse di StartUpMatlabPath
+ self.SPILConfigFrame.Panel.StimulusLabel = wxStaticText(self.SPILConfigFrame.Panel, -1, "waveman.exe:", pos=(5,87), size=(95,20),style=wxALIGN_RIGHT)
+ self.SPILConfigFrame.Panel.StimulusText = wxTextCtrl(self.SPILConfigFrame.Panel, -1, self.Stimulus,pos=(100,85), size=(300, 20))
+ self.SPILConfigFrame.Panel.StimulusBrowse =wxButton(self.SPILConfigFrame.Panel, 125, "...",pos=(405,85),size=(18,20))
+ EVT_BUTTON(self.SPILConfigFrame.Panel.StimulusBrowse, 125, self.OnBrowse)
+ #creazione text control e browse di IssInstallation
+ self.SPILConfigFrame.Panel.IssInstallLabel = wxStaticText(self.SPILConfigFrame.Panel, -1, "T32xxx.exe:", pos=(5,137), size=(95,20),style=wxALIGN_RIGHT)
+ self.SPILConfigFrame.Panel.IssInstallText = wxTextCtrl(self.SPILConfigFrame.Panel, -1, self.IssInstall,pos=(100,135), size=(300, 20))
+ self.SPILConfigFrame.Panel.IssInstallBrowse =wxButton(self.SPILConfigFrame.Panel, 126, "...",pos=(405,135),size=(18,20))
+ EVT_BUTTON(self.SPILConfigFrame.Panel.IssInstallBrowse, 126, self.OnBrowse)
+ #creazione text control e browse di ReportSpil
+ self.SPILConfigFrame.Panel.ReportSpilLabel = wxStaticText(self.SPILConfigFrame.Panel, -1, "ReportDir:", pos=(5,187), size=(95,20),style=wxALIGN_RIGHT)
+ self.SPILConfigFrame.Panel.ReportSpilText = wxTextCtrl(self.SPILConfigFrame.Panel, -1, self.ReportSpil,pos=(100,185), size=(300, 20))
+ self.SPILConfigFrame.Panel.ReportSpilBrowse =wxButton(self.SPILConfigFrame.Panel, 128, "...",pos=(405,185),size=(18,20))
+ EVT_BUTTON(self.SPILConfigFrame.Panel.ReportSpilBrowse, 128, self.OnBrowse)
+
+ #creazione pulsante salvataggio
+ self.SPILConfigFrame.Panel.SaveSPILConfigFrame=wxButton(self.SPILConfigFrame.Panel, 127,'Save Config',pos=(150,230),size=(200,40))
+ EVT_BUTTON(self.SPILConfigFrame.Panel.SaveSPILConfigFrame,127,self.SaveConfig)
+ self.SPILConfigFrame.Show(1)
+
+
+############################################################################################################
+# FUNCTION NAME : ExitProc
+# FUNCTION DESCRIPTION: Processo di chiusura del Launcher
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def ExitProc(self):
+ self.Destroy()
+
+
+
+############################################################################################################
+# FUNCTION NAME : ProjectCombo
+# FUNCTION DESCRIPTION: Creazione di un combobox contenente i progetti scaricati. Nel Selector Frame vi saranno
+# solo i TD relativi al progetto selezionato
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def ProjectCombo(self):
+ #Escludo dal combo i files e le cartelle indicate in Dir2NoShow
+ self.Prj_Lst=[]
+ FileList=glob.glob(self.ProjPath)
+ Dir2NoShow=[string.lower(os.path.split(self.ProjPath)[0]+'\\reports'),string.lower(os.path.split(self.ProjPath)[0]+'\\trash')]
+ for File in FileList:
+ if os.path.isdir(File) and not(Dir2NoShow.count(os.path.normcase(File))):
+ self.Prj_Lst.append(File)
+ if self.Prj_Lst.count(self.CurrentPrj):
+ InitProj=self.CurrentPrj
+ else:
+ if len(self.Prj_Lst)==0:
+ InitProj=''
+ else:
+ InitProj=self.Prj_Lst[0]
+ wxStaticText(self.wnd, -1, "Project:", wxPoint(5, 420), wxSize(75, 18))
+ self.wnd.Project = wxComboBox(self.wnd, 500, InitProj, wxPoint(5, 440), wxSize(180, -1),self.Prj_Lst, wxCB_DROPDOWN)#|wxTE_PROCESS_ENTER)
+
+
+############################################################################################################
+# FUNCTION NAME : DevelopUtility
+# FUNCTION DESCRIPTION: Creazione di un NoteBook per gestire utility dell'ambiente di sviluppo.
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def DevelopUtility(self):
+ self.wnd.Develop=wxNotebook(self.wnd, -1,pos=(5,525),size=(180,150))
+
+ #Tendina del dSPACE
+ self.wnd.Develop.Python = wxWindow(self.wnd.Develop, -1, (0,0), (30,30), wxSUNKEN_BORDER )
+ self.wnd.Develop.Python.Path=wxButton(self.wnd.Develop.Python, 113,'Set Path',(5,5),size=(155,22))
+ EVT_BUTTON(self,113,self.EvtPyUtility)
+ self.wnd.Develop.Python.Clear=wxButton(self.wnd.Develop.Python, 114,'Clr Module',(5,30),size=(155,22))
+ EVT_BUTTON(self,114,self.EvtPyUtility)
+ self.wnd.Develop.Python.ChangeStim=wxButton(self.wnd.Develop.Python, 115,'Change Stim',(5,55),size=(155,22))
+ EVT_BUTTON(self,115,self.EvtPyUtility)
+ #checkbox per la cancellazione dei pyc
+ self.wnd.Develop.Python.PycBox = wxCheckBox(self.wnd.Develop.Python, 146, " Clear .pyc", wxPoint(5, 80), wxSize(150, 20), wxNO_BORDER)
+ self.wnd.Develop.Python.PycBox.SetValue(False)
+ self.wnd.Develop.AddPage(self.wnd.Develop.Python, "dSPACE")
+
+ #Tendina dell' NSI
+ self.wnd.Develop.Nsi = wxWindow(self.wnd.Develop, -1, (0,0), (30,30), wxSUNKEN_BORDER )
+ self.wnd.Develop.AddPage(self.wnd.Develop.Nsi, "NSI")
+
+ #Tendina dello SPIL
+ self.wnd.Develop.Spil = wxWindow(self.wnd.Develop, -1, (0,0), (30,30), wxSUNKEN_BORDER )
+ #time x il debug
+ self.wnd.Develop.Spil.DebugTimeLabel=wxStaticText(self.wnd.Develop.Spil, -1, "Debug Stop Time(ms):",pos=(5,30), size=(95,30),style=wxALIGN_LEFT)
+ self.wnd.Develop.Spil.DebugTimeText = wxTextCtrl(self.wnd.Develop.Spil, -1, "0",pos=(80,33), size=(50, 20))
+ #pulsante x creazione sdt
+ self.wnd.Develop.Spil.SdtOut=wxButton(self.wnd.Develop.Spil, 129,'Output --> Sdt',(5,65),size=(75,20))
+ EVT_BUTTON(self,129,self.EvtSpilUtility)
+ #checkbox del coverage
+ self.wnd.Develop.Spil.CovBox = wxCheckBox(self.wnd.Develop.Spil, 138, " Coverage Enable", wxPoint(5, 5), wxSize(150, 20), wxNO_BORDER)
+ self.wnd.Develop.Spil.CovBox.SetValue(False)
+ #pulsante x apertura stimulus editor
+ self.wnd.Develop.Spil.OpenStim=wxButton(self.wnd.Develop.Spil, 139,'STIM',(90,65),size=(35,20))
+ EVT_BUTTON(self,139,self.EvtSpilUtility)
+ #pulsante x conversione da stimolo a sdt
+ self.wnd.Develop.Spil.OpenStim=wxButton(self.wnd.Develop.Spil, 143,'GEN',(125,65),size=(35,20))
+ EVT_BUTTON(self,143,self.EvtSpilUtility)
+ #pulsante x conversione da STB a sdt
+ self.wnd.Develop.Spil.STB=wxButton(self.wnd.Develop.Spil, 153,'Stb --> Sdt',(5,95),size=(75,20))
+ EVT_BUTTON(self,153,self.EvtSpilUtility)
+
+ self.wnd.Develop.AddPage(self.wnd.Develop.Spil, "SPIL")
+
+
+
+
+
+
+
+############################################################################################################
+# CLASS NAME: TdSelectorLay
+# CLASS DESCRIPTION: Gestione della grafica:
+# -Creazione frame di base
+# -Inizializzazione grafica
+# -Gestione eventi
+#
+# PARAMETERS: wxFrame: oggetto frame
+#
+# LIST OF METHODS: -OnBrowse
+# -SaveConfig
+# -EvtChoice
+# -EvtReload
+# -EvtTcAll
+# -EvtGO
+# -Configuration
+############################################################################################################
+class TdSelectorLay(wxFrame):
+
+ def __init__(self):
+ self.LauncherPath=os.getcwd() #lavoro sempre sulla cartella in cui è presente questo eseguibile
+ self.PythonPath="c:\\Programmi\\dSPACE\\dspace_r34_mlcu\\ControlDesk\\Python\\Python.exe"
+ self.PythonwinPath="c:\\Programmi\\dSPACE\\dspace_r34_mlcu\\ControlDesk\\Python\\Pythonwin\\Pythonwin.exe"
+ self.PylibPath="M:\\Work\\*\\Lib\\*"
+ self.DescrPath="M:\\Work\\*\\*\\*\\lay_descr.txt"
+ self.ProjPath="M:\\Work\\*"
+ self.NSIMSDevDir=r"C:\Programmi\Microsoft Visual Studio\Common\MSDev98"
+ self.NSIMSVCDir=r"C:\Programmi\Microsoft Visual Studio\VC98"
+ self.NSIDir=r"C:\MMB"
+ self.MtlbInstall=r"C:\Programmi\matlab65p1\bin\win32\matlab.exe"
+ self.Stimulus=r"C:\Programmi\waveman\waveman.exe"
+ self.IssInstall=r"C:\Programmi\T32\T32M166.EXE"
+ self.ReportSpil=r"M:\Work\Reports"
+
+
+
+ #se esiste il file config.txt in cui ho già configurato il launcher carico quello altrimenti prendo i default
+ if os.path.exists(self.LauncherPath+'\\config.txt'):
+ f=open(self.LauncherPath+'\\config.txt','r')
+ exec(f.readline()) #PythonPath
+ exec(f.readline()) #PythonwinPath
+ exec(f.readline()) #PylibPath
+ exec(f.readline()) #DescrPath
+ exec(f.readline()) #ProjPath
+ exec(f.readline()) #NSIMSDevDir
+ exec(f.readline()) #NSIMSVCDir
+ exec(f.readline()) #NSIDir
+ exec(f.readline()) #MtlbInstall
+ exec(f.readline()) #Stimulus
+ exec(f.readline()) #IssInstall
+ exec(f.readline()) #ReportSpil
+ f.close()
+ f=open(self.LauncherPath+'\\path.py','w')
+ f.write("LauncherPath=r'"+self.LauncherPath+"'")
+ f.close()
+
+ #creazione frame
+ wxFrame.__init__(self, NULL, -1,'TEST LAUNCHER vs 4.0.18')
+ self.Maximize(1) #massimizzo il frame
+ self.ScreenWidth,self.ScreenHeight=self.GetSizeTuple() #Larghezza e Altezza dello schermo
+ self.MngThr=threading.Event() #oggetto utile per gestire i thread
+ self.MngThr1=threading.Event() #oggetto utile per gestire i thread
+ self.MngThr2=threading.Event() #oggetto utile per gestire i thread
+ #creazione di scroll oriz e vert nel frame
+ self.scmain=wxScrolledWindow(self, -1,pos = wxDefaultPosition, size = wxDefaultSize, style = wxHSCROLL | wxVSCROLL, name = "scrolledWindow")
+ self.scmain.SetScrollbars(10, 10, 106, 70, xPos = 0, yPos = 0, noRefresh = FALSE)
+ self.wnd=wxWindow(self.scmain, -1, (0,10), (200,self.ScreenHeight*1.33), wxSUNKEN_BORDER)
+ #metto icona sulla barra
+ self.SetIcon(wxIcon('launcher.ico',wxBITMAP_TYPE_ICO))
+
+ #creazioni di tutti gli oggetti grafici
+
+ if len(glob.glob(self.ProjPath))>0:
+ self.CurrentPrj=glob.glob(self.ProjPath)[0] #setto il primo progetto trovato come progetto corrente da aprire
+ else:
+ self.CurrentPrj=''
+ ProjectCombo(self)
+ ProjectPath(self) #setta come corrente la directory relativa al progetto selezionato
+ self.TD_DATA=TdParse(self.CurrentDescrPath)[0] #faccio la ricerca dei TD sulla directory corrente
+ self.TD_NumTdXInstr=TdParse(self.CurrentDescrPath)[1] #Numero TD X strumento
+ SelectorFrame(self)
+ LauncherFrame(self)
+ TdBrowse(self)
+ ReloadButton(self)
+ DevelopUtility(self)
+ MenuBar(self)
+
+
+
+
+
+############################################################################################################
+# METHOD NAME: OnBrowse
+# METHOD DESCRIPTION: Evento del tasto di browse. Gestisce le finestre del browse presenti nel menubar
+# per la configurazione del launcher
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante di browse
+#
+############################################################################################################
+ def OnBrowse (self, event):
+ #browse per il python.exe(1) e pythonwin.exe(2)
+ if (event.GetId() == 1 or event.GetId() == 2):
+ self.wnd.dlg = wxFileDialog(self.PyConfigFrame.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ if event.GetId() == 1:
+ self.PyConfigFrame.Panel.PythonText.SetValue(self.wnd.dlg.GetPath())
+ if event.GetId() == 2:
+ self.PyConfigFrame.Panel.PythonwinText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per il path della libreria del python
+ if event.GetId() == 3:
+ self.wnd.dlg = wxDirDialog(self.GenConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.GenConfigFrame.Panel.PylibText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per il file descrittore della TD lay_descr.txt
+ if event.GetId() == 5 :
+ self.wnd.dlg = wxFileDialog(self.GenConfigFrame.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ if event.GetId() == 5:
+ self.GenConfigFrame.Panel.DescrText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per il path del progetto
+ if event.GetId() == 7:
+ self.wnd.dlg = wxDirDialog(self.GenConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.GenConfigFrame.Panel.ProjText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per MSDevDir
+ if event.GetId() == 117:
+ self.wnd.dlg = wxDirDialog(self.NSIConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.NSIConfigFrame.Panel.MSDevDirText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per MSVCDir
+ if event.GetId() == 118:
+ self.wnd.dlg = wxDirDialog(self.NSIConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.NSIConfigFrame.Panel.MSVCDirText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per NSIDir
+ if event.GetId() == 123:
+ self.wnd.dlg = wxDirDialog(self.NSIConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.NSIConfigFrame.Panel.NSIDirText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per l'exe del matlab
+ if event.GetId() == 124 :
+ self.wnd.dlg = wxFileDialog(self.SPILConfigFrame.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.SPILConfigFrame.Panel.MtlbInstallText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per lo stimulus editor
+ if event.GetId() == 125 :
+ self.wnd.dlg = wxFileDialog(self.SPILConfigFrame.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.exe", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.SPILConfigFrame.Panel.StimulusText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per l'exe dell'ISS
+ if event.GetId() == 126 :
+ self.wnd.dlg = wxFileDialog(self.SPILConfigFrame.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.SPILConfigFrame.Panel.IssInstallText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per la directory del report
+ if event.GetId() == 128 :
+ self.wnd.dlg = wxDirDialog(self.SPILConfigFrame.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.SPILConfigFrame.Panel.ReportSpilText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per l'output dello SPIL
+ if event.GetId() == 133:
+ self.wnd.dlg = wxFileDialog(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per il DataDictionary dello SPIL
+ if event.GetId() == 134:
+ self.wnd.dlg = wxFileDialog(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per il file delle tolleranze dello SPIL
+ if event.GetId() == 135:
+ self.wnd.dlg = wxFileDialog(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, message = "", defaultDir = "", defaultFile = "",wildcard = "*.*", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per l'output .mat dello SPIL
+ if event.GetId() == 136:
+ self.wnd.dlg = wxDirDialog(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per la directory contenente gli stimoli
+ if event.GetId() == 144 :
+ self.wnd.dlg = wxDirDialog(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusText.SetValue(self.wnd.dlg.GetPath())
+
+ #browse per la directory contenente gli stimoli
+ if event.GetId() == 154 :
+ self.wnd.dlg = wxDirDialog(self.wnd.Develop.Spil.Stb2SdtDlg.Panel, message = "", defaultPath = "", style = wxOPEN)
+ if self.wnd.dlg.ShowModal() == wxID_OK:
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusText.SetValue(self.wnd.dlg.GetPath())
+
+
+
+ self.wnd.dlg.Destroy()
+
+
+
+############################################################################################################
+# METHOD NAME: SaveConfig
+# METHOD DESCRIPTION: Evento del tasto Save Config per il salvataggio della configurazione inserita tramite
+# menu bar
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante di Save Config
+#
+############################################################################################################
+ def SaveConfig(self, event):
+ #salvataggio ottenuto premendo il save dentro all' InstrumentsSet menu
+ if event.GetId() == 4 :
+ self.PythonPath=self.PyConfigFrame.Panel.PythonText.GetValue()
+ self.PythonwinPath=self.PyConfigFrame.Panel.PythonwinText.GetValue()
+ # se è spuntato il box per la creazione del NS allora chiama GetNS
+ if self.PyConfigFrame.Panel.CreateNSBox.GetValue():
+ self.ThCreateNS=threading.Thread(target=GetNS,args=(self,))
+ self.ThCreateNS.start()
+ self.PyConfigFrame.Destroy()
+ self.PyConfigFrame.Show(0)
+
+ #salvataggio ottenuto premendo il save dentro al GeneralSet menu
+ if event.GetId() == 6:
+ self.DescrPath=self.GenConfigFrame.Panel.DescrText.GetValue()
+ self.ProjPath=self.GenConfigFrame.Panel.ProjText.GetValue()
+ self.PylibPath=self.GenConfigFrame.Panel.PylibText.GetValue()
+ self.GenConfigFrame.Destroy()
+ self.GenConfigFrame.Show(0)
+
+ #salvataggio ottenuto premendo il save dentro al InstrumentsSet menu dell'NSI
+ if event.GetId() == 119:
+ self.NSIMSDevDir=self.NSIConfigFrame.Panel.MSDevDirText.GetValue()
+ self.NSIMSVCDir=self.NSIConfigFrame.Panel.MSVCDirText.GetValue()
+ self.NSIDir=self.NSIConfigFrame.Panel.NSIDirText.GetValue()
+ self.NSIConfigFrame.Destroy()
+ self.NSIConfigFrame.Show(0)
+
+ #salvataggio ottenuto premendo il save dentro al InstrumentsSet menu dell'SPIL
+ if event.GetId() == 127:
+ self.MtlbInstall=self.SPILConfigFrame.Panel.MtlbInstallText.GetValue()
+ self.Stimulus=self.SPILConfigFrame.Panel.StimulusText.GetValue()
+ self.IssInstall=self.SPILConfigFrame.Panel.IssInstallText.GetValue()
+ self.ReportSpil=self.SPILConfigFrame.Panel.ReportSpilText.GetValue()
+ self.SPILConfigFrame.Destroy()
+ self.SPILConfigFrame.Show(0)
+
+ #scrittura del file di config.txt
+ f=open(self.LauncherPath+'\\config.txt','w+')
+ f.write('self.PythonPath=r"'+self.PythonPath+'"')
+ f.write('\nself.PythonwinPath=r"'+self.PythonwinPath+'"')
+ f.write('\nself.PylibPath=r"'+self.PylibPath+'"')
+ f.write('\nself.DescrPath=r"'+self.DescrPath+'"')
+ f.write('\nself.ProjPath=r"'+self.ProjPath+'"')
+ f.write('\nself.NSIMSDevDir=r"'+self.NSIMSDevDir+'"')
+ f.write('\nself.NSIMSVCDir=r"'+self.NSIMSVCDir+'"')
+ f.write('\nself.NSIDir=r"'+self.NSIDir+'"')
+ f.write('\nself.MtlbInstall=r"'+self.MtlbInstall+'"')
+ f.write('\nself.Stimulus=r"'+self.Stimulus+'"')
+ f.write('\nself.IssInstall=r"'+self.IssInstall+'"')
+ f.write('\nself.ReportSpil=r"'+self.ReportSpil+'"')
+ f.close()
+
+
+
+############################################################################################################
+# METHOD NAME: EvtCreateSdt
+# METHOD DESCRIPTION: Evento scatenato dal pulsante di creazione dell'output sdt partendo dalla reg. SPIL
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante di Save Config
+#
+############################################################################################################
+ def EvtCreateSdt(self, event):
+ self.ThCreateSdt=threading.Thread(target=CreateSdt,args=(self,))
+ self.ThCreateSdt.start()
+
+############################################################################################################
+# METHOD NAME: EvtGenerateSdt
+# METHOD DESCRIPTION: Evento scatenato dal pulsante di generazione di SDT pertendo dallo stimolo
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante di Save Config
+#
+############################################################################################################
+ def EvtGenerateSdt(self, event):
+ self.ThGenerateSdt=threading.Thread(target=GenerateSdt,args=(self,))
+ self.ThGenerateSdt.start()
+
+############################################################################################################
+# METHOD NAME: EvtStb2Sdt
+# METHOD DESCRIPTION: Evento scatenato dal pulsante di generazione di SDT partendo da STB
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante di Save Config
+#
+############################################################################################################
+ def EvtStb2Sdt(self, event):
+ self.ThStb2Sdt=threading.Thread(target=Stb2Sdt,args=(self,))
+ self.ThStb2Sdt.start()
+
+
+############################################################################################################
+# METHOD NAME: EvtChoice
+# METHOD DESCRIPTION: Evento del TdBrowse. In base alla selezione fatta, la finestra del SelectorFrame si sposta
+# orizzontalmente di un tot agevolando la ricerca del TD
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al listbox del TdBrowse
+#
+############################################################################################################
+ def EvtChoice(self, event):
+ self.scmain.sc.Scroll(event.GetSelection()*20,0)
+
+
+############################################################################################################
+# METHOD NAME: EvtReload
+# METHOD DESCRIPTION: Evento del pulsante di RELOAD. Ricostruisce la finestra del SelectorFrame rianalizzando il
+# path relativo al progetto selezionato
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al pulsante RELOAD
+#
+############################################################################################################
+ def EvtReload(self, event):
+ #ricostruisce la lista del combo relativo al progetto in quanto potrebbe essere cambiata
+ ProjectPath(self)
+ self.wnd.Project.Destroy()
+ ProjectCombo(self)
+
+ #ricostruisce il SelectorFrame
+ self.scmain.sc.Destroy()
+ self.TD_DATA=TdParse(self.CurrentDescrPath)[0]
+ self.TD_NumTdXInstr=TdParse(self.CurrentDescrPath)[1] #Numero TD X strumento
+ self.wnd.launcher.ch.Destroy()
+ TdBrowse(self)
+ SelectorFrame(self)
+
+
+
+############################################################################################################
+# METHOD NAME: EvtTcAll
+# METHOD DESCRIPTION: Eventi di tutti i pulsanti di NO OL CL. Setta tutti i radiobutton al valore impostato
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato ai pulsanti di NO OL CL
+#
+############################################################################################################
+ def EvtTcAll(self, event):
+ #Gli Id dei pulsanti sono:
+ #NO globale = 0
+ #OL globale = 1
+ #CL globale = 2
+ #NO locale =x0 dove x è la TD relativa
+ #OL locale =x1 dove x è la TD relativa
+ #CL locale =x2 dove x è la TD relativa
+ ev=str(event.GetId())
+ TipoSel=ev[len(ev)-1] #la selezione è rappresenta sempre dalla cifra meno significativat
+ TD=ev[0:len(ev)-1] #il numero di TD è rappresentato dalle restanti cifrea più significativeve
+ #se TD='' significa che è quello globale e perciò faccio il for su tutti i TD
+ if TD=='':
+ for indexTD in range(len(self.TD_DATA)):
+ TD=str(indexTD+1)
+ GeneralSel(self,TD,TipoSel)
+ else:
+ GeneralSel(self,TD,TipoSel)
+
+############################################################################################################
+# METHOD NAME: EvtGO
+# METHOD DESCRIPTION: Al premere del GO raccolgo tutte le selezioni dei TC e lancio il RUN e il controllore di led
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato al GO
+#
+############################################################################################################
+ def EvtGO(self, event):
+ self.GlobalTestList=[]
+ for TD in range(len(self.TD_DATA)):
+ exec('TypeTestListTD'+str(TD+1)+'=[]')
+ for TC in range(len(self.TD_DATA[TD][2])-1):
+ str2exe="TypeTestListTD"+str(TD+1)+".append(self.scmain.sc.TD"+str(TD+1)+".rb"+str(TC+1)+"_"+str(TD+1)+".GetSelection())"
+ exec(str2exe)
+ str2exe="self.GlobalTestList.append(TypeTestListTD"+str(TD+1)+")"
+ exec(str2exe)
+
+ #se non è stato selezionato nessun TC non faccio partire il RUN_TESTl
+ NoTest=1
+ for TypeTestListTD in self.GlobalTestList:
+ if TypeTestListTD.count(1) or TypeTestListTD.count(2):
+ NoTest=0
+ break
+ else:
+ NoTest=1
+ if not(NoTest):
+ #parte il thread che controlla il file dei led
+ self.ThLed=threading.Thread(target=CheckLed,args=(self,))
+ self.ThLed.start()
+ #parte il thread che lancia i launcher dei vari strumenti
+ ThRun=threading.Thread(target=RUN_TEST,args=(self,))
+ ThRun.start()
+
+
+############################################################################################################
+# FUNCTION NAME : RptTest
+# FUNCTION DESCRIPTION: Incremento del TextControl x ripetizione test
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) evento
+#
+############################################################################################################
+ def RptTest(self,event):
+ self.wnd.RptTestTxt.SetValue(str(event.GetPosition()))
+
+
+
+############################################################################################################
+# METHOD NAME: Configuration
+# METHOD DESCRIPTION: gestione eventi menu configurazione
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: eventi legati al menu
+#
+############################################################################################################
+ def Configuration(self,event):
+ #menu Instruments
+ if event.GetId() == 105:
+ PyConfig(self)
+ #menu TD set
+ if event.GetId() == 108:
+ GenConfig(self)
+ #menu NSI set
+ if event.GetId() == 106:
+ NSIConfig(self)
+ #menu SPIL set
+ if event.GetId() == 107:
+ SPILConfig(self)
+ #menu Exit set
+ if event.GetId() == 156:
+ ExitProc(self)
+
+
+############################################################################################################
+# METHOD NAME: EvtPyUtility
+# METHOD DESCRIPTION: Evento dei pulsanti di Utility Python
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato ai pulsanti del python utility
+#
+############################################################################################################
+ def EvtPyUtility(self, event):
+ #Setto il path (in un thread separato con win98 non necessario)
+ if event.GetId() == 113:
+ self.ThSetPath=threading.Thread(target=SetPath,args=(self,))
+ self.ThSetPath.start()
+
+ #Cancello i moduli importati (in un thread separato con win98 non necessario)
+ if event.GetId() == 114:
+ self.ThClrNS=threading.Thread(target=ClrNS,args=(self,))
+ self.ThClrNS.start()
+
+ #Invoco il ChangeStimolo
+ if event.GetId() == 115:
+ #creazione frame generale
+ self.wnd.Develop.Python.ChStimDlg=wxMiniFrame(None, -1,title="Change Stimulus", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.wnd.Develop.Python.ChStimDlg.Panel = wxPanel(self.wnd.Develop.Python.ChStimDlg, -1,pos=(0,0), size=(500,300))
+ self.wnd.Develop.Python.ChStimDlg.Panel.Browse=wxGenericDirCtrl(self.wnd.Develop.Python.ChStimDlg.Panel, -1, dir=self.CurrentPrj,size=(200,200), style=wxDIRCTRL_3D_INTERNAL|wxDIRCTRL_SHOW_FILTERS|wxMULTIPLE,filter="Python files (*.py)|*.py")
+ self.wnd.Develop.Python.ChStimDlg.Panel.Start =wxButton(self.wnd.Develop.Python.ChStimDlg.Panel, 116, "START CHANGE",pos=(250,20),size=(200,30))
+ EVT_BUTTON(self.wnd.Develop.Python.ChStimDlg.Panel.Start, 116, self.EvtChgStim)
+ self.wnd.Develop.Python.ChStimDlg.Panel.Check= wxCheckBox(self.wnd.Develop.Python.ChStimDlg.Panel, -1, "Recurse Sub-Directory", wxPoint(50, 210), wxSize(150, 20), wxNO_BORDER)
+ self.wnd.Develop.Python.ChStimDlg.Show(1)
+
+############################################################################################################
+# METHOD NAME: EvtSpilUtility
+# METHOD DESCRIPTION: Evento dei pulsanti di Utility Spil
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) event: evento legato ai pulsanti del python utility
+#
+############################################################################################################
+ def EvtSpilUtility(self, event):
+ #Invoco il Convert2Sdt per convertire il prodotto dello SPIL .txt in formato sdt .mat
+ if event.GetId() == 129:
+ #creazione frame generale
+ self.wnd.Develop.Spil.Cv2SdtDlg=wxMiniFrame(None, -1,title="Convert2Sdt", pos=(200,100), size=(500,300), style=wxDEFAULT_FRAME_STYLE)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel = wxPanel(self.wnd.Develop.Spil.Cv2SdtDlg, -1,pos=(0,0), size=(500,300))
+ #creazione text control e browse di Output.txt
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtLabel=wxStaticText(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "Output.txt:",pos=(5,7), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtText = wxTextCtrl(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "",pos=(100,5), size=(300, 20))
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtBrowse =wxButton(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, 133, "...",pos=(405,5),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtBrowse, 133, self.OnBrowse)
+ #creazione text control e browse di DataDict.mat
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDLabel=wxStaticText(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "DataDict.mat:",pos=(5,47), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDText = wxTextCtrl(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "",pos=(100,45), size=(300, 20))
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDBrowse =wxButton(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, 134, "...",pos=(405,45),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDBrowse, 134, self.OnBrowse)
+ #creazione text control e browse di Toll.m
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameLabel=wxStaticText(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "TOLL_xxx_GEN.m:",pos=(5,87), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameText = wxTextCtrl(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "",pos=(100,85), size=(300, 20))
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameBrowse =wxButton(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, 135, "...",pos=(405,85),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameBrowse, 135, self.OnBrowse)
+ #creazione text control e browse dell'SDT di Output.mat
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameLabel=wxStaticText(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "File da creare .mat:",pos=(5,127), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameText = wxTextCtrl(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, -1, "",pos=(100,125), size=(300, 20))
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameBrowse =wxButton(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, 136, "...",pos=(405,125),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameBrowse, 136, self.OnBrowse)
+ #creazione pulsante salvataggio
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.CreateSdt=wxButton(self.wnd.Develop.Spil.Cv2SdtDlg.Panel, 137,'Create SdtOut',pos=(150,200),size=(200,40))
+ EVT_BUTTON(self.wnd.Develop.Spil.Cv2SdtDlg.Panel.CreateSdt,137,self.EvtCreateSdt)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Show(1)
+ #Invoco l'apertura dello stimulus editor x SPIL
+ if event.GetId() == 139:
+ self.ThOpenStimulus=threading.Thread(target=OpenStimulus,args=(self,))
+ self.ThOpenStimulus.start()
+ #Invoco l'elaborazione delle tracce ricavate dello stimous editor x ottenere l'SDT
+ if event.GetId() == 143:
+ #creazione frame generale
+ self.wnd.Develop.Spil.Stim2SdtDlg=wxMiniFrame(None, -1,title="Sdt Generator", pos=(200,100), size=(500,200), style=wxDEFAULT_FRAME_STYLE)
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel = wxPanel(self.wnd.Develop.Spil.Stim2SdtDlg, -1,pos=(0,0), size=(500,200))
+ #creazione text control e browse x la cartella contenente gli stimoli
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusLabel=wxStaticText(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, -1, "Stimulus Folder:",pos=(5,37), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusText = wxTextCtrl(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, -1, "",pos=(100,35), size=(300, 20))
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusBrowse =wxButton(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, 144, "...",pos=(405,35),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusBrowse, 144, self.OnBrowse)
+ #creazione text control per lo step dello stimolo
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.StimulusStepLabel=wxStaticText(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, -1, "Stimulus Step [sec]:",pos=(5,77), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.StimulusStepText = wxTextCtrl(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, -1, "0.001",pos=(100,75), size=(50, 20))
+ #creazione pulsante per il processing
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.CreateSdt=wxButton(self.wnd.Develop.Spil.Stim2SdtDlg.Panel, 145,'Converting in Sdt format',pos=(150,120),size=(200,40))
+ EVT_BUTTON(self.wnd.Develop.Spil.Stim2SdtDlg.Panel.CreateSdt,145,self.EvtGenerateSdt)
+ self.wnd.Develop.Spil.Stim2SdtDlg.Show(1)
+ #Invoco l'elaborazione delle tracce ricavate da STB x ottenerle in formato SDT
+ if event.GetId() == 153:
+ #creazione frame generale
+ self.wnd.Develop.Spil.Stb2SdtDlg=wxMiniFrame(None, -1,title="Sdt Generator", pos=(200,100), size=(500,200), style=wxDEFAULT_FRAME_STYLE)
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel = wxPanel(self.wnd.Develop.Spil.Stb2SdtDlg, -1,pos=(0,0), size=(500,200))
+ #creazione text control e browse x la cartella contenente gli stimoli
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusLabel=wxStaticText(self.wnd.Develop.Spil.Stb2SdtDlg.Panel, -1, "Stimulus Folder:",pos=(5,37), size=(95,20),style=wxALIGN_RIGHT)
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusText = wxTextCtrl(self.wnd.Develop.Spil.Stb2SdtDlg.Panel, -1, "",pos=(100,35), size=(300, 20))
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusBrowse =wxButton(self.wnd.Develop.Spil.Stb2SdtDlg.Panel, 154, "...",pos=(405,35),size=(18,20))
+ EVT_BUTTON(self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusBrowse, 154, self.OnBrowse)
+ #creazione pulsante per il processing
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.CreateSdt=wxButton(self.wnd.Develop.Spil.Stb2SdtDlg.Panel, 155,'Converting in Sdt format',pos=(150,120),size=(200,40))
+ EVT_BUTTON(self.wnd.Develop.Spil.Stb2SdtDlg.Panel.CreateSdt,155,self.EvtStb2Sdt)
+ self.wnd.Develop.Spil.Stb2SdtDlg.Show(1)
+
+
+############################################################################################################
+# FUNCTION NAME : EvtChgStim
+# FUNCTION DESCRIPTION: Chiama lo script ClearNS.py
+# - Cancella i moduli importati e le variabili
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+ def EvtChgStim(self,event):
+ self.ThChgStim=threading.Thread(target=ChgStim,args=(self,))
+ self.ThChgStim.start()
+
+
+
+############################################################################################################
+# FUNCTION NAME : EvtLog
+# FUNCTION DESCRIPTION: Apre il file di log
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+ def EvtLog(self,event):
+ os.startfile(r'log.txt')
+
+
+
+
+############################################################################################################
+# CLASS NAME: App
+# CLASS DESCRIPTION: Costrutto base per le interfaccie grafiche
+#
+# PARAMETERS: eventuali parametri da passare al creatore della classe
+# 1) wxApp
+#
+# LIST OF METHODS: OnInit
+############################################################################################################
+class App(wxApp):
+ def OnInit(self):
+ frame=TdSelectorLay()
+ frame.Show(true)
+ return true
+
+
+
+############################################################################################################
+# FUNCTION NAME : GeneralSel
+# FUNCTION DESCRIPTION:
+# E' la funzione che materialmente setta i radiobutton al valore impostato dai pulsanti NO OL CL
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+# 2) TD: stringa indicante il numero della TD su cui agire
+# 3) TipoSel: stringa indicante il tipo di settaggio da effettuare sui radiobutton
+#
+############################################################################################################
+def GeneralSel(self,TD,TipoSel):
+ str2exe="TcNum=len(self.TD_DATA["+TD+"-1][2])-1"
+ exec(str2exe)
+ for indexTC in range(TcNum):
+ str2exe="self.scmain.sc.TD"+TD+".rb"+str(indexTC+1)+"_"+TD+".SetSelection("+TipoSel+")"
+ exec(str2exe)
+
+
+
+
+############################################################################################################
+# FUNCTION NAME : ProjectPath
+# FUNCTION DESCRIPTION:
+# Setta il path per i descrittori di TD e per le librerie in funzione al progetto selezionato
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def ProjectPath(self):
+ self.CurrentPrj=r''+self.wnd.Project.GetValue() #progetto selezionato
+ lensplitPrj=len(string.split(self.CurrentPrj,'\\'))
+ splitPath=string.split(self.DescrPath,'\\')
+ #il path lo compongo partendo dal path del progetto selezionato e aggiungendo le parti del DescrPath rimanenti
+ self.CurrentDescrPath=self.CurrentPrj
+ for i in range(lensplitPrj,len(splitPath)):
+ self.CurrentDescrPath=self.CurrentDescrPath+'\\'+splitPath[i]
+ #il path lo compongo partendo dal path del progetto selezionato e aggiungendo le parti del PylibPath rimanenti
+ self.CurrentPylibPath=self.CurrentPrj
+ splitLib=string.split(self.PylibPath,'\\')
+ for i in range(lensplitPrj,len(splitLib)):
+ self.CurrentPylibPath=self.CurrentPylibPath+'\\'+splitLib[i]
+ if self.CurrentPrj=='':
+ self.CurrentDescrPath=''
+ self.CurrentPylibPath=''
+
+############################################################################################################
+# FUNCTION NAME : CompList
+# FUNCTION DESCRIPTION: Utility per le liste del Led_status e Type_List. Se è stato selezionato un TC
+# e il led corrispondente non si è acceso significa che c'è stato un errore .
+#
+# INPUT PARAMETERS:
+# 1) TL lista dei test case selezionati in un TD
+# 2) LS lista dello stato dei led relativi
+# OUTPUT:
+# 1) Ritorna 0 se c'è un'incongruenza (TC selezionato e Led spento) 1 altrimenti
+############################################################################################################
+def CompList(TL,LS):
+ if (TL!=0) and (LS==0 or LS==None):
+ return 0
+ else:
+ return 1
+
+############################################################################################################
+# FUNCTION NAME : CheckLed
+# FUNCTION DESCRIPTION: Alla fine dell'esecuzione del TD setta il colore dei led in base del risultato dei TC.
+# Funzione lanciata in thread parallelo con il RUN che controlla periodicamente lo stato del file led_status.txt.
+# L'accesso in scrittura e lettura di questo file è regolato attraverso un ulteriore file di flag chiamato
+# led.flg. Questo flag è creato dal PyTdLnc.py e, in generale, da tutti i lanciatori di strumento
+# al termine di ogni esecuzione di TD e cancellato solo alla fine del settaggio dei led della TD.
+# Grazie a questo flag si evita il contemporaneo accesso del file da parte di 2 programmi diversi
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def CheckLed(self):
+ #se per qualsiasi motivo alla partenza si ha il file di flag questo deve essere cancellato
+ if (os.path.exists(self.LauncherPath+'\\led.flg')):
+ os.remove(self.LauncherPath+'\\led.flg')
+ #Aspetto fintantochè non è finita la parte di inizializzazione del file led_status.txt fatta in
+ #RUN_TEST
+ self.MngThr2.wait()
+ self.Iconize(1) #al lancio dei test mi iconizza il launcher
+ stopflg=0 #quando 1 significa che è finita l'esecuzione di tutti i tests di tutti gli strumenti
+ #e posso quindi uscire dalla funzione
+ while not(stopflg):
+ #E' il gestore di thread che mette in wait il thread per 2 secondi
+ #lasciando più CPU per il thread RUN eseguito in parallelo .
+ self.MngThr.wait(2)
+ #lettura led solo se esiste il flag
+ if (os.path.exists(self.LauncherPath+'\\led.flg')):
+ f=open(self.LauncherPath+'\\led_status.txt','r+')
+ exec(f.readline()) #LED_STATUS=[[1,2,0,3],[],[2,1]]
+ exec(f.readline()) #stopInstr=0
+ exec(f.readline()) #stopflgHIL=0
+ exec(f.readline()) #stopflgNSI=0
+ exec(f.readline()) #stopflgSPIL=0
+ #Lo stopflg (quindi la fine del programma) si ha solo quando tutti gli strumenti hanno finito e
+ #e sono state eseguite tutte le ripetizioni indicate
+ stopflg=stopflgHIL&stopflgNSI&stopflgSPIL&(self.nrpt+1==(int(self.wnd.RptTestSpn.GetValue()))) #se 1 significa fine della sessione di tests
+ LedColor=['grey','black','green','red']
+ #setto il colore dei led in base al risultato
+ for indexTD in range(len(LED_STATUS)):
+ if LED_STATUS[indexTD].count(1) or LED_STATUS[indexTD].count(2) or LED_STATUS[indexTD].count(3): #se per un certo TD non sono stati selezionati TC, lascio in grigio
+ for indexTC in range(len(LED_STATUS[indexTD])):
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".led"+str(indexTC+1)+"_"+str(indexTD+1)+".SetBackgroundColour(LedColor["+str(LED_STATUS[indexTD][indexTC])+"])"
+ #es: self.scmain.sc.TD1.led1_1..SetBackgroundColour(LedColor["+str(LED_STATUS[indexTD][indexTC])+"])
+ exec(str2exe)
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".Refresh()" #rinfresco la pagina per vedere l'effetto del set
+ #es: self.scmain.sc.TD1.Refresh()
+ exec(str2exe)
+ #Verifico anche che i test non abbiano avuto problemi nell'esecuzione.
+ #In caso negativo coloro di rosso il pulsante di log.
+ if indexTD>0:
+ if map(CompList,self.GlobalTestList[indexTD-1],LED_STATUS[indexTD-1]).count(0)>0:
+ self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ #stopInstr è il flag che dice se il test con uno degli strumenti è terminato
+ if (stopInstr==1):
+ #Verifico anche che i test non abbiano avuto problemi nell'esecuzione.
+ #In caso negativo coloro di rosso il pulsante di log.
+ #print "indexTD=",indexTD
+ if LED_STATUS!=[]: #LED_STATUS è uguale a[] in caso di debug
+ if map(CompList,self.GlobalTestList[indexTD],LED_STATUS[indexTD]).count(0)>0:
+ self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ #se la sessione di uno strumento è terminata --> semaforo verde x il thread MngThr1
+ self.MngThr1.set()
+ f.seek(0)
+ tmpList=f.readlines()
+ tmpList[1]='stopInstr=0\n' #resetto il flag di fine strumento
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ os.remove(self.LauncherPath+'\\led.flg') #solo alla fine delle mie operazioni rimuovo il flag
+ #abilito i pulsanti di GO ed ENABLE disabilitati nella funzione RUN_TEST
+
+ self.wnd.GO.Enable(TRUE)
+ self.wnd.reload.Enable(TRUE)
+ self.wnd.RptTestSpn.Enable(TRUE)
+ self.wnd.RptTestTxt.Enable(TRUE)
+
+ self.Maximize(1)
+
+
+############################################################################################################
+# FUNCTION NAME : SetPath
+# FUNCTION DESCRIPTION: Chiama lo script SetPath.py
+# - Include nel pythonpath le librerie del progetto e le cartelle dei TD selezionati
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def SetPath(self):
+ #Raccolgo i TD selezionati
+ self.GlobalTestList=[]
+ for TD in range(len(self.TD_DATA)):
+ exec('TypeTestListTD'+str(TD+1)+'=[]')
+ for TC in range(len(self.TD_DATA[TD][2])-1):
+ str2exe="TypeTestListTD"+str(TD+1)+".append(self.scmain.sc.TD"+str(TD+1)+".rb"+str(TC+1)+"_"+str(TD+1)+".GetSelection())"
+ exec(str2exe)
+ str2exe="self.GlobalTestList.append(TypeTestListTD"+str(TD+1)+")"
+ exec(str2exe)
+
+ #Scrivo nel tmp.txt le informazioni sui tests selezionati, sul nome dei tests e sul path della libreria
+ f=open(self.LauncherPath+'\\tmp.txt','w+')
+ f.write('HilTestList='+str(self.GlobalTestList))
+ f.write('\nHilTD_DATA='+str(self.TD_DATA))
+ f.write('\nPylibPath=r"'+str(self.CurrentPylibPath)+'"')
+ f.write('\n')
+ f.close()
+
+ if string.lower(os.path.split(self.PythonwinPath)[1])=="idle.pyw":
+ #command=self.PythonwinPath + " " +self.LauncherPath+'\\SetPath.py'
+ command=""
+ elif string.lower(os.path.split(self.PythonwinPath)[1])=="pythonwin.exe":
+ revpath=string.replace(self.LauncherPath+'\\SetPath.py','\\','/')
+ command=self.PythonwinPath+" /run "+revpath
+ else:
+ print "NOT VALID DEBUGGER\n Change debugger program "
+
+
+
+
+
+ os.system(command)
+
+############################################################################################################
+# FUNCTION NAME : ClrNS
+# FUNCTION DESCRIPTION: Chiama lo script ClearNS.py
+# - Cancella i moduli importati e le variabili
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def ClrNS(self):
+ if string.lower(os.path.split(self.PythonwinPath)[1])=="idle.pyw":
+ #command=self.PythonwinPath +' -d -e "'+self.LauncherPath+'\\ClearNS.py"'
+ command=""
+ elif string.lower(os.path.split(self.PythonwinPath)[1])=="pythonwin.exe":
+ revpath=string.replace(self.LauncherPath+'\\ClearNS.py','\\','/')
+ command=self.PythonwinPath+" /run "+revpath
+ else:
+ print "NOT VALID DEBUGGER\n Change debugger program "
+ os.system(command)
+
+
+
+
+
+############################################################################################################
+# FUNCTION NAME : GetNS
+# FUNCTION DESCRIPTION: Chiama lo script GetNS.py
+# - Crea il file NStmp in cui vi è il NS di riferimento
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def GetNS(self):
+ revpath=string.replace(self.LauncherPath+'\\GetNS.py','\\','/')
+ command=self.PythonwinPath+" /run "+revpath
+ os.system(command)
+
+
+
+
+############################################################################################################
+# FUNCTION NAME : ChgStim
+# FUNCTION DESCRIPTION: Chiama lo script change_stim.py
+# - cambia gli script degli stimoli
+# tipo=0 cambia file
+# tipo=1 cambia contenuto cartelle
+# tipo=2 cambia contenuto cartella e sottocartelle
+#
+#
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def ChgStim(self):
+ if os.path.isdir(self.wnd.Develop.Python.ChStimDlg.Panel.Browse.GetPath()):
+ if self.wnd.Develop.Python.ChStimDlg.Panel.Check.GetValue():
+ tipo = 2
+ else:
+ tipo = 1
+ else:
+ tipo=0
+ f=open(self.LauncherPath+'\\tmpchgstim.txt','w+')
+ f.write('tipo='+str(tipo))
+ f.write('\ntop_path=r"'+self.wnd.Develop.Python.ChStimDlg.Panel.Browse.GetPath()+'"')
+ f.close()
+ revpath=string.replace(self.LauncherPath+'\\change_stim.py','\\','/')
+ command=self.PythonwinPath+" /run "+revpath
+ self.wnd.Develop.Python.ChStimDlg.Destroy()
+ os.system(command)
+
+
+############################################################################################################
+# FUNCTION NAME : CreateSdt
+# FUNCTION DESCRIPTION: Chiama la funzione matlab il matlab GetSdtOut x convertire gli output SPIL in
+# output SDT
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def CreateSdt(self):
+ #SpilMatLib='M:\\Work\\4TV_SPIL_00\\LIB\\SPILMATLIB\n'
+ SpilMatLib=os.path.dirname(glob.glob(self.CurrentPylibPath+'\\SetCalib.m')[0]) #OK generale
+
+ LaunchFile=" /r cd('"+SpilMatLib+"');GetSdtOut('"+\
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutTxtText.GetValue()+"','"+\
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.DDText.GetValue()+"','"+\
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.OutNameText.GetValue()+"','"+\
+ self.wnd.Develop.Spil.Cv2SdtDlg.Panel.SdtNameText.GetValue()+"');quit"
+ os.system(self.MtlbInstall+LaunchFile)
+ self.wnd.Develop.Spil.Cv2SdtDlg.Destroy()
+ self.wnd.Develop.Spil.Cv2SdtDlg.Show(0)
+
+############################################################################################################
+# FUNCTION NAME : OpenStimulus
+# FUNCTION DESCRIPTION: Apre lo stimulus editor x lo spil
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def OpenStimulus(self):
+ os.system(self.Stimulus)
+
+############################################################################################################
+# FUNCTION NAME : GenerateSdt
+# FUNCTION DESCRIPTION: Chiama la funzione matlab Stimulus2Sdt x convertire gli stimoli dello stimulus
+# editor in SDT
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def GenerateSdt(self):
+ #SpilMatLib='M:\\Work\\4TV_SPIL_00\\LIB\\SPILMATLIB\n'
+ SpilMatLib=os.path.dirname(glob.glob(self.CurrentPylibPath+'\\SetCalib.m')[0]) #OK generale
+
+ LaunchFile=" /r cd('"+SpilMatLib+"');Stimulus2Sdt('"+\
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.StimulusStepText.GetValue()+"','"+\
+ self.wnd.Develop.Spil.Stim2SdtDlg.Panel.FolderStimulusText.GetValue()+"');quit"
+ os.system(self.MtlbInstall+LaunchFile)
+ self.wnd.Develop.Spil.Stim2SdtDlg.Destroy()
+ self.wnd.Develop.Spil.Stim2SdtDlg.Show(0)
+
+############################################################################################################
+# FUNCTION NAME : Stb2Sdt
+# FUNCTION DESCRIPTION: Chiama la funzione matlab Stb2Sdt x convertire i pattern di STB
+# in formato SDT
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def Stb2Sdt(self):
+ #SpilMatLib='M:\\Work\\4TV_SPIL_00\\LIB\\SPILMATLIB\n'
+ SpilMatLib=os.path.dirname(glob.glob(self.CurrentPylibPath+'\\SetCalib.m')[0]) #OK generale
+
+ LaunchFile=" /r cd('"+SpilMatLib+"');Stb2Sdt('"+\
+ self.wnd.Develop.Spil.Stb2SdtDlg.Panel.FolderStimulusText.GetValue()+"');quit"
+ os.system(self.MtlbInstall+LaunchFile)
+ self.wnd.Develop.Spil.Stb2SdtDlg.Destroy()
+ self.wnd.Develop.Spil.Stb2SdtDlg.Show(0)
+
+
+############################################################################################################
+# FUNCTION NAME : ClearPyc
+# FUNCTION DESCRIPTION: Funzione che cancella i file .pyc se trova nella stessa cartella file omonimi
+# ma con estensione .py. La ricerca vieve effettuata nelle librerie e nei TD
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def ClearPyc(self):
+ print "Deleting .pyc files..."
+ # Trovo i files pyc presenti nei TD, nelle Librerie e sotto Work
+ PycList=glob.glob(self.CurrentPrj+'\\*\\*\\*.pyc')+glob.glob(os.path.split(self.ProjPath)[0]+'\\*.pyc')
+ # Solo se esiste un corrispondente .py rimuovo il .pyc
+ for Pyc in PycList:
+ if os.path.exists(os.path.splitext(Pyc)[0]+'.py'):
+ os.remove(Pyc)
+
+
+############################################################################################################
+# FUNCTION NAME : RUN_TEST
+# FUNCTION DESCRIPTION: Chiama lo script di lancio PyTdLnc x i test HIL e lancia direttamente i test x NSI e SPIL
+# in modalità RUN o DEBUGU (può chiamare qualsiasi script anche non python)
+# - Disabilita i pulsanti di GO e RELOAD
+# - Crea un file per il log
+# - Resetta lo stato dei led
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def RUN_TEST(self):
+ #Se il lancio è stato fatto in debug non considerare i repeat
+ if self.wnd.launcher.run.GetSelection():
+ self.wnd.RptTestSpn.SetValue(1)
+ #Ripeto lo scenario di test quante volte indicato dal pannello
+ for self.nrpt in range(int(self.wnd.RptTestSpn.GetValue())):
+ if int(self.wnd.RptTestSpn.GetValue())>1:
+ print "\n\n\nRIPETIZIONE SCENARIO N° ",self.nrpt+1
+ NTS=str(self.nrpt+1)+'°'
+ else:
+ NTS=''
+
+ #inizializzo il file led_status.txt con i stopflag degli strumenti = 0
+ f=open('led_status.txt','w')
+ f.writelines(['LED_STATUS=[]\n','stopInstr=0\n','stopflgHIL=0\n','stopflgNSI=0\n','stopflgSPIL=0\n'])
+ f.close()
+ #Dopo l'inizializzazione, in cui ho lavorato sul file led_status.txt, posso dare il verde alla CheckLed
+ #(Se non lo facessi ci sarebbe il rischio di una collisione sul led_status.txt)
+ self.MngThr2.set()
+
+ #Mi assicuro di essere nella directory del Launcher
+ os.chdir(self.LauncherPath)
+ #Metto il semaforo rosso a questo Thread (starò in wait finchè il singolo strumento non ha finito)
+ self.MngThr1.clear()
+ #Disabilita i pulsanti di GO, RELOAD e repeat
+ self.wnd.GO.Enable(FALSE)
+ self.wnd.reload.Enable(FALSE)
+ self.wnd.RptTestSpn.Enable(FALSE)
+ self.wnd.RptTestTxt.Enable(FALSE)
+ #Crea un file per il log a cui i singoli TD devono accedere con l'attributo append
+ #Inizialmente ha colore verde, se vi è qualche errore diventa rosso
+ self.wnd.launcher.log.SetBackgroundColour(wxGREEN)
+ if self.nrpt==0:
+ filelog=open(self.LauncherPath+'\\log.txt','w')
+ else:
+ filelog=open(self.LauncherPath+'\\log.txt','a')
+ filelog.write("\n\n\nSTART %s TESTS SESSION:%s\n"%(NTS,time.ctime(time.time())))
+ filelog.close()
+ #Resetto lo stato dei led
+ for indexTD in range(len(self.GlobalTestList)):
+ for indexTC in range(len(self.GlobalTestList[indexTD])):
+ str2exe="self.scmain.sc.TD"+str(indexTD+1)+".led"+str(indexTC+1)+"_"+str(indexTD+1)+".SetBackgroundColour('grey')"
+ exec(str2exe)
+
+ #Leggo se lanciare il test per ecu sviluippo piuttosto che ecu di produzione
+ if not(self.wnd.launcher.ECU.GetSelection()):
+ self.EcuType="EcuType='sviluppo'"
+ else:
+ self.EcuType="EcuType='produzione'"
+ #Verifico strumento x strumento se sono stati selezionati dei TC
+ InstrExec=[] #InstrExec ha tanti elementi quanti sono gli strumenti e sono settati a 1 se c'è almeno un TC selezionato
+ for nInstr in range(1,len(self.TD_NumTdXInstr)):
+ InstrExec.append(0)
+ #scandisco il GlobalTestList solo nel range dello strumento considerato
+ for TypeTestListTD in self.GlobalTestList[self.TD_NumTdXInstr[nInstr-1]:self.TD_NumTdXInstr[nInstr]]:
+ if TypeTestListTD.count(1) or TypeTestListTD.count(2):
+ InstrExec[nInstr-1]=1
+ break
+
+
+ #----------------------------LANCIO I TEST PER HIL (solo se almeno 1 TC è selezionato) ---------------------------#
+ if InstrExec[0]==1:
+ #Scrivo nel tmp.txt le informazioni sui tests selezionati, sul nome dei tests e sul path della libreria utili per il PyTdLnc.py
+ f=open(self.LauncherPath+'\\tmp.txt','w+')
+ f.write(self.EcuType)
+ f.write('\nHilTestList='+str(self.GlobalTestList[:self.TD_NumTdXInstr[1]]))
+ f.write('\nHilTD_DATA='+str(self.TD_DATA[:self.TD_NumTdXInstr[1]]))
+ f.write('\nPylibPath=r"'+str(self.CurrentPylibPath)+'"')
+ f.write('\n')
+ f.close()
+ try:
+ # Se c'è il flag di cancellatura .pyc, lancio la funzione che dalla root
+ # elimina i pyc se esiste anche il py (sia nelle lib che nei TD)
+ if self.wnd.Develop.Python.PycBox.GetValue():
+ ClearPyc(self)
+
+ #modalità RUN
+ if not(self.wnd.launcher.run.GetSelection()):
+ command=self.PythonPath+' -c "import PyTdLnc"'
+ os.system(command)
+ #modalità DEBUG
+ else:
+ if string.lower(os.path.split(self.PythonwinPath)[1])=="idle.pyw":
+ command=self.PythonwinPath +' -d -e "'+self.LauncherPath+'\\PyTdLnc.py"'
+ print"debug session..."
+ os.system(command)
+ elif string.lower(os.path.split(self.PythonwinPath)[1])=="pythonwin.exe":
+ command=self.PythonwinPath+' PyTdLnc.py'
+ print"debug session..."
+ os.system(command)
+ else:
+ print "NOT VALID DEBUGGER\n Change debugger program "
+ except:
+ #se c'è un'eccezione a questo livello coloro di rosso il pulsante di log
+ self.wnd.launcher.log.SetBackgroundColour(wxRED)
+
+ #Se la piattaforma è NT allora ferma il thread
+ if os.environ.has_key('OS'):
+ self.MngThr1.wait()
+ else:
+ #Se non c'è nessun TC del HIL allora colora di grigio i led relativi e setta lo stopflgHIL a 1
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ exec(tmpList[0])
+ for count in range(self.TD_NumTdXInstr[1]-self.TD_NumTdXInstr[0]):
+ LED_STATUS.append([])
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n' #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ tmpList[2]='stopflgHIL=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+
+
+
+ #---------------------------------LANCIO I TEST PER NSI ((solo se almeno 1 TC è selezionato)
+ self.MngThr1.clear()
+ if InstrExec[1]==1:
+ try:
+ #modalità RUN
+ if not(self.wnd.launcher.run.GetSelection()):
+ RUN_NSI(self)
+ #modalità DEBUG
+ else:
+ DEBUG_NSI(self)
+ finally:
+ #Se la piattaforma è NT allora ferma il thread
+ if os.environ.has_key('OS'):
+ self.MngThr1.wait()
+ else:
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ exec(tmpList[0])
+ for count in range(self.TD_NumTdXInstr[2]-self.TD_NumTdXInstr[1]):
+ LED_STATUS.append([])
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n' #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ tmpList[3]='stopflgNSI=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+
+ #---------------------------------LANCIO I TEST PER LO SPIL ((solo se almeno 1 TC è selezionato)
+ self.MngThr1.clear()
+ if InstrExec[2]==1:
+ try:
+ #modalità RUN
+ if not(self.wnd.launcher.run.GetSelection()):
+ RUN_SPIL(self)
+ #modalità DEBUG
+ else:
+ DEBUG_SPIL(self)
+ finally:
+ #Se la piattaforma è NT allora ferma il thread
+ if os.environ.has_key('OS'):
+ self.MngThr1.wait()
+
+
+ else:
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ exec(tmpList[0])
+ for count in range(self.TD_NumTdXInstr[3]-self.TD_NumTdXInstr[2]):
+ LED_STATUS.append([])
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n' #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ tmpList[4]='stopflgSPIL=1\n' #Se non sono stati settati test x SPIL metto il flag a 1
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+
+
+ #----------------------------------------------------------------------------------------------------------
+ #...
+ #...
+ #...
+
+
+ #Chiudo il file di log
+ filelog=open(self.LauncherPath+'\\log.txt','a+')
+ filelog.write("\nSTOP TESTS SESSION:%s"%time.ctime(time.time()))
+ filelog.close()
+
+
+
+############################################################################################################
+# FUNCTION NAME : RUN_NSI
+# FUNCTION DESCRIPTION: Lancia i TD per NSI, per ogni TD:
+# - crea compile.bat e lo lancia
+# - se compilazione Ok-->crea run.bat e lo lancia
+# - aggiorna il file led_status.txt
+# I parametri dei Tc selezionati, del tipo di ecu e del path sono scritti nel file
+# command.txt dopo la compilazione nella cartella del TD
+# Il risultato dei Tc è passato nel file result.txt creato nella cartella del TD
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def RUN_NSI(self):
+ filelog=open('log.txt','a+')
+ exec(self.EcuType) #EcuType='sviluppo'
+ NsiTestList=self.GlobalTestList[self.TD_NumTdXInstr[1]:self.TD_NumTdXInstr[2]]
+ #es: HilTestList=[[2, 2, 2, 0, 2, 1, 2, 2], [1, 2, 2, 2, 2, 1, 2, 2]]
+ NsiTD_DATA=self.TD_DATA[self.TD_NumTdXInstr[1]:self.TD_NumTdXInstr[2]]
+ #es: NsiTD_DATA=[['M:\\Work\\5sf.01\\Macro_Funzione2.0\\TD_Scot.6.5', 'td_glam', ['TD GLAM', 'Lambda OP', 'CA', 'CCGND', 'CCVCC', 'CCVBAT', 'plausibilit\xe0', 'riscaldatori', 'CC GNDriscaldatore']],
+ # ['M:\\Work\\5sf.01\\Macro_Funzione2.0\\TD_Scot.6.6', 'td_glambis', ['TD GLAMbis', 'Lambda OP', 'CA', 'CCGND', 'CCVCC', 'CCVBAT', 'plausibilit\xe0', 'riscaldatori', 'CC GNDriscaldatore']]]
+ #eseguo i TD selezionati
+ print '\n\n\n\n\n\n\nSTART NSI TESTS SESSION'
+ tmpLed=[]#inizializzo la variabile temporanea che raccoglie lo stato dei led
+ for indexTD in range(len(NsiTestList)):
+ TypeTestListTD=NsiTestList[indexTD]
+ TD_DATA=NsiTD_DATA[indexTD]
+ try:
+ if (TypeTestListTD.count(1) or TypeTestListTD.count(2)): #non eseguo se tutti i TC sono NO
+ path=TD_DATA[0] #es: path='M:\Work\5sf.01\Macro_Funzione2.0\TD_Scot.6.5'
+ #se per qualsiasi motivo alla partenza si ha il file result.txt f=open(str(path)+r'\result.txt','r+')
+ if (os.path.exists(str(path)+r'\result.txt')):
+ os.remove(str(path)+r'\result.txt')
+ #Creo il compile.bat
+ MSDevDir="set MSDevDir="+self.NSIMSDevDir
+ MSVCDir="\n\nset MSVCDir="+self.NSIMSVCDir
+ NSIDir="\n\nset NSIDir="+self.NSIDir
+ PATH="\n\nset PATH=%PATH%;%MSDevDir%\BIN;%MSVCDir%\BIN;"
+ #includo le librerie presenti nel progetto
+ IncludeLib='\n\nset INCLUDE=%INCLUDE%;%MSVCDir%\INCLUDE;%MSVCDir%\MFC\INCLUDE;%MSVCDir%\ATL\INCLUDE;%NSIDir%\INC;'
+ SetLib='\n\nset LIB=%LIB%;%MSVCDir%\LIB;%MSVCDir%\MFC\LIB;%NSIDir%\LIB;'
+ for PathLib in glob.glob(self.CurrentPylibPath):
+ IncludeLib=IncludeLib+str(PathLib)+';'
+ SetLib=SetLib+str(PathLib)+';'
+ #es: set INCLUDE=M:\Work\NSI_00\Lib\TESTLAUNCHERVS20_BOL;M:\Work\NSI_00\Lib\modello;M:\Work\NSI_00\Lib\LibFirstIni;
+
+ #mi metto nella directory della TD
+ CDTD='\n\ncd '+str(path)
+ #es: cd M:\Work\NSI_00\Macro_FunzioneNSI1\NomeTD1.5
+
+ #mi posiziono sul disco della TD
+ Drive='\n\n'+os.path.splitdrive(str(path))[0]
+ #es: M:
+
+ #lancio la compilazione fornendo in uscita nel file make.log l'esito della compilazione
+ command='\n\nmsdev '+ TD_DATA[1]+'.dsp /MAKE '+'"'+TD_DATA[1]+' - Win32 Debug"'+' /USEENV /OUT make.log'
+ #es: msdev Id_TxMsg.dsp /MAKE "Id_TxMsg - Win32 Debug" /USEENV /OUT make.log
+
+ #scrivo il .bat e lo eseguo
+ filebat=open('compile.bat','w+')
+ filebat.write(MSDevDir)
+ filebat.write(MSVCDir)
+ filebat.write(NSIDir)
+ filebat.write(PATH)
+ filebat.write(IncludeLib)
+ filebat.write(SetLib)
+ filebat.write(CDTD)
+ filebat.write(Drive)
+ filebat.write(command)
+ filebat.close()
+ #Lancio la compilazione
+ str2exe=str(self.LauncherPath)+'\compile'
+ #se per qualsiasi motivo alla partenza si ha il file result.txt f=open(str(path)+r'\result.txt','r+')
+ if (os.path.exists(str(path)+r'\make.log')):
+ os.remove(str(path)+r'\make.log')
+ os.system(str2exe) #1 se KO
+ if (os.path.exists(str(path)+r'\make.log')):
+ fileerror=open(str(path)+'\make.log','r')
+ testo=fileerror.read()
+ errorstr=testo.splitlines()[-1]
+ fileerror.close()
+ error=errorstr[string.find(errorstr,'error(s)',0)-2]
+ #In caso di esito positivo creo il file command.txt in cui passo alcuni parametri utili per il test
+ if int(error)==0:
+ commandfile=open(str(path)+'\command.txt','w+')
+ commandfile.write(str(TypeTestListTD)) #test case selezionati es: [0, 0, 0, 2, 0, 0, 0, 0, 0, 0]
+ commandfile.write('\n'+EcuType) #tipo di ecu es: sviluppo
+ commandfile.write('\n'+path) #path della TD stessa es: M:\Work\NSI_00\Macro_FunzioneNSI1\NomeTD1.5
+ commandfile.write('\n'+self.LauncherPath) #path del launcher
+ commandfile.close()
+ #sempre in caso di compilazione positiva creo il .bat per il lancio del .exe
+ #vado a cercare dov'è il file first.ini tra le librerie
+ firstiniDIR=glob.glob(self.CurrentPylibPath+r'\first.ini')
+ # In base a al tipo di ECU passo un second.ini diverso
+ if EcuType=='sviluppo':
+ secondiniDIR=glob.glob(self.CurrentPylibPath+r'\second_DEV.ini')
+ else: #se è di produzione
+ secondiniDIR=glob.glob(self.CurrentPylibPath+r'\second_PROD.ini')
+ #es:
+ command='\n\n'+str(path)+'\\Debug\\'+ TD_DATA[1]+'.exe '+'"'+firstiniDIR[0]+'"'+' 0x50 '+'"'+secondiniDIR[0]+'"'
+ filebat=open('run.bat','w+')
+ filebat.write(CDTD)
+ filebat.write(Drive)
+ #Aggiunta nella variabile d'ambiente LIB i path delle librerie del progetto
+ filebat.write(SetLib)
+ filebat.write(command)
+ filebat.close()
+ #lancio il .bat
+ str2exe=str(self.LauncherPath)+r'\run'
+ os.system(str2exe)
+
+ #Quando il test è finito leggo lo stato dei led nel file result.txt con formato es: [0,1,1,3,2,1]
+ f=open(str(path)+r'\result.txt','r+')
+ TDresult=f.readline()
+ str2exec='tmpLed.append('+TDresult+')'
+ f.close()
+ exec(str2exec)
+
+ #Verifico anche che i test non abbiano avuto problemi nell'esecuzione.
+
+ if map(CompList,TypeTestListTD,TDresult).count(0)>0:
+ filelog.write("\n TEST %s FALLITO\n"%(TD_DATA[2][0]))
+ else:
+ filelog.write("\n TEST %s ESEGUITO\n"%(TD_DATA[2][0]))
+ #Coloro i led della grafica
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines() #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ exec(tmpList[0])
+ LED_STATUS=LED_STATUS+tmpLed
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ #una volta scritto l'esito dei TC nel LED_STATUS resetto la var temporanea
+ tmpLed=[]
+ f=open('led.flg','w+');f.close()
+ else:
+ filelog.write("\n**********\nTD %s FALLITA: compilazione fallita\n %s"%(TD_DATA[2][0],testo))
+ filelog.write("\n**********\n")
+## self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ tmpLed.append([])
+ else:
+ filelog.write("\n**********\nTD %s FALLITA: compilazione non possibile\n Controllare l'installazione NSI"%(TD_DATA[2][0]))
+ filelog.write("\n**********\n")
+## self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ tmpLed.append([])
+ else:
+ tmpLed.append([])
+
+ except:
+ tmpLed.append([])
+ (ErrorType,ErrorValue,ErrorTB)=sys.exc_info()
+ filelog.write("\n**********\nTD %s FALLITA:\nTIPO:%s\nTRACE:\n"%(TD_DATA[2][0],ErrorValue))
+ traceback.print_exc(None,filelog)
+ filelog.write("**********\n")
+ filelog.close()
+ print ("TD %s FALLITA"%TD_DATA[2][0])
+## self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ #Finita la sessione di test per questo strumento setto a 1 i flag di stopInstr e stopflgNSI
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ exec(tmpList[0])
+ LED_STATUS=LED_STATUS+tmpLed
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n' #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ tmpList[1]='stopInstr=1\n'
+ tmpList[3]='stopflgNSI=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+ filelog.close()
+
+############################################################################################################
+# FUNCTION NAME : DEBUG_NSI
+# FUNCTION DESCRIPTION: Lancia il primo TD selezionato per NSI in modalità debug
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def DEBUG_NSI(self):
+ exec(self.EcuType) #EcuType='sviluppo'
+ NsiTestList=self.GlobalTestList[self.TD_NumTdXInstr[1]:self.TD_NumTdXInstr[2]]
+ NsiTD_DATA=self.TD_DATA[self.TD_NumTdXInstr[1]:self.TD_NumTdXInstr[2]]
+ try:
+ print"\n\n\n\n\n\n\nNSI debug session..."
+ if len(NsiTestList)!=0:
+ #vado in debug sul primo TD selezionato
+ for indexTD in range(len(NsiTestList)):
+ if (NsiTestList[indexTD].count(1) or NsiTestList[indexTD].count(2)): #non considero se tutti i TC sono NO
+ break
+ TypeTestListTD=NsiTestList[indexTD]
+ TD_DATA=NsiTD_DATA[indexTD]
+ if (TypeTestListTD.count(1) or TypeTestListTD.count(2)): #non eseguo se tutti i TC sono NO
+ path=TD_DATA[0] #es: path='M:\Work\5sf.01\Macro_Funzione2.0\TD_Scot.6.5'
+
+ #Creo il compile.bat
+ MSDevDir="set MSDevDir="+self.NSIMSDevDir
+ MSVCDir="\n\nset MSVCDir="+self.NSIMSVCDir
+ NSIDir="\n\nset NSIDir="+self.NSIDir
+ PATH="\n\nset PATH=%PATH%;%MSDevDir%\BIN;%MSVCDir%\BIN;"
+ #includo le librerie presenti nel progetto
+ IncludeLib='\n\nset INCLUDE=%INCLUDE%;%MSVCDir%\INCLUDE;%MSVCDir%\MFC\INCLUDE;%MSVCDir%\ATL\INCLUDE;%NSIDir%\INC;'
+ SetLib='\n\nset LIB=%LIB%;%MSVCDir%\LIB;%MSVCDir%\MFC\LIB;%NSIDir%\LIB;'
+ for PathLib in glob.glob(self.CurrentPylibPath):
+ IncludeLib=IncludeLib+str(PathLib)+';'
+ SetLib=SetLib+str(PathLib)+';'
+ CDTD='\n\ncd '+str(path)
+ command='\n\nmsdev '+ TD_DATA[1]+'.dsw /USEENV'
+ Drive='\n\n'+os.path.splitdrive(str(path))[0]
+ commandfile=open(str(path)+'\command.txt','w+')
+ commandfile.write(str(TypeTestListTD))
+ commandfile.write('\n'+EcuType)
+ commandfile.write('\n'+path)
+ commandfile.write('\n'+self.LauncherPath) #path del launcher
+ commandfile.close()
+ filebat=open('run.bat','w+')
+ filebat.write(MSDevDir)
+ filebat.write(MSVCDir)
+ filebat.write(NSIDir)
+ filebat.write(PATH)
+ filebat.write(IncludeLib)
+ filebat.write(SetLib)
+ filebat.write(CDTD) #es: cd M:\Work\NSI_00\Macro_FunzioneNSI1\NomeTD1.5
+ filebat.write(Drive) #es: M:
+ filebat.write(command) #es: msdev Id_TxMsg.dsw /USEENV
+ filebat.close()
+ str2exe=str(self.LauncherPath)+r'\run'
+ if os.system(str2exe):
+ filelog=open('log.txt','a+')
+ filelog.write("\n**********\nTD %s FALLITA: debug non possibile,\n Controllare l'installazione NSI"%(TD_DATA[2][0]))
+ filelog.write("\n**********\n")
+ filelog.close()
+## self.wnd.launcher.log.SetBackgroundColour(wxRED)
+
+ except:
+ (ErrorType,ErrorValue,ErrorTB)=sys.exc_info()
+ filelog.write("\n**********\nTD %s FALLITA:\nTIPO:%s\nTRACE:\n"%(TD_DATA[2][0],ErrorValue))
+ traceback.print_exc(None,filelog)
+ filelog.write("**********\n")
+ filelog.close()
+ print ("TD %s FALLITA"%TD_DATA[2][0])
+## self.wnd.launcher.log.SetBackgroundColour(wxRED)
+
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ tmpList[1]='stopInstr=1\n'
+ tmpList[2]='stopflgHIL=1\n'
+ tmpList[3]='stopflgNSI=1\n'
+ tmpList[4]='stopflgSPIL=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+
+
+############################################################################################################
+# FUNCTION NAME : RUN_SPIL
+# FUNCTION DESCRIPTION: Lancia i TD per SPIL, per ogni TD:
+# - xxxcrea compile.bat e lo lancia
+# - xxxse compilazione Ok-->crea run.bat e lo lancia
+# - xxxxaggiorna il file led_status.txt
+# xxxI parametri dei Tc selezionati, del tipo di ecu e del path sono scritti nel file
+# xxxcommand.txt dopo la compilazione nella cartella del TD
+# xxxIl risultato dei Tc è passato nel file result.txt creato nella cartella del TD
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambienter
+#
+############################################################################################################
+def RUN_SPIL(self):
+
+ filelog=open('log.txt','a+')
+ filelog.close()
+ try:
+ tmpLed=[]#inizializzo la variabile temporanea che raccoglie lo stato dei led
+ exec(self.EcuType) #EcuType='sviluppo'
+ #DIrectory di installazione dell'ISS
+ StartUpIssPath=os.path.dirname(self.IssInstall)
+ SpilTestList=self.GlobalTestList[self.TD_NumTdXInstr[2]:self.TD_NumTdXInstr[3]]
+ #es: SpilTestList=[[2, 2, 2, 0, 2, 1, 2, 2], [1, 2, 2, 2, 2, 1, 2, 2]]
+ SpilTD_DATA=self.TD_DATA[self.TD_NumTdXInstr[2]:self.TD_NumTdXInstr[3]]
+ #Inizializzazione: utile solo per la exception in caso di errore prima di entrare nel for dei TD
+ TD_DATA=SpilTD_DATA[0]
+ #es: SpilTD_DATA=[['M:\\Work\\5sf.01\\Macro_Funzione2.0\\TD_Scot.6.5', 'td_glam', ['TD GLAM', 'Lambda OP', 'CA', 'CCGND', 'CCVCC', 'CCVBAT', 'plausibilit\xe0', 'riscaldatori', 'CC GNDriscaldatore']],
+ # ['M:\\Work\\5sf.01\\Macro_Funzione2.0\\TD_Scot.6.6', 'td_glambis', ['TD GLAMbis', 'Lambda OP', 'CA', 'CCGND', 'CCVCC', 'CCVBAT', 'plausibilit\xe0', 'riscaldatori', 'CC GNDriscaldatore']]]
+ #eseguo i TD selezionati
+ print '\n\n\n\n\n\n\nSTART SPIL TESTS SESSION'
+
+ #Definizioni generali per la costruzione del file MatStrtp.m che lancerà la pre- elaborazione
+ #Ricavo il path del DD dal file load.cmm presente in M:\work\load.cmm, es:DD='Z:\\cdm\\meb\\4TV.dd'
+ f=open(os.path.split(self.ProjPath)[0]+"\\load.cmm",'r+')
+ testo=f.read()
+ f.close()
+ DD_start=string.find(testo,"DD='")
+ if DD_start>=0 :
+ DD_stop=string.find(testo,"\n",DD_start)
+ line=testo[DD_start:DD_stop]
+ exec(line)
+ #Ricavo il path del DD es:ProjectSyncPath='Z:\\'
+ Prj_start=string.find(testo,"ProjectSyncPath='")
+ if Prj_start>=0 :
+ Prj_stop=string.find(testo,"\n",Prj_start)
+ line=testo[Prj_start:Prj_stop]
+ exec(line)
+ #Mi serve la data dell'ultima modifica del DD in quanto
+ #se il .dd a cui ha puntato il DataDict.mat presente è cambiato allora devo ricreare il DataDict.mate
+ DDLastChangeInSec=str(os.stat(DD)[stat.ST_MTIME])
+
+
+ #SpilMatLib='M:\\Work\\4TV_SPIL_00\\LIB\\SPILMATLIB\n'
+ SpilMatLib=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\SetCalib.m')[0]) #OK generale
+
+ DataDictPath=self.LauncherPath+'\\SpilTmp\\DD\\DataDict.mat' #con ricerca se il load.cmm è cambiato
+
+ CalibPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\calibfile.txt' #OK general
+ InputPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\varfile.txt' #OK general
+ EventPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\eventfile.txt' #OK general
+ EventVectorPath=self.LauncherPath+'\\SpilTmp\\Result\\EvtVectAll' #OK general
+ OutputNamePathSpil=self.LauncherPath+'\\SpilTmp\\Result\\outname.txt' #OK general
+ PowerOnPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\PowerOn.txt' #OK general
+ #IRPath='M:\\Work\\4TV_SPIL_00\\LIB\\SPIL4TVST280\\IRlist' #ricerca del file IRlist
+ if glob.glob(self.CurrentPrj+'\\Lib\\*\\IRlist.m')!=[]:
+ IRPath=os.path.splitext(glob.glob(self.CurrentPrj+'\\Lib\\*\\IRlist.m')[0])[0] #OK generale
+ else:
+ IRPath=''
+ IRlistCkdPath=self.LauncherPath+'\\SpilTmp\\Result\\IRlistCkd' #OK generale
+
+ #OutputPathSpil='M:\\SPILLAUNCHER\\SpilTmp\\Result\\output.txt'
+ OutputPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\output.txt'
+ #Dir2Zip="M:\\SPILLAUNCHER\\SpilTmp"
+ Dir2Zip=self.LauncherPath+'\\SpilTmp'
+
+
+ #Lista della START di ISS
+ #Rinomino l'eventuale T32.cmm originale in T32orig.cmm
+ if os.path.exists(StartUpIssPath+'\\T32.cmm'):
+ if not(os.path.exists(StartUpIssPath+'\\T32orig.cmm')):
+ shutil.copy(StartUpIssPath+'\\T32.cmm',StartUpIssPath+'\\T32orig.cmm')
+ StartUpIssFile=['do '+self.LauncherPath+'\\SpilTmp\\Result\\IssStrtp.cmm\n']
+ f=open(StartUpIssPath+'\\T32.cmm','w+')
+ f.writelines(StartUpIssFile)
+ f.close()
+
+ #ProjectPath="C:\\SPIL\\WORK\\SET4TV,02_24"
+ ProjectLibPath=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\Init.cmm')[0])
+ #LibPath="C:\\SPIL\\WORK\\PROGETTO_4TV\\LIB\\SPILSCRIPT"
+ LibPath=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\SPIL.cmm')[0])
+ #Leggo il settaggio del coverage
+ CovFlg=str(self.wnd.Develop.Spil.CovBox.GetValue())
+
+ for indexTD in range(len(SpilTestList)):
+ TypeTestListTD=SpilTestList[indexTD]
+ TD_DATA=SpilTD_DATA[indexTD]
+ #Inizializzo il LED_STATUS
+ TdLed=[]
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines() #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ exec(tmpList[0])
+ LED_STATUS.append(TdLed)
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+
+ if (TypeTestListTD.count(1) or TypeTestListTD.count(2)): #non eseguo se tutti i TC sono NO
+ TimeStartTest = time.time() #Tempo inizio test
+ path=TD_DATA[0] #es: path='M:\Work\5sf.01\Macro_Funzione2.0\TD_Scot.6.5'
+
+ #Definizioni particolari del TD per la costruzione del file MatStrtp.m che lancerà la pre- elaborazione
+ #CalibGenPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_CAL_GEN.mat'
+ CalibGenPathSdt=path+'\\'+TD_DATA[2][0]+'_CAL_GEN.mat'#OK td
+ #TollPath='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\TOLL_SM06_GEN.m' #da script
+ TollPath=path+'\\TOLL_'+TD_DATA[2][0]+'_GEN.m'#OK tc
+ #ReportPath='M:\\WORK\\REPORTS\\report.txt'
+ #ExtName è l'estensione da aggiungere alla cartella del report per individuare il gg hh mm ss del test
+ ExtName="("+str(time.localtime()[2])+"gg)("+str(time.localtime()[3])+"hh)("+str(time.localtime()[4])+"mm)("+str(time.localtime()[5])+"ss)"
+ ReportPath=self.ReportSpil+'\\'+TD_DATA[2][0]+ExtName+'\\report.txt'
+ #Apro il file di report
+ os.makedirs(os.path.dirname(ReportPath))
+ f=open(ReportPath,'w')
+ f.close()
+
+ PrimoTc=1
+ for nTC in range(len(TypeTestListTD)):
+ if TypeTestListTD[nTC]!=0:
+ #Se esiste il file result.txt allora lo cancello
+ if os.path.exists(self.LauncherPath+'\\result.txt'):
+ os.remove(self.LauncherPath+'\\result.txt')
+ #FILE IssStrtp
+ SaveCov=''
+ LoadCov=''
+ CovView=''
+ #se è abilitato il flag del coverage salvo il contenuto del cov nella cartella del report
+ #e a partire dal 2° TC lanciato carico il TC precedente
+ if CovFlg=="1":
+ SaveCov='a.coverage.save '+self.ReportSpil+'\\'+TD_DATA[2][0]+ExtName+'\\cov'
+ CovView='a.coverage.listfunc\n'
+ if PrimoTc==0:
+ LoadCov='a.coverage.load '+self.ReportSpil+'\\'+TD_DATA[2][0]+ExtName+'\\cov\n'
+ IssStrtp=[
+ 'on error goto exception\n',\
+ '&ProjectLibPath="'+ProjectLibPath+'"\n',\
+ '&LauncherPath="'+self.LauncherPath+'\\SpilTmp\\Result'+'"\n',\
+ '&LibPath="'+LibPath+'"\n',\
+ 'cd &ProjectLibPath\n',\
+ '&StopDebugTime=0\n',\
+ '&CovFlg='+CovFlg+'\n',\
+ '&exec="&ProjectLibPath"+"\\t32.cmm"\n',\
+ 'do &exec\n',\
+ LoadCov,\
+ CovView,\
+ 'cd '+ProjectSyncPath+'\n',\
+ '&exec="'+LibPath+'\\SPIL.cmm"\n',\
+ 'do &exec\n',\
+ SaveCov,\
+ '\nquit\n',\
+ 'exception:\n',
+ 'OPEN #10 '+self.LauncherPath+'\\result.txt /Create\n',\
+ 'WRITE #10 %CONTINUE "0"\n',\
+ 'CLOSE #10\n',\
+ 'quit\n']
+
+ #individuo il nome
+ TCname=TD_DATA[2][nTC+1]
+ indexTC=TCname[string.find(TCname,"_TP_")+4:string.find(TCname,"_TP_")+6]
+ #Solo se è il primo Tc devo aggiungere 3 righe allo MatStop.m
+ if PrimoTc:
+ MatStop2=[" IssPath='"+StartUpIssPath+"';\n",\
+ ' GeneralReport(LauncherPath,IssPath,ReportPath)\n']
+ PrimoTc=0
+ else:
+ MatStop2=[]
+
+ #CalibTpPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_CAL_TP_01.mat'
+ CalibTpPathSdt=path+'\\'+TD_DATA[2][0]+'_CAL_TP_'+indexTC+'.mat'#OK tc
+ if not(os.path.exists(CalibTpPathSdt)):
+ CalibTpPathSdt=''
+
+ #InputPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_in.mat'
+ InputPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_in.mat'#OK tc
+
+ #OutputPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_ou.mat'
+ OutputPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_ou.mat'#OK tc
+
+ #TestpointPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_tx.mat' #da script
+ TestpointPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_tx.mat'#OK tc
+ if not(os.path.exists(TestpointPathSdt)):
+ TestpointPathSdt=''
+
+ #Dove andare a salvare lo zip dei risultati
+ ZipNamePath=os.path.dirname(ReportPath)+'\\'+TD_DATA[2][0]+'_TP'+indexTC+'.zip'
+ #Dove andare a salvare le figure dei risultati
+ FigPath=os.path.dirname(ReportPath)+'\\TP_'+indexTC;
+ #Tipo di Test 1-->OL,2-->CL
+ if TypeTestListTD[nTC]==2:
+ TIPOTEST='CL'
+ else:
+ TIPOTEST='OL'
+ #Creazione della lista che poi sarà stampata
+ MatStrtp=["DD='"+DD+"';\n",\
+ "DataDictPath='"+DataDictPath+"';\n",\
+ "CalibGenPathSdt='"+CalibGenPathSdt+"';\n",\
+ "CalibTpPathSdt='"+CalibTpPathSdt+"';\n",\
+ "CalibPathSpil='"+CalibPathSpil+"';\n",\
+ "InputPathSdt='"+InputPathSdt+"';\n",\
+ "InputPathSpil='"+InputPathSpil+"';\n",\
+ "EventPathSpil='"+EventPathSpil+"';\n",\
+ "EventVectorPath='"+EventVectorPath+"';\n",\
+ "OutputPathSdt='"+OutputPathSdt+"';\n",\
+ "TestpointPathSdt='"+TestpointPathSdt+"';\n",\
+ "OutputNamePathSpil='"+OutputNamePathSpil+"';\n",\
+ "IRPath='"+IRPath+"';\n",\
+ "IRlistCkdPath='"+IRlistCkdPath+"';\n",\
+ "TollPath='"+TollPath+"';\n",\
+ "PowerOnPathSpil='"+PowerOnPathSpil+"';\n",\
+ "ProjectLibPath='"+ProjectLibPath+"';\n",\
+ "LauncherPath='"+self.LauncherPath+"';\n",\
+ "DDTime="+DDLastChangeInSec+";\n",\
+ "IOCerr='';\n",\
+ "try\n",\
+ ' cd '+SpilMatLib+'\n',\
+ ' fid=fopen(DataDictPath);\n',' if fid==-1\n',' DDparser(DD,DataDictPath,DDTime);\n',' else\n'," load(DataDictPath,'DDGenTime');\n",' if DDGenTime==DDTime\n'," fprintf('\\nUtilizzo il Data Dictionary già esistente DataDict.mat');\n",' else\n'," fprintf('\\nCreo DataDict.mat nuovo perchè l''esistente non è aggiornato...');\n",' DDparser(DD,DataDictPath,DDTime);\n',' end\n',' end\n',\
+ ' IOCerr=IOCdiag(DataDictPath,CalibGenPathSdt,CalibTpPathSdt,InputPathSdt,OutputPathSdt,TestpointPathSdt,TollPath);\n',' if ~isempty(IOCerr)\n'," error(' SDT NON VALIDA: ');\n"," end\n",\
+ ' SetCalib(CalibGenPathSdt,CalibTpPathSdt,CalibPathSpil,DataDictPath,IRPath,IRlistCkdPath);\n',\
+ ' SetVar(InputPathSdt,InputPathSpil,DataDictPath,EventPathSpil,EventVectorPath,IRlistCkdPath,PowerOnPathSpil,ProjectLibPath);\n',\
+ ' NameOut(OutputNamePathSpil,TollPath,DataDictPath);\n',\
+ "catch\n",\
+ " ResPath=[LauncherPath '\\result.txt'];\n",\
+ " fid = fopen(ResPath,'w+');\n",\
+ " fprintf(fid,'0');\n",\
+ " fclose(fid);\n\n",\
+ " LogPath=[LauncherPath '\log.txt'];\n",\
+ " ErrorLog=[' ' lasterr];\n",\
+ " ErrorLog = strrep(ErrorLog,'\\','\\\\');\n",\
+ " fid = fopen(LogPath,'a');\n",\
+ " fprintf(fid,'\\n Pre-processing error:\\n');\n",\
+ " fprintf(fid,ErrorLog);\n",\
+ " fprintf(fid,IOCerr);\n",\
+ " fclose(fid);\n",\
+ "end\n",\
+ 'clear all\n', "fprintf('\\n\\nFINE PRE-ELABORAZIONE');\n", 'quit\n']
+
+ MatStop1=["OutputPathSdt='"+OutputPathSdt+"';\n",\
+ "TestpointPathSdt='"+TestpointPathSdt+"';\n",\
+ "DataDictPath='"+DataDictPath+"';\n",\
+ "OutputPathSpil='"+OutputPathSpil+"';\n",\
+ "EventVectorPath='"+EventVectorPath+"';\n",\
+ "TollPath='"+TollPath+"';\n",\
+ "ReportPath='"+ReportPath+"';\n",\
+ "ZipNamePath='"+ZipNamePath+"';\n",\
+ "Dir2Zip='"+Dir2Zip+"';\n",\
+ "FigPath='"+FigPath+"';\n",\
+ "TIPOTEST='"+TIPOTEST+"';\n",\
+ "ResPath='"+self.LauncherPath+"\\result.txt';\n",\
+ "LauncherPath='"+self.LauncherPath+"';\n",\
+ "try\n",\
+ ' cd '+SpilMatLib+'\n']
+
+ MatStop3=[ 'FilterOut(OutputPathSdt,TestpointPathSdt,OutputPathSpil,DataDictPath,EventVectorPath,TollPath,ReportPath,ZipNamePath,Dir2Zip,FigPath,ResPath,TIPOTEST);\n',\
+ " fprintf('\\n\\nFINE POST-ELABORAZIONE');\n",\
+ "catch\n",\
+ " ResPath=[LauncherPath '\\result.txt'];\n",\
+ " fid = fopen(ResPath,'w+');\n",\
+ " fprintf(fid,'0');\n",\
+ " fclose(fid);\n\n",\
+ " LogPath=[LauncherPath '\log.txt'];\n",\
+ " ErrorLog=[' ' lasterr];\n",\
+ " ErrorLog = strrep(ErrorLog,'\\','\\\\');\n",\
+ " fid = fopen(LogPath,'a');\n",\
+ " fprintf(fid,'\\n Post-processing error:\\n');\n",\
+ " fprintf(fid,ErrorLog);\n",\
+ " fclose(fid);\n",\
+ "end\n",\
+ 'quit\n']
+ MatStop=MatStop1+MatStop2+MatStop3
+
+
+ #Se esistente ripulisco la cartella temporanea SpilTmp, altrimento la creo nuova
+ if os.path.exists(self.LauncherPath+'\\SpilTmp\\Result'):
+ for file in glob.glob(self.LauncherPath+'\\SpilTmp\\Result\\*.*'):
+ os.remove(file)
+ else:
+ os.makedirs(self.LauncherPath+'\\SpilTmp\\Result')
+
+ #Se non esistente creo nuova cartella DD
+ if not(os.path.exists(self.LauncherPath+'\\SpilTmp\\DD')):
+ os.makedirs(self.LauncherPath+'\\SpilTmp\\DD')
+
+
+ #Riga di lancio del MatStrtp.m
+ StartUpFile=" /r cd('"+self.LauncherPath+"\SpilTmp\Result');MatStrtp"
+ #Stampa del file MatStrtp.m
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\MatStrtp.m','w+')
+ f.writelines(MatStrtp)
+ f.close()
+ os.system(self.MtlbInstall+StartUpFile)
+ SPILcomment=""
+ # Se non c'è stato nessun problema nell'esecuzione dello MatStrtp. continua
+ if not(os.path.exists(self.LauncherPath+'\\result.txt')):
+
+ #Stampa del file IssStrtp.m
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\IssStrtp.cmm','w+')
+ f.writelines(IssStrtp)
+ f.close()
+ #Creo il RunIss.bat per lanciare l'ISS
+ CDTD='\ncd '+StartUpIssPath+'\n'+os.path.splitdrive(StartUpIssPath)[0]
+ command='\n'+self.IssInstall
+ #scrivo il .bat e lo eseguo
+ filebat=open(self.LauncherPath+'\\RunIss.bat','w+')
+ filebat.write(CDTD)
+ filebat.write(command)
+ filebat.close()
+ os.system(self.LauncherPath+'\\RunIss.bat')
+ if not(os.path.exists(self.LauncherPath+'\\result.txt')):
+
+ #Stampa del file startup.m
+ StartUpFile=" /r cd('"+self.LauncherPath+"\SpilTmp\Result');MatStop"
+ #Stampa del file MatStop.m
+ os.makedirs(FigPath)
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\MatStop.m','w+')
+ f.writelines(MatStop)
+ f.close()
+ #se per qualsiasi motivo alla partenza si ha il file result.txt cancellalo
+ if (os.path.exists(self.LauncherPath+r'\result.txt')):
+ os.remove(self.LauncherPath+r'\result.txt')
+ os.system(self.MtlbInstall+StartUpFile)
+ else:
+ SPILcomment=" Errore PRACTICE, rilancia il test in modalità debug per maggiori info\n"
+ #Quando il test è finito leggo lo stato dei led nel file result.txt con formato es: [0,1,1,3,2,1]
+ f=open(self.LauncherPath+r'\result.txt','r+')
+ TcRes=f.readline()
+ f.close()
+
+ str2exec='TdLed.append('+TcRes+')'
+ exec(str2exec)
+ filelog=open('log.txt','a+')
+ if TcRes=='0':
+ filelog.write("\n TEST %s FALLITO\n%s"%(TCname,SPILcomment))
+ else:
+ filelog.write("\n TEST %s ESEGUITO\n"%(TCname))
+ filelog.close()
+ #Coloro i led della grafica
+ while (os.path.exists('led.flg')):
+ pass
+ f=open(self.LauncherPath+r'\led_status.txt','r+')
+ tmpList=f.readlines() #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ exec(tmpList[0])
+# TdLed.append(2)
+ LED_STATUS[-1]=TdLed
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ #una volta scritto l'esito dei TC nel LED_STATUS resetto la var temporanea
+ f=open('led.flg','w+');f.close()
+
+ #Aggiungo nello zip del TC il coverage cumulativo raggiunto col TC stesso
+ if CovFlg=="1":
+ myzip=zipfile.ZipFile(ZipNamePath,'a')
+ myzip.write(self.ReportSpil+'\\'+TD_DATA[2][0]+ExtName+'\\cov.acd')
+ myzip.close()
+
+
+ #Apro il file di report e compilo i campi NOT_DONE per i test case non eseguiti
+ else:
+ TdLed.append(0)
+ f=open(ReportPath,'a+')
+ NotDoneStr=['$TEST_CASE_RESULT_BEGIN\n',\
+ 'NOT_DONE\n',\
+ '$TEST_CASE_RESULT_END\n\n',\
+ '$ATTACHED_FILE_LIST_BEGIN\n',\
+ '$ATTACHED_FILE_LIST_END\n\n',\
+ '$TEST_CASE_RESULT_BEGIN\n',\
+ '$TEST_CASE_RESULT_END\n\n']
+ f.writelines(NotDoneStr)
+ f.close()
+ #Aggiungo al report i tag dei METRICS
+ # Calcolo tempo esecuzione test
+ TimeStopTest = time.time()
+ TimeEsecTest = '%.4f'%((TimeStopTest - TimeStartTest)/3600)
+ f=open(ReportPath,'a+')
+ MetricsStr=['$TEST_REPORT_METRICS_BEGIN\n',\
+ TimeEsecTest+'\n',\
+ '$TEST_REPORT_METRICS_END\n\n']
+ f.writelines(MetricsStr)
+ #Aggiungo al report i tag dei SETUP
+ if (os.path.exists(path+'\\LibraryList.txt')):
+ f=open(path+'\\LibraryList.txt','r+')
+ LibList=f.readlines()
+ f.close()
+ else:
+ LibList=['\nLibraryList.txt non trovata\n']
+ f=open(os.path.split(self.ProjPath)[0]+"\\load.cmm",'r+')
+ LoadList=f.readlines()
+ f.close()
+ f=open(ReportPath,'a+')
+ Setup=['$TEST_SETUP_BEGIN\n']+LibList+LoadList+['\n$TEST_SETUP_END\n\n']
+ f.writelines(Setup)
+ f.close()
+ except:
+ tmpLed.append([])
+ (ErrorType,ErrorValue,ErrorTB)=sys.exc_info()
+ filelog=open('log.txt','a+')
+ filelog.write("\n**********\nTD %s FALLITA:\nTIPO:%s\nTRACE:\n"%(TD_DATA[2][0],ErrorValue))
+ traceback.print_exc(None,filelog)
+ filelog.write("**********\n")
+ filelog.close()
+ print ("TD %s FALLITA"%TD_DATA[2][0])
+ self.wnd.launcher.log.SetBackgroundColour(wxRED)
+
+ #Finita la sessione di test per questo strumento setto a 1 i flag di stopInstr e stopflgSPIL
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ exec(tmpList[0])
+ LED_STATUS=LED_STATUS+tmpLed
+ tmpList[0]='LED_STATUS='+str(LED_STATUS)+'\n' #es: LED_STATUS=[[2, 2, 3, 3, 2, 3, 2, 2],[...]]
+ tmpList[1]='stopInstr=1\n'
+ tmpList[4]='stopflgSPIL=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+ filelog.close()
+ #Riporto gli startup originali
+ if (os.path.exists(StartUpIssPath+'\\T32orig.cmm')):
+ shutil.copy(StartUpIssPath+'\\T32orig.cmm',StartUpIssPath+'\\T32.cmm')
+ os.remove(StartUpIssPath+'\\T32orig.cmm')
+ else:
+ if (os.path.exists(StartUpIssPath+'\\T32.cmm')):
+ os.remove(StartUpIssPath+'\\T32.cmm')
+
+
+############################################################################################################
+# FUNCTION NAME : DEBUG_SPIL
+# FUNCTION DESCRIPTION: Lancia il primo TD selezionato per SPIL in modalità debug
+#
+# INPUT PARAMETERS:
+# 1) self: oggetto che contiene le proprietà di tutto l'ambiente
+#
+############################################################################################################
+def DEBUG_SPIL(self):
+ filelog=open('log.txt','a+')
+ try:
+ SpilTestList=self.GlobalTestList[self.TD_NumTdXInstr[2]:self.TD_NumTdXInstr[3]]
+ SpilTD_DATA=self.TD_DATA[self.TD_NumTdXInstr[2]:self.TD_NumTdXInstr[3]]
+ #Leggo tempo settato per fermarsi e rimetto il default a 0
+ Time2Stop=self.wnd.Develop.Spil.DebugTimeText.GetValue()
+ self.wnd.Develop.Spil.DebugTimeText.SetValue('0')
+ #Default del flag di coverage
+ self.wnd.Develop.Spil.CovBox.SetValue(False)
+ print"\n\n\n\n\n\n\nSpil debug session..."
+ if len(SpilTestList)!=0:
+ #vado in debug sul primo TD selezionato
+ for indexTD in range(len(SpilTestList)):
+ if (SpilTestList[indexTD].count(1) or SpilTestList[indexTD].count(2)): #non considero se tutti i TC sono NO
+ break
+ TypeTestListTD=SpilTestList[indexTD]
+ TD_DATA=SpilTD_DATA[indexTD]
+ if (TypeTestListTD.count(1) or TypeTestListTD.count(2)): #non eseguo se tutti i TC sono NO
+ path=TD_DATA[0] #es: path='M:\Work\5sf.01\Macro_Funzione2.0\TD_Scot.6.5'
+
+ #Ricavo il path del DD dal file load.cmm presente in M:\work\load.cmm, es:DD='Z:\\cdm\\meb\\4TV.dd'
+ f=open(os.path.split(self.ProjPath)[0]+"\\load.cmm",'r+')
+ testo=f.read()
+ f.close()
+ DD_start=string.find(testo,"DD='")
+ if DD_start>=0 :
+ DD_stop=string.find(testo,"\n",DD_start)
+ line=testo[DD_start:DD_stop]
+ exec(line)
+ #Ricavo il path del DD es:ProjectSyncPath='Z:\\'
+ Prj_start=string.find(testo,"ProjectSyncPath='")
+ if Prj_start>=0 :
+ Prj_stop=string.find(testo,"\n",Prj_start)
+ line=testo[Prj_start:Prj_stop]
+ exec(line)
+ #Mi serve la data dell'ultima modifica del DD in quanto
+ #se il .dd a cui ha puntato il DataDict.mat presente è cambiato allora devo ricreare il DataDict.mate
+ DDLastChangeInSec=str(os.stat(DD)[stat.ST_MTIME])
+
+ tmpLed=[]#inizializzo la variabile temporanea che raccoglie lo stato dei led
+
+ #Definizioni generali per la costruzione del file MatStrtp.m che lancerà la pre- elaborazione
+
+ #SpilMatLib='M:\\Work\\4TV_SPIL_00\\LIB\\SPILMATLIB\n'
+ SpilMatLib=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\SetCalib.m')[0]) #OK generale
+
+ DataDictPath=self.LauncherPath+'\\SpilTmp\\DD\\DataDict.mat' #con ricerca se il load.cmm è cambiato
+
+ CalibPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\calibfile.txt' #OK general
+ InputPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\varfile.txt' #OK general
+ EventPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\eventfile.txt' #OK general
+ EventVectorPath=self.LauncherPath+'\\SpilTmp\\Result\\EvtVectAll.mat' #OK general
+ OutputNamePathSpil=self.LauncherPath+'\\SpilTmp\\Result\\outname.txt' #OK general
+ PowerOnPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\PowerOn.txt' #OK general
+ #OutputPathSpil='M:\\SPILLAUNCHER\\SpilTmp\\Result\\output.txt'
+ OutputPathSpil=self.LauncherPath+'\\SpilTmp\\Result\\output.txt'
+ #Dir2Zip="M:\\SPILLAUNCHER\\SpilTmp"
+ Dir2Zip=self.LauncherPath+'\\SpilTmp'
+ StartUpIssPath=os.path.dirname(self.IssInstall)
+ #IRPath='M:\\Work\\4TV_SPIL_00\\LIB\\SPIL4TVST280\\IRlist' #ricerca del file IRlist
+ if glob.glob(self.CurrentPrj+'\\Lib\\*\\IRlist.m')!=[]:
+ IRPath=os.path.splitext(glob.glob(self.CurrentPrj+'\\Lib\\*\\IRlist.m')[0])[0] #OK generale
+ else:
+ IRPath=''
+ IRlistCkdPath=self.LauncherPath+'\\SpilTmp\\Result\\IRlistCkd' #OK generale
+
+ #ProjectPath="C:\\SPIL\\WORK\\SET4TV,02_24"
+ ProjectLibPath=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\Init.cmm')[0])
+ #LibPath="C:\\SPIL\\WORK\\PROGETTO_4TV\\LIB\\SPILSCRIPT"
+ LibPath=os.path.dirname(glob.glob(self.CurrentPrj+'\\Lib\\*\\SPIL.cmm')[0])
+ IssStrtp=['&ProjectLibPath="'+ProjectLibPath+'"\n',\
+ '&LauncherPath="'+self.LauncherPath+'\\SpilTmp\\Result'+'"\n',\
+ '&LibPath="'+LibPath+'"\n',\
+ 'cd &ProjectLibPath\n',\
+ '&StopDebugTime='+Time2Stop+'.\n',\
+ '&CovFlg=0\n',\
+ '&exec="&ProjectLibPath"+"\\t32.cmm"\n',\
+ 'do &exec\n',\
+ 'cd '+ProjectSyncPath+'\n',\
+ '&exec="'+LibPath+'\\SPIL.cmm"\n',\
+ 'do &exec\n']
+ #Definizioni particolari del TD per la costruzione del file MatStrtp.m che lancerà la pre- elaborazione
+ #CalibGenPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_CAL_GEN.mat'
+ CalibGenPathSdt=path+'\\'+TD_DATA[2][0]+'_CAL_GEN.mat'#OK td
+ #TollPath='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\TOLL_SM06_GEN.m' #da script
+ TollPath=path+'\\TOLL_'+TD_DATA[2][0]+'_GEN.m'#OK tc
+
+
+ #ReportPath='M:\\WORK\\REPORTS\\SM06_16143550\\report.txt'
+ #ExtName è l'estensione da aggiungere alla cartella del report per individuare il gg hh mm ss del test
+ ExtName="("+str(time.localtime()[2])+"gg)("+str(time.localtime()[3])+"hh)("+str(time.localtime()[4])+"mm)("+str(time.localtime()[5])+"ss)"
+ ReportPath=self.ReportSpil+'\\'+TD_DATA[2][0]+ExtName+'\\report.txt'
+ #Apro il file di report
+ os.makedirs(os.path.dirname(ReportPath))
+
+ #creo il file di report e appendo i tag dei SETUP
+ if (os.path.exists(path+'\\LibraryList.txt')):
+ f=open(path+'\\LibraryList.txt','r+')
+ LibList=f.readlines()
+ f.close()
+ else:
+ LibList=['\nLibraryList.txt non trovata\n']
+ f=open(os.path.split(self.ProjPath)[0]+"\\load.cmm",'r+')
+ LoadList=f.readlines()
+ f.close()
+ f=open(ReportPath,'w')
+ Setup=['$TEST_SETUP_BEGIN\n']+LibList+LoadList+['\n$TEST_SETUP_END\n\n']
+ f.writelines(Setup)
+ f.close()
+
+ for nTC in range(len(TypeTestListTD)):
+ if TypeTestListTD[nTC]!=0:
+ TCname=TD_DATA[2][nTC+1]
+ indexTC=TCname[string.find(TCname,"_TP_")+4:string.find(TCname,"_TP_")+6]
+
+ #CalibTpPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_CAL_TP_01.mat'
+ CalibTpPathSdt=path+'\\'+TD_DATA[2][0]+'_CAL_TP_'+indexTC+'.mat'#OK tc
+ if not(os.path.exists(CalibTpPathSdt)):
+ CalibTpPathSdt=''
+
+ #InputPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_in.mat'
+ InputPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_in.mat'#OK tc
+
+ #OutputPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_ou.mat'
+ OutputPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_ou.mat'#OK tc
+
+ #TestpointPathSdt='M:\\Work\\4TV_SPIL_00\\MACRO_SFTM\\TD_SM06_4TV\\SM06_TP_02_tx.mat' #da script
+ TestpointPathSdt=path+'\\'+TD_DATA[2][0]+'_TP_'+indexTC+'_tx.mat'#OK tc
+ if not(os.path.exists(TestpointPathSdt)):
+ TestpointPathSdt=''
+
+ #Dove andare a salvare lo zip dei risultati
+ ZipNamePath=os.path.dirname(ReportPath)+'\\'+TD_DATA[2][0]+'_TP'+indexTC+'.zip'
+ #Dove andare a salvare le figure dei risultati
+ FigPath=os.path.dirname(ReportPath)+'\\TP_'+indexTC;
+ #Tipo di Test 1-->OL,2-->CL
+ if TypeTestListTD[nTC]==2:
+ TIPOTEST='CL'
+ else:
+ TIPOTEST='OL'
+ #Creazione della lista che poi sarà stampata
+ MatStrtp=['cd '+SpilMatLib+'\n',\
+ "DD='"+DD+"';\n",\
+ "DataDictPath='"+DataDictPath+"';\n",\
+ "CalibGenPathSdt='"+CalibGenPathSdt+"';\n",\
+ "CalibTpPathSdt='"+CalibTpPathSdt+"';\n",\
+ "CalibPathSpil='"+CalibPathSpil+"';\n",\
+ "InputPathSdt='"+InputPathSdt+"';\n",\
+ "InputPathSpil='"+InputPathSpil+"';\n",\
+ "EventPathSpil='"+EventPathSpil+"';\n",\
+ "EventVectorPath='"+EventVectorPath+"';\n",\
+ "OutputPathSdt='"+OutputPathSdt+"';\n",\
+ "TestpointPathSdt='"+TestpointPathSdt+"';\n",\
+ "OutputNamePathSpil='"+OutputNamePathSpil+"';\n",\
+ "IRPath='"+IRPath+"';\n",\
+ "IRlistCkdPath='"+IRlistCkdPath+"';\n",\
+ "TollPath='"+TollPath+"';\n",\
+ "PowerOnPathSpil='"+PowerOnPathSpil+"';\n",\
+ "ProjectLibPath='"+ProjectLibPath+"';\n",\
+ "DDTime="+DDLastChangeInSec+";\n",\
+ 'fid=fopen(DataDictPath);\n','if fid==-1\n',' DDparser(DD,DataDictPath,DDTime);\n','else\n'," load(DataDictPath,'DDGenTime');\n",' if DDGenTime==DDTime\n'," fprintf('\\nUtilizzo il Data Dictionary già esistente DataDict.mat');\n",' else\n'," fprintf('\\nCreo DataDict.mat nuovo perchè l''esistente non è aggiornato...');\n",' DDparser(DD,DataDictPath,DDTime);\n',' end\n','end\n',\
+ 'SetCalib(CalibGenPathSdt,CalibTpPathSdt,CalibPathSpil,DataDictPath,IRPath,IRlistCkdPath);\n',\
+ 'SetVar(InputPathSdt,InputPathSpil,DataDictPath,EventPathSpil,EventVectorPath,IRlistCkdPath,PowerOnPathSpil,ProjectLibPath);\n',\
+ 'NameOut(OutputNamePathSpil,TollPath,DataDictPath);\n',\
+ 'clear all\n', "fprintf('\\n\\nFINE PRE-ELABORAZIONE');\n", '%quit\n']
+
+
+ MatStop1=['cd '+SpilMatLib+'\n',\
+ "OutputPathSdt='"+OutputPathSdt+"';\n",\
+ "TestpointPathSdt='"+TestpointPathSdt+"';\n",\
+ "DataDictPath='"+DataDictPath+"';\n",\
+ "OutputPathSpil='"+OutputPathSpil+"';\n",\
+ "EventVectorPath='"+EventVectorPath+"';\n",\
+ "TollPath='"+TollPath+"';\n",\
+ "ReportPath='"+ReportPath+"';\n",\
+ "ZipNamePath='"+ZipNamePath+"';\n",\
+ "Dir2Zip='"+Dir2Zip+"';\n",\
+ "FigPath='"+FigPath+"';\n",\
+ "TIPOTEST='"+TIPOTEST+"';\n",\
+ "ResPath='"+self.LauncherPath+"\\result.txt';\n"]
+
+ MatStop2=["LauncherPath='"+self.LauncherPath+"';\n",\
+ "IssPath='"+StartUpIssPath+"';\n",\
+ 'GeneralReport(LauncherPath,IssPath,ReportPath)\n']
+
+ MatStop3=['FilterOut(OutputPathSdt,TestpointPathSdt,OutputPathSpil,DataDictPath,EventVectorPath,TollPath,ReportPath,ZipNamePath,Dir2Zip,FigPath,ResPath,TIPOTEST);\n',\
+ "fprintf('\\n\\nFINE POST-ELABORAZIONE');\n",\
+ '%quit\n']
+ MatStop=MatStop1+MatStop2+MatStop3
+
+ #Se esistente ripulisco la cartella temporanea SpilTmp, altrimento la creo nuova
+ if os.path.exists(self.LauncherPath+'\\SpilTmp\\Result'):
+ for file in glob.glob(self.LauncherPath+'\\SpilTmp\\Result\\*.*'):
+ os.remove(file)
+ else:
+ os.makedirs(self.LauncherPath+'\\SpilTmp\\Result')
+
+ #Se non esistente creo nuova cartella DD
+ if not(os.path.exists(self.LauncherPath+'\\SpilTmp\\DD')):
+ os.makedirs(self.LauncherPath+'\\SpilTmp\\DD')
+
+
+ #Stampa del file MatStrtp.m
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\MatStrtp.m','w+')
+ f.writelines(MatStrtp)
+ f.close()
+ #Stampa del file IssStrtp.m
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\IssStrtp.cmm','w+')
+ f.writelines(IssStrtp)
+ f.close()
+ #Stampa del file MatStop.m
+ os.makedirs(FigPath)
+ f=open(self.LauncherPath+'\\SpilTmp\\Result\\MatStop.m','w+')
+ f.writelines(MatStop)
+ f.close()
+ # Scrivo solo x il primo dei TC selezionati
+
+ break
+
+ except:
+ (ErrorType,ErrorValue,ErrorTB)=sys.exc_info()
+ filelog.write("\n**********\nTD %s FALLITA:\nTIPO:%s\nTRACE:\n"%(TD_DATA[2][0],ErrorValue))
+ traceback.print_exc(None,filelog)
+ filelog.write("**********\n")
+ print ("TD %s FALLITA"%TD_DATA[2][0])
+ self.wnd.launcher.log.SetBackgroundColour(wxRED)
+ #Finita la sessione di test per questo strumento setto a 1 i flag di stopInstr e stopflgSPIL
+ while (os.path.exists('led.flg')):
+ pass
+ f=open('led_status.txt','r+')
+ tmpList=f.readlines()
+ tmpList[0]='LED_STATUS=[[]]\n'
+ tmpList[1]='stopInstr=0\n' #lascio a 0 altrimenti si colora il log di rosso
+ tmpList[4]='stopflgSPIL=1\n'
+ f.seek(0)
+ f.writelines(tmpList)
+ f.close()
+ f=open('led.flg','w+');f.close()
+ filelog.close()
+ #Riporto gli startup originali
+ if (os.path.exists(StartUpIssPath+'\\T32orig.cmm')):
+ shutil.copy(StartUpIssPath+'\\T32orig.cmm',StartUpIssPath+'\\T32.cmm')
+ os.remove(StartUpIssPath+'\\T32orig.cmm')
+ else:
+ if (os.path.exists(StartUpIssPath+'\\T32.cmm')):
+ os.remove(StartUpIssPath+'\\T32.cmm')
+
+
+
+
+
+app=App(0)
+app.MainLoop()
+
diff --git a/pypers/marelli/modulo2/maketable.py b/pypers/marelli/modulo2/maketable.py
new file mode 100755
index 0000000..d4730d1
--- /dev/null
+++ b/pypers/marelli/modulo2/maketable.py
@@ -0,0 +1,60 @@
+# maketable.py
+
+# non graphic
+N = 10
+for i in range(1, N+1):
+ for j in range(1, N+1):
+ print "%4d" % (i*j),
+ print
+
+# HTML
+def maketable(iterable, N):
+ iterable = iter(iterable)
+ print "<table border='1'>"
+ stop = False
+ while not stop:
+ print "<tr>"
+ for j in range(1, N+1):
+ try:
+ print "<td>%s</td>" % iterable.next(),
+ except StopIteration:
+ print "<td></td>"
+ stop = True
+ print "</tr>"
+ print "</table>"
+
+import tempfile, webbrowser, os, sys
+
+def showtable(iterable, N):
+ stdout = sys.stdout
+ fd, name = tempfile.mkstemp(suffix=".html")
+ sys.stdout = os.fdopen(fd, "w")
+ maketable(iterable, N)
+ webbrowser.open(name)
+ sys.stdout = stdout
+
+showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N)
+
+
+
+def get_files_with_ext(ext_set, d):
+ if not isinstance(ext_set, set):
+ ext_set = set([ext_set])
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ if ext.lower() in ext_set:
+ yield os.path.join(cwd, f)
+
+class Picture(object):
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return "<img src=%r width=100>" % self.pathname
+
+if sys.platform == 'win32': drive = "C:\\"
+else: drive = "/"
+showtable(map(Picture, get_files_with_ext(".jpg", drive)), N)
+
+
diff --git a/pypers/marelli/modulo2/mutable_immutable.py b/pypers/marelli/modulo2/mutable_immutable.py
new file mode 100755
index 0000000..8a11466
--- /dev/null
+++ b/pypers/marelli/modulo2/mutable_immutable.py
@@ -0,0 +1,12 @@
+a = 1
+b = a
+
+a += 1
+
+print a, b
+
+a = [1]
+b = a
+a.append(1)
+
+print a, b
diff --git a/pypers/marelli/modulo2/prova.py b/pypers/marelli/modulo2/prova.py
new file mode 100755
index 0000000..ae4b0f6
--- /dev/null
+++ b/pypers/marelli/modulo2/prova.py
@@ -0,0 +1 @@
+while 1:pass
diff --git a/pypers/marelli/modulo2/questionario-in-sol.txt b/pypers/marelli/modulo2/questionario-in-sol.txt
new file mode 100755
index 0000000..983fb8a
--- /dev/null
+++ b/pypers/marelli/modulo2/questionario-in-sol.txt
@@ -0,0 +1,139 @@
+# -*- coding: latin1 -*-
+Soluzioni al questionario
+======================================================================
+
+1. Scrivere un programma che testa se una stringa rappresenta un numero.
+
+try:
+ int(x)
+except ValueError:
+ print "This is not a number!"
+
+from tkSimpleDialog import askfloat
+askfloat("Enter a number", "Number:")
+
+2. Scrivere un programma che lista tutti i files nella directory corrente.
+
+for f in os.listdir("."):
+ print f
+
+3. Come al punto 2, ma il programma deve anche listare tutti i files nelle
+ sottodirectories ricorsivamente.
+
+for cwd, dirs, files in os.walk("."):
+ for f in files:
+ print f
+
+4. Calcolare lo spazio occupato da tutti i files di tipo .txt contenuti in
+ una directory.
+
+import os
+
+def get_text_files(d):
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ if f.lower().endswith(".txt"):
+ fullname = os.path.join(cwd, f)
+ size = os.path.getsize(fullname)
+ yield fullname, size
+
+from operator import itemgetter
+print sum(map(itemgetter(1), get_text_files(".")))
+
+5. Listare i files a seconda delle dimensioni (dal più piccolo al più grande,
+in bytes).
+
+print sorted(get_text_files("."), key=itemgetter(1))
+
+6. Scrivere un test per verificate che una directory sia vuota.
+
+def is_empty(d):
+ if os.listdir(d): return False
+ else: return True
+
+7. Aprire una finestrella contenente la scritta "hello, world!".
+
+# hellotk.pyw
+import Tkinter as t
+root = t.Tk()
+l = t.Label(text="hello")
+l.pack()
+root.mainloop()
+
+# alternativamente
+from tkMessageBox import showinfo
+showinfo(message="hello")
+
+8. Scaricare la pagina Web http://www.example.com da Internet usando Python.
+
+>>> from urllib2 import urlopen
+>>> print urlopen("http://www.example.com").read()
+
+9. Stampare a schermo una tavola delle moltiplicazioni.
+
+ #<maketable.py>
+
+ # non graphic
+ N = 10
+ for i in range(1, N+1):
+ for j in range(1, N+1):
+ print "%4d" % (i*j),
+ print
+
+ # HTML
+ def maketable(iterable, N):
+ iterable = iter(iterable)
+ print "<table border='1'>"
+ stop = False
+ while not stop:
+ print "<tr>"
+ for j in range(1, N+1):
+ try:
+ print "<td>%s</td>" % iterable.next(),
+ except StopIteration:
+ print "<td></td>"
+ stop = True
+ print "</tr>"
+ print "</table>"
+
+ import tempfile, webbrowser, os, sys
+
+ def showtable(iterable, N):
+ stdout = sys.stdout
+ fd, name = tempfile.mkstemp(suffix=".html")
+ sys.stdout = os.fdopen(fd, "w")
+ maketable(iterable, N)
+ webbrowser.open(name)
+ sys.stdout = stdout
+
+ showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N)
+
+ #</maketable.py>
+
+10. Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo
+ in formato ridotto (thumbnails).
+
+ #<maketable.py>
+
+ def get_files_with_ext(ext_set, d):
+ if not isinstance(ext_set, set):
+ ext_set = set([ext_set])
+ for cwd, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(f)
+ if ext.lower() in ext_set:
+ yield os.path.join(cwd, f)
+
+ class Picture(object):
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return "<img src=%r width=100>" % self.pathname
+
+ if sys.platform == 'win32': drive = "C:\\"
+ else: drive = "/"
+ showtable(map(Picture, get_files_with_ext(".jpg", drive)), N)
+
+ #</maketable.py>
+
diff --git a/pypers/marelli/modulo2/questionario-iniziale.txt b/pypers/marelli/modulo2/questionario-iniziale.txt
new file mode 100755
index 0000000..2e545ba
--- /dev/null
+++ b/pypers/marelli/modulo2/questionario-iniziale.txt
@@ -0,0 +1,23 @@
+Domande di conoscenza generale di Python, giusto per avere un'idea della vostra preparazione di base.
+========================================================================================================
+
+1. Scrivere un programma che testa se una stringa rappresenta un numero.
+
+2. Scrivere un programma che lista tutti i files nella directory corrente.
+
+3. Come al punto 2, ma il programma deve anche listare tutti i files nelle sottodirectories ricorsivamente.
+
+4. Calcolare lo spazio occupato da tutti i files di tipo .txt contenuti in una directory.
+
+5. Listare i files a seconda delle dimensioni (dal più piccolo al più grande, in bytes).
+
+6. Scrivere un test per verificate che una directory sia vuota.
+
+7. Aprire una finestrella contenente la scritta "hello, world!".
+
+8. Scaricare la pagina Web http://www.example.com da Internet usando Python.
+
+9. Stampare a schermo una tavola delle moltiplicazioni.
+
+10. Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto (thumbnails).
+
diff --git a/pypers/marelli/modulo2/sort_ci.py b/pypers/marelli/modulo2/sort_ci.py
new file mode 100755
index 0000000..8751ea5
--- /dev/null
+++ b/pypers/marelli/modulo2/sort_ci.py
@@ -0,0 +1,25 @@
+
+from operator import attrgetter
+
+class CI_name(object):
+ def __init__(self, name):
+ self.name = name
+ self.ci_name = name.lower()
+
+def sorted_ci(iterable):
+ ls = map(CI_name, iterable)
+ ls.sort(key=attrgetter("ci_name"))
+ return [el.name for el in ls]
+
+## second implementation:
+
+def sorted_ci(iterable):
+ ls = [(name.lower(), name) for name in iterable]
+ ls.sort()
+ return [el[1] for el in ls]
+
+if __name__ == "__main__": # test
+ ls = "ciao Nina come Va?".split()
+ print sorted(ls)
+ print sorted_ci(ls)
+
diff --git a/pypers/marelli/modulo3/config.py b/pypers/marelli/modulo3/config.py
new file mode 100755
index 0000000..d62928c
--- /dev/null
+++ b/pypers/marelli/modulo3/config.py
@@ -0,0 +1,6 @@
+import sys
+if sys.platform == "win32":
+ root = "C:/"
+else:
+ root = "/mnt/hda2/"
+MUSICDIR = root + "Documents and Settings/micheles/Desktop/Music"
diff --git a/pypers/marelli/modulo3/disaccoppiamento.txt b/pypers/marelli/modulo3/disaccoppiamento.txt
new file mode 100755
index 0000000..d1899d3
--- /dev/null
+++ b/pypers/marelli/modulo3/disaccoppiamento.txt
@@ -0,0 +1 @@
+- vantaggi delle interfaccie di tipo testo
diff --git a/pypers/marelli/modulo3/launcher_with_exec.py b/pypers/marelli/modulo3/launcher_with_exec.py
new file mode 100755
index 0000000..58c383c
--- /dev/null
+++ b/pypers/marelli/modulo3/launcher_with_exec.py
@@ -0,0 +1,5 @@
+script = "script_with_error.py"
+
+#exec file(script).read()
+
+__import__(script[:-3])
diff --git a/pypers/marelli/modulo3/lineinterpreter.py b/pypers/marelli/modulo3/lineinterpreter.py
new file mode 100755
index 0000000..8bc192d
--- /dev/null
+++ b/pypers/marelli/modulo3/lineinterpreter.py
@@ -0,0 +1,55 @@
+## NOTE: THE UNBUFFERED BIT IS ESSENTIAL!
+
+import sys, time
+from twisted.internet import reactor, protocol
+from twisted.protocols import basic
+from ms.concurrency import Popen, PIPE
+
+PORT = 8000
+
+class LineInterpreter(basic.LineReceiver):
+ #from os import sep as delimiter
+
+ def connectionMade(self):
+ self.interpreter = Popen(self.factory.cmd, stdin=PIPE, stdout=PIPE)
+ self.inp = self.interpreter.stdin
+ self.out = self.interpreter.stdout
+ self.transport.write("\r\n%s\r\n" % " ".join(self.factory.cmd))
+ time.sleep(.1)
+ print "".join(self.read_output())
+
+ def connectionLost(self, reason):
+ print >> self.inp, "quit"
+
+ def lineReceived(self, line):
+ print line
+ if line == "quit":
+ self.transport.loseConnection()
+ else:
+ print >> self.inp, line
+ self.transport.write("\r\n%s" % "\r".join(self.read_output()))
+
+ def read_output(self):
+ while True:
+ outline = self.out.readline()
+ yield outline
+ if outline.startswith("(Cmd) "):
+ #yield outline[6:]
+ break
+
+
+if __name__== "__main__":
+ # cmd = sys.argv[1:]
+ cmd = sys.executable, "-u", "tester.py"
+ if cmd:
+ factory = protocol.ServerFactory()
+ factory.protocol = LineInterpreter
+ factory.cmd = cmd
+ reactor.listenTCP(PORT, factory)
+ try:
+ reactor.run()
+ except Exception, e:
+ print e
+ reactor.stop()
+ else:
+ print "usage: %prog line-interpreter-command"
diff --git a/pypers/marelli/modulo3/runsongs.py b/pypers/marelli/modulo3/runsongs.py
new file mode 100755
index 0000000..743bd33
--- /dev/null
+++ b/pypers/marelli/modulo3/runsongs.py
@@ -0,0 +1,31 @@
+import os, random, threading
+from ms.file_utils import ifiles
+from ms.concurrency import Popen, locked
+
+PLAYER = "mplay32 /play /close".split()
+MUSICDIR = os.environ.get("HOMEPATH", os.environ["HOME"]) + "/Desktop/Music"
+
+songs = list(ifiles(MUSICDIR, lambda f : f.endswith(".mp3")))
+
+def run(func, *args, **kw):
+ threading.Thread(None, func, args=args, kwargs=kw).start()
+
+def gen_songs():
+ for i in range(2):
+ yield random.choice(songs)
+
+def play(song):
+ return Popen(PLAYER + [song])
+
+@locked
+def play_many(user):
+ for song in gen_songs():
+ print user, song
+ player = play(song)
+ player.wait()
+
+if __name__ == "__main__":
+ run(play_many, "user1")
+ run(play_many, "user2")
+ run(play_many, "user3")
+
diff --git a/pypers/marelli/modulo3/script_with_error.py b/pypers/marelli/modulo3/script_with_error.py
new file mode 100755
index 0000000..6e2e746
--- /dev/null
+++ b/pypers/marelli/modulo3/script_with_error.py
@@ -0,0 +1 @@
+print 1/0
diff --git a/pypers/marelli/modulo3/tester.py b/pypers/marelli/modulo3/tester.py
new file mode 100755
index 0000000..f99b477
--- /dev/null
+++ b/pypers/marelli/modulo3/tester.py
@@ -0,0 +1,119 @@
+import os, cmd, config
+from ms.file_utils import ifiles
+from ms.concurrency import Popen
+from operator import attrgetter
+import sys, re
+
+def strip_number(song, rx=re.compile(r"\s*\d\d?\. ")):
+ return rx.sub("", os.path.basename(song))
+
+class TestProcess(object):
+ def __init__(self, number, name, popencmd):
+ self.name = name
+ self.number = number
+ self.popencmd = popencmd
+ def start(self):
+ self.proc = Popen(self.popencmd)
+ def stop(self):
+ if self.is_running():
+ self.proc.kill()
+ def is_running(self):
+ return hasattr(self, "proc") and self.proc.is_running()
+
+def popencmd(song):
+ if sys.platform == "win32":
+ return "mplay32", "/play", "/close", song
+ else:
+ return "xterm", "-geometry", "70x14", "-e", "mpg123", song
+
+def str2int(args):
+ for arg in args.split():
+ try:
+ i = int(arg)
+ except ValueError:
+ print "Warning: argument %s is not an integer, ignored" % arg
+ else:
+ yield i
+
+class Tester(cmd.Cmd):
+ use_rawinput = False
+ prompt = ">>" + chr(0)
+ def preloop(self):
+ self.songs = list(ifiles(config.MUSICDIR,
+ lambda f: f.endswith(".mp3")))
+ self.tests = dict(
+ [i, TestProcess(i, strip_number(song), popencmd(song))]
+ for i, song in enumerate(self.songs))
+ self.started_tests = set()
+ self.do_show()
+
+ def running_tests(self):
+ for test in self.started_tests:
+ if test.is_running():
+ yield test
+
+ def run_test(self, testnumber):
+ if testnumber in self.tests:
+ test = self.tests[testnumber]
+ if test.is_running():
+ print "Test #%s is already running!" % test.number
+ else:
+ test.start()
+ self.started_tests.add(test)
+ print "test #%s started" % test.number
+ else:
+ print "There is no test #%s" % testnumber
+
+ def stop_test(self, testnumber):
+ if testnumber in (test.number for test in self.running_tests()):
+ self.tests[testnumber].stop()
+ print "Test #%s stopped." % testnumber
+ else:
+ print "There is no process #%s running" % testnumber
+
+ def do_show(self, arg=""):
+ if arg == "runnable":
+ self.show_runnable()
+ elif arg == "already_run":
+ self.show_already_run()
+ elif arg is "":
+ self.show_already_run()
+ self.show_runnable()
+ else:
+ print "Unknown argument %s" % arg
+
+ def show_runnable(self):
+ print "Runnable tests:"
+ if set(self.tests.itervalues()) != self.started_tests:
+ for test in self.tests.itervalues():
+ if not test in self.started_tests:
+ print test.number, test.name
+ else: # all tests have been started
+ print "None"
+
+ def show_already_run(self):
+ print "Already run tests:"
+ if not self.started_tests:
+ print "None"
+ else:
+ for test in sorted(self.started_tests, key=attrgetter("number")):
+ print test.number, test.name
+
+ def do_run(self, args):
+ run = map(self.run_test, str2int(args))
+ if not run: map(self.run_test, range(len(self.tests))) # run all
+
+ def do_stop(self, args):
+ stop = map(self.stop_test, str2int(args))
+ if not stop: self.postloop() # stop all
+
+ def do_quit(self, arg):
+ print "exiting ..."
+ return True
+
+ def postloop(self):
+ for test in self.running_tests():
+ self.stop_test(test.number)
+
+if __name__ == "__main__":
+ Tester().cmdloop()
diff --git a/pypers/marelli/modulo3/tester_server.py b/pypers/marelli/modulo3/tester_server.py
new file mode 100755
index 0000000..245089f
--- /dev/null
+++ b/pypers/marelli/modulo3/tester_server.py
@@ -0,0 +1,6 @@
+import sys
+from ms.twisted_utils import run
+
+if __name__ == "__main__":
+ print "Listening on localhost 1025 ..."
+ run([sys.executable, "tester.py"], 1025)
diff --git a/pypers/marelli/modulo3/tester_server0.py b/pypers/marelli/modulo3/tester_server0.py
new file mode 100755
index 0000000..ec79542
--- /dev/null
+++ b/pypers/marelli/modulo3/tester_server0.py
@@ -0,0 +1,60 @@
+"""
+Portable twisted server for pythonic command interpreters. Multiple clients
+can connect to it with
+
+$ telnet localhost 1025
+
+The interpreter prompt is expected to end with chr(0).
+"""
+
+import sys
+from twisted.protocols import basic
+from twisted.internet import protocol, reactor
+from subprocess import Popen, PIPE
+
+def read(inp, sep=chr(0), fix_nl=True):
+ """Read data from ``inp`` until a separator ``sep`` is found. If
+ ``fix_nl`` is True, it converts newlines in \r\n, the standard for
+ Internet line protocols."""
+ out = []
+ while True:
+ char = inp.read(1)
+ if char == sep:
+ break
+ elif char == "\n" and fix_nl:
+ out.append("\r\n")
+ else:
+ out.append(char)
+ return "".join(out) + " "
+
+# Poor man using Popen, not ProcessProtocol as in ptyserv
+class InterpreterProtocol(basic.LineReceiver):
+ def connectionMade(self):
+ print "Got new client!"
+ self.cli = Popen([sys.executable, "-u", self.factory.cmd_interpreter],
+ stdin=PIPE, stdout=PIPE)
+ self.inp = self.cli.stdin
+ self.out = self.cli.stdout
+ self.transport.write(read(self.out))
+
+ def connectionLost(self, reason):
+ print "Lost a client!"
+ print >> self.inp, "quit"
+
+ def lineReceived(self, line):
+ print "received", repr(line)
+ if line == "quit":
+ self.transport.loseConnection()
+ else:
+ print >> self.inp, line
+ self.transport.write(read(self.out))
+
+def run(py_cmd_interpr, port=1025):
+ factory = protocol.ServerFactory()
+ factory.protocol = InterpreterProtocol
+ factory.cmd_interpreter = py_cmd_interpr
+ reactor.listenTCP(port, factory)
+ reactor.run()
+
+if __name__ == "__main__":
+ run("tester.py", 1025)
diff --git a/pypers/marelli/modulo4/_main.py b/pypers/marelli/modulo4/_main.py
new file mode 100755
index 0000000..7d252b1
--- /dev/null
+++ b/pypers/marelli/modulo4/_main.py
@@ -0,0 +1,9 @@
+# _main.py
+
+class C(object):
+ pass
+
+def f(x):
+ return x
+
+
diff --git a/pypers/marelli/modulo4/code2utest.py b/pypers/marelli/modulo4/code2utest.py
new file mode 100755
index 0000000..32e7335
--- /dev/null
+++ b/pypers/marelli/modulo4/code2utest.py
@@ -0,0 +1,32 @@
+"""
+Convert tests from modules into test cases.
+"""
+
+import sys, unittest, time
+from ms.misc_utils import import_
+
+def makeSuite(name, code):
+ dic = {}
+ exec compile(code, name, 'exec') in dic
+ tests = []
+ for name, test in dic.iteritems():
+ if name.startswith("test"):
+ tests.append([name, test])
+ TC = type(name, (unittest.TestCase,), dict(tests))
+ return unittest.makeSuite(TC)
+
+class Printer(object):
+ def write(self, text):
+ sys.stdout.write(text)
+
+def run(modulenames, stream=Printer(), descriptions=1, verbosity=1):
+ runner = unittest.TextTestRunner(stream, descriptions, verbosity)
+ suites = [makeSuite(name, code) for name, code in modulenames]
+ return runner.run(unittest.TestSuite(suites))
+
+def fnames2list(fnames):
+ return [(fname[:-3], file(fname).read()) for fname in fnames]
+
+if __name__ == "__main__": # example
+ print run(fnames2list('utest_1.py utest_2.py'.split()))
+
diff --git a/pypers/marelli/modulo4/ex1.txt b/pypers/marelli/modulo4/ex1.txt
new file mode 100755
index 0000000..dec7913
--- /dev/null
+++ b/pypers/marelli/modulo4/ex1.txt
@@ -0,0 +1,15 @@
+ #<_main.py>
+
+ class C(object):
+ pass
+
+ def f(x):
+ return x
+
+ #</_main.py>
+
+>>> f(1) == 1
+True
+
+>>> C
+<class '_main.C'>
diff --git a/pypers/marelli/modulo4/ex2.txt b/pypers/marelli/modulo4/ex2.txt
new file mode 100755
index 0000000..9e003a9
--- /dev/null
+++ b/pypers/marelli/modulo4/ex2.txt
@@ -0,0 +1,5 @@
+>>> class C(object): pass
+...
+
+>>> C
+<class '__main__.C'>
diff --git a/pypers/marelli/modulo4/iter2thread.py b/pypers/marelli/modulo4/iter2thread.py
new file mode 100755
index 0000000..0a7c1ed
--- /dev/null
+++ b/pypers/marelli/modulo4/iter2thread.py
@@ -0,0 +1,15 @@
+import time
+from ms.concurrency import gen2thread
+
+@gen2thread
+def genNumbers(self, N):
+ for i in range(N):
+ time.sleep(.2)
+ print i
+ yield None
+
+
+t = genNumbers(10)
+t.start()
+time.sleep(1)
+t.stop()
diff --git a/pypers/marelli/modulo4/multi_iter.py b/pypers/marelli/modulo4/multi_iter.py
new file mode 100755
index 0000000..d3a5ac2
--- /dev/null
+++ b/pypers/marelli/modulo4/multi_iter.py
@@ -0,0 +1,22 @@
+from ms.file_utils import ifiles
+import os
+
+def gen(fname):
+ for line in file(fname):
+ yield line
+
+def multi_iter(iters):
+ iters = list(iters) # to avoid side effects
+ while iters:
+ for it in iters:
+ try:
+ yield it.next()
+ except StopIteration:
+ iters.remove(it)
+
+ci =multi_iter(gen(f) for f in
+ ifiles("/home/micheles/md/python/Timing",
+ lambda f: f.endswith(".txt")))
+
+for line in ci:
+ print line,
diff --git a/pypers/marelli/modulo4/remote_tester_client.py b/pypers/marelli/modulo4/remote_tester_client.py
new file mode 100755
index 0000000..b991019
--- /dev/null
+++ b/pypers/marelli/modulo4/remote_tester_client.py
@@ -0,0 +1,46 @@
+"""Read code from a file, send it to the server and display the result.
+
+usage: %prog testfile [options]
+
+"""
+
+import os, sys
+
+from twisted.protocols.basic import LineReceiver
+from twisted.internet import reactor, protocol
+
+from remote_tester_server import PORT
+
+class ClientFactory(protocol.ClientFactory):
+ class protocol(LineReceiver):
+ def connectionMade(self):
+ print "Sending data to server ..."
+ send(self.factory.name_code, self.transport)
+ def connectionLost(self, arg):
+ print "Connection lost!", arg
+ def lineReceived(self, line):
+ print line
+ if line == "bye":
+ pass
+ #self.transport.loseConnection("regular exit")
+ #reactor.stop()
+ def __init__(self, name, lines):
+ self.name_code = name, lines
+
+def send((name, rfile), transport):
+ transport.write("BEGIN %s\r\n" % name)
+ for line in rfile:
+ transport.write(line.rstrip() + "\r\n")
+ transport.write("END\r\n")
+
+def send_and_wait(fname, lines):
+ reactor.connectTCP("localhost", PORT, ClientFactory(fname, lines))
+ reactor.run()
+
+if __name__ == "__main__":
+ try:
+ testfile = sys.argv[1]
+ except IndexError:
+ print __doc__
+ else:
+ send_and_wait(os.path.basename(testfile), file(testfile, "U"))
diff --git a/pypers/marelli/modulo4/remote_tester_server.py b/pypers/marelli/modulo4/remote_tester_server.py
new file mode 100755
index 0000000..c03edfb
--- /dev/null
+++ b/pypers/marelli/modulo4/remote_tester_server.py
@@ -0,0 +1,59 @@
+"""Get test code from the client, run it and return the result."""
+
+import sys, unittest, time, threading
+
+from twisted.protocols.basic import LineReceiver
+from twisted.internet import reactor, protocol
+from twisted.internet.threads import deferToThread
+
+from ms.misc_utils import import_
+
+PORT = 1025
+
+class Wrapper(object): # it seems buffered :-(
+ def __init__(self, obj):
+ self._obj = obj
+ def write(self, text):
+ self._obj.write(text.replace("\n", "\r\n"))
+ #sys.stdout.write(text)
+
+# NOTE: different protocols for different clients, but same factory
+# transport is an instance of twisted.internet.tcp.Server, which a
+# subclass of Connection
+class ServerFactory(protocol.ServerFactory):
+ class protocol(LineReceiver):
+ def connectionMade(self):
+ print "Test case got from client %s" % self
+ def connectionLost(self, reason):
+ print "Connection lost!"
+ def lineReceived(self, line):
+ print line # good for debugging
+ if line.startswith("BEGIN"): # open test file
+ fname = "server_" + line.split()[1]
+ self.name = fname[:-3]
+ self.f = file(fname, "w")
+ elif line == "END": # close test file, run tests
+ self.f.close()
+ de = run([self.name], Wrapper(self.transport))
+ de.addErrback(sys.stdout.write)
+ de.addCallback(lambda _: self.transport.write("bye\r\n"))
+ else: # build test file
+ print >> self.f, line # possibile race or not?
+
+def makeSuite(module):
+ tests = []
+ for name, test in module.__dict__.iteritems():
+ if name.startswith("test"):
+ tests.append([name, test])
+ TC = type(module.__name__, (unittest.TestCase,), dict(tests))
+ return unittest.makeSuite(TC)
+
+@deferToThread.__get__ # make asynchronous
+def run(modulenames, stream=sys.stdout, descriptions=1, verbosity=1):
+ runner = unittest.TextTestRunner(stream, descriptions, verbosity)
+ suites = [makeSuite(import_(module)) for module in modulenames]
+ return runner.run(unittest.TestSuite(suites))
+
+if __name__ == "__main__":
+ reactor.listenTCP(PORT, ServerFactory())
+ reactor.run()
diff --git a/pypers/marelli/modulo4/server_utest_1.py b/pypers/marelli/modulo4/server_utest_1.py
new file mode 100755
index 0000000..5c354b8
--- /dev/null
+++ b/pypers/marelli/modulo4/server_utest_1.py
@@ -0,0 +1,6 @@
+
+def test_1(self):
+ self.assertEqual(1, 1)
+
+def test_2(self):
+ self.assertEqual(1, 2)
diff --git a/pypers/marelli/modulo4/server_utest_2.py b/pypers/marelli/modulo4/server_utest_2.py
new file mode 100755
index 0000000..e25fc94
--- /dev/null
+++ b/pypers/marelli/modulo4/server_utest_2.py
@@ -0,0 +1,8 @@
+import time
+
+def test_1(self):
+ self.assertEqual(1, 1)
+
+def test_2(self):
+ time.sleep(1)
+ self.assertEqual(1, 2)
diff --git a/pypers/marelli/modulo4/test2utest.py b/pypers/marelli/modulo4/test2utest.py
new file mode 100755
index 0000000..458bf32
--- /dev/null
+++ b/pypers/marelli/modulo4/test2utest.py
@@ -0,0 +1,27 @@
+"""
+Convert tests from modules into test cases.
+"""
+
+import sys, unittest, time
+from ms.misc_utils import import_
+
+def makeSuite(module):
+ tests = []
+ for name, test in module.__dict__.iteritems():
+ if name.startswith("test"):
+ tests.append([name, test])
+ TC = type(module.__name__, (unittest.TestCase,), dict(tests))
+ return unittest.makeSuite(TC)
+
+class Printer(object):
+ def write(self, text):
+ sys.stdout.write(text)
+
+def run(modulenames, stream=Printer(), descriptions=1, verbosity=1):
+ runner = unittest.TextTestRunner(stream, descriptions, verbosity)
+ suites = [makeSuite(import_(module)) for module in modulenames]
+ return runner.run(unittest.TestSuite(suites))
+
+if __name__ == "__main__": # example
+ print run(['utest_1', 'utest_2'])
+
diff --git a/pypers/marelli/modulo4/test_1.py b/pypers/marelli/modulo4/test_1.py
new file mode 100755
index 0000000..3a0b1c8
--- /dev/null
+++ b/pypers/marelli/modulo4/test_1.py
@@ -0,0 +1,5 @@
+def test1(a=42, b=43):
+ assert a==b
+
+def test2(a=42, b=42):
+ assert a==b
diff --git a/pypers/marelli/modulo4/test_2.py b/pypers/marelli/modulo4/test_2.py
new file mode 100755
index 0000000..52bf590
--- /dev/null
+++ b/pypers/marelli/modulo4/test_2.py
@@ -0,0 +1,10 @@
+"""
+Generative tests: generators returning check functions and their parameters
+"""
+def isequal(x, y):
+ assert x == y
+
+def test_gen():
+ for i, j in (1, 1), (2, 3), (4, 5):
+ yield isequal, i, j
+
diff --git a/pypers/marelli/modulo4/test_3.py b/pypers/marelli/modulo4/test_3.py
new file mode 100755
index 0000000..c376083
--- /dev/null
+++ b/pypers/marelli/modulo4/test_3.py
@@ -0,0 +1,12 @@
+from tkMessageBox import showinfo
+
+class Test1(object):
+ def setup_class(cls):
+ pass
+ #showinfo("setup", "setup")
+ def teardown_class(cls):
+ print "teardown"
+ def test_m1(self):
+ assert 1==0
+ def test_m2(self):
+ assert 1==0
diff --git a/pypers/marelli/modulo4/test_parent_child/child.py b/pypers/marelli/modulo4/test_parent_child/child.py
new file mode 100755
index 0000000..098ed4b
--- /dev/null
+++ b/pypers/marelli/modulo4/test_parent_child/child.py
@@ -0,0 +1,4 @@
+import time
+for i in range(10):
+ print "%s I am the child!" % i
+ time.sleep(1)
diff --git a/pypers/marelli/modulo4/test_parent_child/parent.py b/pypers/marelli/modulo4/test_parent_child/parent.py
new file mode 100755
index 0000000..0dcc172
--- /dev/null
+++ b/pypers/marelli/modulo4/test_parent_child/parent.py
@@ -0,0 +1,9 @@
+from subprocess import Popen
+from time import sleep
+from os import getpid
+
+print >> file("parent.pid", "w"), getpid()
+child = Popen(["python", "child.py"])
+print "Parent id: %s" % getpid()
+print "Child id: %s" % child.pid
+sleep(60)
diff --git a/pypers/marelli/modulo4/test_parent_child/test_kill_parent.sh b/pypers/marelli/modulo4/test_parent_child/test_kill_parent.sh
new file mode 100755
index 0000000..4d026a4
--- /dev/null
+++ b/pypers/marelli/modulo4/test_parent_child/test_kill_parent.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# test that killing the parent does NOT kill the child
+python parent.py&
+sleep 3
+kill `cat parent.pid`
+sleep 1
+ps aux | grep python
diff --git a/pypers/marelli/modulo4/threads.txt b/pypers/marelli/modulo4/threads.txt
new file mode 100755
index 0000000..a70350b
--- /dev/null
+++ b/pypers/marelli/modulo4/threads.txt
@@ -0,0 +1,22 @@
+Programming with threads
+=====================================
+
+Issues with threads
+---------------------------------
+
+1. You cannot kill a thread.
+2. Exceptions are harder.
+3. There is always the risk of race conditions and deadlocks.
+4. Threads are not scalable.
+
+Partial solution:
+
+- use Twisted threads.
+
+Use case for threads
+--------------------------------------
+
+1. Exchanging info is easier.
+2. Applications with a mainloop (GUI, WEB)
+3. Threads as observers.
+4. Simulations (ex. simulating many users) \ No newline at end of file
diff --git a/pypers/marelli/modulo4/threads_ex.py b/pypers/marelli/modulo4/threads_ex.py
new file mode 100755
index 0000000..eb2f3dd
--- /dev/null
+++ b/pypers/marelli/modulo4/threads_ex.py
@@ -0,0 +1,26 @@
+from ms.concurrency import ThreadFactory
+import time
+
+@ThreadFactory
+def do_nothing(thread, err=False):
+ if err: raise RuntimeError()
+ for i in range(10):
+ if not thread.running: break
+ time.sleep(.5)
+ print thread.getName()
+ return i
+
+d1 = do_nothing(err=True)
+print do_nothing
+d2 = do_nothing()
+
+d1.start()
+d2.start()
+print d1.getResult()
+d1.finish()
+time.sleep(1)
+d2.finish()
+print d1.isAlive()
+print d2.isAlive()
+print d1.getResult()
+print d2.getResult()
diff --git a/pypers/marelli/modulo4/threads_twisted.py b/pypers/marelli/modulo4/threads_twisted.py
new file mode 100755
index 0000000..8bdefb3
--- /dev/null
+++ b/pypers/marelli/modulo4/threads_twisted.py
@@ -0,0 +1,36 @@
+from twisted.internet.threads import deferToThread
+from twisted.internet import reactor
+from twisted.python import threadable; threadable.init(True)
+import time
+
+def print_(x):
+ print x
+
+@deferToThread.__get__
+def do_nothing(err=False):
+ if err: raise RuntimeError()
+ for i in range(10):
+ #if not thread.running: break
+ time.sleep(.5)
+ return i
+
+d1 = do_nothing()
+d1.addBoth(print_)
+d2 = do_nothing(err=False)
+d2.addBoth(print_)
+
+## print do_nothing
+## d2 = do_nothing()
+
+## d1.start()
+## d2.start()
+## print d1.getResult()
+## d1.finish()
+## time.sleep(1)
+## d2.finish()
+## print d1.isAlive()
+## print d2.isAlive()
+## print d1.getResult()
+## print d2.getResult()
+
+reactor.run()
diff --git a/pypers/marelli/modulo4/utest_1.py b/pypers/marelli/modulo4/utest_1.py
new file mode 100755
index 0000000..5c354b8
--- /dev/null
+++ b/pypers/marelli/modulo4/utest_1.py
@@ -0,0 +1,6 @@
+
+def test_1(self):
+ self.assertEqual(1, 1)
+
+def test_2(self):
+ self.assertEqual(1, 2)
diff --git a/pypers/marelli/modulo4/utest_2.py b/pypers/marelli/modulo4/utest_2.py
new file mode 100755
index 0000000..e25fc94
--- /dev/null
+++ b/pypers/marelli/modulo4/utest_2.py
@@ -0,0 +1,8 @@
+import time
+
+def test_1(self):
+ self.assertEqual(1, 1)
+
+def test_2(self):
+ time.sleep(1)
+ self.assertEqual(1, 2)
diff --git a/pypers/marelli/modulo4/x.py b/pypers/marelli/modulo4/x.py
new file mode 100755
index 0000000..973fab9
--- /dev/null
+++ b/pypers/marelli/modulo4/x.py
@@ -0,0 +1,3 @@
+print __file__
+while 1:
+ pass
diff --git a/pypers/marelli/modulo5/fix-server.py b/pypers/marelli/modulo5/fix-server.py
new file mode 100755
index 0000000..6749058
--- /dev/null
+++ b/pypers/marelli/modulo5/fix-server.py
@@ -0,0 +1,48 @@
+
+############ FIX A MISSING FEATURE IN HTTPServer ################
+
+from BaseHTTPServer import HTTPServer
+
+# The only exception that can propagate here is SystemExit
+def handle_exit(self, request, client_address):
+ raise SystemExit("Server is shutting down")
+
+HTTPServer.handle_error = handle_exit
+
+############ FIX A MISSING FEATURE IN Publisher ################
+
+import time
+from quixote.publish import PublishError
+
+def process_request(self, request):
+ """(request : HTTPRequest) -> HTTPResponse
+
+ Process a single request, given an HTTPRequest object. The
+ try_publish() method will be called to do the work and
+ exceptions will be handled here.
+ """
+ self._set_request(request)
+ start_time = time.time()
+ try:
+ self.parse_request(request)
+ output = self.try_publish(request)
+ except PublishError, exc:
+ # Exit the publishing loop and return a result right away.
+ output = self.finish_interrupted_request(exc)
+ except SystemExit: # <==== THIS IS
+ raise # <==== THE FIX
+ except:
+ # Some other exception, generate error messages to the logs, etc.
+ output = self.finish_failed_request()
+ output = self.filter_output(request, output)
+ self.logger.log_request(request, start_time)
+ if output:
+ if self.config.compress_pages and request.get_encoding(["gzip"]):
+ compress = True
+ else:
+ compress = False
+ request.response.set_body(output, compress)
+ self._clear_request()
+ return request.response
+
+Publisher.process_request = process_request
diff --git a/pypers/marelli/modulo5/protected.py b/pypers/marelli/modulo5/protected.py
new file mode 100755
index 0000000..0398ad5
--- /dev/null
+++ b/pypers/marelli/modulo5/protected.py
@@ -0,0 +1,30 @@
+"""An example of protected methods."""
+
+class B(object):
+ def __init__(self):
+ self.hello()
+ def hello(self):
+ print "hello!"
+
+class C(B):
+ def hello(self):
+ print "cucu!"
+
+b = B()
+c = C()
+
+##
+
+class B(object):
+ def __init__(self): # guaranteed to call the 'hello' method in THIS class
+ self.__hello()
+ def __hello(self):
+ print "hello!"
+
+class C(B):
+ def __hello(self): # won't be called by B.__init__
+ print "cucu!"
+
+b = B()
+c = C()
+
diff --git a/pypers/marelli/modulo5/protected2.py b/pypers/marelli/modulo5/protected2.py
new file mode 100755
index 0000000..fbbb1f3
--- /dev/null
+++ b/pypers/marelli/modulo5/protected2.py
@@ -0,0 +1,11 @@
+class B(object):
+ def __p(self):
+ print "hello!"
+ def __init__(self):
+ self.__p()
+
+class C(B):
+ def __init__(self):
+ self.__p()
+
+c = C()
diff --git a/pypers/marelli/modulo5/threads_vs_gen.py b/pypers/marelli/modulo5/threads_vs_gen.py
new file mode 100755
index 0000000..f1a0a3d
--- /dev/null
+++ b/pypers/marelli/modulo5/threads_vs_gen.py
@@ -0,0 +1,37 @@
+import threading
+from ms.file_utils import ifiles
+
+def count(it):
+ n = 0
+ for line in it:
+ n += len(line)
+ return n
+
+#print count(ifiles("/home/micheles", lambda f: f.endswith(".txt"))) # 4610
+
+def synchronous():
+ for f in ifiles("/home/micheles", lambda f: f.endswith(".txt")):
+ #print f, count(file(f))
+ count(file(f))
+
+# python -mtimeit -n 1 -s "from threads_vs_gen import synchronous" "synchronous()"
+def threaded():
+ for f in ifiles("/home/micheles", lambda f: f.endswith(".txt")):
+ threading.Thread(None, count, args=(file(f),)).start()
+
+
+
+# python -mtimeit -n 1 -s "from threads_vs_gen import threaded" "threaded()"
+
+def count_gen(f):
+ n = 0
+ for _ in file(f):
+ n += 1
+ yield n
+
+def asynchronous():
+ iters = map(count_gen, ifiles("/home/micheles",
+ lambda f: f.endswith(".txt")))
+ for it in iters: # do not iterate on exhausted iterators
+ it.next()
+
diff --git a/pypers/marelli/programma-svolto.html b/pypers/marelli/programma-svolto.html
new file mode 100755
index 0000000..104798c
--- /dev/null
+++ b/pypers/marelli/programma-svolto.html
@@ -0,0 +1,409 @@
+<?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>Corso Python Magneti Marelli</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="corso-python-magneti-marelli">
+<h1 class="title">Corso Python Magneti Marelli</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Data:</th><td class="field-body">19-23 Settembre</td>
+</tr>
+<tr class="field"><th class="docinfo-name">Docente:</th><td class="field-body">Michele Simionato</td>
+</tr>
+</tbody>
+</table>
+<div class="section" id="programma">
+<h1><a name="programma">Programma</a></h1>
+<p>Si tratta di un corso di 20 ore suddiviso in 5 moduli di 4 ore ciascuno.</p>
+<div class="section" id="modulo-1-programmazione-di-base-in-python">
+<h2><a name="modulo-1-programmazione-di-base-in-python">Modulo 1: Programmazione di base in Python.</a></h2>
+<p><em>In questo modulo si ripasseranno molto brevemente le basi di Python e
+si discuteranno le soluzioni al questionario di ammissione. Lo scopo
+piu' che altro e' quello di conoscersi e di chiarire il livello medio
+dei partecipanti e coprire eventuali buchi nella preparazione di base.</em></p>
+<ul class="simple">
+<li>correzione esercizi ... OK</li>
+<li>gestione processi ... OK</li>
+<li>iteratori e generatori ... *</li>
+<li>differenza mutabili/immutabili ... *</li>
+<li>python -m &lt;nome module&gt; ... OK</li>
+<li>set ... OK</li>
+</ul>
+</div>
+<div class="section" id="modulo-2-strumenti-di-sviluppo-debugging-e-introspezione">
+<h2><a name="modulo-2-strumenti-di-sviluppo-debugging-e-introspezione">Modulo 2: Strumenti di sviluppo, debugging e introspezione.</a></h2>
+<p><a href="#id1" name="id2"><span class="problematic" id="id2">*</span></a>In questo modulo discutero' gli strumenti da me utilizzati per sviluppare
+in Python sotto Windows. Parlero' di Cygwin come ambiente di lavoro,
+di Python e di IPython come interpreti interattivi, di Idle e di PythonWin
+come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discutero'
+alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot,
+etc.).</p>
+<div class="system-message" id="id1">
+<p class="system-message-title">System Message: <a name="id1">WARNING/2</a> (<tt class="docutils">programma-svolto.txt</tt>, line 31); <em><a href="#id2">backlink</a></em></p>
+Inline emphasis start-string without end-string.</div>
+<ul class="simple">
+<li>cygwin ... OK</li>
+<li>Idle ... OK</li>
+<li>PythonWin ... OK</li>
+<li>WingIDE, Eric/Qt Designer, Komodo, Boa Constructor ...</li>
+<li>Emacs/Vi ... OK</li>
+<li>help in linea ... OK</li>
+<li>pydoc ... OK</li>
+<li>ActiveState help ... OK</li>
+<li>ipython ... *</li>
+<li>matplotlib ... *</li>
+<li>numarray ... *</li>
+</ul>
+</div>
+<div class="section" id="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni">
+<h2><a name="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni">Modulo 3. Tipici errori di programmazione e gestione delle eccezioni.</a></h2>
+<p><em>Si discuteranno buoni e cattivi esempi di programmazione presi da software
+reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche
+per interpretare i tracebacks di Python e per identificare l'origine dei
+problemi.</em></p>
+<ul class="simple">
+<li>uso di 'assert' ... OK</li>
+<li>come scrivere le proprie classi di eccezioni ... OK</li>
+<li>evitare blocchi try grossi ... OK</li>
+<li>possibilita' di multiple except clause ... OK</li>
+<li>evitare except nudi .. OK</li>
+<li>evitare eccezioni di tipo stringa ... OK</li>
+<li>problemi delle eccezioni con i threads ... OK</li>
+<li>segreti del try/finally ... OK</li>
+<li>problemi con exec ... OK</li>
+<li>__import__ e execfile ... OK</li>
+<li>analisi del Launcher ... OK</li>
+</ul>
+</div>
+<div class="section" id="modulo-4-sviluppo-orientato-ai-test">
+<h2><a name="modulo-4-sviluppo-orientato-ai-test">Modulo 4. Sviluppo orientato ai test</a></h2>
+<p><em>Come scrivere software con tecnologie agili, con lo scopo di ridurre e
+tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest.
+Esempi di programmazione test driven.</em></p>
+<ul class="simple">
+<li>doctest ... OK</li>
+<li>unittest ... *</li>
+</ul>
+</div>
+<div class="section" id="modulo-5-design-documentazione-e-manutenzione-di-librarie">
+<h2><a name="modulo-5-design-documentazione-e-manutenzione-di-librarie">Modulo 5: Design, documentazione e manutenzione di librarie</a></h2>
+<p><em>Pratiche di programmazione &quot;in the large&quot;. Moduli, packages, strumenti di
+documentazione e di installazione. Applicazioni pratiche di principi generali
+quali disaccoppiamento, modularità, non duplicazione del codice.</em></p>
+<ul class="simple">
+<li>README.txt, HISTORY.txt, ... OK</li>
+<li>zipimport ... OK</li>
+<li>moduli, paths ...</li>
+<li>packages, .pth ...</li>
+<li>docutils ... OK</li>
+<li>distutils ...</li>
+<li>come fissare moduli di terze parti ... *</li>
+<li>evitare l'ereditarieta' multipla ... *</li>
+<li>interfacce grafiche/testuali ... *</li>
+<li>variabili dummies &quot;_&quot; ... OK</li>
+<li>a volte la docstring puo' rimpiazzare il commento ... OK</li>
+</ul>
+</div>
+<div class="section" id="modulo-6-domande-estemporanee">
+<h2><a name="modulo-6-domande-estemporanee">Modulo 6: Domande estemporanee</a></h2>
+<p><em>Rispondero' alle domande dell'audience, anche al di fuori dal programma,
+se di interesse generale</em>.</p>
+<ul class="simple">
+<li>come faccio a trovare le informazioni ... OK
++ newsgroups!
++ Python Cookbook
++ Libri</li>
+<li>dove posso trovare esempi di buon codice Python ... OK</li>
+<li>come funziona 'del' ... OK</li>
+<li>come funziona 'import', 'reload' ... OK</li>
+<li>comunicazione tra processi ... *</li>
+</ul>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/marelli/programma-svolto.txt b/pypers/marelli/programma-svolto.txt
new file mode 100755
index 0000000..f00bd87
--- /dev/null
+++ b/pypers/marelli/programma-svolto.txt
@@ -0,0 +1,141 @@
+Corso Python Magneti Marelli
+======================================================
+
+:Data: 19-23 Settembre
+:Docente: Michele Simionato
+
+
+Programma
+---------------------------------------
+
+Si tratta di un corso di 20 ore suddiviso in 5 moduli di 4 ore ciascuno.
+
+Modulo 1: Programmazione di base in Python.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*In questo modulo si ripasseranno molto brevemente le basi di Python e
+si discuteranno le soluzioni al questionario di ammissione. Lo scopo
+piu' che altro e' quello di conoscersi e di chiarire il livello medio
+dei partecipanti e coprire eventuali buchi nella preparazione di base.*
+
+- correzione esercizi ... OK
+- gestione processi ... OK
+- iteratori e generatori ... OK
+- differenza mutabili/immutabili ... OK
+- python -m <nome module> ... OK
+- set ... OK
+
+Modulo 2: Strumenti di sviluppo, debugging e introspezione.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*In questo modulo discutero' gli strumenti da me utilizzati per sviluppare
+in Python sotto Windows. Parlero' di Cygwin come ambiente di lavoro,
+di Python e di IPython come interpreti interattivi, di Idle e di PythonWin
+come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discutero'
+alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot,
+etc.).*
+
+- cygwin ... OK
+- Idle ... OK
+- PythonWin ... OK
+- WingIDE, Eric/Qt Designer, Komodo, Boa Constructor ... OK
+- Emacs/Vi ... OK
+- help in linea ... OK
+- pydoc ... OK
+- ActiveState help ... OK
+- ipython ... OK
+- matplotlib ... OK
+
+Modulo 3. Tipici errori di programmazione e gestione delle eccezioni.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Si discuteranno buoni e cattivi esempi di programmazione presi da software
+reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche
+per interpretare i tracebacks di Python e per identificare l'origine dei
+problemi.*
+
+- uso di 'assert' ... OK
+- come scrivere le proprie classi di eccezioni ... OK
+- evitare blocchi try grossi ... OK
+- possibilita' di multiple except clause ... OK
+- evitare except nudi .. OK
+- evitare eccezioni di tipo stringa ... OK
+- problemi delle eccezioni con i threads ... OK
+- segreti del try/finally ... OK
+- problemi con exec ... OK
+- __import__ e execfile ... OK
+- analisi del Launcher ... OK
+
+Modulo 4. Sviluppo orientato ai test
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Come scrivere software con tecnologie agili, con lo scopo di ridurre e
+tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest.
+Esempi di programmazione test driven.*
+
+- unittest
+ + setUp/tearDown ... OK
+ + expected exceptions ... OK
+
+- doctest
+ + doctest in un file separato ... OK
+ + doctest convertiti in unittest ... OK
+ + expected exceptions ... OK
+
+
+Modulo 5: Design, documentazione e manutenzione di librarie
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Pratiche di programmazione "in the large". Moduli, packages, strumenti di
+documentazione e di installazione. Applicazioni pratiche di principi generali
+quali disaccoppiamento, modularità, non duplicazione del codice.*
+
+- README.txt, HISTORY.txt, ... OK
+- zipimport ... OK
+- docutils ... OK
+- distutils ...
+- variabili dummies "_" ... OK
+- a volte la docstring puo' rimpiazzare il commento ... OK
+
+- disaccoppiamento (divide et impera)
+ + problemi degli ambienti integrati ... OK
+ + rimuovere o cambiare componenti ... OK
+ + core + estensioni (interfacce grafiche/testuali) ... OK
+ + threads vs. process ... OK
+ + evitare l'ereditarieta' multipla (singola) ... OK
+
+- modularita' (divide et impera)
+ + moduli, paths ... OK
+ + packages, .pth ... OK
+ + divisione delle responsabilita' ... OK
+
+- non-duplicazione del codice
+ + come fissare moduli di terze parti ... OK
+ + scriversi funzioni di utilita' riutilizzabili ... OK
+
+- KISS (keep it simple, stupid!) ... OK
+ (non facciamoci del male)
+
+- farsi un prototipo ... OK
+ + ?? do I need it ?? (eliminare l'inessenziale)
+ + il prototipo vi puo' diventare il core
+
+- "andare avanti a lavoro finito" ... OK
+ + scrivi una riga e la testi SUBITO!
+
+Modulo 6: Domande estemporanee
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Rispondero' alle domande dell'audience, anche al di fuori dal programma,
+se di interesse generale*.
+
+- come faccio a trovare le informazioni ... OK
+ + newsgroups!
+ + Python Cookbook
+ + Libri
+
+- dove posso trovare esempi di buon codice Python ... OK
+- come funziona 'del' ... OK
+- come funziona 'import', 'reload' ... OK
+- comunicazione tra processi ... OK
+- come funzionano le variabili "__" ... OK
diff --git a/pypers/marelli/questionario-fin.txt b/pypers/marelli/questionario-fin.txt
new file mode 100755
index 0000000..ef4fbb1
--- /dev/null
+++ b/pypers/marelli/questionario-fin.txt
@@ -0,0 +1,6 @@
+Questionario finale
+------------------------------------
+
+- ordinare in maniera case-insensitive;
+- print a nested list with indentation;
+- define chop;
diff --git a/pypers/marelli/questionario-iniziale.html b/pypers/marelli/questionario-iniziale.html
new file mode 100755
index 0000000..eef63aa
--- /dev/null
+++ b/pypers/marelli/questionario-iniziale.html
@@ -0,0 +1,303 @@
+<?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>Domande di conoscenza generale di Python, giusto per avere un'idea della vostra preparazione di base.</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="domande-di-conoscenza-generale-di-python-giusto-per-avere-un-idea-della-vostra-preparazione-di-base">
+<h1 class="title">Domande di conoscenza generale di Python, giusto per avere un'idea della vostra preparazione di base.</h1>
+<ol class="arabic simple">
+<li>Scrivere un programma che testa se una stringa rappresenta un numero.</li>
+<li>Scrivere un programma che lista tutti i files nella directory corrente.</li>
+<li>Come al punto 2, ma il programma deve anche listare tutti i files nelle sottodirectories ricorsivamente.</li>
+<li>Calcolare lo spazio occupato da tutti i files di tipo .txt contenuti in una directory.</li>
+<li>Listare i files a seconda delle dimensioni (dal più piccolo al più grande, in bytes).</li>
+<li>Scrivere un test per verificate che una directory sia vuota.</li>
+<li>Aprire una finestrella contenente la scritta &quot;hello, world!&quot;.</li>
+<li>Scaricare la pagina Web <a class="reference" href="http://www.example.com">http://www.example.com</a> da Internet usando Python.</li>
+<li>Stampare a schermo una tavola delle moltiplicazioni.</li>
+<li>Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto (thumbnails).</li>
+</ol>
+</div>
+</body>
+</html>
diff --git a/pypers/marelli/scaletta.txt b/pypers/marelli/scaletta.txt
new file mode 100755
index 0000000..dd5d5b2
--- /dev/null
+++ b/pypers/marelli/scaletta.txt
@@ -0,0 +1,55 @@
+- cygwin
+- pythonwin doc
+- pydoc
+- minidoc
+- python help
+- ipython
+- dot and dot_utils
+- matplotlib
+- gnuplot
+
+- python as glue language: subprocess and Popen
+- show tkplayer and microplayer
+
+
+- solutions to the questionary
+- sort_ci exercise
+- mutable-immutable
+
+- style guide
+- launcher with exec
+- pdb
+- exc_debugger.py
+- try .. finally
+- Graphics/stderr.py
+- logging to an HTTP server
+
+
+- mostrare doctest, le lezioni di Oxford, e il modulo dei decoratori
+. mostrare il doctester
+- mostrare py.test
+- qualche cenno su unittest
+- come scrivere un interprete di comandi per gestire i tests
+- simulatori di molti utenti di un'applicazione Web (?)
+
+USE SHORT FUNCTIONS (less comments needed, simplify a posteriori fixes)
+
+- example of OptionParser
+
+DO NOT CHANGE THE SOURCE CODE!!
+
+- how to fix bugs with dynamic overriding (example in quixote_utils)
+
+DECOUPLE LOGIC FROM USER INTERFACE
+
+- example of OptionParser
+
+COMPOSITION IS OFTEN BETTER THAN INHERITANCE
+
+- example of OptionParser
+
+###########
+
+ADVANCED OOP TIPS AND TRICKS
+
+- protected variables
diff --git a/pypers/meta.txt b/pypers/meta.txt
new file mode 100755
index 0000000..31adcf4
--- /dev/null
+++ b/pypers/meta.txt
@@ -0,0 +1,1024 @@
+THE MAGIC OF METACLASSES - PART I
+==========================================================================
+
+ .. line-block::
+
+ *Metaclasses are deeper magic than 99% of users should ever
+ worry about. If you wonder whether you need them, you don't
+ (the people who actually need them know with certainty that
+ they need them, and don't need an explanation about why).*
+ --Tim Peters
+
+Python always had metaclasses, since they are inherent to its object
+model. However, before Python 2.2, metaclasses where tricky and their
+study could cause the programmer's brain to explode [#]_. Nowadays,
+the situation has changed, and the reader should be able to understand
+this chapter without risk for his/her brain (however I do not give any
+warranty ;)
+
+Put it shortly, metaclasses give to the Python programmer
+complete control on the creation of classes. This simple statement
+has far reaching consequences, since the ability of interfering with
+the process of class creation, enable the programmer to make miracles.
+
+In this and in the following chapters, I will show some of these
+miracles.
+
+This chapter will focus on subtle problems of metaclasses in inheritance
+and multiple inheritance, including multiple inheritance of metaclasses
+with classes and metaclasses with metaclasses.
+
+The next chapter will focus more on applications.
+
+
+.. [#] Metaclasses in Python 1.5 [A.k.a the killer joke]
+ http://www.python.org/doc/essays/metaclasses/
+
+There is very little documentation about metaclasses, except Guido's
+essays and the papers by David Mertz and myself published in IBMdeveloperWorks
+
+ http://www-106.ibm.com/developerworks/library/l-pymeta.html
+
+Metaclasses as class factories
+------------------------------------------------------------------------
+
+In the Python object model (inspired from the Smalltalk, that had metaclasses
+a quarter of century ago!) classes themselves are objects.
+Now, since objects are instances of classes, that means that classes
+themselves can be seen as instances of special classes called *metaclasses*.
+Notice that things get hairy soon, since by following this idea, one could
+say the metaclasses themselves are classes and therefore objects; that
+would mean than even metaclasses can be seen as
+instances of special classes called meta-metaclasses. On the other hand,
+meta-meta-classes can be seen as instances of meta-meta-metaclasses,
+etc. Now, it should be obvious why metaclasses have gained such a
+reputation of brain-exploders ;). However, fortunately, the situation
+is not so bad in practice, since the infinite recursion of metaclasses is
+avoided because there is a metaclass that is the "mother of all metaclasses":
+the built-in metaclass *type*. 'type' has the property of being its own
+metaclass, therefore the recursion stops. Consider for instance the following
+example:
+
+ >>> class C(object): pass # a generic class
+ >>> type(C) #gives the metaclass of C
+ <type 'type'>
+ >>> type(type(C)) #gives the metaclass of type
+ <type 'type'>
+
+The recursion stops, since the metaclass of 'type' is 'type'.
+One cool consequence of classes being instances of 'type',
+is that since *type* is a subclass of object,
+
+ >>> issubclass(type,object)
+ True
+
+any Python class is not only a subclass of ``object``, but also
+an instance of 'object':
+
+ >>> isinstance(C,type)
+ True
+ >>> isinstance(C,object)
+ True
+ >>> issubclass(C,object)
+ True
+
+Notice that 'type' is an instance of itself (!) and therefore of 'object':
+
+ >>> isinstance(type,type) # 'type' is an instance of 'type'
+ True
+ >>> isinstance(type,object) # therefore 'type' is an instance of 'object'
+ True
+
+As it is well known, ``type(X)`` returns the type of ``X``; however,
+``type`` has also a second form in which it acts as a class factory.
+The form is ``type(name,bases,dic)`` where ``name`` is the name of
+the new class to be created, bases is the tuple of its bases and dic
+is the class dictionary. Let me give a few examples:
+
+ >>> C=type('C',(),{})
+ >>> C
+ <class '__main__.C'>
+ >>> C.__name__
+ 'C'
+ >>> C.__bases__
+ (<type 'object'>,)
+ >>> C.__dict__
+ <dict-proxy object at 0x8109054>
+
+Notice that since all metaclasses inherits from ``type``, as a consequences
+all metaclasses can be used as class factories.
+
+A fairy tale example will help in understanding the concept
+and few subtle points on how attributes are transmitted from metaclasses
+to their instances.
+
+Let me start by defining a 'Nobility' metaclass :
+
+ >>> class Nobility(type): attributes="Power,Richness,Beauty"
+
+instances of 'Nobility' are classes such 'Princes', 'Dukes', 'Barons', etc.
+
+ >>> Prince=Nobility("Prince",(),{})
+
+Instances of 'Nobility' inherits its attributes, just as instances of normal
+classes inherits the class docstring:
+
+ >>> Prince.attributes
+ 'Power,Richness,Beauty'
+
+Nevertheless, 'attributes' will not be retrieved by the ``dir`` function:
+
+ >>> print dir(Prince)
+ ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__weakref__']
+
+However, this is a limitation of ``dir``, in reality ``Prince.attributes``
+is there. On the other hand, the situation is different for a specific
+'Prince' object
+
+ >>> charles=Prince()
+ >>> charles.attributes #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'Prince' object has no attribute 'attributes'
+
+The transmission of metaclass attributes is not transitive:
+instances of the metaclass inherits the attributes, but not the instances
+of the instances. This behavior is by design and is needed in order to avoid
+troubles with special methods. This point will be throughly
+explained in the last paragraph. For the moment, I my notice that the
+behaviour is reasonable, since the abstract qualities 'Power,Richness,Beauty'
+are more qualities of the 'Prince' class than of one specific representative.
+They can always be retrieved via the ``__class__`` attribute:
+
+ >>> charles.__class__.attributes
+ 'Power,Richness,Beauty'
+
+Le me now define a metaclass 'Froggyness':
+
+ >>> class Frogginess(type): attributes="Powerlessness,Poverty,Uglyness"
+
+Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.
+
+ >>> Frog=Frogginess("Frog",(),{})
+ >>> Frog.attributes
+ 'Powerlessness,Poverty,Uglyness'
+
+However, in Python miracles can happen:
+
+ >>> def miracle(Frog): Frog.__class__=Nobility
+ >>> miracle(Frog); Frog.attributes
+ 'Powerlessness,Richness,Beauty'
+
+In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.
+
+However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:
+
+ >>> Frog.attributes="poor, small, ugly"
+ >>> jack=Frog(); jack.attributes
+ 'poor, small, ugly'
+
+Then the miracle cannot work:
+
+ ::
+
+ #<fairytale2.py>
+
+ class Nobility(type): attributes="Power, Richness, Beauty"
+ Prince=Nobility("Prince",(),{})
+ charles=Prince()
+
+ class Frogginess(type): attributes="Inpuissance, Poverty, Uglyness"
+ Frog=Frogginess("Frog",(),{})
+ Frog.attributes="poor, small, ugly"
+ jack=Frog()
+
+ def miracle(Frog): Frog.__class__=Nobility
+
+ miracle(Frog)
+
+ print "I am",Frog.attributes,"even if my class is",Frog.__class__
+
+ #</fairytale2.py>
+
+Output:
+
+ ::
+
+ I am poor, small, ugly even if my class is <class '__main__.Nobility'>
+
+The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is ``poor, small, ugly``. If you think a bit, it makes sense.
+
+Remark:
+
+In Python 2.3 there are restrictions when changing the ``__class__``
+attribute for classes:
+
+ >>> C=type('C',(),{})
+ >>> C.__class__ = Nobility #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: __class__ assignment: only for heap types
+
+Here changing ``C.__class__`` is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:
+
+ >>> print bool.__doc__ # in Python 2.2 would give an error
+ bool(x) -> bool
+ Returns True when the argument x is true, False otherwise.
+ The builtins True and False are the only two instances of the class bool.
+ The class bool is a subclass of the class int, and cannot be subclassed.
+
+In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:
+
+ >>> class M1(type): f=lambda cls: 'M1.f' #metaclass1
+ >>> class M2(type): f=lambda cls: 'M2.f' #metaclass2
+ >>> B=M1('B',(),{}) # B receives M1.f
+ >>> class C(B): pass #C receives M1.f
+ >>> B.f()
+ 'M1.f'
+ B.__class__=M2 #change the metaclass
+ >>> B.f() #B receives M2.f
+ 'M2.f'
+ C.f() #however C does *not* receive M2.f
+ >>> C.f()
+ 'M1.f'
+ >>> type(B)
+ <class '__main__.M2'>
+ >>> type(C)
+ <class '__main__.M1'>
+
+Metaclasses as class modifiers
+----------------------------------------------------------------------
+
+The interpretation of metaclasses in terms of class factories is quite
+straightforward and I am sure that any Pythonista will be at home
+with the concept. However, metaclasses have such a reputation of black
+magic since their typical usage is *not* as class factories, but as
+*class modifiers*. This means that metaclasses are typically
+used to modify *in fieri* classes. The trouble is that the
+modification can be utterly magical.
+Here there is another fairy tale example showing the syntax
+(via the ``__metaclass__`` hook) and the magic of the game:
+
+ ::
+
+ #<oopp.py>
+
+ class UglyDuckling(PrettyPrinted):
+ "A plain, regular class"
+ formatstring="Not beautiful, I am %s"
+
+ class MagicallyTransformed(type):
+ "Metaclass changing the formatstring of its instances"
+ def __init__(cls,*args):
+ cls.formatstring="Very beautiful, since I am %s"
+
+ class TransformedUglyDuckling(PrettyPrinted):
+ "A class metamagically modified"
+ __metaclass__ = MagicallyTransformed
+ formatstring="Not beautiful, I am %s" # will be changed
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> print UglyDuckling()
+ Not beautiful, I am <UglyDuckling>
+
+In this example, even if in 'TransformedUglyDuckling' we explicitely
+set the formatstring to "Not beautiful, I am %s", the metaclass changes
+it to "Very beautiful, even if I am %s" and thus
+
+ >>> print TransformedUglyDuckling() # gives
+ Very beautiful, since I am <TransformedUglyDuckling>
+
+Notice that the ``__metaclass__`` hook passes to the metaclass
+``MagicallyTransformed`` the name, bases and dictionary of the class
+being created, i.e. 'TransformedUglyDucking'.
+
+Metaclasses, when used as class modifiers, act *differently*
+from functions, when inheritance is
+involved. To clarify this subtle point, consider a subclass 'Swan'
+of 'UglyDuckling':
+
+
+ >>> from oopp import *
+ >>> class Swan(UglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+Now, let me define a simple function acting as a class modifier:
+
+ >>> def magicallyTransform(cls):
+ ... "Modifies the class formatstring"
+ ... customize(cls,formatstring="Very beautiful, even if I am %s")
+ ... return cls
+
+The function works:
+
+ >>> magicallyTransform(UglyDuckling)
+ >>> print UglyDuckling()
+ Very beautiful, even if I am <UglyDuckling>
+
+This approach is destructive, since we cannot have the original
+and the transformed class at the same time, and has potentially bad side
+effects in the derived classes. Nevertheless, in this case it works
+and it is not dangereous for the derived class 'Swan', since 'Swan'
+explicitly overrides the 'formatstring' attribute and doesn't care about
+the change in 'UglyDuckling.formatstring'. Therefore the output
+of
+
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+is still the same as before the action of the function ``magicallyTransform``.
+The situation is quite different if we use the 'MagicallyTransformed'
+metaclass:
+
+ >>> from oopp import *
+ >>> class Swan(TransformedUglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+
+ >>> print TransformedUglyDuckling()
+ Very beautiful, since I am <UglyDuckling>
+ >>> print Swan() # does *not* print "Very beautiful, I am <Swan>"
+ Very beautiful, since I am <Swan>
+
+Therefore, not only the metaclass has magically transformed the
+'TransformedUglyDuckling.formatstring', it has also transformed the
+'Swan.formatstring'! And that, despite the fact that
+'Swan.formatstring' is explicitly set.
+
+The reason for this behaviour is that since 'UglyDuckling' is a base
+class with metaclass 'MagicallyTransformed', and since 'Swan' inherits from
+'UglyDuckling', then 'Swan' inherits the metaclass 'MagicallyTransformed',
+which is automatically called at 'Swan' creation time.
+That's the reason why metaclasses are much more magical and much
+more dangerous than
+functions: functions do not override attributes in the derived classes,
+metaclasses do, since they are automagically called at the time of
+creation of the subclass. In other words, functions are explicit,
+metaclasses are implicit. Nevertheless, this behavior can be pretty
+useful in many circumstances, and it is a feature, not a bug. In the
+situations where this behavior is not intended, one should use a function,
+not a metaclass. In general, metaclasses are better than functions,
+since metaclasses are classes and as such they can inherit one from each
+other. This means that one can improve a basic metaclass trough
+(multiple) inheritance, with *reuse* of code.
+
+A few caveats about the usage of metaclasses
+------------------------------------------------------------------------
+
+Let me start with some caveats about the ``__metaclass__`` hook, which
+commonly used and quite powerful, but also quite dangereous.
+
+Let's imagine a programmer not
+knowing about metaclasses and looking at the 'TransformedUglyDuckling'
+code (assuming there are no comments): she would probably think
+that "__metaclass__" is some special attribute used for introspection
+purposes only, with no other effects, and she would probably expect
+the output of the script to be "Not much, I am the class
+TransformedUglyDucking" whereas it is exacly the contrary! In other
+words, when metaclasses are involved, *what you see, is not what you get*.
+The situation is even more implicit when the metaclass is inherited
+from some base class, therefore lacking also the visual clue of the hook.
+
+For these reasons, metaclasses are something to be used with great care;
+they can easily make your code unreadable and confuse inexpert programmers.
+Moreover, it is more difficult to debug programs involving metaclasses, since
+methods are magically transformed by routines defined in the metaclass,
+and the code you see in the class is *not* what Python sees. I think
+the least confusing way of using metaclasses, is to concentrate all
+the dynamics on them and to write empty classes except for the
+metaclass hook. If you write a class with no methods such as
+
+ ::
+
+ class TransformedUglyDuckling(object):
+ __metaclass__=MagicallyTransformed
+
+then the only place to look at, is the metaclass. I have found extremely
+confusing to have some of the methods defined in the class and some in
+the metaclass, especially during debugging.
+
+Another point to make, is that the ``__metaclass__``
+hook should not be used to modify pre-existing classes,
+since it requires modifying the source code (even if it is enough to
+change one line only). Moreover, it is confusing, since adding a
+``__metaclass__`` attribute *after* the class creation would not do the job:
+
+ >>> from oopp import UglyDuckling, MagicallyTransformed
+ >>> UglyDuckling.__metaclass__=MagicallyTransformed
+ >>> print UglyDuckling()
+ "Not much, I am the class UglyDuckling"
+
+The reason is that we have to think of UglyDuckling as an instance of
+``type``, the built-in metaclasses; merely adding a ``__metaclass__``
+attribute does not re-initialize the class.
+The problem is elegantly solved by avoiding the hook and creating
+an enhanced copy of the original class trough ``MagicallyTransformed``
+used as a class factory.
+
+ >>> name=UglyDuckling.__name__
+ >>> bases=UglyDuckling.__bases__
+ >>> dic=UglyDuckling.__dict__.copy()
+ >>> UglyDuckling=MagicallyTransformed(name,bases,dic)
+
+Notice that I have recreated 'UglyDuckling', giving to the new class
+the old identifier.
+
+ >>> print UglyDuckling()
+ Very beautiful, since I am <UglyDuckling>>
+
+The metaclass of this new 'UglyDuckling' has been specified and will
+accompanies all future children of 'UglyDuckling':
+
+ >>> class Swan(UglyDuckling): pass
+ ...
+ >>> type(Swan)
+ <class '__main__.MagicallyTransformed'>
+
+Another caveat, is in the overridding of `` __init__`` in the metaclass.
+This is quite common in the case of metaclasses called trough the
+``__metaclass__`` hook mechanism, since in this case the class
+has been already defined (if not created) in the class statement,
+and we are interested in initializing it, more than in recreating
+it (which is still possible, by the way).
+The problem is that overriding ``__init__`` has severe limitations
+with respect to overriding ``__new__``,
+since the 'name', 'bases' and 'dic' arguments cannot be directly
+changed. Let me show an example:
+
+ ::
+
+ #<init_in_metaclass.py>
+
+ from oopp import *
+
+ class M(type):
+ "Shows that dic cannot be modified in __init__, only in __new__"
+ def __init__(cls,name,bases,dic):
+ name='C name cannot be changed in __init__'
+ bases='cannot be changed'
+ dic['changed']=True
+
+ class C(object):
+ __metaclass__=M
+ changed=False
+
+ print C.__name__ # => C
+ print C.__bases__ # => (<type 'object'>,)
+ print C.changed # => False
+
+ #</init_in_metaclass.py>
+
+The output of this script is ``False``: the dictionary cannot be changed in
+``__init__`` method. However, replacing ``dic['changed']=True`` with
+``cls.changed=True`` would work. Analougously, changing ``cls.__name__``
+would work. On the other hand, ``__bases__`` is a read-only attribute and
+cannot be changed once the class has been created, therefore there is no
+way it can be touched in ``__init__``. However, ``__bases__`` could be
+changed in ``__new__`` before the class creation.
+
+Metaclasses and inheritance
+-------------------------------------------------------------------------
+
+It is easy to get confused about the difference between a metaclass
+and a mix-in class in multiple inheritance, since
+both are denoted by adjectives and both share the same idea of
+enhancing a hierarchy. Moreover, both mix-in classes and metaclasses
+can be inherited in the whole hierarchy.
+Nevertheless, they behaves differently
+and there are various subtle point to emphasize. We have already
+noticed in the first section that attributes of a metaclass
+are transmitted to its instances, but not to the instances of the
+instances, whereas the normal inheritance is transitive: the
+grandfather transmits its attributes to the children and to the grandchild
+too. The difference can be represented with the following picture, where
+'M' is the metaclass, 'B' a base class, 'C' a children of 'B'
+and c an instance of 'C':
+
+ ::
+
+ M (attr) B (attr)
+ : |
+ C (attr) C (attr)
+ : :
+ c () c (attr)
+
+Notice that here the relation of instantiation is denoted by a dotted line.
+
+This picture is valid when C has metaclass M but not base class, on when C
+has base class but not metaclass. However, what happens whrn the class C has
+both a metaclass M and a base class B ?
+
+ >>> class M(type): a='M.a'
+ >>> class B(object): a='B.a'
+ >>> class C(B): __metaclass__=M
+ >>> c=C()
+
+The situation can be represented by in the following graph,
+
+ ::
+
+ (M.a) M B (B.a)
+ : /
+ : /
+ (?) C
+ :
+ :
+ (?) c
+
+
+Here the metaclass M and the base class B are fighting one against the other.
+Who wins ? C should inherit the attribute 'B.a' from its base B, however,
+the metaclass would like to induce an attribute 'M.a'.
+The answer is that the inheritance constraint wins on the metaclass contraint:
+
+ >>> C.a
+ 'B.a'
+ >>> c.a
+ 'B.a'
+
+The reason is the same we discussed in the fairy tale example: 'M.a' is
+an attribute of the metaclass, if its instance C has already a specified
+attributed C.a (in this case specified trough inheritance from B), then
+the attribute is not modified. However, one could *force* the modification:
+
+ >>> class M(type):
+ ... def __init__(cls,*args): cls.a='M.a'
+ >>> class C(B): __metaclass__=M
+ >>> C.a
+ 'M.a'
+
+In this case the metaclass M would win on the base class B. Actually,
+this is not surprising, since it is explicit. What could be surprising,
+had we not explained why inheritance silently wins, is that
+
+ >>> c.a
+ 'B.a'
+
+This explain the behaviour for special methods like
+``__new__,__init__,__str__``,
+etc. which are defined both in the class and the metaclass with the same
+name (in both cases,they are inherited from ``object``).
+
+In the chapter on objects, we learned that the printed representation of
+an object can be modified by overring the ``__str__`` methods of its
+class. In the same sense, the printed representation of a class can be
+modified by overring the ``__str__`` methods of its metaclass. Let me show an
+example:
+
+ ::
+
+ #<oopp.py>
+
+ class Printable(PrettyPrinted,type):
+ """Apparently does nothing, but actually makes PrettyPrinted acting as
+ a metaclass."""
+
+ #</oopp.py>
+
+Instances of 'Printable' are classes with a nice printable representation:
+
+ >>> from oopp import Printable
+ >>> C=Printable('Classname',(),{})
+ >>> print C
+ Classname
+
+However, the internal string representation stays the same:
+
+ >>> C # invokes Printable.__repr__
+ <class '__main__.Classname'>
+
+Notice that the name of class 'C' is ``Classname`` and not 'C' !
+
+Consider for instance the following code:
+
+ >>> class M(type):
+ ... def __str__(cls):
+ ... return cls.__name__
+ ... def method(cls):
+ ... return cls.__name__
+ ...
+ >>> class C(object):
+ ... __metaclass__=M
+ >>> c=C()
+
+In this case the ``__str__`` method in ``M`` cannot override the
+``__str__`` method in C, which is inherited from ``object``.
+Moreover, if you experiment a little, you will see that
+
+ >>> print C # is equivalent to print M.__str__(C)
+ C
+ >>> print c # is equivalent to print C.__str__(c)
+ <__main__.C object at 0x8158f54>
+
+
+The first ``__str__`` is "attached" to the metaclass and the
+second to the class.
+
+Consider now the standard method "method". It is both attached to the
+metaclass
+
+ >>> print M.method(C)
+ C
+
+and to the class
+
+ >>> print C.method() #in a sense, this is a class method, i.e. it receives
+ C #the class as first argument
+
+Actually it can be seen as a class method of 'C' (cfr. Guido van Rossum
+"Unifying types and classes in Python 2.2". When he discusses
+classmethods he says: *"Python also has real metaclasses, and perhaps
+methods defined in a metaclass have more right to the name "class method";
+but I expect that most programmers won't be using metaclasses"*). Actually,
+this is the SmallTalk terminology, Unfortunately, in Python the word
+``classmethod`` denotes an attribute descriptor, therefore it is better
+to call the methods defined in a metaclass *metamethods*, in order to avoid
+any possible confusion.
+
+The difference between ``method`` and ``__str__`` is that you cannot use the
+syntax
+
+ >>> print C.__str__() #error
+ TypeError: descriptor '__str__' of 'object' object needs an argument
+
+because of the confusion with the other __str__; you can only use the
+syntax
+
+ >>> print M.__str__(C)
+
+Suppose now I change C's definition by adding a method called "method":
+
+ ::
+
+ class C(object):
+ __metaclass__=M
+ def __str__(self):
+ return "instance of %s" % self.__class__
+ def method(self):
+ return "instance of %s" % self.__class__
+
+If I do so, then there is name clashing and the previously working
+statement print C.method() gives now an error:
+
+ ::
+
+ Traceback (most recent call last):
+ File "<stdin>", line 24, in ?
+ TypeError: unbound method method() must be called with C instance as
+ first argument (got nothing instead)
+
+Conclusion: ``__str__, __new__, __init__`` etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. ``M.__str__(C)``),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. ``C.method()`` instead of
+``M.method(C)``).
+Metaclass methods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for ``__str__``,
+``___init__``, etc.
+
+Conflicting metaclasses
+----------------------------------------------------------------------------
+
+Consider a class 'A' with metaclass 'M_A' and a class 'B' with
+metaclass 'M_B'; suppose I derive 'C' from 'A' and 'B'. The question is:
+what is the metaclass of 'C' ? Is it 'M_A' or 'M_B' ?
+
+The correct answer (see "Putting metaclasses to work" for a thought
+discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+'M_A' and 'M_B', as in the following graph:
+
+
+ .. figure:: fig1.gif
+
+However, Python is not yet that magic, and it does not automatically create
+'M_C'. Instead, it will raise a ``TypeError``, warning the programmer of
+the possible confusion:
+
+ >>> class M_A(type): pass
+ >>> class M_B(type): pass
+ >>> A=M_A('A',(),{})
+ >>> B=M_B('B',(),{})
+ >>> class C(A,B): pass #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metatype conflict among bases
+
+This is an example where the metaclasses 'M_A' and 'M_B' fight each other
+to generate 'C' instead of cooperating. The metatype conflict can be avoided
+by assegning the correct metaclass to 'C' by hand:
+
+ >>> class C(A,B): __metaclass__=type("M_AM_B",(M_A,M_B),{})
+ >>> type(C)
+ <class '__main__.M_AM_B'>
+
+In general, a class A(B, C, D , ...) can be generated without conflicts only
+if type(A) is a subclass of each of type(B), type(C), ...
+
+In order to avoid conflicts, the following function, that generates
+the correct metaclass by looking at the metaclasses of the base
+classes, is handy:
+
+ ::
+
+ #<oopp.py>
+
+ metadic={}
+
+ def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+ #</oopp.py>
+
+This function is particularly smart since:
+
+ 1. Avoid duplications ..
+
+ 2. Remember its results.
+
+We may generate the child of a tuple of base classes with a given metaclass
+and avoiding metatype conflicts thanks to the following ``child`` function:
+
+ ::
+
+ #<oopp.py>
+
+ def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> class C(A,B): __metaclass__=makecls()
+ >>> print C,type(C)
+ <class 'oopp.AB_'> <class 'oopp._M_AM_B'>
+
+Notice that the automatically generated metaclass does not pollute the
+namespace:
+
+ >>> _M_A_M_B #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ NameError: name '_M_A_M_B' is not defined
+
+It can only be accessed as ``type(C)``.
+
+Put it shortly, the ``child`` function allows to generate a child from bases
+enhanced by different custom metaclasses, by generating under the hood a
+compatibile metaclass via multiple inheritance from the original metaclasses.
+However, this logic can only work if the original metaclasses are
+cooperative, i.e. their methods are written in such a way to avoid
+collisions. This can be done by using the cooperative the ``super`` call
+mechanism discussed in chapter 4.
+
+Cooperative metaclasses
+----------------------------------------------------------------------------
+
+In this section I will discuss how metaclasses can be composed with
+classes and with metaclasses, too. Since we will discuss even
+complicated hierarchies, it is convenient to have an utility
+routine printing the MRO of a given class:
+
+ ::
+
+ #<oopp.py>
+
+ def MRO(cls):
+ count=0; out=["MRO of %s:" % cls.__name__]
+ for c in cls.__mro__:
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=" %s - %s(%s)" % (count,name,bases)
+ if type(c) is not type: s+="[%s]" % type(c).__name__
+ out.append(s); count+=1
+ return '\n'.join(out)
+
+ #</oopp.py>
+
+Notice that ``MRO`` also prints the metaclass' name in square brackets, for
+classes enhanced by a non-trivial metaclass.
+
+Consider for instance the following hierarchy:
+
+ >>> from oopp import MRO
+ >>> class B(object): pass
+ >>> class M(B,type): pass
+ >>> class C(B): __metaclass__=M
+
+Here 'M' is a metaclass that inherits from 'type' and the base class 'B'
+and 'C' is both an instance of 'M' and a child of 'B'. The inheritance
+graph can be draw as
+
+ ::
+
+ object
+ / \
+ B type
+ | \ /
+ | M
+ \ :
+ \ :
+ C
+
+Suppose now we want to retrieve the ``__new__`` method of B's superclass
+with respect to the MRO of C: obviously, this is ``object.__new__``, since
+
+ >>> print MRO(C)
+ MRO of C:
+ 0 - C(B)[M]
+ 1 - B(object)
+ 2 - object()
+
+This allows to create an instance of 'C' in this way:
+
+ >>> super(B, C).__new__(C)
+ <__main__.C object at 0x4018750c>
+
+It is interesting to notice that this would not work in Python 2.2,
+due to a bug in the implementation of ``super``, therefore do not
+try this trick with older version of Python.
+
+Notice that everything works
+only because ``B`` inherits the ``object.__new__`` staticmethod that
+is cooperative and it turns out that it calls ``type.__new__``. However,
+if I give to 'B' a non-cooperative method
+
+ >>> B.__new__=staticmethod(lambda cls,*args: object.__new__(cls))
+
+things do not work:
+
+ >>> M('D',(),{}) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 1, in <lambda>
+ TypeError: object.__new__(M) is not safe, use type.__new__()
+
+A cooperative method would solve the problem:
+
+ >>> B.__new__=staticmethod(lambda m,*args: super(B,m).__new__(m,*args))
+ >>> M('D',(),{}) # calls B.__new__(M,'D',(),{})
+ <class '__main__.D'>
+
+Cooperation between classes and metaclasses is pretty tricky.
+This is why autosuper is so difficult to get right.
+
+Consider this example, with object and type instances of MetaSuper::
+
+ class B(object):
+ def __init__(self, *args):
+ print "B.__init__"
+ self.__super.__init__(*args)
+
+ class M(B, type):
+ def __init__(self, n, b, d):
+ print "M.__init__"
+ self.__super.__init__(n, b, d)
+
+ class C(B):
+ __metaclass__ = M
+ def __init__(self):
+ print "C.__init__"
+ self.__super.__init__()
+
+What happens here at C creation? The metaclass _MMetaSuper is generated,
+with ancestors M, B, superobject, supertype, MetaSuper, safetype, type,
+which calls M.__init_(C, )_ and then B.__init__(C) which calls
+the *unbound* method superobject.__init__(C ..) and NOT the *bound* method
+MetaSuper.__init__(C, ) which would be the "right" thing to do in this
+situation (but not in other situations). So C._C__super is NOT initialized :-(
+
+Metamethods vs class methods
+-------------------------------------------------------------------
+
+Meta-methods, i.e. methods defined in
+a metaclass.
+
+Python has already few built-in metamethods such as ``.mro()``,
+ ``__subclass__()``, ``__delattr__`` and possibly others.
+Moreover ``__name__`` is an example of meta-descriptor.
+These are methods of the metaclass 'type' and there of any of its
+sub-metaclasses.
+
+ >>> dir(type)
+ ['__base__', '__bases__', '__basicsize__', '__call__', '__class__',
+ '__cmp__', '__delattr__', '__dict__', '__dictoffset__', '__doc__',
+ '__flags__', '__getattribute__', '__hash__', '__init__', '__itemsize__',
+ '__module__', '__mro__', '__name__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__subclasses__', '__weakrefoffset__', 'mro']
+
+
+ >>> print type.mro.__doc__
+ mro() -> list
+ return a type's method resolution order
+ >>> print type.__subclasses__.__doc__
+ __subclasses__() -> list of immediate subclasses
+
+ >>> class A(object): pass
+ >>> class B(A): pass
+ >>> B.mro()
+ [<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
+ >>> A.__subclasses__()
+ [<class '__main__.B'>]
+
+Notice that ``mro()`` and ``__subclasses__`` are not retrieved by ``dir``.
+
+Let me constrast metamethods with the more traditional classmethods.
+In many senses, the to concepts are akin:
+
+ >>> class M(type):
+ ... "Metaclass with a (meta)method mm"
+ ... def mm(cls): return cls
+ >>> D=M('D',(),{'cm':classmethod(lambda cls: cls)})
+ >>> # instance of M with a classmethod cm
+ >>> D.mm # the metamethod
+ <bound method M.mm of <class '__main__.C'>>
+ >>> D.cm # the classmethod
+ <unbound method D.<lambda>>
+
+Notice the similarities between the classmethod and the metamethod:
+
+ >>> D.mm.im_self, D.cm.im_self # the same
+ (<class '__main__.D'>, <class '__main__.D'>)
+ >>> D.mm.im_class, D.cm.im_class # still the same
+ (<class '__main__.M'>, <class '__main__.M'>)
+
+There are no surprises for ``im_func``:
+
+ >>> D.mm.im_func, D.cm.im_func
+ (<function mm at 0x402c272c>, <function <lambda> at 0x402c280c>)
+
+Nevertheless, there are differences: metamethods are not bounded to
+instances of the class
+
+ >>> D().cm() # the classmethod works fine
+ <class '__main__.D'>
+ >>> D().mm() # the metamethod does not: error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'D' object has no attribute 'mm'
+
+and they are not retrieved by ``dir``:
+
+ >>> from oopp import *
+ >>> attributes(D).keys() # mm is not retrieved, only cm
+ ['cm']
+
+ >>> cm.__get__('whatever') #under Python 2.2.0 would give a serious error
+ Segmentation fault
+ >>> cm.__get__(None) #under Python 2.3 there is no error
+ <bound method type.<lambda> of <type 'NoneType'>>
+
+Moreover metamethods behaves differently with respect to multiple
+inheritance. If a class A define a classmethod cA and a class B
+defines a classmethod cB, then the class C(A,B) inherits both the
+classmethods cA and cB. In the case of metamethods defined in M_A
+and M_B, the same is true only if one resolves the meta-type
+conflict by hand, by generating the metaclass M_C(M_A,M_B). In this
+sense, classmethods are simpler to use than metamethods.
diff --git a/pypers/meta/fig1.fig b/pypers/meta/fig1.fig
new file mode 100755
index 0000000..eeb6fd4
--- /dev/null
+++ b/pypers/meta/fig1.fig
@@ -0,0 +1,28 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 10875 4575 10875 5550
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 10875 6075 10875 6825
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 6525 6000 6525 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 3750 5775 6000 5775
+4 0 0 50 0 0 24 0.0000 4 255 885 2775 5925 Noble\001
+4 0 0 50 0 0 24 0.0000 4 255 960 6075 5925 Prince\001
+4 0 0 50 0 0 24 0.0000 4 255 1050 6075 7200 charles\001
+4 0 0 50 0 0 24 0.0000 4 255 795 10575 5925 Duke\001
+4 0 0 50 0 0 24 0.0000 4 330 1170 10275 4500 Nobility\001
+4 0 0 50 0 0 24 0.0000 4 255 540 10650 7200 earl\001
+4 0 0 50 0 0 24 0.0000 4 255 4125 2700 4500 Instantiation vs. Inheritance\001
diff --git a/pypers/meta/fig2.fig b/pypers/meta/fig2.fig
new file mode 100755
index 0000000..b3544ec
--- /dev/null
+++ b/pypers/meta/fig2.fig
@@ -0,0 +1,27 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 5700 2850 5700 4050
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 5700 4650 5700 5700
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 3600 4350 5550 4350
+4 0 0 50 0 0 24 0.0000 4 255 330 5550 2775 M\001
+4 0 0 50 0 0 24 0.0000 4 255 240 5625 4500 C\001
+4 0 0 50 0 0 24 0.0000 4 180 165 5625 6000 c\001
+4 0 0 50 0 0 24 0.0000 4 255 240 3300 4500 B\001
+4 0 0 50 0 0 24 0.0000 4 330 825 6075 2775 (M.a)\001
+4 0 0 50 0 0 24 0.0000 4 330 405 6075 4500 (?)\001
+4 0 0 50 0 0 24 0.0000 4 330 405 6075 6000 (?)\001
+4 0 0 50 0 0 24 0.0000 4 330 735 2250 4500 (B.a)\001
+4 0 0 50 0 0 24 0.0000 4 330 5490 2475 2025 Combined Superclass and Metaclass \001
diff --git a/pypers/meta/fig3.fig b/pypers/meta/fig3.fig
new file mode 100755
index 0000000..078c787
--- /dev/null
+++ b/pypers/meta/fig3.fig
@@ -0,0 +1,32 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+2 1 0 1 0 7 50 0 -1 0.000 2 0 -1 1 0 3
+ 0 0 2.00 120.00 240.00
+ 2100 2850 2100 4050 7575 4050
+2 1 0 1 0 7 50 0 -1 0.000 2 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 2700 2550 4725 2550
+2 1 0 1 0 7 50 0 -1 0.000 2 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 5475 2550 7050 2550
+2 1 1 1 0 7 50 0 -1 4.000 2 0 -1 0 0 2
+ 7800 2850 7800 3825
+2 1 1 1 0 7 50 0 -1 4.000 2 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 7800 4350 7800 5250
+4 0 0 50 0 0 24 0.0000 4 330 4965 2175 1200 Metaclasses and Special Methods\001
+4 0 0 50 0 0 24 0.0000 4 330 885 1725 2700 object\001
+4 0 0 50 0 0 24 0.0000 4 300 615 4800 2700 type\001
+4 0 0 50 0 0 24 0.0000 4 255 1335 7200 2700 Printable\001
+4 0 0 50 0 0 24 0.0000 4 255 240 7725 4200 C\001
+4 0 0 50 0 0 24 0.0000 4 180 165 7725 5625 c\001
+4 0 0 50 0 0 16 0.0000 4 210 885 2400 3000 (__str__)\001
+4 0 0 50 0 0 16 0.0000 4 210 885 8250 3000 (__str__)\001
+4 0 0 50 0 0 16 0.0000 4 225 1530 8175 4200 (object.__str__)\001
diff --git a/pypers/meta/fig3.txt b/pypers/meta/fig3.txt
new file mode 100755
index 0000000..672e040
--- /dev/null
+++ b/pypers/meta/fig3.txt
@@ -0,0 +1,9 @@
+
+
+ M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
diff --git a/pypers/meta/fig4.fig b/pypers/meta/fig4.fig
new file mode 100755
index 0000000..bcfea5f
--- /dev/null
+++ b/pypers/meta/fig4.fig
@@ -0,0 +1,37 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 9150 1650 9150 3225
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 3750 3675 6000 5700
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 9075 3750 6600 5700
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 6300 3750 6300 5550
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 3750 1650 3750 3150
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 8550 1500 6525 3075
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 2.00 120.00 240.00
+ 4200 1500 6075 3075
+4 0 0 50 0 0 24 0.0000 4 330 780 3300 1500 M_A\001
+4 0 0 50 0 0 24 0.0000 4 330 765 8700 1500 M_B\001
+4 0 0 50 0 0 24 0.0000 4 255 240 6150 6000 C\001
+4 0 0 50 0 0 24 0.0000 4 255 255 3600 3600 A\001
+4 0 0 50 0 0 24 0.0000 4 255 240 9000 3600 B\001
+4 0 0 50 0 0 24 0.0000 4 330 4725 3975 600 Avoiding the Metaclass Conflict\001
+4 0 0 50 0 0 24 0.0000 4 330 765 5850 3600 M_C\001
diff --git a/pypers/meta/fill.py b/pypers/meta/fill.py
new file mode 100755
index 0000000..ec92c0b
--- /dev/null
+++ b/pypers/meta/fill.py
@@ -0,0 +1,4 @@
+import textwrap
+
+text=file('meta.txt').read()
+print textwrap.fill(text,70,replace_whitespace=False)
diff --git a/pypers/meta/meta1.html b/pypers/meta/meta1.html
new file mode 100755
index 0000000..683a2a8
--- /dev/null
+++ b/pypers/meta/meta1.html
@@ -0,0 +1,399 @@
+<?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></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<p>Metaclass programming in Python
+Contents:
+Review of object-oriented programming
+A metaprogramming rejoinder
+Metaclasses: a solution looking for a problem?
+Solving problems with magic
+More ways to solve problems with magic
+Meta conveniences
+Resources
+About the authors
+Rate this article
+Related content:
+AspectJ brings AOP to the Java language
+XML Matters: Enforcing validity with the gnosis.xml.validity library
+Guide to Python introspection
+Subscribe to the developerWorks newsletter
+developerWorks Toolbox subscription
+Also in the Linux zone:
+Tutorials
+Tools and products
+Code and components
+Articles
+Pushing object-oriented programming to the next level</p>
+<p>Level: Introductory</p>
+<p>David Mertz, Ph.D. (<a class="reference" href="mailto:mertz&#64;gnosis.cx">mertz&#64;gnosis.cx</a>), Developer, Gnosis Software, Inc.
+Michele Simionato, Ph.D. (<a class="reference" href="mailto:mis6+&#64;pitt.edu">mis6+&#64;pitt.edu</a>), Physicist, University of Pittsburgh</p>
+<p>February 26, 2003
+Most readers are already familiar with the concepts of object-oriented
+programming: inheritance, encapsulation, polymorphism. But the creation
+of objects of a given class, with certain parents, is usually thought of
+as a &quot;just so&quot; operation. It turns out that a number of new programming
+constructs become either easier, or possible at all, when you can
+customize the process of object creation. Metaclasses enable certain
+types of &quot;aspect-oriented programming,&quot; for example, you can enhance
+classes with features like tracing capabilities, object persistence,
+exception logging, and more.</p>
+<p>Review of object-oriented programming
+Let's start with a 30-second review of just what OOP is. In an object-oriented programming language, you can define classes, whose purpose is to bundle together related data and behaviors. These classes can inherit some or all of their qualities from their parents, but they can also define attributes (data) or methods (behaviors) of their own. At the end of the process, classes generally act as templates for the creation of instances (at times also called simply objects). Different instances of the same class will typically have different data, but it will come in the same shape -- for example, the Employee objects bob and jane both have a .salary and a .room_number, but not the same room and salary as each other.</p>
+<p>Some OOP languages, including Python, allow for objects to be introspective (also called reflective). That is, an introspective object is able to describe itself: What class does the instance belong to? What ancestors does that class have? What methods and attributes are available to the object? Introspection lets a function or method that handles objects make decisions based on what kind of object it is passed. Even without introspection, functions frequently branch based on instance data -- for example, the route to jane.room_number differs from that to bob.room_number because they are in different rooms. With introspection, you can also safely calculate the bonus jane gets, while skipping the calculation for bob, for example, because jane has a .profit_share attribute, or because bob is an instance of the subclass Hourly(Employee).</p>
+<p>A metaprogramming rejoinder
+The basic OOP system sketched above is quite powerful. But there is one element brushed over in the description: in Python (and other languages), classes are themselves objects that can be passed around and introspected. Since objects, as stated, are produced using classes as templates, what acts as a template for producing classes? The answer, of course, is metaclasses.</p>
+<p>Python has always had metaclasses. But the machinery involved in metaclasses became much better exposed with Python 2.2. Specifically, with version 2.2, Python stopped being a language with just one special (mostly hidden) metaclass that created every class object. Now programmers can subclass the aboriginal metaclass type and even dynamically generate classes with varying metaclasses. Of course, just because you can manipulate metaclasses in Python 2.2, that does not explain why you might want to.</p>
+<p>Moreover, you do not need to use custom metaclasses to manipulate the production of classes. A slightly less brain-melting concept is a class factory: An ordinary function can return a class that was dynamically created within the function body. In traditional Python syntax, you can write:</p>
+<p>Python 1.5.2 (#0, Jun 27 1999, 11:23:01) [...]
+Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+&gt;&gt;&gt; def class_with_method(func):
+... class klass: pass
+... setattr(klass, func.__name__, func)
+... return klass
+...
+&gt;&gt;&gt; def say_foo(self): print 'foo'
+...
+&gt;&gt;&gt; Foo = class_with_method(say_foo)
+&gt;&gt;&gt; foo = Foo()
+&gt;&gt;&gt; foo.say_foo()
+foo</p>
+<p>The factory function class_with_method() dynamically creates and returns a class that contains the method/function passed into the factory. The class itself is manipulated within the function body before being returned. The new module provides a more concise spelling, but without the same options for custom code within the body of the class factory, for example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from new import classobj
+&gt;&gt;&gt; Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
+&gt;&gt;&gt; Foo2().bar()
+'bar'
+&gt;&gt;&gt; Foo2().say_foo()
+foo
+</pre>
+<p>In all these cases, the behaviors of the class (Foo, Foo2) are not directly written as code, but are instead created by calling functions at runtime, with dynamic arguments. And it should be emphasized that it is not merely the instances that are so dynamically created, but the classes themselves.</p>
+<p>Metaclasses: a solution looking for a problem?
+Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). -- Python Guru Tim Peters</p>
+<p>Methods (of classes), like plain functions, can return objects. So in that sense it is obvious that class factories can be classes just as easily as they can be functions. In particular, Python 2.2+ provides a special class called type that is just such a class factory. Of course, readers will recognize type() as a less ambitious built-in function of older Python versions -- fortunately, the behaviors of the old type() function are maintained by the type class (in other words, type(obj) returns the type/class of the object obj). The new type class works as a class factory in just the same way that the function new.classobj long has:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; X = type('X',(),{'foo':lambda self:'foo'})
+&gt;&gt;&gt; X, X().foo()
+(&lt;class '__main__.X'&gt;, 'foo')
+</pre>
+<p>But since type is now a (meta)class, you are free to subclass it:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ChattyType(type):
+... def __new__(cls, name, bases, dct):
+... print &quot;Allocating memory for class&quot;, name
+... return type.__new__(cls, name, bases, dct)
+... def __init__(cls, name, bases, dct):
+... print &quot;Init'ing (configuring) class&quot;, name
+... super(ChattyType, cls).__init__(name, bases, dct)
+...
+&gt;&gt;&gt; X = ChattyType('X',(),{'foo':lambda self:'foo'})
+Allocating memory for class X
+Init'ing (configuring) class X
+&gt;&gt;&gt; X, X().foo()
+(&lt;class '__main__.X'&gt;, 'foo')
+</pre>
+<p>The magic methods .__new__() and .__init__() are special, but in conceptually the same way they are for any other class. The .__init__() method lets you configure the created object; the .__new__() method lets you customize its allocation. The latter, of course, is not widely used, but exists for every Python 2.2 new-style class (usually inherited but not overridden).</p>
+<p>There is one feature of type descendents to be careful about; it catches everyone who first plays with metaclasses. The first argument to methods is conventionally called cls rather than self, because the methods operate on the produced class, not the metaclass. Actually, there is nothing special about this; all methods attach to their instances, and the instance of a metaclass is a class. A non-special name makes this more obvious:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Printable(type):
+... def whoami(cls): print &quot;I am a&quot;, cls.__name__
+...
+&gt;&gt;&gt; Foo = Printable('Foo',(),{})
+&gt;&gt;&gt; Foo.whoami()
+I am a Foo
+&gt;&gt;&gt; Printable.whoami()
+Traceback (most recent call last):
+TypeError: unbound method whoami() [...]
+</pre>
+<p>All this surprisingly non-remarkable machinery comes with some syntax sugar that both makes working with metaclasses easier, and confuses new users. There are several elements to the extra syntax. The resolution order of these new variations is tricky though. Classes can inherit metaclasses from their ancestors -- notice that this is not the same thing as having metaclasses as ancestors (another common confusion). For old-style classes, defining a global _metaclass_ variable can force a custom metaclass to be used. But most of the time, and the safest approach, is to set a _metaclass_ class attribute for a class that wants to be created via a custom metaclass. You must set the variable in the class definition itself since the metaclass is not used if the attribute is set later (after the class object has already been created). For example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Bar:
+... __metaclass__ = Printable
+... def foomethod(self): print 'foo'
+...
+&gt;&gt;&gt; Bar.whoami()
+I am a Bar
+&gt;&gt;&gt; Bar().foomethod()
+foo
+</pre>
+<p>Solving problems with magic
+So far, we have seen the basics of metaclasses. But putting metaclasses to work is more subtle. The challenge with utilizing metaclasses is that in typical OOP design, classes do not really do much. The inheritance structure of classes is useful to encapsulate and package data and methods, but it is typically instances that one works with in the concrete.</p>
+<p>There are two general categories of programming tasks where we think metaclasses are genuinely valuable.</p>
+<p>The first, and probably more common category is where you do not know at design time exactly what a class needs to do. Obviously, you will have some idea about it, but some particular detail might depend on information that is not available until later. &quot;Later&quot; itself can be of two sorts: (a) When a library module is used by an application; (b) At runtime when some situation exists. This category is close to what is often called &quot;Aspect-Oriented Programming&quot; (AOP). We'll show what we think is an elegant example:</p>
+<p>% cat dump.py
+#!/usr/bin/python
+import sys
+if len(sys.argv) &gt; 2:</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 153)</p>
+Unexpected indentation.</div>
+<blockquote>
+module, metaklass = sys.argv[1:3]
+m = __import__(module, globals(), locals(), [metaklass])
+__metaclass__ = getattr(m, metaklass)</blockquote>
+<dl class="docutils">
+<dt>class Data:</dt>
+<dd><dl class="first docutils">
+<dt>def __init__(self):</dt>
+<dd>self.num = 38
+self.lst = ['a','b','c']
+self.str = 'spam'</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 162)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p class="last">dumps = lambda self: <cite>self</cite>
+__str__ = lambda self: self.dumps()</p>
+</dd>
+</dl>
+<p>data = Data()
+print data</p>
+<p>% dump.py
+&lt;__main__.Data instance at 1686a0&gt;</p>
+<p>As you would expect, this application prints out a rather generic description of the data object (a conventional instance object). But if runtime arguments are passed to the application, we can get a rather different result:</p>
+<p>% dump.py gnosis.magic MetaXMLPickler
+&lt;?xml version=&quot;1.0&quot;?&gt;
+&lt;!DOCTYPE PyObject SYSTEM &quot;PyObjects.dtd&quot;&gt;
+&lt;PyObject module=&quot;__main__&quot; class=&quot;Data&quot; id=&quot;720748&quot;&gt;
+&lt;attr name=&quot;lst&quot; type=&quot;list&quot; id=&quot;980012&quot; &gt;</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 180)</p>
+Unexpected indentation.</div>
+<blockquote>
+&lt;item type=&quot;string&quot; value=&quot;a&quot; /&gt;
+&lt;item type=&quot;string&quot; value=&quot;b&quot; /&gt;
+&lt;item type=&quot;string&quot; value=&quot;c&quot; /&gt;</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 183)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>&lt;/attr&gt;
+&lt;attr name=&quot;num&quot; type=&quot;numeric&quot; value=&quot;38&quot; /&gt;
+&lt;attr name=&quot;str&quot; type=&quot;string&quot; value=&quot;spam&quot; /&gt;
+&lt;/PyObject&gt;</p>
+<p>The particular example uses the serialization style of gnosis.xml.pickle, but the most current gnosis.magic package also contains metaclass serializers MetaYamlDump, MetaPyPickler, and MetaPrettyPrint. Moreover, a user of the dump.py &quot;application&quot; can impose the use of any &quot;MetaPickler&quot; desired, from any Python package that defines one. Writing an appropriate metaclass for this purpose will look something like:</p>
+<dl class="docutils">
+<dt>class MetaPickler(type):</dt>
+<dd><p class="first">&quot;Metaclass for gnosis.xml.pickle serialization&quot;
+def __init__(cls, name, bases, dict):</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 195)</p>
+Unexpected indentation.</div>
+<blockquote class="last">
+from gnosis.xml.pickle import dumps
+super(MetaPickler, cls).__init__(name, bases, dict)
+setattr(cls, 'dumps', dumps)</blockquote>
+</dd>
+</dl>
+<p>The remarkable achievement of this arrangement is that the application programmer need have no knowledge about what serialization will be used -- nor even whether serialization or some other cross-sectional capability will be added at the command-line.</p>
+<p>Perhaps the most common use of metaclasses is similar to that of MetaPicklers: adding, deleting, renaming, or substituting methods for those defined in the produced class. In our example, a &quot;native&quot; Data.dump() method is replaced by a different one from outside the application, at the time the class Data is created (and therefore in every subsequent instance).</p>
+<p>More ways to solve problems with magic
+There is a programming niche where classes are often more important than instances. For example, declarative mini-languages are Python libraries whose program logic is expressed directly in class declarations. David examines them in his article &quot;Create declarative mini-languages&quot;. In such cases, using metaclasses to affect the process of class creation can be quite powerful.</p>
+<p>One class-based declarative framework is gnosis.xml.validity. Under this framework, you declare a number of &quot;validity classes&quot; that express a set of constraints about valid XML documents. These declarations are very close to those contained in DTDs. For example, a &quot;dissertation&quot; document can be configured with the code:</p>
+<p>from gnosis.xml.validity import *
+class figure(EMPTY): pass
+class _mixedpara(Or): _disjoins = (PCDATA, figure)
+class paragraph(Some): _type = _mixedpara
+class title(PCDATA): pass
+class _paras(Some): _type = paragraph
+class chapter(Seq): _order = (title, _paras)
+class dissertation(Some): _type = chapter</p>
+<p>If you try to instantiate the dissertation class without the right component subelements, a descriptive exception is raised; likewise for each of the subelements. The proper subelements will be generated from simpler arguments when there is only one unambiguous way of &quot;lifting&quot; the arguments to the correct types.</p>
+<p>Even though validity classes are often (informally) based on a pre-existing DTD, instances of these classes print themselves as unadorned XML document fragments, for example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from simple_diss import *
+&gt;&gt;&gt; ch = LiftSeq(chapter, ('It Starts','When it began'))
+&gt;&gt;&gt; print ch
+&lt;chapter&gt;&lt;title&gt;It Starts&lt;/title&gt;
+&lt;paragraph&gt;When it began&lt;/paragraph&gt;
+&lt;/chapter&gt;
+</pre>
+<p>By using a metaclass to create the validity classes, we can generate a DTD out of the class declarations themselves (and add an extra method to the classes while we do it):</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from gnosis.magic import DTDGenerator, \
+... import_with_metaclass, \
+... from_import
+&gt;&gt;&gt; d = import_with_metaclass('simple_diss',DTDGenerator)
+&gt;&gt;&gt; from_import(d,'**')
+&gt;&gt;&gt; ch = LiftSeq(chapter, ('It Starts','When it began'))
+&gt;&gt;&gt; print ch.with_internal_subset()
+&lt;?xml version='1.0'?&gt;
+&lt;!DOCTYPE chapter [
+&lt;!ELEMENT figure EMPTY&gt;
+&lt;!ELEMENT dissertation (chapter)+&gt;
+&lt;!ELEMENT chapter (title,paragraph+)&gt;
+&lt;!ELEMENT title (#PCDATA)&gt;
+&lt;!ELEMENT paragraph ((#PCDATA|figure))+&gt;
+]&gt;
+&lt;chapter&gt;&lt;title&gt;It Starts&lt;/title&gt;
+&lt;paragraph&gt;When it began&lt;/paragraph&gt;
+&lt;/chapter&gt;
+</pre>
+<p>The package gnosis.xml.validity knows nothing about DTDs and internal subsets. Those concepts and capabilities are introduced entirely by the metaclass DTDGenerator, without any change made to either gnosis.xml.validity or simple_diss.py. DTDGenerator does not substitute its own .__str__() method into classes it produces -- you can still print the unadorned XML fragment -- but it a metaclass could easily modify such magic methods.</p>
+<p>Meta conveniences
+The package gnosis.magic contains several utilities for working with
+metaclasses, as well as some sample metaclasses you can use in
+aspect-oriented programming. The most important of these utilities
+is import_with_metaclass(). This function, utilized in the above
+example, lets you import a third-party module, but create all the
+module classes using a custom metaclass rather than type. Whatever
+new capability you might want to impose on that third-party module
+can be defined in a metaclass that you create (or get from somewhere
+else altogether). gnosis.magic contains some pluggable serialization
+metaclasses; some other package might contain tracing capabilities,
+or object persistence, or exception logging, or something else.</p>
+<p>The import_with_metclass() function illustrates several qualities
+of metaclass programming:</p>
+<dl class="docutils">
+<dt>def import_with_metaclass(modname, metaklass):</dt>
+<dd><p class="first">&quot;Module importer substituting custom metaclass&quot;
+class Meta(object): __metaclass__ = metaklass
+dct = {'__module__':modname}
+mod = __import__(modname)
+for key, val in mod.__dict__.items():</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 281)</p>
+Unexpected indentation.</div>
+<blockquote>
+<dl class="docutils">
+<dt>if inspect.isclass(val):</dt>
+<dd>setattr(mod, key, type(key,(val,Meta),dct))</dd>
+</dl>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 283)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p class="last">return mod</p>
+</dd>
+</dl>
+<p>One notable style in this function is that an ordinary class Meta is
+produced using the specified metaclass. But once Meta is added as an
+ancestor, its descendent is also produced using the custom metaclass.
+In principle, a class like Meta could carry with it both a metaclass
+producer and a set of inheritable methods -- the two aspects of its
+bequest are orthogonal.</p>
+<p>Resources</p>
+<blockquote>
+<ul class="simple">
+<li>A useful book on metaclasses is Putting Metaclasses to Work by</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 295)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Ira R. Forman and Scott Danforth (Addison-Wesley; 1999).</p>
+<blockquote>
+<ul class="simple">
+<li>For metaclasses in Python specifically, Guido van Rossum's</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 298)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>essay, &quot;Unifying types and classes in Python 2.2&quot; is useful as well.</p>
+<blockquote>
+<ul>
+<li><dl class="first docutils">
+<dt>Also by David on developerWorks, read:</dt>
+<dd><p class="first last">o &quot;Guide to Python introspection&quot;
+o &quot;Create declarative mini-languages&quot;
+o &quot;XML Matters: Enforcing validity with the gnosis.xml.</p>
+</dd>
+</dl>
+</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 304)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>validity library&quot;</p>
+<blockquote>
+<ul class="simple">
+<li>Don't know Tim Peters? You should! Begin with Tim's wiki page</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 308)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>and end with reading <a class="reference" href="news:comp.lang.python">news:comp.lang.python</a> more regularly.</p>
+<blockquote>
+<ul class="simple">
+<li>New to AOP? You may find this &quot;Introduction to Aspect-Oriented</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 311)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Programming&quot; (PDF) by Ken Wing Kuen Lee of the Hong Kong University
+of Science and Technology interesting.</p>
+<blockquote>
+<ul class="simple">
+<li>Gregor Kiczales and his team at Xerox PARC coined the term</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 315)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>aspect-oriented programming in the 1990s and championed it as a
+way to allow software programmers to spend more time writing code
+and less time correcting it.</p>
+<blockquote>
+<ul class="simple">
+<li>&quot;Connections between Demeter/Adaptive Programming and</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 320)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Aspect-Oriented Programming (AOP)&quot; by Karl J. Lieberherr also
+describes AOP.</p>
+<blockquote>
+<ul class="simple">
+<li>You'll also find subject-oriented programming interesting. As</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 324)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>described by the folks at IBM Research, it's essentially the same
+thing as aspect-oriented programming.</p>
+<blockquote>
+<ul class="simple">
+<li>Find and download the Gnosis utils, mentioned several times in</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 328)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>this article, at David's site.</p>
+<blockquote>
+<ul class="simple">
+<li>Find more resources for Linux developers in the developerWorks</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 331)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Linux zone.</p>
+<p>About the authors
+David Mertz David Mertz thought his brain would melt when he wrote
+about continuations or semi-coroutines, but he put the gooey mess
+back in his skull cavity and moved on to metaclasses. David may be
+reached at <a class="reference" href="mailto:mertz&#64;gnosis.cx">mertz&#64;gnosis.cx</a>; his life pored over at his personal
+Web page. Suggestions and recommendations on this, past, or future
+columns are welcome. Learn about his forthcoming book, Text
+Processing in Python.</p>
+<p>Michele Simionato Michele Simionato is a plain, ordinary, theoretical
+physicist who was driven to Python by a quantum fluctuation that could
+well have passed without consequences had he not met David Mertz. He
+will let his readers judge the final outcome. Michele can be reached
+at <a class="reference" href="mailto:mis6+&#64;pitt.edu">mis6+&#64;pitt.edu</a>.</p>
+</div>
+</body>
+</html>
diff --git a/pypers/meta/meta1.txt b/pypers/meta/meta1.txt
new file mode 100755
index 0000000..f2d041f
--- /dev/null
+++ b/pypers/meta/meta1.txt
@@ -0,0 +1,469 @@
+Metaclass programming in Python
+--------------------------------
+
+.. contents:
+
+Pushing object-oriented programming to the next level
+
+Level: Introductory
+
+David Mertz, Ph.D. (mertz@gnosis.cx), Developer, Gnosis Software, Inc.
+Michele Simionato, Ph.D. (mis6+@pitt.edu), Physicist, University of Pittsburgh
+
+February 26, 2003
+
+Most readers are already familiar with the concepts of object-oriented
+programming: inheritance, encapsulation, polymorphism. But the creation
+of objects of a given class, with certain parents, is usually thought of
+as a "just so" operation. It turns out that a number of new programming
+constructs become either easier, or possible at all, when you can
+customize the process of object creation. Metaclasses enable certain
+types of "aspect-oriented programming," for example, you can enhance
+classes with features like tracing capabilities, object persistence,
+exception logging, and more.
+
+Review of object-oriented programming Let's start with a 30-second
+review of just what OOP is. In an object-oriented programming
+language, you can define classes, whose purpose is to bundle together
+related data and behaviors. These classes can inherit some or all of
+their qualities from their parents, but they can also define
+attributes (data) or methods (behaviors) of their own. At the end of
+the process, classes generally act as templates for the creation of
+instances (at times also called simply objects). Different instances
+of the same class will typically have different data, but it will come
+in the same shape -- for example, the Employee objects bob and jane
+both have a .salary and a .room_number, but not the same room and
+salary as each other.
+
+Some OOP languages, including Python, allow for objects to be
+introspective (also called reflective). That is, an introspective
+object is able to describe itself: What class does the instance belong
+to? What ancestors does that class have? What methods and attributes
+are available to the object? Introspection lets a function or method
+that handles objects make decisions based on what kind of object it is
+passed. Even without introspection, functions frequently branch based
+on instance data -- for example, the route to jane.room_number differs
+from that to bob.room_number because they are in different rooms. With
+introspection, you can also safely calculate the bonus jane gets,
+while skipping the calculation for bob, for example, because jane has
+a .profit_share attribute, or because bob is an instance of the
+subclass Hourly(Employee).
+
+A metaprogramming rejoinder The basic OOP system sketched above is
+quite powerful. But there is one element brushed over in the
+description: in Python (and other languages), classes are themselves
+objects that can be passed around and introspected. Since objects, as
+stated, are produced using classes as templates, what acts as a
+template for producing classes? The answer, of course, is metaclasses.
+
+Python has always had metaclasses. But the machinery involved in
+metaclasses became much better exposed with Python 2.2. Specifically,
+with version 2.2, Python stopped being a language with just one
+special (mostly hidden) metaclass that created every class object. Now
+programmers can subclass the aboriginal metaclass type and even
+dynamically generate classes with varying metaclasses. Of course, just
+because you can manipulate metaclasses in Python 2.2, that does not
+explain why you might want to.
+
+Moreover, you do not need to use custom metaclasses to manipulate the
+production of classes. A slightly less brain-melting concept is a
+class factory: An ordinary function can return a class that was
+dynamically created within the function body. In traditional Python
+syntax, you can write:
+
+
+Python 1.5.2 (#0, Jun 27 1999, 11:23:01) [...]
+Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+>>> def class_with_method(func):
+... class klass: pass
+... setattr(klass, func.__name__, func)
+... return klass
+...
+>>> def say_foo(self): print 'foo'
+...
+>>> Foo = class_with_method(say_foo)
+>>> foo = Foo()
+>>> foo.say_foo()
+foo
+
+The factory function class_with_method() dynamically creates and
+returns a class that contains the method/function passed into the
+factory. The class itself is manipulated within the function body
+before being returned. The new module provides a more concise
+spelling, but without the same options for custom code within the body
+of the class factory, for example:
+
+
+
+>>> from new import classobj
+>>> Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
+>>> Foo2().bar()
+'bar'
+>>> Foo2().say_foo()
+foo
+
+In all these cases, the behaviors of the class (Foo, Foo2) are not
+directly written as code, but are instead created by calling functions
+at runtime, with dynamic arguments. And it should be emphasized that
+it is not merely the instances that are so dynamically created, but
+the classes themselves.
+
+Metaclasses: a solution looking for a problem?
+
+Metaclasses are deeper
+magic than 99% of users should ever worry about. If you wonder whether
+you need them, you don't (the people who actually need them know with
+certainty that they need them, and don't need an explanation about
+why). -- Python Guru Tim Peters
+
+Methods (of classes), like plain functions, can return objects. So in
+that sense it is obvious that class factories can be classes just as
+easily as they can be functions. In particular, Python 2.2+ provides a
+special class called type that is just such a class factory. Of
+course, readers will recognize type() as a less ambitious built-in
+function of older Python versions -- fortunately, the behaviors of the
+old type() function are maintained by the type class (in other words,
+type(obj) returns the type/class of the object obj). The new type
+class works as a class factory in just the same way that the function
+new.classobj long has:
+
+
+
+>>> X = type('X',(),{'foo':lambda self:'foo'})
+>>> X, X().foo()
+(<class '__main__.X'>, 'foo')
+
+But since type is now a (meta)class, you are free to subclass it:
+
+
+
+>>> class ChattyType(type):
+... def __new__(cls, name, bases, dct):
+... print "Allocating memory for class", name
+... return type.__new__(cls, name, bases, dct)
+... def __init__(cls, name, bases, dct):
+... print "Init'ing (configuring) class", name
+... super(ChattyType, cls).__init__(name, bases, dct)
+...
+>>> X = ChattyType('X',(),{'foo':lambda self:'foo'})
+Allocating memory for class X
+Init'ing (configuring) class X
+>>> X, X().foo()
+(<class '__main__.X'>, 'foo')
+
+The magic methods .__new__() and .__init__() are special, but in
+conceptually the same way they are for any other class. The
+.__init__() method lets you configure the created object; the
+.__new__() method lets you customize its allocation. The latter, of
+course, is not widely used, but exists for every Python 2.2 new-style
+class (usually inherited but not overridden).
+
+There is one feature of type descendents to be careful about; it
+catches everyone who first plays with metaclasses. The first argument
+to methods is conventionally called cls rather than self, because the
+methods operate on the produced class, not the metaclass. Actually,
+there is nothing special about this; all methods attach to their
+instances, and the instance of a metaclass is a class. A non-special
+name makes this more obvious:
+
+
+
+>>> class Printable(type):
+... def whoami(cls): print "I am a", cls.__name__
+...
+>>> Foo = Printable('Foo',(),{})
+>>> Foo.whoami()
+I am a Foo
+>>> Printable.whoami()
+Traceback (most recent call last):
+TypeError: unbound method whoami() [...]
+
+All this surprisingly non-remarkable machinery comes with some syntax
+sugar that both makes working with metaclasses easier, and confuses
+new users. There are several elements to the extra syntax. The
+resolution order of these new variations is tricky though. Classes can
+inherit metaclasses from their ancestors -- notice that this is not
+the same thing as having metaclasses as ancestors (another common
+confusion). For old-style classes, defining a global _metaclass_
+variable can force a custom metaclass to be used. But most of the
+time, and the safest approach, is to set a _metaclass_ class attribute
+for a class that wants to be created via a custom metaclass. You must
+set the variable in the class definition itself since the metaclass is
+not used if the attribute is set later (after the class object has
+already been created). For example:
+
+
+
+>>> class Bar:
+... __metaclass__ = Printable
+... def foomethod(self): print 'foo'
+...
+>>> Bar.whoami()
+I am a Bar
+>>> Bar().foomethod()
+foo
+
+Solving problems with magic
+
+So far, we have seen the basics of metaclasses. But putting
+metaclasses to work is more subtle. The challenge with utilizing
+metaclasses is that in typical OOP design, classes do not really do
+much. The inheritance structure of classes is useful to encapsulate
+and package data and methods, but it is typically instances that one
+works with in the concrete.
+
+There are two general categories of programming tasks where we think
+metaclasses are genuinely valuable.
+
+The first, and probably more common category is where you do not know
+at design time exactly what a class needs to do. Obviously, you will
+have some idea about it, but some particular detail might depend on
+information that is not available until later. "Later" itself can be
+of two sorts: (a) When a library module is used by an application; (b)
+At runtime when some situation exists. This category is close to what
+is often called "Aspect-Oriented Programming" (AOP). We'll show what
+we think is an elegant example:
+
+
+
+% cat dump.py
+#!/usr/bin/python
+import sys
+if len(sys.argv) > 2:
+ module, metaklass = sys.argv[1:3]
+ m = __import__(module, globals(), locals(), [metaklass])
+ __metaclass__ = getattr(m, metaklass)
+
+class Data:
+ def __init__(self):
+ self.num = 38
+ self.lst = ['a','b','c']
+ self.str = 'spam'
+ dumps = lambda self: `self`
+ __str__ = lambda self: self.dumps()
+
+data = Data()
+print data
+
+% dump.py
+<__main__.Data instance at 1686a0>
+
+As you would expect, this application prints out a rather generic description of the data object (a conventional instance object). But if runtime arguments are passed to the application, we can get a rather different result:
+
+
+
+% dump.py gnosis.magic MetaXMLPickler
+<?xml version="1.0"?>
+<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
+<PyObject module="__main__" class="Data" id="720748">
+<attr name="lst" type="list" id="980012" >
+ <item type="string" value="a" />
+ <item type="string" value="b" />
+ <item type="string" value="c" />
+</attr>
+<attr name="num" type="numeric" value="38" />
+<attr name="str" type="string" value="spam" />
+</PyObject>
+
+The particular example uses the serialization style of
+gnosis.xml.pickle, but the most current gnosis.magic package also
+contains metaclass serializers MetaYamlDump, MetaPyPickler, and
+MetaPrettyPrint. Moreover, a user of the dump.py "application" can
+impose the use of any "MetaPickler" desired, from any Python package
+that defines one. Writing an appropriate metaclass for this purpose
+will look something like:
+
+
+
+class MetaPickler(type):
+ "Metaclass for gnosis.xml.pickle serialization"
+ def __init__(cls, name, bases, dict):
+ from gnosis.xml.pickle import dumps
+ super(MetaPickler, cls).__init__(name, bases, dict)
+ setattr(cls, 'dumps', dumps)
+
+The remarkable achievement of this arrangement is that the application
+programmer need have no knowledge about what serialization will be
+used -- nor even whether serialization or some other cross-sectional
+capability will be added at the command-line.
+
+Perhaps the most common use of metaclasses is similar to that of
+MetaPicklers: adding, deleting, renaming, or substituting methods for
+those defined in the produced class. In our example, a "native"
+Data.dump() method is replaced by a different one from outside the
+application, at the time the class Data is created (and therefore in
+every subsequent instance).
+
+More ways to solve problems with magic
+
+There is a programming niche where classes are often more important
+than instances. For example, declarative mini-languages are Python
+libraries whose program logic is expressed directly in class
+declarations. David examines them in his article "Create declarative
+mini-languages". In such cases, using metaclasses to affect the
+process of class creation can be quite powerful.
+
+One class-based declarative framework is gnosis.xml.validity. Under
+this framework, you declare a number of "validity classes" that
+express a set of constraints about valid XML documents. These
+declarations are very close to those contained in DTDs. For example, a
+"dissertation" document can be configured with the code:
+
+
+
+from gnosis.xml.validity import *
+class figure(EMPTY): pass
+class _mixedpara(Or): _disjoins = (PCDATA, figure)
+class paragraph(Some): _type = _mixedpara
+class title(PCDATA): pass
+class _paras(Some): _type = paragraph
+class chapter(Seq): _order = (title, _paras)
+class dissertation(Some): _type = chapter
+
+If you try to instantiate the dissertation class without the right
+component subelements, a descriptive exception is raised; likewise for
+each of the subelements. The proper subelements will be generated from
+simpler arguments when there is only one unambiguous way of "lifting"
+the arguments to the correct types.
+
+Even though validity classes are often (informally) based on a
+pre-existing DTD, instances of these classes print themselves as
+unadorned XML document fragments, for example:
+
+
+
+>>> from simple_diss import *
+>>> ch = LiftSeq(chapter, ('It Starts','When it began'))
+>>> print ch
+<chapter><title>It Starts</title>
+<paragraph>When it began</paragraph>
+</chapter>
+
+By using a metaclass to create the validity classes, we can generate a
+DTD out of the class declarations themselves (and add an extra method
+to the classes while we do it):
+
+
+
+>>> from gnosis.magic import DTDGenerator, \
+... import_with_metaclass, \
+... from_import
+>>> d = import_with_metaclass('simple_diss',DTDGenerator)
+>>> from_import(d,'**')
+>>> ch = LiftSeq(chapter, ('It Starts','When it began'))
+>>> print ch.with_internal_subset()
+<?xml version='1.0'?>
+<!DOCTYPE chapter [
+<!ELEMENT figure EMPTY>
+<!ELEMENT dissertation (chapter)+>
+<!ELEMENT chapter (title,paragraph+)>
+<!ELEMENT title (#PCDATA)>
+<!ELEMENT paragraph ((#PCDATA|figure))+>
+]>
+<chapter><title>It Starts</title>
+<paragraph>When it began</paragraph>
+</chapter>
+
+The package gnosis.xml.validity knows nothing about DTDs and internal
+subsets. Those concepts and capabilities are introduced entirely by
+the metaclass DTDGenerator, without any change made to either
+gnosis.xml.validity or simple_diss.py. DTDGenerator does not
+substitute its own .__str__() method into classes it produces -- you
+can still print the unadorned XML fragment -- but it a metaclass could
+easily modify such magic methods.
+
+Meta conveniences
+
+The package gnosis.magic contains several utilities for working with
+metaclasses, as well as some sample metaclasses you can use in
+aspect-oriented programming. The most important of these utilities
+is import_with_metaclass(). This function, utilized in the above
+example, lets you import a third-party module, but create all the
+module classes using a custom metaclass rather than type. Whatever
+new capability you might want to impose on that third-party module
+can be defined in a metaclass that you create (or get from somewhere
+else altogether). gnosis.magic contains some pluggable serialization
+metaclasses; some other package might contain tracing capabilities,
+or object persistence, or exception logging, or something else.
+
+The import_with_metclass() function illustrates several qualities
+of metaclass programming::
+
+
+
+def import_with_metaclass(modname, metaklass):
+ "Module importer substituting custom metaclass"
+ class Meta(object): __metaclass__ = metaklass
+ dct = {'__module__':modname}
+ mod = __import__(modname)
+ for key, val in mod.__dict__.items():
+ if inspect.isclass(val):
+ setattr(mod, key, type(key,(val,Meta),dct))
+ return mod
+
+One notable style in this function is that an ordinary class Meta is
+produced using the specified metaclass. But once Meta is added as an
+ancestor, its descendent is also produced using the custom metaclass.
+In principle, a class like Meta could carry with it both a metaclass
+producer and a set of inheritable methods -- the two aspects of its
+bequest are orthogonal.
+
+Resources
+
+ * A useful book on metaclasses is Putting Metaclasses to Work by
+Ira R. Forman and Scott Danforth (Addison-Wesley; 1999).
+
+ * For metaclasses in Python specifically, Guido van Rossum's
+essay, "Unifying types and classes in Python 2.2" is useful as well.
+
+ * Also by David on developerWorks, read:
+ o "Guide to Python introspection"
+ o "Create declarative mini-languages"
+ o "XML Matters: Enforcing validity with the gnosis.xml.
+validity library"
+
+
+ * Don't know Tim Peters? You should! Begin with Tim's wiki page
+and end with reading news:comp.lang.python more regularly.
+
+ * New to AOP? You may find this "Introduction to Aspect-Oriented
+Programming" (PDF) by Ken Wing Kuen Lee of the Hong Kong University
+of Science and Technology interesting.
+
+ * Gregor Kiczales and his team at Xerox PARC coined the term
+aspect-oriented programming in the 1990s and championed it as a
+way to allow software programmers to spend more time writing code
+and less time correcting it.
+
+ * "Connections between Demeter/Adaptive Programming and
+Aspect-Oriented Programming (AOP)" by Karl J. Lieberherr also
+describes AOP.
+
+ * You'll also find subject-oriented programming interesting. As
+described by the folks at IBM Research, it's essentially the same
+thing as aspect-oriented programming.
+
+ * Find and download the Gnosis utils, mentioned several times in
+this article, at David's site.
+
+ * Find more resources for Linux developers in the developerWorks
+Linux zone.
+
+About the authors
+
+David Mertz
+ David Mertz thought his brain would melt when he wrote
+ about continuations or semi-coroutines, but he put the gooey mess
+ back in his skull cavity and moved on to metaclasses. David may be
+ reached at mertz@gnosis.cx; his life pored over at his personal
+ Web page. Suggestions and recommendations on this, past, or future
+ columns are welcome. Learn about his forthcoming book, Text
+ Processing in Python.
+
+
+Michele Simionato
+ Michele Simionato is a plain, ordinary, theoretical
+ physicist who was driven to Python by a quantum fluctuation that could
+ well have passed without consequences had he not met David Mertz. He
+ will let his readers judge the final outcome. Michele can be reached
+ at mis6+@pitt.edu.
diff --git a/pypers/meta/meta2.html b/pypers/meta/meta2.html
new file mode 100755
index 0000000..e97bd7d
--- /dev/null
+++ b/pypers/meta/meta2.html
@@ -0,0 +1,528 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Metaclass Programming in Python, Part 2: Understanding the Arcana of Inheritance and Instance Creation</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="metaclass-programming-in-python-part-2-understanding-the-arcana-of-inheritance-and-instance-creation">
+<h1 class="title">Metaclass Programming in Python, Part 2: Understanding the Arcana of Inheritance and Instance Creation</h1>
+<blockquote>
+<table class="field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">author:</th><td class="field-body">Michele Simionato, Ph.D., University of Pittsburgh</td>
+</tr>
+<tr class="field"><th class="field-name">author:</th><td class="field-body">David Mertz, Ph.D., Gnosis Software, Inc.</td>
+</tr>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">June 2003</td>
+</tr>
+<tr class="field"><th class="field-name">Abstract:</th><td class="field-body">Our initial developerWorks article on metaclass programming
+prompted quite a bit of feedback, some of it from perplexed
+readers still trying to grasp the subtleties of Python
+metaclasses. This article revisits the working of
+metaclasses and their relation to other OOP concepts. We
+contrast class instantiation with inheritance, distinguish
+classmethods and metamethods, and explain and solve metaclass
+conflicts.</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<div class="section" id="metaclasses-and-their-discontents">
+<h1><a name="metaclasses-and-their-discontents">METACLASSES AND THEIR DISCONTENTS</a></h1>
+<blockquote>
+<p>In our earlier article on metaclass programming in Python, we
+introduced the concept of metaclasses, showed some of their power,
+and demonstrated their use in solving problems such as dynamic
+customization of classes and libraries at run-time.</p>
+<p>That article has proved quite popular, but there were ellisions in
+our condensed initial summary. Certain details in the use of
+metaclasses merit futher explanation. Based on the feedback of our
+readers and on discussions in comp.lang.python, we decided to
+address some of those trickier point in this second article. In
+particular, we think the following points are important for any
+programmer wanting to master metaclasses:</p>
+<p>(1) Users must understand the differences and interactions between
+metaclass programming and traditional object-oriented programming
+(under both single and multiple inheritance).</p>
+<p>(2) Python 2.2 added the built-in functions 'staticmethod()' and
+'classmethod()' to create methods that do not require an instance
+object during invocation. To an extent, -classmethods- overlap in
+purpose with (meta)methods defined in metaclasses. But the precise
+similarities and differences have also generated confusion in the
+mind of many programmers.</p>
+<p>(3) User should understand the cause and the resolution of metatype
+conflicts. This becomes essential when you want to use more than
+one custom metaclass. We explain the concept of -composition- of
+metaclasses.</p>
+</blockquote>
+</div>
+<div class="section" id="instantiation-versus-inheritance">
+<h1><a name="instantiation-versus-inheritance">INSTANTIATION VERSUS INHERITANCE</a></h1>
+<blockquote>
+<p>Many programmers are confused about the difference between a
+metaclass and a base class. At the superficial level of &quot;determing&quot;
+a class, both look similar. But once you look any deeper, the
+concepts drift apart.</p>
+<p>Before presenting some examples, it is worth being precise about
+some nomenclature. An -instance- is a Python object that was
+&quot;manufactured&quot; by a class; the class acts as a sort of template for
+the instance. Every instance is an instance of -exactly one- class
+(but a class might have multiple instances). What we often call an
+instance object--or perhaps a &quot;simple instance&quot;--is &quot;final&quot; in the
+sense it cannot act as a template for other objects (but it might
+still be a -factory- or a -delegate-, which serve overlapping
+purposes).</p>
+<p>Some instance objects are themselves classes; and all classes are
+instances of a corresponding -metaclass-. Even classes only come
+into existence through the instantiation mechanism. Usually classes
+are instances of the built-in, standard metaclass 'type'; it is only
+when we specify metaclasses other than 'type' that we need to think
+about metaclass programming. We also call the class used to
+instantiate an object the -type- of that object.</p>
+<p>Running -orthogonal- to the idea of instantiation is the notion of
+inheritance. Here, a class can have one or multiple parents, not
+just one unique type. And parents can have parents, creating a
+transitive subclass relation, conveniently accessible with the
+built-in function 'issubclass()'. For example, if we define a few
+classes and an istance:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A(object): a1 = &quot;A&quot;
+...
+&gt;&gt;&gt; class B(object): a2 = &quot;B&quot;
+...
+&gt;&gt;&gt; class C(A,B): a3 = &quot;C(A,B)&quot;
+...
+&gt;&gt;&gt; class D(C): a4 = &quot;D(C)&quot;
+...
+&gt;&gt;&gt; d = D()
+&gt;&gt;&gt; d.a5 = &quot;instance d of D&quot;
+</pre>
+</blockquote>
+<p>Then we can test the relations:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; issubclass(D,C)
+True
+&gt;&gt;&gt; issubclass(D,A)
+True
+&gt;&gt;&gt; issubclass(A,B)
+False
+&gt;&gt;&gt; issubclass(d,D)
+[...]
+TypeError: issubclass() arg 1 must be a class
+</pre>
+</blockquote>
+<p>The interesting question now--the one necessary for understanding
+the contrast between superclasses and metaclasses--is how an
+attribute like 'd.attr' is resolved. For simplicity, we discuss
+only the standard look-rule, not the fallback to '.__getattr__()'.
+The first step in such resolution is to look in 'd.__dict__' for the
+name 'attr'. If found, that's that; but if not, something fancy
+needs to happen, e.g.:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; d.__dict__, d.a5, d.a1
+({'a5': 'instance d'}, 'instance d', 'A')
+</pre>
+</blockquote>
+<p>The trick to finding an attribute that isn't attached to an instance
+is to look for it in the class of the instance, then after that in
+all the superclasses. The order in which superclasses are checked
+is called the -method resolution order- for the class. You can look
+at it with the (meta)method '.mro()' (but only from class objects):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; [k.__name__ for k in d.__class__.mro()]
+['D', 'C', 'A', 'B', 'object']
+</pre>
+</blockquote>
+<p>In other words, the access to 'd.attr' first looks in 'd.__dict__',
+then in 'D.__dict__', 'C.__dict__', 'A.__dict__', 'B.__dict__', and
+finally in 'object.__dict__'. If the name is not found in any of
+those places, an 'AttributeError' is raised.</p>
+<p>Notice that metaclasses were never mentioned in the lookup procedure.</p>
+</blockquote>
+</div>
+<div class="section" id="metaclasses-versus-ancestors">
+<h1><a name="metaclasses-versus-ancestors">METACLASSES VERSUS ANCESTORS</a></h1>
+<blockquote>
+<p>Here is a simple example of normal inheritance. We define a 'Noble'
+base class, with subclasses such as 'Prince', 'Duke', 'Baron', etc.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; for s in &quot;Power Wealth Beauty&quot;.split(): exec '%s=&quot;%s&quot;'%(s,s)
+...
+&gt;&gt;&gt; class Noble(object): # ...in fairy tale world
+... attributes = Power, Wealth, Beauty
+...
+&gt;&gt;&gt; class Prince(Noble):
+... pass
+...
+&gt;&gt;&gt; Prince.attributes
+('Power', 'Wealth', 'Beauty')
+</pre>
+</blockquote>
+<p>The class 'Prince' inherits the attributes of the class 'Noble'. An
+instance of 'Prince' still follows the lookup chain discussed above:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; charles=Prince()
+&gt;&gt;&gt; charles.attributes # ...remember, not the real world
+('Power', 'Wealth', 'Beauty')
+</pre>
+</blockquote>
+<p>If the 'Duke' class should happen to have a custom metaclasses, it
+can obtain some attributes that way:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Nobility(type): attributes = Power, Wealth, Beauty
+...
+&gt;&gt;&gt; class Duke(object): __metaclass__ = Nobility
+...
+</pre>
+</blockquote>
+<p>As well as being a class, 'Duke' is an instance of the metaclass
+'Nobility'--attribute lookup proceeds as with any object:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Duke.attributes
+('Power', 'Wealth', 'Beauty')
+</pre>
+</blockquote>
+<p>But 'Nobility' is -not- a superclass of 'Duke', so there is no
+reason why an -instance- of 'Duke' would find 'Nobility.attributes':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; Duke.mro()
+[&lt;class '__main__.Duke'&gt;, &lt;type 'object'&gt;]
+&gt;&gt;&gt; earl = Duke()
+&gt;&gt;&gt; earl.attributes
+[...]
+AttributeError: 'Duke' object has no attribute 'attributes'
+</pre>
+</blockquote>
+<p>The availability of metaclass attributes is not transitive; i.e. the
+attributes of a metaclass are available to its instances, but not to
+the instances of the instances. Just this is the main difference
+between metaclasses and superclasses. A diagram emphasizes the
+orthogonality of inheritence and instantiation:</p>
+<div class="figure">
+<p><img alt="fig1.png" src="fig1.png" /></p>
+</div>
+<p>Figure 1--Instantiation versus Inheritance</p>
+<p>Since 'earl' still has a class, you can indirectly retrieve the
+attributes, however:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; earl.__class__.attributes
+</pre>
+</blockquote>
+<p>Figure 1 contrasts simple cases where -either- inheritance or
+metaclasses are involved, but not both. Sometimes, however, a class
+C has both a custom metaclass M and a base class B:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... a = 'M.a'
+... x = 'M.x'
+...
+&gt;&gt;&gt; class B(object): a = 'B.a'
+...
+&gt;&gt;&gt; class C(B): __metaclass__=M
+...
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>Graphically:</p>
+<div class="figure">
+<p><img alt="fig2.png" src="fig2.png" /></p>
+<p class="caption">Figure 2--Combined Superclass and Metaclass</p>
+</div>
+<p>From the prior explanation, we could imagine that 'C.a' would
+resolve to -either- 'M.a' or 'B.a'. As it turns out, lookup on a
+class follows its MRO before it looks in its instantiating
+metaclass:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.a, C.x
+('B.a', 'M.x')
+&gt;&gt;&gt; c.a
+'B.a'
+&gt;&gt;&gt; c.x
+[...]
+AttributeError: 'C' object has no attribute 'x'
+</pre>
+</blockquote>
+<p>You can still enforce a attribute value using a metaclass, you just
+need to set it on the class object being instantiated rather than as
+an attribute of the metaclass:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... def __init__(cls, *args):
+... cls.a = 'M.a'
+...
+&gt;&gt;&gt; class C(B): __metaclass__=M
+...
+&gt;&gt;&gt; C.a, C().a
+('M.a', 'M.a')
+</pre>
+</blockquote>
+</blockquote>
+</div>
+<div class="section" id="more-on-class-magic">
+<h1><a name="more-on-class-magic">MORE ON CLASS MAGIC</a></h1>
+<blockquote>
+<p>The fact that the instantiation constraint is weaker than the
+inheritance constraint is essential for implementing the special
+methods like '.__new__()', '.__init__()', '.__str__()', etc. We
+will discuss the '.__str__()' method; an analysis is similar for the
+other special methods.</p>
+<p>Readers probably know that the printed representation of a class
+object can be modified by overring its '.__str__()' method. In the
+same sense, the printed representation of a class can be modified by
+overring the '.__str__()' methods of its metaclass. For instance:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Printable(type):
+... def __str__(cls):
+... return &quot;This is class %s&quot; % cls.__name__
+...
+&gt;&gt;&gt; class C(object): __metaclass__ = Printable
+...
+&gt;&gt;&gt; print C # equivalent to print Printable.__str__(C)
+This is class C
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; print c # equivalent to print C.__str__(c)
+&lt;C object at 0x40380a6c&gt;
+</pre>
+</blockquote>
+<p>The situation can be represented with the following diagram:</p>
+<div class="figure">
+<p><img alt="fig3.png" src="fig3.png" /></p>
+<p class="caption">Figure 3-Metaclasses and Magic Methods</p>
+</div>
+<p>From the previous discussion, it is clear that the '.__str__()'
+method in 'Printable' cannot override the '.__str__()' method in C,
+which is inherited from 'object' and therefore has precedence;
+printing 'c' still gives the standard result.</p>
+<p>If C inherited its '.__str__()' method from 'Printable' rather than
+from 'object', it would cause a problem: 'C' instances do not have
+a '.__name__' attribute and printing 'c' would generate an error.
+Of course, you could still define a '.__str__()' method in 'C' that
+would change the way 'c' prints.</p>
+</blockquote>
+</div>
+<div class="section" id="classmethods-vs-metamethods">
+<h1><a name="classmethods-vs-metamethods">CLASSMETHODS VS. METAMETHODS</a></h1>
+<blockquote>
+<p>Another common confusion arise between Python classmethods and
+methods defined in a metaclass, best called -metamethods-.</p>
+<p>Consider this example:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(Printable):
+... def mm(cls):
+... return &quot;I am a metamethod of %s&quot; % cls.__name__
+...
+&gt;&gt;&gt; class C(object):
+... __metaclass__=M
+... def cm(cls):
+... return &quot;I am a classmethod of %s&quot; % cls.__name__
+... cm=classmethod(cm)
+...
+&gt;&gt;&gt; c=C()
+</pre>
+</blockquote>
+<p>Part of the confusion is due to the fact that in the Smalltalk
+terminology, 'C.mm' would be called a &quot;class method of 'C'.&quot;
+Python classmethods are a different beast, however.</p>
+<p>The metamethod &quot;mm&quot; can be invoked both from either the metaclass or
+from the class, but not from the instance. The classmethod can be
+called both from the class and from its instances (but does not
+exist in the metaclass).</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print M.mm(C)
+I am a metamethod of C
+&gt;&gt;&gt; print C.mm()
+I am a metamethod of C
+&gt;&gt;&gt; print c.mm()
+[...]
+AttributeError: 'C' object has no attribute 'mm'
+&gt;&gt;&gt; print C.cm()
+I am a classmethod of C
+&gt;&gt;&gt; print c.cm()
+I am a classmethod of C
+</pre>
+</blockquote>
+<p>Also, the metamethod is retrieved by 'dir(M)' but not by 'dir(C)'
+whereas the classmethod is retrieved by 'dir(C)' and 'dir(c)'.</p>
+<p>You can only call the metaclass method that are defined in the
+class MRO by dispatching on the metaclass (built-ins like 'print' do
+this behind the scenes):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C.__str__()
+[...]
+TypeError: descriptor '__str__' of 'object' object needs an argument
+&gt;&gt;&gt; print M.__str__(C)
+This is class C
+</pre>
+</blockquote>
+<p>It is important to notice that this dispatch conflict is not limited
+to magic methods. If we change 'C' by adding an attribute 'C.mm',
+the same issue exists (it does not matter if the name is a regular
+method, classmethod, staticmethod, or simple attribute):</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.mm=lambda self: &quot;I am a regular method of %s&quot; % self.__class__
+&gt;&gt;&gt; print C.mm()
+[...]
+TypeError: unbound method &lt;lambda&gt;() must be called with
+ C instance as first argument (got nothing instead)
+</pre>
+</blockquote>
+</blockquote>
+</div>
+<div class="section" id="conflicting-metaclasses">
+<h1><a name="conflicting-metaclasses">CONFLICTING METACLASSES</a></h1>
+<blockquote>
+<p>Once you work seriously with metaclasses, you will be bitten at
+least once by metaclass/metatype conflicts. Consider a class 'A'
+with metaclass 'M_A' and a class 'B' with metaclass 'M_B'; suppose
+we derive 'C' from 'A' and 'B'. The question is: what is the
+metaclass of 'C'? Is it 'M_A' or 'M_B'?</p>
+<p>The correct answer (see &quot;Putting metaclasses to work&quot; for a
+discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+'M_A' and 'M_B', as in the following graph:</p>
+<dl>
+<dt>{Figure 4--Avoiding the Metaclass Conflict:</dt>
+<dd><a class="reference" href="http://gnosis.cx/secret/metaclass-conflict.gif">http://gnosis.cx/secret/metaclass-conflict.gif</a>}</dd>
+</dl>
+<p>However, Python does not (yet) automatically create 'M_C'. Instead,
+it raises a 'TypeError', warning the programmer of the conflict:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M_A(type): pass
+...
+&gt;&gt;&gt; class M_B(type): pass
+...
+&gt;&gt;&gt; class A(object): __metaclass__ = M_A
+...
+&gt;&gt;&gt; class B(object): __metaclass__ = M_B
+...
+&gt;&gt;&gt; class C(A,B): pass # Error message less specific under 2.2
+[...]
+TypeError: metaclass conflict: the metaclass of a derived class must
+ be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+</blockquote>
+<p>The metatype conflict can be avoided by manually creating the needed
+metaclass for 'C':</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; M_AM_B = type(&quot;M_AM_B&quot;, (M_A,M_B), {})
+&gt;&gt;&gt; class C(A,B): __metaclass__ = M_AM_B
+...
+&gt;&gt;&gt; type(C)
+&lt;class 'M_AM_B'&gt;
+</pre>
+</blockquote>
+<p>The resolution of metatype conflicts becomes more complicated when
+you wish to &quot;inject&quot; additional metaclasses into a class, beyond
+those in its ancestors. As well, depending on the metaclasses of
+parent classes, redundant metaclasses can occur--both identical
+metaclasses in different ancestors and superclass/subclass
+relationships among metaclasses. The module [noconflict] is
+available to help users resolve these issues in a robust and
+automatic way (see Resources).</p>
+</blockquote>
+</div>
+<div class="section" id="conclusion">
+<h1><a name="conclusion">CONCLUSION</a></h1>
+<blockquote>
+There are quite a number of warnings and corner cases discussed in
+this article. Working with metaclasses requires a certain degree of
+trial-and-error before the behavior becomes wholly intuitive.
+However, the issues are by no means intractable--this fairly short
+article touches on most of the pitfalls. Play with the cases
+yourself. You will find, at the end of the day, that whole new
+realms of program generalization are available with metaclasses; the
+gains are well worth the few dangers.</blockquote>
+</div>
+<div class="section" id="resources">
+<h1><a name="resources">RESOURCES</a></h1>
+<blockquote>
+<p>We continue to recomment this useful book on metaclasses:</p>
+<blockquote>
+<em>Putting Metaclasses to Work</em> by Ira R. Forman, Scott Danforth,
+Addison-Wesley 1999</blockquote>
+<p>For metaclasses in Python specifically, Guido van Rossum's
+essay, <em>Unifying types and classes in Python 2.2</em> is useful:</p>
+<blockquote>
+<a class="reference" href="http://www.python.org/2.2/descrintro.html">http://www.python.org/2.2/descrintro.html</a></blockquote>
+<p>Raymond Hettinger has written an excellent article on the
+-descriptor protocol- introducted in Python 2.2. Descriptors
+are a means to to alter the behavior of attribute/method
+access, which is an interesting programming technique in
+itself. But of particular value relative to this article is
+Hettinger's explanation of the lookup chain that underlies
+Python's concept of OOP:</p>
+<blockquote>
+<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">http://users.rcn.com/python/download/Descriptor.htm</a></blockquote>
+<p>Michele's [noconflict] module is discussed in the online Active
+State Python Cookbook. This module lets users automatically resolve
+metatype conflicts.</p>
+<blockquote>
+<a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197</a></blockquote>
+<p>The Gnosis Utilities library contains a number of tools for
+working with metaclasses, generally within the [gnosis.magic]
+subpackage. You may download the last stable version of whole
+package from:</p>
+<blockquote>
+<a class="reference" href="http://gnosis.cx/download/Gnosis_Utils-current.tar.gz">http://gnosis.cx/download/Gnosis_Utils-current.tar.gz</a></blockquote>
+<p>Or browse the experimental branch, which includes a version of
+[noconflict] at:</p>
+<blockquote>
+<a class="reference" href="http://gnosis.cx/download/gnosis/">http://gnosis.cx/download/gnosis/</a></blockquote>
+<p>Coauthor Michele has written an article on the new method
+resolution order (MRO) algorithm in Python 2.3. While most
+programmers can remain blissfully ignorant on the details of
+the changes, it is worthwhile for all Python programmers to
+understand the concept of MRO--and perhaps have an inkling
+that better and worse approaches exist:</p>
+<blockquote>
+<a class="reference" href="http://www.python.org/2.3/mro.html">http://www.python.org/2.3/mro.html</a></blockquote>
+</blockquote>
+</div>
+<div class="section" id="about-the-authors">
+<h1><a name="about-the-authors">ABOUT THE AUTHORS</a></h1>
+<blockquote>
+<p><img alt="img_dqm.png" src="img_dqm.png" /></p>
+<p>David Mertz thought his brain would melt when he wrote about
+continuations or semi-coroutines, but he put the gooey mess
+back in his skull cavity and moved on to metaclasses. David
+may be reached at <a class="reference" href="mailto:mertz&#64;gnosis.cx">mertz&#64;gnosis.cx</a>; his life pored over at
+<a class="reference" href="http://gnosis.cx/publish/">http://gnosis.cx/publish/</a>. Suggestions and recommendations on
+this, past, or future, columns are welcomed. His book <em>Text
+Processing in Python</em> has a webpage at <a class="reference" href="http://gnosis.cx/TPiP/">http://gnosis.cx/TPiP/</a>.</p>
+<p><img alt="m-simionato.png" src="m-simionato.png" /></p>
+<p>Michele Simionato is a plain, ordinary, theoretical physicist
+who was driven to Python by a quantum fluctuation that could
+well have passed without consequences, had he not met David
+Mertz. Now he has been trapped in Python gravitational field.
+He will let his readers judge the final outcome. Michele can
+be reached at <a class="reference" href="http://www.phyast.pitt.edu/~micheles/">http://www.phyast.pitt.edu/~micheles/</a></p>
+</blockquote>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/meta/meta2.txt b/pypers/meta/meta2.txt
new file mode 100755
index 0000000..ef58cc4
--- /dev/null
+++ b/pypers/meta/meta2.txt
@@ -0,0 +1,498 @@
+Metaclass Programming in Python, Part 2: Understanding the Arcana of Inheritance and Instance Creation
+========================================================================================================
+
+
+ :author: Michele Simionato, Ph.D., University of Pittsburgh
+ :author: David Mertz, Ph.D., Gnosis Software, Inc.
+ :date: June 2003
+
+ :Abstract:
+
+ Our initial developerWorks article on metaclass programming
+ prompted quite a bit of feedback, some of it from perplexed
+ readers still trying to grasp the subtleties of Python
+ metaclasses. This article revisits the working of
+ metaclasses and their relation to other OOP concepts. We
+ contrast class instantiation with inheritance, distinguish
+ classmethods and metamethods, and explain and solve metaclass
+ conflicts.
+
+
+METACLASSES AND THEIR DISCONTENTS
+------------------------------------------------------------------------
+
+ In our earlier article on metaclass programming in Python, we
+ introduced the concept of metaclasses, showed some of their power,
+ and demonstrated their use in solving problems such as dynamic
+ customization of classes and libraries at run-time.
+
+ That article has proved quite popular, but there were ellisions in
+ our condensed initial summary. Certain details in the use of
+ metaclasses merit futher explanation. Based on the feedback of our
+ readers and on discussions in comp.lang.python, we decided to
+ address some of those trickier point in this second article. In
+ particular, we think the following points are important for any
+ programmer wanting to master metaclasses:
+
+ (1) Users must understand the differences and interactions between
+ metaclass programming and traditional object-oriented programming
+ (under both single and multiple inheritance).
+
+ (2) Python 2.2 added the built-in functions 'staticmethod()' and
+ 'classmethod()' to create methods that do not require an instance
+ object during invocation. To an extent, -classmethods- overlap in
+ purpose with (meta)methods defined in metaclasses. But the precise
+ similarities and differences have also generated confusion in the
+ mind of many programmers.
+
+ (3) User should understand the cause and the resolution of metatype
+ conflicts. This becomes essential when you want to use more than
+ one custom metaclass. We explain the concept of -composition- of
+ metaclasses.
+
+
+INSTANTIATION VERSUS INHERITANCE
+-------------------------------------------------------------------------
+
+ Many programmers are confused about the difference between a
+ metaclass and a base class. At the superficial level of "determing"
+ a class, both look similar. But once you look any deeper, the
+ concepts drift apart.
+
+ Before presenting some examples, it is worth being precise about
+ some nomenclature. An -instance- is a Python object that was
+ "manufactured" by a class; the class acts as a sort of template for
+ the instance. Every instance is an instance of -exactly one- class
+ (but a class might have multiple instances). What we often call an
+ instance object--or perhaps a "simple instance"--is "final" in the
+ sense it cannot act as a template for other objects (but it might
+ still be a -factory- or a -delegate-, which serve overlapping
+ purposes).
+
+ Some instance objects are themselves classes; and all classes are
+ instances of a corresponding -metaclass-. Even classes only come
+ into existence through the instantiation mechanism. Usually classes
+ are instances of the built-in, standard metaclass 'type'; it is only
+ when we specify metaclasses other than 'type' that we need to think
+ about metaclass programming. We also call the class used to
+ instantiate an object the -type- of that object.
+
+ Running -orthogonal- to the idea of instantiation is the notion of
+ inheritance. Here, a class can have one or multiple parents, not
+ just one unique type. And parents can have parents, creating a
+ transitive subclass relation, conveniently accessible with the
+ built-in function 'issubclass()'. For example, if we define a few
+ classes and an istance:
+
+ >>> class A(object): a1 = "A"
+ ...
+ >>> class B(object): a2 = "B"
+ ...
+ >>> class C(A,B): a3 = "C(A,B)"
+ ...
+ >>> class D(C): a4 = "D(C)"
+ ...
+ >>> d = D()
+ >>> d.a5 = "instance d of D"
+
+ Then we can test the relations:
+
+ >>> issubclass(D,C)
+ True
+ >>> issubclass(D,A)
+ True
+ >>> issubclass(A,B)
+ False
+ >>> issubclass(d,D)
+ [...]
+ TypeError: issubclass() arg 1 must be a class
+
+
+ The interesting question now--the one necessary for understanding
+ the contrast between superclasses and metaclasses--is how an
+ attribute like 'd.attr' is resolved. For simplicity, we discuss
+ only the standard look-rule, not the fallback to '.__getattr__()'.
+ The first step in such resolution is to look in 'd.__dict__' for the
+ name 'attr'. If found, that's that; but if not, something fancy
+ needs to happen, e.g.:
+
+ >>> d.__dict__, d.a5, d.a1
+ ({'a5': 'instance d'}, 'instance d', 'A')
+
+ The trick to finding an attribute that isn't attached to an instance
+ is to look for it in the class of the instance, then after that in
+ all the superclasses. The order in which superclasses are checked
+ is called the -method resolution order- for the class. You can look
+ at it with the (meta)method '.mro()' (but only from class objects):
+
+ >>> [k.__name__ for k in d.__class__.mro()]
+ ['D', 'C', 'A', 'B', 'object']
+
+ In other words, the access to 'd.attr' first looks in 'd.__dict__',
+ then in 'D.__dict__', 'C.__dict__', 'A.__dict__', 'B.__dict__', and
+ finally in 'object.__dict__'. If the name is not found in any of
+ those places, an 'AttributeError' is raised.
+
+ Notice that metaclasses were never mentioned in the lookup procedure.
+
+
+METACLASSES VERSUS ANCESTORS
+-------------------------------------------------------------------------
+
+ Here is a simple example of normal inheritance. We define a 'Noble'
+ base class, with subclasses such as 'Prince', 'Duke', 'Baron', etc.
+
+ >>> for s in "Power Wealth Beauty".split(): exec '%s="%s"'%(s,s)
+ ...
+ >>> class Noble(object): # ...in fairy tale world
+ ... attributes = Power, Wealth, Beauty
+ ...
+ >>> class Prince(Noble):
+ ... pass
+ ...
+ >>> Prince.attributes
+ ('Power', 'Wealth', 'Beauty')
+
+ The class 'Prince' inherits the attributes of the class 'Noble'. An
+ instance of 'Prince' still follows the lookup chain discussed above:
+
+ >>> charles=Prince()
+ >>> charles.attributes # ...remember, not the real world
+ ('Power', 'Wealth', 'Beauty')
+
+ If the 'Duke' class should happen to have a custom metaclasses, it
+ can obtain some attributes that way:
+
+ >>> class Nobility(type): attributes = Power, Wealth, Beauty
+ ...
+ >>> class Duke(object): __metaclass__ = Nobility
+ ...
+
+ As well as being a class, 'Duke' is an instance of the metaclass
+ 'Nobility'--attribute lookup proceeds as with any object:
+
+ >>> Duke.attributes
+ ('Power', 'Wealth', 'Beauty')
+
+ But 'Nobility' is -not- a superclass of 'Duke', so there is no
+ reason why an -instance- of 'Duke' would find 'Nobility.attributes':
+
+ >>> Duke.mro()
+ [<class '__main__.Duke'>, <type 'object'>]
+ >>> earl = Duke()
+ >>> earl.attributes
+ [...]
+ AttributeError: 'Duke' object has no attribute 'attributes'
+
+ The availability of metaclass attributes is not transitive; i.e. the
+ attributes of a metaclass are available to its instances, but not to
+ the instances of the instances. Just this is the main difference
+ between metaclasses and superclasses. A diagram emphasizes the
+ orthogonality of inheritence and instantiation:
+
+ .. figure:: fig1.png
+
+ Figure 1--Instantiation versus Inheritance
+
+
+ Since 'earl' still has a class, you can indirectly retrieve the
+ attributes, however:
+
+ >>> earl.__class__.attributes
+
+ Figure 1 contrasts simple cases where -either- inheritance or
+ metaclasses are involved, but not both. Sometimes, however, a class
+ C has both a custom metaclass M and a base class B:
+
+ >>> class M(type):
+ ... a = 'M.a'
+ ... x = 'M.x'
+ ...
+ >>> class B(object): a = 'B.a'
+ ...
+ >>> class C(B): __metaclass__=M
+ ...
+ >>> c=C()
+
+ Graphically:
+
+ .. figure:: fig2.png
+
+ Figure 2--Combined Superclass and Metaclass
+
+ From the prior explanation, we could imagine that 'C.a' would
+ resolve to -either- 'M.a' or 'B.a'. As it turns out, lookup on a
+ class follows its MRO before it looks in its instantiating
+ metaclass:
+
+ >>> C.a, C.x
+ ('B.a', 'M.x')
+ >>> c.a
+ 'B.a'
+ >>> c.x
+ [...]
+ AttributeError: 'C' object has no attribute 'x'
+
+ You can still enforce a attribute value using a metaclass, you just
+ need to set it on the class object being instantiated rather than as
+ an attribute of the metaclass:
+
+ >>> class M(type):
+ ... def __init__(cls, *args):
+ ... cls.a = 'M.a'
+ ...
+ >>> class C(B): __metaclass__=M
+ ...
+ >>> C.a, C().a
+ ('M.a', 'M.a')
+
+
+MORE ON CLASS MAGIC
+------------------------------------------------------------------------
+
+ The fact that the instantiation constraint is weaker than the
+ inheritance constraint is essential for implementing the special
+ methods like '.__new__()', '.__init__()', '.__str__()', etc. We
+ will discuss the '.__str__()' method; an analysis is similar for the
+ other special methods.
+
+ Readers probably know that the printed representation of a class
+ object can be modified by overring its '.__str__()' method. In the
+ same sense, the printed representation of a class can be modified by
+ overring the '.__str__()' methods of its metaclass. For instance:
+
+ >>> class Printable(type):
+ ... def __str__(cls):
+ ... return "This is class %s" % cls.__name__
+ ...
+ >>> class C(object): __metaclass__ = Printable
+ ...
+ >>> print C # equivalent to print Printable.__str__(C)
+ This is class C
+ >>> c = C()
+ >>> print c # equivalent to print C.__str__(c)
+ <C object at 0x40380a6c>
+
+ The situation can be represented with the following diagram:
+
+ .. figure:: fig3.png
+
+ Figure 3-Metaclasses and Magic Methods
+
+ From the previous discussion, it is clear that the '.__str__()'
+ method in 'Printable' cannot override the '.__str__()' method in C,
+ which is inherited from 'object' and therefore has precedence;
+ printing 'c' still gives the standard result.
+
+ If C inherited its '.__str__()' method from 'Printable' rather than
+ from 'object', it would cause a problem: 'C' instances do not have
+ a '.__name__' attribute and printing 'c' would generate an error.
+ Of course, you could still define a '.__str__()' method in 'C' that
+ would change the way 'c' prints.
+
+
+CLASSMETHODS VS. METAMETHODS
+-----------------------------------------------------------------
+
+ Another common confusion arise between Python classmethods and
+ methods defined in a metaclass, best called -metamethods-.
+
+ Consider this example:
+
+ >>> class M(Printable):
+ ... def mm(cls):
+ ... return "I am a metamethod of %s" % cls.__name__
+ ...
+ >>> class C(object):
+ ... __metaclass__=M
+ ... def cm(cls):
+ ... return "I am a classmethod of %s" % cls.__name__
+ ... cm=classmethod(cm)
+ ...
+ >>> c=C()
+
+ Part of the confusion is due to the fact that in the Smalltalk
+ terminology, 'C.mm' would be called a "class method of 'C'."
+ Python classmethods are a different beast, however.
+
+ The metamethod "mm" can be invoked both from either the metaclass or
+ from the class, but not from the instance. The classmethod can be
+ called both from the class and from its instances (but does not
+ exist in the metaclass).
+
+ >>> print M.mm(C)
+ I am a metamethod of C
+ >>> print C.mm()
+ I am a metamethod of C
+ >>> print c.mm()
+ [...]
+ AttributeError: 'C' object has no attribute 'mm'
+ >>> print C.cm()
+ I am a classmethod of C
+ >>> print c.cm()
+ I am a classmethod of C
+
+ Also, the metamethod is retrieved by 'dir(M)' but not by 'dir(C)'
+ whereas the classmethod is retrieved by 'dir(C)' and 'dir(c)'.
+
+ You can only call the metaclass method that are defined in the
+ class MRO by dispatching on the metaclass (built-ins like 'print' do
+ this behind the scenes):
+
+ >>> print C.__str__()
+ [...]
+ TypeError: descriptor '__str__' of 'object' object needs an argument
+ >>> print M.__str__(C)
+ This is class C
+
+ It is important to notice that this dispatch conflict is not limited
+ to magic methods. If we change 'C' by adding an attribute 'C.mm',
+ the same issue exists (it does not matter if the name is a regular
+ method, classmethod, staticmethod, or simple attribute):
+
+ >>> C.mm=lambda self: "I am a regular method of %s" % self.__class__
+ >>> print C.mm()
+ [...]
+ TypeError: unbound method <lambda>() must be called with
+ C instance as first argument (got nothing instead)
+
+
+CONFLICTING METACLASSES
+----------------------------------------------------------------------------
+
+ Once you work seriously with metaclasses, you will be bitten at
+ least once by metaclass/metatype conflicts. Consider a class 'A'
+ with metaclass 'M_A' and a class 'B' with metaclass 'M_B'; suppose
+ we derive 'C' from 'A' and 'B'. The question is: what is the
+ metaclass of 'C'? Is it 'M_A' or 'M_B'?
+
+ The correct answer (see "Putting metaclasses to work" for a
+ discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+ 'M_A' and 'M_B', as in the following graph:
+
+ .. figure:: fig4.png
+
+ Figure 4--Avoiding the Metaclass Conflict
+
+ However, Python does not (yet) automatically create 'M_C'. Instead,
+ it raises a 'TypeError', warning the programmer of the conflict:
+
+ >>> class M_A(type): pass
+ ...
+ >>> class M_B(type): pass
+ ...
+ >>> class A(object): __metaclass__ = M_A
+ ...
+ >>> class B(object): __metaclass__ = M_B
+ ...
+ >>> class C(A,B): pass # Error message less specific under 2.2
+ [...]
+ TypeError: metaclass conflict: the metaclass of a derived class must
+ be a (non-strict) subclass of the metaclasses of all its bases
+
+ The metatype conflict can be avoided by manually creating the needed
+ metaclass for 'C':
+
+ >>> M_AM_B = type("M_AM_B", (M_A,M_B), {})
+ >>> class C(A,B): __metaclass__ = M_AM_B
+ ...
+ >>> type(C)
+ <class 'M_AM_B'>
+
+ The resolution of metatype conflicts becomes more complicated when
+ you wish to "inject" additional metaclasses into a class, beyond
+ those in its ancestors. As well, depending on the metaclasses of
+ parent classes, redundant metaclasses can occur--both identical
+ metaclasses in different ancestors and superclass/subclass
+ relationships among metaclasses. The module [noconflict] is
+ available to help users resolve these issues in a robust and
+ automatic way (see Resources).
+
+
+CONCLUSION
+------------------------------------------------------------------------
+
+ There are quite a number of warnings and corner cases discussed in
+ this article. Working with metaclasses requires a certain degree of
+ trial-and-error before the behavior becomes wholly intuitive.
+ However, the issues are by no means intractable--this fairly short
+ article touches on most of the pitfalls. Play with the cases
+ yourself. You will find, at the end of the day, that whole new
+ realms of program generalization are available with metaclasses; the
+ gains are well worth the few dangers.
+
+
+RESOURCES
+------------------------------------------------------------------------
+
+ We continue to recomment this useful book on metaclasses:
+
+ *Putting Metaclasses to Work* by Ira R. Forman, Scott Danforth,
+ Addison-Wesley 1999
+
+ For metaclasses in Python specifically, Guido van Rossum's
+ essay, *Unifying types and classes in Python 2.2* is useful:
+
+ http://www.python.org/2.2/descrintro.html
+
+ Raymond Hettinger has written an excellent article on the
+ -descriptor protocol- introducted in Python 2.2. Descriptors
+ are a means to to alter the behavior of attribute/method
+ access, which is an interesting programming technique in
+ itself. But of particular value relative to this article is
+ Hettinger's explanation of the lookup chain that underlies
+ Python's concept of OOP:
+
+ http://users.rcn.com/python/download/Descriptor.htm
+
+ Michele's [noconflict] module is discussed in the online Active
+ State Python Cookbook. This module lets users automatically resolve
+ metatype conflicts.
+
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+
+ The Gnosis Utilities library contains a number of tools for
+ working with metaclasses, generally within the [gnosis.magic]
+ subpackage. You may download the last stable version of whole
+ package from:
+
+ http://gnosis.cx/download/Gnosis_Utils-current.tar.gz
+
+ Or browse the experimental branch, which includes a version of
+ [noconflict] at:
+
+ http://gnosis.cx/download/gnosis/
+
+ Coauthor Michele has written an article on the new method
+ resolution order (MRO) algorithm in Python 2.3. While most
+ programmers can remain blissfully ignorant on the details of
+ the changes, it is worthwhile for all Python programmers to
+ understand the concept of MRO--and perhaps have an inkling
+ that better and worse approaches exist:
+
+ http://www.python.org/2.3/mro.html
+
+
+ABOUT THE AUTHORS
+------------------------------------------------------------------------
+
+ .. image:: img_dqm.png
+
+ David Mertz thought his brain would melt when he wrote about
+ continuations or semi-coroutines, but he put the gooey mess
+ back in his skull cavity and moved on to metaclasses. David
+ may be reached at mertz@gnosis.cx; his life pored over at
+ http://gnosis.cx/publish/. Suggestions and recommendations on
+ this, past, or future, columns are welcomed. His book *Text
+ Processing in Python* has a webpage at http://gnosis.cx/TPiP/.
+
+ .. image:: m-simionato.png
+
+ Michele Simionato is a plain, ordinary, theoretical physicist
+ who was driven to Python by a quantum fluctuation that could
+ well have passed without consequences, had he not met David
+ Mertz. Now he has been trapped in Python gravitational field.
+ He will let his readers judge the final outcome. Michele can
+ be reached at http://www.phyast.pitt.edu/~micheles/
+
+
diff --git a/pypers/meta/meta_threading.py b/pypers/meta/meta_threading.py
new file mode 100755
index 0000000..132d12b
--- /dev/null
+++ b/pypers/meta/meta_threading.py
@@ -0,0 +1,36 @@
+from types import FunctionType
+from threading import RLock
+
+def makeThreadsafeMethod(func):
+ def threadsafemethod(self, *args, **kwargs):
+ self._monitor_lockObj.acquire()
+ print ">>got lock"
+ try:
+ return func(self, *args, **kwargs)
+ finally:
+ self._monitor_lockObj.release()
+ print "<<released lock"
+ return threadsafemethod
+
+def convert_methods(dic):
+ methods=[ v for k,v in dic.iteritems() if isinstance(v, FunctionType) ]
+ for m in methods:
+ dic[m.__name__] = makeThreadsafeMethod(m)
+ dic["_monitor_lockObj"] = RLock()
+
+
+class ThreadSafeMethodsMetaClass(type):
+ def __new__(meta, name, bases, dic):
+ convert_methods(dic)
+ return super(ThreadSafeMethodsMetaClass, meta).__new__(meta, name, bases, dic)
+
+class MyClass(object):
+ __metaclass__=ThreadSafeMethodsMetaClass
+
+ def __init__(self):
+ print "init!"
+ def method(self, a1, a2):
+ print a1,a2
+
+m=MyClass()
+m.method("irmen",42)
diff --git a/pypers/meta/metadd.txt b/pypers/meta/metadd.txt
new file mode 100755
index 0000000..f7b3147
--- /dev/null
+++ b/pypers/meta/metadd.txt
@@ -0,0 +1,196 @@
+
+Other points that could be added or not ...
+----------------------------------------------------------------------------
+
+1. Widespread packages using metaclasses:
+
+ - gnosis.magic
+ - Psyco
+ - ...
+
+2. Changing metaclasses on the fly.
+
+Let us now define a metaclass 'Froggyness':
+
+ >>> class Frogginess(type): attributes="Powerlessness,Poverty,Uglyness"
+
+Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.
+
+ >>> Frog=Frogginess("Frog",(),{})
+ >>> Frog.attributes
+ 'Powerlessness,Poverty,Uglyness'
+
+However, in Python miracles can happen:
+
+ >>> def miracle(Frog): Frog.__class__=Nobility
+ >>> miracle(Frog); Frog.attributes
+ 'Powerlessness,Richness,Beauty'
+
+In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.
+
+However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:
+
+ >>> Frog.attributes="poor, small, ugly"
+ >>> jack=Frog(); jack.attributes
+ 'poor, small, ugly'
+
+Then the miracle cannot work:
+
+ ::
+
+ #<fairytale2.py>
+
+ class Nobility(type): attributes="Power, Richness, Beauty"
+ Prince=Nobility("Prince",(),{})
+ charles=Prince()
+
+ class Frogginess(type): attributes="Inpuissance, Poverty, Uglyness"
+ Frog=Frogginess("Frog",(),{})
+ Frog.attributes="poor, small, ugly"
+ jack=Frog()
+
+ def miracle(Frog): Frog.__class__=Nobility
+
+ miracle(Frog)
+
+ print "I am",Frog.attributes,"even if my class is",Frog.__class__
+
+ #</fairytale2.py>
+
+Output:
+
+ ::
+
+ I am poor, small, ugly even if my class is <class '__main__.Nobility'>
+
+The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is ``poor, small, ugly``. If you think a bit, it makes sense.
+
+Remark:
+
+In Python 2.3 there are restrictions when changing the ``__class__``
+attribute for classes:
+
+ >>> C=type('C',(),{})
+ >>> C.__class__ = Nobility #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: __class__ assignment: only for heap types
+
+Here changing ``C.__class__`` is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:
+
+ >>> print bool.__doc__ # in Python 2.2 would give an error
+ bool(x) -> bool
+ Returns True when the argument x is true, False otherwise.
+ The builtins True and False are the only two instances of the class bool.
+ The class bool is a subclass of the class int, and cannot be subclassed.
+
+In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:
+
+ >>> class M1(type): f=lambda cls: 'M1.f' #metaclass1
+ >>> class M2(type): f=lambda cls: 'M2.f' #metaclass2
+ >>> B=M1('B',(),{}) # B receives M1.f
+ >>> class C(B): pass #C receives M1.f
+ >>> B.f()
+ 'M1.f'
+ B.__class__=M2 #change the metaclass
+ >>> B.f() #B receives M2.f
+ 'M2.f'
+ C.f() #however C does *not* receive M2.f
+ >>> C.f()
+ 'M1.f'
+ >>> type(B)
+ <class '__main__.M2'>
+ >>> type(C)
+ <class '__main__.M1'>
+
+An example: counting instances
+-------------------------------------------------------------------------
+
+# This is elaborated from some post of Alex Martelli
+# I will flesh out this if you like the example; the lines with
+# super could be skipped if you think it is too advanced
+# if you don't like it, I have plenty of other examples ;)
+
+ ::
+
+ class Logger(object):
+ """Each time an object is created, the counter is increased; notice
+ that each time a new subclass is derived, the counter is reset,
+ therefore different subclasses have different counters."""
+ class __metaclass__(type):
+ def __init__(cls,*args):
+ cls.counter=0 # rests the counter at each derivation
+ return super(cls.__metaclass__,cls).__init__(*args)
+ def __call__(cls,*args,**kw):
+ cls.counter+=1 # increases the counter at each instantiation
+ print 'Created instance #%s of class %s' % (cls.counter,cls)
+ return super(cls.__metaclass__,cls).__call__(*args,**kw)
+
+This is an example with an *anonymous inner metaclass*, made cooperative
+for use in multiple inheritance of metaclasses ;)
+
+
+Python has already few built-in metamethods: ``.mro()``
+and ``__subclass__``. These are methods of the metaclass ``type`` and
+therefore of any of its sub-metaclasses.
+
+ >>> print type.mro.__doc__
+ mro() -> list
+ return a type's method resolution order
+
+ >>> print type.__subclasses__.__doc__
+ __subclasses__() -> list of immediate subclasses
+
+ >>> class A(object): pass
+ ...
+ >>> class B(A): pass
+ ...
+ >>> B.mro()
+ [<class 'B'>, <class 'A'>, <type 'object'>]
+ >>> A.__subclasses__()
+ [<class 'B'>]
+
+Clashing of metamethods with regular methods
+------------------------------------------------------------------
+
+It is interesting to notice that,
+This explains what happerns for special methods.
+The ``print c`` statement works because it is equivalent to
+
+ >>> print C.__str__(c)
+ <C object at 0x40380a6c>
+
+whereas ``print C`` works because it is equivalent to
+
+ >>> print Printable.__str__(C)
+ This is class C
+
+Conclusion: ``__str__, __new__, __init__`` etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. ``B.__str__(C)``),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. ``C.mm()`` instead of
+``B.mm(C)``).
+
+Metamethods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for ``__str__``,
+``___init__``, etc.
diff --git a/pypers/meta/metatype.html b/pypers/meta/metatype.html
new file mode 100755
index 0000000..f83b6bb
--- /dev/null
+++ b/pypers/meta/metatype.html
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.8: http://docutils.sourceforge.net/" />
+<title>SOLVING THE METACLASS CONFLICT</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="solving-the-metaclass-conflict">
+<h1 class="title">SOLVING THE METACLASS CONFLICT</h1>
+<div class="section" id="summary">
+<h1><a name="summary">Summary:</a></h1>
+<p>Any serious user of metaclasses has been bitten at least once by the
+infamous metaclass/metatype conflict. Here I give a general recipe to
+solve the problem, as well as some theory and some examples.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;noconflict.py&gt;
+
+def _generatemetaclass(bases,metas):
+ &quot;&quot;&quot;Internal function called by inferred. 'bases' is tuple of base classes
+ and 'metas' a tuple of metaclasses.&quot;&quot;&quot;
+
+ metas=[m for m in metas if m is not type]
+ trivial=lambda m: m is type or m in metas
+ metabases=metas+[mb for mb in map(type,bases) if not trivial(mb)]
+ metaname=&quot;_&quot;+''.join([m.__name__ for m in metabases])
+ if not metabases: # trivial metabase
+ return type
+ elif len(metabases)==1: # single metabase
+ return metabases[0]
+ else: # multiple metabases
+ # creates new metaclass,shift possible conflict to meta-metaclasses
+ return type(metaname,tuple(metabases),{})
+
+def memoize(f):
+ &quot;&quot;&quot;This closure remembers all f invocations&quot;&quot;&quot;
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+_generatemetaclass=memoize(_generatemetaclass)
+
+def inferred(*metas):
+ &quot;&quot;&quot;Class factory avoiding metatype conflicts: if the base classes have
+ metaclasses conflicting within themselves or with the given metaclass,
+ it automatically generates a compatible metaclass and instantiate the
+ inferred class from it.&quot;&quot;&quot;
+ return lambda n,b,d: _generatemetaclass(b,metas or (type,))(n,b,d)
+
+#&lt;/noconflict.py&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="discussion">
+<h1><a name="discussion">Discussion</a></h1>
+<p>I think that not too many programmers are familiar with metaclasses and
+metatype conflicts, therefore let me be pedagogical ;)</p>
+<p>The simplest case where a metatype conflict happens is the following.
+Consider a class <tt class="literal"><span class="pre">A</span></tt> with metaclass <tt class="literal"><span class="pre">M_A</span></tt> and a class <tt class="literal"><span class="pre">B</span></tt> with
+an independent metaclass <tt class="literal"><span class="pre">M_B</span></tt>; suppose we derive <tt class="literal"><span class="pre">C</span></tt> from <tt class="literal"><span class="pre">A</span></tt>
+and <tt class="literal"><span class="pre">B</span></tt>. The question is: what is the metaclass of <tt class="literal"><span class="pre">C</span></tt> ?
+Is it <tt class="literal"><span class="pre">M_A</span></tt> or <tt class="literal"><span class="pre">M_B</span></tt> ?</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M_A(type):
+... pass
+&gt;&gt;&gt; class M_B(type):
+... pass
+&gt;&gt;&gt; class A(object):
+... __metaclass__=M_A
+&gt;&gt;&gt; class B(object):
+... __metaclass__=M_B
+&gt;&gt;&gt; class C(A,B):
+... pass
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+</blockquote>
+<p>The correct answer (see the book &quot;Putting metaclasses to work&quot; for a
+thought discussion) is <tt class="literal"><span class="pre">M_C</span></tt>, where <tt class="literal"><span class="pre">M_C</span></tt> is a metaclass that inherits
+from <tt class="literal"><span class="pre">M_A</span></tt> and <tt class="literal"><span class="pre">M_B</span></tt>, as in the following graph, where instantiation
+is denoted by colon lines:</p>
+<blockquote>
+<pre class="literal-block">
+M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
+</pre>
+</blockquote>
+<p>However, Python is not that magic, and it does not automatically create
+<tt class="literal"><span class="pre">M_C</span></tt>. Instead, it raises a <tt class="literal"><span class="pre">TypeError</span></tt>, warning the programmer of
+the possible confusion. The metatype conflict can be avoided
+by assegning the correct metaclass to <tt class="literal"><span class="pre">C</span></tt> by hand:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M_AM_B(M_A,M_B): pass
+...
+&gt;&gt;&gt; class C(A,B):
+... __metaclass__=M_AM_B
+&gt;&gt;&gt; C,type(C)
+(&lt;class 'C'&gt;, &lt;class 'M_AM_B'&gt;)
+</pre>
+</blockquote>
+<p>In general, a class <tt class="literal"><span class="pre">A(B,</span> <span class="pre">C,</span> <span class="pre">D</span> <span class="pre">,</span> <span class="pre">...)</span></tt> can be generated without conflicts
+only if <tt class="literal"><span class="pre">type(A)</span></tt> is a subclass of each of <tt class="literal"><span class="pre">type(B),</span> <span class="pre">type(C),</span> <span class="pre">...</span></tt></p>
+<p>It is possible to automatically avoid conflicts, by defining a smart
+class factory that generates the correct metaclass by looking at the
+metaclasses of the base classes. This is done via the <tt class="literal"><span class="pre">inferred</span></tt>
+class factory, wich internally invokes the <tt class="literal"><span class="pre">_generatemetaclass</span></tt>
+function. When <tt class="literal"><span class="pre">_generatemetaclass</span></tt> is invoked with the same bases and
+the same metaclasses it should return the same metaclass. This is done by
+keeping a list of the generated metaclasses thanks to the <tt class="literal"><span class="pre">with_memory</span></tt>
+closure. Now, when <tt class="literal"><span class="pre">_generatemetaclass</span></tt> is invoked with the same arguments
+it returns the same metaclass.</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import sys; sys.path.append('.') # for doctest purposes
+&gt;&gt;&gt; from noconflict import inferred
+&gt;&gt;&gt; class C(A,B):
+... __metaclass__=inferred()
+&gt;&gt;&gt; C
+&lt;class 'C'&gt;
+&gt;&gt;&gt; type(C) # automatically generated metaclass
+&lt;class 'noconflict._M_AM_B'&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M_A(type): pass
+...
+&gt;&gt;&gt; class M_B(type): pass
+...
+&gt;&gt;&gt; class A: __metaclass__=M_A
+...
+&gt;&gt;&gt; class B: __metaclass__=M_B
+...
+&gt;&gt;&gt; class E(A,B):
+... __metaclass__=inferred()
+&gt;&gt;&gt; type(E)
+&lt;class 'noconflict._M_AM_B'&gt;
+&gt;&gt;&gt; class F(A,B):
+... __metaclass__=inferred()
+&gt;&gt;&gt; type(F)
+&lt;class 'noconflict._M_AM_B'&gt;
+&gt;&gt;&gt; type(E) is type(F)
+True
+</pre>
+</blockquote>
+<p>Another example where <tt class="literal"><span class="pre">inferred()</span></tt> can solve the conflict is the
+following:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(A):
+... __metaclass__=M_B
+Traceback (most recent call last):
+ File &quot;&lt;string&gt;&quot;, line 1, in ?
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+</blockquote>
+<p>Here the problem is that since <tt class="literal"><span class="pre">D</span></tt> inherits from <tt class="literal"><span class="pre">A</span></tt>, its metaclass
+must inherit from <tt class="literal"><span class="pre">M_A</span></tt> and cannot be <tt class="literal"><span class="pre">M_B</span></tt>.</p>
+<p><tt class="literal"><span class="pre">inferred</span></tt> solves the problem by automatically inheriting both from
+<tt class="literal"><span class="pre">M_B</span></tt> and <tt class="literal"><span class="pre">M_A</span></tt>:</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(A):
+... __metaclass__=inferred(M_B)
+&gt;&gt;&gt; type(D)
+&lt;class 'noconflict._M_BM_A'&gt;
+</pre>
+</blockquote>
+<p>Note: these examples here have been checked with doctest on Python 2.3b1.</p>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="metatype.txt">View document source</a>.
+Generated on: 2003-06-06 22:08 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/meta/metatype.txt b/pypers/meta/metatype.txt
new file mode 100755
index 0000000..49ad95c
--- /dev/null
+++ b/pypers/meta/metatype.txt
@@ -0,0 +1,188 @@
+SOLVING THE METACLASS CONFLICT
+=========================================================================
+
+Summary:
+------------------------------------------------------------------------
+
+Any serious user of metaclasses has been bitten at least once by the
+infamous metaclass/metatype conflict. Here I give a general recipe to
+solve the problem, as well as some theory and some examples.
+
+ ::
+
+ #<noconflict.py>
+
+ def _generatemetaclass(bases,metas,priority):
+ """Internal function called by clsfactory. 'bases' is tuple of
+ base classes, 'metas' a tuple of metaclasses and priority is a boolean."""
+
+ trivial=lambda m: m is type or m in metas
+ metabases=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabases+metas, metas+metabases)[priority]
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ if not metabases: # trivial metabase
+ return type
+ elif len(metabases)==1: # single metabase
+ return metabases[0]
+ else: # multiple metabases
+ # create new metaclass,shift possible conflict to meta-metaclasses
+ return type(metaname,metabases,{})
+
+ def memoize(f):
+ """This closure remembers all f invocations"""
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+ _generatemetaclass=memoize(_generatemetaclass)
+
+ def clsfactory(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ clsfactory(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+ #</noconflict.py>
+
+Discussion
+---------------------------------------------------------
+
+I think that not too many programmers are familiar with metaclasses and
+metatype conflicts, therefore let me be pedagogical ;)
+
+The simplest case where a metatype conflict happens is the following.
+Consider a class ``A`` with metaclass ``M_A`` and a class ``B`` with
+an independent metaclass ``M_B``; suppose we derive ``C`` from ``A``
+and ``B``. The question is: what is the metaclass of ``C`` ?
+Is it ``M_A`` or ``M_B`` ?
+
+ >>> class M_A(type):
+ ... pass
+ >>> class M_B(type):
+ ... pass
+ >>> class A(object):
+ ... __metaclass__=M_A
+ >>> class B(object):
+ ... __metaclass__=M_B
+ >>> class C(A,B):
+ ... pass
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The correct answer (see the book "Putting metaclasses to work" for a
+thought discussion) is ``M_C``, where ``M_C`` is a metaclass that inherits
+from ``M_A`` and ``M_B``, as in the following graph, where instantiation
+is denoted by colon lines:
+
+ ::
+
+
+ M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
+
+
+However, Python is not that magic, and it does not automatically create
+``M_C``. Instead, it raises a ``TypeError``, warning the programmer of
+the possible confusion. The metatype conflict can be avoided
+by assegning the correct metaclass to ``C`` by hand:
+
+ >>> class M_AM_B(M_A,M_B): pass
+ ...
+ >>> class C(A,B):
+ ... __metaclass__=M_AM_B
+ >>> C,type(C)
+ (<class 'C'>, <class 'M_AM_B'>)
+
+In general, a class ``A(B, C, D , ...)`` can be generated without conflicts
+only if ``type(A)`` is a subclass of each of ``type(B), type(C), ...``
+
+It is possible to automatically avoid conflicts, by defining a smart
+class factory that generates the correct metaclass by looking at the
+metaclasses of the base classes. This is done via the ``clsfactory``
+class factory, wich internally invokes the ``_generatemetaclass``
+function. When ``_generatemetaclass`` is invoked with the same bases and
+the same metaclasses it should return the same metaclass. This is done by
+keeping a list of the generated metaclasses thanks to the ``with_memory``
+closure. Now, when ``_generatemetaclass`` is invoked with the same arguments
+it returns the same metaclass.
+
+ >>> import sys; sys.path.append('.') # for doctest purposes
+ >>> from noconflict import clsfactory
+ >>> class C(A,B):
+ ... __metaclass__=clsfactory()
+ >>> C
+ <class 'C'>
+ >>> type(C) # automatically generated metaclass
+ <class 'noconflict._M_AM_B'>
+
+ >>> class M_A(type): pass
+ ...
+ >>> class M_B(type): pass
+ ...
+ >>> class A: __metaclass__=M_A
+ ...
+ >>> class B: __metaclass__=M_B
+ ...
+ >>> class E(A,B):
+ ... __metaclass__=clsfactory()
+ >>> type(E)
+ <class 'noconflict._M_AM_B'>
+ >>> class F(A,B):
+ ... __metaclass__=clsfactory()
+ >>> type(F)
+ <class 'noconflict._M_AM_B'>
+ >>> type(E) is type(F)
+ True
+
+Another example where ``clsfactory()`` can solve the conflict is the
+following:
+
+ >>> class D(A):
+ ... __metaclass__=M_B
+ Traceback (most recent call last):
+ File "<string>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+Here the problem is that since ``D`` inherits from ``A``, its metaclass must
+inherit from ``M_A`` and cannot be ``M_B``.
+
+``clsfactory`` solves the problem by automatically inheriting both from
+``M_A`` and ``M_B``:
+
+ >>> class D(A):
+ ... __metaclass__=clsfactory(M_B)
+ >>> type(D)
+ <class 'noconflict._M_AM_B'>
+
+In some case, the user may want ``M_B`` to have the priority over ``M_A``.
+This is easily done:
+
+ >>> class D(A):
+ ... __metaclass__=clsfactory(M_B,priority=True)
+ >>> type(D)
+ <class 'noconflict._M_BM_A'>
+
+----
+
+I thank David Mertz for help in polishing the code. These examples here have
+been checked with doctest on Python 2.3b1.
diff --git a/pypers/meta/metatype2.txt b/pypers/meta/metatype2.txt
new file mode 100755
index 0000000..58727b4
--- /dev/null
+++ b/pypers/meta/metatype2.txt
@@ -0,0 +1,199 @@
+SOLVING THE METACLASS CONFLICT
+=========================================================================
+
+Summary:
+------------------------------------------------------------------------
+
+Any serious user of metaclasses has been bitten at least once by the
+infamous metaclass/metatype conflict. Here I give a general recipe to
+solve the problem, as well as some theory and some examples.
+
+ ::
+
+ #<noconflict.py>
+
+ metadic={}
+
+ def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+ def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+ #</noconflict.py>
+
+Discussion
+---------------------------------------------------------
+
+I think that not too many programmers are familiar with metaclasses and
+metatype conflicts, therefore let me be pedagogical ;)
+
+The simplest case where a metatype conflict happens is the following.
+Consider a class ``A`` with metaclass ``M_A`` and a class ``B`` with
+an independent metaclass ``M_B``; suppose we derive ``C`` from ``A``
+and ``B``. The question is: what is the metaclass of ``C`` ?
+Is it ``M_A`` or ``M_B`` ?
+
+ >>> class M_A(type):
+ ... pass
+ >>> class M_B(type):
+ ... pass
+ >>> class A(object):
+ ... __metaclass__=M_A
+ >>> class B(object):
+ ... __metaclass__=M_B
+ >>> class C(A,B):
+ ... pass
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The correct answer (see the book "Putting metaclasses to work" for a
+thought discussion) is ``M_C``, where ``M_C`` is a metaclass that inherits
+from ``M_A`` and ``M_B``, as in the following graph, where instantiation
+is denoted by colon lines:
+
+ ::
+
+
+ M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
+
+
+However, Python is not that magic, and it does not automatically create
+``M_C``. Instead, it raises a ``TypeError``, warning the programmer of
+the possible confusion. The metatype conflict can be avoided
+by assegning the correct metaclass to ``C`` by hand:
+
+ >>> class M_AM_B(M_A,M_B): pass
+ ...
+ >>> class C(A,B):
+ ... __metaclass__=M_AM_B
+ >>> C,type(C)
+ (<class 'C'>, <class 'M_AM_B'>)
+
+In general, a class ``A(B, C, D , ...)`` can be generated without conflicts
+only if ``type(A)`` is a subclass of each of ``type(B), type(C), ...``
+
+It is possible to automatically avoid conflicts, by defining a smart
+class factory that generates the correct metaclass by looking at the
+metaclasses of the base classes. This is done via the ``makecls``
+class factory, wich internally invokes the ``_generatemetaclass``
+function.
+
+ >>> from noconflict import makecls
+ >>> class C(A,B):
+ ... __metaclass__=makecls()
+ >>> C
+ <class 'C'>
+ >>> type(C) # automatically generated metaclass
+ <class 'noconflict._M_AM_B'>
+
+In order to avoid to generate twice the same metaclass,,
+they are stored in a dictionary. In particular, when ``_generatemetaclass``
+is invoked with the same arguments it returns the same metaclass.
+
+ >>> class D(A,B):
+ ... __metaclass__=makecls()
+ >>> type(D)
+ <class 'noconflict._M_AM_B'>
+ >>> type(C) is type(D)
+ True
+
+Another example where ``makecls()`` can solve the conflict is the
+following:
+
+ >>> class D(A):
+ ... __metaclass__=M_B
+ Traceback (most recent call last):
+ File "<string>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+Here the problem is that since ``D`` inherits from ``A``, its metaclass must
+inherit from ``M_A`` and cannot be ``M_B``.
+
+``makecls`` solves the problem by automatically inheriting both from
+``M_A`` and ``M_B``:
+
+ >>> class D(A):
+ ... __metaclass__=makecls(M_B)
+ >>> type(D)
+ <class 'noconflict._M_AM_B'>
+
+In some case, the user may want ``M_B`` to have the priority over ``M_A``.
+This is easily done:
+
+ >>> class D(A):
+ ... __metaclass__=makecls(M_B,priority=True)
+ >>> type(D)
+ <class 'noconflict._M_BM_A'>
+
+``_generatemetaclass`` automatically skips unneeded metaclasses,
+
+ >>> class M0(type): pass
+ ...
+ >>> class M1(M0): pass
+ ...
+ >>> class B: __metaclass__=M0
+ ...
+ >>> class C(B): __metaclass__=makecls(M1)
+ ...
+ >>> print C,type(C)
+ <class 'C'> <class 'M1'>
+
+i.e. in this example where ``M1`` is a subclass of ``M0``, it
+returns ``M1`` and does not generate a redundant metaclass ``_M0M1``.
+
+``makecls`` also solves the meta-metaclass conflict, and generic higher
+order conflicts:
+
+ >>> class MM1(type): pass
+ ...
+ >>> class MM2(type): pass
+ ...
+ >>> class M1(type): __metaclass__=MM1
+ ...
+ >>> class M2(type): __metaclass__=MM2
+ ...
+ >>> class A: __metaclass__=M1
+ ...
+ >>> class B: __metaclass__=M2
+ ...
+ >>> class C(A,B): __metaclass__=makecls()
+ ...
+ >>> print C,type(C),type(type(C))
+ <class 'C'> <class 'noconflict._M1M2'> <class 'noconflict._MM1MM2'>
+
+----
+
+I thank David Mertz for help in polishing the original version of the code.
+This version has largerly profited from discussion with Phillip J. Eby.
+
+These examples here have been checked with doctest on Python 2.3b1.
diff --git a/pypers/meta/noconf.py b/pypers/meta/noconf.py
new file mode 100755
index 0000000..f42c809
--- /dev/null
+++ b/pypers/meta/noconf.py
@@ -0,0 +1,78 @@
+# noconflict.py
+
+metadic={}
+
+def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+# solving the meta-metaclass conflict
+class MM1(type): pass
+class MM2(type): pass
+class M1(type):
+ __metaclass__=MM1
+class M2(type):
+ __metaclass__=MM2
+class A: __metaclass__=M1
+class B: __metaclass__=M2
+class C(A,B):
+ __metaclass__=makecls()
+#print C,type(C),type(type(C))
+#=> <class '__main__.C'> <class '__main__._M1M2'> <class '__main__._MM1MM2'>
+
+#from oopp import pretty
+#print pretty(_generatemetaclass.dic)
+
+#print _generatemetaclass.result
+#print _generatemetaclass.argskw
+
+class D(A,B):
+ __metaclass__=makecls()
+
+#print type(D) is type(C)
+
+# skipping unneeded metaclasses
+class M(type): pass
+class M1(M): pass
+
+class B: __metaclass__=M
+class C(B):
+ __metaclass__=makecls(M1)
+
+#print C,type(C),type(type(C))
+#=> <class '__main__.C'> <class '__main__.M1'> <type 'type'>
+
+# reuse already generated metaclasses
+class M1(type): pass
+class M2(type): pass
+class B1(object): __metaclass__=M1
+class B2(object): __metaclass__=M2
+class B3(B2): pass
+class C1(B1,B2): __metaclass__=makecls()
+class C2(B1,B3): __metaclass__=makecls()
+
+print type(C1) is type(C2)
diff --git a/pypers/meta/noconf2.py b/pypers/meta/noconf2.py
new file mode 100755
index 0000000..2da1009
--- /dev/null
+++ b/pypers/meta/noconf2.py
@@ -0,0 +1,88 @@
+# noconflict.py
+
+def memoize(f): #NOT WORKING
+ """This closure remembers all f invocations"""
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+def memoize(f): # WORKING
+ """This closure stores all f invocations in a dictionary; notice that
+ f cannot have keywords arguments or dictionaries as arguments."""
+ dic={} # internal dictionary
+ wrapped_f=lambda *args: dic.setdefault(args,f(*args))
+ wrapped_f.dic=dic # make dic available outside
+ return wrapped_f
+
+def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabases=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabases+metas, metas+metabases)[priority]
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ if not metabases: # trivial metabase
+ return type
+ elif len(metabases)==1: # single metabase
+ return metabases[0]
+ else: # multiple metabases
+ return clsfactory()(metaname,metabases,{})
+
+_generatemetaclass=memoize(_generatemetaclass)
+
+def clsfactory(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ clsfactory(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+# solving the meta-metaclass conflict
+class MM1(type): pass
+class MM2(type): pass
+class M1(type):
+ __metaclass__=MM1
+class M2(type):
+ __metaclass__=MM2
+class A: __metaclass__=M1
+class B: __metaclass__=M2
+class C(A,B):
+ __metaclass__=clsfactory()
+print C,type(C),type(type(C))
+#=> <class '__main__.C'> <class '__main__._M1M2'> <class '__main__._MM1MM2'>
+
+from oopp import pretty
+print pretty(_generatemetaclass.dic)
+
+#print _generatemetaclass.result
+#print _generatemetaclass.argskw
+
+class D(A,B):
+ __metaclass__=clsfactory()
+
+
+print type(D) is type(C)
+
+# skipping unneeded metaclasses
+class M(type): pass
+class M1(M): pass
+
+class B: __metaclass__=M
+class C(B):
+ __metaclass__=clsfactory(M1)
+
+print C,type(C),type(type(C))
+#=> <class '__main__.C'> <class '__main__.M1'> <type 'type'>
diff --git a/pypers/meta/noconflict.py b/pypers/meta/noconflict.py
new file mode 100755
index 0000000..ba75e47
--- /dev/null
+++ b/pypers/meta/noconflict.py
@@ -0,0 +1,33 @@
+# noconflict.py
+
+metadic={}
+
+def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+
diff --git a/pypers/meta/noconflict.txt b/pypers/meta/noconflict.txt
new file mode 100755
index 0000000..f6b1c64
--- /dev/null
+++ b/pypers/meta/noconflict.txt
@@ -0,0 +1,66 @@
+#<noconflict.py>
+
+def memoize(f):
+ """This closure remembers all f invocations"""
+ argskw,result = [],[]
+ def _(*args,**kw):
+ akw=args,kw
+ try: # returns a previously stored result
+ return result[argskw.index(akw)]
+ except ValueError: # there is no previously stored result
+ argskw.append(akw) # update argskw
+ result.append(f(*args,**kw)) # update result
+ return result[-1] # return the new result
+ _.argskw=argskw #makes the argskw list accessible outside
+ _.result=result #makes the result list accessible outside
+ return _
+
+def clsfactory(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ clsfactory(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ trivial=lambda m: sum([issubclass(M,m) for M in metas]) # hackish lambda:
+ # m is trivial if it is a superclass of at least one of the explicit metas
+
+ def generatemetaclass(bases):
+ metabases=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ print metabases; raise SystemExit()
+ metabases=(metabases+metas, metas+metabases)[priority]
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ if not metabases: # trivial metabase
+ return type
+ elif len(metabases)==1: # single metabase
+ return metabases[0]
+ else: # multiple metabases
+ # create new metaclass,shift possible conflict to meta-metaclasses
+ return clsfactory()(metaname,metabases,{})
+
+ generatemetaclass=memoize(generatemetaclass)
+
+ return lambda name,bases,dic: generatemetaclass(bases)(name,bases,dic)
+
+#</noconflict.py>
+
+>>> from noconflict import clsfactory
+>>> class MM1(type): pass
+...
+>>> class MM2(type): pass
+...
+>>> class M1(type):
+... __metaclass__=MM1
+>>> class M2(type):
+... __metaclass__=MM2
+>>> class A: __metaclass__=M1
+...
+>>> class B: __metaclass__=M2
+...
+>>> class C(A,B):
+... __metaclass__=clsfactory()
+>>> print C,type(C),type(type(C))
+<class 'C'> <class 'noconflict._M1M2'> <class 'noconflict._MM1MM2'>
+
diff --git a/pypers/meta/noconflict_alex.py b/pypers/meta/noconflict_alex.py
new file mode 100755
index 0000000..2aafd4f
--- /dev/null
+++ b/pypers/meta/noconflict_alex.py
@@ -0,0 +1,84 @@
+# noconflict.py
+"""Deep, **DEEP** magic to remove metaclass conflicts.
+
+``noconflict`` provides the ``safetype`` metaclass, the mother of conflict-free
+metaclasses. The suggested import syntax for usage in other modules is
+
+ from safetype import safetype as type
+
+If you override ``__new__`` when you derive from ``safetype``,
+you should do it cooperatively."""
+
+import inspect, types, __builtin__
+from ms.iter_utils import skip_redundant
+try: set # python version >= 2.4
+except NameError: # python version <= 2.3
+ from sets import Set as set
+
+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__):
+ """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,), right_metas=())
+ 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)
+ return super(mcl, meta).__new__(meta, n, b, d)
+ else:
+ raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)
diff --git a/pypers/meta/proposal2.txt b/pypers/meta/proposal2.txt
new file mode 100755
index 0000000..70b16c2
--- /dev/null
+++ b/pypers/meta/proposal2.txt
@@ -0,0 +1,49 @@
+Proposal for a new paper METACLASS PROGRAMMING IN PYTHON - PART 2
+--------------------- -------------------------------------------
+
+In the early days, metaclasses were considered an esoteric subject, for
+wizards only. After the coming of Python 2.2, they have been better exposed
+to the programmer, but still their usage has been considered mysterious by
+most users. However, nowadays, this perception is changing and more and more
+ programmers are beginning to explore this fascinating topic.
+
+In our earlier paper on metaclass programming in Python, we introduced
+the concept of metaclasses, showing how powerful they are, and
+how they can be used in real life, for solving problems such as
+dynamic customization of classes and libraries at run-time.
+
+This paper has proved quite popular, and we followup by
+filling in some of the gaps in our initial summary, that was
+necessarily compact.
+
+There are many subtilities in the usage of metaclasses, which deserves
+to be better explored. Based on the feedback of our readers and the
+discussions in comp.lang.python, we think that the following points are
+crucial for the programmer wanting to master metaclasses:
+
+1) understanding the subtle differences and interactions between
+metaclass-oriented programming and more traditional object-oriented
+programming that utilizes multiple inheritance and mix-in classes.
+Many programmers become confused on the subtle distinctions, since
+inheritance in metaclasses works differently than usual inheritance.
+
+2) Python 2.2 has added the built-in 'staticmethod()' and 'classmethod()'
+that allows to call methods from classes. Superficially, that overlaps with
+the behavior of methods defined in metaclasses, but nonetheless differ
+importantly. This has also generated a lot of confusion in the mind of
+many programmers.
+
+3) understanding the origin and the resolution of metatype conflicts,
+that becomes ubiquitous at the moment when the user wants to use more
+than a single custom metaclass. This amount to understanding the issue of
+composition (in the sense of multiple inheritance) of metaclasses.
+
+As well as clarifying these OOP concepts, this paper provides several
+additional concrete examples where metaclasses ease programming
+tasks.
+
+
+---
+
+D. Mertz
+M. Simionato 4-30-2003
diff --git a/pypers/meta/safetype.txt b/pypers/meta/safetype.txt
new file mode 100755
index 0000000..7ef12c5
--- /dev/null
+++ b/pypers/meta/safetype.txt
@@ -0,0 +1,160 @@
+SOLVING THE METACLASS CONFLICT
+=========================================================================
+
+Summary:
+------------------------------------------------------------------------
+
+Any serious user of metaclasses has been bitten at least once by the
+infamous metaclass/metatype conflict. The module ``safetype`` is the solution.
+
+Discussion
+---------------------------------------------------------
+
+I think that not too many programmers are familiar with metaclasses and
+metatype conflicts, therefore let me be pedagogical ;)
+
+The simplest case where a metatype conflict happens is the following.
+Consider a class ``A`` with metaclass ``M_A`` and a class ``B`` with
+an independent metaclass ``M_B``; suppose we derive ``C`` from ``A``
+and ``B``. The question is: what is the metaclass of ``C`` ?
+Is it ``M_A`` or ``M_B`` ?
+
+ >>> class M_A(type):
+ ... pass
+ >>> class M_B(type):
+ ... pass
+ >>> class A(object):
+ ... __metaclass__=M_A
+ >>> class B(object):
+ ... __metaclass__=M_B
+ >>> class C(A,B):
+ ... pass
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The correct answer (see the book "Putting metaclasses to work" for a
+thought discussion) is ``M_C``, where ``M_C`` is a metaclass that inherits
+from ``M_A`` and ``M_B``, as in the following graph, where instantiation
+is denoted by colon lines:
+
+ ::
+
+
+ M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
+
+
+However, Python is not that magic, and it does not automatically create
+``M_C``. Instead, it raises a ``TypeError``, warning the programmer of
+the possible confusion. The metatype conflict can be avoided
+by assegning the correct metaclass to ``C`` by hand:
+
+ >>> class M_AM_B(M_A,M_B): pass
+ ...
+ >>> class C(A,B):
+ ... __metaclass__=M_AM_B
+ >>> C,type(C)
+ (<class 'C'>, <class 'M_AM_B'>)
+
+In general, a class ``A(B, C, D , ...)`` can be generated without conflicts
+only if ``type(A)`` is a subclass of each of ``type(B), type(C), ...``
+
+
+Another example where the conflict arise is the
+following:
+
+ >>> class D(A):
+ ... __metaclass__=M_B
+ Traceback (most recent call last):
+ File "<string>", line 1, in ?
+ TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+Here the problem is that since ``D`` inherits from ``A``, its metaclass must
+inherit from ``M_A`` and cannot be ``M_B``.
+
+It is possible to automatically avoid conflicts, by deriving the metaclasses
+from the ``safetype`` metaclass:
+
+ >>> from safetype import safetype as type
+ >>> class M_A(type):
+ ... pass
+ >>> class M_B(type):
+ ... pass
+ >>> class A(object):
+ ... __metaclass__=M_A
+ >>> class B(object):
+ ... __metaclass__=M_B
+ >>> class C(A,B):
+ ... pass
+ >>> C
+ <class 'C'>
+ >>> type(C) # automatically generated metaclass
+ <class 'safetype.M_AM_B'>
+
+In order to avoid to generate twice the same metaclass,,
+they are stored in a dictionary. In particular, when ``_generatemetaclass``
+is invoked with the same arguments it returns the same metaclass.
+
+ >>> class D(A,B):
+ ... pass
+ >>> type(D)
+ <class 'safetype.M_AM_B'>
+ >>> type(C) is type(D)
+ True
+
+``safetype`` solves the problem by automatically inheriting both from
+``M_A`` and ``M_B``:
+
+ >>> class D(A):
+ ... __metaclass__=M_B
+ >>> type(D)
+ <class 'safetype.M_BM_A'>
+
+``generatemetaclass`` automatically skips unneeded metaclasses,
+
+ >>> class M0(type): pass
+ ...
+ >>> class M1(M0): pass
+ ...
+ >>> class B: __metaclass__=M0
+ ...
+ >>> class C(B): __metaclass__=M1
+ ...
+ >>> print C,type(C)
+ <class 'C'> <class 'M1'>
+
+i.e. in this example where ``M1`` is a subclass of ``M0``, it
+returns ``M1`` and does not generate a redundant metaclass ``M0M1``.
+
+``makecls`` also solves the meta-metaclass conflict, and generic higher
+order conflicts:
+
+ >>> class MM1(type): pass
+ ...
+ >>> class MM2(type): pass
+ ...
+ >>> class M1(type): __metaclass__=MM1
+ ...
+ >>> class M2(type): __metaclass__=MM2
+ ...
+ >>> class A: __metaclass__=M1
+ ...
+ >>> class B: __metaclass__=M2
+ ...
+ >>> class C(A,B): pass
+ ...
+ >>> print C,type(C),type(type(C))
+ <class 'C'> <class 'safetype.M1M2'> <class 'safetype.MM1MM2'>
+
+----
+
+I thank David Mertz for help in polishing the original version of the code.
+This version has largerly profited from discussion with Phillip J. Eby.
+
+These examples here have been checked with doctest on Python 2.3.
diff --git a/pypers/meta/testnoconflict.py b/pypers/meta/testnoconflict.py
new file mode 100755
index 0000000..fc2983e
--- /dev/null
+++ b/pypers/meta/testnoconflict.py
@@ -0,0 +1,242 @@
+# testnoconflict.py
+from noconflict_alex import classmaker
+
+errormsg23 = "multiple bases have instance lay-out conflict"
+errormsg24 = """Error when calling the metaclass bases
+ multiple bases have instance lay-out conflict"""
+
+try:
+ set
+except NameError: # we are using Python 2.3
+ errormsg = errormsg23
+else: # we are using Python 2.4
+ errormsg = errormsg24
+
+# First, the trivial cases
+
+#single old-style-would-be class
+class C:
+ __metaclass__ = classmaker()
+
+# here needed_metas = ()
+assert C.__class__ is type
+
+#single new style class
+class C(object):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (type,)
+assert C.__class__ is type
+
+# class inheriting from old-style
+
+class O: pass # old-style
+
+class O_: pass # old-style
+
+try:
+ class C(O):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ assert "a new-style class can't have only classic bases" in str(e)
+
+try:
+ class C(O, O_):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ assert "a new-style class can't have only classic bases" in str(e)
+
+# inheriting from both old style and new style works
+
+class C(O, object):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (type,)
+
+assert type(C) is type
+
+# the simplest non-trivial case (M_A and M_B)
+
+class M_A(type): pass
+class M_B(type): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C(A,B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_A from left
+
+class C(B):
+ __metaclass__ = classmaker((M_A,))
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_B from right
+
+class C(A):
+ __metaclass__ = classmaker(right_metas = (M_B,))
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# redundant injections
+
+class C:
+ __metaclass__ = classmaker (left_metas = (M_B,), right_metas = (M_A, M_B,))
+
+# here needed_metas = (M_B, M_A), the last M_B is correctly removed
+assert C.__class__.__name__ == "_M_BM_A"
+
+# composing an old-style class and a metaclass
+
+class O: pass
+
+try:
+ class C(O, M_A):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# the other way around
+
+try:
+ class C(M_A, O):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing an new-style class and a metaclass
+
+class N(object): pass
+
+try:
+ class C(N, M_A):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# the other way around
+
+try:
+ class C(M_A, N):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing a non-trivial class and a metaclass
+
+class C(B, M_A):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# the other way around
+
+class C(M_A, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# a more bizarre hierarchy
+
+class C(B, M_B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, B, M_B, type, object]
+
+# changing the order
+
+class C(M_B, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, M_B, type, B, object]
+
+# meta-metaclasses
+
+class MM_X(type): pass
+class MM_Y(type): pass
+
+class M_X(type): __metaclass__ = MM_X
+class M_Y(type): __metaclass__ = MM_Y
+
+class X(type): __metaclass__ = M_X
+class Y(type): __metaclass__ = M_Y
+
+class Z(X,Y): __metaclass__ = classmaker()
+
+# here needed_metas = (M_X, M_Y)
+
+assert Z.__class__.__name__ == "_M_XM_Y"
+
+# in order to construct _M_XM_Y classmaker has to
+# construct _MM_XMM_Y first:
+
+assert Z.__class__.__class__.__name__ == "_MM_XMM_Y"
+
+class C(Z, B): __metaclass__ = classmaker()
+
+# here needed_metas = (_M_XM_Y, M_B)
+
+# composition of many metaclasses
+
+class M_A(type): pass
+class M_B(type): pass
+class M_C(M_B): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C: __metaclass__ = M_C
+class D: __metaclass__ = M_B
+
+class E(A, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+assert E.__class__.__name__ == "_M_AM_B"
+
+class F(C):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+
+assert F.__class__.__name__ == "M_C"
+
+class G(E, F):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (_M_AM_B, M_C)
+assert G.__class__.__name__ == "__M_AM_BM_C"
+
+## Test for C-coded metaclasses, i.e. Zope ExtensionClass
+
+try: # is Zope installed?
+ from OFS.Folder import Folder # an instance of Zope ExtensionClass
+
+ class C(Folder): # no problem here
+ __metaclass__ = classmaker()
+
+ assert C.__class__.__name__ == "ExtensionClass"
+
+ try:
+ class C(Folder):
+ __metaclass__ = classmaker((M_A,))
+ except TypeError, e:
+ e[1] == 'Incompatible root metatypes'
+
+except ImportError:
+ pass # will skip the ExtensionClass test
diff --git a/pypers/meta/testsafetype.py b/pypers/meta/testsafetype.py
new file mode 100755
index 0000000..b3c6336
--- /dev/null
+++ b/pypers/meta/testsafetype.py
@@ -0,0 +1,192 @@
+# testnoconflict.py
+from safetype import safetype as type
+
+errormsg23 = "multiple bases have instance lay-out conflict"
+errormsg24 = """Error when calling the metaclass bases
+ multiple bases have instance lay-out conflict"""
+
+try:
+ set
+except NameError: # we are using Python 2.3
+ errormsg = errormsg23
+else: # we are using Python 2.4
+ errormsg = errormsg24
+
+
+# the simplest non-trivial case (M_A and M_B)
+
+class M_A(type): pass
+class M_B(type): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C(A,B):
+ pass
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_A from left
+
+class C(B):
+ __metaclass__ = M_A
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_B from left
+
+class C(A):
+ __metaclass__ = M_B
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_BM_A"
+
+# composing an old-style class and a metaclass
+
+class O: pass
+
+try:
+ class C(O, M_A):
+ pass
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# the other way around
+
+try:
+ class C(M_A, O):
+ pass
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing an new-style class and a metaclass
+
+class N(object): pass
+
+try:
+ class C(N, M_A):
+ pass
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# the other way around
+
+try:
+ class C(M_A, N):
+ pass
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing a non-trivial class and a metaclass
+
+class C(B, M_A):
+ pass
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# the other way around
+
+class C(M_A, B):
+ pass
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# a more bizarre hierarchy
+
+class C(B, M_B):
+ pass
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, B, M_B, type, object]
+
+# changing the order
+
+class C(M_B, B):
+ pass
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, M_B, type, B, object]
+
+# meta-metaclasses
+
+class MM_X(type): pass
+class MM_Y(type): pass
+
+class M_X(type): __metaclass__ = MM_X
+class M_Y(type): __metaclass__ = MM_Y
+
+class X(type): __metaclass__ = M_X
+class Y(type): __metaclass__ = M_Y
+
+class Z(X,Y): pass
+
+# here needed_metas = (M_X, M_Y)
+
+assert Z.__class__.__name__ == "_M_XM_Y"
+
+# in order to construct _M_XM_Y classmaker has to
+# construct _MM_XMM_Y first:
+
+assert Z.__class__.__class__.__name__ == "_MM_XMM_Y"
+
+class C(Z, B): pass
+
+# here needed_metas = (_M_XM_Y, M_B)
+
+# composition of many metaclasses
+
+class M_A(type): pass
+class M_B(type): pass
+class M_C(M_B): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C: __metaclass__ = M_C
+class D: __metaclass__ = M_B
+
+class E(A, B):
+ pass
+
+# here needed_metas = (M_A, M_B)
+assert E.__class__.__name__ == "_M_AM_B"
+
+class F(C):
+ pass
+
+# here needed_metas = (M_A, M_B)
+
+assert F.__class__.__name__ == "M_C"
+
+class G(E, F):
+ pass
+
+# here needed_metas = (_M_AM_B, M_C)
+assert G.__class__.__name__ == "__M_AM_BM_C"
+
+## Test for C-coded metaclasses, i.e. Zope ExtensionClass
+
+try: # is Zope installed?
+ from OFS.Folder import Folder # an instance of Zope ExtensionClass
+
+ class C(Folder): # no problem here
+ pass
+
+ assert C.__class__.__name__ == "ExtensionClass"
+
+ try:
+ class C(Folder):
+ __metaclass__ = M_A
+ except TypeError, e:
+ e[1] == 'Incompatible root metatypes'
+
+except ImportError:
+ pass # will skip the ExtensionClass test
diff --git a/pypers/mro/Makefile b/pypers/mro/Makefile
new file mode 100755
index 0000000..64bb0fb
--- /dev/null
+++ b/pypers/mro/Makefile
@@ -0,0 +1,3 @@
+mro.html: mro.txt
+ /usr/local/bin/python /home/micheles/packages/docutils/tools/html.py \
+ --footnote-references=brackets mro.txt mro.html
diff --git a/pypers/mro/mettiinrete.py b/pypers/mro/mettiinrete.py
new file mode 100755
index 0000000..6801d47
--- /dev/null
+++ b/pypers/mro/mettiinrete.py
@@ -0,0 +1,19 @@
+"Upload the files which have been modified less than twelve hours ago"
+
+import os,time
+
+pscp=r"C:\WINDOWS\Desktop\Internet\pscp -pw IjrumHrG "
+alpha=" micheles@alpha.phyast.pitt.edu:public_html/python/"
+
+reftime=time.time()-3600*12 # twelve hours ago
+
+def copy_to(host,fname):
+ os.rename(fname,fname.lower())
+ os.system(pscp+'"'+fname+'"'+host)
+
+cd="d:\\mydocs\\pypers\\mro\\"
+
+for f in os.listdir(cd):
+ modtime=os.stat(f).st_mtime
+ if modtime>reftime and not f.endswith('.py'): copy_to(alpha,cd+f)
+
diff --git a/pypers/mro/mro.html b/pypers/mro/mro.html
new file mode 100755
index 0000000..6788fad
--- /dev/null
+++ b/pypers/mro/mro.html
@@ -0,0 +1,794 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>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">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 />
+<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="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 />
+<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="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 />
+<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="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="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 />
+<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 />
+<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="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 />
+<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 />
+<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="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="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="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>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="mro.txt">View document source</a>.
+Generated on: 2003-11-17 04:32 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/mro/mro.py b/pypers/mro/mro.py
new file mode 100755
index 0000000..6edf2c0
--- /dev/null
+++ b/pypers/mro/mro.py
@@ -0,0 +1,80 @@
+# 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)
+
diff --git a/pypers/mro/mro.txt b/pypers/mro/mro.txt
new file mode 100755
index 0000000..9fb59ff
--- /dev/null
+++ b/pypers/mro/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/mymodule.py b/pypers/mymodule.py
new file mode 100755
index 0000000..b4cf803
--- /dev/null
+++ b/pypers/mymodule.py
@@ -0,0 +1,3 @@
+"""
+This is my module.
+"""
diff --git a/pypers/mysecondscript.py b/pypers/mysecondscript.py
new file mode 100755
index 0000000..fa80d48
--- /dev/null
+++ b/pypers/mysecondscript.py
@@ -0,0 +1,3 @@
+import oopp
+print "True =",True,
+print "False =",False
diff --git a/pypers/notbug.txt b/pypers/notbug.txt
new file mode 100755
index 0000000..46702cd
--- /dev/null
+++ b/pypers/notbug.txt
@@ -0,0 +1,31 @@
+Ask why SyntaxError: because a dict is not hashable!
+
+from oopp import customized
+
+class Customizable(object):
+ """Classes inhering from 'Customizable' have a 'with' method acting as
+ an object modifier"""
+ def __getitem__(self,**kw):
+ print kw
+ with=customized
+ With=classmethod(customized)
+ class __metaclass__(type):
+ def __getitem__(cls,kw=None):
+ print kw
+
+c=Customizable()
+c.with(a='a')
+print c.a
+
+Customizable.With(b='b')
+
+#Customizable[a='b']
+
+print c.b
+
+print c[kw='a'] # SyntaxError: invalid syntax
+
+
+def f(kw='b'): print kw
+
+f(kw='a') # works
diff --git a/pypers/notes.txt b/pypers/notes.txt
new file mode 100755
index 0000000..0f9a720
--- /dev/null
+++ b/pypers/notes.txt
@@ -0,0 +1,56 @@
+
+ class SuperAware(type,Customizable):
+ """Instances of SuperAware inherit a private attribute __sup
+ which provide a handy way to call cooperative methods."""
+ # DOES NOT WORK WITH NEW, calls (super).__new__, not (super.__new__)
+ sup=super # default, customizable
+ def __init__(cls,*args):
+ setattr(cls,'_%s__sup' % cls.__name__,cls.sup(cls))
+ super(SuperAware,cls).__init__(*args) # usual cooperative call
+
+
+Notice that this trick comes from Guido himself (in his essay on
+"Type/class unification")
+
+Let me show how ``SuperAware`` can be used in practice.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+``__init__`` method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting
+feature in a separate mix-in class: then the feature can be added to the
+original class via multiple inheritance, without touching the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+``__init__`` method of the counter class must me cooperative. The
+SuperAware metaclass provides some syntactic sugar for this job:
+
+ ::
+
+ #<oopp.py>
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # __metaclass__=SuperAware
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # self.__sup.__init__(*args,**kw) # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # super(WithCounter,self).__init__(*args,**kw)
+ # # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
diff --git a/pypers/nre.html b/pypers/nre.html
new file mode 100755
index 0000000..035e8ff
--- /dev/null
+++ b/pypers/nre.html
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Module nre</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="module-nre">
+<h1 class="title">Module <tt class="literal"><span class="pre">nre</span></tt></h1>
+<p>nre - new style regular expressions</p>
+<p>The <tt class="literal"><span class="pre">nre</span></tt> module adopt an object-oriented approach to regular expressions.
+It provides only a class, <tt class="literal"><span class="pre">Regexp</span></tt>, which instances are regular expression
+objects, or reobj for short. It can be imported with</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from nre import Regexp
+</pre>
+</blockquote>
+<p>(notice that <tt class="literal"><span class="pre">from</span> <span class="pre">nre</span> <span class="pre">import</span> <span class="pre">*</span></tt> would have the same effect).</p>
+<div class="section" id="classes">
+<h1><a name="classes">Classes</a></h1>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">Regexp(object):</span></tt></p>
+<blockquote>
+<p>The class of regular expression objects. Instantiated with the signature</p>
+<p>reobj=Regexp(pattern,name=None,doc=None,isgroup=False)</p>
+<p>pattern is a (raw) string which can be associated with a valid
+regular expression; it the pattern contains an sequence ' #',
+only the chars on the left of ' #' are retained, whereas all
+the chars on the right of ' #' are assumed to be a comment.
+If you want to avoid this interpretation, you must escape #.
+Then the comment is stripped with the pattern and assigned with
+the docstring, unless an explicit docstring was already provided.
+if name is not None, associates a name to the reobj;
+if doc is not None, associates a docstring to the reobj;
+if isgroup is True, converts the regexp in a named group
+<tt class="literal"><span class="pre">(?P&lt;name&gt;regexp)</span></tt> or an numbered group <tt class="literal"><span class="pre">(regexp)</span></tt> if
+name is None.</p>
+<p>re-objects are callable. For instance</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a=Regexp('x # named group')('a')
+</pre>
+<p>returns a named group regular expression, whereas</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; b=Regexp('x # numbered group')
+</pre>
+<p>returns a numbered group regexp.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print a
+&gt;&gt;&gt; print b
+</pre>
+<p><tt class="literal"><span class="pre">compose(self,other,oper):</span></tt></p>
+<blockquote>
+Compose regular expression objects with strings or with themselves.
+<tt class="literal"><span class="pre">oper</span></tt> is one of the strings &quot;__add__&quot;, &quot;__radd__&quot;, &quot;__or__&quot;,
+&quot;__ror__&quot;.</blockquote>
+<p><tt class="literal"><span class="pre">__ror__(self,other):</span></tt></p>
+<blockquote>
+<p>Combines a regular expression object to a string
+or another reobj using <tt class="literal"><span class="pre">|</span></tt> . Works from the right. Example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print 'x' | Regexp('y')
+&lt;reobj '&lt;noname&gt;':x|y&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">__radd__(self,other):</span></tt></p>
+<blockquote>
+<p>Adds a regular expression object to a string or another
+reobj. Works from the right. Example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print 'x' + Regexp('y')
+&lt;reobj '&lt;noname&gt;':xy&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">__or__(self,other):</span></tt></p>
+<blockquote>
+<p>Combines a regular expression object to a string
+or to another reobj using <tt class="literal"><span class="pre">|</span></tt> . Works from the left. Example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print Regexp('x') | 'y'
+&lt;reobj '&lt;noname&gt;':x|y&gt;
+</pre>
+<p>Works well with named reobj too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a=Regexp('x',name='a')
+&gt;&gt;&gt; b=Regexp('y',name='b')
+&gt;&gt;&gt; print a|b
+&lt;reobj 'a__b':x|y&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">__add__(self,other):</span></tt></p>
+<blockquote>
+<p>Adds a regular expression object to a string or
+to another reobj. Works from the left. Example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print Regexp('x') + 'y'
+&lt;reobj '&lt;noname&gt;':xy&gt;
+</pre>
+<p>Works well with named reobj too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a=Regexp('x',name='a')
+&gt;&gt;&gt; b=Regexp('y',name='b')
+&gt;&gt;&gt; print a+b
+&lt;reobj 'a_b':xy&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">__call__(self,name=None):</span></tt></p>
+<blockquote>
+Returns a grouped regular expression object. The group is named
+if the regexp was already named or if an explicit name is passed.</blockquote>
+</blockquote>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="nre.rst">View document source</a>.
+Generated on: 2003-09-23 10:57 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/objects.txt b/pypers/objects.txt
new file mode 100755
index 0000000..7d8bf77
--- /dev/null
+++ b/pypers/objects.txt
@@ -0,0 +1,916 @@
+THE BEAUTY OF OBJECTS
+===========================================================================
+
+In this chapter I will show how to define generic objects in Python, and
+how to manipulate them.
+
+User defined objects
+--------------------------------------------------------------------------
+
+In Python, one cannot directly modify methods and attributes of built-in
+types, since this would be a potentially frightening source of bugs.
+Imagine for instance of changing the sort method of a list and invoking an
+external module expecting the standard sort: all kind of hideous outcome
+could happen.
+
+Nevertheless, in Python, as in all OOP languages, the user can define
+her own kind of objects, customized to satisfy her needs. In order to
+define a new object, the user must define the class of the objects she
+needs. The simplest possible class is a do-nothing class:
+
+ ::
+
+ #<oopp.py>
+
+ class Object(object):
+ "A convenient Object class"
+
+ #</oopp.py>
+
+Elements of the ``Object`` class can be created (instantiated) quite
+simply:
+
+ >>> from oopp import Object
+ >>> obj1=Object()
+ >>> obj1
+ <oopp.Object object at 0x81580ec>
+ >>> obj2=Object()
+ obj2
+ <object.Object object at 0x8156704>
+
+Notice that the hexadecimal number 0x81580ec is nothing else that the
+unique object reference to ``obj1``
+
+ >>> hex(id(obj1))
+ '0x81580ec'
+
+whereas 0x8156704 is the object reference of ``obj2``:
+
+ >>> hex(id(obj2))
+ '0x8156704'
+
+However, at this point ``obj1`` and ``obj2`` are generic
+doing nothing objects . Nevertheless, they have
+at least an useful attribute, the class docstring:
+
+ >>> obj1.__doc__ #obj1 docstring
+ 'A convenient Object class'
+ >>> obj2.__doc__ # obj2 docstring: it's the same
+ 'A convenient Object class'
+
+Notice that the docstring is associate to the class and therefore all
+the instances share the same docstring, unless one explicitly assigns
+a different docstring to some instance. ``__doc__``
+is a class attribute (or a static attribute for readers familiar with the
+C++/Java terminology) and the expression is actually syntactic sugar for
+
+ >>> class Object(object): # with explicit assignement to __doc__
+ ... __doc__ = "A convenient Object class"
+
+
+Since instances of 'Object' can be modified, I can transform them in
+anything I want. For instance, I can create a simple clock:
+
+ >>> myclock=Object()
+ >>> myclock
+ <__main__.Object object at 0x8124614>
+
+A minimal clock should at least print the current time
+on the system. This is given by the ``get_time`` function
+we defined in the first chapter. We may "attach" that function
+to our clock as follows:
+
+ >>> import oopp
+ >>> myclock.get_time=oopp.get_time
+ >>> myclock.get_time # this is a function, not a method
+ <function get_time at 0x815c40c>
+
+In other words, we have converted the ``oopp.get_time`` function to a
+``get_time`` function of the object ``myclock``. The procedure works
+
+ >>> myclock.get_time()
+ '15:04:57'
+
+but has a disadvantage: if we instantiate another
+clock
+
+ >>> from oopp import Object
+ >>> otherclock=Object()
+
+the other clock will ``not`` have a get_time method:
+
+ >>> otherclock.get_time() #first attempt; error
+ AttributeError: 'Object' object has no attribute 'get_time'
+
+Notice instead that the docstring is a *class attribute*, i.e. it
+is defined both for the class and *all instances* of the class,
+therefore even for ``otherclock``:
+
+ >>> Object.__doc__
+ 'A convenient Object class'
+ >>> otherclock.__doc__
+ 'A convenient Object class'
+
+We would like to convert the ``get_time`` function to a
+``get_time`` method for the *entire* class 'Object', i.e. for all its
+instances. Naively, one would be tempted to write the following:
+
+ >>> Object.get_time = oopp.get_time
+
+However this would not work:
+
+ >>> otherclock.get_time() #second attempt; still error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: oopp.get_time() takes no arguments (1 given)
+
+This error message is something that all Python beginners encounter
+(and sometimes even non-beginners ;-). The solution is to introduce
+an additional argument:
+
+ >>> Object.get_time=lambda self : oopp.get_time()
+ >>> otherclock.get_time # this is method now, not a function
+ <bound method Object.<lambda> of <__main__.Object object at 0x815881c>>
+ >>> otherclock.get_time() #third attempt
+ '15:28:41'
+
+Why this works ? The explanation is the following:
+when Python encounters an expression of the form
+``objectname.methodname()`` it looks if there is a already a method
+*attached* to the object:
+
+ a. if yes it invokes it with no arguments
+ (this is why our first example worked);
+ b. if not it looks at the class of the object; if there is a method
+ bound to the class it invokes that method *by passing the
+ object as first argument*.
+
+When we invoked ``otherclock.get_time()`` in our second attempt, Python
+found that the function ``get_time`` was defined at the class level,
+and sent it the ``otherclock`` object as first argument: however ``get_time``
+was bind to ``func_get_time``, which is function with *no* arguments: whence
+the error message. The third attempt worked since, thanks to the
+lambda function trick, the ``get_time`` function has been converted to
+a function accepting a first argument.
+
+Therefore that's the rule: in Python, one can define methods
+at the class level, provided one explitely introduces a first argument
+containing the object on which the method is invoked.
+
+This first argument is traditionally called ``self``; the name 'self' is not
+enforced, one could use any other valid Python identifier, however the
+convention is so widespread that practically everybody uses it;
+pychecker will even raise a warning in the case you don't follow the
+convention.
+
+I have just shown one the most interesting features of Python, its
+*dynamicity*: you can create the class first and add methods to it later.
+That logic cannot be followed in typical compiled language as C++. On the
+other hand, one can also define methods in a static, more traditional way:
+
+ ::
+
+ #<clock1.py>
+
+ "Shows how to define methods inside the class (statically)"
+
+ import oopp
+
+ class Clock(object):
+ 'Clock class; version 0.1'
+ def get_time(self): # method defined inside the class
+ return oopp.get_time()
+
+ myclock=Clock() #creates a Clock instance
+ print myclock.get_time() # print the current time
+
+ #</clock1.py>
+
+In this case we have defined the ``get_time`` method inside the class as a
+normal function with an explicit first argument called self; this is
+entirely equivalent to the use of a lambda function.
+
+The syntax ``myclock.get_time()`` is actually syntactic sugar for
+``Clock.get_time(myclock)``.
+
+In this second form, it is clear the ``get_time`` is really "attached" to the
+class, not to the instance.
+
+Objects have static methods and classmethods
+-----------------------------------------------------------------------------
+
+ .. line-block::
+
+ *There should be one--and preferably only one--obvious way to do it*
+ -- Tim Peters, *The Zen of Python*.
+
+
+For any rule, there is an exception, and despite the Python's motto
+there are many ways to define methods in classes. The way I presented
+before was the obvious one before the Python 2.2 revolution; however,
+nowadays there is another possibility that, even if less obvious, has the
+advantage of some elegance (and it is also slightly more efficient too, even if
+efficiency if never a primary concern for a Python programmer).
+We see that the first argument in the ``get_time`` method is useless,
+since the time is computed from the ``time.asctime()`` function which
+does not require any information about the object that is calling
+it. This waste is ugly, and since according to the Zen of Python
+
+ *Beautiful is better than ugly.*
+
+we should look for another way. The solution is to use a *static method*:
+when a static method is invoked, the calling object is *not* implicitly passed
+as first argument. Therefore we may use a normal function with no additional
+first argument to define the ``get_time`` method:
+
+ ::
+
+ #<oopp.py>
+
+ class Clock(object):
+ 'Clock with a staticmethod'
+ get_time=staticmethod(get_time)
+
+ #</oopp.py>
+
+Here is how it works:
+
+ >>> from oopp import Clock
+ >>> Clock().get_time() # get_time is bound both to instances
+ '10:34:23'
+ >>> Clock.get_time() # and to the class
+ '10:34:26'
+
+The staticmethod idiom converts the lambda function to a
+static method of the class 'Clock'. Notice that one can avoid the
+lambda expression and use the (arguably more Pythonic) idiom
+
+ ::
+
+ def get_time()
+ return oopp.get_time()
+ get_time=staticmethod(oopp.get_time)
+
+as the documentation suggests:
+
+ >>> print staticmethod.__doc__
+ staticmethod(function) -> method
+ Convert a function to be a static method.
+ A static method does not receive an implicit first argument.
+ To declare a static method, use this idiom:
+ class C:
+ def f(arg1, arg2, ...): ...
+ f = staticmethod(f)
+ It can be called either on the class (e.g. C.f()) or on an instance
+ (e.g. C().f()). The instance is ignored except for its class.
+ Static methods in Python are similar to those found in Java or C++.
+ For a more advanced concept, see the classmethod builtin.
+
+At the present the notation for static methods is still rather ugly,
+but it is expected to improve in future versions of Python (probably
+in Python 2.4). Documentation for static methods can
+be found in Guido's essay and in the PEP.. : however this is intended for
+developers.
+
+As the docstring says, static methods are also "attached" to the
+class and may be called with the syntax ``Clock.get_time()``.
+
+A similar remark applies for the so called *classmethods*:
+
+ >>> print classmethod.__doc__
+ classmethod(function) -> method
+ Convert a function to be a class method.
+ A class method receives the class as implicit first argument,
+ just like an instance method receives the instance.
+ To declare a class method, use this idiom:
+ class C:
+ def f(cls, arg1, arg2, ...): ...
+ f = classmethod(f)
+ It can be called either on the class (e.g. C.f()) or on an instance
+ (e.g. C().f()). The instance is ignored except for its class.
+ If a class method is called for a derived class, the derived class
+ object is passed as the implied first argument.
+ Class methods are different than C++ or Java static methods.
+ If you want those, see the staticmethod builtin.
+
+
+#When a regular method is invoked, a reference to the calling object is
+#implicitely passed as first argument; instead, when a static method is
+#invoked, no reference to the calling object is passed.
+
+As the docstring says, classmethods are convenient when one wants to pass
+to a method the calling *class*, not the calling object. Here there is an
+example:
+
+ >>> class Clock(object): pass
+ >>> Clock.name=classmethod(lambda cls: cls.__name__)
+ >>> Clock.name() # called by the class
+ 'Clock'
+ >>> Clock().name() # called by an instance
+ 'Clock'
+
+Notice that classmethods (and staticmethods too)
+can only be attached to classes, not to objects:
+
+ >>> class Clock(object): pass
+ >>> c=Clock()
+ >>> c.name=classmethod(lambda cls: cls.__name__)
+ >>> c.name() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: 'classmethod' object is not callable
+
+gives a TypeError. The reason is that classmethods and staticmethods
+are implemented
+trough *attribute descriptors*. This concept will be discussed in detail in a
+forthcoming in chapter 6.
+
+Notice that classmethods are not proving any fundamental feature, since
+one could very well use a normal method and retrieve the class with
+``self.__class__`` as we did in the first chapter.
+Therefore, we could live without (actually, I think they are a non-essential
+complication to the language).
+Nevertheless, now that we have them, we can use them, since
+they come handy in various circumstances, as we will see in the following.
+
+Objects have their privacy
+---------------------------------------------------------------------------
+
+In some situations, it is convenient to give to the developer
+some information that should be hided to the final user. To this
+aim Python uses private names (i.e. names starting with a single
+underscore) and private attributes (i.e. attributes starting with
+a double underscore).
+
+
+Consider for instance the following script:
+
+ ::
+
+ #<privacy.py>
+
+ import time
+
+ class Clock(object):
+ __secret="This Clock is quite stupid."
+
+ myclock=Clock()
+ try: print myclock.__secret
+ except Exception,e: print "AttributeError:",e
+
+ #</privacy.py>
+
+The output of this script is
+
+ ::
+
+ AttributeError: 'Clock' object has no attribute '__secret'
+
+Therefore, even if the Clock object *does* have a ``__secret`` attribute,
+the user cannot access it ! In this way she cannot discover that
+actually "This Clock is quite stupid."
+
+In other programming languages, attributes like ``__secret`` are
+called "private" attributes. However, in Python private attributes
+are not really private and their secrets can be accessed with very
+little effort.
+
+First of all, we may notice that ``myclock`` really contains a secret
+by using the builtin function ``dir()``:
+
+ ::
+
+ dir(myclock)
+ ['_Clock__secret', '__class__', '__delattr__', '__dict__', '__doc__',
+ '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
+ '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__']
+
+We see that the first attribute of myclock is '_Clock__secret``,
+which we may access directly:
+
+ ::
+
+ print myclock._Clock__secret
+ This clock is quite stupid.
+
+We see here the secret of private variables in Python: the *name mangling*.
+When Python sees a name starting with two underscores (and not ending
+with two underscores, otherwise it would be interpreted as a special
+attribute), internally it manage it as ``_Classname__privatename``.
+Notice that if 'Classname' begins with underscores, the leading underscores
+are stripped in such a way to guarantee that the private name starts with
+only *one* underscore. For instance, the '__secret' private attribute
+of classes such as 'Clock', '_Clock', '__Clock', '___Clock', etc. is
+mangled to '_Clock__secret'.
+
+Private names in Python are *not* intended to keep secrets: they
+have other uses.
+
+1. On one hand, private names are a suggestion to the developer.
+ When the Python programmer sees a name starting with one or two
+ underscores in a program written by others, she understands
+ that name should not be of concern for the final user, but it
+ only concerns the internal implementation.
+
+2. On the other hand, private names are quite useful in class
+ inheritance, since they provides safety with respect to the overriding
+ operation. This point we will discussed in the next chapter.
+
+3. Names starting with an underscore are not imported by the
+ statement ``from module import *``
+
+Remark: it makes no sense to define names with double underscores
+outside classes, since the name mangling doesn't work in this case.
+Let me show an example:
+
+ >>> class Clock(object): __secret="This Clock is quite stupid"
+ >>> def tellsecret(self): return self.__secret
+ >>> Clock.tellsecret=tellsecret
+ >>> Clock().tellsecret() #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 2, in tellsecret
+ AttributeError: 'Clock' object has no attribute '__secret'
+
+The explanation is that since ``tellsecret()`` is defined outside the class,
+``__secret`` is not expanded to ``_Clock__secret`` and therefore cannot be
+retrieved, whereas
+
+ >>> class Clock(object):
+ ... __secret="This Clock is quite stupid"
+ ... def tellsecret(self): return self.__secret
+ >>> Clock().tellsecret()
+ This Clock is quite stupid
+
+will work. In other words, private variables are attached to classes.
+
+Objects have properties
+-------------------------------------------------------------------------------
+
+In the previous section we have shown that private variables are of
+little use for keeping secrets: if a developer really wants to restrict
+the access to some methods or attributes, she has to resort to
+*properties*.
+
+Let me show an example:
+
+ ::
+
+ #<secret.py>
+
+ import oopp
+
+ class Clock(object):
+ 'Clock class with a secret'
+
+ you_know_the_pw = False #default
+
+ def give_pw(self, pw):
+ """Check if your know the password. For security, one should crypt
+ the password."""
+ self.you_know_the_pw = (pw == "xyz")
+
+ def get_secret(self):
+ if self.you_know_the_pw:
+ return "This clock doesn't work."
+ else:
+ return "You must give the right password to access 'secret'"
+
+ secret = property(get_secret)
+
+ c = Clock()
+ print c.secret # => You must give the right password to access 'secret'
+ c.give_pw('xyz') # gives the right password
+ print c.secret # => This clock doesn't work.
+ print Clock.secret # => <property object at 0x814c1b4>
+
+ #</secret.py>
+
+In this script, one wants to restrict the access to the attribute
+'secret', which can be accessed only is the user provide the
+correct password. Obviously, this example is not very secure,
+since I have hard coded the password 'xyz' in the source code,
+which is easily accessible. In reality, one should crypt the
+password a perform a more sophisticated test than the trivial
+check ``(pw=="xyz")``; anyway, the example is only intended to
+shown the uses of properties, not to be really secure.
+
+The key action is performed by the descriptor class ``property`` that
+converts the function ``get_secret`` in a property object. Additional
+informations on the usage of ``property`` can be obtained from the
+docstring:
+
+ >>> print property.__doc__
+ property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
+ fget is a function to be used for getting an attribute value, and likewise
+ fset is a function for setting, and fdel a function for del'ing, an
+ attribute. Typical use is to define a managed attribute x:
+ class C(object):
+ def getx(self): return self.__x
+ def setx(self, value): self.__x = value
+ def delx(self): del self.__x
+ x = property(getx, setx, delx, "I'm the 'x' property.")
+
+Properties are another example of attribute descriptors.
+
+Objects have special methods
+---------------------------------------------------------------------------
+
+From the beginning, we stressed that objects have special attributes that
+may turn handy, as for instance the docstring ``__doc__`` and the class
+name attribute ``__class__``. They have special methods, too.
+
+With little doubt, the most useful special method is the ``__init__``
+method, that *initializes* an object right after its creation. ``__init__``
+is typically used to pass parameters to *object factories*. Let me an
+example with geometric figures:
+
+ ::
+
+ #<oopp.py>
+
+ class GeometricFigure(object): #an example of object factory
+ """This class allows to define geometric figures according to their
+ equation in the cartesian plane. It will be extended later."""
+ def __init__(self,equation,**parameters):
+ "Specify the cartesian equation of the object and its parameters"
+ self.eq=equation
+ self.par=parameters
+ for k,v in self.par.items(): #replaces the parameters in the equation
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq,{})
+ # dynamically creates the function 'contains'
+
+ #</oopp.py>
+
+Here it is how it works:
+
+ >>> from oopp import *
+ >>> disk=GeometricFigure('(x-x0)**2+(y-y0)**2 <= r**2', x0=0,y0=0,r=5)
+ >>> # creates a disk of radius 5 centered in the origing
+ >>> disk.contains(1,2) #asks if the point (1,2) is inside the disk
+ True
+ >>> disk.contains(4,4) #asks if the point (4,4) is inside the disk
+ False
+
+
+Let me continue the section on special methods with some some observations on
+``__repr__`` and ``__str__``.Notice that I
+will not discuss all the subtleties; for a thought discussion, see the
+thread "Using __repr__ or __str__" in c.l.p. (Google is your friend).
+The following discussion applies to new style classes, old style classes
+are subtly different; moreover.
+
+When one writes
+
+ >>> disk
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+one obtains the *string representation* of the object. Actually, the previous
+line is syntactic sugar for
+
+ >>> print repr(disk)
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+or
+
+ >>> print disk.__repr__()
+ <oopp.GeometricFigure instance at 0x81b496c>
+
+The ``repr`` function extracts the string representation from the
+the special method ``__repr__``, which can be redefined in order to
+have objects pretty printed. Notice that ``repr`` is conceptually
+different from the ``str`` function that controls the output of the ``print``
+statement. Actually, ``print o`` is syntactic sugar for ``print str(o)``
+which is sugar for ``print o.__str__()``.
+
+If for instance we define
+
+ ::
+
+ #<oopp.py>
+
+ class PrettyPrinted(object):
+ formatstring='%s' # default
+ def __str__(self):
+ """Returns the name of self in quotes, possibly formatted via
+ self.formatstring. If self has no name, returns the name
+ of its class in angular brackets."""
+ try: #look if the selfect has a name
+ name="'%s'" % self.__name__
+ except AttributeError: #if not, use the name of its class
+ name='<%s>' % type(self).__name__
+ if hasattr(self,'formatstring'):
+ return self.formatstring % name
+ else:
+ return name
+
+ #</oopp.py>
+
+then we have
+
+ >>> from oopp import PrettyPrinted
+ >>> o=PrettyPrinted() # o is an instance of PrettyPrinted
+ >>> print o #invokes o.__str__() which in this case returns o.__class__.name
+ <PrettyPrinted>
+
+whereas
+
+ >>> o # i.e. print repr(o)
+ <oopp.PrettyPrinted object at 0x400a006c>
+
+However, in most cases ``__repr__`` and ``__str__`` gives the same
+output, since if ``__str__`` is not explicitely defined it defaults
+to ``__repr__``. Therefore, whereas modifying ``__str__``
+does not change ``__repr__``, modifying ``__repr__`` changes ``__str__``,
+if ``__str__`` is not explicitely given:
+
+ ::
+
+ #<fairytale1.py>
+
+ "__repr__ can also be a regular method, not a classmethod"
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __str__(self):
+ return "I am a "+self.attributes+' '+self.__class__.__name__
+
+ jack=Frog(); print repr(jack),jack
+ charles=Prince(); print repr(charles),charles
+
+ #</fairytale1.py>
+
+The output of this script is:
+
+ ::
+
+ <Frog object at 0x81866ec> I am a poor, small, ugly Frog
+ <Prince object at 0x818670c> I am a rich, tall, beautiful Prince
+
+for jack and charles respectively.
+
+``__str__`` and ``__repr__`` are also called by the formatting
+operators "%s" and "%r".
+
+Notice that i) ``__str__`` can be most naturally
+rewritten as a class method; ii) Python is magic:
+
+ ::
+
+ #<fairytale2.py>
+
+ """Shows two things:
+ 1) redefining __repr__ automatically changes the output of __str__
+ 2) the class of an object can be dinamically changed! """
+
+ class Frog(object):
+ attributes="poor, small, ugly"
+ def __repr__(cls):
+ return "I am a "+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+ class Prince(object):
+ attributes='rich, tall, beautiful'
+ def __repr__(cls):
+ return "I am a "+cls.attributes+' '+cls.__name__
+ __repr__=classmethod(__repr__)
+
+ def princess_kiss(frog):
+ frog.__class__=Prince
+
+ jack=Frog()
+ princess_kiss(jack)
+ print jack # the same as repr(jack)
+
+ #</fairytale2.py>
+
+Now the output for jack is "I am a rich, tall, beautiful Prince" !
+In Python you may dynamically change the class of an object!!
+
+Of course, this is a feature to use with care ;-)
+
+There are many others special methods, such as __new__, __getattr__,
+__setattr__, etc. They will be discussed in the next chapters, in
+conjunction with inheritance.
+
+Objects can be called, added, subtracted, ...
+---------------------------------------------------------------------------
+
+Python provides a nice generalization of functions, via the concept
+of *callable objects*. A callable object is an object with a ``__call__``
+special method. They can be used to define "functions" that remember
+how many times they are invoked:
+
+ ::
+
+ #<call.py>
+
+ class MultiplyBy(object):
+ def __init__(self,n):
+ self.n=n
+ self.counter=0
+ def __call__(self,x):
+ self.counter+=1
+ return self.n*x
+
+ double=MultiplyBy(2)
+ res=double(double(3)) # res=12
+ print "double is callable: %s" % callable(double)
+ print "You have called double %s times." % double.counter
+
+ #</call.py>
+
+With output
+
+ ::
+
+ double is callable: True
+ You have called double 2 times.
+
+The script also show that callable objects (including functions)
+can be recognized with the ``callable`` built-in function.
+
+Callable object solves elegantly the problem of having "static" variables
+inside functions (cfr. with the 'double' example in chapter 2).
+A class with a ``__call__`` method can be used to generate an entire
+set of customized "functions". For this reason, callable objects are
+especially useful in the conjunction with object factories. Let me show
+an application to my factory of geometric figures:
+
+ ::
+
+ #<oopp.py>
+
+ class Makeobj(object):
+ """A factory of object factories. Makeobj(cls) returns instances
+ of cls"""
+ def __init__(self,cls,*args):
+ self.cls=cls
+ self.args=args
+ def __call__(self,**pars):
+ return self.cls(*self.args,**pars)
+
+ #</oopp.py>
+
+ #<factory.py>
+
+ from oopp import Makeobj,GeometricFigure
+
+ makedisk=Makeobj(GeometricFigure,'(x-x0)**2+(y-y0)**2<r**2')
+ makesquare=Makeobj(GeometricFigure,'abs(x-x0)<L and abs(y-y0)<L')
+ disk=makedisk(x0=0,y0=0,r=10) # make a disk of radius 10
+ square=makesquare(x0=0,y0=0,L=20) # make a disk of side 10
+
+ print disk.contains(9,9) # => False
+ print square.contains(9,9) # => True
+ #etc.
+
+ #</factory.py>
+
+This factory generates callable objects, such as ``makedisk`` and
+``makesquare`` that returns geometric objects. It gives a nicer interface
+to the object factory provided by 'GeometricFigure'.
+
+Notice that the use of the expression ``disk.contains(9,9)`` in order to
+know if the point of coordinates (9,9) is contained in the disk, it is
+rather inelegant: it would be much better to be able to ask if
+``(9,9) in disk``. This is possibile, indeed: and the secrets is to
+define the special method ``__contains__``. This is done in the next
+example, that I think give a good taste of the beauty of objects
+
+ ::
+
+ #<funnyformatter.py>
+
+ from oopp import Makeobj
+
+ Nrow=50; Ncol=78
+
+ class GeometricFigure(object):
+ """This class allows to define geometric figures according to their
+ equation in the cartesian plane. Moreover addition and subtraction
+ of geometric figures are defined as union and subtraction of sets."""
+ def __init__(self,equation,**parameters):
+ "Initialize "
+ self.eq=equation
+ self.par=parameters
+ for (k,v) in self.par.items(): #replaces the parameters
+ self.eq=self.eq.replace(k,str(v))
+ self.contains=eval('lambda x,y : '+self.eq,{})
+ def combine(self,fig,operator):
+ """Combine self with the geometric figure fig, using the
+ operators "or" (addition) and "and not" (subtraction)"""
+ comboeq="("+self.eq+")"+operator+"("+fig.eq+")"
+ return GeometricFigure(comboeq)
+ def __add__(self,fig):
+ "Union of sets"
+ return self.combine(fig,' or ')
+ def __sub__(self,fig):
+ "Subtraction of sets"
+ return self.combine(fig,' and not')
+ def __contains__(self,point): #point is a tuple (x,y)
+ return self.contains(*point)
+
+ makedisk=Makeobj(GeometricFigure,'(x-x0)**2/4+(y-y0)**2 <= r**2')
+ upperdisk=makedisk(x0=38,y0=7,r=5)
+ smalldisk=makedisk(x0=38,y0=30,r=5)
+ bigdisk=makedisk(x0=38,y0=30,r=14)
+
+ def format(text,shape):
+ "Format the text in the shape given by figure"
+ text=text.replace('\n',' ')
+ out=[]; i=0; col=0; row=0; L=len(text)
+ while 1:
+ if (col,row) in shape:
+ out.append(text[i]); i+=1
+ if i==L: break
+ else:
+ out.append(" ")
+ if col==Ncol-1:
+ col=0; out.append('\n') # starts new row
+ if row==Nrow-1: row=0 # starts new page
+ else: row+=1
+ else: col+=1
+ return ''.join(out)
+
+ composition=bigdisk-smalldisk+upperdisk
+ print format(text='Python Rules!'*95,shape=composition)
+
+ #</funnyformatter.py>
+
+I leave as an exercise for the reader to understand how does it work and to
+play with other geometric figures (he can also generate them trough the
+'Makeobj' factory). I think it is nicer to show its output:
+
+ ::
+
+
+ Pyt
+ hon Rules!Pyt
+ hon Rules!Python
+ Rules!Python Rules!
+ Python Rules!Python
+ Rules!Python Rules!P
+ ython Rules!Python
+ Rules!Python Rules!
+ Python Rules!Pyth
+ on Rules!Pyth
+ on
+
+
+
+ Rul
+ es!Python Rules!Pytho
+ n Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Pyth
+ on Rules!Python Rules!Python Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Pyt
+ hon Rules!Python Rules!Python Rules!Python Rules!
+ Python Rules!Python Rules!Python Rules!Python Rules
+ !Python Rules!Python Rule s!Python Rules!Python Rul
+ es!Python Rules!Pyth on Rules!Python Rule
+ s!Python Rules!Pyth on Rules!Python Rul
+ es!Python Rules!Py thon Rules!Python
+ Rules!Python Rules !Python Rules!Pyth
+ on Rules!Python Ru les!Python Rules!P
+ ython Rules!Python Rules!Python Rule
+ s!Python Rules!Pyt hon Rules!Python R
+ ules!Python Rules!P ython Rules!Python
+ Rules!Python Rules!P ython Rules!Python R
+ ules!Python Rules!Python Rules!Python Rules!Python
+ Rules!Python Rules!Python Rules!Python Rules!Pytho
+ n Rules!Python Rules!Python Rules!Python Rules!Py
+ thon Rules!Python Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rules!P
+ ython Rules!Python Rules!Python Rul
+ es!Python Rules!Python Rules!
+ Python Rules!Python R
+ ule
+
+
+
+
+
+
+
+ s!
+
+Remark.
+
+Unfortunately, "funnyformatter.py" does not reuse old code: in spite of the
+fact that we already had in our library the 'GeometricFigure' class, with
+an "__init__" method that is exactly the same of the "__init__" method in
+"funnyformatter.py", we did not reuse that code. We simply did a cut
+and paste. This means that if we later find a bug in the ``__init__`` method,
+we will have to fix it twice, both in the script and in the library. Also,
+if we plan to extend the method later, we will have to extend it twice.
+Fortunately, this nasty situation can be avoided: but this requires the
+power of inheritance.
diff --git a/pypers/oldstuff.txt b/pypers/oldstuff.txt
new file mode 100755
index 0000000..9cb045e
--- /dev/null
+++ b/pypers/oldstuff.txt
@@ -0,0 +1,752 @@
+A few useful classes
+--------------------------------------------------------------------------
+
+::
+
+ #<oopp.py>
+
+ class DevNull(object):
+ """This class fakes a null file. For instance
+ "print >> DevNull, obj" trows away the object,
+ i.e. nothing is written at all."""
+ write=staticmethod(lambda text:None) # do nothing
+
+ #</oopp.py>
+
+
+Let me conclude, now, with a discussion of when it is appropriate
+to use the dynamical features of Python to enhance instance objects.
+The answer is: more or less never.
+
+-----------------------------------------------------------------------
+
+ class SuperAware(type,Customizable):
+ """Instances of SuperAware inherit a private attribute __sup
+ which provide a handy way to call cooperative methods."""
+ # DOES NOT WORK WITH NEW, calls (super).__new__, not (super.__new__)
+ sup=super # default, customizable
+ def __init__(cls,*args):
+ setattr(cls,'_%s__sup' % cls.__name__,cls.sup(cls))
+ super(SuperAware,cls).__init__(*args) # usual cooperative call
+
+
+Notice that this trick comes from Guido himself (in his essay on
+"Type/class unification")
+
+Let me show how ``SuperAware`` can be used in practice.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+``__init__`` method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting
+feature in a separate mix-in class: then the feature can be added to the
+original class via multiple inheritance, without touching the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+``__init__`` method of the counter class must me cooperative. The
+SuperAware metaclass provides some syntactic sugar for this job:
+
+ ::
+
+ #<oopp.py>
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # __metaclass__=SuperAware
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # self.__sup.__init__(*args,**kw) # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # super(WithCounter,self).__init__(*args,**kw)
+ # # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
+
+Overcoming the limitations of ``super``
+---------------------------------------------------------------------------
+
+Working with complicated class hierarchies is a typical are for
+metaprogramming techniques. As we saw in the previous chapter, the
+typical mechanism for dealing with methods in complicated hierarchies
+is trough cooperative ``super`` calls.
+
+In the following sections, I will implement an 'Ancestor'
+class, to provide a replacement and enhancement of ``super``.
+'Ancestor' objects are instantiated as ``Ancestor(cls,obj,degree)``,
+where 'cls' is a class, 'obj' and instance of a subclass of 'cls', and
+'degree' is a positive integer number, describing the degree of parentship
+of the ancestor class with the original class 'cls', with respect
+to the MRO of 'obj'.
+
+
+Let me show how Ancestor will be used, first. The real implementation of
+'Ancestor' will be postponed of a few paragraphs, since it requires the
+understanding of attribute descriptors. But it is important to have clear
+which are the features we expect from 'Ancestor' objects (one could write
+a test suite first).
+
+ >>> from oopp import Ancestor, ExampleBaseClass
+ >>> class C(ExampleBaseClass): pass
+ ...
+ >>> Ancestor(C,C(),1).m()
+ 'regular method'
+ >>> Ancestor(C,C(),1).s()
+ 'staticmethod'
+ >>> Ancestor(C,C(),1).c()
+ 'classmethod'
+
+Thus ``Ancestor`` objects work like ``super`` objects in the cases in which
+``super`` works; moreover ``Ancestor`` objects do not have the shortcmomings
+of ``super`` objects:
+
+ >>> Ancestor(C,C(),1).p
+ 'property'
+ >>> Ancestor(C,C(),1).__name__
+ 'ExampleBaseClass'
+
+In addition, one can retry the farthest ancestors:
+
+ >>> Ancestor(C,C(),1).cls
+ <class 'oopp.ExampleBaseClass'>
+ >>> Ancestor(C,C(),2).cls
+ <type 'object'>
+
+Of course, one cannot go to far away:
+
+ >>> Ancestor(C,C(),3).cls #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "oopp.py", line 199, in __new__
+ raise AncestorError("%s is not an ancestor of degree %s of %s"
+ oopp.AncestorError: C is not an ancestor of degree 3 of C
+
+It is interesting to show what happens for the ``__init__`` method:
+
+ >>> Ancestor(C,C(),1).__init__
+ <function __init__ at 0x81b1514>
+
+ >>> Ancestor(C,C(),1).cls.__init__
+ <built-in method __init__ of type object at 0x80e6280>
+
+Having specified the behavior of 'Ancestor', now we have only to
+implement it ;)
+
+Implementation of the Ancestor class
+--------------------------------------------------------------------------
+
+Having understood how objects are created in Python 2.2, we may now
+provide the code for the implementation of 'Ancestor'.
+
+ ::
+
+ #<oopp.py>
+
+ class AncestorError(Exception):
+ """Cannot find an ancestor of degree %s of %s with respect to the
+ MRO of %s"""
+
+ class Ancestor(AvoidDuplication):
+ """Given a class klass, an instance obj of a subclass of klass,
+ and the degree of parentship (degree=1 for the direct superclass),
+ returns an Ancestor object with an ancestor attribute corresponding
+ to the ancestor class."""
+
+ def __new__(cls, klass, obj, degree):
+ 'cls.instance is inherited from AvoidDuplication'
+ super(Ancestor,cls).__new__(cls,klass,obj) #makes cls.instance
+ if cls.isnew:
+ cls.obj=obj
+ cls.cls=klass
+ if inspect.isclass(obj): mro=list(obj.__mro__)
+ else: mro=list(obj.__class__.__mro__)
+ #mro=obj.__class__.mro()
+ try:
+ cls.instance.cls=mro[mro.index(klass)+degree]
+ except:
+ raise AncestorError(AncestorError.__doc__
+ % (degree, klass, obj.__class__.__name__))
+ return cls.instance
+
+ def __getattr__(self,attr):
+ a=getattr(self.cls,attr) #only routines have a __get__ method
+ if inspect.ismethod(a):
+ if inspect.isclass(a.im_self): #class method attached to self.cls
+ b=a.__get__(self.cls,self.cls)
+ else: #normal method attached to self.obj
+ b=a.__get__(self.obj,self.cls)
+ elif inspect.isfunction(a):
+ b=a #do nothing to functions and staticmethods
+ else:
+ try:
+ b=a.__get__(self.obj,self.cls) #properties
+ except AttributeError:
+ b=a #do nothing to simple attributes
+ return b
+
+ #</oopp.py>
+
+Here is an example of application to our paleoanthropology hierarchy:
+
+ >>> from oopp import Ancestor, HomoSapiensSapiens
+ >>> modernman = HomoSapiensSapiens()
+ >>> Ancestor(HomoSapiensSapiens,modernman,1).cls
+ <class 'oopp.HomoSapiens'>
+ >>> Ancestor(HomoSapiensSapiens,modernman,2).cls
+ <class 'oopp.HomoHabilis'>
+ >>> Ancestor(HomoSapiensSapiens,modernman,3).cls
+ <class 'oopp.Homo'>
+
+One (possibile) issue of the Ancestor class in this form, is that it gives
+access to bound methods only; I leave as an exercise for the reader to
+generalize the class in such a way to give access to unbound methods too,
+i.e. mimicking more closely the behavior of ``super``.
+
+The same behaviour is seen, even more clearly, for Ancestor objects:
+
+ >>> from oopp import *
+ >>> Ancestor(Clock,SingleClock(),1).cls #.
+ <class 'oopp.Singleton'>
+ >>> Ancestor(Clock,Clock(),1).cls
+ <type 'object'
+
+Metaclass-aided inheritance
+----------------------------------------------------------------------------
+
+In chapters 4 and 5, we discussed the concept of cooperative
+methods and we implemented the idea via the ``super`` call mechanism,
+later improved by using ``Ancestor`` objects.
+While theoretically nice, the ``Ancestor`` mechanism is still
+syntactically ugly, since we have to explicitly give to ``Ancestor``
+the name of the class where ``Ancestor`` is invoked.
+This is redundant and annoying, especially because changing the name
+of that class in the sources would require modifying all the ``Ancestor`` calls
+(of course one could use alias for the class name, but this is a workaround,
+not a solution). Metaclasses allow to improved the
+situation quite a lot. In this section I show a solution based on a
+trick discussed by Guido in his essay.
+
+The trick is to use private variables: private variables contain the
+name of the class in which they are defined (trough the mangling mechanism).
+This information can therefore be passed to ``ancestor`` without having
+to pass the class name explicitly. This idea can be implemented in a
+metaclass such as the following:
+
+ ::
+
+ #<oopp.py>
+
+ class AncestorAware(type):
+ def __init__(cls, *args, **kw):
+ setattr(cls,"_%s__ancestor" % cls.__name__,
+ lambda self,degree=1: Ancestor(cls,self,degree))
+
+ class AncestorHomo(Homo):
+ __metaclass__ = AncestorAware
+ __action=""
+ def can(self):
+ self.__ancestor().can()
+ print self.__action
+
+ #</oopp.py>
+
+
+Notice that we have defined the class ``AncestorHomo``, which enhances
+the features provided by Homo trough the metaclass ``AncestorAware``.
+
+Now, if we use the private method ``__ancestor`` inside one of the ancestor
+classes, it will be read as ``_<ancestor-name>__ancestor`, i.e. as
+``ancestor(<ancestor>)``: therefore the parent of the ancestor can be
+called without providing explicitly its name. Here is the final script:
+
+ ::
+
+ #<paleoanthropology2.py>
+
+ import oopp
+
+ class HomoErectus(oopp.AncestorHomo):
+ __action=" - walk"
+ def can(self):
+ self.__ancestor().can()
+ print self.__action
+
+
+ class HomoHabilis(HomoErectus):
+ __action=" - make tools"
+ def can(self):
+ self.__ancestor().can()
+ print self.__action
+
+
+ class HomoSapiens(HomoHabilis):
+ __action=" - make abstractions"
+ def can(self):
+ self.__ancestor().can()
+ print self.__action
+
+
+ class HomoSapiensSapiens(HomoSapiens):
+ __action=" - make art"
+ def can(self):
+ self.__ancestor().can()
+ print self.__action
+
+ modernman=HomoSapiensSapiens()
+ modernman.can()
+
+ #</paleoanthropology2.py>
+
+The output is:
+
+ ::
+
+ HomoSapiensSapiens can:
+ - walk
+ - make tools
+ - make abstractions
+ - make art
+
+Finally, we are done! Now the hierarchy is elegant and easily
+extensible/modifiable. For instance, it is obvious how to insert
+a new element in the
+hierarchy, as for example HomoErectus between Homo and HomoHabilis.
+It is enough to add the class
+
+ ::
+
+ class HomoErectus(Homo):
+ def can(self):
+ ancestor(HomoErectus,self).can()
+ print " - walk"
+
+and to change the first line of the class HomoHabilis, making it
+inheriting from HomoErectus instead of HomoHabilis.
+
+
+Let me check that 'HomoSapiensSapiens' now inherits from HomoErectus
+and therefore can walk:
+
+ >>> from oopp import HomoSapiensSapiens
+ >>> HomoSapiensSapiens().can()
+
+
+Sometimes the features provided by metaclasses can be emulated trough
+inheritance: however using metaclasses is *not* the same than using
+inheritance. Let me point out the differences trough some example.
+
+.. [#] The present notation for attribute descriptors such as staticmethods
+ and classmethods is rather ugly: however, this will probably change
+ in the future (probably in Python 2.4).
+
+ class SuperAware(type,Customizable):
+ """Instances of SuperAware inherit a private attribute __sup
+ which provide a handy way to call cooperative methods."""
+ # DOES NOT WORK WITH NEW, calls (super).__new__, not (super.__new__)
+ sup=super # default, customizable
+ def __init__(cls,*args):
+ setattr(cls,'_%s__sup' % cls.__name__,cls.sup(cls))
+ super(SuperAware,cls).__init__(*args) # usual cooperative call
+
+
+Notice that this trick comes from Guido himself (in his essay on
+"Type/class unification")
+
+Let me show how ``SuperAware`` can be used in practice.
+
+A common requirement for a class is the ability to count the number of its
+instances. This is a quite easy problem: it is enough to increments a counter
+each time an instance of that class is initialized. However, this idea can
+be implemented in the wrong way. i.e. naively one could implement
+counting capabilities in a class without such capabilities by modifying the
+``__init__`` method explicitly in the original source code.
+A better alternative is to follow the bottom-up approach and to implement
+the counting
+feature in a separate mix-in class: then the feature can be added to the
+original class via multiple inheritance, without touching the source.
+Moreover, the counter class becomes a reusable components that can be
+useful for other problems, too. In order to use the mix-in approach, the
+``__init__`` method of the counter class must me cooperative. The
+SuperAware metaclass provides some syntactic sugar for this job:
+
+ ::
+
+ #<oopp.py>
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # __metaclass__=SuperAware
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # self.__sup.__init__(*args,**kw) # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
+
+ # class WithCounter(object):
+ # """Mixin class counting the total number of its instances and storing
+ # it in the class attribute count."""
+
+ # count=1 # class attribute (or static attribute in C++/Java terminology)
+
+ # def __init__(self,*args,**kw):
+ # super(WithCounter,self).__init__(*args,**kw)
+ # # anonymous cooperative call
+ # type(self).counter+=1 # increments the class attribute
+
+Therefore the approach is nice, but it has the disadvantage that *all* methods
+are traced (or timed), whereas it would be preferable to have the
+capability of passing to the metaclass the explicit list of the methods we
+want to trace (or to time). This can be done quite elegantly with
+a fair amount of metaclass magic, i.e. with a meta-metaclass
+"WithWrappingCapabilities':
+
+ ::
+
+ #<oopp.py>
+
+ class WithWrappingCapabilities(type):
+ # This is a meta-metaclass!
+ """Instances of WithWrappingCapabilities are metaclasses with
+ wrapping capabilities."""
+
+ wrapper=lambda x: x # default, identity function wrapper
+ # to be overridden in the instances of WithWrappingCapabilities
+
+ def __init__(meta,*args): # __init__ of the meta-metaclass
+
+ def init(cls,*args): # __init__ of the metaclass
+ """Wraps the methods of the class with the given wrapper,
+ depending on the arguments passed to the metaclass"""
+
+ super(meta,cls).__init__(*args) # cooperative call
+ # in case the metaclass has to be multiple inherited
+
+ wrapper=vars(meta)['wrapper']
+
+ wraplist=getattr(meta,'wraplist','ALL')
+ if wraplist=='ALL':
+ condition=lambda k,v: True # wrap all
+ else:
+ condition=lambda k,v: k in wraplist
+ wrap(cls,wrapper,condition)
+
+ meta.__init__=init
+
+ #</oopp.py>
+
+This solves the problem of passing parameters to 'Traced' and 'Timed':
+
+ ::
+
+ #<oopp.py>
+
+ class Traced(Reflective):
+ """Metaclass providing tracing capabilities to the methods in
+ square brackets"""
+ __metaclass__=ClsFactory[WithWrappingCapabilities]
+ wrapper=tracedmethod
+ wrapper.logfile=sys.stdout
+ wraplist=['ALL']
+
+ class Timed(Reflective):
+ """Metaclass providing timing capabilities to the methods in
+ square brackets"""
+ __metaclass__=ClsFactory[WithWrappingCapabilities]
+ wrapper=timedmethod
+ wrapper.logfile=sys.stdout
+ wraplist=['ALL']
+
+ #class Remembering(Reflective):
+ # """Metaclass providing memory capabilities to the methods in
+ # square brackets"""
+ # __metaclass__=ClsFactory[WithWrappingCapabilities]
+ # wrapper=withmemory
+
+ #</oopp.py>
+
+Let me give a toy example of wrong design.
+
+Both Java and C++ have Abstract Base Classes, i.e. classes that cannot
+be instantiated; it is pretty easy to emulate them in Python, by overriding
+the ``__new__`` staticmethod in such a way to forbids object creation:
+
+ ::
+
+ #<oopp.py>
+
+ class Beautiful(NonInstantiable,PrettyPrinted):
+ "An example of wrong mix-in class"
+ formatstring="%s is beautiful"
+
+ #</oopp.py>
+
+The class 'Beautiful' here, describes an abstract property without specific
+realization. is intended to be used as a mix-in class: classes derived from
+'Beautiful', will inherits a nice string representation.
+However, I made a design mistake in this hierarchy, since 'Prince' inherits
+from 'Beautiful' which is 'NonInstantiable' and therefore has become
+non-instantiable too. This means that I cannot print instances of
+'Prince' as I originally intended. Python dynamism allows me to
+correct my mistake
+
+ ::
+
+ #<beautifulprince.py>
+
+ from oopp import *
+
+ class Prince(Beautiful): pass
+ #non-instantiable, uncorrect class
+ #Prince.__new__ is inherited from NonInstantiable.__new__
+
+ Prince.__new__=object.__new__
+ #overrides Beautiful.__new__ and fixes the mistake
+
+ try: b=Beautiful()
+ except NonInstantiableError,e: print e
+ charles=Prince()
+ charles.formatstring="Not much for an instance of class %s"
+ print "---\nHow beautiful is Prince charles?\n",charles
+
+ #</beautifulprince.py>
+
+Output:
+
+ ::
+
+ <class '__main__.Beautiful'> cannot be instantiated
+
+ ---
+ How beautiful is Prince charles ?
+ Not much for an instance of class Prince
+
+From this short script we learn (in addition to the fact that
+Prince Charles is not especially beautiful) that:
+
+1. the class 'NonInstantiable' cannot be instantiated since instantiation
+ involves calling the ``__new__`` method which raises an exception.
+
+2. the class 'Beautiful' cannot be instantiated since it inherits from
+ class NonInstantiable;
+
+3. the class 'Prince' can be instantiated since its ``__new__`` method (that
+ would be the ``NonInstantiable.__new__``) is redefined to be the standard
+ ``object.__new__`` method;
+
+Improving the ``super`` mechanism
+-----------------------------------------------------------------------------
+
+The most typical problem in multiple inheritance hierarchies is name clashing
+due to unwanted overriding. The problem of conflicting methods can be avoided
+by making them cooperative, i.e. by using the ``super`` mechanism. However,
+as I discussed in the previous chapter, the built-in super has problems
+of its own. Therefore, I will discuss here how the ``super`` mechanism can be
+improved.
+
+I show here a solution based on a custom attribute descriptor called
+'Super(C,S)' that calls ``ancestor(C,S)`` via an internal function
+``_super(C,S,methname)``.
+
+ ::
+
+ #<oopp.py>
+
+ def _super(C,S,methname):
+ """Internal function invoking ancestor. Returns an attribute
+ descriptor object."""
+ if methname=='__name__': # special case
+ meth=ancestor(C,S)[1].__name__
+ else:
+ for c in ancestor(C,S)[1:]:
+ meth=c.__dict__.get(methname)
+ if meth: break # if found
+ if not meth: raise AttributeError,methname # not found
+ return convert2descriptor(meth) # if needed
+
+ class Super(object):
+ """Invoked as Super(cls,obj).meth returns the supermethod of
+ cls with respect to the MRO of obj. If obj is a subclass of cls,
+ it returns the unbound supermethod of obj; otherwise, if obj is an
+ instance of some subclass of cls, it returns the obj-bound method."""
+ def __init__(self,cls,obj):
+ self.cls=cls
+ self.obj=obj
+ def __getattribute__(self,name):
+ obj=object.__getattribute__(self,'obj')
+ cls=object.__getattribute__(self,'cls')
+ if hasattr(obj,'__bases__') and issubclass(obj,cls):
+ # if obj is a subclass, return unbound method
+ return _super(cls,obj,name).__get__(None,obj)
+ else: # if obj is an instance, return bound method
+ S=type(obj)
+ return _super(cls,S,name).__get__(obj,S)
+
+ #</oopp.py>
+
+This code also show one can redefine ``__getattribute__`` properly,
+i.e. invoking ``object.__getattribute__`` inside ``__getattribute__``
+and *not* using the built-in function ``getattr``. The reason is
+that ``getattr(self,attr)`` works by calling ``self.__getattribute__(attr)``
+and this would induce an infinite recursion.
+
+
+Let me show few examples of usage with my paleonthropologycal hierarchy.
+First of all, I introduce a modern man:
+
+ >>> from oopp import *
+ >>> man=HomoSapiensSapiens()
+
+A modern man can do plenty of things:
+
+ >>> man.can()
+ <HomoSapiensSapiens> can:
+ - make tools
+ - make abstractions
+ - make art
+
+Let me show that ``Super(cls,subcls)`` returns an unbound method
+
+ >>> Super(HomoSapiens,HomoSapiensSapiens).can
+ <unbound method HomoSapiensSapiens.can>
+
+whereas ``Super(cls,instance)`` returns a bound method:
+
+ >>> Super(HomoSapiens,man).can
+ <bound method HomoSapiensSapiens.can of
+ <oopp.HomoSapiensSapiens object at 0x4017506c>>
+
+Both are methods of 'HomoHabilis'
+
+ >>> ancestor(HomoSapiens,HomoSapiensSapiens)[1]
+ <class 'oopp.HomoHabilis'>
+
+therefore
+
+ >>> Super(HomoSapiens,HomoSapiensSapiens).can(man)
+ <HomoSapiensSapiens> can:
+ - make tools
+ >>> Super(HomoSapiens,man).can()
+ <HomoSapiensSapiens> can:
+ - make tools
+
+Notice that the current implementation works subtly with the ``__class__``
+attribute:
+
+ >>> Super(HomoSapiens,man).__class__, type(Super(HomoSapiens,man))
+ (<class 'oopp.HomoSapiensSapiens'>, <class 'oopp.Super'>)
+ >>> Super(HomoSapiens,HomoSapiensSapiens).__class__,
+ ... type(Super(HomoSapiens,HomoSapiensSapiens))
+ (<attribute '__class__' of 'object' objects>, <class 'oopp.Super'>)
+
+Let me show now how it works with properties:
+
+ >>> class C(ExampleBaseClass): pass
+ >>> c=C()
+ >>> Super(C,C).p
+ <property object at 0x401c766c>
+ >>> Super(C,c).p
+ 'property'
+
+Moreover it works with '__name__':
+
+ >>> Super(C,c).__name__
+ 'ExampleBaseClass'
+ >>> Super(C,C).__name__
+ 'ExampleBaseClass'
+
+In the case of ``__new__`` and ``__init__``, even if ``Super`` is
+less efficient than
+the ``super`` built-in, one pays the price only once, at the object
+creation time, which is supposed to be a less frequent operation than
+a normal method call.
+
+--------------------------------------------------------------------------:
+
+ #<oopp.py>
+
+ def child(*bases,**options):
+ """Class factory avoiding metatype conflicts: if the base classes have
+ metaclasses conflicting within themselves or with the given metaclass,
+ it automatically generates a compatible metaclass and instantiate the
+ child class from it. The recognized keywords in the option dictionary
+ are name, dic and meta."""
+ name=options.get('name',''.join([b.__name__ for b in bases])+'_')
+ dic=options.get('dic',{})
+ metas=options.get('metas',(type,))
+ return _generatemetaclass(bases,metas)(name,bases,dic)
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> C=child(A,B)
+ >>> print C,type(C)
+ <class 'oopp.AB_'> <class 'oopp._M_AM_B'>
+
+ class ClsFactory(BracketCallable):
+ """Bracket callable non-cooperative class acting as
+ a factory of class factories.
+
+ ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+ . They automatically converts functions to static methods
+ if the input object is not a class. If an explicit name is not passed
+ the name of the created class is obtained by adding an underscore to
+ the name of the original object."""
+
+ returnclass=False # ClsFactory[X] returns an *instance* of ClsFactory
+
+ def __call__(self, *args):
+ """Generates a new class using self.meta and avoiding conflicts.
+ The first metaobject can be a dictionary, an object with a
+ dictionary (except a class), or a simple name."""
+
+ # default attributes
+ self.name="CreatedWithClsFactory"
+ self.bases=()
+ self.dic={}
+ self.metas=self.bracket_args
+
+ if len(args)==1:
+ arg=args[0]
+ if isinstance(arg,str): # is a name
+ self.name=arg
+ elif hasattr(arg,'__name__'): # has a name
+ self.name=arg.__name__+'_'
+ self.setbasesdic(arg)
+ elif len(args)==2:
+ self.name=args[0]
+ assert isinstance(self.name,str) # must be a name
+ self.setbasesdic(args[1])
+ elif len(args)==3: # must be name,bases,dic
+ self.name=args[0]
+ self.bases+=args[1]
+ self.dic.update(args[2])
+ if len(args)<3 and not self.bases: # creating class from a non-class
+ for k,v in self.dic.iteritems():
+ if isfunction(v): self.dic[k]=staticmethod(v)
+ return child(*self.bases,**vars(self))
+
+ def setbasesdic(self,obj):
+ if isinstance(obj,tuple): # is a tuple
+ self.bases+=obj
+ elif hasattr(obj,'__bases__'): # is a class
+ self.bases+=obj.__bases__
+ if isinstance(obj,dict): # is a dict
+ self.dic.update(obj)
+ elif hasattr(obj,"__dict__"): # has a dict
+ self.dic.update(obj.__dict__)
diff --git a/pypers/oopp.py b/pypers/oopp.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/oopp.py
diff --git a/pypers/optparse/cutted-stuff.txt b/pypers/optparse/cutted-stuff.txt
new file mode 100755
index 0000000..2e0cbc9
--- /dev/null
+++ b/pypers/optparse/cutted-stuff.txt
@@ -0,0 +1,58 @@
+So, command line tools are still at the heart of most applications,
+including graphical applications, where they are most probably hidden
+under a fancy graphical cover.
+
+
+Are you an old time Unix programmer? Or just a Windows hobbyst?
+Are you having trouble with command line scripts?
+Forget about the old-fashioned and cumbersome getopt module:
+now you have optparse at your disposal, and writing command line
+scripts in Python is a breeze!
+
+I always disliked Python to be called a "scripting" language.
+
+First of all because the definition is reductive, since Python
+is really a general purpose language; secondly, because I found
+Python facilities in scripting tasks to be somewhat weaker than its
+(impressive) facilities in other programming tasks.
+However, the new additions in Python 2.3 and especially the optparse
+module, are making me to change my mind.
+
+Greg Ward's optparse provides an fully fledged Object Oriented API for
+command line argument parsing, which has the advantage of putting in
+the standard library all the features of its ancestor Optik, a module
+which is probably well known to serious writers of command line scripts.
+
+Writing command line scripts is one of the most common task
+in every day life, since they are invaluable not only as
+small tools, but as interfaces to complex applications;
+for instance it makes a lot of sense to write a text-only
+interface to a graphics application in the developing phase.
+The script interface allows to test the application quickly
+and is essential in the design phase: once the logic of the
+application is decided after experiments with the command line,
+you may go on and implement the fancy graphics.
+
+First of all, let me say that optparse comes with a very good
+documentation that will answer all the questions of the more
+sophisticated users. There is no point in rewriting the standard
+documentation here: what I will try to do instead is to show how
+it works in practice in the simplest cases.
+The aim of this article is make the reader want to read the documentation.
+
+
+
+``optaparse`` is cross-platform and perfectly portable module,
+written in pure Python, which however by design it favors the
+the good old Unix philosophy for what concerns command line arguments,
+since
+
+
+Experienced Unix programmers may want to skip this section.
+
+One nice feature of optparse, in my opinion, is that it makes you
+want to use Unix conventions even when you are scripting under
+Windows. This is not such a bad thing, since Unix philosophy about
+optional arguments makes a lot of sense. Of course, optparse does
+not force you to follows Unix conventions, but it suggests you
+that this is the Pythonic obvious way to do it.
diff --git a/pypers/optparse/example.py b/pypers/optparse/example.py
new file mode 100755
index 0000000..7f12565
--- /dev/null
+++ b/pypers/optparse/example.py
@@ -0,0 +1,18 @@
+"""An example script invoking optionparse.
+
+ usage: %prog [options] args
+ -p, --positional: print positional arguments
+ -1, --option1=OPTION1: print option1
+ -2, --option2=OPTION2: print option2
+"""
+
+import optionparse
+opt, args = optionparse.parse(__doc__)
+if not opt and not args:
+ optionparse.exit()
+if opt.positional:
+ print args
+if opt.option1:
+ print opt.option1
+if opt.option2:
+ print opt.option2
diff --git a/pypers/optparse/example0.py b/pypers/optparse/example0.py
new file mode 100755
index 0000000..aa3c454
--- /dev/null
+++ b/pypers/optparse/example0.py
@@ -0,0 +1,12 @@
+"""
+ Example.
+
+ usage: %prog [options] args
+
+
+"""
+
+import optionparse
+opt, args = optionparse.parse(__doc__)
+
+print opt
diff --git a/pypers/optparse/example1.py b/pypers/optparse/example1.py
new file mode 100755
index 0000000..e524193
--- /dev/null
+++ b/pypers/optparse/example1.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+"""
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: don't make backup copies
+"""
+import optparse, sys, re
+
+def replace(regx,repl,files,backup_option=True):
+ rx=re.compile(regx)
+ for fname in files:
+ txt=file(fname,"U").read()
+ if backup_option:
+ print >> file(fname+".bak","w"), txt ,
+ print >> file(fname,"w"), rx.sub(repl,txt) ,
+
+parser = optparse.OptionParser("usage: %prog files [options]")
+
+parser.add_option("-x", "--regx",
+ help="regular expression")
+parser.add_option("-r", "--repl",
+ help="replacement string")
+parser.add_option("-n", "--nobackup",
+ action="store_false", default=True,
+ help="do not make backup copies")
+
+option, files = parser.parse_args()
+
+if not files:
+ print "No files given!"
+ print __doc__
+elif option.regx and option.repl:
+ replace(option.regx, option.repl,args, not option.nobackup)
+else:
+ print "Missing options or unrecognized options."
+
diff --git a/pypers/optparse/example2.py b/pypers/optparse/example2.py
new file mode 100755
index 0000000..5f16cdc
--- /dev/null
+++ b/pypers/optparse/example2.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+"""
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: don't make backup copies
+ -R, --restore: restore the original from the backup
+"""
+import optionparse, sys, re
+
+def replace(regx,repl,files,backup_option=True):
+ rx=re.compile(regx)
+ for fname in files:
+ # you could a test to see if the file exists and can be read here
+ txt=file(fname,"U").read()
+ if backup_option:
+ print >> file(fname+".bak","w"), txt ,
+ print >> file(fname,"w"), rx.sub(repl,txt) ,
+
+def restore(fname):
+ if os.path.exists(fname+".bak"):
+ shutil.copyfile(fname+".bak",fname)
+ else:
+ print "Sorry, there is no backup copy for %s" % fname
+
+if __name__=='__main__':
+ option,args=optionparse.parse(__doc__)
+ if not option and not args:
+ optionparse.exit()
+ elif option.regx and option.repl:
+ replace(option.regx, option.repl,args, not option.nobackup)
+ elif option.restore:
+ for fname in args: restore(fname)
+ else:
+ print "Missing options or unrecognized options."
+
diff --git a/pypers/optparse/file1.txt b/pypers/optparse/file1.txt
new file mode 100755
index 0000000..8510414
--- /dev/null
+++ b/pypers/optparse/file1.txt
@@ -0,0 +1,8 @@
+Some date:
+
+ n11-04-1969
+ n27-02-2004
+
+
+
+
diff --git a/pypers/optparse/file2.txt b/pypers/optparse/file2.txt
new file mode 100755
index 0000000..d14c8c1
--- /dev/null
+++ b/pypers/optparse/file2.txt
@@ -0,0 +1,4 @@
+Some date:
+
+ 11-04-1969
+ 27-02-2004 \ No newline at end of file
diff --git a/pypers/optparse/intro.txt b/pypers/optparse/intro.txt
new file mode 100755
index 0000000..62e7206
--- /dev/null
+++ b/pypers/optparse/intro.txt
@@ -0,0 +1,83 @@
+> Hello Michele,
+>
+> Thank you for your proposals. I'll talk with Kendall and Peter about
+> all of your proposals but we would
+> definitely be interested in an article on the optparse module.
+>
+> What I would need from you is the introduction (for our planning and
+> our work on the next table of contents)
+> and an estimated date when you could get a first draft to us.
+>
+> Regards,
+>
+> Mark
+
+Okay, here is the introduction. For what concerns the draft, I can work
+on it over the weekend and send it to you on Monday. It is not a big
+job since I already have some notes for a recipe I planned to submit
+to the Python Cookbook and I can expand on that. I don't know how
+long it will be at the end, but I would try to make it more or less the
+size of a DeveloperWorks paper, i.e. 2500 words.
+Please let me know if this is too much or too little. Also, if everything
+is approved, let me know about the compensation and the needed paperwork.
+Best regards,
+
+ Michele
+
+P.S. I will send you the draft in reStructuredText format, if it is
+okay for you.
+
+-----------------------------------------------------------------------
+
+
+The optparse module: writing command line tools the easy way
+=======================================================================
+
+"The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead." -- Greg Ward, optparse author
+
+Introduction
+-----------------------------------------------------------------------
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not changed:
+command line tools are still fast, efficient, portable, easy to use
+and - more importantly - reliable. You can count on them.
+You can expect command line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with memory
+constraints such as embedded devices. When you really need them, command
+line tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a "scripting" language - to provide
+facilities to help the programmer in the task of writing command
+line tools. In Python, for a long time the support for this kind of
+tasks has been devoted to the old fashioned getopt module. I have
+never been particularly fond of getopt, since it required the programmer
+to do a sensible amount of boring job even for the parsing of simple
+argument lines. However, the situation has changed with the coming of
+Python 2.3, and thanks to the great job of Greg Ward (the author of
+optparse and Optik, its precursor) now the Python programmer
+has at her disposal - in the standard library and not as an
+add-on module - a fully fledged Object Oriented API for command
+line argument parsing, which makes writing Unix-style command
+line tools easy, efficient and fast.
+
+The only minor disadvantage of optparse is that it is a kind of
+sophisticated tool, and requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of optparse that she will use in the 90% of
+the cases.
+
+Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of optparse, showing how easy is to use it and how convenient it
+really is. Also, I will show some trick that will make your life with
+optparse much easier. This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need optparse even more than Unix programmers - and does not
+require any particular expertise to be fully appreciated. \ No newline at end of file
diff --git a/pypers/optparse/invoice.html b/pypers/optparse/invoice.html
new file mode 100755
index 0000000..4c7de9a
--- /dev/null
+++ b/pypers/optparse/invoice.html
@@ -0,0 +1,59 @@
+<?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></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<p>Invoice for beehive KG (optparse article)</p>
+<p>From:</p>
+<pre class="literal-block">
+Michele Simionato, Ph. D.
+Via Nervi 2
+00055 Ladispoli (ROMA)
+Telefono: 06 99229171
+Codice Fiscale: SMNMHL69D11F241A
+Partita IVA: 07905191008
+</pre>
+<p>To:</p>
+<pre class="literal-block">
+beehive KG
+Neues Kranzler Eck
+Kurfuerstendamm 21
+
+D - 10719 Berlin
+Germany
+</pre>
+<p>For:</p>
+<pre class="literal-block">
+Article optparse published in Py magazine
+</pre>
+<p>Amount:</p>
+<blockquote>
+Euro 97,02</blockquote>
+<p>My Bank info:</p>
+<pre class="literal-block">
+Cassa di Risparmio di Venezia
+Filiale di Pianiga
+Via Roma 1
+ITALY
+
+ABI: C 06345
+CAB: 36230
+Account: 0700075870H
+Code swift/bic: CVCEIT2V
+IBAN: IT47 C063 4536 2300 7400 0758 70H
+</pre>
+<dl>
+<dt>Signed</dt>
+<dd>Michele Simionato</dd>
+<dt>Date</dt>
+<dd>31 Aug 2004</dd>
+</dl>
+</div>
+</body>
+</html>
diff --git a/pypers/optparse/invoice.tex b/pypers/optparse/invoice.tex
new file mode 100755
index 0000000..8a4ab85
--- /dev/null
+++ b/pypers/optparse/invoice.tex
@@ -0,0 +1,120 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper]{geometry}
+%% 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}}
+% 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{}
+\author{}
+\date{}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+
+From:
+\begin{ttfamily}\begin{flushleft}
+\mbox{Michele~Simionato,~Ph.~D.}\\
+\mbox{Via~Nervi~2}\\
+\mbox{00055~Ladispoli~(ROMA)}\\
+\mbox{Email~michele.simionato@gmail.com}\\
+\mbox{Partita~IVA:~~~~07905191008}
+\end{flushleft}\end{ttfamily}
+
+To:
+\begin{ttfamily}\begin{flushleft}
+\mbox{beehive~KG}\\
+\mbox{Neues~Kranzler~Eck}\\
+\mbox{Kurfuerstendamm~21}\\
+\mbox{}\\
+\mbox{D~-~10719~~Berlin}\\
+\mbox{Germany}
+\end{flushleft}\end{ttfamily}
+
+Invoice for:
+\begin{quote}
+
+Article optparse published in Py magazine
+\end{quote}
+
+Amount:
+\begin{quote}
+
+Euro 97,02
+\end{quote}
+
+Bank info:
+\begin{ttfamily}\begin{flushleft}
+\mbox{Cassa~di~Risparmio~di~Venezia}\\
+\mbox{Filiale~di~Pianiga}\\
+\mbox{Via~Roma~1}\\
+\mbox{ITALY}\\
+\mbox{}\\
+\mbox{ABI:~~C~06345}\\
+\mbox{CAB:~~36230}\\
+\mbox{Account:~~0700075870H~}\\
+\mbox{Code~swift/bic:~~CVCEIT2V~}\\
+\mbox{IBAN:~IT47~C063~4536~2300~7400~0758~70H}
+\end{flushleft}\end{ttfamily}
+\begin{description}
+%[visit_definition_list_item]
+\item[Signed]
+%[visit_definition]
+
+Michele Simionato
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[Date]
+%[visit_definition]
+
+31 Aug 2004
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+
+\end{document}
+
diff --git a/pypers/optparse/invoice.txt b/pypers/optparse/invoice.txt
new file mode 100755
index 0000000..ef034e7
--- /dev/null
+++ b/pypers/optparse/invoice.txt
@@ -0,0 +1,47 @@
+From::
+
+ Michele Simionato, Ph. D.
+ Via Nervi 2
+ 00055 Ladispoli (ROMA)
+ Email michele.simionato@gmail.com
+ Partita IVA: 07905191008
+
+To::
+
+ beehive KG
+ Neues Kranzler Eck
+ Kurfuerstendamm 21
+
+ D - 10719 Berlin
+ Germany
+
+Invoice for:
+
+ Article optparse published in Py magazine
+
+
+Amount:
+
+ Euro 97,02
+
+
+Bank info::
+
+ Cassa di Risparmio di Venezia
+ Filiale di Pianiga
+ Via Roma 1
+ ITALY
+
+ ABI: C 06345
+ CAB: 36230
+ Account: 0700075870H
+ Code swift/bic: CVCEIT2V
+ IBAN: IT47 C063 4536 2300 7400 0758 70H
+
+
+Signed
+ Michele Simionato
+
+
+Date
+ 31 Aug 2004
diff --git a/pypers/optparse/letter.txt b/pypers/optparse/letter.txt
new file mode 100755
index 0000000..bdc87a3
--- /dev/null
+++ b/pypers/optparse/letter.txt
@@ -0,0 +1,7 @@
+Okay, finally this Sunday morning I have found the time to finish the optparse
+article :)
+
+I send it to you in reStructuredText format, let me know if you prefer
+another format, or if needs some further editing in some part. I also
+send you my optionparse module, a wrapper which makes optparse easier
+to use.
diff --git a/pypers/optparse/optionparse.py b/pypers/optparse/optionparse.py
new file mode 100755
index 0000000..a9f990c
--- /dev/null
+++ b/pypers/optparse/optionparse.py
@@ -0,0 +1,64 @@
+"""\
+A much simplified interface to optparse.
+You should use optionparse in your scripts as follow.
+First, write a module level docstring containing something like this:
+
+'''usage: %prog files [options]
+ -d, --delete: delete all files
+ -e, --erase = ERASE: erase the given file'''
+
+Then write a main program of this kind:
+
+# sketch of a script to delete files
+if __name__=='__main__':
+ import optionparse
+ option,args=optionparse.parse(__doc__)
+ if not args and not option: optionparse.exit()
+ elif option.delete: print "Delete all files"
+ elif option.erase: print "Delete the given file"
+
+Notice that ``optionparse`` parses the docstring by looking at the
+characters ",", ":", "=", "\\n", so be careful in using them. If
+the docstring is not correctly formatted you will get a SyntaxError.
+"""
+
+import optparse, re, sys
+
+USAGE = re.compile(r'(?s)\s*usage: (.*?)(\n[ \t]*\n|$)')
+
+def nonzero(self): # will become the nonzero method of optparse.Values
+ "True if options were given"
+ for v in self.__dict__.itervalues():
+ if v is not None: return True
+ return False
+
+optparse.Values.__nonzero__ = nonzero # dynamically fix optparse.Values
+
+class ParsingError(Exception): pass
+
+optionstring=""
+
+def exit(msg=""):
+ raise SystemExit(msg or optionstring.replace("%prog",sys.argv[0]))
+
+def parse(docstring, arglist=None):
+ global optionstring
+ optionstring = docstring
+ match = USAGE.search(optionstring)
+ if not match: raise ParsingError("Cannot find the option string")
+ optlines = match.group(1).splitlines()
+ try:
+ p = optparse.OptionParser(optlines[0])
+ for line in optlines[1:]:
+ opt, help=line.split(':')[:2]
+ short,long=opt.split(',')[:2]
+ if '=' in opt:
+ action='store'
+ long=long.split('=')[0]
+ else:
+ action='store_true'
+ p.add_option(short.strip(),long.strip(),
+ action = action, help = help.strip())
+ except (IndexError,ValueError):
+ raise ParsingError("Cannot parse the option string correctly")
+ return p.parse_args(arglist)
diff --git a/pypers/optparse/paper.html b/pypers/optparse/paper.html
new file mode 100755
index 0000000..a22df52
--- /dev/null
+++ b/pypers/optparse/paper.html
@@ -0,0 +1,453 @@
+<?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.1: http://docutils.sourceforge.net/" />
+<title>The optparse module: writing command-line tools the easy way</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="the-optparse-module-writing-command-line-tools-the-easy-way">
+<h1 class="title">The optparse module: writing command-line tools the easy way</h1>
+<blockquote>
+<table class="field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Status:</th><td class="field-body">Draft</td>
+</tr>
+<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="field-name">E-mail:</th><td class="field-body"><a class="reference" href="mailto:michele.simionato&#64;partecs.com">michele.simionato&#64;partecs.com</a></td>
+</tr>
+<tr class="field"><th class="field-name">Date:</th><td class="field-body">May 2004</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p><em>The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.</em> -- Greg Ward, optparse author</p>
+<div class="section" id="introduction">
+<h1><a name="introduction">Introduction</a></h1>
+<p>Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.</p>
+<p>Hence, it is important for a programming language - especially
+one that wants to be called a &quot;scripting&quot; language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the``getopt`` module. I have never
+been particularly fond of <tt class="literal"><span class="pre">getopt</span></tt>, since it required
+a sensible amount of coding even for the parsing of simple
+command-lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+<tt class="literal"><span class="pre">optparse</span></tt> a.k.a. <tt class="literal"><span class="pre">Optik</span></tt>) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.</p>
+<p>The only disadvantage of <tt class="literal"><span class="pre">optparse</span></tt> is that it is
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of <tt class="literal"><span class="pre">optparse</span></tt> that she will use in the 90% of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of <tt class="literal"><span class="pre">optparse</span></tt>. Also, I will show some trick that will make your life
+with <tt class="literal"><span class="pre">optparse</span></tt> much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need <tt class="literal"><span class="pre">optparse</span></tt> even more than Unix programmers; it does not
+require any particular expertise to be fully appreciated.</p>
+</div>
+<div class="section" id="a-simple-example">
+<h1><a name="a-simple-example">A simple example</a></h1>
+<p>I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and I do not always have sed/awk or
+even Emacs installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.</p>
+<p>The tool takes a bunch of files and replace a given regular expression
+everywhere in-place; moreover, it saves a backup copy of the original
+un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with specialized tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utilities in Python, and actually many people
+(including myself) are actively replacing some Unix commands and bash
+scripts with Python scripts. In real life, I have extended a lot
+the minimal tool that I describe here, and I continue to tune it as
+needed. For instance, you can make it to work recursively and/or on
+remote directories.</p>
+<p>As a final note, let me notice that I find <tt class="literal"><span class="pre">optparse</span></tt>
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in the Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and <tt class="literal"><span class="pre">optparse</span></tt>, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.</p>
+</div>
+<div class="section" id="the-unix-philosophy-for-command-line-arguments">
+<h1><a name="the-unix-philosophy-for-command-line-arguments">The Unix philosophy for command-line arguments</a></h1>
+<p>In order to understand how <tt class="literal"><span class="pre">optparse</span></tt> works, it is essential
+to understand the Unix philosophy about command-lines arguments.
+As Greg Ward puts it:</p>
+<p><em>The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...</em></p>
+<p>So, I think my Windows readers will be best served if I put here
+a brief summary of the Unix terminology. Old time Unix geeks may safely
+skip this section. Let me just notice that <tt class="literal"><span class="pre">optparse</span></tt> could be
+extended to implement other kinds of conventions for optional argument
+parsing..</p>
+<p>Here is optparse/Unix/GNU terminology:
+the arguments given to a command-line script - <em>i.e.</em> the arguments
+that Python stores in the list <tt class="literal"><span class="pre">sys.argv[1:]</span></tt> - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.</p>
+<p>In the example of the search/replace tool,
+I will need two options with an argument - since I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying whether or not a backup of the original
+files needs to be performed. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.</p>
+<p>Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that you want to convert them in
+the American format MM-DD-YYYY. If you are sure that all your dates
+are in the correct format, your can match them with a simple regular
+expression such as <tt class="literal"><span class="pre">(\d\d)-(\d\d)-(\d\d\d\d)</span></tt>
+(this regular expression looks for strings composed of three
+groups of digits separated by dashes, with the first and
+second group composed by two digits and the last group
+composed by four digits).</p>
+<p>In this particular example it is not so important to make a backup
+copy of the original files, since to revert to the original
+format it is enough to run the script again. So the syntax to use
+would be something like</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py --nobackup --regx=&quot;(\d\d)-(\d\d)-(\d\d\d\d)&quot; \
+ --repl=&quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>In order to emphasize the portability, I have used a generic
+<tt class="literal"><span class="pre">$&gt;</span></tt> promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as flexible as
+a Python script).</p>
+<p>The syntax here has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be <tt class="literal"><span class="pre">-x</span></tt> for <tt class="literal"><span class="pre">--regx</span></tt>, <tt class="literal"><span class="pre">-r</span></tt> for <tt class="literal"><span class="pre">--repl</span></tt>
+and <tt class="literal"><span class="pre">-n</span></tt> for <tt class="literal"><span class="pre">--nobackup</span></tt>; moreover, the <tt class="literal"><span class="pre">=</span></tt> sign can safely be
+removed. Then the previous command reads</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -n -x&quot;(\dd)-(\dd)-(\d\d\d\d)&quot; -r&quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -nx &quot;(\dd)-(\dd)-(\d\d\d\d)&quot; -r &quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>means the same as the previous line, i.e. <tt class="literal"><span class="pre">-nx</span></tt> is parsed as
+<tt class="literal"><span class="pre">-n</span> <span class="pre">-x</span></tt>. You can also freely exchange the order of the options,
+for instance in this way:</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -nr &quot;\2-\1-\3&quot; *.txt -x &quot;(\dd)-(\dd)-(\d\d\d\d)&quot;
+</pre>
+</blockquote>
+<p>This command will be parsed exactly as before, i.e. options and option
+arguments are not positional.</p>
+</div>
+<div class="section" id="how-does-it-work-in-practice">
+<h1><a name="how-does-it-work-in-practice">How does it work in practice?</a></h1>
+<p>Having stated the requirements, we may start implementing our
+search and replace tool. The first step, and the most important
+one, is to write down the documentation string, even if you
+will have to wait until the last section to understand
+why the docstring is the most important part of this script ;)</p>
+<blockquote>
+<pre class="literal-block">
+#!/usr/bin/env python
+&quot;&quot;&quot;
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+&quot;&quot;&quot;
+</pre>
+</blockquote>
+<p>On Windows the first line in unnecessary, but is good practice to have it
+in the Unix world.</p>
+<p>The next step is to write down a simple search and replace routine:</p>
+<blockquote>
+<pre class="literal-block">
+import re
+
+def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, &quot;U&quot;).read()
+ if backup_option:
+ print &gt;&gt; file(fname+&quot;.bak&quot;, &quot;w&quot;), txt,
+ print &gt;&gt; file(fname, &quot;w&quot;), rx.sub(repl, txt),
+</pre>
+</blockquote>
+<p>This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the &quot;U&quot; option in the line</p>
+<blockquote>
+<pre class="literal-block">
+txt=file(fname,&quot;U&quot;).read()
+</pre>
+</blockquote>
+<p>This is a new feature of Python 2.3. Text files open with the &quot;U&quot;
+option are read in &quot;Universal&quot; mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should &quot;massage&quot; the code a bit.
+Also, a full fledged script would check if the file exists
+and can be read, and would do something in the case it is not,
+but I ask you to forgive me for skipping on these points,
+since the thing I am really interested in is the <tt class="literal"><span class="pre">optparse</span></tt>
+module.</p>
+<p>So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the <tt class="literal"><span class="pre">OptionParser</span></tt> class provided by <tt class="literal"><span class="pre">optparse</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+import optparse
+parser = optparse.OptionParser(&quot;usage: %prog files [options]&quot;)
+</pre>
+</blockquote>
+<p>The string <tt class="literal"><span class="pre">&quot;usage:</span> <span class="pre">%prog</span> <span class="pre">files</span> <span class="pre">[options]&quot;</span></tt> will be used to
+print a customized usage message, where <tt class="literal"><span class="pre">%prog</span></tt> will be replaced
+by the name of the script (in this case <cite>replace.py`</cite>). You
+may safely omit it and <tt class="literal"><span class="pre">optparse</span></tt> will use a default
+<tt class="literal"><span class="pre">&quot;usage:</span> <span class="pre">%prog</span> <span class="pre">[options]&quot;</span></tt> string.</p>
+<p>Then, you tell the parser informations about which options
+it must recognize:</p>
+<blockquote>
+<pre class="literal-block">
+parser.add_option(&quot;-x&quot;, &quot;--regx&quot;,
+ help=&quot;regular expression&quot;)
+parser.add_option(&quot;-r&quot;, &quot;--repl&quot;,
+ help=&quot;replacement string&quot;)
+parser.add_option(&quot;-n&quot;, &quot;--nobackup&quot;,
+ action=&quot;store_true&quot;,
+ help=&quot;do not make backup copies&quot;)
+</pre>
+</blockquote>
+<p>The <tt class="literal"><span class="pre">help</span></tt> keyword argument is intended to document the
+intent of the given option; it is also used by <tt class="literal"><span class="pre">optparse</span></tt> in the
+usage message. The <tt class="literal"><span class="pre">action=store_true</span></tt> keyword argument is
+used to distinguish flags from options with arguments, it tells
+<tt class="literal"><span class="pre">optparse</span></tt> to set the flag <tt class="literal"><span class="pre">nobackup</span></tt> to <tt class="literal"><span class="pre">True</span></tt> if <tt class="literal"><span class="pre">-n</span></tt>
+or <tt class="literal"><span class="pre">--nobackup</span></tt> is given in the command line.</p>
+<p>Finally, you tell the parse to do its job and to parse the command line:</p>
+<blockquote>
+<pre class="literal-block">
+option, files = parser.parse_args()
+</pre>
+</blockquote>
+<p>The <tt class="literal"><span class="pre">.parse_args()</span></tt> method returns two values: <tt class="literal"><span class="pre">option</span></tt>,
+which is an instance of the <tt class="literal"><span class="pre">optparse.Option</span></tt> class, and <tt class="literal"><span class="pre">files</span></tt>,
+which is a list of positional arguments.
+The <tt class="literal"><span class="pre">option</span></tt> object has attributes - called <em>destionations</em> in
+<tt class="literal"><span class="pre">optparse</span></tt> terminology - corresponding to the given options.
+In our example, <tt class="literal"><span class="pre">option</span></tt> will have the attributes <tt class="literal"><span class="pre">option.regx</span></tt>,
+<tt class="literal"><span class="pre">option.repl</span></tt> and <tt class="literal"><span class="pre">option.nobackup</span></tt>.</p>
+<p>If no options are passed to the command line, all these attributes
+are initialized to <tt class="literal"><span class="pre">None</span></tt>, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+<tt class="literal"><span class="pre">True</span></tt> if they are given, to <tt class="literal"><span class="pre">None</span></tt> otherwise. So, in our example
+<tt class="literal"><span class="pre">option.nobackup</span></tt> is <tt class="literal"><span class="pre">True</span></tt> if the flag <tt class="literal"><span class="pre">-n</span></tt> or <tt class="literal"><span class="pre">--nobackup</span></tt>
+is given.
+The list <tt class="literal"><span class="pre">files</span></tt> contains the files passed
+to the command line (assuming you passed
+the names of accessible text files in your system).</p>
+<p>The main logic can be as simple as the following:</p>
+<blockquote>
+<pre class="literal-block">
+if not files:
+ print &quot;No files given!&quot;
+elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+else:
+ print &quot;Missing options or unrecognized options.&quot;
+ print __doc__ # documentation on how to use the script
+</pre>
+</blockquote>
+<p>A nice feature of <tt class="literal"><span class="pre">optparse</span></tt> is that an help option is automatically
+created, so <tt class="literal"><span class="pre">replace.py</span> <span class="pre">-h</span></tt> (or <tt class="literal"><span class="pre">replace.py</span> <span class="pre">--help</span></tt>) will work as
+you may expect:</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py --help
+usage: replace.py files [options]
+
+
+options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+</pre>
+</blockquote>
+<p>You may programmatically print the usage message by invoking
+<tt class="literal"><span class="pre">parser.print_help()</span></tt>.</p>
+<p>At this point you may test your script and see that it works as
+advertised.</p>
+</div>
+<div class="section" id="how-to-reduce-verbosity-and-make-your-life-with-optparse-happier">
+<h1><a name="how-to-reduce-verbosity-and-make-your-life-with-optparse-happier">How to reduce verbosity and make your life with <tt class="literal"><span class="pre">optparse</span></tt> happier</a></h1>
+<p>The approach we followed in the previous example has a disadvantage:
+it involves a certain amount of verbosity/redundance. Suppose for instance
+we want to add the ability to restore the original file from the backup copy.
+Then, we have to change the script in three points: in the docstring,
+in the <tt class="literal"><span class="pre">add_option</span></tt> list, and in the <tt class="literal"><span class="pre">if</span> <span class="pre">..</span> <span class="pre">elif</span> <span class="pre">..</span> <span class="pre">else</span> <span class="pre">...</span></tt>
+statement. At least one of this is redundant.
+One would be tempted to think that the information in the documentation
+string is redundant, since it is already magically provided in the help
+options: however, I will take the opposite view, that the information
+in the help options is redundant, since it is already contained in
+the docstring. It is a sacrilege to write a Python script without a
+docstring, since the docstring should always be available to automatic
+documentation tools such as pydoc.
+Since the docstring cannot be removed or shortened, the idea is to
+extract information from it, in such a way to avoid the boring task
+of writing by hand the <tt class="literal"><span class="pre">parser.add_option</span></tt> lines.
+I implemented this idea in a cookbook recipe, by writing an
+<tt class="literal"><span class="pre">optionparse</span></tt> module which is just a thin wrapper around <tt class="literal"><span class="pre">optparse</span></tt>.
+The relevant code is <a class="reference" href="optionparse.py">here</a>. The relevant functions are <tt class="literal"><span class="pre">optionparse.parse</span></tt>
+which parses the docstring and <tt class="literal"><span class="pre">optionparse.exit</span></tt> which exits the
+execution by displaying an usage message.
+To show how to use them, let me rewrite the
+search and replace tool (including the new restore option) in this way:</p>
+<pre class="literal-block">
+#!/usr/bin/env python
+&quot;&quot;&quot;
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ -R, --restore: restore the original from the backup
+&quot;&quot;&quot;
+import optionparse, os, shutil, re
+
+def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ # TODO: add a test to see if the file exists and can be read
+ txt = file(fname, &quot;U&quot;).read()
+ if backup_option:
+ print &gt;&gt; file(fname+&quot;.bak&quot;, &quot;w&quot;), txt
+ print &gt;&gt; file(fname, &quot;w&quot;), rx.sub(repl,txt)
+
+def restore(files): # restor original files from backup files
+ for fname in files:
+ if os.path.exists(fname+&quot;.bak&quot;):
+ shutil.copyfile(fname+&quot;.bak&quot;,fname)
+ else:
+ print &quot;Sorry, there is no backup copy for %s&quot; % fname
+
+if __name__=='__main__':
+ option, files = optionparse.parse(__doc__)
+ # optionparse.parse parses both the docstring and the command line!
+ if not files:
+ optionparse.exit()
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ elif option.restore:
+ restore(files)
+ else:
+ print &quot;Missing options or unrecognized options.&quot;
+</pre>
+<p>The code is quite readable. Internally <tt class="literal"><span class="pre">optionparse.parse(__doc__)</span></tt>
+works by generating an option parser from the docstring, then applying it
+to the command-line arguments.
+Working a bit more, one could also devise various tricks to avoid
+the redundance in the <tt class="literal"><span class="pre">if</span></tt> statement (for instance using a
+dictionary of functions and dispatching according to the name of
+the given option). However this simple recipe is good enough to
+provide a minimal wrapper to <tt class="literal"><span class="pre">optparse</span></tt>. It requires a minimum effort
+and works well for the most common case. For instance, the paper you are
+reading now has been written by using <tt class="literal"><span class="pre">optionparse</span></tt>: I used it to
+write a simple wrapper to docutils - the standard
+Python tool which converts (restructured) text files to HTML pages.
+It is also nice to notice that internally
+docutils itself uses <tt class="literal"><span class="pre">optparse</span></tt> to do its job, so actually this
+paper has been composed by using <tt class="literal"><span class="pre">optparse</span></tt> twice!</p>
+<p>Finally, you should keep in mind that this article only scratch the
+surface of <tt class="literal"><span class="pre">optparse</span></tt>, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a <tt class="literal"><span class="pre">store_false</span></tt> action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. The serious user of <tt class="literal"><span class="pre">optparse</span></tt> is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I will think that this article has fullfilled
+its function of &quot;appetizer&quot; to <tt class="literal"><span class="pre">optparse</span></tt>, if it has stimulate
+the reader to study more.</p>
+</div>
+<div class="section" id="references">
+<h1><a name="references">References</a></h1>
+<ul class="simple">
+<li><tt class="literal"><span class="pre">optparse</span></tt> is documented in the standard library</li>
+<li>the <tt class="literal"><span class="pre">optionparse</span></tt> module can be found <a class="reference" href="optionparse.py">here</a>.</li>
+<li>I wrote a Python Cookbook <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844">recipe</a> about optionparse.</li>
+</ul>
+</div>
+</div>
+<hr class="footer" />
+<div class="footer">
+<a class="reference" href="paper.txt">View document source</a>.
+Generated on: 2004-05-28 09:31 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/optparse/paper.tex b/pypers/optparse/paper.tex
new file mode 100755
index 0000000..5fc56c2
--- /dev/null
+++ b/pypers/optparse/paper.tex
@@ -0,0 +1,574 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper]{geometry}
+%% 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}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{style.tex}
+\title{The optparse module: writing command-line tools the easy way}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={The optparse module: writing command-line tools the easy way}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+\begin{quote}
+\begin{quote}
+\begin{description}
+\item [Status:]
+Draft
+
+
+\item [Author:]
+Michele Simionato
+
+
+\item [E-mail:]
+\href{mailto:michele.simionato@partecs.com}{michele.simionato@partecs.com}
+
+
+\item [Date:]
+April 2004
+
+
+\end{description}
+\end{quote}
+\end{quote}
+
+\emph{The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.} -- Greg Ward, optparse author
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\pdfbookmark[0]{Introduction}{introduction}
+\section*{Introduction}
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a ``scripting'' language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the (old fashioned) \texttt{getopt} module. I have never
+been particularly fond of \texttt{getopt}, since it required the programmer
+to do a sensible amount of boring job even for the parsing of simple
+argument lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+\texttt{optparse} a.k.a. \texttt{Optik}) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.
+
+The only disadvantage of \texttt{optparse} is that it is
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10{\%} of the features of \texttt{optparse} that she will use in the 90{\%} of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of \texttt{optparse}, showing how easy is to use it. Also, I will
+show some trick that will make your life with \texttt{optparse} much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need \texttt{optparse} even more than Unix programmers - and does not
+require any particular expertise to be fully appreciated.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-simple-example}{}
+\pdfbookmark[0]{A simple example}{a-simple-example}
+\section*{A simple example}
+
+I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and not always I have Emacs or
+another powerful editor installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files, look for a given regular expression
+and replace it everywhere in-place; moreover, it saves a backup copy of
+the original un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with \texttt{sed} or other tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utilities in Python, and actually many people
+(including myself) are actively replacing some Unix commands and bash
+scripts with Python scripts. In real life, I have hacked quite a lot
+the minimal tool that I describe here, and I continue to tune it as the
+need raises.
+
+As a final note, let me notice that I find \texttt{optparse}
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in a Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and \texttt{optparse}, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-unix-philosophy-for-command-line-arguments}{}
+\pdfbookmark[0]{The Unix philosophy for command-line arguments}{the-unix-philosophy-for-command-line-arguments}
+\section*{The Unix philosophy for command-line arguments}
+
+In order to understand how \texttt{optparse} works, it is essential
+to understand the Unix philosophy about command-lines arguments.
+As Greg Ward puts it:
+
+\emph{The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...}
+
+So, I think my Windows readers will be best served if I put here
+a brief summary of the Unix terminology. Old time Unix programmers may safely
+skip this section. Let me just notice that \texttt{optparse} could easily be
+extended to implement other kinds of conventions for optional argument
+parsing, but very likely you \emph{don't want} to do that.
+
+Here is optparse/Unix/GNU terminology:
+the arguments given to a command-line script - \emph{i.e.} the arguments
+that Python stores in the list \texttt{sys.argv[1:]} - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have option arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.
+
+In the example of the search/replace tool I was
+talking about, I will need two options with an argument - since I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying if a backup of the original files has
+to be performed or not. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.
+
+Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that I want to convert them in
+the American format MM-DD-YYYY. If I am sure that all my dates
+are in the correct format, I can match them with a simple regular
+expression such as \texttt{({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)} . Notice that
+this regular expression can be complicated at will, or can be
+written differently, but it is enough for the purpose of this
+paper and has the advantage of being simple enough to be
+understood even by readers with very little familiarity with
+regexes (it essentially looks for strings composed of the
+groups of digits separated by dashes, with the first and
+second group composed by two digits and the last group
+composed by four digits).
+
+In this particular example it is not so important to make a backup
+copy of the original files, since to reverts to the original
+format it is enough to run again the script: then the position
+of the month will be switched again back to the European
+convention. So the syntax to use would be something like
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}>~replace.py~--nobackup~--regx="({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~{\textbackslash}}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~--repl="{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In order to emphasize the portability, I have used a generic
+\texttt{{\$}>} promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as scalable as
+a Python script).
+
+The double slash syntax has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be \texttt{-x} for \texttt{--regx}, \texttt{-r} for \texttt{--repl}
+and \texttt{-n} for \texttt{--nobackup}; moreover, the \texttt{=} sign can safely be
+removed. Then the previous command reads
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}>~replace.py~-n~-x"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~-r"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}>~replace.py~-nx~"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~-r~"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+means the same as the previous line, i.e. \texttt{-nx} is parsed as
+\texttt{-n -x}. You can also freely exchange the order of the options
+(provided option arguments are kept right
+after their respective options):
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}>~replace.py~-nr~"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt~-x~"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This command will be parsed exactly as before, since options and option
+arguments are not positional.
+
+
+%___________________________________________________________________________
+
+\hypertarget{how-does-it-work-in-practice}{}
+\pdfbookmark[0]{How does it work in practice?}{how-does-it-work-in-practice}
+\section*{How does it work in practice?}
+
+Having stated the requirements, we may start implementing our
+search and replace tool. The first step, and the most important
+one, is to write down the documentation string, even if you
+will have to wait until the last section to understand
+why the docstring is the most important part of this script ;)
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}!/usr/bin/env~python}\\
+\mbox{"""}\\
+\mbox{Given~a~sequence~of~text~files,~replaces~everywhere}\\
+\mbox{a~regular~expression~x~with~a~replacement~string~s.}\\
+\mbox{}\\
+\mbox{~~usage:~{\%}prog~files~[options]}\\
+\mbox{~~-x,~--regx=REGX:~regular~expression}\\
+\mbox{~~-r,~--repl=REPL:~replacement~string}\\
+\mbox{~~-n,~--nobackup:~do~not~make~backup~copies}\\
+\mbox{"""}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+For the sake of my Windows readers here I notice
+that the first line is not needed if you work on
+Windows only, where it is just a comment, but in
+the Unix world it is important since it allows the
+shell to recognize the script as a python script.
+So, it is a good habit to use it, it is harmless in Windows
+and helpful in Unix.
+
+The next step is to write down a simple search and replace routine:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{import~optparse,~re}\\
+\mbox{}\\
+\mbox{def~replace(regx,repl,files,backup{\_}option=True):}\\
+\mbox{~~~~rx=re.compile(regx)}\\
+\mbox{~~~~for~fname~in~files:}\\
+\mbox{~~~~~~~~txt=file(fname,"U").read()}\\
+\mbox{~~~~~~~~if~backup{\_}option:}\\
+\mbox{~~~~~~~~~~~~print~>>~file(fname+".bak",~"w"),~txt,}\\
+\mbox{~~~~~~~~print~>>~file(fname,"w"),~rx.sub(repl,txt),}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the ``U'' option in the line
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{txt=file(fname,"U").read()}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This is a new feature of Python 2.3. Text files open with the ``U''
+option are read in ``Universal'' mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should ``massage'' a bit the code.
+Also, a fully fledget script would check if the file exists
+and can be read, and would do something in the case it is not,
+but I think you will forbid me for skipping on these points,
+since the thing I am really interested in is the \texttt{optparse}
+module that, as I am sure you noticed, I have already imported
+at the top.
+
+So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the \texttt{OptionParser} class provided by \texttt{optparse}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{parser~=~optparse.OptionParser("usage:~{\%}prog~files~[options]")}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The string \texttt{"usage: {\%}prog files [options]"} will be used to
+print a customized usage message, where \texttt{{\%}prog} will be replaced
+by the name of the script - in this case - \texttt{replace.py}. You
+may safely omit it and \texttt{optparse} will use a default
+\texttt{"usage: {\%}prog [options]"} string.
+
+Then, you tell the parser informations about which options
+it must recognize:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{parser.add{\_}option("-x",~"--regx",}\\
+\mbox{~~~~~~~~~~~~~~~~help="regular~expression")}\\
+\mbox{parser.add{\_}option("-r",~"--repl",}\\
+\mbox{~~~~~~~~~~~~~~~~help="replacement~string")}\\
+\mbox{parser.add{\_}option("-n",~"--nobackup",}\\
+\mbox{~~~~~~~~~~~~~~~~action="store{\_}true",}\\
+\mbox{~~~~~~~~~~~~~~~~help="do~not~make~backup~copies")}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The \texttt{help} keyword argument is intended to document the
+intent of the given option; it is also used by \texttt{optparse} in the
+usage message. The \texttt{action=store{\_}true} keyword argument is
+used to distinguish flags from options with arguments, it tells
+\texttt{optparse} to set the flag \texttt{nobackup} to \texttt{True} if \texttt{-n}
+or \texttt{--nobackup} is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{option,~files~=~parser.parse{\_}args()}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The \texttt{.parse{\_}args()} method returns two values: an object \texttt{option},
+which is an instance of the \texttt{optparse.Option} class, and a list
+of positional arguments
+The \texttt{option} object has attributes - called \emph{destionations} in
+\texttt{optparse} terminology - corresponding to the given options.
+In our example, \texttt{option} will have the attributes \texttt{option.regx},
+\texttt{option.repl} and \texttt{option.nobackup}.
+
+If no options are passed to the command line, all these attributes
+are initialized to \texttt{None}, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+\texttt{True} if they are given, to``None`` otherwise. So, in our example
+\texttt{option.nobackup} is \texttt{True} if the flag \texttt{-n} or \texttt{--nobackup}
+is given.
+The list \texttt{files} in our example contains the files passed
+to the command line that must be replaced (assuming you passed
+the names of accessible files in your system).
+
+At this point, we may dispatch to the right routine according to
+the given command line with a simple \texttt{if} statement such as the
+following:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{if~not~files:}\\
+\mbox{~~~~print~"No~files~given!"}\\
+\mbox{elif~option.regx~and~option.repl:}\\
+\mbox{~~~~replace(option.regex,~option.repl,~files,~not~option.nobackup)}\\
+\mbox{else:}\\
+\mbox{~~~~print~"Missing~options~or~unrecognized~options."}\\
+\mbox{~~~~print~{\_}{\_}doc{\_}{\_}~{\#}~documentation~on~how~to~use~the~script}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+A nice feature of \texttt{optparse} is that an help option is automatically
+created, so \texttt{replace.py -h} (or \texttt{replace.py --help}) will work as
+you may expect:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}>~replace.py~--help}\\
+\mbox{usage:~replace.py~files~[options]}\\
+\mbox{}\\
+\mbox{}\\
+\mbox{options:}\\
+\mbox{~~-h,~--help~~~~~~~~~~~show~this~help~message~and~exit}\\
+\mbox{~~-xREGX,~--regx=REGX~~regular~expression}\\
+\mbox{~~-rREPL,~--repl=REPL~~replacement~string}\\
+\mbox{~~-n,~--nobackup~~~~~~~do~not~make~backup~copies}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+You may programmatically print the usage message by invoking
+\texttt{parser.print{\_}help()}.
+
+At this point you may test your script and see that it works as
+advertised.
+
+
+%___________________________________________________________________________
+
+\hypertarget{how-to-reduce-verbosity-and-make-your-life-with-optparse-happier}{}
+\pdfbookmark[0]{How to reduce verbosity and make your life with optparse happier}{how-to-reduce-verbosity-and-make-your-life-with-optparse-happier}
+\section*{How to reduce verbosity and make your life with \texttt{optparse} happier}
+
+The approach we followed in the previous example has a disadvantage:
+it involves a certain amount of verbosity/redundance. Suppose for instance
+we want to add another option to the script, for instance the ability to
+restore the original file from the backup copy, which is quite handy in
+case something went wrong with the replace.
+Then, we have to change the script in three points: in the docstring,
+in the \texttt{add{\_}option} list, and in the \texttt{if .. elif .. else ...}
+statement. At least one of this is redundant.
+One would be tempted to think that the information in the documentation
+string is redundant, since it is already magically provided in the help
+options: however, I will take the opposite view, that the information
+in the help options is redundant, since it is already contained in
+the docstring. It is a kind of sacrilege to write a Python script
+without a docstring explaining what it does, so the docstring cannot
+be removed or shortened, since it must be available to automatic
+documentation tools such as pydoc. So, the idea is to
+extract information from the docstring, and to avoid altogether
+the boring task of writing by hand the \texttt{parser.add{\_}option} lines.
+I implemented this idea in a cookbook recipe, by writing an
+\texttt{optionparse} module which is just a thin wrapper around \texttt{optparse}.
+
+Here I just show how the script (including the new restore option)
+will look by using my \texttt{optionparse} wrapper:
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}!/usr/bin/env~python}\\
+\mbox{"""}\\
+\mbox{Given~a~sequence~of~text~files,~replaces~everywhere}\\
+\mbox{a~regular~expression~x~with~a~replacement~string~s.}\\
+\mbox{}\\
+\mbox{~~usage:~{\%}prog~files~[options]}\\
+\mbox{~~-x,~--regx=REGX:~regular~expression}\\
+\mbox{~~-r,~--repl=REPL:~replacement~string}\\
+\mbox{~~-n,~--nobackup:~do~not~make~backup~copies}\\
+\mbox{~~-R,~--restore:~restore~the~original~from~the~backup}\\
+\mbox{"""}\\
+\mbox{import~optionparse,~os,~shutil,~re}\\
+\mbox{}\\
+\mbox{def~replace(regx,repl,files,backup{\_}option=True):}\\
+\mbox{~~~~rx=re.compile(regx)}\\
+\mbox{~~~~for~fname~in~files:}\\
+\mbox{~~~~~~~~{\#}~TODO:~add~a~test~to~see~if~the~file~exists~and~can~be~read}\\
+\mbox{~~~~~~~~txt=file(fname,"U").read()}\\
+\mbox{~~~~~~~~if~backup{\_}option:}\\
+\mbox{~~~~~~~~~~~~print~>>~file(fname+".bak","w"),~txt}\\
+\mbox{~~~~~~~~print~>>~file(fname,"w"),~rx.sub(repl,txt)}\\
+\mbox{}\\
+\mbox{def~restore(files):}\\
+\mbox{~~~~for~fname~in~files:~~~~~~~}\\
+\mbox{~~~~~~~~if~os.path.exists(fname+".bak"):}\\
+\mbox{~~~~~~~~~~~~shutil.copyfile(fname+".bak",fname)}\\
+\mbox{~~~~~~~~else:}\\
+\mbox{~~~~~~~~~~~~print~"Sorry,~there~is~no~backup~copy~for~{\%}s"~{\%}~fname}\\
+\mbox{}\\
+\mbox{if~{\_}{\_}name{\_}{\_}=='{\_}{\_}main{\_}{\_}':}\\
+\mbox{~~~~option,files=optionparse.parse({\_}{\_}doc{\_}{\_})}\\
+\mbox{~~~~{\#}~optionparse.parse~parses~both~the~docstring~and~the~command~line!}\\
+\mbox{~~~~if~not~files:}\\
+\mbox{~~~~~~~~optionparse.exit()}\\
+\mbox{~~~~elif~option.regx~and~option.repl:}\\
+\mbox{~~~~~~~~replace(option.regex,~option.repl,~files,~not~option.nobackup)}\\
+\mbox{~~~~elif~option.restore:}\\
+\mbox{~~~~~~~~restore(files)}\\
+\mbox{~~~~else:}\\
+\mbox{~~~~~~~~print~"Missing~options~or~unrecognized~options."}
+\end{flushleft}\end{ttfamily}
+
+Working a bit more, one could also devise various tricks to avoid
+the redundance in the \texttt{if} statement (for instance using a
+dictionary of functions and dispatching according to the name of
+the given option). However this simple recipe is good enough to
+provide a minimal wrapper to \texttt{optparse} which requires a minimum effort
+and works well for the most common case. For instance, the paper you are
+reading now has been written by using \texttt{optionparse}: I used it to
+write a simple wrapper to docutils - the standard
+Python tools to convert text files to HTML pages - to customize
+its behavior to my needs. It is also nicer to notice that internally
+docutils itself uses \texttt{optparse} to do its job, so actually this
+paper has been composed by using \texttt{optparse} twice!
+
+Finally, you should keep in mind that this article only scratch the
+surface of \texttt{optparse}, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a \texttt{store{\_}false} action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. So, the serious user of \texttt{optparse} is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I will think that this article has fullfilled
+its function of ``appetizer'' to \texttt{optparse}, if it has stimulate
+the reader to study more.
+
+
+%___________________________________________________________________________
+
+\hypertarget{references}{}
+\pdfbookmark[0]{References}{references}
+\section*{References}
+\begin{itemize}
+\item
+\texttt{optparse} is documented in the standard library
+
+\item
+the \texttt{optionparse} module can be found \href{http://www.phyast.pitt.edu/~micheles/python/optionparse}{here}.
+
+\item
+I wrote a Python Cookbook \href{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844}{recipe} about optionparse.
+
+\end{itemize}
+
+\end{document}
+
diff --git a/pypers/optparse/paper.txt b/pypers/optparse/paper.txt
new file mode 100755
index 0000000..80c328c
--- /dev/null
+++ b/pypers/optparse/paper.txt
@@ -0,0 +1,453 @@
+The optparse module: writing command-line tools the easy way
+=======================================================================
+
+ :Status: Draft
+ :Author: Michele Simionato
+ :E-mail: michele.simionato@partecs.com
+ :Date: May 2004
+
+*The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.* -- Greg Ward, optparse author
+
+Introduction
+-----------------------------------------------------------------------
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a "scripting" language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the``getopt`` module. I have never
+been particularly fond of ``getopt``, since it required
+a sensible amount of coding even for the parsing of simple
+command-lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+``optparse`` a.k.a. ``Optik``) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.
+
+The only disadvantage of ``optparse`` is that it is
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of ``optparse`` that she will use in the 90% of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of ``optparse``. Also, I will show some trick that will make your life
+with ``optparse`` much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need ``optparse`` even more than Unix programmers; it does not
+require any particular expertise to be fully appreciated.
+
+A simple example
+---------------------------------------
+
+I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and I do not always have sed/awk or
+even Emacs installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files and replace a given regular expression
+everywhere in-place; moreover, it saves a backup copy of the original
+un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with specialized tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utilities in Python, and actually many people
+(including myself) are actively replacing some Unix commands and bash
+scripts with Python scripts. In real life, I have extended a lot
+the minimal tool that I describe here, and I continue to tune it as
+needed. For instance, you can make it to work recursively and/or on
+remote directories.
+
+As a final note, let me notice that I find ``optparse``
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in the Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and ``optparse``, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.
+
+The Unix philosophy for command-line arguments
+-------------------------------------------------
+
+In order to understand how ``optparse`` works, it is essential
+to understand the Unix philosophy about command-lines arguments.
+As Greg Ward puts it:
+
+*The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...*
+
+So, I think my Windows readers will be best served if I put here
+a brief summary of the Unix terminology. Old time Unix geeks may safely
+skip this section. Let me just notice that ``optparse`` could be
+extended to implement other kinds of conventions for optional argument
+parsing..
+
+Here is optparse/Unix/GNU terminology:
+the arguments given to a command-line script - *i.e.* the arguments
+that Python stores in the list ``sys.argv[1:]`` - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.
+
+In the example of the search/replace tool,
+I will need two options with an argument - since I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying whether or not a backup of the original
+files needs to be performed. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.
+
+Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that you want to convert them in
+the American format MM-DD-YYYY. If you are sure that all your dates
+are in the correct format, your can match them with a simple regular
+expression such as ``(\d\d)-(\d\d)-(\d\d\d\d)``
+(this regular expression looks for strings composed of three
+groups of digits separated by dashes, with the first and
+second group composed by two digits and the last group
+composed by four digits).
+
+In this particular example it is not so important to make a backup
+copy of the original files, since to revert to the original
+format it is enough to run the script again. So the syntax to use
+would be something like
+
+ ::
+
+ $> replace.py --nobackup --regx="(\d\d)-(\d\d)-(\d\d\d\d)" \
+ --repl="\2-\1-\3" *.txt
+
+
+In order to emphasize the portability, I have used a generic
+``$>`` promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as flexible as
+a Python script).
+
+The syntax here has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be ``-x`` for ``--regx``, ``-r`` for ``--repl``
+and ``-n`` for ``--nobackup``; moreover, the ``=`` sign can safely be
+removed. Then the previous command reads
+
+ ::
+
+ $> replace.py -n -x"(\dd)-(\dd)-(\d\d\d\d)" -r"\2-\1-\3" *.txt
+
+You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance
+
+ ::
+
+ $> replace.py -nx "(\dd)-(\dd)-(\d\d\d\d)" -r "\2-\1-\3" *.txt
+
+means the same as the previous line, i.e. ``-nx`` is parsed as
+``-n -x``. You can also freely exchange the order of the options,
+for instance in this way:
+
+ ::
+
+ $> replace.py -nr "\2-\1-\3" *.txt -x "(\dd)-(\dd)-(\d\d\d\d)"
+
+This command will be parsed exactly as before, i.e. options and option
+arguments are not positional.
+
+How does it work in practice?
+-----------------------------
+
+Having stated the requirements, we may start implementing our
+search and replace tool. The first step, and the most important
+one, is to write down the documentation string, even if you
+will have to wait until the last section to understand
+why the docstring is the most important part of this script ;)
+
+ ::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ """
+
+On Windows the first line in unnecessary, but is good practice to have it
+in the Unix world.
+
+The next step is to write down a simple search and replace routine:
+
+ ::
+
+ import re
+
+ def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, "U").read()
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt,
+ print >> file(fname, "w"), rx.sub(repl, txt),
+
+This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the "U" option in the line
+
+ ::
+
+ txt=file(fname,"U").read()
+
+This is a new feature of Python 2.3. Text files open with the "U"
+option are read in "Universal" mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should "massage" the code a bit.
+Also, a full fledged script would check if the file exists
+and can be read, and would do something in the case it is not,
+but I ask you to forgive me for skipping on these points,
+since the thing I am really interested in is the ``optparse``
+module.
+
+So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the ``OptionParser`` class provided by ``optparse``:
+
+ ::
+
+ import optparse
+ parser = optparse.OptionParser("usage: %prog files [options]")
+
+The string ``"usage: %prog files [options]"`` will be used to
+print a customized usage message, where ``%prog`` will be replaced
+by the name of the script (in this case `replace.py``). You
+may safely omit it and ``optparse`` will use a default
+``"usage: %prog [options]"`` string.
+
+Then, you tell the parser informations about which options
+it must recognize:
+
+ ::
+
+ parser.add_option("-x", "--regx",
+ help="regular expression")
+ parser.add_option("-r", "--repl",
+ help="replacement string")
+ parser.add_option("-n", "--nobackup",
+ action="store_true",
+ help="do not make backup copies")
+
+The ``help`` keyword argument is intended to document the
+intent of the given option; it is also used by ``optparse`` in the
+usage message. The ``action=store_true`` keyword argument is
+used to distinguish flags from options with arguments, it tells
+``optparse`` to set the flag ``nobackup`` to ``True`` if ``-n``
+or ``--nobackup`` is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+
+ ::
+
+ option, files = parser.parse_args()
+
+The ``.parse_args()`` method returns two values: ``option``,
+which is an instance of the ``optparse.Option`` class, and ``files``,
+which is a list of positional arguments.
+The ``option`` object has attributes - called *destionations* in
+``optparse`` terminology - corresponding to the given options.
+In our example, ``option`` will have the attributes ``option.regx``,
+``option.repl`` and ``option.nobackup``.
+
+If no options are passed to the command line, all these attributes
+are initialized to ``None``, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+``True`` if they are given, to ``None`` otherwise. So, in our example
+``option.nobackup`` is ``True`` if the flag ``-n`` or ``--nobackup``
+is given.
+The list ``files`` contains the files passed
+to the command line (assuming you passed
+the names of accessible text files in your system).
+
+The main logic can be as simple as the following:
+
+ ::
+
+ if not files:
+ print "No files given!"
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ else:
+ print "Missing options or unrecognized options."
+ print __doc__ # documentation on how to use the script
+
+A nice feature of ``optparse`` is that an help option is automatically
+created, so ``replace.py -h`` (or ``replace.py --help``) will work as
+you may expect:
+
+ ::
+
+ $> replace.py --help
+ usage: replace.py files [options]
+
+
+ options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+
+
+You may programmatically print the usage message by invoking
+``parser.print_help()``.
+
+At this point you may test your script and see that it works as
+advertised.
+
+How to reduce verbosity and make your life with ``optparse`` happier
+---------------------------------------------------------------------
+
+The approach we followed in the previous example has a disadvantage:
+it involves a certain amount of verbosity/redundance. Suppose for instance
+we want to add the ability to restore the original file from the backup copy.
+Then, we have to change the script in three points: in the docstring,
+in the ``add_option`` list, and in the ``if .. elif .. else ...``
+statement. At least one of this is redundant.
+One would be tempted to think that the information in the documentation
+string is redundant, since it is already magically provided in the help
+options: however, I will take the opposite view, that the information
+in the help options is redundant, since it is already contained in
+the docstring. It is a sacrilege to write a Python script without a
+docstring, since the docstring should always be available to automatic
+documentation tools such as pydoc.
+Since the docstring cannot be removed or shortened, the idea is to
+extract information from it, in such a way to avoid the boring task
+of writing by hand the ``parser.add_option`` lines.
+I implemented this idea in a cookbook recipe, by writing an
+``optionparse`` module which is just a thin wrapper around ``optparse``.
+The relevant code is here_. The relevant functions are ``optionparse.parse``
+which parses the docstring and ``optionparse.exit`` which exits the
+execution by displaying an usage message.
+To show how to use them, let me rewrite the
+search and replace tool (including the new restore option) in this way::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ -R, --restore: restore the original from the backup
+ """
+ import optionparse, os, shutil, re
+
+ def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ # TODO: add a test to see if the file exists and can be read
+ txt = file(fname, "U").read()
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt
+ print >> file(fname, "w"), rx.sub(repl,txt)
+
+ def restore(files): # restor original files from backup files
+ for fname in files:
+ if os.path.exists(fname+".bak"):
+ shutil.copyfile(fname+".bak",fname)
+ else:
+ print "Sorry, there is no backup copy for %s" % fname
+
+ if __name__=='__main__':
+ option, files = optionparse.parse(__doc__)
+ # optionparse.parse parses both the docstring and the command line!
+ if not files:
+ optionparse.exit()
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ elif option.restore:
+ restore(files)
+ else:
+ print "Missing options or unrecognized options."
+
+The code is quite readable. Internally ``optionparse.parse(__doc__)``
+works by generating an option parser from the docstring, then applying it
+to the command-line arguments.
+Working a bit more, one could also devise various tricks to avoid
+the redundance in the ``if`` statement (for instance using a
+dictionary of functions and dispatching according to the name of
+the given option). However this simple recipe is good enough to
+provide a minimal wrapper to ``optparse``. It requires a minimum effort
+and works well for the most common case. For instance, the paper you are
+reading now has been written by using ``optionparse``: I used it to
+write a simple wrapper to docutils - the standard
+Python tool which converts (restructured) text files to HTML pages.
+It is also nice to notice that internally
+docutils itself uses ``optparse`` to do its job, so actually this
+paper has been composed by using ``optparse`` twice!
+
+Finally, you should keep in mind that this article only scratch the
+surface of ``optparse``, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a ``store_false`` action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. The serious user of ``optparse`` is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I will think that this article has fullfilled
+its function of "appetizer" to ``optparse``, if it has stimulate
+the reader to study more.
+
+References
+--------------------------
+
+- ``optparse`` is documented in the standard library
+- the ``optionparse`` module can be found here_.
+- I wrote a Python Cookbook recipe_ about optionparse.
+
+.. _here: optionparse.py
+
+.. _recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844
diff --git a/pypers/optparse/paper0.txt b/pypers/optparse/paper0.txt
new file mode 100755
index 0000000..bd4f4ea
--- /dev/null
+++ b/pypers/optparse/paper0.txt
@@ -0,0 +1,457 @@
+The optparse module: writing command line tools the easy way
+=======================================================================
+
+ :Status: Draft
+ :Author: Michele Simionato
+ :E-mail: michelesimionato@libero.it
+ :Date: March 2004
+
+*The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.* -- Greg Ward, optparse author
+
+Introduction
+-----------------------------------------------------------------------
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command
+line tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a "scripting" language - to provide
+facilities to help the programmer in the task of writing command
+line tools. For a long time Python support for this kind of
+tasks has been devoted to the old fashioned ``getopt`` module. I have
+never been particularly fond of ``getopt``, since it required the programmer
+to do a sensible amount of boring job even for the parsing of simple
+argument lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+``optparse`` and ``Optik``, its precursor) now the Python programmer
+has at her disposal - in the standard library and not as an
+add-on module - a fully fledged Object Oriented API for command
+line arguments parsing, which makes writing Unix-style command
+line tools easy, efficient and fast.
+
+The only "disadvantage" of ``optparse`` is that it is a kind of
+sophisticated tool, and requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of ``optparse`` that you will use in the 90% of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of ``optparse``, showing how simple is to use it. Also, I will
+show some trick that will make your life with ``optparse`` much easier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need ``optparse`` even more than Unix programmers - and does not
+require any particular expertise to be fully appreciated.
+
+A simple example
+---------------------------------------
+
+I will take as pedagogical example a little tool I wrote in real life,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and not always I have Emacs or
+another powerful editor installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files, look for a given regular expression
+and replace it everywhere in-place; moreover, it saves a backup copy of
+the original un-modified files and give the option to recover
+them if I want to. All of this can be done more efficiently in the
+Unix world with ``sed`` or other tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write command line tools in Python, and actually many people
+(including myself) are actively replacing bash scripts with Python
+scripts which wraps Unix tools or even replace them, if easy of modification
+is an important requirement. So, in real life, I have hacked
+quite a lot the minimal tool that I am describing here, and my
+real tool does much more than that.
+
+Also - as a side note - let me notice that I find ``optparse``
+to be much more useful in a Windows environment than in the Unix world,
+including in the term the various flavors of Linux and Mac OS X.
+The reason is that the pletora
+of pretty good command line tools which are available under Unix are
+missing in a Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and ``optparse``, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - notice for instance that Python
+is expanding to the embedded market too, including PDA's, cellular
+phones, and more.
+
+The Unix philosophy for command line arguments
+-------------------------------------------------
+
+In order to understand how ``optparse`` works, it is essential
+to understand the Unix terminology about command lines arguments.
+As Greg Ward puts it:
+
+*The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...*
+
+So, I think my Windows readers will be best served if I put here
+a brier summary of the Unix terminology. Old time Unix programmers may safely
+skip this section. ``optparse`` could easily be extended to implement
+other kinds of conventions for optional argument parsing, but very likely
+you *don't want* to do that, since the Unix conventions make a lot of sense.
+
+Here is optparse/Unix/GNU terminology:
+the arguments given to a command line script - *i.e.* the arguments
+that Python stores in the list ``sys.argv[1:]`` - are classified as
+positional arguments, options and option arguments.
+Options can be distinguished since they are prefixed by a dash ``-``
+or a double dash ``--``; options can have option arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command line and it is not an option or
+an option argument.
+
+For instance, in the example of the search/replace tool I was
+talking about, I will have three options, since I want to pass to
+the script a regular expression, a replacement string, and a
+flag specifying if a backup of the original files has to be
+performed or not. Both the regular expression option and the
+replacement string option require an argument, whereas the
+backup option does not require an argument and it is just a
+flag: or it is given, or it is not given.
+
+Consider - for the sake of the example - the following situations:
+you have a bunch of
+text files in my current directory containing dates in the
+European format DD-MM-YYYY, and that I want to convert them in
+the American format MM-DD-YYYY. If I am sure that all my dates
+are in the correct format, I can match them with a simple regular
+expression such as ``(\d\d)-(\d\d)-(\d\d\d\d)`` . Notice that
+this regular expression can be complicated at will, or can be
+written differently, but it is enough for the purpose of this
+paper and has the advantage of being simple enough to be
+understood even by readers with very little familiarity with
+regexes (it essentially looks for strings composed of the
+groups of digits separated by dashes, with the first and
+second group composed by two digits and the last group
+composed by four digits).
+
+In this particular example it is not so important to make a backup
+copy of the original files, since to reverts to the original
+format it is enough to run again the script: then the position
+of the month will be switched again back to the European
+convention. So the syntax to use would be something like
+
+ ::
+
+ $> replace.py --nobackup --regx="(\d\d)-(\d\d)-(\d\d\d\d)" --repl="\2-\1-\3" *.txt
+
+
+In order to emphasize the portability, I have used a generic
+``$>`` promtp, meaning that these examples works equally well both on
+Unix and on Windows.
+
+The double slash syntax has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be ``-x`` for ``--regx``, ``-r`` for ``--repl``
+and ``-n`` for ``--nobackup``; moreover, the ``=`` sign can safely be
+removed. Then the previous command reads
+
+ ::
+
+ $> replace.py -n -x"(\dd)-(\dd)-(\d\d\d\d)" -r"\2-\1-\3" *.txt
+
+You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance
+
+ ::
+
+ $> replace.py -nx "(\dd)-(\dd)-(\d\d\d\d)" -r "\2-\1-\3" *.txt
+
+means the same as the previous line, i.e. ``-nx`` is parsed as
+``-n -x``. You can also freely exchange the order of the options
+(provided option arguments are kept right
+after their respective options):
+
+ ::
+
+ $> replace.py -nr "\2-\1-\3" *.txt -x "(\dd)-(\dd)-(\d\d\d\d)"
+
+This command will be parsed exactly as before, since options and option
+arguments are not positional.
+
+How does it work in practice?
+-----------------------------
+
+So, I have stated the requirements. At this point we may start writing
+our search and replace tool. The first step, and the most important
+one, is to write down the documentation string:
+
+ ::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: don't make backup copies
+ """
+
+You will have to wait until the last section to understand
+why the docstring is the most important part of this script ;)
+Here I just notice, for the sake of Windows users,
+that the first line is not needed if you work on
+Windows only, where it is just a comment, but in
+the Unix world it is important since it allows the
+shell to recognize the script as a python script.
+So, it is a good habit to use it, it is harmless in Windows
+and helpful in Unix.
+
+The next step is to write down a simple search and replace routine:
+
+ ::
+
+ import optparse, re
+
+ def replace(regx,repl,files,backup_option=True):
+ rx=re.compile(regx)
+ for fname in files:
+ txt=file(fname,"U").read()
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt,
+ print >> file(fname,"w"), rx.sub(repl,txt),
+
+This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the "U" option in the line
+
+ ::
+
+ txt=file(fname,"U").read()
+
+This is a new feature of Python 2.3. Text files open with the "U"
+option are read in "Universal" mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. Notice that the script
+works by reading the whole file in memory: this is bad practice,
+and here I am assuming that you will use this script only on short
+files that will fit in you memory, otherwise you should "massage"
+a bit the code.
+Also, a fully fledget script would check if the file exists
+and can be read, and would do something in the case it is not,
+but I think you will forbid me for skipping on these points,
+since the thing I am really interested in is the ``optparse``
+module that, as I am sure you noticed, I have already imported
+at the top.
+
+So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the ``OptionParser`` class provided by ``optparse``:
+
+ ::
+
+ parser = optparse.OptionParser("usage: %prog files [options]")
+
+The string ``"usage: %prog files [options]"`` will be used to
+print an customized usage message, where %prog will be replaced
+by the name of the script - in this case - ``replace.py``. You
+may safely omit it and ``optparse`` will use a default
+``"usage: %prog [options]"`` string.
+
+Then, you tell the parser informations about which options
+it must recognize:
+
+ ::
+
+ parser.add_option("-x", "--regx",
+ help="regular expression")
+ parser.add_option("-r", "--repl",
+ help="replacement string")
+ parser.add_option("-n", "--nobackup",
+ action="store_true",
+ help="do not make backup copies")
+
+The ``help`` keyword argument is intended to document the
+intent of the given option; it is also used by ``optparse`` in the
+usage message. The ``action=store_true`` keyword argument is
+used to distinguish flags from options with arguments, it tells
+``optparse`` to set the flag ``nobackup`` to ``True`` if ``-n``
+or ``--nobackup`` is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+
+ ::
+
+ option, files = parser.parse_args()
+
+The ``.parse_args()`` method returns two values: an object ``option``,
+which is an instance of the ``optparse.Option`` class, and a list
+of positional arguments
+The ``option`` object has attributes - called *destionations* in
+``optparse`` terminology - corresponding to the given options.
+In our example, ``option`` will have the attributes ``option.regx``,
+``option.repl`` and ``option.nobackup``.
+
+If no options are passed to the command line, all these attributes
+are initialized to ``None``, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+``True`` if they are given, to``None`` otherwise. So, in our example
+``option.nobackup`` is ``True`` if the flag ``-n`` or ``--nobackup``
+is given.
+The list ``files`` in our example contains the files passed
+to the command line that must be replaced (assuming you passed
+the names of accessible files in your system).
+
+At this point, we may dispatch to the right routines according to
+the given command line with a simple ``if`` statement such as the
+following:
+
+ ::
+
+ if not files:
+ print "No files given!"
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ else:
+ print "Missing options or unrecognized options."
+ print __doc__ # documentation on how to use the script
+
+A nice feature of ``optparse`` is that an help option is automatically
+created, so ``replace.py -h`` (or ``replace.py --help``) will work as
+you may expect:
+
+ ::
+
+ $> replace.py --help
+ usage: replace.py files [options]
+
+
+ options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+
+
+You may programmatically print the usage message by invoking
+``parser.print_help()``.
+
+At this point you may test your script and see that it works as
+advertised.
+
+How to reduce verbosity and make your life with ``optparse`` happier
+---------------------------------------------------------------------
+
+The approach we followed in the previous example has a disadvantage
+verbosity/redundance. Suppose for instance we want to add another
+option to the script, for instance the ability to restore the original file
+from the backup copy, which is quite handy in the case something goes
+wrong with the replace.
+Then, we have to change the script in three points: in the docstring,
+in the ``add_option`` list, and in the ``if .. elif .. else ...``
+statement. At least one of this is redundant.
+One would be tempted to think that the information in the documentation
+string is redundant, since it is already magically provided in the help
+options: however, I will take the opposite view, that the information
+in the help options is redundant, since it is already contained in
+the docstring. It is a kind of sacrilege to write a Python script
+without a docstring explaining what it does, so the docstring cannot
+be removed or shortened, since it must be available to automatic
+documentation tools such as pydoc. So, the idea is to
+extract information from the docstring, and to avoid altogether
+the boring task of writing by hand the ``parser.add_option`` lines.
+I implemented this idea in a cookbook recipe, by writing an
+``optionparse`` module which is just a thin wrapper around ``optparse``.
+Interested readers may look at the implementation at ...,
+
+here I just show how the script (including the new restore option)
+will look by using my ``optionparse`` wrapper:
+
+ ::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ -R, --restore: restore the original from the backup
+ """
+ import optionparse, os, shutil, re
+
+ def replace(regx,repl,files,backup_option=True):
+ rx=re.compile(regx)
+ for fname in files:
+ # you could a test to see if the file exists and can be read here
+ txt=file(fname,"U").read()
+ if backup_option:
+ print >> file(fname+".bak","w"), txt
+ print >> file(fname,"w"), rx.sub(repl,txt)
+
+ def restore(files):
+ for fname in files:
+ if os.path.exists(fname+".bak"):
+ shutil.copyfile(fname+".bak",fname)
+ else:
+ print "Sorry, there is no backup copy for %s" % fname
+
+ if __name__=='__main__':
+ option,files=optionparse.parse(__doc__)
+ # optionparse.parse parses both the docstring and the command line!
+ if not files:
+ optionparse.exit()
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ elif option.restore:
+ restore(files)
+ else:
+ print "Missing options or unrecognized options."
+
+Working a bit more, one could also devise various tricks to avoid
+the redundance in the ``if`` statement (for instance using a
+dictionary of functions and dispatching according to the name of
+the given option). However this simple recipe is good enough to
+provide a minimal wrapper to ``optparse`` with requires a minimum effort
+as it works well for the most common case. For instance, the same
+paper you are reading now, has been written by using it: I used
+``optionparse`` to write a simple wrapper to docutils - the standard
+Python tools to convert text files to HTML pages - to customize
+its behavior to my needs. It is also nicer to notice that internally
+docutils itself uses ``optparse`` to do its job, so actually this
+paper has been composed by using ``optparse`` twice!
+
+Finally, you should keep in mind that this article only scratch the
+surface of ``optparse``, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a ``store_false`` action and much more, even if often you don't need
+all this power. Still, it is handy to have it at your disposal when
+you need it. So, the serious user of ``optparse`` is strongly
+encorauged to read the standard documentation coming with it, which
+is quite good and detailed. I will think that this article has fullfilled
+its function of "appetizer" to ``optparse``, if it has stimulate
+the reader to dive into ``optparse``. \ No newline at end of file
diff --git a/pypers/optparse/paper2.html b/pypers/optparse/paper2.html
new file mode 100755
index 0000000..67f132a
--- /dev/null
+++ b/pypers/optparse/paper2.html
@@ -0,0 +1,373 @@
+<?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.9: http://docutils.sourceforge.net/" />
+<title>The optparse module: writing command-line tools the easy way</title>
+</head>
+<body>
+<div class="document" id="the-optparse-module-writing-command-line-tools-the-easy-way">
+<h1 class="title">The optparse module: writing command-line tools the easy way</h1>
+<blockquote>
+<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">Status:</th><td class="field-body">Draft</td>
+</tr>
+<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="field-name">E-mail:</th><td class="field-body"><a class="reference" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
+</tr>
+<tr class="field"><th class="field-name">Date:</th><td class="field-body">May 2004</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p><em>The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.</em> -- Greg Ward, optparse author</p>
+<div class="section" id="introduction">
+<h1><a name="introduction">Introduction</a></h1>
+<p>Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.</p>
+<p>Hence, it is important for a programming language - especially
+one that wants to be called a &quot;scripting&quot; language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the``getopt`` module. I have never
+been particularly fond of <tt class="docutils literal"><span class="pre">getopt</span></tt>, since it required
+a sensible amount of coding even for the parsing of simple
+command-lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+<tt class="docutils literal"><span class="pre">optparse</span></tt> a.k.a. <tt class="docutils literal"><span class="pre">Optik</span></tt>) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.</p>
+<p>The only disadvantage of <tt class="docutils literal"><span class="pre">optparse</span></tt> is that it is a
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of <tt class="docutils literal"><span class="pre">optparse</span></tt> that she will use in the 90% of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of <tt class="docutils literal"><span class="pre">optparse</span></tt>. Also, I will show some trick that will make your life
+with <tt class="docutils literal"><span class="pre">optparse</span></tt> much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need <tt class="docutils literal"><span class="pre">optparse</span></tt> even more than Unix programmers; it does not
+require any particular expertise to be fully appreciated.</p>
+</div>
+<div class="section" id="a-simple-example">
+<h1><a name="a-simple-example">A simple example</a></h1>
+<p>I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and I do not always have sed/awk or
+even Emacs installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.</p>
+<p>The tool takes a bunch of files and replace a given regular expression
+everywhere in-place; moreover, it saves a backup copy of the original
+un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with specialized tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utility in Python (or in Perl, but I am writing on
+Pyzine now ;)</p>
+<p>As a final note, let me notice that I find <tt class="docutils literal"><span class="pre">optparse</span></tt>
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in the Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and <tt class="docutils literal"><span class="pre">optparse</span></tt>, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.</p>
+</div>
+<div class="section" id="the-unix-philosophy-for-command-line-arguments">
+<h1><a name="the-unix-philosophy-for-command-line-arguments">The Unix philosophy for command-line arguments</a></h1>
+<p>In order to understand how <tt class="docutils literal"><span class="pre">optparse</span></tt> works, it is essential
+to understand the Unix philosophy about command-lines arguments.</p>
+<p>As Greg Ward puts it:</p>
+<p><em>The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...</em></p>
+<p>Here is a brief summary of the terminology:
+the arguments given to a command-line script - <em>i.e.</em> the arguments
+that Python stores in the list <tt class="docutils literal"><span class="pre">sys.argv[1:]</span></tt> - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.</p>
+<p>In the example of the search/replace tool,
+I will need two options with an argument - I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying whether or not a backup of the original
+files needs to be performed. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.</p>
+<p>Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that you want to convert them in
+the American format MM-DD-YYYY. If you are sure that all your dates
+are in the correct format, your can match them with a simple regular
+expression such as <tt class="docutils literal"><span class="pre">(\d\d)-(\d\d)-(\d\d\d\d)</span></tt>.</p>
+<p>In this particular example it is not so important to make a backup
+copy of the original files, since to revert to the original
+format it is enough to run the script again. So the syntax to use
+would be something like</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py --nobackup --regx=&quot;(\d\d)-(\d\d)-(\d\d\d\d)&quot; \
+ --repl=&quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>In order to emphasize the portability, I have used a generic
+<tt class="docutils literal"><span class="pre">$&gt;</span></tt> promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as flexible as
+a Python script).</p>
+<p>The syntax here has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be <tt class="docutils literal"><span class="pre">-x</span></tt> for <tt class="docutils literal"><span class="pre">--regx</span></tt>, <tt class="docutils literal"><span class="pre">-r</span></tt> for <tt class="docutils literal"><span class="pre">--repl</span></tt>
+and <tt class="docutils literal"><span class="pre">-n</span></tt> for <tt class="docutils literal"><span class="pre">--nobackup</span></tt>; moreover, the <tt class="docutils literal"><span class="pre">=</span></tt> sign can safely be
+removed. Then the previous command reads</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -n -x&quot;(\dd)-(\dd)-(\d\d\d\d)&quot; -r&quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -nx &quot;(\dd)-(\dd)-(\d\d\d\d)&quot; -r &quot;\2-\1-\3&quot; *.txt
+</pre>
+</blockquote>
+<p>means the same as the previous line, i.e. <tt class="docutils literal"><span class="pre">-nx</span></tt> is parsed as
+<tt class="docutils literal"><span class="pre">-n</span> <span class="pre">-x</span></tt>. You can also freely exchange the order of the options,
+for instance in this way:</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py -nr &quot;\2-\1-\3&quot; *.txt -x &quot;(\dd)-(\dd)-(\d\d\d\d)&quot;
+</pre>
+</blockquote>
+<p>This command will be parsed exactly as before, i.e. options and option
+arguments are not positional.</p>
+</div>
+<div class="section" id="how-does-it-work-in-practice">
+<h1><a name="how-does-it-work-in-practice">How does it work in practice?</a></h1>
+<p>Having stated the requirements, we may start implementing our
+search and replace tool. The first step, is to write down the
+documentation string:</p>
+<blockquote>
+<pre class="literal-block">
+#!/usr/bin/env python
+&quot;&quot;&quot;
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+&quot;&quot;&quot;
+</pre>
+</blockquote>
+<p>On Windows the first line in unnecessary, but is good practice to have it
+in the Unix world.</p>
+<p>The next step is to write down a simple search and replace routine:</p>
+<blockquote>
+<pre class="literal-block">
+import re
+
+def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, &quot;U&quot;).read() # quick &amp; dirty
+ if backup_option:
+ print &gt;&gt; file(fname+&quot;.bak&quot;, &quot;w&quot;), txt,
+ print &gt;&gt; file(fname, &quot;w&quot;), rx.sub(repl, txt),
+</pre>
+</blockquote>
+<p>This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the &quot;U&quot; option in the line</p>
+<blockquote>
+<pre class="literal-block">
+txt=file(fname,&quot;U&quot;).read()
+</pre>
+</blockquote>
+<p>This is a new feature of Python 2.3. Text files open with the &quot;U&quot;
+option are read in &quot;Universal&quot; mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should &quot;massage&quot; the code a bit.
+Also, a full fledged script would check if the file exists
+and can be read, and would do something in the case it is not.</p>
+<p>So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the <tt class="docutils literal"><span class="pre">OptionParser</span></tt> class provided by <tt class="docutils literal"><span class="pre">optparse</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+import optparse
+parser = optparse.OptionParser(&quot;usage: %prog files [options]&quot;)
+</pre>
+</blockquote>
+<p>The string <tt class="docutils literal"><span class="pre">&quot;usage:</span> <span class="pre">%prog</span> <span class="pre">files</span> <span class="pre">[options]&quot;</span></tt> will be used to
+print a customized usage message, where <tt class="docutils literal"><span class="pre">%prog</span></tt> will be replaced
+by the name of the script (in this case <cite>replace.py`</cite>). You
+may safely omit it and <tt class="docutils literal"><span class="pre">optparse</span></tt> will use a default
+<tt class="docutils literal"><span class="pre">&quot;usage:</span> <span class="pre">%prog</span> <span class="pre">[options]&quot;</span></tt> string.</p>
+<p>Then, you tell the parser informations about which options
+it must recognize:</p>
+<blockquote>
+<pre class="literal-block">
+parser.add_option(&quot;-x&quot;, &quot;--regx&quot;,
+ help=&quot;regular expression&quot;)
+parser.add_option(&quot;-r&quot;, &quot;--repl&quot;,
+ help=&quot;replacement string&quot;)
+parser.add_option(&quot;-n&quot;, &quot;--nobackup&quot;,
+ action=&quot;store_true&quot;,
+ help=&quot;do not make backup copies&quot;)
+</pre>
+</blockquote>
+<p>The <tt class="docutils literal"><span class="pre">help</span></tt> keyword argument is intended to document the
+intent of the given option; it is also used by <tt class="docutils literal"><span class="pre">optparse</span></tt> in the
+usage message. The <tt class="docutils literal"><span class="pre">action=store_true</span></tt> keyword argument is
+used to distinguish flags from options with arguments, it tells
+<tt class="docutils literal"><span class="pre">optparse</span></tt> to set the flag <tt class="docutils literal"><span class="pre">nobackup</span></tt> to <tt class="docutils literal"><span class="pre">True</span></tt> if <tt class="docutils literal"><span class="pre">-n</span></tt>
+or <tt class="docutils literal"><span class="pre">--nobackup</span></tt> is given in the command line.</p>
+<p>Finally, you tell the parse to do its job and to parse the command line:</p>
+<blockquote>
+<pre class="literal-block">
+option, files = parser.parse_args()
+</pre>
+</blockquote>
+<p>The <tt class="docutils literal"><span class="pre">.parse_args()</span></tt> method returns two values: <tt class="docutils literal"><span class="pre">option</span></tt>,
+which is an instance of the <tt class="docutils literal"><span class="pre">optparse.Option</span></tt> class, and <tt class="docutils literal"><span class="pre">files</span></tt>,
+which is a list of positional arguments.
+The <tt class="docutils literal"><span class="pre">option</span></tt> object has attributes - called <em>destionations</em> in
+<tt class="docutils literal"><span class="pre">optparse</span></tt> terminology - corresponding to the given options.
+In our example, <tt class="docutils literal"><span class="pre">option</span></tt> will have the attributes <tt class="docutils literal"><span class="pre">option.regx</span></tt>,
+<tt class="docutils literal"><span class="pre">option.repl</span></tt> and <tt class="docutils literal"><span class="pre">option.nobackup</span></tt>.</p>
+<p>If no options are passed to the command line, all these attributes
+are initialized to <tt class="docutils literal"><span class="pre">None</span></tt>, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+<tt class="docutils literal"><span class="pre">True</span></tt> if they are given, to <tt class="docutils literal"><span class="pre">None</span></tt> otherwise. So, in our example
+<tt class="docutils literal"><span class="pre">option.nobackup</span></tt> is <tt class="docutils literal"><span class="pre">True</span></tt> if the flag <tt class="docutils literal"><span class="pre">-n</span></tt> or <tt class="docutils literal"><span class="pre">--nobackup</span></tt>
+is given.
+The list <tt class="docutils literal"><span class="pre">files</span></tt> contains the files passed
+to the command line (assuming you passed
+the names of accessible text files in your system).</p>
+<p>The main logic can be as simple as the following:</p>
+<blockquote>
+<pre class="literal-block">
+if not files:
+ print &quot;No files given!&quot;
+elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+else:
+ print &quot;Missing options or unrecognized options.&quot;
+ print __doc__ # documentation on how to use the script
+</pre>
+</blockquote>
+<p>A nice feature of <tt class="docutils literal"><span class="pre">optparse</span></tt> is that an help option is automatically
+created, so <tt class="docutils literal"><span class="pre">replace.py</span> <span class="pre">-h</span></tt> (or <tt class="docutils literal"><span class="pre">replace.py</span> <span class="pre">--help</span></tt>) will work as
+you may expect:</p>
+<blockquote>
+<pre class="literal-block">
+$&gt; replace.py --help
+usage: replace.py files [options]
+
+
+options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+</pre>
+</blockquote>
+<p>You may programmatically print the usage message by invoking
+<tt class="docutils literal"><span class="pre">parser.print_help()</span></tt>.</p>
+<p>At this point you may test your script and see that it works as
+advertised.</p>
+</div>
+<div class="section" id="how-to-reduce-verbosity-and-make-your-life-with-optparse-happier">
+<h1><a name="how-to-reduce-verbosity-and-make-your-life-with-optparse-happier">How to reduce verbosity and make your life with <tt class="docutils literal"><span class="pre">optparse</span></tt> happier</a></h1>
+<p>The power of <tt class="docutils literal"><span class="pre">optparse``comes</span> <span class="pre">with</span> <span class="pre">a</span> <span class="pre">penalty:</span> <span class="pre">using</span> <span class="pre">``optparse</span></tt> in
+the standard way, as I explained before, involves a certain amount of
+verbosity/redundance.</p>
+<p>Suppose for instance
+I want to add the ability to restore the original file from the backup copy.
+Then, we have to change the script in three points: in the docstring,
+in the <tt class="docutils literal"><span class="pre">add_option</span></tt> list, and in the <tt class="docutils literal"><span class="pre">if</span> <span class="pre">..</span> <span class="pre">elif</span> <span class="pre">..</span> <span class="pre">else</span> <span class="pre">...</span></tt>
+statement. At least one of this is redundant.</p>
+<p>The redundance can be removed by parsing the docstring to infer the
+options to be recognized. This avoids the boring task
+of writing by hand the <tt class="docutils literal"><span class="pre">parser.add_option</span></tt> lines.
+I implemented this idea in a cookbook recipe, by writing an
+<tt class="docutils literal"><span class="pre">optionparse</span></tt> module which is just a thin wrapper around <tt class="docutils literal"><span class="pre">optparse</span></tt>.
+For sake of space, I cannot repeat it here, but you can find the code
+and a small explanation in the Python Cookbook (see the reference below).
+It is really easy to use. For instance, the paper you are
+reading now has been written by using <tt class="docutils literal"><span class="pre">optionparse</span></tt>: I used it to
+write a simple wrapper to docutils - the standard
+Python tool which converts (restructured) text files to HTML pages.
+It is also nice to notice that internally
+docutils itself uses <tt class="docutils literal"><span class="pre">optparse</span></tt> to do its job, so actually this
+paper has been composed by using <tt class="docutils literal"><span class="pre">optparse</span></tt> twice!</p>
+<p>Finally, you should keep in mind that this article only scratch the
+surface of <tt class="docutils literal"><span class="pre">optparse</span></tt>, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a <tt class="docutils literal"><span class="pre">store_false</span></tt> action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. The serious user of <tt class="docutils literal"><span class="pre">optparse</span></tt> is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I think that this article has fullfilled
+its function of &quot;appetizer&quot; to <tt class="docutils literal"><span class="pre">optparse</span></tt>, if it has stimulate
+the reader to learn more.</p>
+</div>
+<div class="section" id="references">
+<h1><a name="references">References</a></h1>
+<ul class="simple">
+<li><tt class="docutils literal"><span class="pre">optparse/optik</span></tt> is a sourceforge project on its own:
+<a class="reference" href="http://optik.sourceforge.net">http://optik.sourceforge.net</a></li>
+<li>starting from Python 2.3, <tt class="docutils literal"><span class="pre">optparse</span></tt> is included in the standard library:
+<a class="reference" href="http://www.python.org/doc/2.3.4/lib/module-optparse.html">http://www.python.org/doc/2.3.4/lib/module-optparse.html</a></li>
+<li>I wrote a Python Cookbook recipe about optparse:
+<a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844</a></li>
+</ul>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/optparse/paper2.tex b/pypers/optparse/paper2.tex
new file mode 100755
index 0000000..3058645
--- /dev/null
+++ b/pypers/optparse/paper2.tex
@@ -0,0 +1,507 @@
+\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 optparse module: writing command-line tools the easy way}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={The optparse module: writing command-line tools the easy way}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\setlength{\locallinewidth}{\linewidth}
+\begin{quote}
+\begin{quote}
+\begin{description}
+\item [Status:]
+Draft
+
+
+\item [Author:]
+Michele Simionato
+
+
+\item [E-mail:]
+\href{mailto:michele.simionato@gmail.com}{michele.simionato@gmail.com}
+
+
+\item [Date:]
+May 2004
+
+
+\end{description}
+\end{quote}
+\end{quote}
+
+\emph{The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.} -{}- Greg Ward, optparse author
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\pdfbookmark[0]{Introduction}{introduction}
+\section*{Introduction}
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a ``scripting'' language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the``getopt`` module. I have never
+been particularly fond of \texttt{getopt}, since it required
+a sensible amount of coding even for the parsing of simple
+command-lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+\texttt{optparse} a.k.a. \texttt{Optik}) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.
+
+The only disadvantage of \texttt{optparse} is that it is a
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10{\%} of the features of \texttt{optparse} that she will use in the 90{\%} of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of \texttt{optparse}. Also, I will show some trick that will make your life
+with \texttt{optparse} much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need \texttt{optparse} even more than Unix programmers; it does not
+require any particular expertise to be fully appreciated.
+
+
+%___________________________________________________________________________
+
+\hypertarget{a-simple-example}{}
+\pdfbookmark[0]{A simple example}{a-simple-example}
+\section*{A simple example}
+
+I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and I do not always have sed/awk or
+even Emacs installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files and replace a given regular expression
+everywhere in-place; moreover, it saves a backup copy of the original
+un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with specialized tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utility in Python (or in Perl, but I am writing on
+Pyzine now ;)
+
+As a final note, let me notice that I find \texttt{optparse}
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in the Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and \texttt{optparse}, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-unix-philosophy-for-command-line-arguments}{}
+\pdfbookmark[0]{The Unix philosophy for command-line arguments}{the-unix-philosophy-for-command-line-arguments}
+\section*{The Unix philosophy for command-line arguments}
+
+In order to understand how \texttt{optparse} works, it is essential
+to understand the Unix philosophy about command-lines arguments.
+
+As Greg Ward puts it:
+
+\emph{The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...}
+
+Here is a brief summary of the terminology:
+the arguments given to a command-line script - \emph{i.e.} the arguments
+that Python stores in the list \texttt{sys.argv{[}1:]} - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.
+
+In the example of the search/replace tool,
+I will need two options with an argument - I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying whether or not a backup of the original
+files needs to be performed. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.
+
+Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that you want to convert them in
+the American format MM-DD-YYYY. If you are sure that all your dates
+are in the correct format, your can match them with a simple regular
+expression such as \texttt{({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)}.
+
+In this particular example it is not so important to make a backup
+copy of the original files, since to revert to the original
+format it is enough to run the script again. So the syntax to use
+would be something like
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}>~replace.py~-{}-nobackup~-{}-regx="({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~{\textbackslash}~\\
+~~~~~~~~~~~~~~~~~~~~~~~~~-{}-repl="{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt
+}\end{quote}
+\end{quote}
+
+In order to emphasize the portability, I have used a generic
+\texttt{{\$}>} promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as flexible as
+a Python script).
+
+The syntax here has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be \texttt{-x} for \texttt{-{}-regx}, \texttt{-r} for \texttt{-{}-repl}
+and \texttt{-n} for \texttt{-{}-nobackup}; moreover, the \texttt{=} sign can safely be
+removed. Then the previous command reads
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}>~replace.py~-n~-x"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~-r"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt
+}\end{quote}
+\end{quote}
+
+You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}>~replace.py~-nx~"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"~-r~"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt
+}\end{quote}
+\end{quote}
+
+means the same as the previous line, i.e. \texttt{-nx} is parsed as
+\texttt{-n -x}. You can also freely exchange the order of the options,
+for instance in this way:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}>~replace.py~-nr~"{\textbackslash}2-{\textbackslash}1-{\textbackslash}3"~*.txt~-x~"({\textbackslash}dd)-({\textbackslash}dd)-({\textbackslash}d{\textbackslash}d{\textbackslash}d{\textbackslash}d)"
+}\end{quote}
+\end{quote}
+
+This command will be parsed exactly as before, i.e. options and option
+arguments are not positional.
+
+
+%___________________________________________________________________________
+
+\hypertarget{how-does-it-work-in-practice}{}
+\pdfbookmark[0]{How does it work in practice?}{how-does-it-work-in-practice}
+\section*{How does it work in practice?}
+
+Having stated the requirements, we may start implementing our
+search and replace tool. The first step, is to write down the
+documentation string:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\#}!/usr/bin/env~python~\\
+"{}"{}"~\\
+Given~a~sequence~of~text~files,~replaces~everywhere~\\
+a~regular~expression~x~with~a~replacement~string~s.~\\
+~\\
+~~usage:~{\%}prog~files~{[}options]~\\
+~~-x,~-{}-regx=REGX:~regular~expression~\\
+~~-r,~-{}-repl=REPL:~replacement~string~\\
+~~-n,~-{}-nobackup:~do~not~make~backup~copies~\\
+"{}"{}"
+}\end{quote}
+\end{quote}
+
+On Windows the first line in unnecessary, but is good practice to have it
+in the Unix world.
+
+The next step is to write down a simple search and replace routine:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+import~re~\\
+~\\
+def~replace(regx,~repl,~files,~backup{\_}option=True):~\\
+~~~~rx~=~re.compile(regx)~\\
+~~~~for~fname~in~files:~\\
+~~~~~~~~txt~=~file(fname,~"U").read()~{\#}~quick~{\&}~dirty~\\
+~~~~~~~~if~backup{\_}option:~\\
+~~~~~~~~~~~~print~>{}>~file(fname+".bak",~"w"),~txt,~\\
+~~~~~~~~print~>{}>~file(fname,~"w"),~rx.sub(repl,~txt),
+}\end{quote}
+\end{quote}
+
+This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the ``U'' option in the line
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+txt=file(fname,"U").read()
+}\end{quote}
+\end{quote}
+
+This is a new feature of Python 2.3. Text files open with the ``U''
+option are read in ``Universal'' mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should ``massage'' the code a bit.
+Also, a full fledged script would check if the file exists
+and can be read, and would do something in the case it is not.
+
+So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the \texttt{OptionParser} class provided by \texttt{optparse}:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+import~optparse~~\\
+parser~=~optparse.OptionParser("usage:~{\%}prog~files~{[}options]")
+}\end{quote}
+\end{quote}
+
+The string \texttt{"usage: {\%}prog files {[}options]"} will be used to
+print a customized usage message, where \texttt{{\%}prog} will be replaced
+by the name of the script (in this case \titlereference{replace.py`}). You
+may safely omit it and \texttt{optparse} will use a default
+\texttt{"usage: {\%}prog {[}options]"} string.
+
+Then, you tell the parser informations about which options
+it must recognize:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+parser.add{\_}option("-x",~"-{}-regx",~\\
+~~~~~~~~~~~~~~~~help="regular~expression")~\\
+parser.add{\_}option("-r",~"-{}-repl",~\\
+~~~~~~~~~~~~~~~~help="replacement~string")~\\
+parser.add{\_}option("-n",~"-{}-nobackup",~\\
+~~~~~~~~~~~~~~~~action="store{\_}true",~\\
+~~~~~~~~~~~~~~~~help="do~not~make~backup~copies")
+}\end{quote}
+\end{quote}
+
+The \texttt{help} keyword argument is intended to document the
+intent of the given option; it is also used by \texttt{optparse} in the
+usage message. The \texttt{action=store{\_}true} keyword argument is
+used to distinguish flags from options with arguments, it tells
+\texttt{optparse} to set the flag \texttt{nobackup} to \texttt{True} if \texttt{-n}
+or \texttt{-{}-nobackup} is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+option,~files~=~parser.parse{\_}args()
+}\end{quote}
+\end{quote}
+
+The \texttt{.parse{\_}args()} method returns two values: \texttt{option},
+which is an instance of the \texttt{optparse.Option} class, and \texttt{files},
+which is a list of positional arguments.
+The \texttt{option} object has attributes - called \emph{destionations} in
+\texttt{optparse} terminology - corresponding to the given options.
+In our example, \texttt{option} will have the attributes \texttt{option.regx},
+\texttt{option.repl} and \texttt{option.nobackup}.
+
+If no options are passed to the command line, all these attributes
+are initialized to \texttt{None}, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+\texttt{True} if they are given, to \texttt{None} otherwise. So, in our example
+\texttt{option.nobackup} is \texttt{True} if the flag \texttt{-n} or \texttt{-{}-nobackup}
+is given.
+The list \texttt{files} contains the files passed
+to the command line (assuming you passed
+the names of accessible text files in your system).
+
+The main logic can be as simple as the following:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+if~not~files:~\\
+~~~~print~"No~files~given!"~\\
+elif~option.regx~and~option.repl:~\\
+~~~~replace(option.regex,~option.repl,~files,~not~option.nobackup)~\\
+else:~\\
+~~~~print~"Missing~options~or~unrecognized~options."~\\
+~~~~print~{\_}{\_}doc{\_}{\_}~{\#}~documentation~on~how~to~use~the~script
+}\end{quote}
+\end{quote}
+
+A nice feature of \texttt{optparse} is that an help option is automatically
+created, so \texttt{replace.py -h} (or \texttt{replace.py -{}-help}) will work as
+you may expect:
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}>~replace.py~-{}-help~\\
+usage:~replace.py~files~{[}options]~\\
+~\\
+~\\
+options:~\\
+~~-h,~-{}-help~~~~~~~~~~~show~this~help~message~and~exit~\\
+~~-xREGX,~-{}-regx=REGX~~regular~expression~\\
+~~-rREPL,~-{}-repl=REPL~~replacement~string~\\
+~~-n,~-{}-nobackup~~~~~~~do~not~make~backup~copies
+}\end{quote}
+\end{quote}
+
+You may programmatically print the usage message by invoking
+\texttt{parser.print{\_}help()}.
+
+At this point you may test your script and see that it works as
+advertised.
+
+
+%___________________________________________________________________________
+
+\hypertarget{how-to-reduce-verbosity-and-make-your-life-with-optparse-happier}{}
+\pdfbookmark[0]{How to reduce verbosity and make your life with optparse happier}{how-to-reduce-verbosity-and-make-your-life-with-optparse-happier}
+\section*{How to reduce verbosity and make your life with \texttt{optparse} happier}
+
+The power of \texttt{optparse`{}`comes with a penalty: using `{}`optparse} in
+the standard way, as I explained before, involves a certain amount of
+verbosity/redundance.
+
+Suppose for instance
+I want to add the ability to restore the original file from the backup copy.
+Then, we have to change the script in three points: in the docstring,
+in the \texttt{add{\_}option} list, and in the \texttt{if .. elif .. else ...}
+statement. At least one of this is redundant.
+
+The redundance can be removed by parsing the docstring to infer the
+options to be recognized. This avoids the boring task
+of writing by hand the \texttt{parser.add{\_}option} lines.
+I implemented this idea in a cookbook recipe, by writing an
+\texttt{optionparse} module which is just a thin wrapper around \texttt{optparse}.
+For sake of space, I cannot repeat it here, but you can find the code
+and a small explanation in the Python Cookbook (see the reference below).
+It is really easy to use. For instance, the paper you are
+reading now has been written by using \texttt{optionparse}: I used it to
+write a simple wrapper to docutils - the standard
+Python tool which converts (restructured) text files to HTML pages.
+It is also nice to notice that internally
+docutils itself uses \texttt{optparse} to do its job, so actually this
+paper has been composed by using \texttt{optparse} twice!
+
+Finally, you should keep in mind that this article only scratch the
+surface of \texttt{optparse}, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a \texttt{store{\_}false} action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. The serious user of \texttt{optparse} is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I think that this article has fullfilled
+its function of ``appetizer'' to \texttt{optparse}, if it has stimulate
+the reader to learn more.
+
+
+%___________________________________________________________________________
+
+\hypertarget{references}{}
+\pdfbookmark[0]{References}{references}
+\section*{References}
+\begin{itemize}
+\item {}
+\texttt{optparse/optik} is a sourceforge project on its own:
+\href{http://optik.sourceforge.net}{http://optik.sourceforge.net}
+
+\item {}
+starting from Python 2.3, \texttt{optparse} is included in the standard library:
+\href{http://www.python.org/doc/2.3.4/lib/module-optparse.html}{http://www.python.org/doc/2.3.4/lib/module-optparse.html}
+
+\item {}
+I wrote a Python Cookbook recipe about optparse:
+\href{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844}{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844}
+
+\end{itemize}
+
+\end{document}
+
diff --git a/pypers/optparse/paper2.txt b/pypers/optparse/paper2.txt
new file mode 100755
index 0000000..00e00cd
--- /dev/null
+++ b/pypers/optparse/paper2.txt
@@ -0,0 +1,381 @@
+The optparse module: writing command-line tools the easy way
+=======================================================================
+
+ :Status: Draft
+ :Author: Michele Simionato
+ :E-mail: michele.simionato@gmail.com
+ :Date: May 2004
+
+*The optparse module is a powerful, flexible, extensible, easy-to-use
+command-line parsing library for Python. Using optparse, you can add
+intelligent, sophisticated handling of command-line options to your
+scripts with very little overhead.* -- Greg Ward, optparse author
+
+Introduction
+-----------------------------------------------------------------------
+
+Once upon a time, when graphic interfaces were still to be dreamed
+about, command-line tools were the body and the soul of all programming
+tools. Many years have passed since then, but some things have not
+changed: command-line tools are still fast, efficient, portable, easy
+to use and - more importantly - reliable. You can count on them.
+You can expect command-line scripts to work in any situation,
+during the installation phase, in a situation of disaster recovery, when
+your window manager breaks down and even in systems with severe
+memory/hardware constraints. When you really need them, command-line
+tools are always there.
+
+Hence, it is important for a programming language - especially
+one that wants to be called a "scripting" language - to provide
+facilities to help the programmer in the task of writing command-line
+tools. For a long time Python support for this kind of tasks has
+been provided by the``getopt`` module. I have never
+been particularly fond of ``getopt``, since it required
+a sensible amount of coding even for the parsing of simple
+command-lines. However, with the coming of Python 2.3 the situation
+has changed: thanks to the great job of Greg Ward (the author of
+``optparse`` a.k.a. ``Optik``) now the Python programmer
+has at her disposal (in the standard library and not as an
+add-on module) a fully fledged Object Oriented API for
+command-line arguments parsing, which makes writing Unix-style
+command-line tools easy, efficient and fast.
+
+The only disadvantage of ``optparse`` is that it is a
+sophisticated tool, which requires some time to be fully mastered.
+The purpose of this paper is to help the reader to rapidly get the
+10% of the features of ``optparse`` that she will use in the 90% of
+the cases. Taking as an example a real life application - a search and
+replace tool - I will guide the reader through (some of) the wonders
+of ``optparse``. Also, I will show some trick that will make your life
+with ``optparse`` much happier.
+This paper is intended for both Unix and
+Windows programmers - actually I will argue that Windows programmers
+need ``optparse`` even more than Unix programmers; it does not
+require any particular expertise to be fully appreciated.
+
+A simple example
+---------------------------------------
+
+I will take as pedagogical example a little tool I wrote some time ago,
+a multiple files search and replace tool. I needed it because I am
+not always working under Unix, and I do not always have sed/awk or
+even Emacs installed, so it made sense to have this
+little Python script in my toolbox. It is only few lines long,
+it can always be modified and extended with a minimal effort,
+works on every platform (including my PDA) and has the advantage
+of being completely command-line driven:
+it does not require to have any graphics library installed
+and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files and replace a given regular expression
+everywhere in-place; moreover, it saves a backup copy of the original
+un-modified files and give the option to recover
+them when I want to. Of course, all of this can be done more efficiently
+in the Unix world with specialized tools, but those tools are written
+in C and they are not as easily customizable as a Python script, that
+you may change in real time to suit your needs. So, it makes sense
+to write this kind of utility in Python (or in Perl, but I am writing on
+Pyzine now ;)
+
+As a final note, let me notice that I find ``optparse``
+to be much more useful in the Windows world than in the Unix/Linux/Mac OS X
+world. The reason is that the pletora
+of pretty good command-line tools which are available under Unix are
+missing in the Windows environment, or do not have a satisfactory
+equivalent. Therefore,
+it makes sense to write a personal collection of command-line scripts
+for your more common task, if you need to work on many platforms and
+portability is an important requirement.
+Using Python and ``optparse``, you may write your own scripts
+once and having them to run on every platform running Python,
+which means in practice any traditional platform and increasingly
+more of the non-traditional ones - Python is spreading into the
+embedded market too, including PDA's, cellular phones, and more.
+
+The Unix philosophy for command-line arguments
+-------------------------------------------------
+
+In order to understand how ``optparse`` works, it is essential
+to understand the Unix philosophy about command-lines arguments.
+
+As Greg Ward puts it:
+
+*The purpose of optparse is to make it very easy to provide the
+most standard, obvious, straightforward, and user-friendly user
+interface for Unix command-line programs. The optparse philosophy
+is heavily influenced by the Unix and GNU toolkits ...*
+
+
+Here is a brief summary of the terminology:
+the arguments given to a command-line script - *i.e.* the arguments
+that Python stores in the list ``sys.argv[1:]`` - are classified in
+three groups: options, option arguments and positional arguments.
+Options can be distinguished since they are prefixed by a dash
+or a double dash; options can have arguments or not
+(there is at most an option argument right after each option);
+options without arguments are called flags. Positional arguments
+are what it is left in the command-line after you remove options
+and option arguments.
+
+In the example of the search/replace tool,
+I will need two options with an argument - I want
+to pass to the script a regular expression and a replacement string -
+and I will need a flag specifying whether or not a backup of the original
+files needs to be performed. Finally, I will need a number of positional
+arguments to store the names of the files on which the search and
+replace will act.
+
+Consider - for the sake of the example - the following situations:
+you have a bunch of text files in the current directory containing dates
+in the European format DD-MM-YYYY, and that you want to convert them in
+the American format MM-DD-YYYY. If you are sure that all your dates
+are in the correct format, your can match them with a simple regular
+expression such as ``(\d\d)-(\d\d)-(\d\d\d\d)``.
+
+In this particular example it is not so important to make a backup
+copy of the original files, since to revert to the original
+format it is enough to run the script again. So the syntax to use
+would be something like
+
+ ::
+
+ $> replace.py --nobackup --regx="(\d\d)-(\d\d)-(\d\d\d\d)" \
+ --repl="\2-\1-\3" *.txt
+
+
+In order to emphasize the portability, I have used a generic
+``$>`` promtp, meaning that these examples work equally well on
+both Unix and Windows (of course on Unix I could do the same
+job with sed or awk, but these tools are not as flexible as
+a Python script).
+
+The syntax here has the advantage of being
+quite clear, but the disadvantage of being quite verbose, and it is
+handier to use abbreviations for the name of the options. For instance,
+sensible abbreviations can be ``-x`` for ``--regx``, ``-r`` for ``--repl``
+and ``-n`` for ``--nobackup``; moreover, the ``=`` sign can safely be
+removed. Then the previous command reads
+
+ ::
+
+ $> replace.py -n -x"(\dd)-(\dd)-(\d\d\d\d)" -r"\2-\1-\3" *.txt
+
+You see here the Unix convention at work: one-letter options
+(a.k.a. short options) are prefixed with a single dash, whereas
+long options are prefixed with a double dash. The advantage of the
+convention is that short options can be composed: for instance
+
+ ::
+
+ $> replace.py -nx "(\dd)-(\dd)-(\d\d\d\d)" -r "\2-\1-\3" *.txt
+
+means the same as the previous line, i.e. ``-nx`` is parsed as
+``-n -x``. You can also freely exchange the order of the options,
+for instance in this way:
+
+ ::
+
+ $> replace.py -nr "\2-\1-\3" *.txt -x "(\dd)-(\dd)-(\d\d\d\d)"
+
+This command will be parsed exactly as before, i.e. options and option
+arguments are not positional.
+
+How does it work in practice?
+-----------------------------
+
+Having stated the requirements, we may start implementing our
+search and replace tool. The first step, is to write down the
+documentation string:
+
+ ::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ """
+
+On Windows the first line in unnecessary, but is good practice to have it
+in the Unix world.
+
+The next step is to write down a simple search and replace routine:
+
+ ::
+
+ import re
+
+ def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, "U").read() # quick & dirty
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt,
+ print >> file(fname, "w"), rx.sub(repl, txt),
+
+This replace routine is entirely unsurprising, the only thing you
+may notice is the usage of the "U" option in the line
+
+ ::
+
+ txt=file(fname,"U").read()
+
+This is a new feature of Python 2.3. Text files open with the "U"
+option are read in "Universal" mode: this means that Python takes
+care for you of the newline pain, i.e. this script will work
+correctly everywhere, independently by the newline
+conventions of your operating system. The script works by reading
+the whole file in memory: this is bad practice, and here I am assuming
+that you will use this script only on short files that will fit in
+your memory, otherwise you should "massage" the code a bit.
+Also, a full fledged script would check if the file exists
+and can be read, and would do something in the case it is not.
+
+So, how does it work? It is quite simple, really.
+First you need to instantiate an argument line parser from
+the ``OptionParser`` class provided by ``optparse``:
+
+ ::
+
+ import optparse
+ parser = optparse.OptionParser("usage: %prog files [options]")
+
+The string ``"usage: %prog files [options]"`` will be used to
+print a customized usage message, where ``%prog`` will be replaced
+by the name of the script (in this case `replace.py``). You
+may safely omit it and ``optparse`` will use a default
+``"usage: %prog [options]"`` string.
+
+Then, you tell the parser informations about which options
+it must recognize:
+
+ ::
+
+ parser.add_option("-x", "--regx",
+ help="regular expression")
+ parser.add_option("-r", "--repl",
+ help="replacement string")
+ parser.add_option("-n", "--nobackup",
+ action="store_true",
+ help="do not make backup copies")
+
+The ``help`` keyword argument is intended to document the
+intent of the given option; it is also used by ``optparse`` in the
+usage message. The ``action=store_true`` keyword argument is
+used to distinguish flags from options with arguments, it tells
+``optparse`` to set the flag ``nobackup`` to ``True`` if ``-n``
+or ``--nobackup`` is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+
+ ::
+
+ option, files = parser.parse_args()
+
+The ``.parse_args()`` method returns two values: ``option``,
+which is an instance of the ``optparse.Option`` class, and ``files``,
+which is a list of positional arguments.
+The ``option`` object has attributes - called *destionations* in
+``optparse`` terminology - corresponding to the given options.
+In our example, ``option`` will have the attributes ``option.regx``,
+``option.repl`` and ``option.nobackup``.
+
+If no options are passed to the command line, all these attributes
+are initialized to ``None``, otherwise they are initialized to
+the argument option. In particular flag options are initialized to
+``True`` if they are given, to ``None`` otherwise. So, in our example
+``option.nobackup`` is ``True`` if the flag ``-n`` or ``--nobackup``
+is given.
+The list ``files`` contains the files passed
+to the command line (assuming you passed
+the names of accessible text files in your system).
+
+The main logic can be as simple as the following:
+
+ ::
+
+ if not files:
+ print "No files given!"
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ else:
+ print "Missing options or unrecognized options."
+ print __doc__ # documentation on how to use the script
+
+A nice feature of ``optparse`` is that an help option is automatically
+created, so ``replace.py -h`` (or ``replace.py --help``) will work as
+you may expect:
+
+ ::
+
+ $> replace.py --help
+ usage: replace.py files [options]
+
+
+ options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+
+
+You may programmatically print the usage message by invoking
+``parser.print_help()``.
+
+At this point you may test your script and see that it works as
+advertised.
+
+How to reduce verbosity and make your life with ``optparse`` happier
+---------------------------------------------------------------------
+
+The power of ``optparse``comes with a penalty: using ``optparse`` in
+the standard way, as I explained before, involves a certain amount of
+verbosity/redundance.
+
+Suppose for instance
+I want to add the ability to restore the original file from the backup copy.
+Then, we have to change the script in three points: in the docstring,
+in the ``add_option`` list, and in the ``if .. elif .. else ...``
+statement. At least one of this is redundant.
+
+The redundance can be removed by parsing the docstring to infer the
+options to be recognized. This avoids the boring task
+of writing by hand the ``parser.add_option`` lines.
+I implemented this idea in a cookbook recipe, by writing an
+``optionparse`` module which is just a thin wrapper around ``optparse``.
+For sake of space, I cannot repeat it here, but you can find the code
+and a small explanation in the Python Cookbook (see the reference below).
+It is really easy to use. For instance, the paper you are
+reading now has been written by using ``optionparse``: I used it to
+write a simple wrapper to docutils - the standard
+Python tool which converts (restructured) text files to HTML pages.
+It is also nice to notice that internally
+docutils itself uses ``optparse`` to do its job, so actually this
+paper has been composed by using ``optparse`` twice!
+
+Finally, you should keep in mind that this article only scratch the
+surface of ``optparse``, which is quite sophisticated.
+For instance you can specify default values, different destinations,
+a ``store_false`` action and much more, even if often you don't need
+all this power. Still, it is handy to have the power at your disposal when
+you need it. The serious user of ``optparse`` is strongly
+encorauged to read the documentation in the standard library, which
+is pretty good and detailed. I think that this article has fullfilled
+its function of "appetizer" to ``optparse``, if it has stimulate
+the reader to learn more.
+
+References
+--------------------------
+
+- ``optparse/optik`` is a sourceforge project on its own:
+ http://optik.sourceforge.net
+
+- starting from Python 2.3, ``optparse`` is included in the standard library:
+ http://www.python.org/doc/2.3.4/lib/module-optparse.html
+
+- I wrote a Python Cookbook recipe about optparse:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844
diff --git a/pypers/optparse/paper2it.txt b/pypers/optparse/paper2it.txt
new file mode 100755
index 0000000..90ebc55
--- /dev/null
+++ b/pypers/optparse/paper2it.txt
@@ -0,0 +1,390 @@
+Il modulo optparse: scrivere uno strumento a riga di comando in modo semplice
+=======================================================================
+
+ :Status: Draft
+ :Author: Michele Simionato
+ :E-mail: michele.simionato@gmail.com
+ :Date: May 2004
+
+*Il modulo optparse è una potente, flessibile, estendibile, facile da usare
+libreria d'analisi per Python. Usando optparse, è possibile realizzare una gestione
+sofisticata ed intelligente delle opzioni a riga di comando per i propri script
+con veramente poco codice aggiuntivo.* -- Greg Ward, autore di optparse
+
+Introduzione
+-----------------------------------------------------------------------
+
+Una volta tanto tempo fa, quando ancora le interfacce grafiche
+potevano essere solo sognate, gli strumenti a riga di comando erano
+il corpo e l'anima di tutti gli strumenti di programmazione. Sono passati
+molti anni da allora, ma alcune cose non sono cambiate, gli strumenti
+a riga di comando sono ancora veloci, efficienti, portabili, semplici
+da usare e - molto importante - affidabili. Puoi contare su essi.
+Puoi aspettarti che funzionino in ogni situazione, durante la fase
+di installazione, in un situazione di ripristino di sistema, quando
+il window manager non funziona ed anche su sistemi con pesanti vincoli
+di memoria/hardware
+
+
+Dunque, è importante per un linguaggio di programmazione - specialmente
+per i cosiddetti linguaggi di "scripting" - fornire i mezzi per aiutare
+il programmatore nel compito di scrivere strumenti a riga di comando.
+Per molto tempo per questo tipo di compiti Python ha messo a disposizione
+il modulo ``getopt``. Non ho mai particolarmente gradito ``getopt``, visto
+che richiedeva un discreto ammontare di codice anche solo per analizzare
+semplici righe di comando. Comunque, con l'arrivo di Python 2.3 la
+situazione è cambiata: grazie al grosso lavoro di Greg Ward (l'autore di
+``optparse`` conosciuto anche come ``Optik``) ora i programmatori Python
+hanno a loro disposizione (nella libreria standard e non come un modulo
+esterno) una matura API Orientata agli Oggetti per l'analisi delle
+righe di comando, la quale rende la scrittura di strumenti a riga di
+comando nello stile Unix facile, efficiente e veloce.
+
+
+L'unico svantaggio di ``optparse`` è che è uno strumento sofisticato, che
+richiede un pò di tempo per essere completamente padroneggiato.
+L'obiettivo di questo articolo è di aiutare il lettore ad apprendere
+rapidamente il 10% delle caratteristiche di ``optparse`` che userà
+nel 90% dei casi. Prendendo ad esempio un'applicazione di uso comune -
+uno strumento di ricerca e sostituzione - guiderò il lettore attraverso
+le meraviglie di ``optparse``. Inoltre, mostreò alcuni trucchetti che
+renderanno il vostro rapporto con ``optparse`` più felice.
+Questo articolo è pensato sia per i programmatori che lavorano su Unix
+che per quelli che lavorano su Windows - in realtà sosterrò che i
+programmatori che lavorano su Windows hanno bisogno di ``optparse``
+anche più di quelli che lavorano su Unix; per godere fino in fondo di
+questo articolo non avete bisogno di alcuna particolare esperienza.
+
+Un semplice esempio
+---------------------------------------
+
+Utilizzerò come esempio didattico un piccolo programma che ho scritto
+qualche tempo fa, uno strumento di ricerca e sostituzione che lavora
+su più file. Ne avevo bisogno perchè non lavoro sempre su Unix, e non
+sempre ho a disposizione sed/awk oppure Emacs, quindi aveva senso avere
+questo piccolo script Python nella mia cassetta degli attrezzi. E' lungo
+poche righe, può essere modificato ed esteso con uno sforzo minimo,
+funziona su tutte le piattaforme (compreso il mio PDA) ed ha il vantaggio
+di essere completamente pilotabile a riga di comando:
+non richiede alcuna libreria grafica installata e posso usarlo su
+una macchina remota tramite ssh.
+
+
+Lo script prende una serie di file e sostituisce una data espressione
+regolare ovunque, inoltre, salva una copia di backup dei file originali
+(non modificati) e da la possibilità di ripristinarli quando voglio.
+Naturalmente, tutto questo può essere fatto in modo efficiente su Unix
+con degli strumenti specializzati, ma questi strumenti sono scritti in C
+e non sono facilmente personalizzabili come gli script Python, che puoi
+cambiare in tempo reale per adeguarli alle tue necessità. Quindi, ha senso
+scrivere uno strumento di questo tipo in Python (oppure in Perl, ma ora
+su scrivendo su PyJ.it ;-) )
+
+Come nota finale, lasciatemi sottolineare che trovo ``optparse`` molto più
+utile su Windows che non su Unix/Linux/Mac OS X. Il motivo sta nella
+moltitudine di buoni strumenti a riga di comando che sono disponibili su Unix
+ma che invece mancano in ambiente Windows, o che comunque non hanno un equivalente
+soddisfacente. Perciò, è ragionevole scriversi una collezione personale di
+priccoli script a riga di comando per i propri compiti più comuni, soprattutto
+se si ha la necessità di lavorare su diverse piattaforme e la portabilità
+è un requisito importante.
+Usando Python e ``optparse``, potete scrivere i vostri script una volta ed
+utilizzarli su ogni piattaforma dove gira Python, che in pratica significa
+su ogni piattaforma tradizionale e sempre più su quelle meno tradizionali -
+Python si sta anche espandendo nel mercato embedded, inclusi PDA, telefoni
+cellulari e altro.
+
+La filosofia Unix per gli argomenti a riga di comando
+-------------------------------------------------
+
+Al fine di capire come funziona ``optparse``, è essenziale capire
+quale sia la filosofia Unix in riguardo agli argomenti a riga di comando.
+
+Come dice Greg Ward:
+
+*Il compito di optparse è di rendere veramente semplice fornire l'interfaccia
+ più standard, ovvia, lineare, e facile da usare per strumenti a riga di
+ comando su Unix. La filosofia di optparse è pesantemente influenzata dalle
+ raccolte di strumenti di Unix e GNU ...*
+
+Ecco un piccolo sommario della terminologia:
+gli argomenti forniti ad uno script a riga di comando - *cioè* gli
+argomenti che Python memorizza nella lista ``sys.argv[1:]`` - sono
+classificati in tre gruppi: opzioni, argumenti delle opzioni e argomenti
+posizionali.
+Le opzioni possono essere distinte perchè hanno come prefisso un trattino
+oppure un doppio trattino; le opzioni possono avere argomenti o meno
+(c'è al massimo un argomento dopo ogni opzione);
+le opzioni senza argomenti sono chiamate flag. Gli argomenti posizionali
+sono tutto ciò che rimane nella riga di comando dopo aver rimosso le
+opzioni ed i rispettivi argomenti.
+
+Nell'esempio dello strumento di ricerca e sostituzione, avrò bisogno
+di due opzioni con argomento - voglio passare allo script un'espressione
+regolare e una stringa sostituta - avrò bisogno di una flag indicante
+se è necessario effettuare o meno il backup dei file originali. Per concludere,
+avrò bisogno di un certo numero di argomenti posizionali per memorizzare
+i nomi dei files sui quali agirà la ricerca e la sostituizione.
+
+Considerate - giusto per esempio - le seguenti situazioni:
+avete uan serie di file di testo nella directory corrente contenenti delle
+date nel formato Europeo DD-MM-YYYY, e volete convertirle nel formato
+Americano MM-DD-YYYY. Se siete certi che tutte le date sono nel formato
+corretto, potete trovarle con una semplice espressione regolare come
+``(\d\d)-(\d\d)-(\d\d\d\d)``.
+
+In questo particolare esempio non è così importante fare una copia di
+backup dei file originali, visto che per ripristinare il formato originale
+è sufficiente lanciare nuovamente lo script. Quindi la sintassi da
+utilizzare sarebbe qualcosa tipo
+
+ ::
+
+ $> replace.py --nobackup --regx="(\d\d)-(\d\d)-(\d\d\d\d)" \
+ --repl="\2-\1-\3" *.txt
+
+
+Al fine di enfatizzare la portabilità, ho usato un prompt generico
+``$>``, questo significa che questi esempi funzioneranno ugualmente
+bene sia su Unix che su Windows (naturalmente su Unix avrei potuto
+ottenere gli stessi risultati con sed oppure awk, ma questi
+strumenti non sono tanto flessibili quanto uno script Python).
+
+Questa sintassi ha il vantaggio di essere piuttosto chiara, ma lo
+svantaggio di essere abbastanza prolissa, inoltre è più semplice usare
+abbreviazioni per il nome delle opzioni. Ad esempio, abbreviazioni
+significative possono essere ``-x`` per ``--regx``, ``-r`` per ``--repl``
+e ``-n`` per ``--nobackup``; inoltre, il simbolo ``=`` può essere
+tranquillamente rimosso. Quindi la precedente riga di comando diviene
+
+ ::
+
+ $> replace.py -n -x"(\dd)-(\dd)-(\d\d\d\d)" -r"\2-\1-\3" *.txt
+
+Qui vedete le convenzioni Unix al lavoro: le opzioni ad una singola
+lettera (conosciute anche come opzioni brevi) sono precedute da un
+singolo trattino, mentre le opzioni lunghe sono precedute da un
+doppio trattino. Il vantaggio della convenzione consiste nella
+possibilità di combinare le opzioni brevi: ad esempio
+
+ ::
+
+ $> replace.py -nx "(\dd)-(\dd)-(\d\d\d\d)" -r "\2-\1-\3" *.txt
+
+ha lo stesso significato della riga precedente, cioè ``-nx`` è
+lo stesso di ``-n -x``. Potete anche scambiare liberamente l'ordine
+delle options, ad esempio in questo modo:
+
+ ::
+
+ $> replace.py -nr "\2-\1-\3" *.txt -x "(\dd)-(\dd)-(\d\d\d\d)"
+
+Questo comando sarà interpretato esattamente come i precedenti, cioè
+le optioni ed i loro argomenti non sono posizionali.
+
+Come funziona in pratica?
+-----------------------------
+
+Avendo presentato i requisiti, possiamo cominciare ad implementare
+il nostro strumento di "ricerca e sostituzione". Il primo passo è la
+scrittura della documentation string:
+
+ ::
+
+ #!/usr/bin/env python
+ """
+ Given a sequence of text files, replaces everywhere
+ a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ """
+
+Su Windows la prima riga non è necessaria, ma è buona abitudine averla
+nel mondo Unix.
+
+Il prossimo passo è la scrittura di una semplice routine di ricerca e
+sostituzione
+
+ ::
+
+ import re
+
+ def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, "U").read() # quick & dirty
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt,
+ print >> file(fname, "w"), rx.sub(repl, txt),
+
+
+Questa routine di sostituzione è piuttosto scontata, l'unica
+cosa che potete notare è l'utilizzo dell'opzione "U" nella riga
+
+ ::
+
+ txt=file(fname,"U").read()
+
+Questa è una caratteristica introdotta in Python 2.3. I file di testo
+aperti con l'opzione "U" sono letti nel modo "Universale": questo
+significa che Python si prende cura al vostro posto dei problemi
+legati agli accapo, quindi questo script funzionerà correttamente
+ovunque, indipendentemente dalle convenzioni sugli accapo del vostro
+sistema operativo. Lo script legge tutto il file in memoria: questa
+è una cattiva pratica, e qui sto assumendo che userete questo script
+solo su file piccoli adatti alle dimensioni della vostra memoria,
+altrimenti dovreste leggermente "ritoccare" il codice.
+Inoltre, uno script completo dovrebbe testare che il file esista e
+e sia leggibile, e dovrebbe fare qualcosa nel caso contrario.
+
+Quindi, come funziona? E' molto semplice, veramente.
+Prima di tutto avete bisogno di istanziare una parser per la
+riga degli argomenti dalla classe ``OptionParse`` fornita da
+``optparse``.
+
+ ::
+
+ import optparse
+ parser = optparse.OptionParser("usage: %prog files [options]")
+
+La stringa ``"usage: %prog files [options]"`` sarà utilizzata
+per stampare un messaggio di utilizzo personalizzato, dove
+``%prog`` sarà sostituito dal nome dello script (in questo caso
+``replace.py``). Potete ometterlo senza problemi e ``optparse``
+userà la stringa predefinita ``"usage: %prog [options]"``.
+
+A questo punto, indicate al parser le informazioni sulle opzioni che
+deve riconoscere:
+
+ ::
+
+ parser.add_option("-x", "--regx",
+ help="regular expression")
+ parser.add_option("-r", "--repl",
+ help="replacement string")
+ parser.add_option("-n", "--nobackup",
+ action="store_true",
+ help="do not make backup copies")
+
+L'argomento della parola chiave ``help`` serve a documentare gli intenti
+della data opzione; è anche utilizzata da ``optparse`` nel messaggio
+sull'utilizzo.
+L'argomento della parola chiave ``action=store_true`` è usato per distinguere
+le flag dalle opzioni con argomenti, dice ad ``optparse`` di settare la
+flag ``nobackup`` su ``True`` se ``-n`` oppure ``--nobackup`` è passato alla
+riga di comando.
+
+Per concludere, bisogna dire al parser di fare il suo lavoro analizzando
+la riga di comando.
+
+ ::
+
+ option, files = parser.parse_args()
+
+Il metodo ``.parse_args()`` restituisce due valori: ``option``,
+il quale è un'istanza della classe ``optparse.Option``, e ``files``,
+che è una lista di argomenti posizionali.
+L'oggetto ``option`` ha degli attributi - chiamati *destinazioni*
+nella terminologia di ``optparse`` - corrispondenti alle date opzioni.
+Nel nostro esempio, ``option`` avrà gli attributi ``option.regx``,
+``option.repl`` e ``option.nobackup``.
+
+Se non vengono passate opzioni alla riga di comando, tutti questi
+attributi sono inizializati a ``None``, altrimenti sono inizializzati
+col valore dell'argomento dell'opzione. In particolare, le flag sono
+inizializzate con ``True`` se vengono passate, altrimenti a ``None``.
+Quindi, nel nostro esempio ``option.nobackup`` è ``True`` se la flag
+``-n`` o ``--nobackup`` è presente.
+
+La lista ``files`` contiene i file passati alla riga di comando
+(assumendo che abbiate passato i nomi di file di testo accessibili
+nel vostro sistema).
+
+Il codice può essere semplice come il seguente:
+
+ ::
+
+ if not files:
+ print "Non hai indicato alcun file!"
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ else:
+ print "Missing options or unrecognized options."
+ print __doc__ # documentation on how to use the script
+
+Una simpatica caratterisca di ``optparse`` è che viene creata automaticamente
+una opzione di help, quindi ``replace.py -h`` (oppure ``replace.py --help``)
+funzionerà come ci aspetteremmo:
+
+ ::
+
+ $> replace.py --help
+ usage: replace.py files [options]
+
+
+ options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+
+
+Potete stampare da programma il messaggio d'utilizzo invocando
+``parser.print_help()``.
+
+A questo punto potete testare il vostro script e vedere se funziona
+come annunciato.
+
+Come ridurre la verbosità e rendere più felice la vostra vita con ``optparse``
+---------------------------------------------------------------------
+
+La forza di ``optparse`` si accompagna ad una penalizzazione: usare
+``optparse`` nel modo standard, come ho mostrato prima, richiede un certo
+ammontare di verbosità/ridondanza.
+
+Ad esempio supponete che io voglia aggiungere l'abilità di ripristinare il
+file originale dalla copia di backup.
+Bene, dobbiamo cambiare lo script in tre punti: nella docstring, nella
+lista ``add_option``, e nel blocco ``if .. elif .. else ...``. Almeno
+uno di questi è ridondante.
+
+La ridondanza può essere rimossa analizzando la docstring al fine di dedurre le
+opzioni da riconoscere. Questo ci evita il noioso compito di scrivere
+a mano le righe ``parser.add_option``.
+Ho implementato questa idea in una ricetta del Coockbook, scrivendo
+un modulo ``optionparse`` che è giusto un piccolo wrapper di ``optparse``.
+Per motivi di spazio, non posso riportarlo qui, ma potete trovare il codice
+ed una piccola spiegazione nel Python Coockbook (guardate nei riferimenti in
+basso).
+E' veramente semplice da usare. Ad esempio, l'articolo che state leggendo ora
+è stato scritto usando ``optionparse``: L'ho utilizzato per scrivere un
+piccolo wrapper per le docutils - lo strumento standard di Python che converte
+file di testo (ristrutturati) in pagine HTML.
+E' inoltre interessante notare che internamente anche le docutils a loro
+volta usano ``optparse`` per svolgere il proprio compito, quindi in realtà
+questo articolo è stato composto utilizzando ``optparse`` due volte!
+
+Per concludere, dovreste tenere a mente che questo articolo gratta
+solo la superfice di ``optparse``, il quale è piuttosto sofisticato.
+Ad esempio potete specificare dei valori predefiniti, differenti destinazioni,
+un'azione ``store_false`` e molto altro, anche se solitamente non avrete
+bisogno di tutta questa potenza. L'utente serio di ``optparse`` è caldamente
+invitato a leggere la documentazione nella libreria standard, che è
+molto buona e dettagliata. Penso che questo articolo abbia soddisfatto
+la sua funzione di "stuzzichino" di ``optparse``, se ha stimolato il lettore a
+cercare altre informazioni
+
+Riferimenti
+--------------------------
+
+- ``optparse/optik`` è un progetto indipendente su sourceforge:
+ http://optik.sourceforge.net
+
+- a partire da Python 2.3, ``optparse`` è incluso nella libreria standard:
+ http://www.python.org/doc/2.3.4/lib/module-optparse.html
+
+- ho scritto una ricetta per il Python Cookbook su optparse:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844
diff --git a/pypers/optparse/revision.txt b/pypers/optparse/revision.txt
new file mode 100755
index 0000000..f841557
--- /dev/null
+++ b/pypers/optparse/revision.txt
@@ -0,0 +1,100 @@
+Hi Mark!
+
+I have found a number of misprints, due to the fact I have sent my
+article in reStructuredText format. The advantage of reStructuredText is
+that can be converted in HTML automatically. If you prefer, I can send you
+directly the HTML version, I thought the text version was easier to edit
+and you didn't express any preference when I asked about it.
+
+These are the issues with reStructuredText markup:
+
+- ``xxx`` should be displayed in a typewriter font
+
+- *xxx* should be displayed in italic
+
+- :: starts a literal block (rendered as <pre> ...</pre>)
+
+- xxx_ denotes an HTML link
+
+Words to be fixed are ``getopt``, `optparse``, etc., the *i.e.*,
+*destinations*, way::, here_
+
+Moreover, the indentation in the code went mixed up, so please check it
+again.
+
+I also found a misprint in the sentence "The only disadvantage of ``optparse``
+is that it is sophisticated tool", an "a" is missing: "The only disadvantage
+of ``optparse`` is that it is a sophisticated tool".
+
+I saw that you cutted some sentences, probably due to lenght limits. Something
+went lost here:
+
+"It makes sense to write this kind of utility in Python. remote directories."
+
+The original was:
+
+
+"So, it makes sense
+to write this kind of utilities in Python, and actually many people
+(including myself) are actively replacing some Unix commands and bash
+scripts with Python scripts. In real life, I have extended a lot
+the minimal tool that I describe here, and I continue to tune it as
+needed. For instance, you can make it to work recursively and/or on
+remote directories."
+
+I would rewrite the sentence as follows:
+
+"It makes sense to write this kind of utility in Python (or in Perl, but this
+is Pyzine ;)"
+
+
+in such a way that it is short and makes clear that I know about the
+existence of Perl.
+
+
+
+> I had some difficulty understanding your draft at the end. As part of
+> the references you seemed to include some code that was in the Recipe.
+> Does this need to be repeated?
+
+Re-reading it after a couple of months, I do realize that it is pretty
+difficult to follow the last part. I see two solutions:
+
+1. The code of the recipe has to go inside the paper and some additional
+ explanation has to be added;
+
+2. We kill the last paragraph and just put a reference to the Cookbook
+ recipe.
+
+In the first case the paper can become too long; in the second case it
+can become too short (then we should restore the cutted sentences).
+
+I have no preferences, tell me the option you like the most and that you
+think would be the best interest of Py readers. The only point I wanted
+to make is that it is possible to reduce the verbosity of optparse with
+some relatively simple trick (the recipe). BTW, the recipe got
+enthusiastic comments (see
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278844) so I
+think it was appreciated. However it is not standard optionparse and can
+be found in the cookbook, so it can be cutted if there are lenght
+constraints.
+
+All links should be working; if something is missing is because of
+a) reStructuredText markup getting mixed up or b) sometimes the
+Cookbook site goes offline and the link get lost or c) you are
+referring to the local link to the cookbook recipe code, since
+originally I thought it must go at the end of the article as an
+appendix (I thought the reader could not follow the argument
+without seeing it and I didn't want to force him/her to go to
+the cookbook site while reading the paper, also because of b).
+
+So let me know what you prefer to do for this last part and I will
+fix it.
+
+With my best regards,
+
+
+ Michele Simionato
+
+P.S. I will send you my bio and picture in a forthcoming email.
+
diff --git a/pypers/optparse/their-cuts.txt b/pypers/optparse/their-cuts.txt
new file mode 100755
index 0000000..8eab196
--- /dev/null
+++ b/pypers/optparse/their-cuts.txt
@@ -0,0 +1,179 @@
+
+optparse
+command-line parsing library for Python
+- - - - - - - - - - - -
+By Michele Simioniato | July 10, 2004
+
+
+"The optparse module is a powerful, flexible, extensible, easy-to-use command-line parsing library for Python. Using optparse, you can add intelligent, sophisticated handling of command-line options to your scripts with very little overhead."
+-- Greg Ward, optparse author
+Introduction
+
+Once upon a time, when graphic interfaces were still to be dreamed about, command-line tools were the body and the soul of all programming tools. Many years have passed since then, but some things have not changed: command-line tools are still fast, efficient, portable, easy to use and - more importantly - reliable. You can count on them. When you really need them, command-line tools are always there.
+
+It's important for a programming language - especially one that wants to be called a "scripting" language - to provide facilities to help the programmer in the task of writing command-line tools. For a long time Python support for this kind of tasks has been provided by the``getopt`` module. I have never been particularly fond of ``getopt``, since it required a sensible amount of coding even for the parsing of simple command-lines. However, with the coming of Python 2.3 the situation has changed: thanks to the great job of Greg Ward (the author of ``optparse`` a.k.a. ``Optik``) now the Python programmer has at her disposal (in the standard library and not as an add-on module) a fully fledged object-oriented API for command-line arguments parsing, which makes writing Unix-style command-line tools easy, efficient and fast.
+
+The only disadvantage of ``optparse`` is that it is sophisticated tool, which requires some time to be fully mastered. The purpose of this paper is to help the reader to rapidly get the 10% of the features of ``optparse`` that she will use in the 90% of the cases. Taking as an example a real life application - a search and replace tool - I will guide the reader through (some of) the wonders of ``optparse``. Also I will show some trick that will make your life with ``optparse`` much happier. This paper is intended for both Unix and Windows programmers, and it does not require any particular expertise to be fully appreciated.
+A simple example
+
+I will take as an example a little tool I wrote some time ago, a multiple files search and replace tool. I needed it because I am not always working under Unix, and I do not always have sed/awk or even Emacs installed, so it made sense to have this little Python script in my toolbox. It is only few lines long, it can always be modified and extended with a minimal effort, works on every platform (including my PDA) and has the advantage of being completely command-line driven. It does not require to have any graphics library installed and I can use it when I work on a remote machine via ssh.
+
+The tool takes a bunch of files and replace a given regular expression everywhere in-place; moreover, it saves a backup copy of the original unmodified files and give the option to recover them when I want to. Of course, all of this can be done more efficiently in the Unix world with specialized tools, but those tools are written in C and they are not as easily customizable as a Python script. It makes sense to write this kind of utility in Python. remote directories.
+
+Finally, let me note that I find ``optparse`` to be much more useful in the Windows world than in the Unix/Linux/OS X world. The reason is that the plethora of pretty good command-line tools which are available under Unix are missing in the Windows environment or do not have a satisfactory equivalent.
+The Unix philosophy for command-line arguments
+
+In order to understand how ``optparse`` works, it is essential to understand the Unix philosophy about command-lines arguments. As Greg Ward puts it:
+
+"The purpose of optparse is to make it very easy to provide the most standard, obvious, straightforward, and user-friendly user interface for Unix command-line programs. The optparse philosophy is heavily influenced by the Unix and GNU toolkits ..."
+
+Here is optparse/Unix/GNU terminology: the arguments given to a command-line script - *i.e.* the arguments that Python stores in the list ``sys.argv[1:]`` - are classified in three groups: options, option arguments, and positional arguments. Options can be distinguished since they are prefixed by a dash or a double dash; options can have arguments or not (there is at most an option argument after each option); options without arguments are called flags. Positional arguments are what is left in the command-line after you remove options and option arguments.
+
+In the example of the search/replace tool, I will need two options with an argument - since I want to pass to the script a regular expression and a replacement string - and I will need a flag specifying whether or not a backup of the original files needs to be performed. Finally, I will need a number of positional arguments to store the names of the files on which the search and replace will act.
+
+Consider that following situations: you have a bunch of text files in the current directory containing dates in the European format DD-MM-YYYY, and that you want to convert them in the American format MM-DD-YYYY. If you are sure that all your dates are in the correct format, your can match them with a simple regular expression such as ``(\d\d)-(\d\d)-(\d\d\d\d)``.
+
+In this example it is not so important to make a backup copy of the original files, since to revert to the original format it is enough to run the script again. So the syntax to use would be something like:
+$> replace.py --nobackup --regx="(\d\d)-(\d\d)-(\d\d\d\d)" \ --repl="\2-\1-\3" *.txt
+
+The syntax here has the advantage of being quite clear, but the disadvantage of being quite verbose, and it is handier to use abbreviations for the name of the options. For instance, sensible abbreviations can be ``-x`` for ``--regx``, ``-r`` for ``--repl`` and ``-n`` for ``--nobackup``; moreover, the ``=`` sign can safely be removed. Then the previous command reads
+$> replace.py -n -x"(\dd)-(\dd)-(\d\d\d\d)" -r"\2-\1-\3" *.txt
+
+You see here the Unix convention at work: one-letter options (a.k.a. short options) are prefixed with a single dash, whereas long options are prefixed with a double dash. The advantage of the convention is that short options can be composed: for instance
+$> replace.py -nx "(\dd)-(\dd)-(\d\d\d\d)" -r "\2-\1-\3" *.txt
+
+means the same as the previous line, i.e. ``-nx`` is parsed as ``-n -x``. You can also freely exchange the order of the options, for instance in this way:
+$> replace.py -nr "\2-\1-\3" *.txt -x "(\dd)-(\dd)-(\d\d\d\d)"
+
+This command will be parsed exactly as before, i.e. options and option arguments are not positional.
+How does it work in practice?
+
+Having stated the requirements, we may start implementing our search and replace tool. The first step, and the most important one, is to write down the documentation string:
+#!/usr/bin/env python
+"""
+Given a sequence of text files, replaces everywhere
+a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+
+The next step is to write down a simple search and replace routine:
+import re
+
+def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ txt = file(fname, "U").read()
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt,
+ print >> file(fname, "w"), rx.sub(repl, txt),
+
+This replace routine is entirely unsurprising, the only thing you may notice is the usage of the "U" option in the line
+txt=file(fname,"U").read()
+
+This is a new feature of Python 2.3. Text files open with the "U" option are read in "Universal" mode: this means that Python takes care of the newline pain, i.e. this script will work correctly everywhere, irregardless of the newline conventions of your operating system. The script works by reading the whole file in memory: this is bad practice, and here I am assuming that you will use this script only on short files that will fit in memory, otherwise you should "massage" the code a bit. Also, a full fledged script would check if the file exists and can be read, and would do something in the case it is not.
+
+How does it work? It is quite simple, really. First you need to instantiate an argument line parser from the ``OptionParser`` class provided by ``optparse``:
+import optparse
+parser = optparse.OptionParser("usage: %prog files [options]")
+
+The string ``"usage: %prog files [options]"`` will be used to print a customized usage message, where ``%prog`` will be replaced by the name of the script (in this case `replace.py``). You may safely omit it and ``optparse`` will use a default ``"usage: %prog [options]"`` string.
+
+Then, you tell the parser informations about which options it must recognize:
+parser.add_option("-x", "--regx", help="regular expression")
+parser.add_option("-r", "--repl", help="replacement string")
+parser.add_option("-n", "--nobackup", action="store_true", help="do not make backup copies")
+
+The ``help`` keyword argument is intended to document the intent of the given option; it is also used by ``optparse`` in the usage message. The ``action=store_true`` keyword argument is used to distinguish flags from options with arguments, it tells ``optparse`` to set the flag ``nobackup`` to ``True`` if ``-n`` or ``--nobackup`` is given in the command line.
+
+Finally, you tell the parse to do its job and to parse the command line:
+option, files = parser.parse_args()
+
+The ``.parse_args()`` method returns two values: ``option``, which is an instance of the ``optparse.Option`` class, and ``files``, which is a list of positional arguments. The ``option`` object has attributes - called *destinations* in ``optparse`` terminology - corresponding to the given options. In our example, ``option`` will have the attributes ``option.regx``, ``option.repl`` and ``option.nobackup``.
+
+If no options are passed to the command line, all these attributes are initialized to ``None``, otherwise they are initialized to the argument option. In particular flag options are initialized to ``True`` if they are given, to ``None`` otherwise. So, in our example ``option.nobackup`` is ``True`` if the flag ``-n`` or ``--nobackup`` is given. The list ``files`` contains the files passed to the command line (assuming you passed the names of accessible text files in your system).
+
+The main logic can be as simple as the following:
+ if not files:
+ print "No files given!"
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ else:
+ print "Missing options or unrecognized options."
+ print __doc__ # documentation on how to use the script
+
+A nice feature of ``optparse`` is that an help option is automatically created, so ``replace.py -h`` (or ``replace.py --help``) will work as you may expect:
+$> replace.py --help
+usage: replace.py files [options]
+
+
+options:
+ -h, --help show this help message and exit
+ -xREGX, --regx=REGX regular expression
+ -rREPL, --repl=REPL replacement string
+ -n, --nobackup do not make backup copies
+
+You may programmatically print the usage message by invoking ``parser.print_help()``.
+
+At this point you may test your script and see that it works as advertised.
+How to reduce verbosity and make your life with ``optparse`` happier
+
+The approach we followed in the previous example has a disadvantage: it involves a certain amount of redundance. Suppose, for example, that we want to add the ability to restore the original file from the backup copy. Then, we have to change the script in three points: in the docstring, in the ``add_option`` list, and in the ``if .. elif .. else ...`` statement. At least one of these is redundant.
+
+You might be tempted to think that the information in the documentation string is redundant, since it is already magically provided in the help options. On the contrary, the information in the help options is redundant, since it is already contained in the docstring. It is a sacrilege to write a Python script without a docstring, since the docstring should always be available to automatic documentation tools such as pydoc.
+
+Since the docstring cannot be removed or shortened, the idea is to extract information from it, in such a way to avoid the boring task of writing by hand the ``parser.add_option`` lines. I implemented this idea in a cookbook recipe, by writing an ``optionparse`` module which is just a thin wrapper around ``optparse``. The relevant code is here_. The relevant functions are ``optionparse.parse`` which parses the docstring and ``optionparse.exit`` which exits the execution by displaying an usage message. To show how to use them, let me rewrite the search and replace tool (including the new restore option) in this way::
+#!/usr/bin/env python
+
+
+Given a sequence of text files, replaces everywhere a regular expression x with a replacement string s.
+
+ usage: %prog files [options]
+ -x, --regx=REGX: regular expression
+ -r, --repl=REPL: replacement string
+ -n, --nobackup: do not make backup copies
+ -R, --restore: restore the original from the backup
+
+import optionparse, os, shutil, re
+
+def replace(regx, repl, files, backup_option=True):
+ rx = re.compile(regx)
+ for fname in files:
+ # TODO: add a test to see if the file exists and can be read
+ txt = file(fname, "U").read()
+ if backup_option:
+ print >> file(fname+".bak", "w"), txt
+ print >> file(fname, "w"), rx.sub(repl,txt)
+
+
+def restore(files): # restor original files from backup files
+ for fname in files:
+ if os.path.exists(fname+".bak"):
+ shutil.copyfile(fname+".bak",fname)
+ else:
+ print "Sorry, there is no backup copy for %s" % fname
+
+if __name__=='__main__':
+ option, files = optionparse.parse(__doc__)
+ # optionparse.parse parses both the docstring and the command line!
+ if not files:
+ optionparse.exit()
+ elif option.regx and option.repl:
+ replace(option.regex, option.repl, files, not option.nobackup)
+ elif option.restore:
+ restore(files)
+ else:
+ print "Missing options or unrecognized options."
+
+The code is quite readable. Internally ``optionparse.parse(__doc__)`` works by generating an option parser from the docstring, then applying it to the command-line arguments. You could devise various tricks to avoid the redundance in the ``if`` statement (for instance using a dictionary of functions and dispatching according to the name of the given option). However this simple recipe is good enough to provide a minimal wrapper to ``optparse``. It requires a minimum effort and works well for the most common case. For instance, the paper you are reading now has been written by using ``optionparse``: I used it to write a simple wrapper to docutils - the standard Python tool which converts (restructured) text files to HTML pages. It is also nice to notice that internally docutils itself uses ``optparse`` to do its job, so actually this paper has been composed by using ``optparse`` twice.
+
+Finally, you should keep in mind that this article only scratch the surface of ``optparse``, which is quite sophisticated. For instance you can specify default values, different destinations, a ``store_false`` action and more. The serious user of ``optparse`` is strongly encorauged to read the documentation in the standard library, which is pretty good and detailed. I think that this article has fullfilled its function of "appetizer" to ``optparse``, if it has stimulate the reader to study more.
+References:
+
+optparse is documented in the standard library.
+
+the ``optionparse`` module can be found here
+
+I wrote a Python Cookbook recipe about optionparse.
diff --git a/pypers/optparse/x.txt b/pypers/optparse/x.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/optparse/x.txt
diff --git a/pypers/output.txt b/pypers/output.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/output.txt
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"))
diff --git a/pypers/pep318/Makefile b/pypers/pep318/Makefile
new file mode 100755
index 0000000..9249fb8
--- /dev/null
+++ b/pypers/pep318/Makefile
@@ -0,0 +1,23 @@
+decorators.rst: decorators.py $S/minidoc.py
+ $S/minidoc.py -r decorators -r
+decorators.html: decorators.txt decorators.png decorators.rst
+ $S/rst.py decorators.html
+decorators.dvi: decorators.txt decorators.rst
+ $S/rst.py -t decorators.tex
+ perl -pi -e 's/\(non-strict/\n(non-strict/' decorators.tex
+ perl -pi -e 's/as first/\nas first/' decorators.tex
+ $S/rst.py -d decorators.dvi
+decorators.pdf: decorators.dvi decorators.ps
+ dvipdf decorators
+decorators.ps: makegraph.dot
+ dot makegraph.dot -Tps -o decorators.ps -Gsize="5.5,6"
+decorators.png: makegraph.dot
+ dot makegraph.dot -Tpng -o decorators.png -Gsize="5.5,6"
+decorators.zip: decorators.html decorators.pdf \
+ decorators.py README.txt $S/safetype.py $S/doct.py
+ zip -j decorators decorators.py decorators.png decorators.ps \
+ decorators.html decorators.txt decorators.pdf decorators.rst\
+ README.txt makegraph.dot $S/safetype.py $S/doct.py
+dist: decorators.zip
+working: decorators.zip
+ unzip -o decorators.zip -d working
diff --git a/pypers/pep318/README.txt b/pypers/pep318/README.txt
new file mode 100755
index 0000000..341bf64
--- /dev/null
+++ b/pypers/pep318/README.txt
@@ -0,0 +1,48 @@
+DECORATORS README
+========================================================================
+
+The ``decorators`` distribution contains the following files:
+
+1. README.txt (your are reading it)
+
+2. decorators.txt (the documentation in ReStructuredText format)
+
+3. decorators.html (the documentation in HTML format)
+
+4. decorators.pdf (the documentation in pdf format)
+
+5. decorators.py (the heart of the distribution)
+
+6. noconflict.py (imported by decorators, resolves metaclass conflicts)
+
+7. doct.py (utility to extract tests from the documentation)
+
+8. decorators.ps (a figure included in decorators.pdf)
+
+9. decorators.png (a figure included in decorators.html)
+
+10. makegraph.dot (DOT script generating the figure)
+
+11. decorators.rst (the quick reference to decorators.py)
+
+12. safetype.rst (the quick reference to safetype.py)
+
+``noconflict`` and ``doct`` can be used as standalone
+modules too. They are documented in the on-line Python cookbook.
+
+After running ``python doct.py decorators.txt`` a number of files will be
+generated, including a module ``customdec.py`` containing the examples
+of custom decorators discussed in the documentation.
+
+If the tests fail, then there is something wrong with your Python
+installation, and I cannot help you since I don't have your machine
+at my disposal :-( It works for me both under Red Hat 7.3 and
+Windows 98SE. Notice that Python 2.3 is required.
+
+If you use the decorators module in your code (of course, you should
+not use it in production software!) and you find some bug of unexpected
+behaviour, please send a bug report to me:
+
+ MicheleSimionato@libero.it
+
+That's all, folks. Enjoy!
diff --git a/pypers/pep318/__main__.html b/pypers/pep318/__main__.html
new file mode 100755
index 0000000..06494a9
--- /dev/null
+++ b/pypers/pep318/__main__.html
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Documentation of the __main__ module</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="documentation-of-the-main-module">
+<h1 class="title">Documentation of the __main__ module</h1>
+<p>Short utility to extract documentation from a module</p>
+<div class="section" id="documented-metaclasses">
+<h1><a name="documented-metaclasses">Documented metaclasses</a></h1>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>__main__.rst</tt>, line 6)</p>
+Section empty; must have contents.</div>
+</div>
+<div class="section" id="documented-classes">
+<h1><a name="documented-classes">Documented classes</a></h1>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>__main__.rst</tt>, line 10)</p>
+Section empty; must have contents.</div>
+</div>
+<div class="section" id="documented-functions">
+<h1><a name="documented-functions">Documented functions</a></h1>
+<p><tt class="literal"><span class="pre">publish_cmdline(reader=None,</span> <span class="pre">reader_name='standalone'</span></tt></p>
+<p>Set up &amp; run a <cite>Publisher</cite>. For command-line front ends.</p>
+<p>Parameters:</p>
+<ul class="simple">
+<li><cite>reader</cite>: A <cite>docutils.readers.Reader</cite> object.</li>
+<li><cite>reader_name</cite>: Name or alias of the Reader class to be instantiated if
+no <cite>reader</cite> supplied.</li>
+<li><cite>parser</cite>: A <cite>docutils.parsers.Parser</cite> object.</li>
+<li><cite>parser_name</cite>: Name or alias of the Parser class to be instantiated if
+no <cite>parser</cite> supplied.</li>
+<li><cite>writer</cite>: A <cite>docutils.writers.Writer</cite> object.</li>
+<li><cite>writer_name</cite>: Name or alias of the Writer class to be instantiated if
+no <cite>writer</cite> supplied.</li>
+<li><cite>settings</cite>: Runtime settings object.</li>
+<li><cite>settings_spec</cite>: Extra settings specification; a <cite>docutils.SettingsSpec</cite>
+subclass. Used only if no <cite>settings</cite> specified.</li>
+<li><cite>settings_overrides</cite>: A dictionary containing program-specific overrides
+of component settings.</li>
+<li><cite>argv</cite>: Command-line argument list to use instead of <tt class="literal"><span class="pre">sys.argv[1:]</span></tt>.</li>
+<li><cite>usage</cite>: Usage string, output if there's a problem parsing the command
+line.</li>
+<li><cite>description</cite>: Program description, output for the &quot;--help&quot; option
+(along with command-line option descriptions).</li>
+</ul>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="__main__.rst">View document source</a>.
+Generated on: 2003-09-20 09:39 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/addtests.txt b/pypers/pep318/addtests.txt
new file mode 100755
index 0000000..67d96d2
--- /dev/null
+++ b/pypers/pep318/addtests.txt
@@ -0,0 +1,232 @@
+Additional tests
+=======================================================================
+
+You cannot add magic methods magically *after*:
+
+>>> from customdec import *
+>>> enhance_classes()
+
+
+>>> class C:
+... "[Decorated]"
+...
+>>> identity=lambda x:x
+>>> C.identity=identity
+>>> C.identity(1)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method <lambda>() must be called with C instance as first argument (got int instance instead)
+
+(it could be done by modifying ``__setattr__`` in ``Decorated``).
+
+
+Usage of decorator
+------------------------------------------------------------------------
+
+>>> decorator(1)
+
+
+Printing representation
+------------------------------------------------------------------------
+
+A delicate point: inverting the docstring with __metaclass__
+
+>>> class M(type):
+... def __str__(cls):
+... return cls.__name__
+>>> class C(object):
+... __metaclass__=M
+... "[Decorated]" # not considered docstring!
+... def __str__(self):
+... return '<C>'
+>>> print C.__doc__
+None
+>>> C=decorator(C)
+>>> print type(C),C,C()
+<class 'safetype.MClassDecorator'> C <C>
+
+
+>>> class C(object):
+... "[Decorated]"
+... __metaclass__=M
+... def __str__(self):
+... return '<C>'
+>>> C=decorator(C)
+>>> print type(C),C,C()
+<class 'safetype.MClassDecoratorDecorated'> C <C>
+>>> #from MROgraph import MROgraph; g=MROgraph(type(C))
+
+Tricky ways of passing parameters to decorators
+------------------------------------------------------------------------
+
+This can be avoided by converting ``logfile`` from a class variable
+to an instance variable in the ``__init__`` method:
+
+ ::
+
+ #<chatty1.py>
+
+ import sys,customdec,decorators
+
+ class chattymethod1(customdec.chattymethod):
+ def __init__(self,func):
+ super(chattymethod1,self).__init__(func)
+ self.logfile=self.logfile # class variable -> instance variable
+
+ class D:
+ chattymethod1.logfile=sys.stdout
+ def f(self): pass
+ f=chattymethod1(f)
+
+ chattymethod1.logfile=file('file.log','w')
+ def g(self): pass
+ g=chattymethod1(g)
+
+ d=D()
+
+ #</chatty1.py>
+
+Here is the testing:
+
+>>> from chatty1 import D,d
+>>> D.f(d)
+calling <chattymethod1:f> from chatty1.D
+
+>>> D.g(d) # message written in file.log
+>>> D.f(d) # message correctly written in stdout, not in file.log
+calling <chattymethod1:f> from chatty1.D
+
+This works as it is but it is really ugly and fragile: for instance
+the magic docstring syntax
+
+ ::
+
+ class D:
+ "[Decorated]"
+ chattymethod1.logfile=sys.stdout
+ def f(self): "[chattymethod1]"
+
+ chattymethod1.logfile=file('file.log','w')
+ def g(self):"[chattymethod1]"
+
+
+will not work since the logfile attribute will be modified *before*
+the conversion in decorators, so both ``f`` and ``g`` will output to
+'file.log'.
+
+Tests on method wrappers
+-------------------------------------------------------
+
+>>> class C: pass
+...
+>>> c=C()
+>>> def f(x): return x
+...
+>>> sm=staticmethod(f)
+>>> C.sm=sm
+>>> C.sm.im_func # correct
+Traceback (most recent call last):
+ ...
+AttributeError: 'function' object has no attribute 'im_func'
+
+(idem for ``c.sm.im_func``). On the other hand
+
+>>> cm=classmethod(f)
+>>> C.cm=cm
+>>> assert C.cm.im_func is f
+>>> assert c.cm.im_func is f
+>>> assert C.cm.im_class is C
+>>> assert c.cm.im_class is C
+>>> assert C.cm.im_self is C
+>>> assert c.cm.im_self is C
+
+Test on tracedmethod from the instance
+-------------------------------------------------
+
+>>> from example6 import C
+>>> print C.__dict__['fact']
+<classmethodtracedmethod:fact>
+>>> C().fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+
+Tests on composition of decorators
+-------------------------------------------------:
+
+To decorate something which is already decorated:
+
+>>> def g(x): "[tracedmethod]"
+...
+>>> tm=decorator(g) # creates a tracedmethod from f
+>>> print decorator(tm) # trying to decorate a tracedmethod
+<tracedmethod:g>
+
+Staticmethod vs classmethod.
+
+Staticmethod is non-cooperative, therefore staticmethodclassmethod
+acts as a pure staticmethod:
+
+>>> class C:
+... pass
+>>> c=C()
+>>> smcm=staticmethod(classmethod(f))
+>>> print smcm
+<staticmethodclassmethod:f>
+>>> C.smcm=smcm
+>>> C.smcm(1)
+1
+>>> c.smcm(1)
+1
+
+
+Classmethod vs staticmethod
+
+Idem: staticmethod gains:
+
+>>> cmsm=classmethod(staticmethod(f))
+>>> print cmsm
+<classmethodstaticmethod:f>
+>>> C.cmsm=cmsm
+>>> C.cmsm(1)
+1
+>>> c.cmsm(1)
+1
+
+
+Staticmethod vs tracedmethod works:
+
+>>> class C(object):
+... "[Decorated]"
+... def fact(n):
+... "[staticmethod, tracedmethod]"
+... if n==0: return 1
+... else: return n*C.fact(n-1)
+>>> C=decorator(C)
+
+Called from the class:
+
+>>> C.fact(2)
+Calling 'C.fact' with arguments 2(){} ...
+ Calling 'C.fact' with arguments 1(){} ...
+ Calling 'C.fact' with arguments (){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+Called from the instance:
+
+>>> C().fact(2)
+Calling 'C.fact' with arguments 2(){} ...
+ Calling 'C.fact' with arguments 1(){} ...
+ Calling 'C.fact' with arguments (){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
diff --git a/pypers/pep318/bug.py b/pypers/pep318/bug.py
new file mode 100755
index 0000000..f94a80f
--- /dev/null
+++ b/pypers/pep318/bug.py
@@ -0,0 +1,50 @@
+# bug.py
+
+"""
+
+Test a bug of doctest in redirecting stdout.
+
+The module prnt is as follows:
+
+# prnt.py
+import sys
+f=sys.stdout
+
+def hello():
+ print >> f, 'hello'
+
+Notice that
+
+ print >> sys.stdout, 'hello'
+
+and
+
+ print 'hello'
+
+would work instead!
+
+"""
+
+import doctest,__main__
+
+def f1():
+ """
+ First docstring saying
+
+ >>> import prnt
+ >>> prnt.hello()
+ hello
+
+ """
+
+def f2():
+ """
+ Second docstring saying
+
+ >>> import prnt
+ >>> prnt.hello()
+ hello
+
+ """
+
+doctest.testmod(__main__)
diff --git a/pypers/pep318/bug.txt b/pypers/pep318/bug.txt
new file mode 100755
index 0000000..8713044
--- /dev/null
+++ b/pypers/pep318/bug.txt
@@ -0,0 +1,22 @@
+Attention:
+
+get -> _get not possible! Why ?!
+
+
+Moreover:
+
+
+>>> from tracing import E
+>>> E.__dict__['__init__'].output=file('err','w')
+
+
+>>> e=E()
+ Calling 'B.__init__' with arguments <E instance>(){} ...
+ Calling 'D.__init__' with arguments <E instance>(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+
+
+>>> import chatty2
+>>> print file('file1.log').read().rstrip()
+calling <chattymethod2:f> from <C instance>
diff --git a/pypers/pep318/chatty.py b/pypers/pep318/chatty.py
new file mode 100755
index 0000000..432c161
--- /dev/null
+++ b/pypers/pep318/chatty.py
@@ -0,0 +1,28 @@
+# chatty.py
+
+from customdec import decorated,chattymethod
+chattymethod.logfile=file('file1.log','w')
+
+class C(object):
+ "[Decorated]"
+ def f():
+ "[chattymethod, staticmethod]"
+
+C=decorated(C)
+c=C()
+
+c.f()
+C.f()
+
+
+
+chattymethod.logfile=file('file2.log','w')
+
+def g():
+ "[chattymethod,staticmethod]"
+
+C.g=decorated(g)
+C.g # message written in file2.log
+C.f # message written in file2.log
+
+
diff --git a/pypers/pep318/chatty1.py b/pypers/pep318/chatty1.py
new file mode 100755
index 0000000..649f6f2
--- /dev/null
+++ b/pypers/pep318/chatty1.py
@@ -0,0 +1,21 @@
+# chatty1.py
+
+import sys,customdec,decorators
+
+class chattymethod1(customdec.chattymethod):
+ def __init__(self,func):
+ super(chattymethod1,self).__init__(func)
+ self.logfile=self.logfile # class variable -> instance variable
+
+class D:
+ chattymethod1.logfile=sys.stdout
+ def f(self): pass
+ f=chattymethod1(f)
+
+ chattymethod1.logfile=file('file.log','w')
+ def g(self): pass
+ g=chattymethod1(g)
+
+d=D()
+
+
diff --git a/pypers/pep318/chatty2.py b/pypers/pep318/chatty2.py
new file mode 100755
index 0000000..38be546
--- /dev/null
+++ b/pypers/pep318/chatty2.py
@@ -0,0 +1,28 @@
+# chatty2.py
+
+import customdec; customdec.enhance_classes()
+
+# sets the log files
+log1=file('file1.log','w')
+log2=file('file2.log','w')
+
+class C:
+ "[Decorated]"
+ def f(self):
+ "[chattymethod2]"
+ f.logfile=log1 # function attribute
+ def g(self):
+ "[chattymethod2]"
+ g.logfile=log2 # function attribute
+
+assert C.__dict__['f'].logfile is log1 # check the conversion
+assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr.
+
+c=C() # C instantiation
+
+c.f() # print a message in file1.log
+c.g() # print a message in file2.log
+
+log1.close(); log2.close() # finally
+
+
diff --git a/pypers/pep318/chatty3.py b/pypers/pep318/chatty3.py
new file mode 100755
index 0000000..d1895d9
--- /dev/null
+++ b/pypers/pep318/chatty3.py
@@ -0,0 +1,13 @@
+# chatty3.py
+
+"[Decorated]"
+
+import decorators,customdec; decorators.decorated()
+
+class C:
+ def f():
+ "[chattymethod2, staticmethod]"
+
+c=C()
+
+
diff --git a/pypers/pep318/customdec.py b/pypers/pep318/customdec.py
new file mode 100755
index 0000000..52a0d0d
--- /dev/null
+++ b/pypers/pep318/customdec.py
@@ -0,0 +1,68 @@
+# customdec.py
+
+from decorators import *
+
+class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+
+
+class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+
+
+class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -> dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+
+
+class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print >> cls.output,"%s created" % cls
+
+
+
+from types import FunctionType
+
+class Traced(Decorated):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType) and name!='__str__':
+ # cannot trace __str__, since it is invoked by
+ # tracedmethod and would generate infinite recursion
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d) # decorates the methods
+
+
diff --git a/pypers/pep318/decorators.html b/pypers/pep318/decorators.html
new file mode 100755
index 0000000..59d584f
--- /dev/null
+++ b/pypers/pep318/decorators.html
@@ -0,0 +1,1534 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Implementing PEP 318 (decorators)</title>
+<meta name="author" content="Michele Simionato" />
+<meta name="date" content="September 2003" />
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="implementing-pep-318-decorators">
+<h1 class="title">Implementing PEP 318 (decorators)</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Module:</th><td class="field-body">decorators</td>
+</tr>
+<tr><th class="docinfo-name">Version:</th>
+<td>0.5</td></tr>
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr class="field"><th class="docinfo-name">e-mail:</th><td class="field-body"><a class="reference" href="mailto:MicheleSimionato&#64;libero.it">MicheleSimionato&#64;libero.it</a></td>
+</tr>
+<tr class="field"><th class="docinfo-name">Licence:</th><td class="field-body">Python-like</td>
+</tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>September 2003</td></tr>
+<tr class="field"><th class="docinfo-name">Disclaimer:</th><td class="field-body">This is experimental code. Use it at your own risk!</td>
+</tr>
+</tbody>
+</table>
+<div class="contents topic" id="contents">
+<p class="topic-title"><a name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#using-decorators" id="id4" name="id4">Using decorators</a><ul>
+<li><a class="reference" href="#basics" id="id5" name="id5">Basics</a></li>
+<li><a class="reference" href="#decorating-methods" id="id6" name="id6">Decorating methods</a></li>
+<li><a class="reference" href="#decorating-classes" id="id7" name="id7">Decorating classes</a></li>
+<li><a class="reference" href="#adding-magic" id="id8" name="id8">Adding magic</a></li>
+<li><a class="reference" href="#defining-method-decorators" id="id9" name="id9">Defining method decorators</a></li>
+<li><a class="reference" href="#defining-class-decorators" id="id10" name="id10">Defining class decorators</a></li>
+<li><a class="reference" href="#composing-decorators" id="id11" name="id11">Composing decorators</a></li>
+<li><a class="reference" href="#diving-into-magic" id="id12" name="id12">Diving into magic</a></li>
+<li><a class="reference" href="#advanced-usage" id="id13" name="id13">Advanced usage</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#the-implementation" id="id14" name="id14">The implementation</a><ul>
+<li><a class="reference" href="#module-decorators" id="id15" name="id15">Module <tt class="literal"><span class="pre">decorators</span></tt></a><ul>
+<li><a class="reference" href="#id3" id="id16" name="id16">Metaclasses</a></li>
+<li><a class="reference" href="#classes" id="id17" name="id17">Classes</a></li>
+<li><a class="reference" href="#functions" id="id18" name="id18">Functions</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="using-decorators">
+<h1><a class="toc-backref" href="#id4" name="using-decorators">Using decorators</a></h1>
+<p>Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of <a class="reference" href="http://www.python.org/pep">PEP 318</a> in pure Python
+(2.3).</p>
+<p>Here is the rationale:</p>
+<ul class="simple">
+<li>some kind of decorator syntax is scheduled to go in Python 2.4,
+therefore it is interesting to play with the concept;</li>
+<li>it is nice to play with decorators now, without having to
+wait for one year or so;</li>
+<li>it is much easier to experiment with a pure Python implementation
+than with a C implementation;</li>
+<li>the implementation can be seen as an exercise on modern Python
+programming and may be valuable to people wanting to study the most
+advanced new constructs in Python (<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">descriptors</a>, <a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta2.html">metaclasses</a>,
+<a class="reference" href="http://www.python.org/2.3/descrintro.html">cooperative methods</a>, etc.)</li>
+</ul>
+<div class="section" id="basics">
+<h2><a class="toc-backref" href="#id5" name="basics">Basics</a></h2>
+<p>PEP 318 has the goal of providing a nice syntactic sugar for expressions like</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ return x
+identity=staticmethod(identity)
+</pre>
+</blockquote>
+<p>or</p>
+<blockquote>
+<pre class="literal-block">
+def name(cls):
+ return cls.__name__
+name=classmethod(name)
+</pre>
+</blockquote>
+<p>which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x)[staticmethod]:
+ return x
+
+def name(cls)[classmethod]:
+ return cls.__name__
+</pre>
+</blockquote>
+<p>involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+</pre>
+</blockquote>
+<p>The implementation is able to recognize such magic docstrings
+and automatically converts methods with magic docstrings in
+method decorators.</p>
+<p>Decorators are nothing else than a sophisticated kind of wrappers.
+The <tt class="literal"><span class="pre">decorators</span></tt> module provides support both for method decorators
+and class decorators:</p>
+<ul class="simple">
+<li><em>Method decorators</em> are classes taking a single function as input and
+producing a descriptor object as output. <tt class="literal"><span class="pre">staticmethod</span></tt> and
+<tt class="literal"><span class="pre">classmethod</span></tt> are two examples of already existing
+method decorators (actually my implementation rewrites them).
+A knowledge of descriptors <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a> is not needed in order to use the <tt class="literal"><span class="pre">decorator</span></tt>
+module; however it is welcomed for advanced users wanting to implement
+custom method decorators.</li>
+<li><em>Class decorators</em> are metaclasses taking a class as imput and returning
+a decorated class as output. A good understanding of metaclasses is needed
+in order to be able to write custom class decorators, but no knowledge
+at all is required in order to use the pre-defined class decorators
+provided by the module.</li>
+</ul>
+<p>Notice that properties are not decorators according to my definitions,
+since they take four functions as input, <tt class="literal"><span class="pre">get,</span> <span class="pre">set,</span> <span class="pre">del_</span></tt> and <tt class="literal"><span class="pre">doc</span></tt>.
+Whereas the decorators concept could be generalized to the case of
+multiple inputs, I don't see the need for such complication, so
+properties are not implemented as method decorators. Moreover, I
+think it is much better to use them trough a class decorator.</p>
+<p>Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the documentation short.</p>
+<p>Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.</p>
+<table class="footnote" frame="void" id="id2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>Descriptors are objects with a <tt class="literal"><span class="pre">__get__</span></tt> method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip on this point ;).</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="decorating-methods">
+<h2><a class="toc-backref" href="#id6" name="decorating-methods">Decorating methods</a></h2>
+<p>Before talking about the implementation details, I will show
+how the <tt class="literal"><span class="pre">decorators</span></tt> module works in practice. The simplest and safest
+usage is by means of the <tt class="literal"><span class="pre">decorators.decorated()</span></tt> function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+is simple but verbose, so magic shortcuts will be discussed in the next
+sections.</p>
+<p>Here, let me give an example, showing that method decorators work both for
+<a class="reference" href="http://www.python.org/2.3/descrintro.html">new style classes and old style classes</a>:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example1.py&gt;
+
+import decorators
+
+def do_nothing(self):
+ &quot;No magic docstring here&quot;
+dec_do_nothing=decorators.decorated(do_nothing)
+
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+dec_identity=decorators.decorated(identity)
+
+def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+dec_name=decorators.decorated(name)
+
+class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+class NewStyle(object):
+ name=dec_name
+
+o=OldStyle() # creates an old style instance
+n=NewStyle() # creates a new style instance
+
+#&lt;/example1.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example1 import * # for testing purposes
+</pre>
+<p>In this example, both <tt class="literal"><span class="pre">dec_identity</span></tt> and <tt class="literal"><span class="pre">dec_name</span></tt> are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects in the interpreter, since they have a re-defined
+printing representation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print dec_identity
+&lt;staticmethod:identity&gt;
+&gt;&gt;&gt; print dec_name
+&lt;classmethod:name&gt;
+</pre>
+<p>On the other hand, <tt class="literal"><span class="pre">do_nothing</span></tt> does not have a magic
+docstring, therefore it is not converted to a decorator object;
+actually it is <em>exactly</em> the original function</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dec_do_nothing is do_nothing # not converted
+True
+</pre>
+<p>and it works as a standard method:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; o.do_nothing() # does nothing, correct
+</pre>
+<p>On the contrary, <tt class="literal"><span class="pre">dec_</span> <span class="pre">identity</span></tt> works as a staticmethod,</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; OldStyle.identity(1) # called from the class
+1
+&gt;&gt;&gt; o.identity(1) # called from the instance
+1
+</pre>
+<p>whereas <tt class="literal"><span class="pre">dec_name</span></tt> works as a classmethod:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; NewStyle.name() # called from the class
+'NewStyle'
+&gt;&gt;&gt; n.name() # called from the instance
+'NewStyle'
+</pre>
+<p>Notice that, I have re-implemented the built-in
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, so</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_identity,staticmethod)
+False
+</pre>
+<p>and</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_name,classmethod)
+False
+</pre>
+<p>It is possible to recognize method decorators since they provides
+a couple of special attributes:</p>
+<ul>
+<li><p class="first"><tt class="literal"><span class="pre">__func__</span></tt>: returning the function from which they originated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert dec_identity.__func__ is identity
+&gt;&gt;&gt; assert dec_name.__func__ is name
+</pre>
+</li>
+<li><p class="first"><tt class="literal"><span class="pre">__klass__</span></tt>: returning the class where they where defined:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; dec_identity.__klass__
+&lt;class 'safetype.?'&gt;
+</pre>
+</li>
+</ul>
+<p>The question mark here means that the definition class is unknown.
+The <tt class="literal"><span class="pre">__klass__</span></tt> attribute can be set by hand or automatically
+with the trick explained in the next section.</p>
+</div>
+<div class="section" id="decorating-classes">
+<h2><a class="toc-backref" href="#id7" name="decorating-classes">Decorating classes</a></h2>
+<p>The problem with the approach just described
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically decorating their
+methods according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with &quot;[Decorated]&quot;
+and by decorating the class.
+Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example2.py&gt;
+
+from decorators import decorated
+from example1 import do_nothing,identity,name
+
+class B(object):
+ &quot;This is a regular class&quot;
+
+B=decorated(B) # does nothing
+
+class C(B):
+ &quot;[Decorated]&quot;
+ do_nothing=do_nothing
+ identity=identity
+ class Inner: # old style class
+ &quot;[Decorated]&quot; # required docstring
+ name=name
+
+C=decorated(C) # regenerates the class converting methods in decorators
+c=C()
+
+#&lt;/example2.py&gt;
+</pre>
+</blockquote>
+<p>Under the hood <tt class="literal"><span class="pre">decorators.decorated()</span></tt> recognizes the class level
+magic docstring &quot;[Decorated]&quot; and creates an instance of the
+<tt class="literal"><span class="pre">decorators.Decorated</span></tt> metaclass.
+Internally the metaclass invokes <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. Moreover, it decorates inner classes,
+if a suitable docstring is found, and it works recursively.</p>
+<p>Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example2 import *
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert C.Inner.name() == 'Inner'
+&gt;&gt;&gt; assert c.identity(1) == 1
+&gt;&gt;&gt; assert c.Inner.name() == 'Inner'
+</pre>
+<p>Notice that adding <tt class="literal"><span class="pre">identity</span></tt> after the class creation with the syntax
+<tt class="literal"><span class="pre">C.identity=identity</span></tt> would not work;
+<tt class="literal"><span class="pre">C.identity=decorators.decorated(identity)</span></tt> is required:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.identity=decorators.decorated(identity)
+&gt;&gt;&gt; C.identity(1) # it works
+1
+</pre>
+<p>If a class misses the magic docstring, nothing happens:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; B # returns the original B
+&lt;class 'example2.B'&gt;
+</pre>
+<p>The mechanism works for old style classes too,
+since the metaclass automagically converts the old style classes in a
+new style one:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(C.Inner) # C.Inner is an instance of decorator.Decorated
+&lt;class 'decorators.Decorated'&gt;
+</pre>
+<p>The enhancement provided by the metaclass includes a new default
+printing representation for both the class</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print C # returns the name of D and of its metaclass
+&lt;class C[Decorated]&gt;
+</pre>
+<p>and its instances:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print c
+&lt;C instance&gt;
+</pre>
+<p>On the other hand, if a custom printing representation is already
+defined, the metaclass takes it without any change.</p>
+<p>One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(C):
+... def name(cls):
+... &quot;[classmethod]&quot;
+... return cls.__name__
+&gt;&gt;&gt; print D.name()
+D
+</pre>
+<p>Decorating whole classes presents another advantage: the decorated methods know
+the class where they were defined via the special attribute <tt class="literal"><span class="pre">__klass__</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D.__dict__['name'].__klass__ # the class where 'name' is defined
+&lt;class 'D'&gt;
+</pre>
+<p>This is useful for introspection and debugging purposes.</p>
+</div>
+<div class="section" id="adding-magic">
+<h2><a class="toc-backref" href="#id8" name="adding-magic">Adding magic</a></h2>
+<p>The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt>
+on top of you module. Then all methods in all classes with a magic docstring
+will be checked for magic docstrings and automagically decorated if needed.
+For instance, the previous example would be written</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example3.py&gt;
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ &quot;[Decorated]&quot; # magic docstring here
+ def do_nothing(self):
+ &quot;No magic docstring here&quot;
+
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+class D(object):
+ &quot;Undecorated&quot; # no magic docstring here
+ def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+c=C(); d=D()
+
+#&lt;/example3.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">C</span></tt> has a <tt class="literal"><span class="pre">[Decorated]</span></tt> docstring, so its methods
+are automatically decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example3 import *
+&gt;&gt;&gt; assert c.do_nothing() is None
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert c.identity(1) == 1
+</pre>
+<p>On the other hand, since <tt class="literal"><span class="pre">D</span></tt> misses a magic docstring,
+its <tt class="literal"><span class="pre">name</span></tt> method is not decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; hasattr(D.__dict__['name'],'__func__') # not a decorator
+False
+</pre>
+<p>Since <tt class="literal"><span class="pre">D.name</span></tt> is a regular method and not a classmethod, <tt class="literal"><span class="pre">D.name()</span></tt>
+gives an error:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance as first argument (got nothing instead)
+</pre>
+<p>Under the hood, the magic works by enhancing the <tt class="literal"><span class="pre">object</span></tt> class
+of the module with a <tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> metaclass:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import example4
+&gt;&gt;&gt; type(example4.object)
+&lt;class 'decorators.ClassDecorator'&gt;
+</pre>
+<p>Notice that for safety reasons the enhancement is only on the module
+<tt class="literal"><span class="pre">object</span></tt> class, not on the <tt class="literal"><span class="pre">__builtin__.object</span></tt> class. The dangers of
+adding too much magic are discussed in the <a class="reference" href="#diving-into-magic">Diving into magic</a> section.</p>
+</div>
+<div class="section" id="defining-method-decorators">
+<h2><a class="toc-backref" href="#id9" name="defining-method-decorators">Defining method decorators</a></h2>
+<p>The <tt class="literal"><span class="pre">decorators</span></tt> module contains two predefinite method decorators,
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The <tt class="literal"><span class="pre">decorators.MethodDecorator</span></tt> class which is here
+exactly for that purpose.</p>
+<p>Custom decorators are expected to be implemented by subclassing
+<tt class="literal"><span class="pre">MethodDecorator</span></tt> and by overriding its <tt class="literal"><span class="pre">get</span></tt> method. The
+<tt class="literal"><span class="pre">get</span></tt> method automagically induces a <tt class="literal"><span class="pre">__get__</span></tt> method, turning the
+class in a descriptor. The machinery is needed since <tt class="literal"><span class="pre">__get__</span></tt> cannot
+be made cooperative using the standard <tt class="literal"><span class="pre">super</span></tt> mechanism because
+there would be a confusion between <tt class="literal"><span class="pre">super.__get__</span></tt> and the decorator
+<tt class="literal"><span class="pre">__get__</span></tt>. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators <em>too</em> easy, since they are potentially dangerous.</p>
+<p>In order to give a simple example, let me show the implementation
+of a <tt class="literal"><span class="pre">chattymethod</span></tt> that prints a message when it is called:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+from decorators import *
+
+class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Notice the usage of the <tt class="literal"><span class="pre">super().get</span></tt> trick. This guarantees that
+<tt class="literal"><span class="pre">chattymethod</span></tt> will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.</p>
+<p>Here is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import customdec # adds chattymethod to the list of known decorators
+&gt;&gt;&gt; customdec.enhance_classes() # automagically enhances classes
+&gt;&gt;&gt; class C:
+... &quot; [Decorated] &quot;
+... def f(self):
+... &quot;&quot;&quot;
+... [ chattymethod ]
+... &quot;&quot;&quot;
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.f()
+calling &lt;chattymethod:f&gt; from &lt;C instance&gt;
+</pre>
+<p>By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.</p>
+<p>One can check that the syntax <tt class="literal"><span class="pre">C.f(c)</span></tt> works too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.f(c)
+calling &lt;chattymethod:f&gt; from &lt;class C[Decorated]&gt;
+</pre>
+<p>A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind</p>
+<blockquote>
+<pre class="literal-block">
+def f(self)[chattymethod(logfile=file('file1.log','w'))]
+</pre>
+</blockquote>
+<p>In my view, there are too many parenthesis in this syntax, and it
+becomes rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the <tt class="literal"><span class="pre">decorators</span></tt> module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.</p>
+<p>A simple minded solution is to change the defaults by hand:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from customdec import chattymethod,decorated
+&gt;&gt;&gt; chattymethod.logfile=file('file.log','w')
+&gt;&gt;&gt; def g(self):
+... &quot;[chattymethod]&quot;
+&gt;&gt;&gt; C.g=decorated(g)
+&gt;&gt;&gt; c.g() # will print a message on file.log
+</pre>
+<p>This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.f()
+</pre>
+<p>will print a message to <tt class="literal"><span class="pre">file.log</span></tt> too, and not to standard output.
+Here is the confirmation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chattymethod.logfile.close()
+&gt;&gt;&gt; print file('file.log').read().rstrip()
+calling &lt;chattymethod:g&gt; from &lt;C instance&gt;
+calling &lt;chattymethod:f&gt; from &lt;C instance&gt;
+</pre>
+<p>A better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the <tt class="literal"><span class="pre">__init__</span></tt> method. Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">__init__</span></tt> method has the signature
+<tt class="literal"><span class="pre">__init__(self,objfunc)</span></tt>, where the <tt class="literal"><span class="pre">objfunc</span></tt> object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding <tt class="literal"><span class="pre">__init__</span></tt>.</p>
+<p>Here is the testing:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty2.py&gt;
+
+import customdec; customdec.enhance_classes()
+
+# sets the log files
+log1=file('file1.log','w')
+log2=file('file2.log','w')
+
+class C:
+ &quot;[Decorated]&quot;
+ def f(self):
+ &quot;[chattymethod2]&quot;
+ f.logfile=log1 # function attribute
+ def g(self):
+ &quot;[chattymethod2]&quot;
+ g.logfile=log2 # function attribute
+
+assert C.__dict__['f'].logfile is log1 # check the conversion
+assert C.__dict__['g'].logfile is log2 # function attr. -&gt; decorator attr.
+
+c=C() # C instantiation
+
+c.f() # print a message in file1.log
+c.g() # print a message in file2.log
+
+log1.close(); log2.close() # finally
+
+#&lt;/chatty2.py&gt;
+</pre>
+</blockquote>
+<p>Let me check the contents of the log files:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import chatty2
+&gt;&gt;&gt; print file('file1.log').read().rstrip()
+calling &lt;chattymethod2:f&gt; from &lt;C instance&gt;
+&gt;&gt;&gt; print file('file2.log').read().rstrip()
+calling &lt;chattymethod2:g&gt; from &lt;C instance&gt;
+</pre>
+<p><tt class="literal"><span class="pre">chattymethod</span></tt> is the poor man version of <tt class="literal"><span class="pre">tracedmethod</span></tt>, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class tracedmethod(MethodDecorator):
+ &quot;Descriptor class, converts a method in a traced method&quot;
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -&gt; dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write(&quot;%sCalling '%s.%s' with arguments &quot; %
+ (i,clsname,self.funcname))
+ self.output.write(&quot;%s%s ...\n&quot; % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write(&quot;%s'%s.%s' called with result: %s\n&quot;
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method wrapper object, so it is
+possible to use <tt class="literal"><span class="pre">im_func</span></tt> to retrieve the internal function
+<tt class="literal"><span class="pre">tracedmeth</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... &quot;[Decorated]&quot;
+... def f(self):
+... &quot;[tracedmethod]&quot;
+&gt;&gt;&gt; c=C(); c.f()
+Calling 'C.f' with arguments &lt;C instance&gt;(){} ...
+'C.f' called with result: None
+&gt;&gt;&gt; c.f.im_func.__name__
+'tracedmeth'
+</pre>
+<p>As soon as the <tt class="literal"><span class="pre">tracedmethod</span></tt> module is loaded, the <tt class="literal"><span class="pre">tracedmethod</span></tt> class
+is added to the list of know decorators, so one should use the
+&quot;[tracedmethod]&quot; docstring and not something like
+&quot;[customdec.tracedmethod]&quot;.</p>
+<p>Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example9.py&gt;
+
+import customdec; customdec.enhance_classes()
+
+logfile=file('file3.log','w')
+
+class C(object):
+ &quot;[Decorated]&quot;
+ def fact(self,n):
+ &quot;[tracedmethod] The good old factorial.&quot;
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+C().fact(2) # write a message to logfile
+
+logfile.close()
+
+#&lt;/example9.py&gt;
+</pre>
+</blockquote>
+<p>Here is the content of <tt class="literal"><span class="pre">file3.log</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import example9 # creates file3.log
+&gt;&gt;&gt; print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments &lt;C instance&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;C instance&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;C instance&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+</pre>
+</div>
+<div class="section" id="defining-class-decorators">
+<h2><a class="toc-backref" href="#id10" name="defining-class-decorators">Defining class decorators</a></h2>
+<p>PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a couple of
+class decorator at work, the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>, which gives
+to its instances the ability to interpret class level magic docstrings, and
+its subclass <tt class="literal"><span class="pre">Decorated</span></tt>, which converts functions in method decorators
+if suitable docstrings are found.</p>
+<p>To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference of deriving from <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead
+of deriving from <tt class="literal"><span class="pre">type</span></tt>.</p>
+<p>To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print &gt;&gt; cls.output,&quot;%s created&quot; % cls
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">Logged</span></tt> is derived by the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+(meta-)metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>).
+Logging capabilities can be added to a class
+by simply using the magic docstring syntax:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;logged.py&gt;
+
+import customdec; customdec.enhance_classes()
+
+class D(object): # will print a message at D creation
+ &quot;[Logged]&quot;
+
+#&lt;/logged.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import logged
+&lt;class 'logged.D'&gt; created
+</pre>
+<p>Notice that the printing representation of <tt class="literal"><span class="pre">D</span></tt> involves the name
+of <tt class="literal"><span class="pre">D</span></tt> preceded by the name of its metaclass, which in this case
+is <tt class="literal"><span class="pre">Logged</span></tt></p>
+<p>Each time an instance of <tt class="literal"><span class="pre">Logged</span></tt> is created, a similar message is printed:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(logged.D):
+... pass
+&lt;class 'E'&gt; created
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">E</span></tt> does not have any magic docstring</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.__doc__ # no docstring
+</pre>
+<p>but still it inherits its magic from <tt class="literal"><span class="pre">D</span></tt>.</p>
+<p>Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+from types import FunctionType
+
+class Traced(Decorated):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType) and name!='__str__':
+ # cannot trace __str__, since it is invoked by
+ # tracedmethod and would generate infinite recursion
+ func.__doc__=&quot;[tracedmethod] &quot; + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d) # decorates the methods
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... &quot;&quot;&quot;[Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... in method decorator objects.&quot;&quot;&quot;
+... def f1(self): pass
+... def f2(self): pass
+...
+&gt;&gt;&gt; type(C)
+&lt;class 'customdec.Traced'&gt;
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.f1()
+Calling 'C.f1' with arguments &lt;C instance&gt;(){} ...
+'C.f1' called with result: None
+&gt;&gt;&gt; c.f2()
+Calling 'C.f2' with arguments &lt;C instance&gt;(){} ...
+'C.f2' called with result: None
+</pre>
+<p>By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt>
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example7.py&gt;
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes(&quot;[Decorated]&quot;)
+
+class C1(object): # automagically converted to a decorated class
+ identity=identity
+
+class C2: # automagically converted to a decorated class
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+#&lt;/example7.py&gt;
+</pre>
+</blockquote>
+<p>The magic works both for new style classes such as <tt class="literal"><span class="pre">C1</span></tt></p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example7 import C1,c1
+&gt;&gt;&gt; assert C1.identity(1) == 1
+&gt;&gt;&gt; assert c1.identity(1) == 1
+</pre>
+<p>and old style classes such as <tt class="literal"><span class="pre">C2</span></tt> by implicitly converting them
+to new style classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example7 import C2,c2
+&gt;&gt;&gt; assert C2.name() == 'C2'
+&gt;&gt;&gt; assert c2.name() == 'C2'
+</pre>
+</div>
+<div class="section" id="composing-decorators">
+<h2><a class="toc-backref" href="#id11" name="composing-decorators">Composing decorators</a></h2>
+<p>Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example6.py&gt;
+
+&quot;How to trace a class method&quot;
+
+import customdec; customdec.enhance_classes()
+
+class C(object):
+ &quot;[Decorated]&quot;
+ def fact(cls,n): # a traced classmethod
+ &quot;[classmethod,tracedmethod]&quot;
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+#&lt;/example6.py&gt;
+</pre>
+</blockquote>
+<p>Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example6 import C
+&gt;&gt;&gt; C.fact(2)
+Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+</pre>
+<p>You may easily check that calling <tt class="literal"><span class="pre">C().fact</span></tt> will work too.</p>
+<p>Under the hood the syntax</p>
+<blockquote>
+<pre class="literal-block">
+[classmethod,tracedmethod]
+</pre>
+</blockquote>
+<p>generates a <tt class="literal"><span class="pre">classmethodtracedmethod</span></tt> class obtained via
+multiple inheritance:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(C.__dict__['fact'])
+&lt;class 'safetype.classmethodtracedmethod'&gt;
+</pre>
+<p>Notice that the order <em>does</em> matter and using the docstring
+&quot;[tracedmethod,classmethod]&quot; will not work:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D:
+... &quot;[Decorated]&quot;
+... def f(cls):
+... &quot;[tracedmethod,classmethod]&quot;
+&gt;&gt;&gt; D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead)
+</pre>
+<p>The problem here is that <tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method-wrapper object
+which expects a D instance as first argument whereas it gets <tt class="literal"><span class="pre">None</span></tt> since
+it is called from the class. On the other hand,</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D().f()
+Calling 'D.f' with arguments &lt;D instance&gt;(){} ...
+'D.f' called with result: None
+</pre>
+<p>will work. When <tt class="literal"><span class="pre">classmethod</span></tt> precedes <tt class="literal"><span class="pre">tracedmethod</span></tt>, then
+<tt class="literal"><span class="pre">classmethod</span></tt> passes to <tt class="literal"><span class="pre">tracedmeth</span></tt> a non-empty first argument,
+i.e. the calling class, even when called from the instance:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C().fact(2)
+Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+</pre>
+<p>If we try to trace a staticmethod, we will get a different error with
+the order &quot;tracedmethod, staticmethod&quot;:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class F(object):
+... &quot;[Decorated]&quot;
+... def fact(n):
+... &quot;[tracedmethod,staticmethod]&quot;
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+&gt;&gt;&gt; F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead)
+</pre>
+<p>The message is self-explanatory.</p>
+<p>On the other hand, composing the decorators in the order
+&quot;[tracedmethod,staticmethod]&quot; will work just fine.</p>
+<p>It is possible to compose class decorators just as method decorators,
+by using the docstring syntax: for instance we may create a class
+which is both <tt class="literal"><span class="pre">Decorated</span></tt> and <tt class="literal"><span class="pre">Logged</span></tt> and by trivially writing</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... &quot;[Decorated,Logged]&quot;
+... def f():
+... &quot;[staticmethod]&quot;
+... return 'it works!'
+&lt;class C[DecoratedLogged]&gt; created
+&gt;&gt;&gt; C().f()
+'it works!'
+</pre>
+</div>
+<div class="section" id="diving-into-magic">
+<h2><a class="toc-backref" href="#id12" name="diving-into-magic">Diving into magic</a></h2>
+<p>If you never use metaclasses, this part can be safely skipped. On the
+other hand, if you are a metaclass user, you <em>must</em> read this section
+in order to keep your code working.</p>
+<p>The <tt class="literal"><span class="pre">decorators</span></tt> module provide a <tt class="literal"><span class="pre">ClassDecorator</span></tt> metaclass which
+converts a regular (both old style or new style) class in a class with
+the ability to recognize magic docstrings. Under the hood, the
+<tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt> trick works by decorating the
+<tt class="literal"><span class="pre">object</span></tt> class with <tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and by setting
+the custom metaclass to <tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and it is
+equivalent to</p>
+<blockquote>
+<pre class="literal-block">
+import decorators
+object=decorators.ClassDecorator(object) # decorates new style classes
+__metaclass__= decorators.ClassDecorator # decorates old style classes
+</pre>
+</blockquote>
+<p>If you want the magic to work only for new style classes only, you may
+forget the second line; if you want the magic to work for old style
+classes only, you may forget the first line.</p>
+<p>The <tt class="literal"><span class="pre">decorators.enhance_classes(&quot;[SomeClassDec]&quot;)</span></tt> syntax looks at the
+magic docstring and executes something like</p>
+<blockquote>
+<pre class="literal-block">
+import decorators
+object=decorators.SomeClassDec(object) # decorates all new style classes
+__metaclass__= decorators.SomeClassDec # decorates all old style classes
+</pre>
+</blockquote>
+<p>The problem with the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntaxes is that it is
+not 100% safe under metaclass conflicts. In order to explain the issue,
+let me give an example.</p>
+<p>Suppose we enhance the <tt class="literal"><span class="pre">object</span></tt> class in the interpreter namespace:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import decorators; decorators.enhance_classes()
+</pre>
+<p>making it an instance of <tt class="literal"><span class="pre">ClassDecorator</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; object.__class__
+&lt;class 'decorators.ClassDecorator'&gt;
+</pre>
+<p>Now, if we naively create a custom metaclass <tt class="literal"><span class="pre">M</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... &quot;Some non-trivial code here...&quot;
+</pre>
+<p>and we try to use it to enhance a new style class <tt class="literal"><span class="pre">NwithM</span></tt>, we will get a
+conflict:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class NwithM(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>The problem is that the previous line tries to create a class <tt class="literal"><span class="pre">NwithM</span></tt>
+which should have both metaclasses <tt class="literal"><span class="pre">ClassDecorator</span></tt> and <tt class="literal"><span class="pre">M</span></tt>:
+a conflict follows.</p>
+<p>Fortunately, the decorators module imports the <tt class="literal"><span class="pre">type</span></tt> metaclass from
+my <tt class="literal"><span class="pre">safetype</span></tt> module just to avoid this kind of problems. If we
+derive <tt class="literal"><span class="pre">M</span></tt> from <tt class="literal"><span class="pre">decorators.type</span></tt> (which is really <tt class="literal"><span class="pre">safetype.type</span></tt>)
+the conflict is automagically avoided:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(decorators.type):
+... &quot;This metaclass is conflict-proof&quot;
+&gt;&gt;&gt; class NwithM(object): # it works!
+... __metaclass__=M
+</pre>
+<p>The reason why the conflict is avoided is that the <tt class="literal"><span class="pre">safetype</span></tt> module
+(which makes use of really <em>deep</em> metaclass magic) automatically
+creates the composed class <tt class="literal"><span class="pre">MClassDecorator</span></tt> by multiply inheriting
+from <tt class="literal"><span class="pre">M</span></tt> and from <tt class="literal"><span class="pre">ClassDecorator</span></tt>. Then, <tt class="literal"><span class="pre">NwithM</span></tt> can be safely
+created as an instance of <tt class="literal"><span class="pre">safetype.MClassDecorator</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(NwithM)
+&lt;class 'safetype.MClassDecorator'&gt;
+</pre>
+<p>The situation with old style classes is worse since
+apparently</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class OwithM: # old style class with metaclass M
+... __metaclass__=M
+... def sm():
+... &quot;[staticmethod]&quot;
+</pre>
+<p>gives no error, but actually <tt class="literal"><span class="pre">M</span></tt> overrides
+<tt class="literal"><span class="pre">ClassDecorator</span></tt>, so <tt class="literal"><span class="pre">OwithM</span></tt> will not recognize magic docstrings:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(OwithM)
+&lt;class 'M'&gt;
+&gt;&gt;&gt; OwithM.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with OwithM instance as first argument (got nothing instead)
+</pre>
+<p>The simpler solution is to convert <tt class="literal"><span class="pre">OwithM</span></tt> in a new style class:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class NwithM(OwithM,object):
+... __metaclass__=M
+... def sm():
+... &quot;[staticmethod]&quot;
+&gt;&gt;&gt; type(NwithM)
+&lt;class 'safetype.MClassDecorator'&gt;
+</pre>
+<p>Now <tt class="literal"><span class="pre">NwithM</span></tt> is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so <tt class="literal"><span class="pre">NwithM</span></tt>
+subclasses with a &quot;[Decorated]&quot; docstring will be decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(NwithM):
+... &quot;[Decorated]&quot;
+... def cm(cls):
+... &quot;[classmethod]&quot;
+... print '.cm() called from',cls
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.cm() # it works
+.cm() called from &lt;class E[MClassDecoratorDecorated]&gt;
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">sm</span></tt> was defined in <tt class="literal"><span class="pre">NwithM</span></tt>, the undecorated class: therefore
+it is not decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead)
+</pre>
+<p>The error message says that <tt class="literal"><span class="pre">sm</span></tt> is an unbound method and not a
+static method.</p>
+<p>It is possible to go even deeper in <strong>black</strong> magic, and to decorate all
+the new style classes in <em>all</em> modules, by decorating the
+<tt class="literal"><span class="pre">__builtin__.object</span></tt>. Still, naively redefining the <tt class="literal"><span class="pre">__builtin__object</span></tt>
+class is risky, since it will induce metaclass conflicts in other modules
+using metaclasses. In other words, it will work only if you import modules
+not using metaclasses, or modules using metaclasses safely, i.e. modules
+taking care of possible conflicts by using <tt class="literal"><span class="pre">safetype.type</span></tt> as base metaclass.
+If you really enjoy black magic, you may solve the problem by
+redefining the <tt class="literal"><span class="pre">__builtin__.type</span></tt> as <tt class="literal"><span class="pre">safetype.type</span></tt>.
+The <tt class="literal"><span class="pre">decorators</span></tt> module does not go so deep in dark magic, but still
+there are cases where you may want to do it. In these cases you must be
+explicit and redefine the builtins by hand, without help from the
+module. For instance, one of my original motivation for the usage
+of metaclasses/class decorators was to use them for debugging purposes.
+For instance, I wanted to trace the execution of methods in
+complicate inheritance hierarchies, <em>without changing the source code</em>.
+For simple situations, i.e. in absence of inheritance and of pre-existing
+metaclasses, the function <tt class="literal"><span class="pre">import_with_metaclass</span></tt> discussed in the
+<a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta.html">first paper on metaclasses</a> written in collaboration with David Mertz, works
+fine but in general the implementation of a working <tt class="literal"><span class="pre">import_with_metaclass</span></tt>
+is cumbersome. For debugging purposes, the quick and dirty way can be a
+good enough way, so let me show how we can redefine the builtins <tt class="literal"><span class="pre">object</span></tt> and
+<tt class="literal"><span class="pre">type</span></tt> <em>before</em> importing the module, in such a way to enhance <em>all</em>
+its classes with tracing capabilities.</p>
+<p>Let me start from a module using an unsafe metaclass <tt class="literal"><span class="pre">M</span></tt>, such that it
+cannot be traced trivially by decorating its classes <em>after</em> the import;
+moreover there is an inheritance hierarchy, such that it cannot be
+traced correctly by naively decorating all the classes one after the
+other (unless one modifies the original source code, but this forbidden
+by the rules of the game ;)</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracing.py&gt;
+
+&quot;&quot;&quot;
+This is a pre-existing module not using decorators but using multiple
+inheritance and unsafe metaclasses. We want to trace it for debugging
+purposes.
+&quot;&quot;&quot;
+
+class M(type):
+ &quot;There should be some non-trivial code here&quot;
+
+class B(object):
+ def __init__(self):
+ super(B,self).__init__()
+
+class D(object):
+ __metaclass__=M
+ def __init__(self):
+ super(D,self).__init__()
+
+class E(B,D):
+ def __init__(self):
+ super(E,self).__init__()
+
+ #&lt;/tracing.py&gt;
+</pre>
+</blockquote>
+<p>Everything works is we modify the builtins before importing the module:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import __builtin__
+&gt;&gt;&gt; __object__=__builtin__.object # the original 'object' class
+&gt;&gt;&gt; __builtin__.object=customdec.Traced('tracedobject',(),{})
+&gt;&gt;&gt; __builtin__.type=customdec.type # a safe 'type' class
+</pre>
+<p>Now, when we import the module, all the classes <tt class="literal"><span class="pre">M</span></tt>, <tt class="literal"><span class="pre">B</span></tt>, <tt class="literal"><span class="pre">D</span></tt> and <tt class="literal"><span class="pre">E</span></tt>
+will be created starting for the tricked builtins, so they will be traced
+and safe under conflicts. For instance, let me show that <tt class="literal"><span class="pre">E</span></tt> is created
+with a conflict safe <tt class="literal"><span class="pre">MTraced</span></tt> metaclass:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracing import E
+&gt;&gt;&gt; print E
+&lt;class E[MTraced]&gt;
+</pre>
+<p>This shows that the <tt class="literal"><span class="pre">__init__</span></tt> methods are traced indeed and shows the
+Method Resolution Order:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; e=E()
+Calling 'E.__init__' with arguments &lt;E instance&gt;(){} ...
+ Calling 'B.__init__' with arguments &lt;E instance&gt;(){} ...
+ Calling 'D.__init__' with arguments &lt;E instance&gt;(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None
+</pre>
+<p>This works, indeed. At the end, one should not forget to restore
+the standard builtins, otherwise it will trace <em>all</em> the classes
+created thereafter.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; __builtin__.object=__object__ # restore the __builtin__
+&gt;&gt;&gt; __builtin__.type=decorators.__type__ # restore the __builtin__
+</pre>
+<p>At the end, I will notice that it is possible to solve the problem more
+nicely, without redefining the builtins, but I will discuss the solution
+elsewhere ;)</p>
+</div>
+<div class="section" id="advanced-usage">
+<h2><a class="toc-backref" href="#id13" name="advanced-usage">Advanced usage</a></h2>
+<p>Whereas the average programmer is expected to use the <tt class="literal"><span class="pre">decorated()</span></tt>
+and <tt class="literal"><span class="pre">enhance_classes()</span></tt> functions only, the <tt class="literal"><span class="pre">decorators</span></tt> module provides
+facilities which may be useful to the advanced programmer.</p>
+<p>The simplest is an utility function which retrieves the list of
+recognized decorators, <tt class="literal"><span class="pre">decorators.getdec()</span></tt>. The precise syntax is
+<tt class="literal"><span class="pre">decorators.getdec(docstring)</span></tt>, where <tt class="literal"><span class="pre">docstring</span></tt>
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance <tt class="literal"><span class="pre">decorators.getdec('[MethodDecorator]')</span></tt>
+gives the list of all subclasses of <tt class="literal"><span class="pre">MethodDecorator</span></tt>, i.e. all method
+decorators, whereas <tt class="literal"><span class="pre">decorators.getdec('[ClassDecorator]')</span></tt>
+gives the list of the known class decorators. The utility of the function
+is that it also returns decorators that have been automagically created:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; decorators.getdec(&quot;[classmethodtracedmethod]&quot;)
+[&lt;class 'safetype.classmethodtracedmethod'&gt;]
+</pre>
+<p>It is even possible to use the comma notation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; decorators.getdec(&quot;[classmethod,tracedmethod]&quot;)
+[&lt;class 'safetype.classmethodtracedmethod'&gt;]
+</pre>
+<p>If you mispell a decorator name you get an helpful error message:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E:
+... &quot;[Decorated]&quot;
+... def f():
+... &quot;[staticmeth]&quot;
+Traceback (most recent call last):
+ .. a cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+</pre>
+<p>By design, the <tt class="literal"><span class="pre">decorated</span></tt> function is quite limited, and does not
+decorate functions without magic docstrings. This is safe and also convenient
+for internal usage of the <tt class="literal"><span class="pre">decorated</span></tt> function in the <tt class="literal"><span class="pre">Decorated</span></tt>
+metaclass. Nevertheless, advanced users may want to have the ability
+of decorating functions by hand, without invoking <cite>decorators.decorated()`</cite>
+and magic docstrings. This is possible, by calling the decorator directly.
+For instance, here is how to convert a lambda function in a staticmethod:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; do_nothing=decorators.staticmethod(lambda:None)
+&gt;&gt;&gt; print do_nothing # ``do_nothing`` is a static method
+&lt;staticmethod:&lt;lambda&gt;&gt;
+</pre>
+<p>The most convenient usage of this feature is in the composition of decorators.
+For instance, we may compose the just defined <tt class="literal"><span class="pre">staticmethod</span></tt> with a
+<tt class="literal"><span class="pre">chattymethod2</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chattystatic=customdec.chattymethod2(do_nothing)
+&gt;&gt;&gt; print chattystatic
+&lt;chattymethod2staticmethod:&lt;lambda&gt;&gt;
+</pre>
+<p>The syntax</p>
+<blockquote>
+<tt class="literal"><span class="pre">decorator1(decorator2(obj))</span></tt></blockquote>
+<p>automagically creates a composed class <tt class="literal"><span class="pre">decorator1decorator2</span></tt> in the
+<tt class="literal"><span class="pre">safetype</span></tt> module (or recycle it, if <tt class="literal"><span class="pre">decorator1decorator2</span></tt> has
+been already created) and it is equivalent to</p>
+<blockquote>
+<tt class="literal"><span class="pre">decorator1decorator2(obj)</span></tt></blockquote>
+<p>Here is the check that everything works:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B:
+... chattystatic=chattystatic
+&gt;&gt;&gt; B.chattystatic()
+calling &lt;chattymethod2staticmethod:&lt;lambda&gt;&gt; from &lt;class 'B'&gt;
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">chattystatic</span></tt> does not know the class where it
+is defined:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chattystatic.__klass__
+&lt;class 'safetype.?'&gt;
+</pre>
+<p>unless the <tt class="literal"><span class="pre">__klass__</span></tt> attribute is explicitly set.</p>
+<p>This is the hierarchy:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for C in type(chattystatic).mro(): print C
+...
+&lt;class 'safetype.chattymethod2staticmethod'&gt;
+&lt;class 'customdec.chattymethod2'&gt;
+&lt;class 'customdec.chattymethod'&gt;
+&lt;class 'decorators.staticmethod'&gt;
+&lt;class 'decorators.MethodDecorator'&gt;
+&lt;class 'decorators.Decorator'&gt;
+&lt;type 'object'&gt;
+</pre>
+<p>One can also compose classes by hand, by using class decorators:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... &quot;[Logged]&quot;
+... def f(): &quot;[staticmethod]&quot;
+...
+&lt;class 'C'&gt; created
+&gt;&gt;&gt; C=customdec.Traced(C)
+&lt;class C[TracedLogged]&gt; created
+</pre>
+<p>The <tt class="literal"><span class="pre">decorators.enhance_classes(&lt;classdecorator&gt;)</span></tt> syntax performs
+the composition automagically:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example8.py&gt;
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes(&quot;[Decorated]&quot;)
+
+class C1: # automagically converted to a Decorated class
+ identity=identity
+
+class C2: # automagically converted to a DecoratedLogged class
+ &quot;[Logged]&quot;
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+#&lt;/example8.py&gt;
+</pre>
+</blockquote>
+<p>In this example the class <tt class="literal"><span class="pre">C2</span></tt> has already a magic docstring. This means that
+<tt class="literal"><span class="pre">C2</span></tt> has to be enhanced both from <tt class="literal"><span class="pre">Logged</span></tt> and from <tt class="literal"><span class="pre">Decorated</span></tt>.
+This is done by automagically creating a <tt class="literal"><span class="pre">DecoratedLogged</span></tt> class
+decorator:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example8 import C1,C2,c1,c2
+&lt;class C2[DecoratedLogged]&gt; created
+</pre>
+<p>The second line is printed because of the logging capabilities of <tt class="literal"><span class="pre">C2</span></tt>.
+Moreover, since <tt class="literal"><span class="pre">C2</span></tt> is decorated too, the following will work:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert C2.name() == 'C2'
+&gt;&gt;&gt; assert c2.name() == 'C2'
+</pre>
+<p>Idem for <tt class="literal"><span class="pre">C1</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert C1.identity(1) == 1
+&gt;&gt;&gt; assert c1.identity(1) == 1
+</pre>
+</div>
+</div>
+<div class="section" id="the-implementation">
+<h1><a class="toc-backref" href="#id14" name="the-implementation">The implementation</a></h1>
+<p>This part can be safely skipped, unless you are a <em>really</em> curious and
+you want to know how the implementation works.</p>
+<p>The module is rather short (less than 150 lines if you skip comments and
+docstrings) but far from being trivial, since it is based on extensive
+usage of metaclass wizardry. Moreover,
+it invokes the <tt class="literal"><span class="pre">safetype</span></tt> module to solve metaclass conflicts, and
+<tt class="literal"><span class="pre">safetype</span></tt> contains 50 lines of <em>really</em> deep metaclass wizardry.</p>
+<blockquote>
+<div class="figure">
+<p><img alt="decorators.png" src="decorators.png" /></p>
+</div>
+</blockquote>
+<p>The main class-metaclass hierarchy is represented in the figure, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.</p>
+<p>For instance, this is the Method Resolution Order for the <tt class="literal"><span class="pre">Decorated</span></tt>
+metaclass:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for i,c in enumerate(decorators.Decorated.__mro__):
+... print i,c,&quot;[%s]&quot; % type(c).__name__
+0 &lt;class 'decorators.Decorated'&gt; [MetaDecorator]
+1 &lt;class 'decorators.ClassDecorator'&gt; [MetaDecorator]
+2 &lt;class 'safetype.safetype'&gt; [type]
+3 &lt;type 'type'&gt; [type]
+4 &lt;class 'decorators.Decorator'&gt; [MetaDecorator]
+5 &lt;type 'object'&gt; [type]
+</pre>
+<p><tt class="literal"><span class="pre">Decorator</span></tt> is the mother of all decorators. Its main purpose it to
+provide the <tt class="literal"><span class="pre">MetaDecorator</span></tt>
+metaclass to all decorators.</p>
+<p>The <tt class="literal"><span class="pre">safetype</span></tt> metaclass, imported from the <tt class="literal"><span class="pre">safetype</span></tt> module,
+ensures that class decorators can generate classes without incurring
+in conflicts.</p>
+<p>Since class decorators are metaclasses,
+<tt class="literal"><span class="pre">MetaDecorator</span></tt> is actually a meta-metaclass with respect to
+instances of decorated classes. For this reason if</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C=decorators.ClassDecorator('C',(),{})
+</pre>
+<p>then</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__class__
+&lt;class 'decorators.ClassDecorator'&gt;
+</pre>
+<p>but</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__metaclass__
+&lt;class 'decorators.MetaDecorator'&gt;
+</pre>
+<p>since the <tt class="literal"><span class="pre">C.__metaclass__</span></tt> attribute is inherited from <tt class="literal"><span class="pre">Decorator</span></tt>.</p>
+<p>On the other hand, <tt class="literal"><span class="pre">MetaDecorator</span></tt> is a simple metaclass with
+respect to instances of decorated methods.</p>
+<p>The implementation is relatively smart. Consider for instance the case of
+the <tt class="literal"><span class="pre">logged</span></tt> example. In that example class <tt class="literal"><span class="pre">D</span></tt> was a subclass
+of a tricked <tt class="literal"><span class="pre">object</span></tt> class, enhanced by <tt class="literal"><span class="pre">ClassDecorator</span></tt>. Moreover <tt class="literal"><span class="pre">D</span></tt>
+had a <tt class="literal"><span class="pre">Logged</span></tt> docstring. Therefore it should have been an instance of a
+<tt class="literal"><span class="pre">ClassDecoratorLogged</span></tt> metaclass. But logged was
+a subclass of <tt class="literal"><span class="pre">ClassDecorator</span></tt>, therefore it already had all the features
+of <tt class="literal"><span class="pre">ClassDecorator</span></tt> and it would have been redundant to create
+<tt class="literal"><span class="pre">ClassDecoratorLogged</span></tt>, when``Logged`` would have been enough.
+So <tt class="literal"><span class="pre">Logged</span></tt> was used and <tt class="literal"><span class="pre">ClassDecoratorLogged</span></tt> was never created.
+All this magic is in the <tt class="literal"><span class="pre">safetype.remove_redundant(*classes)</span></tt> function.</p>
+<p>The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. Probably, they do not matter unless one has to decorate
+thousands or millions of functions and classes.</p>
+<p>Finally, a word about bugs. The <tt class="literal"><span class="pre">decorators</span></tt> module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~200 tests, most of them extracted from the documentation you are reading),
+I cannot guarantee that it is correct. If somebody finds a bug or an
+unexpected behavior, please let me know and I will fix it or document it.</p>
+<!-- References: -->
+<div class="section" id="module-decorators">
+<h2><a class="toc-backref" href="#id15" name="module-decorators">Module <tt class="literal"><span class="pre">decorators</span></tt></a></h2>
+<p>A module to implement pep318 (decorator syntax) via magic doctrings.
+For the full documentation see
+<a class="reference" href="http://www.phyast.pitt.edu/~micheles/python/decorators.html">http://www.phyast.pitt.edu/~micheles/python/decorators.html</a> .</p>
+<div class="section" id="id3">
+<h3><a class="toc-backref" href="#id16" name="id3">Metaclasses</a></h3>
+<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">ClassDecorator(type,Decorator):</span></tt></p>
+<blockquote>
+Metaclass callable with one or three arguments, having its calling
+syntax redefined by <tt class="literal"><span class="pre">MetaDecorator</span></tt>.</blockquote>
+<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">Decorated(ClassDecorator):</span></tt></p>
+<blockquote>
+<p>Metaclass which decorates the methods of its instances according
+to the docstrings. It redefines <tt class="literal"><span class="pre">__str__</span></tt> on
+its instances and the default <tt class="literal"><span class="pre">__str__</span></tt> on the instances of its
+instances.</p>
+<p><tt class="literal"><span class="pre">__str__(cls):</span></tt></p>
+<blockquote>
+Redefine the printing representation of classes</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">safetype(type):</span></tt></p>
+<blockquote>
+Overrides the <tt class="literal"><span class="pre">__new__</span></tt> method of the <tt class="literal"><span class="pre">type</span></tt> metaclass, making the
+generation of classes conflict-proof.</blockquote>
+<p><tt class="literal"><span class="pre">metaclass</span> <span class="pre">MetaDecorator(type):</span></tt></p>
+<blockquote>
+<p>Metaclass inducing a certain amount of magic on decorators:</p>
+<ol class="arabic simple">
+<li>Each time a decorator is defined in any module, it is stored in
+MetaDecorator.dic and MetaDecorator.ls.</li>
+<li>If the (method) decorator has a 'get' method, a '__get__' method
+is automagically created as an alias to 'get'.</li>
+<li>Decorators calls are dispatched to the decorator _call_
+classmethod.</li>
+</ol>
+<p><tt class="literal"><span class="pre">__call__(dec,*args):</span></tt></p>
+<blockquote>
+This is the heart of the module. Infers the correct decorator class
+from <tt class="literal"><span class="pre">dec</span></tt> and the docstring and creates the correct decorator object.
+Returns the original object if <tt class="literal"><span class="pre">dec</span></tt> is the trivial <tt class="literal"><span class="pre">Decorator</span></tt>
+class and no docstring is found.</blockquote>
+<p><tt class="literal"><span class="pre">__init__(dec,*args):</span></tt></p>
+<blockquote>
+Update the metaclass attributes <tt class="literal"><span class="pre">dic</span></tt> and <tt class="literal"><span class="pre">ls</span></tt>;
+alias <tt class="literal"><span class="pre">get</span></tt> to <tt class="literal"><span class="pre">__get__</span></tt>.</blockquote>
+</blockquote>
+</div>
+<div class="section" id="classes">
+<h3><a class="toc-backref" href="#id17" name="classes">Classes</a></h3>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">UnknownDecoratorError(Exception):</span></tt></p>
+<blockquote>
+The name says it all.</blockquote>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">Decorator(object):</span></tt></p>
+<blockquote>
+Instance of MetaDecorator and mothers of all decorators. When called
+in the form <tt class="literal"><span class="pre">Decorator(obj)</span></tt> with obj having a magic docstring, it returns
+a decorated object; otherwise it returns the original object.</blockquote>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">staticmethod(MethodDecorator):</span></tt></p>
+<blockquote>
+Decorator, converts a function in a staticmethod</blockquote>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">MethodDecorator(Decorator):</span></tt></p>
+<blockquote>
+<p>Descriptor class callable with a function or a descriptor object
+as argument. It defines a default printing representation on method
+decorators objects and a default <tt class="literal"><span class="pre">get</span></tt> method. All the rest is
+provided by the metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>.</p>
+<p><tt class="literal"><span class="pre">get(self,obj,cls=None):</span></tt></p>
+<blockquote>
+aliased to __get__ by the metaclass, to be overridden</blockquote>
+<p><tt class="literal"><span class="pre">__str__(self):</span></tt></p>
+<blockquote>
+Printing representation of kind &lt;decoratorclass:functionname&gt;</blockquote>
+<p><tt class="literal"><span class="pre">__init__(self,objfunc):</span></tt></p>
+<blockquote>
+objfunc is a decorator object or a function</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">class</span> <span class="pre">classmethod(MethodDecorator):</span></tt></p>
+<blockquote>
+Decorator, converts a function in a classmethod</blockquote>
+</div>
+<div class="section" id="functions">
+<h3><a class="toc-backref" href="#id18" name="functions">Functions</a></h3>
+<p><tt class="literal"><span class="pre">enhance_classes(magicstring='[ClassDecorator]'):</span></tt></p>
+<blockquote>
+Enhances all the classes in the caller module with a metaclass
+built from the given magicstring.</blockquote>
+<p><tt class="literal"><span class="pre">remove_redundant(bases):</span></tt></p>
+<blockquote>
+<p>Returns a tuple of non-redundant base classes.
+Given a sequence of base classes, a class is redundant if it is duplicated
+or if it is implied by the others, i.e. it is an ancestor of at least one
+of the other classes. For instance, if <tt class="literal"><span class="pre">C</span></tt> is derived from <tt class="literal"><span class="pre">B</span></tt>, in the
+sequence <tt class="literal"><span class="pre">C,B</span></tt> the class <tt class="literal"><span class="pre">B</span></tt> is redundant, since all its features are
+already provided by <tt class="literal"><span class="pre">C</span></tt>. Therefore <tt class="literal"><span class="pre">B</span></tt>
+is removed and <tt class="literal"><span class="pre">remove_redundant</span></tt> returns the tuple <tt class="literal"><span class="pre">(C,)</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object): pass
+...
+&gt;&gt;&gt; class C(B): pass
+...
+&gt;&gt;&gt; import safetype; safetype.remove_redundant([C,B])
+(&lt;class 'C'&gt;,)
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">compose(dclasses,*defaultclasses):</span></tt></p>
+<blockquote>
+Retrieves or creates a decorator for a tuple of decorators. If
+defaults decorators are given, they get the precedence. It removes
+redundant classes.</blockquote>
+<p><tt class="literal"><span class="pre">instance_str(self):</span></tt></p>
+<blockquote>
+Redefines the printing representation of simple objects</blockquote>
+<p><tt class="literal"><span class="pre">getdec(magicstring=&quot;[Decorator]&quot;):</span></tt></p>
+<blockquote>
+Given a magicstring describing a valid decorator name, returns the list
+of its subclasses. By default returns the full list of decorators.</blockquote>
+<p><tt class="literal"><span class="pre">decorate(objdict):</span></tt></p>
+<blockquote>
+Takes an object with a dictionary and decorates all its functions
+and classes according to their docstrings.</blockquote>
+<p><tt class="literal"><span class="pre">decorators_in(docstring):</span></tt></p>
+<blockquote>
+Takes a docstring and returns a (possibly empty) tuple of decorator
+classes.</blockquote>
+</div>
+</div>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="decorators.txt">View document source</a>.
+Generated on: 2003-09-30 10:07 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/decorators.py b/pypers/pep318/decorators.py
new file mode 100755
index 0000000..738b403
--- /dev/null
+++ b/pypers/pep318/decorators.py
@@ -0,0 +1,184 @@
+# decorators.py
+
+"""
+A module to implement pep318 (decorator syntax) via magic doctrings.
+For the full documentation see
+http://www.phyast.pitt.edu/~micheles/python/decorators.html .
+"""
+
+import sys, re, inspect
+from types import FunctionType,ClassType
+from safetype import remove_redundant,__type__,safetype as type
+
+#from printerr import printerr
+
+############################ UTILITIES ##############################
+
+MAGICDOC=re.compile(r'\s*\[([\w_\s,]*)\]')
+#regexp to recognize magic docstrings
+
+class UnknownDecoratorError(Exception):
+ "The name says it all."
+
+class MetaDecorator(type):
+ """Metaclass inducing a certain amount of magic on decorators:
+
+ 1. each time a decorator class is defined in any module, it is stored in
+ MetaDecorator.dic and MetaDecorator.ls;
+ 2. if the decorator class has a 'get' method, a '__get__' method
+ is automagically created as an alias to 'get';
+ 3. decorators calls are dispatched to the decorator _call_
+ classmethod.
+ The net effect is that the call decoratorclass(...) returns an
+ instance of a *subclass* of decoratorclass.
+ """
+
+ dic,ls={},[]
+
+ def __init__(dec,*args):
+ super(MetaDecorator,dec).__init__(*args)
+ MetaDecorator.dic[dec.__name__]=dec
+ MetaDecorator.ls.append(dec)
+ get=dec.__dict__.get('get') # look at get
+ if get: dec.__get__=get # alias __get__ to get
+
+ def __call__(dec,*args):
+ """This is the heart of the module. Infers the correct decorator class
+ from ``dec`` and the docstring and creates the correct decorator
+ object. Returns the original object if ``dec`` is the trivial
+ ``Decorator`` class and no docstring is found."""
+ nargs=len(args)
+ if nargs==1: # simple call of kind dec(obj)
+ obj=args[0]
+ if not isinstance( # not method decorator, function or class
+ obj,(MethodDecorator,FunctionType,ClassType,__type__)):
+ return # do nothing
+ elif isinstance(obj,decorator): # compose with obj.__class__ too
+ dec=compose(decorators_in(obj.__doc__), dec, obj.__class__)
+ else: # compose with dec only
+ dec=compose(decorators_in(obj.__doc__), dec)
+ elif nargs==3: # class decorator called with three arguments
+ dec=compose(decorators_in(args[2].get('__doc__')),dec)
+ return dec._call_(*args) # dispatch to the correct _call_ classmethod
+
+def compose(dclasses,*defaultclasses):
+ """Retrieves or creates a decorator for a tuple of decorators. If
+ defaults decorators are given, they get the precedence. It removes
+ redundant classes."""
+ dclasses=remove_redundant(defaultclasses+dclasses)
+ decname=''.join([d.__name__ for d in dclasses])
+ dec=MetaDecorator.dic.get(decname)
+ if not dec: dec=type(decname,dclasses,{})
+ return dec
+
+def decorators_in(docstring):
+ """Takes a docstring and returns a (possibly empty) tuple of decorator
+ classes."""
+ match=MAGICDOC.match(docstring or '')
+ if not match: return ()
+ decnames=map(str.strip,match.group(1).split(',')) # get the names
+ try: dclasses=[MetaDecorator.dic[n] for n in decnames] # get the decorators
+ except KeyError: raise UnknownDecoratorError(n)
+ return tuple(dclasses)
+
+def decorate(objdict):
+ """Takes an object with a dictionary and decorates all its functions
+ and classes according to their docstrings."""
+ for name,obj in objdict.__dict__.items(): # works for classes too
+ #if getattr(obj,'__doc__',None): # non-empty docstring
+ dec_obj=decorator(obj)
+ if dec_obj:
+ setattr(dec_obj,'__klass__',objdict)
+ setattr(objdict,name,dec_obj)
+
+def getdec(magicstring="[decorator]"):
+ """Given a magicstring describing a valid decorator name, returns the list
+ of its subclasses. By default returns the full list of decorators."""
+ dec=compose(decorators_in(magicstring))
+ isdecorator=lambda x: issubclass(x,dec)
+ return filter(isdecorator,MetaDecorator.ls)
+
+def instance_str(self):
+ "Redefines the printing representation of simple objects"
+ return "<%s instance>" % self.__class__.__name__
+
+####################### BASIC DECORATORS ###########################
+
+class decorator(object):
+ """Instance of MetaDecorator and mothers of all decorators. When called
+ in the form ``decorator(obj)`` with obj having a magic docstring, it
+ returns a decorated object; otherwise it returns ``None``."""
+ __metaclass__=MetaDecorator
+ def _call_(dec,obj):
+ "Returns None, all the interesting stuff is in MetaDecorator.__call__"
+ _call_=classmethod(_call_)
+
+class MethodDecorator(decorator):
+ """Descriptor class callable with a function or a descriptor object
+ as argument. It defines a default printing representation on method
+ decorators objects and a default ``get`` method. All the rest is
+ provided by the metaclass ``MetaDecorator``.
+ """
+ __klass__=type('?',(),{}) # dummy definition class, to be overridden
+ def __init__(self,objfunc):
+ "objfunc is a decorator object or a function"
+ assert isinstance(objfunc,(FunctionType,decorator))
+ super(MethodDecorator,self).__init__(objfunc)
+ self.__func__=getattr(objfunc,'__func__',objfunc)
+ def get(self,obj,cls=None):
+ "aliased to __get__ by the metaclass, to be overridden"
+ return self.__func__.__get__(obj,cls)
+ def __str__(self):
+ "Printing representation of kind <decoratorclass:functionname>"
+ return '<%s:%s>' % (self.__class__.__name__,self.__func__.__name__)
+ def _call_(dec,obj):
+ "Returns a method decorator object."
+ return type.__call__(dec,obj) # calls __new__ and __init__
+ _call_=classmethod(_call_)
+
+class ClassDecorator(type,decorator):
+ """Metaclass callable with one or three arguments, having its calling
+ syntax redefined by ``MetaDecorator``."""
+ def _call_(dec,*args):
+ "Returns a decorated class"
+ a=args[0] # first argument; must be a string or a class
+ if inspect.isclass(a): args=a.__name__,a.__bases__,a.__dict__.copy()
+ return type.__call__(dec,*args) # calls __new__ and __init__
+ _call_=classmethod(_call_)
+
+class Decorated(ClassDecorator):
+ """Metaclass which decorates the methods of its instances according
+ to the docstrings. It redefines ``__str__`` on
+ its instances and the default ``__str__`` on the instances of its
+ instances."""
+ def __init__(cls,name,bases,dic):
+ super(Decorated,cls).__init__(name,bases,dic)
+ if cls.__str__ is object.__str__: cls.__str__=instance_str
+ # redefine default __str__
+ decorate(cls)
+ def __str__(cls):
+ "Redefine the printing representation of classes"
+ return '<class %s[%s]>' % (cls.__name__,cls.__class__.__name__)
+
+#################### Built-in Method decorators ######################
+
+class staticmethod(MethodDecorator):
+ "Decorator, converts a function in a staticmethod"
+ def get(self,obj,cls=None):
+ return super(staticmethod,self).get(obj,cls).im_func
+
+class classmethod(MethodDecorator):
+ "Decorator, converts a function in a classmethod"
+ def get(self,obj,cls=None):
+ if cls is None: cls=type(obj)
+ return super(classmethod,self).get(cls,cls)
+
+#################### functions of the API #################################
+
+def enhance_classes(magicstring='[ClassDecorator]'):
+ """Enhances all the classes in the caller module with a metaclass
+ built from the given magicstring."""
+ callerglobals=sys._getframe(1).f_globals
+ dec=compose(decorators_in(magicstring))
+ callerglobals['__metaclass__']=dec
+ callerglobals['object']=dec('object',(),{})
diff --git a/pypers/pep318/decorators.tex b/pypers/pep318/decorators.tex
new file mode 100755
index 0000000..7dbc220
--- /dev/null
+++ b/pypers/pep318/decorators.tex
@@ -0,0 +1,1733 @@
+\documentclass[11pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper,margin=2cm]{geometry}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
+\newenvironment{optionlist}[1]
+{\begin{list}{}
+ {\setlength{\labelwidth}{#1}
+ \setlength{\rightmargin}{1cm}
+ \setlength{\leftmargin}{\rightmargin}
+ \addtolength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}{\optionlistlabel}}
+}{\end{list}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{/mnt/exp/MyDocs/pypers/style.tex}
+\title{Implementing PEP 318 (decorators)}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Implementing PEP 318 (decorators)},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Module}: &
+ decorators \\
+\textbf{Version}: &
+ 0.5 \\
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{e-mail}: &
+ MicheleSimionato@libero.it \\
+\textbf{Licence}: &
+ Python-like \\
+\textbf{Date}: &
+ September 2003 \\
+\textbf{Disclaimer}: &
+ This is experimental code. Use it at your own risk! \\
+\end{tabularx}
+\end{center}
+
+
+
+
+\hypertarget{contents}{}\subsection*{~\hfill Contents\hfill ~}
+\pdfbookmark[0]{Contents}{contents}
+\begin{list}{}{}
+\item \href{#using-decorators}{Using decorators}
+\begin{list}{}{}
+\item \href{#basics}{Basics}
+
+\item \href{#decorating-methods}{Decorating methods}
+
+\item \href{#decorating-classes}{Decorating classes}
+
+\item \href{#adding-magic}{Adding magic}
+
+\item \href{#defining-method-decorators}{Defining method decorators}
+
+\item \href{#defining-class-decorators}{Defining class decorators}
+
+\item \href{#composing-decorators}{Composing decorators}
+
+\item \href{#diving-into-magic}{Diving into magic}
+
+\item \href{#advanced-usage}{Advanced usage}
+
+\end{list}
+
+\item \href{#the-implementation}{The implementation}
+\begin{list}{}{}
+\item \href{#module-decorators}{Module \texttt{decorators}}
+\begin{list}{}{}
+\item \href{#id3}{Metaclasses}
+
+\item \href{#classes}{Classes}
+
+\item \href{#functions}{Functions}
+
+\end{list}
+
+\end{list}
+
+\end{list}
+
+
+
+%___________________________________________________________________________
+
+\hypertarget{using-decorators}{}
+\section*{Using decorators}
+\pdfbookmark[0]{Using decorators}{using-decorators}
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of \href{http://www.python.org/pep}{PEP 318} in pure Python
+(2.3).
+
+Here is the rationale:
+\begin{itemize}
+\item
+some kind of decorator syntax is scheduled to go in Python 2.4,
+therefore it is interesting to play with the concept;
+
+\item
+it is nice to play with decorators now, without having to
+wait for one year or so;
+
+\item
+it is much easier to experiment with a pure Python implementation
+than with a C implementation;
+
+\item
+the implementation can be seen as an exercise on modern Python
+programming and may be valuable to people wanting to study the most
+advanced new constructs in Python (\href{http://users.rcn.com/python/download/Descriptor.htm}{descriptors}, \href{http://www-106.ibm.com/developerworks/library/l-pymeta2.html}{metaclasses},
+\href{http://www.python.org/2.3/descrintro.html}{cooperative methods}, etc.)
+
+\end{itemize}
+
+
+%___________________________________________________________________________
+
+\hypertarget{basics}{}
+\subsection*{Basics}
+\pdfbookmark[1]{Basics}{basics}
+
+PEP 318 has the goal of providing a nice syntactic sugar for expressions like
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~identity(x):}\\
+\mbox{~~~~return~x}\\
+\mbox{identity=staticmethod(identity)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+or
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~name(cls):}\\
+\mbox{~~~return~cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{name=classmethod(name)}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~identity(x)[staticmethod]:}\\
+\mbox{~~~~return~x}\\
+\mbox{}\\
+\mbox{def~name(cls)[classmethod]:}\\
+\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~identity(x):}\\
+\mbox{~~~~"[staticmethod]"}\\
+\mbox{~~~~return~x}\\
+\mbox{}\\
+\mbox{def~name(cls):}\\
+\mbox{~~~~"[classmethod]"}\\
+\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The implementation is able to recognize such magic docstrings
+and automatically converts methods with magic docstrings in
+method decorators.
+
+Decorators are nothing else than a sophisticated kind of wrappers.
+The \texttt{decorators} module provides support both for method decorators
+and class decorators:
+\begin{itemize}
+\item
+\emph{Method decorators} are classes taking a single function as input and
+producing a descriptor object as output. \texttt{staticmethod} and
+\texttt{classmethod} are two examples of already existing
+method decorators (actually my implementation rewrites them).
+A knowledge of descriptors [\hyperlink{id2}{1}] is not needed in order to use the \texttt{decorator}
+module; however it is welcomed for advanced users wanting to implement
+custom method decorators.
+
+\item
+\emph{Class decorators} are metaclasses taking a class as imput and returning
+a decorated class as output. A good understanding of metaclasses is needed
+in order to be able to write custom class decorators, but no knowledge
+at all is required in order to use the pre-defined class decorators
+provided by the module.
+
+\end{itemize}
+
+Notice that properties are not decorators according to my definitions,
+since they take four functions as input, \texttt{get, set, del{\_}} and \texttt{doc}.
+Whereas the decorators concept could be generalized to the case of
+multiple inputs, I don't see the need for such complication, so
+properties are not implemented as method decorators. Moreover, I
+think it is much better to use them trough a class decorator.
+
+Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the documentation short.
+
+Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.
+\begin{figure}[b]\hypertarget{id2}[1]
+Descriptors are objects with a \texttt{{\_}{\_}get{\_}{\_}} method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip on this point ;).
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{decorating-methods}{}
+\subsection*{Decorating methods}
+\pdfbookmark[1]{Decorating methods}{decorating-methods}
+
+Before talking about the implementation details, I will show
+how the \texttt{decorators} module works in practice. The simplest and safest
+usage is by means of the \texttt{decorators.decorated()} function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using \texttt{decorators.decorated()}
+is simple but verbose, so magic shortcuts will be discussed in the next
+sections.
+
+Here, let me give an example, showing that method decorators work both for
+\href{http://www.python.org/2.3/descrintro.html}{new style classes and old style classes}:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example1.py{$>$}}\\
+\mbox{}\\
+\mbox{import~decorators}\\
+\mbox{}\\
+\mbox{def~do{\_}nothing(self):}\\
+\mbox{~~~"No~magic~docstring~here"}\\
+\mbox{dec{\_}do{\_}nothing=decorators.decorated(do{\_}nothing)}\\
+\mbox{}\\
+\mbox{def~identity(x):}\\
+\mbox{~~~~"[staticmethod]"}\\
+\mbox{~~~~return~x}\\
+\mbox{dec{\_}identity=decorators.decorated(identity)~}\\
+\mbox{}\\
+\mbox{def~name(cls):}\\
+\mbox{~~~~"[classmethod]"}\\
+\mbox{~~~~return~cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{dec{\_}name=decorators.decorated(name)}\\
+\mbox{}\\
+\mbox{class~OldStyle:}\\
+\mbox{~~~~do{\_}nothing=dec{\_}do{\_}nothing}\\
+\mbox{~~~~identity=dec{\_}identity}\\
+\mbox{}\\
+\mbox{class~NewStyle(object):}\\
+\mbox{~~~~name=dec{\_}name}\\
+\mbox{}\\
+\mbox{o=OldStyle()~{\#}~creates~an~old~style~instance}\\
+\mbox{n=NewStyle()~{\#}~creates~a~new~style~instance}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example1.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+\begin{verbatim}>>> from example1 import * # for testing purposes\end{verbatim}
+
+In this example, both \texttt{dec{\_}identity} and \texttt{dec{\_}name} are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects in the interpreter, since they have a re-defined
+printing representation:
+\begin{verbatim}>>> print dec_identity
+<staticmethod:identity>
+>>> print dec_name
+<classmethod:name>\end{verbatim}
+
+On the other hand, \texttt{do{\_}nothing} does not have a magic
+docstring, therefore it is not converted to a decorator object;
+actually it is \emph{exactly} the original function
+\begin{verbatim}>>> dec_do_nothing is do_nothing # not converted
+True\end{verbatim}
+
+and it works as a standard method:
+\begin{verbatim}>>> o.do_nothing() # does nothing, correct\end{verbatim}
+
+On the contrary, \texttt{dec{\_} identity} works as a staticmethod,
+\begin{verbatim}>>> OldStyle.identity(1) # called from the class
+1
+>>> o.identity(1) # called from the instance
+1\end{verbatim}
+
+whereas \texttt{dec{\_}name} works as a classmethod:
+\begin{verbatim}>>> NewStyle.name() # called from the class
+'NewStyle'
+>>> n.name() # called from the instance
+'NewStyle'\end{verbatim}
+
+Notice that, I have re-implemented the built-in
+\texttt{staticmethod} and \texttt{classmethod}, so
+\begin{verbatim}>>> isinstance(dec_identity,staticmethod)
+False\end{verbatim}
+
+and
+\begin{verbatim}>>> isinstance(dec_name,classmethod)
+False\end{verbatim}
+
+It is possible to recognize method decorators since they provides
+a couple of special attributes:
+\begin{itemize}
+\item
+\texttt{{\_}{\_}func{\_}{\_}}: returning the function from which they originated:
+\begin{verbatim}>>> assert dec_identity.__func__ is identity
+>>> assert dec_name.__func__ is name\end{verbatim}
+
+\item
+\texttt{{\_}{\_}klass{\_}{\_}}: returning the class where they where defined:
+\begin{verbatim}>>> dec_identity.__klass__
+<class 'safetype.?'>\end{verbatim}
+
+\end{itemize}
+
+The question mark here means that the definition class is unknown.
+The \texttt{{\_}{\_}klass{\_}{\_}} attribute can be set by hand or automatically
+with the trick explained in the next section.
+
+
+%___________________________________________________________________________
+
+\hypertarget{decorating-classes}{}
+\subsection*{Decorating classes}
+\pdfbookmark[1]{Decorating classes}{decorating-classes}
+
+The problem with the approach just described
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically decorating their
+methods according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with ``[Decorated]''
+and by decorating the class.
+Here is an example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example2.py{$>$}}\\
+\mbox{}\\
+\mbox{from~decorators~import~decorated}\\
+\mbox{from~example1~import~do{\_}nothing,identity,name}\\
+\mbox{}\\
+\mbox{class~B(object):}\\
+\mbox{~~~~"This~is~a~regular~class"}\\
+\mbox{}\\
+\mbox{B=decorated(B)~{\#}~does~nothing}\\
+\mbox{}\\
+\mbox{class~C(B):}\\
+\mbox{~~~"[Decorated]"}\\
+\mbox{~~~do{\_}nothing=do{\_}nothing}\\
+\mbox{~~~identity=identity}\\
+\mbox{~~~class~Inner:~{\#}~old~style~class}\\
+\mbox{~~~~~~~"[Decorated]"~{\#}~required~docstring~~~}\\
+\mbox{~~~~~~~name=name}\\
+\mbox{}\\
+\mbox{C=decorated(C)~{\#}~regenerates~the~class~converting~methods~in~decorators}\\
+\mbox{c=C()}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example2.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Under the hood \texttt{decorators.decorated()} recognizes the class level
+magic docstring ``[Decorated]'' and creates an instance of the
+\texttt{decorators.Decorated} metaclass.
+Internally the metaclass invokes \texttt{decorators.decorated()}
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. Moreover, it decorates inner classes,
+if a suitable docstring is found, and it works recursively.
+
+Here is the testing:
+\begin{verbatim}>>> from example2 import *
+>>> assert C.identity(1) == 1
+>>> assert C.Inner.name() == 'Inner'
+>>> assert c.identity(1) == 1
+>>> assert c.Inner.name() == 'Inner' \end{verbatim}
+
+Notice that adding \texttt{identity} after the class creation with the syntax
+\texttt{C.identity=identity} would not work;
+\texttt{C.identity=decorators.decorated(identity)} is required:
+\begin{verbatim}>>> C.identity=decorators.decorated(identity)
+>>> C.identity(1) # it works
+1\end{verbatim}
+
+If a class misses the magic docstring, nothing happens:
+\begin{verbatim}>>> B # returns the original B
+<class 'example2.B'>\end{verbatim}
+
+The mechanism works for old style classes too,
+since the metaclass automagically converts the old style classes in a
+new style one:
+\begin{verbatim}>>> type(C.Inner) # C.Inner is an instance of decorator.Decorated
+<class 'decorators.Decorated'>\end{verbatim}
+
+The enhancement provided by the metaclass includes a new default
+printing representation for both the class
+\begin{verbatim}>>> print C # returns the name of D and of its metaclass
+<class C[Decorated]>\end{verbatim}
+
+and its instances:
+\begin{verbatim}>>> print c
+<C instance>\end{verbatim}
+
+On the other hand, if a custom printing representation is already
+defined, the metaclass takes it without any change.
+
+One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:
+\begin{verbatim}>>> class D(C):
+... def name(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print D.name()
+D\end{verbatim}
+
+Decorating whole classes presents another advantage: the decorated methods know
+the class where they were defined via the special attribute \texttt{{\_}{\_}klass{\_}{\_}}:
+\begin{verbatim}>>> D.__dict__['name'].__klass__ # the class where 'name' is defined
+<class 'D'>\end{verbatim}
+
+This is useful for introspection and debugging purposes.
+
+
+%___________________________________________________________________________
+
+\hypertarget{adding-magic}{}
+\subsection*{Adding magic}
+\pdfbookmark[1]{Adding magic}{adding-magic}
+
+The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking \texttt{decorators.decorated()}
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing \texttt{decorators.enhance{\_}classes()}
+on top of you module. Then all methods in all classes with a magic docstring
+will be checked for magic docstrings and automagically decorated if needed.
+For instance, the previous example would be written
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example3.py{$>$}}\\
+\mbox{}\\
+\mbox{import~decorators;~decorators.enhance{\_}classes()}\\
+\mbox{}\\
+\mbox{class~C:}\\
+\mbox{~~~~"[Decorated]"~{\#}~magic~docstring~here~}\\
+\mbox{~~~~def~do{\_}nothing(self):}\\
+\mbox{~~~~~~~"No~magic~docstring~here"}\\
+\mbox{}\\
+\mbox{~~~~def~identity(x):}\\
+\mbox{~~~~~~~~"[staticmethod]"}\\
+\mbox{~~~~~~~~return~x}\\
+\mbox{}\\
+\mbox{class~D(object):}\\
+\mbox{~~~~"Undecorated"~{\#}~no~magic~docstring~here}\\
+\mbox{~~~~def~name(cls):}\\
+\mbox{~~~~~~~~"[classmethod]"}\\
+\mbox{~~~~~~~~return~cls.{\_}{\_}name{\_}{\_}}\\
+\mbox{}\\
+\mbox{c=C();~d=D()}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example3.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+\texttt{C} has a \texttt{[Decorated]} docstring, so its methods
+are automatically decorated:
+\begin{verbatim}>>> from example3 import *
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert c.identity(1) == 1 \end{verbatim}
+
+On the other hand, since \texttt{D} misses a magic docstring,
+its \texttt{name} method is not decorated:
+\begin{verbatim}>>> hasattr(D.__dict__['name'],'__func__') # not a decorator
+False\end{verbatim}
+
+Since \texttt{D.name} is a regular method and not a classmethod, \texttt{D.name()}
+gives an error:
+\begin{verbatim}>>> D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance
+as first argument (got nothing instead)\end{verbatim}
+
+Under the hood, the magic works by enhancing the \texttt{object} class
+of the module with a \texttt{decorators.ClassDecorator} metaclass:
+\begin{verbatim}>>> import example4
+>>> type(example4.object)
+<class 'decorators.ClassDecorator'>\end{verbatim}
+
+Notice that for safety reasons the enhancement is only on the module
+\texttt{object} class, not on the \texttt{{\_}{\_}builtin{\_}{\_}.object} class. The dangers of
+adding too much magic are discussed in the \href{#diving-into-magic}{Diving into magic} section.
+
+
+%___________________________________________________________________________
+
+\hypertarget{defining-method-decorators}{}
+\subsection*{Defining method decorators}
+\pdfbookmark[1]{Defining method decorators}{defining-method-decorators}
+
+The \texttt{decorators} module contains two predefinite method decorators,
+\texttt{staticmethod} and \texttt{classmethod}, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The \texttt{decorators.MethodDecorator} class which is here
+exactly for that purpose.
+
+Custom decorators are expected to be implemented by subclassing
+\texttt{MethodDecorator} and by overriding its \texttt{get} method. The
+\texttt{get} method automagically induces a \texttt{{\_}{\_}get{\_}{\_}} method, turning the
+class in a descriptor. The machinery is needed since \texttt{{\_}{\_}get{\_}{\_}} cannot
+be made cooperative using the standard \texttt{super} mechanism because
+there would be a confusion between \texttt{super.{\_}{\_}get{\_}{\_}} and the decorator
+\texttt{{\_}{\_}get{\_}{\_}}. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators \emph{too} easy, since they are potentially dangerous.
+
+In order to give a simple example, let me show the implementation
+of a \texttt{chattymethod} that prints a message when it is called:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}customdec.py{$>$}}\\
+\mbox{}\\
+\mbox{from~decorators~import~*~~}\\
+\mbox{}\\
+\mbox{class~chattymethod(MethodDecorator):}\\
+\mbox{~~~~logfile=sys.stdout~{\#}~default}\\
+\mbox{~~~~def~get(self,obj,cls=None):~{\#}~same~signature~as~{\_}{\_}get{\_}{\_}}\\
+\mbox{~~~~~~~~self.logfile.write('calling~{\%}s~from~{\%}s{\textbackslash}n'~{\%}~(self,obj~or~cls))}\\
+\mbox{~~~~~~~~return~super(chattymethod,self).get(obj,cls)}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/customdec.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice the usage of the \texttt{super().get} trick. This guarantees that
+\texttt{chattymethod} will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.
+
+Here is an example of usage:
+\begin{verbatim}>>> import customdec # adds chattymethod to the list of known decorators
+>>> customdec.enhance_classes() # automagically enhances classes
+>>> class C:
+... " [Decorated] "
+... def f(self):
+... """
+... [ chattymethod ]
+... """
+>>> c=C()
+>>> c.f()
+calling <chattymethod:f> from <C instance>\end{verbatim}
+
+By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.
+
+One can check that the syntax \texttt{C.f(c)} works too:
+\begin{verbatim}>>> C.f(c)
+calling <chattymethod:f> from <class C[Decorated]>\end{verbatim}
+
+A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{def~f(self)[chattymethod(logfile=file('file1.log','w'))]}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In my view, there are too many parenthesis in this syntax, and it
+becomes rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the \texttt{decorators} module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.
+
+A simple minded solution is to change the defaults by hand:
+\begin{verbatim}>>> from customdec import chattymethod,decorated
+>>> chattymethod.logfile=file('file.log','w')
+>>> def g(self):
+... "[chattymethod]"
+>>> C.g=decorated(g)
+>>> c.g() # will print a message on file.log\end{verbatim}
+
+This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore
+\begin{verbatim}>>> c.f()\end{verbatim}
+
+will print a message to \texttt{file.log} too, and not to standard output.
+Here is the confirmation:
+\begin{verbatim}>>> chattymethod.logfile.close()
+>>> print file('file.log').read().rstrip()
+calling <chattymethod:g> from <C instance>
+calling <chattymethod:f> from <C instance>\end{verbatim}
+
+A better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the \texttt{{\_}{\_}init{\_}{\_}} method. Here is an example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}customdec.py{$>$}}\\
+\mbox{}\\
+\mbox{class~chattymethod2(chattymethod):~}\\
+\mbox{~~~~logfile=sys.stdout~{\#}~default}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,objfunc):}\\
+\mbox{~~~~~~~~super(chattymethod2,self).{\_}{\_}init{\_}{\_}(objfunc)}\\
+\mbox{~~~~~~~~logfile=getattr(self.{\_}{\_}func{\_}{\_},'logfile',None)}\\
+\mbox{~~~~~~~~if~logfile:~self.logfile=logfile~}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/customdec.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Notice that the \texttt{{\_}{\_}init{\_}{\_}} method has the signature
+\texttt{{\_}{\_}init{\_}{\_}(self,objfunc)}, where the \texttt{objfunc} object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding \texttt{{\_}{\_}init{\_}{\_}}.
+
+Here is the testing:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}chatty2.py{$>$}}\\
+\mbox{}\\
+\mbox{import~customdec;~customdec.enhance{\_}classes()}\\
+\mbox{}\\
+\mbox{{\#}~sets~the~log~files}\\
+\mbox{log1=file('file1.log','w')}\\
+\mbox{log2=file('file2.log','w')}\\
+\mbox{}\\
+\mbox{class~C:}\\
+\mbox{~~~~"[Decorated]"}\\
+\mbox{~~~~def~f(self):~}\\
+\mbox{~~~~~~~~"[chattymethod2]"}\\
+\mbox{~~~~f.logfile=log1~{\#}~function~attribute}\\
+\mbox{~~~~def~g(self):~}\\
+\mbox{~~~~~~~~"[chattymethod2]"}\\
+\mbox{~~~~g.logfile=log2~{\#}~function~attribute}\\
+\mbox{}\\
+\mbox{assert~C.{\_}{\_}dict{\_}{\_}['f'].logfile~is~log1~{\#}~check~the~conversion~}\\
+\mbox{assert~C.{\_}{\_}dict{\_}{\_}['g'].logfile~is~log2~{\#}~function~attr.~-{$>$}~decorator~attr.}\\
+\mbox{}\\
+\mbox{c=C()~{\#}~C~instantiation}\\
+\mbox{}\\
+\mbox{c.f()~{\#}~print~a~message~in~file1.log}\\
+\mbox{c.g()~{\#}~print~a~message~in~file2.log}\\
+\mbox{}\\
+\mbox{log1.close();~log2.close()~{\#}~finally}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/chatty2.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Let me check the contents of the log files:
+\begin{verbatim}>>> import chatty2
+>>> print file('file1.log').read().rstrip()
+calling <chattymethod2:f> from <C instance>
+>>> print file('file2.log').read().rstrip()
+calling <chattymethod2:g> from <C instance>\end{verbatim}
+
+\texttt{chattymethod} is the poor man version of \texttt{tracedmethod}, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}customdec.py{$>$}}\\
+\mbox{}\\
+\mbox{class~tracedmethod(MethodDecorator):}\\
+\mbox{~~~~"Descriptor~class,~converts~a~method~in~a~traced~method"}\\
+\mbox{~~~~indent=0;~output=sys.stdout~{\#}~defaults}\\
+\mbox{}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self,objfunc):}\\
+\mbox{~~~~~~~~super(tracedmethod,self).{\_}{\_}init{\_}{\_}(objfunc)}\\
+\mbox{~~~~~~~~self.funcname=self.{\_}{\_}func{\_}{\_}.{\_}{\_}name{\_}{\_}}\\
+\mbox{~~~~~~~~output=getattr(self.{\_}{\_}func{\_}{\_},'output',None)~}\\
+\mbox{~~~~~~~~if~output:~self.output=output~{\#}~func.attr.~-{$>$}~dec.attr.}\\
+\mbox{}\\
+\mbox{~~~~def~get(self,obj,cls):}\\
+\mbox{~~~~~~~~clsname=self.{\_}{\_}klass{\_}{\_}.{\_}{\_}name{\_}{\_}~{\#}~definition~clas}\\
+\mbox{~~~~~~~~def~tracedmeth(obj,*args,**kw):}\\
+\mbox{~~~~~~~~~~~~i='~'*self.indent~{\#}~default~indentation}\\
+\mbox{~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.indent+=4~{\#}~increases~indentation}\\
+\mbox{~~~~~~~~~~~~self.output.write("{\%}sCalling~'{\%}s.{\%}s'~with~arguments~"~{\%}~}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(i,clsname,self.funcname))}\\
+\mbox{~~~~~~~~~~~~self.output.write("{\%}s{\%}s~...{\textbackslash}n"~{\%}~(obj~or~'',str(args)+str(kw)))}\\
+\mbox{~~~~~~~~~~~~res=super(tracedmethod,self).get(obj,cls)(*args,**kw)}\\
+\mbox{~~~~~~~~~~~~self.output.write("{\%}s'{\%}s.{\%}s'~called~with~result:~{\%}s{\textbackslash}n"}\\
+\mbox{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\%}~(i,clsname,self.funcname,res))}\\
+\mbox{~~~~~~~~~~~~self.{\_}{\_}class{\_}{\_}.indent-=4~{\#}~restores~default~indentation}\\
+\mbox{~~~~~~~~~~~~return~res}\\
+\mbox{~~~~~~~~return~tracedmeth.{\_}{\_}get{\_}{\_}(obj,cls)~{\#}~method~wrapper}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/customdec.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+\texttt{tracedmethod.get} returns a method wrapper object, so it is
+possible to use \texttt{im{\_}func} to retrieve the internal function
+\texttt{tracedmeth}:
+\begin{verbatim}>>> class C:
+... "[Decorated]"
+... def f(self):
+... "[tracedmethod]"
+>>> c=C(); c.f()
+Calling 'C.f' with arguments <C instance>(){} ...
+'C.f' called with result: None
+>>> c.f.im_func.__name__
+'tracedmeth'\end{verbatim}
+
+As soon as the \texttt{tracedmethod} module is loaded, the \texttt{tracedmethod} class
+is added to the list of know decorators, so one should use the
+``[tracedmethod]'' docstring and not something like
+``[customdec.tracedmethod]''.
+
+Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example9.py{$>$}}\\
+\mbox{}\\
+\mbox{import~customdec;~customdec.enhance{\_}classes()}\\
+\mbox{}\\
+\mbox{logfile=file('file3.log','w')}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~"[Decorated]"}\\
+\mbox{~~~~def~fact(self,n):}\\
+\mbox{~~~~~~~~"[tracedmethod]~The~good~old~factorial."}\\
+\mbox{~~~~~~~~if~n==0:~return~1}\\
+\mbox{~~~~~~~~else:~return~n*self.fact(n-1)}\\
+\mbox{~~~~fact.output=logfile}\\
+\mbox{}\\
+\mbox{C().fact(2)~{\#}~write~a~message~to~logfile}\\
+\mbox{}\\
+\mbox{logfile.close()}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example9.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is the content of \texttt{file3.log}:
+\begin{verbatim}>>> import example9 # creates file3.log
+>>> print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments <C instance>(2,){} ...
+ Calling 'C.fact' with arguments <C instance>(1,){} ...
+ Calling 'C.fact' with arguments <C instance>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{defining-class-decorators}{}
+\subsection*{Defining class decorators}
+\pdfbookmark[1]{Defining class decorators}{defining-class-decorators}
+
+PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a couple of
+class decorator at work, the metaclass \texttt{ClassDecorator}, which gives
+to its instances the ability to interpret class level magic docstrings, and
+its subclass \texttt{Decorated}, which converts functions in method decorators
+if suitable docstrings are found.
+
+To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference of deriving from \texttt{ClassDecorator} instead
+of deriving from \texttt{type}.
+
+To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}customdec.py{$>$}}\\
+\mbox{}\\
+\mbox{class~Logged(ClassDecorator):}\\
+\mbox{~~~~output=sys.stdout}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,name,bases,dic):}\\
+\mbox{~~~~~~~~super(Logged,cls).{\_}{\_}init{\_}{\_}(name,bases,dic)}\\
+\mbox{~~~~~~~~print~{$>$}{$>$}~cls.output,"{\%}s~created"~{\%}~cls}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/customdec.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+\texttt{Logged} is derived by the metaclass \texttt{ClassDecorator},
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+(meta-)metaclass \texttt{MetaDecorator}).
+Logging capabilities can be added to a class
+by simply using the magic docstring syntax:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}logged.py{$>$}}\\
+\mbox{}\\
+\mbox{import~customdec;~customdec.enhance{\_}classes()}\\
+\mbox{}\\
+\mbox{class~D(object):~{\#}~will~print~a~message~at~D~creation}\\
+\mbox{~~~~"[Logged]"}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/logged.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+\begin{verbatim}>>> import logged
+<class 'logged.D'> created\end{verbatim}
+
+Notice that the printing representation of \texttt{D} involves the name
+of \texttt{D} preceded by the name of its metaclass, which in this case
+is \texttt{Logged}
+
+Each time an instance of \texttt{Logged} is created, a similar message is printed:
+\begin{verbatim}>>> class E(logged.D):
+... pass
+<class 'E'> created\end{verbatim}
+
+Notice that \texttt{E} does not have any magic docstring
+\begin{verbatim}>>> E.__doc__ # no docstring\end{verbatim}
+
+but still it inherits its magic from \texttt{D}.
+
+Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}customdec.py{$>$}}\\
+\mbox{}\\
+\mbox{from~types~import~FunctionType~~}\\
+\mbox{}\\
+\mbox{class~Traced(Decorated):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(cls,n,b,d):}\\
+\mbox{~~~~~~~~for~name,func~in~d.iteritems():}\\
+\mbox{~~~~~~~~~~~~if~isinstance(func,FunctionType)~and~name!='{\_}{\_}str{\_}{\_}':~}\\
+\mbox{~~~~~~~~~~~~~~~~{\#}~cannot~trace~{\_}{\_}str{\_}{\_},~since~it~is~invoked~by}\\
+\mbox{~~~~~~~~~~~~~~~~{\#}~tracedmethod~and~would~generate~infinite~recursion}\\
+\mbox{~~~~~~~~~~~~~~~~func.{\_}{\_}doc{\_}{\_}="[tracedmethod]~"~+~(func.{\_}{\_}doc{\_}{\_}~or~'')}\\
+\mbox{~~~~~~~~super(Traced,cls).{\_}{\_}init{\_}{\_}(n,b,d)~{\#}~decorates~the~methods}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/customdec.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is an example of usage:
+\begin{verbatim}>>> class C(object):
+... """[Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... in method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> type(C)
+<class 'customdec.Traced'>
+>>> c=C()
+>>> c.f1()
+Calling 'C.f1' with arguments <C instance>(){} ...
+'C.f1' called with result: None
+>>> c.f2()
+Calling 'C.f2' with arguments <C instance>(){} ...
+'C.f2' called with result: None\end{verbatim}
+
+By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use \texttt{decorators.enhance{\_}classes()}
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example7.py{$>$}}\\
+\mbox{}\\
+\mbox{from~example2~import~identity,name}\\
+\mbox{import~inspect,~decorators;~decorators.enhance{\_}classes("[Decorated]")}\\
+\mbox{}\\
+\mbox{class~C1(object):~{\#}~automagically~converted~to~a~decorated~class}\\
+\mbox{~~~~identity=identity~}\\
+\mbox{}\\
+\mbox{class~C2:~{\#}~automagically~converted~to~a~decorated~class}\\
+\mbox{~~~~name=name}\\
+\mbox{}\\
+\mbox{c1=C1()~{\#}~C1~instance}\\
+\mbox{c2=C2()~{\#}~C2~instance}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example7.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The magic works both for new style classes such as \texttt{C1}
+\begin{verbatim}>>> from example7 import C1,c1
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1 \end{verbatim}
+
+and old style classes such as \texttt{C2} by implicitly converting them
+to new style classes:
+\begin{verbatim}>>> from example7 import C2,c2
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2' \end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{composing-decorators}{}
+\subsection*{Composing decorators}
+\pdfbookmark[1]{Composing decorators}{composing-decorators}
+
+Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example6.py{$>$}}\\
+\mbox{}\\
+\mbox{"How~to~trace~a~class~method"}\\
+\mbox{}\\
+\mbox{import~customdec;~customdec.enhance{\_}classes()}\\
+\mbox{}\\
+\mbox{class~C(object):}\\
+\mbox{~~~~"[Decorated]"}\\
+\mbox{~~~~def~fact(cls,n):~{\#}~a~traced~classmethod}\\
+\mbox{~~~~~~~~"[classmethod,tracedmethod]"}\\
+\mbox{~~~~~~~~if~n==0:~return~1}\\
+\mbox{~~~~~~~~else:~return~n*cls.fact(n-1)}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example6.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here is the testing:
+\begin{verbatim}>>> from example6 import C
+>>> C.fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2\end{verbatim}
+
+You may easily check that calling \texttt{C().fact} will work too.
+
+Under the hood the syntax
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{[classmethod,tracedmethod]}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+generates a \texttt{classmethodtracedmethod} class obtained via
+multiple inheritance:
+\begin{verbatim}>>> type(C.__dict__['fact'])
+<class 'safetype.classmethodtracedmethod'>\end{verbatim}
+
+Notice that the order \emph{does} matter and using the docstring
+``[tracedmethod,classmethod]'' will not work:
+\begin{verbatim}>>> class D:
+... "[Decorated]"
+... def f(cls):
+... "[tracedmethod,classmethod]"
+>>> D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance
+as first argument (got nothing instead)\end{verbatim}
+
+The problem here is that \texttt{tracedmethod.get} returns a method-wrapper object
+which expects a D instance
+as first argument whereas it gets \texttt{None} since
+it is called from the class. On the other hand,
+\begin{verbatim}>>> D().f()
+Calling 'D.f' with arguments <D instance>(){} ...
+'D.f' called with result: None\end{verbatim}
+
+will work. When \texttt{classmethod} precedes \texttt{tracedmethod}, then
+\texttt{classmethod} passes to \texttt{tracedmeth} a non-empty first argument,
+i.e. the calling class, even when called from the instance:
+\begin{verbatim}>>> C().fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2\end{verbatim}
+
+If we try to trace a staticmethod, we will get a different error with
+the order ``tracedmethod, staticmethod'':
+\begin{verbatim}>>> class F(object):
+... "[Decorated]"
+... def fact(n):
+... "[tracedmethod,staticmethod]"
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+>>> F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance
+as first argument (got int instance instead)\end{verbatim}
+
+The message is self-explanatory.
+
+On the other hand, composing the decorators in the order
+``[tracedmethod,staticmethod]'' will work just fine.
+
+It is possible to compose class decorators just as method decorators,
+by using the docstring syntax: for instance we may create a class
+which is both \texttt{Decorated} and \texttt{Logged} and by trivially writing
+\begin{verbatim}>>> class C:
+... "[Decorated,Logged]"
+... def f():
+... "[staticmethod]"
+... return 'it works!'
+<class C[DecoratedLogged]> created
+>>> C().f()
+'it works!'\end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{diving-into-magic}{}
+\subsection*{Diving into magic}
+\pdfbookmark[1]{Diving into magic}{diving-into-magic}
+
+If you never use metaclasses, this part can be safely skipped. On the
+other hand, if you are a metaclass user, you \emph{must} read this section
+in order to keep your code working.
+
+The \texttt{decorators} module provide a \texttt{ClassDecorator} metaclass which
+converts a regular (both old style or new style) class in a class with
+the ability to recognize magic docstrings. Under the hood, the
+\texttt{decorators.enhance{\_}classes()} trick works by decorating the
+\texttt{object} class with \texttt{decorators.ClassDecorator} and by setting
+the custom metaclass to \texttt{decorators.ClassDecorator} and it is
+equivalent to
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{import~decorators}\\
+\mbox{object=decorators.ClassDecorator(object)~{\#}~decorates~new~style~classes}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}=~decorators.ClassDecorator~{\#}~decorates~old~style~classes}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+If you want the magic to work only for new style classes only, you may
+forget the second line; if you want the magic to work for old style
+classes only, you may forget the first line.
+
+The \texttt{decorators.enhance{\_}classes("[SomeClassDec]")} syntax looks at the
+magic docstring and executes something like
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{import~decorators}\\
+\mbox{object=decorators.SomeClassDec(object)~{\#}~decorates~all~new~style~classes}\\
+\mbox{{\_}{\_}metaclass{\_}{\_}=~decorators.SomeClassDec~{\#}~decorates~all~old~style~classes}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The problem with the \texttt{enhance{\_}classes()} syntaxes is that it is
+not 100{\%} safe under metaclass conflicts. In order to explain the issue,
+let me give an example.
+
+Suppose we enhance the \texttt{object} class in the interpreter namespace:
+\begin{verbatim}>>> import decorators; decorators.enhance_classes()\end{verbatim}
+
+making it an instance of \texttt{ClassDecorator}:
+\begin{verbatim}>>> object.__class__
+<class 'decorators.ClassDecorator'>\end{verbatim}
+
+Now, if we naively create a custom metaclass \texttt{M}:
+\begin{verbatim}>>> class M(type):
+... "Some non-trivial code here..." \end{verbatim}
+
+and we try to use it to enhance a new style class \texttt{NwithM}, we will get a
+conflict:
+\begin{verbatim}>>> class NwithM(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a
+(non-strict) subclass of the metaclasses of all its bases\end{verbatim}
+
+The problem is that the previous line tries to create a class \texttt{NwithM}
+which should have both metaclasses \texttt{ClassDecorator} and \texttt{M}:
+a conflict follows.
+
+Fortunately, the decorators module imports the \texttt{type} metaclass from
+my \texttt{safetype} module just to avoid this kind of problems. If we
+derive \texttt{M} from \texttt{decorators.type} (which is really \texttt{safetype.type})
+the conflict is automagically avoided:
+\begin{verbatim}>>> class M(decorators.type):
+... "This metaclass is conflict-proof"
+>>> class NwithM(object): # it works!
+... __metaclass__=M\end{verbatim}
+
+The reason why the conflict is avoided is that the \texttt{safetype} module
+(which makes use of really \emph{deep} metaclass magic) automatically
+creates the composed class \texttt{MClassDecorator} by multiply inheriting
+from \texttt{M} and from \texttt{ClassDecorator}. Then, \texttt{NwithM} can be safely
+created as an instance of \texttt{safetype.MClassDecorator}:
+\begin{verbatim}>>> type(NwithM)
+<class 'safetype.MClassDecorator'>\end{verbatim}
+
+The situation with old style classes is worse since
+apparently
+\begin{verbatim}>>> class OwithM: # old style class with metaclass M
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"\end{verbatim}
+
+gives no error, but actually \texttt{M} overrides
+\texttt{ClassDecorator}, so \texttt{OwithM} will not recognize magic docstrings:
+\begin{verbatim}>>> type(OwithM)
+<class 'M'>
+>>> OwithM.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with OwithM instance
+as first argument (got nothing instead)\end{verbatim}
+
+The simpler solution is to convert \texttt{OwithM} in a new style class:
+\begin{verbatim}>>> class NwithM(OwithM,object):
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+>>> type(NwithM)
+<class 'safetype.MClassDecorator'>\end{verbatim}
+
+Now \texttt{NwithM} is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so \texttt{NwithM}
+subclasses with a ``[Decorated]'' docstring will be decorated:
+\begin{verbatim}>>> class E(NwithM):
+... "[Decorated]"
+... def cm(cls):
+... "[classmethod]"
+... print '.cm() called from',cls\end{verbatim}
+\begin{verbatim}>>> E.cm() # it works
+.cm() called from <class E[MClassDecoratorDecorated]>\end{verbatim}
+
+Notice that \texttt{sm} was defined in \texttt{NwithM}, the undecorated class: therefore
+it is not decorated:
+\begin{verbatim}>>> E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance
+as first argument (got nothing instead)\end{verbatim}
+
+The error message says that \texttt{sm} is an unbound method and not a
+static method.
+
+It is possible to go even deeper in \textbf{black} magic, and to decorate all
+the new style classes in \emph{all} modules, by decorating the
+\texttt{{\_}{\_}builtin{\_}{\_}.object}. Still, naively redefining the \texttt{{\_}{\_}builtin{\_}{\_}object}
+class is risky, since it will induce metaclass conflicts in other modules
+using metaclasses. In other words, it will work only if you import modules
+not using metaclasses, or modules using metaclasses safely, i.e. modules
+taking care of possible conflicts by using \texttt{safetype.type} as base metaclass.
+If you really enjoy black magic, you may solve the problem by
+redefining the \texttt{{\_}{\_}builtin{\_}{\_}.type} as \texttt{safetype.type}.
+The \texttt{decorators} module does not go so deep in dark magic, but still
+there are cases where you may want to do it. In these cases you must be
+explicit and redefine the builtins by hand, without help from the
+module. For instance, one of my original motivation for the usage
+of metaclasses/class decorators was to use them for debugging purposes.
+For instance, I wanted to trace the execution of methods in
+complicate inheritance hierarchies, \emph{without changing the source code}.
+For simple situations, i.e. in absence of inheritance and of pre-existing
+metaclasses, the function \texttt{import{\_}with{\_}metaclass} discussed in the
+\href{http://www-106.ibm.com/developerworks/library/l-pymeta.html}{first paper on metaclasses} written in collaboration with David Mertz, works
+fine but in general the implementation of a working \texttt{import{\_}with{\_}metaclass}
+is cumbersome. For debugging purposes, the quick and dirty way can be a
+good enough way, so let me show how we can redefine the builtins \texttt{object} and
+\texttt{type} \emph{before} importing the module, in such a way to enhance \emph{all}
+its classes with tracing capabilities.
+
+Let me start from a module using an unsafe metaclass \texttt{M}, such that it
+cannot be traced trivially by decorating its classes \emph{after} the import;
+moreover there is an inheritance hierarchy, such that it cannot be
+traced correctly by naively decorating all the classes one after the
+other (unless one modifies the original source code, but this forbidden
+by the rules of the game ;)
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}tracing.py{$>$}}\\
+\mbox{}\\
+\mbox{"""}\\
+\mbox{This~is~a~pre-existing~module~not~using~decorators~but~using~multiple}\\
+\mbox{inheritance~and~unsafe~metaclasses.~We~want~to~trace~it~for~debugging~}\\
+\mbox{purposes.}\\
+\mbox{"""}\\
+\mbox{~}\\
+\mbox{class~M(type):~}\\
+\mbox{~~~~"There~should~be~some~non-trivial~code~here"}\\
+\mbox{}\\
+\mbox{class~B(object):~}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\
+\mbox{~~~~~~~~super(B,self).{\_}{\_}init{\_}{\_}()}\\
+\mbox{}\\
+\mbox{class~D(object):~}\\
+\mbox{~~~~{\_}{\_}metaclass{\_}{\_}=M}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\
+\mbox{~~~~~~~~super(D,self).{\_}{\_}init{\_}{\_}()}\\
+\mbox{}\\
+\mbox{class~E(B,D):}\\
+\mbox{~~~~def~{\_}{\_}init{\_}{\_}(self):}\\
+\mbox{~~~~~~~~super(E,self).{\_}{\_}init{\_}{\_}()}\\
+\mbox{}\\
+\mbox{~{\#}{$<$}/tracing.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Everything works is we modify the builtins before importing the module:
+\begin{verbatim}>>> import __builtin__
+>>> __object__=__builtin__.object # the original 'object' class
+>>> __builtin__.object=customdec.Traced('tracedobject',(),{})
+>>> __builtin__.type=customdec.type # a safe 'type' class\end{verbatim}
+
+Now, when we import the module, all the classes \texttt{M}, \texttt{B}, \texttt{D} and \texttt{E}
+will be created starting for the tricked builtins, so they will be traced
+and safe under conflicts. For instance, let me show that \texttt{E} is created
+with a conflict safe \texttt{MTraced} metaclass:
+\begin{verbatim}>>> from tracing import E
+>>> print E
+<class E[MTraced]>\end{verbatim}
+
+This shows that the \texttt{{\_}{\_}init{\_}{\_}} methods are traced indeed and shows the
+Method Resolution Order:
+\begin{verbatim}>>> e=E()
+Calling 'E.__init__' with arguments <E instance>(){} ...
+ Calling 'B.__init__' with arguments <E instance>(){} ...
+ Calling 'D.__init__' with arguments <E instance>(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None\end{verbatim}
+
+This works, indeed. At the end, one should not forget to restore
+the standard builtins, otherwise it will trace \emph{all} the classes
+created thereafter.
+\begin{verbatim}>>> __builtin__.object=__object__ # restore the __builtin__
+>>> __builtin__.type=decorators.__type__ # restore the __builtin__\end{verbatim}
+
+At the end, I will notice that it is possible to solve the problem more
+nicely, without redefining the builtins, but I will discuss the solution
+elsewhere ;)
+
+
+%___________________________________________________________________________
+
+\hypertarget{advanced-usage}{}
+\subsection*{Advanced usage}
+\pdfbookmark[1]{Advanced usage}{advanced-usage}
+
+Whereas the average programmer is expected to use the \texttt{decorated()}
+and \texttt{enhance{\_}classes()} functions only, the \texttt{decorators} module provides
+facilities which may be useful to the advanced programmer.
+
+The simplest is an utility function which retrieves the list of
+recognized decorators, \texttt{decorators.getdec()}. The precise syntax is
+\texttt{decorators.getdec(docstring)}, where \texttt{docstring}
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance \texttt{decorators.getdec('[MethodDecorator]')}
+gives the list of all subclasses of \texttt{MethodDecorator}, i.e. all method
+decorators, whereas \texttt{decorators.getdec('[ClassDecorator]')}
+gives the list of the known class decorators. The utility of the function
+is that it also returns decorators that have been automagically created:
+\begin{verbatim}>>> decorators.getdec("[classmethodtracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]\end{verbatim}
+
+It is even possible to use the comma notation:
+\begin{verbatim}>>> decorators.getdec("[classmethod,tracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]\end{verbatim}
+
+If you mispell a decorator name you get an helpful error message:
+\begin{verbatim}>>> class E:
+... "[Decorated]"
+... def f():
+... "[staticmeth]"
+Traceback (most recent call last):
+ .. a cryptic traceback here ..
+UnknownDecoratorError: staticmeth\end{verbatim}
+
+By design, the \texttt{decorated} function is quite limited, and does not
+decorate functions without magic docstrings. This is safe and also convenient
+for internal usage of the \texttt{decorated} function in the \texttt{Decorated}
+metaclass. Nevertheless, advanced users may want to have the ability
+of decorating functions by hand, without invoking decorators.decorated()`
+and magic docstrings. This is possible, by calling the decorator directly.
+For instance, here is how to convert a lambda function in a staticmethod:
+\begin{verbatim}>>> do_nothing=decorators.staticmethod(lambda:None)
+>>> print do_nothing # ``do_nothing`` is a static method
+<staticmethod:<lambda>>\end{verbatim}
+
+The most convenient usage of this feature is in the composition of decorators.
+For instance, we may compose the just defined \texttt{staticmethod} with a
+\texttt{chattymethod2}:
+\begin{verbatim}>>> chattystatic=customdec.chattymethod2(do_nothing)
+>>> print chattystatic
+<chattymethod2staticmethod:<lambda>>\end{verbatim}
+
+The syntax
+\begin{quote}
+
+\texttt{decorator1(decorator2(obj))}
+\end{quote}
+
+automagically creates a composed class \texttt{decorator1decorator2} in the
+\texttt{safetype} module (or recycle it, if \texttt{decorator1decorator2} has
+been already created) and it is equivalent to
+\begin{quote}
+
+\texttt{decorator1decorator2(obj)}
+\end{quote}
+
+Here is the check that everything works:
+\begin{verbatim}>>> class B:
+... chattystatic=chattystatic
+>>> B.chattystatic()
+calling <chattymethod2staticmethod:<lambda>> from <class 'B'>\end{verbatim}
+
+Notice that \texttt{chattystatic} does not know the class where it
+is defined:
+\begin{verbatim}>>> chattystatic.__klass__
+<class 'safetype.?'>\end{verbatim}
+
+unless the \texttt{{\_}{\_}klass{\_}{\_}} attribute is explicitly set.
+
+This is the hierarchy:
+\begin{verbatim}>>> for C in type(chattystatic).mro(): print C
+...
+<class 'safetype.chattymethod2staticmethod'>
+<class 'customdec.chattymethod2'>
+<class 'customdec.chattymethod'>
+<class 'decorators.staticmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.Decorator'>
+<type 'object'>\end{verbatim}
+
+One can also compose classes by hand, by using class decorators:
+\begin{verbatim}>>> class C:
+... "[Logged]"
+... def f(): "[staticmethod]"
+...
+<class 'C'> created
+>>> C=customdec.Traced(C)
+<class C[TracedLogged]> created\end{verbatim}
+
+The \texttt{decorators.enhance{\_}classes({$<$}classdecorator{$>$})} syntax performs
+the composition automagically:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}example8.py{$>$}}\\
+\mbox{}\\
+\mbox{from~example2~import~identity,name}\\
+\mbox{import~inspect,~decorators;~decorators.enhance{\_}classes("[Decorated]")}\\
+\mbox{}\\
+\mbox{class~C1:~{\#}~automagically~converted~to~a~Decorated~class}\\
+\mbox{~~~~identity=identity~}\\
+\mbox{}\\
+\mbox{class~C2:~{\#}~automagically~converted~to~a~DecoratedLogged~class}\\
+\mbox{~~~~"[Logged]"}\\
+\mbox{~~~~name=name}\\
+\mbox{}\\
+\mbox{c1=C1()~{\#}~C1~instance}\\
+\mbox{c2=C2()~{\#}~C2~instance}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/example8.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+In this example the class \texttt{C2} has already a magic docstring. This means that
+\texttt{C2} has to be enhanced both from \texttt{Logged} and from \texttt{Decorated}.
+This is done by automagically creating a \texttt{DecoratedLogged} class
+decorator:
+\begin{verbatim}>>> from example8 import C1,C2,c1,c2
+<class C2[DecoratedLogged]> created\end{verbatim}
+
+The second line is printed because of the logging capabilities of \texttt{C2}.
+Moreover, since \texttt{C2} is decorated too, the following will work:
+\begin{verbatim}>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2' \end{verbatim}
+
+Idem for \texttt{C1}:
+\begin{verbatim}>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1 \end{verbatim}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-implementation}{}
+\section*{The implementation}
+\pdfbookmark[0]{The implementation}{the-implementation}
+
+This part can be safely skipped, unless you are a \emph{really} curious and
+you want to know how the implementation works.
+
+The module is rather short (less than 150 lines if you skip comments and
+docstrings) but far from being trivial, since it is based on extensive
+usage of metaclass wizardry. Moreover,
+it invokes the \texttt{safetype} module to solve metaclass conflicts, and
+\texttt{safetype} contains 50 lines of \emph{really} deep metaclass wizardry.
+\begin{quote}
+\begin{figure}
+
+\includegraphics{decorators.ps}
+\end{figure}
+\end{quote}
+
+The main class-metaclass hierarchy is represented in the figure, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.
+
+For instance, this is the Method Resolution Order for the \texttt{Decorated}
+metaclass:
+\begin{verbatim}>>> for i,c in enumerate(decorators.Decorated.__mro__):
+... print i,c,"[%s]" % type(c).__name__
+0 <class 'decorators.Decorated'> [MetaDecorator]
+1 <class 'decorators.ClassDecorator'> [MetaDecorator]
+2 <class 'safetype.safetype'> [type]
+3 <type 'type'> [type]
+4 <class 'decorators.Decorator'> [MetaDecorator]
+5 <type 'object'> [type]\end{verbatim}
+
+\texttt{Decorator} is the mother of all decorators. Its main purpose it to
+provide the \texttt{MetaDecorator}
+metaclass to all decorators.
+
+The \texttt{safetype} metaclass, imported from the \texttt{safetype} module,
+ensures that class decorators can generate classes without incurring
+in conflicts.
+
+Since class decorators are metaclasses,
+\texttt{MetaDecorator} is actually a meta-metaclass with respect to
+instances of decorated classes. For this reason if
+\begin{verbatim}>>> C=decorators.ClassDecorator('C',(),{})\end{verbatim}
+
+then
+\begin{verbatim}>>> C.__class__
+<class 'decorators.ClassDecorator'>\end{verbatim}
+
+but
+\begin{verbatim}>>> C.__metaclass__
+<class 'decorators.MetaDecorator'>\end{verbatim}
+
+since the \texttt{C.{\_}{\_}metaclass{\_}{\_}} attribute is inherited from \texttt{Decorator}.
+
+On the other hand, \texttt{MetaDecorator} is a simple metaclass with
+respect to instances of decorated methods.
+
+The implementation is relatively smart. Consider for instance the case of
+the \texttt{logged} example. In that example class \texttt{D} was a subclass
+of a tricked \texttt{object} class, enhanced by \texttt{ClassDecorator}. Moreover \texttt{D}
+had a \texttt{Logged} docstring. Therefore it should have been an instance of a
+\texttt{ClassDecoratorLogged} metaclass. But logged was
+a subclass of \texttt{ClassDecorator}, therefore it already had all the features
+of \texttt{ClassDecorator} and it would have been redundant to create
+\texttt{ClassDecoratorLogged}, when``Logged`` would have been enough.
+So \texttt{Logged} was used and \texttt{ClassDecoratorLogged} was never created.
+All this magic is in the \texttt{safetype.remove{\_}redundant(*classes)} function.
+
+The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. Probably, they do not matter unless one has to decorate
+thousands or millions of functions and classes.
+
+Finally, a word about bugs. The \texttt{decorators} module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+{\~{ }}200 tests, most of them extracted from the documentation you are reading),
+I cannot guarantee that it is correct. If somebody finds a bug or an
+unexpected behavior, please let me know and I will fix it or document it.
+% References:
+
+
+%___________________________________________________________________________
+
+\hypertarget{module-decorators}{}
+\subsection*{Module \texttt{decorators}}
+\pdfbookmark[1]{Module decorators}{module-decorators}
+
+A module to implement pep318 (decorator syntax) via magic doctrings.
+For the full documentation see
+\href{http://www.phyast.pitt.edu/~micheles/python/decorators.html}{http://www.phyast.pitt.edu/{\~{ }}micheles/python/decorators.html} .
+
+
+%___________________________________________________________________________
+
+\hypertarget{id3}{}
+\subsubsection*{Metaclasses}
+\pdfbookmark[2]{Metaclasses}{id3}
+
+\texttt{metaclass ClassDecorator(type,Decorator):}
+\begin{quote}
+
+Metaclass callable with one or three arguments, having its calling
+syntax redefined by \texttt{MetaDecorator}.
+\end{quote}
+
+\texttt{metaclass Decorated(ClassDecorator):}
+\begin{quote}
+
+Metaclass which decorates the methods of its instances according
+to the docstrings. It redefines \texttt{{\_}{\_}str{\_}{\_}} on
+its instances and the default \texttt{{\_}{\_}str{\_}{\_}} on the instances of its
+instances.
+
+\texttt{{\_}{\_}str{\_}{\_}(cls):}
+\begin{quote}
+
+Redefine the printing representation of classes
+\end{quote}
+\end{quote}
+
+\texttt{metaclass safetype(type):}
+\begin{quote}
+
+Overrides the \texttt{{\_}{\_}new{\_}{\_}} method of the \texttt{type} metaclass, making the
+generation of classes conflict-proof.
+\end{quote}
+
+\texttt{metaclass MetaDecorator(type):}
+\begin{quote}
+
+Metaclass inducing a certain amount of magic on decorators:
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1}.}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item
+Each time a decorator is defined in any module, it is stored in
+MetaDecorator.dic and MetaDecorator.ls.
+
+\item
+If the (method) decorator has a 'get' method, a '{\_}{\_}get{\_}{\_}' method
+is automagically created as an alias to 'get'.
+
+\item
+Decorators calls are dispatched to the decorator {\_}call{\_}
+classmethod.
+
+\end{list}
+
+\texttt{{\_}{\_}call{\_}{\_}(dec,*args):}
+\begin{quote}
+
+This is the heart of the module. Infers the correct decorator class
+from \texttt{dec} and the docstring and creates the correct decorator object.
+Returns the original object if \texttt{dec} is the trivial \texttt{Decorator}
+class and no docstring is found.
+\end{quote}
+
+\texttt{{\_}{\_}init{\_}{\_}(dec,*args):}
+\begin{quote}
+
+Update the metaclass attributes \texttt{dic} and \texttt{ls};
+alias \texttt{get} to \texttt{{\_}{\_}get{\_}{\_}}.
+\end{quote}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{classes}{}
+\subsubsection*{Classes}
+\pdfbookmark[2]{Classes}{classes}
+
+\texttt{class UnknownDecoratorError(Exception):}
+\begin{quote}
+
+The name says it all.
+\end{quote}
+
+\texttt{class Decorator(object):}
+\begin{quote}
+
+Instance of MetaDecorator and mothers of all decorators. When called
+in the form \texttt{Decorator(obj)} with obj having a magic docstring, it returns
+a decorated object; otherwise it returns the original object.
+\end{quote}
+
+\texttt{class staticmethod(MethodDecorator):}
+\begin{quote}
+
+Decorator, converts a function in a staticmethod
+\end{quote}
+
+\texttt{class MethodDecorator(Decorator):}
+\begin{quote}
+
+Descriptor class callable with a function or a descriptor object
+as argument. It defines a default printing representation on method
+decorators objects and a default \texttt{get} method. All the rest is
+provided by the metaclass \texttt{MetaDecorator}.
+
+\texttt{get(self,obj,cls=None):}
+\begin{quote}
+
+aliased to {\_}{\_}get{\_}{\_} by the metaclass, to be overridden
+\end{quote}
+
+\texttt{{\_}{\_}str{\_}{\_}(self):}
+\begin{quote}
+
+Printing representation of kind {$<$}decoratorclass:functionname{$>$}
+\end{quote}
+
+\texttt{{\_}{\_}init{\_}{\_}(self,objfunc):}
+\begin{quote}
+
+objfunc is a decorator object or a function
+\end{quote}
+\end{quote}
+
+\texttt{class classmethod(MethodDecorator):}
+\begin{quote}
+
+Decorator, converts a function in a classmethod
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{functions}{}
+\subsubsection*{Functions}
+\pdfbookmark[2]{Functions}{functions}
+
+\texttt{enhance{\_}classes(magicstring='[ClassDecorator]'):}
+\begin{quote}
+
+Enhances all the classes in the caller module with a metaclass
+built from the given magicstring.
+\end{quote}
+
+\texttt{remove{\_}redundant(bases):}
+\begin{quote}
+
+Returns a tuple of non-redundant base classes.
+Given a sequence of base classes, a class is redundant if it is duplicated
+or if it is implied by the others, i.e. it is an ancestor of at least one
+of the other classes. For instance, if \texttt{C} is derived from \texttt{B}, in the
+sequence \texttt{C,B} the class \texttt{B} is redundant, since all its features are
+already provided by \texttt{C}. Therefore \texttt{B}
+is removed and \texttt{remove{\_}redundant} returns the tuple \texttt{(C,)}:
+\begin{verbatim}>>> class B(object): pass
+...
+>>> class C(B): pass
+...
+>>> import safetype; safetype.remove_redundant([C,B])
+(<class 'C'>,)\end{verbatim}
+\end{quote}
+
+\texttt{compose(dclasses,*defaultclasses):}
+\begin{quote}
+
+Retrieves or creates a decorator for a tuple of decorators. If
+defaults decorators are given, they get the precedence. It removes
+redundant classes.
+\end{quote}
+
+\texttt{instance{\_}str(self):}
+\begin{quote}
+
+Redefines the printing representation of simple objects
+\end{quote}
+
+\texttt{getdec(magicstring="[Decorator]"):}
+\begin{quote}
+
+Given a magicstring describing a valid decorator name, returns the list
+of its subclasses. By default returns the full list of decorators.
+\end{quote}
+
+\texttt{decorate(objdict):}
+\begin{quote}
+
+Takes an object with a dictionary and decorates all its functions
+and classes according to their docstrings.
+\end{quote}
+
+\texttt{decorators{\_}in(docstring):}
+\begin{quote}
+
+Takes a docstring and returns a (possibly empty) tuple of decorator
+classes.
+\end{quote}
+
+\end{document}
+
diff --git a/pypers/pep318/decorators.txt b/pypers/pep318/decorators.txt
new file mode 100755
index 0000000..fb7f321
--- /dev/null
+++ b/pypers/pep318/decorators.txt
@@ -0,0 +1,1413 @@
+Implementing PEP 318 (decorators)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+:Module: decorators
+:Version: 0.5
+:Author: Michele Simionato
+:e-mail: MicheleSimionato@libero.it
+:Licence: Python-like
+:Date: September 2003
+:Disclaimer: This is experimental code. Use it at your own risk!
+
+.. contents::
+
+Using decorators
+=========================================================================
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of `PEP 318`_ in pure Python
+(2.3).
+
+Here is the rationale:
+
+* some kind of decorator syntax is scheduled to go in Python 2.4,
+ therefore it is interesting to play with the concept;
+
+* it is nice to play with decorators now, without having to
+ wait for one year or so;
+
+* it is much easier to experiment with a pure Python implementation
+ than with a C implementation;
+
+* the implementation can be seen as an exercise on modern Python
+ programming and may be valuable to people wanting to study the most
+ advanced new constructs in Python (descriptors_, metaclasses_,
+ `cooperative methods`_, etc.)
+
+Basics
+--------------------------------------------------------------------
+
+PEP 318 has the goal of providing a nice syntactic sugar for expressions like
+
+ ::
+
+ def identity(x):
+ return x
+ identity=staticmethod(identity)
+
+or
+
+ ::
+
+ def name(cls):
+ return cls.__name__
+ name=classmethod(name)
+
+which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)
+
+ ::
+
+ def identity(x)[staticmethod]:
+ return x
+
+ def name(cls)[classmethod]:
+ return cls.__name__
+
+involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:
+
+ ::
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+The implementation is able to recognize such magic docstrings
+and automatically converts methods with magic docstrings in
+method decorators.
+
+Decorators are nothing else than a sophisticated kind of wrappers.
+The ``decorators`` module provides support both for method decorators
+and class decorators:
+
++ *Method decorators* are classes taking a single function as input and
+ producing a descriptor object as output. ``staticmethod`` and
+ ``classmethod`` are two examples of already existing
+ method decorators (actually my implementation rewrites them).
+ A knowledge of descriptors [1]_ is not needed in order to use the ``decorator``
+ module; however it is welcomed for advanced users wanting to implement
+ custom method decorators.
+
++ *Class decorators* are metaclasses taking a class as imput and returning
+ a decorated class as output. A good understanding of metaclasses is needed
+ in order to be able to write custom class decorators, but no knowledge
+ at all is required in order to use the pre-defined class decorators
+ provided by the module.
+
+Notice that properties are not decorators according to my definitions,
+since they take four functions as input, ``get, set, del_`` and ``doc``.
+Whereas the decorators concept could be generalized to the case of
+multiple inputs, I don't see the need for such complication, so
+properties are not implemented as method decorators. Moreover, I
+think it is much better to use them trough a class decorator.
+
+Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the documentation short.
+
+Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.
+
+.. [1] Descriptors are objects with a ``__get__`` method; they are quite
+ sophisticated, but fortunately they have been wonderfully explained by
+ Raymond Hettinger already, so I am allowed to skip on this point ;).
+
+Decorating methods
+------------------------------------------------------------------------
+
+Before talking about the implementation details, I will show
+how the ``decorators`` module works in practice. The simplest and safest
+usage is by means of the ``decorators.decorator()`` function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using ``decorators.decorator()``
+is simple but verbose, so magic shortcuts will be discussed in the next
+sections.
+
+Here, let me give an example, showing that method decorators work both for
+`new style classes and old style classes`_:
+
+ ::
+
+ #<example1.py>
+
+ import decorators
+
+ def do_nothing(self):
+ "No magic docstring here"
+ dec_do_nothing=decorators.decorator(do_nothing)
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+ dec_identity=decorators.decorator(identity)
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+ dec_name=decorators.decorator(name)
+
+ class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+ class NewStyle(object):
+ name=dec_name
+
+ o=OldStyle() # creates an old style instance
+ n=NewStyle() # creates a new style instance
+
+ #</example1.py>
+
+>>> from example1 import * # for testing purposes
+
+In this example, both ``dec_identity`` and ``dec_name`` are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects in the interpreter, since they have a re-defined
+printing representation:
+
+>>> print dec_identity
+<staticmethod:identity>
+>>> print dec_name
+<classmethod:name>
+
+On the other hand, ``do_nothing`` does not have a magic
+docstring, therefore it is *not* converted to a decorator object:
+actually, in this case ``decorator`` returns ``None``:
+
+
+>>> print dec_do_nothing
+None
+
+This is useful since one can tests if an object is a decorator
+object with idioms like
+
+ ``if decorator(obj): do_something()``
+
+and one can convert generic objects with the idiom
+
+ ``dec_obj=decorator(obj) or object``
+
+which returns the original object if no magic docstring is found.
+
+
+Let me check that ``dec_ identity`` works correctly as a staticmethod,
+
+>>> OldStyle.identity(1) # called from the class
+1
+>>> o.identity(1) # called from the instance
+1
+
+whereas ``dec_name`` works as a classmethod:
+
+>>> NewStyle.name() # called from the class
+'NewStyle'
+>>> n.name() # called from the instance
+'NewStyle'
+
+Notice that, I have re-implemented the built-in
+``staticmethod`` and ``classmethod``, so
+
+>>> isinstance(dec_identity,staticmethod)
+False
+
+and
+
+>>> isinstance(dec_name,classmethod)
+False
+
+It is possible to recognize method decorators since they provides
+a couple of special attributes:
+
+- ``__func__``: returning the function from which they originated:
+
+ >>> assert dec_identity.__func__ is identity
+ >>> assert dec_name.__func__ is name
+
+- ``__klass__``: returning the class where they where defined:
+
+ >>> dec_identity.__klass__
+ <class 'safetype.?'>
+
+The question mark here means that the definition class is unknown.
+The ``__klass__`` attribute can be set by hand or automatically
+with the trick explained in the next section.
+
+Decorating classes
+----------------------------------------------------------------------
+
+The problem with the approach just described
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically decorating their
+methods according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with "[Decorated]"
+and by decorating the class.
+Here is an example:
+
+ ::
+
+ #<example2.py>
+
+ from decorators import decorator
+ from example1 import do_nothing,identity,name
+
+ class B(object):
+ "This is a regular class"
+
+ B=decorator(B) or B # return the original B
+
+ class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ class Inner: # old style class
+ "[Decorated]" # required docstring
+ name=name
+
+ C=decorator(C) # regenerate the class converting methods in decorators
+ c=C()
+
+ #</example2.py>
+
+Under the hood ``decorators.decorator()`` recognizes the class level
+magic docstring "[Decorated]" and creates an instance of the
+``decorators.Decorated`` metaclass.
+Internally the metaclass invokes ``decorators.decorator()``
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. Moreover, it decorates inner classes,
+if a suitable docstring is found, and it works recursively.
+
+Here is the testing:
+
+>>> from example2 import *
+>>> assert C.identity(1) == 1
+>>> assert C.Inner.name() == 'Inner'
+>>> assert c.identity(1) == 1
+>>> assert c.Inner.name() == 'Inner'
+
+Notice that adding ``identity`` after the class creation with the syntax
+``C.identity=identity`` would not work;
+``C.identity=decorators.decorator(identity)`` is required:
+
+>>> C.identity=decorators.decorator(identity)
+>>> C.identity(1) # it works
+1
+
+If a class misses the magic docstring, nothing happens:
+
+>>> B # returns the original B
+<class 'example2.B'>
+
+The mechanism works for old style classes too,
+since the metaclass automagically converts the old style classes in a
+new style one:
+
+>>> type(C.Inner) # C.Inner is an instance of decorator.Decorated
+<class 'decorators.Decorated'>
+
+The enhancement provided by the metaclass includes a new default
+printing representation for both the class
+
+>>> print C # returns the name of D and of its metaclass
+<class C[Decorated]>
+
+and its instances:
+
+>>> print c
+<C instance>
+
+On the other hand, if a custom printing representation is already
+defined, the metaclass takes it without any change.
+
+One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:
+
+>>> class D(C):
+... def name(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print D.name()
+D
+
+Decorating whole classes presents another advantage: the decorated methods know
+the class where they were defined via the special attribute ``__klass__``:
+
+>>> D.__dict__['name'].__klass__ # the class where 'name' is defined
+<class 'D'>
+
+This is useful for introspection and debugging purposes.
+
+Adding magic
+----------------------------------------------------------------------------
+
+The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking ``decorators.decorator()``
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing ``decorators.enhance_classes()``
+on top of you module. Then all methods in all classes with a magic docstring
+will be checked for magic docstrings and automagically decorated if needed.
+For instance, the previous example would be written
+
+ ::
+
+ #<example3.py>
+
+ import decorators; decorators.enhance_classes()
+
+ class C:
+ "[Decorated]" # magic docstring here
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ class D(object):
+ "Undecorated" # no magic docstring here
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ c=C(); d=D()
+
+ #</example3.py>
+
+``C`` has a ``[Decorated]`` docstring, so its methods
+are automatically decorated:
+
+>>> from example3 import *
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert c.identity(1) == 1
+
+On the other hand, since ``D`` misses a magic docstring,
+its ``name`` method is not decorated:
+
+>>> hasattr(D.__dict__['name'],'__func__') # not a decorator
+False
+
+Since ``D.name`` is a regular method and not a classmethod, ``D.name()``
+gives an error:
+
+>>> D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance as first argument (got nothing instead)
+
+Under the hood, the magic works by enhancing the ``object`` class
+of the module with a ``decorators.ClassDecorator`` metaclass:
+
+>>> import example4
+>>> type(example4.object)
+<class 'decorators.ClassDecorator'>
+
+Notice that for safety reasons the enhancement is only on the module
+``object`` class, not on the ``__builtin__.object`` class. The dangers of
+adding too much magic are discussed in the `Diving into magic`_ section.
+
+Defining method decorators
+-----------------------------------------------------------------------
+
+The ``decorators`` module contains two predefinite method decorators,
+``staticmethod`` and ``classmethod``, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The ``decorators.MethodDecorator`` class which is here
+exactly for that purpose.
+
+Custom decorators are expected to be implemented by subclassing
+``MethodDecorator`` and by overriding its ``get`` method. The
+``get`` method automagically induces a ``__get__`` method, turning the
+class in a descriptor. The machinery is needed since ``__get__`` cannot
+be made cooperative using the standard ``super`` mechanism because
+there would be a confusion between ``super.__get__`` and the decorator
+``__get__``. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators *too* easy, since they are potentially dangerous.
+
+In order to give a simple example, let me show the implementation
+of a ``chattymethod`` that prints a message when it is called:
+
+ ::
+
+ #<customdec.py>
+
+ from decorators import *
+
+ class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+ #</customdec.py>
+
+Notice the usage of the ``super().get`` trick. This guarantees that
+``chattymethod`` will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.
+
+Here is an example of usage:
+
+>>> import customdec # adds chattymethod to the list of known decorators
+>>> customdec.enhance_classes() # automagically enhances classes
+>>> class C:
+... " [Decorated] "
+... def f(self):
+... """
+... [ chattymethod ]
+... """
+>>> c=C()
+>>> c.f()
+calling <chattymethod:f> from <C instance>
+
+By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.
+
+One can check that the syntax ``C.f(c)`` works too:
+
+>>> C.f(c)
+calling <chattymethod:f> from <class C[Decorated]>
+
+A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind
+
+ ::
+
+ def f(self)[chattymethod(logfile=file('file1.log','w'))]
+
+In my view, there are too many parenthesis in this syntax, and it
+becomes rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the ``decorators`` module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.
+
+A simple minded solution is to change the defaults by hand:
+
+>>> from customdec import chattymethod,decorator
+>>> chattymethod.logfile=file('file.log','w')
+>>> def g(self):
+... "[chattymethod]"
+>>> C.g=decorator(g)
+>>> c.g() # will print a message on file.log
+
+This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore
+
+>>> c.f()
+
+will print a message to ``file.log`` too, and not to standard output.
+Here is the confirmation:
+
+>>> chattymethod.logfile.close()
+>>> print file('file.log').read().rstrip()
+calling <chattymethod:g> from <C instance>
+calling <chattymethod:f> from <C instance>
+
+A better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the ``__init__`` method. Here is an example:
+
+ ::
+
+ #<customdec.py>
+
+ class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+ #</customdec.py>
+
+Notice that the ``__init__`` method has the signature
+``__init__(self,objfunc)``, where the ``objfunc`` object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding ``__init__``.
+
+Here is the testing:
+
+ ::
+
+ #<chatty2.py>
+
+ import customdec; customdec.enhance_classes()
+
+ # sets the log files
+ log1=file('file1.log','w')
+ log2=file('file2.log','w')
+
+ class C:
+ "[Decorated]"
+ def f(self):
+ "[chattymethod2]"
+ f.logfile=log1 # function attribute
+ def g(self):
+ "[chattymethod2]"
+ g.logfile=log2 # function attribute
+
+ assert C.__dict__['f'].logfile is log1 # check the conversion
+ assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr.
+
+ c=C() # C instantiation
+
+ c.f() # print a message in file1.log
+ c.g() # print a message in file2.log
+
+ log1.close(); log2.close() # finally
+
+ #</chatty2.py>
+
+Let me check the contents of the log files:
+
+>>> import chatty2
+>>> print file('file1.log').read().rstrip()
+calling <chattymethod2:f> from <C instance>
+>>> print file('file2.log').read().rstrip()
+calling <chattymethod2:g> from <C instance>
+
+``chattymethod`` is the poor man version of ``tracedmethod``, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.
+
+ ::
+
+ #<customdec.py>
+
+ class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -> dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+ #</customdec.py>
+
+``tracedmethod.get`` returns a method wrapper object, so it is
+possible to use ``im_func`` to retrieve the internal function
+``tracedmeth``:
+
+>>> class C:
+... "[Decorated]"
+... def f(self):
+... "[tracedmethod]"
+>>> c=C(); c.f()
+Calling 'C.f' with arguments <C instance>(){} ...
+'C.f' called with result: None
+>>> c.f.im_func.__name__
+'tracedmeth'
+
+As soon as the ``tracedmethod`` module is loaded, the ``tracedmethod`` class
+is added to the list of know decorators, so one should use the
+"[tracedmethod]" docstring and not something like
+"[customdec.tracedmethod]".
+
+Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:
+
+ ::
+
+ #<example9.py>
+
+ import customdec; customdec.enhance_classes()
+
+ logfile=file('file3.log','w')
+
+ class C(object):
+ "[Decorated]"
+ def fact(self,n):
+ "[tracedmethod] The good old factorial."
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+ C().fact(2) # write a message to logfile
+
+ logfile.close()
+
+ #</example9.py>
+
+Here is the content of ``file3.log``:
+
+>>> import example9 # creates file3.log
+>>> print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments <C instance>(2,){} ...
+ Calling 'C.fact' with arguments <C instance>(1,){} ...
+ Calling 'C.fact' with arguments <C instance>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+
+Defining class decorators
+-----------------------------------------------------------------------
+
+PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a couple of
+class decorator at work, the metaclass ``ClassDecorator``, which gives
+to its instances the ability to interpret class level magic docstrings, and
+its subclass ``Decorated``, which converts functions in method decorators
+if suitable docstrings are found.
+
+To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference of deriving from ``ClassDecorator`` instead
+of deriving from ``type``.
+
+To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:
+
+ ::
+
+ #<customdec.py>
+
+ class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print >> cls.output,"%s created" % cls
+
+ #</customdec.py>
+
+``Logged`` is derived by the metaclass ``ClassDecorator``,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+(meta-)metaclass ``MetaDecorator``).
+Logging capabilities can be added to a class
+by simply using the magic docstring syntax:
+
+ ::
+
+ #<logged.py>
+
+ import customdec; customdec.enhance_classes()
+
+ class D(object): # will print a message at D creation
+ "[Logged]"
+
+ #</logged.py>
+
+>>> import logged
+<class 'logged.D'> created
+
+Notice that the printing representation of ``D`` involves the name
+of ``D`` preceded by the name of its metaclass, which in this case
+is ``Logged``
+
+Each time an instance of ``Logged`` is created, a similar message is printed:
+
+>>> class E(logged.D):
+... pass
+<class 'E'> created
+
+Notice that ``E`` does not have any magic docstring
+
+>>> E.__doc__ # no docstring
+
+but still it inherits its magic from ``D``.
+
+Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:
+
+ ::
+
+ #<customdec.py>
+
+ from types import FunctionType
+
+ class Traced(Decorated):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType) and name!='__str__':
+ # cannot trace __str__, since it is invoked by
+ # tracedmethod and would generate infinite recursion
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d) # decorates the methods
+
+ #</customdec.py>
+
+Here is an example of usage:
+
+>>> class C(object):
+... """[Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... in method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> type(C)
+<class 'customdec.Traced'>
+>>> c=C()
+>>> c.f1()
+Calling 'C.f1' with arguments <C instance>(){} ...
+'C.f1' called with result: None
+>>> c.f2()
+Calling 'C.f2' with arguments <C instance>(){} ...
+'C.f2' called with result: None
+
+By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use ``decorators.enhance_classes()``
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:
+
+ ::
+
+ #<example7.py>
+
+ from example2 import identity,name
+ import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+ class C1(object): # automagically converted to a decorated class
+ identity=identity
+
+ class C2: # automagically converted to a decorated class
+ name=name
+
+ c1=C1() # C1 instance
+ c2=C2() # C2 instance
+
+ #</example7.py>
+
+The magic works both for new style classes such as ``C1``
+
+>>> from example7 import C1,c1
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1
+
+and old style classes such as ``C2`` by implicitly converting them
+to new style classes:
+
+>>> from example7 import C2,c2
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2'
+
+Composing decorators
+---------------------------------------------------------------------
+
+Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod:
+
+ ::
+
+ #<example6.py>
+
+ "How to trace a class method"
+
+ import customdec; customdec.enhance_classes()
+
+ class C(object):
+ "[Decorated]"
+ def fact(cls,n): # a traced classmethod
+ "[classmethod,tracedmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+ #</example6.py>
+
+Here is the testing:
+
+>>> from example6 import C
+>>> C.fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+You may easily check that calling ``C().fact`` will work too.
+
+Under the hood the syntax
+
+ ::
+
+ [classmethod,tracedmethod]
+
+generates a ``classmethodtracedmethod`` class obtained via
+multiple inheritance:
+
+>>> type(C.__dict__['fact'])
+<class 'safetype.classmethodtracedmethod'>
+
+Notice that the order *does* matter and using the docstring
+"[tracedmethod,classmethod]" will not work:
+
+>>> class D:
+... "[Decorated]"
+... def f(cls):
+... "[tracedmethod,classmethod]"
+>>> D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead)
+
+The problem here is that ``tracedmethod.get`` returns a method-wrapper object
+which expects a D instance as first argument whereas it gets ``None`` since
+it is called from the class. On the other hand,
+
+>>> D().f()
+Calling 'D.f' with arguments <D instance>(){} ...
+'D.f' called with result: None
+
+will work. When ``classmethod`` precedes ``tracedmethod``, then
+``classmethod`` passes to ``tracedmeth`` a non-empty first argument,
+i.e. the calling class, even when called from the instance:
+
+>>> C().fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+If we try to trace a staticmethod, we will get a different error with
+the order "tracedmethod, staticmethod":
+
+>>> class F(object):
+... "[Decorated]"
+... def fact(n):
+... "[tracedmethod,staticmethod]"
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+>>> F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead)
+
+The message is self-explanatory.
+
+On the other hand, composing the decorators in the order
+"[tracedmethod,staticmethod]" will work just fine.
+
+It is possible to compose class decorators just as method decorators,
+by using the docstring syntax: for instance we may create a class
+which is both ``Decorated`` and ``Logged`` and by trivially writing
+
+>>> class C:
+... "[Decorated,Logged]"
+... def f():
+... "[staticmethod]"
+... return 'it works!'
+<class C[DecoratedLogged]> created
+>>> C().f()
+'it works!'
+
+Diving into magic
+-----------------------------------------------------------------------
+
+If you never use metaclasses, this part can be safely skipped. On the
+other hand, if you are a metaclass user, you *must* read this section
+in order to keep your code working.
+
+The ``decorators`` module provide a ``ClassDecorator`` metaclass which
+converts a regular (both old style or new style) class in a class with
+the ability to recognize magic docstrings. Under the hood, the
+``decorators.enhance_classes()`` trick works by decorating the
+``object`` class with ``decorators.ClassDecorator`` and by setting
+the custom metaclass to ``decorators.ClassDecorator`` and it is
+equivalent to
+
+ ::
+
+ import decorators
+ object=decorators.ClassDecorator(object) # decorates new style classes
+ __metaclass__= decorators.ClassDecorator # decorates old style classes
+
+If you want the magic to work only for new style classes only, you may
+forget the second line; if you want the magic to work for old style
+classes only, you may forget the first line.
+
+The ``decorators.enhance_classes("[SomeClassDec]")`` syntax looks at the
+magic docstring and executes something like
+
+ ::
+
+ import decorators
+ object=decorators.SomeClassDec(object) # decorates all new style classes
+ __metaclass__= decorators.SomeClassDec # decorates all old style classes
+
+The problem with the ``enhance_classes()`` syntaxes is that it is
+not 100% safe under metaclass conflicts. In order to explain the issue,
+let me give an example.
+
+Suppose we enhance the ``object`` class in the interpreter namespace:
+
+>>> import decorators; decorators.enhance_classes()
+
+making it an instance of ``ClassDecorator``:
+
+>>> object.__class__
+<class 'decorators.ClassDecorator'>
+
+Now, if we naively create a custom metaclass ``M``:
+
+>>> class M(type):
+... "Some non-trivial code here..."
+
+and we try to use it to enhance a new style class ``NwithM``, we will get a
+conflict:
+
+>>> class NwithM(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The problem is that the previous line tries to create a class ``NwithM``
+which should have both metaclasses ``ClassDecorator`` and ``M``:
+a conflict follows.
+
+Fortunately, the decorators module imports the ``type`` metaclass from
+my ``safetype`` module just to avoid this kind of problems. If we
+derive ``M`` from ``decorators.type`` (which is really ``safetype.type``)
+the conflict is automagically avoided:
+
+>>> class M(decorators.type):
+... "This metaclass is conflict-proof"
+>>> class NwithM(object): # it works!
+... __metaclass__=M
+
+The reason why the conflict is avoided is that the ``safetype`` module
+(which makes use of really *deep* metaclass magic) automatically
+creates the composed class ``MClassDecorator`` by multiply inheriting
+from ``M`` and from ``ClassDecorator``. Then, ``NwithM`` can be safely
+created as an instance of ``safetype.MClassDecorator``:
+
+>>> type(NwithM)
+<class 'safetype.MClassDecorator'>
+
+The situation with old style classes is worse since
+apparently
+
+>>> class OwithM: # old style class with metaclass M
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+
+gives no error, but actually ``M`` overrides
+``ClassDecorator``, so ``OwithM`` will not recognize magic docstrings:
+
+>>> type(OwithM)
+<class 'M'>
+>>> OwithM.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with OwithM instance as first argument (got nothing instead)
+
+The simpler solution is to convert ``OwithM`` in a new style class:
+
+>>> class NwithM(OwithM,object):
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+>>> type(NwithM)
+<class 'safetype.MClassDecorator'>
+
+Now ``NwithM`` is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so ``NwithM``
+subclasses with a "[Decorated]" docstring will be decorated:
+
+>>> class E(NwithM):
+... "[Decorated]"
+... def cm(cls):
+... "[classmethod]"
+... print '.cm() called from',cls
+
+>>> E.cm() # it works
+.cm() called from <class E[MClassDecoratorDecorated]>
+
+Notice that ``sm`` was defined in ``NwithM``, the undecorated class: therefore
+it is not decorated:
+
+>>> E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead)
+
+The error message says that ``sm`` is an unbound method and not a
+static method.
+
+It is possible to go even deeper in **black** magic, and to decorate all
+the new style classes in *all* modules, by decorating the
+``__builtin__.object``. Still, naively redefining the ``__builtin__object``
+class is risky, since it will induce metaclass conflicts in other modules
+using metaclasses. In other words, it will work only if you import modules
+not using metaclasses, or modules using metaclasses safely, i.e. modules
+taking care of possible conflicts by using ``safetype.type`` as base metaclass.
+If you really enjoy black magic, you may solve the problem by
+redefining the ``__builtin__.type`` as ``safetype.type``.
+The ``decorators`` module does not go so deep in dark magic, but still
+there are cases where you may want to do it. In these cases you must be
+explicit and redefine the builtins by hand, without help from the
+module. For instance, one of my original motivation for the usage
+of metaclasses/class decorators was to use them for debugging purposes.
+For instance, I wanted to trace the execution of methods in
+complicate inheritance hierarchies, *without changing the source code*.
+For simple situations, i.e. in absence of inheritance and of pre-existing
+metaclasses, the function ``import_with_metaclass`` discussed in the
+`first paper on metaclasses`_ written in collaboration with David Mertz, works
+fine but in general the implementation of a working ``import_with_metaclass``
+is cumbersome. For debugging purposes, the quick and dirty way can be a
+good enough way, so let me show how we can redefine the builtins ``object`` and
+``type`` *before* importing the module, in such a way to enhance *all*
+its classes with tracing capabilities.
+
+Let me start from a module using an unsafe metaclass ``M``, such that it
+cannot be traced trivially by decorating its classes *after* the import;
+moreover there is an inheritance hierarchy, such that it cannot be
+traced correctly by naively decorating all the classes one after the
+other (unless one modifies the original source code, but this forbidden
+by the rules of the game ;)
+
+ ::
+
+ #<tracing.py>
+
+ """
+ This is a pre-existing module not using decorators but using multiple
+ inheritance and unsafe metaclasses. We want to trace it for debugging
+ purposes.
+ """
+
+ class M(type):
+ "There should be some non-trivial code here"
+
+ class B(object):
+ def __init__(self):
+ super(B,self).__init__()
+
+ class D(object):
+ __metaclass__=M
+ def __init__(self):
+ super(D,self).__init__()
+
+ class E(B,D):
+ def __init__(self):
+ super(E,self).__init__()
+
+ #</tracing.py>
+
+Everything works is we modify the builtins before importing the module:
+
+>>> import __builtin__
+>>> __object__=__builtin__.object # the original 'object' class
+>>> __builtin__.object=customdec.Traced('tracedobject',(),{})
+>>> __builtin__.type=customdec.type # a safe 'type' class
+
+Now, when we import the module, all the classes ``M``, ``B``, ``D`` and ``E``
+will be created starting for the tricked builtins, so they will be traced
+and safe under conflicts. For instance, let me show that ``E`` is created
+with a conflict safe ``MTraced`` metaclass:
+
+>>> from tracing import E
+>>> print E
+<class E[MTraced]>
+
+This shows that the ``__init__`` methods are traced indeed and shows the
+Method Resolution Order:
+
+>>> e=E()
+Calling 'E.__init__' with arguments <E instance>(){} ...
+ Calling 'B.__init__' with arguments <E instance>(){} ...
+ Calling 'D.__init__' with arguments <E instance>(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None
+
+This works, indeed. At the end, one should not forget to restore
+the standard builtins, otherwise it will trace *all* the classes
+created thereafter.
+
+>>> __builtin__.object=__object__ # restore the __builtin__
+>>> __builtin__.type=decorators.__type__ # restore the __builtin__
+
+At the end, I will notice that it is possible to solve the problem more
+nicely, without redefining the builtins, but I will discuss the solution
+elsewhere ;)
+
+Advanced usage
+---------------------------------------------------------------------------
+
+Whereas the average programmer is expected to use the ``decorator()``
+and ``enhance_classes()`` functions only, the ``decorators`` module provides
+facilities which may be useful to the advanced programmer.
+
+The simplest is an utility function which retrieves the list of
+recognized decorators, ``decorators.getdec()``. The precise syntax is
+``decorators.getdec(docstring)``, where ``docstring``
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance ``decorators.getdec('[MethodDecorator]')``
+gives the list of all subclasses of ``MethodDecorator``, i.e. all method
+decorators, whereas ``decorators.getdec('[ClassDecorator]')``
+gives the list of the known class decorators. The utility of the function
+is that it also returns decorators that have been automagically created:
+
+>>> decorators.getdec("[classmethodtracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]
+
+It is even possible to use the comma notation:
+
+>>> decorators.getdec("[classmethod,tracedmethod]")
+[<class 'safetype.classmethodtracedmethod'>]
+
+If you mispell a decorator name you get an helpful error message:
+
+>>> class E:
+... "[Decorated]"
+... def f():
+... "[staticmeth]"
+Traceback (most recent call last):
+ .. a cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+
+
+By design, the ``decorated`` function is quite limited, and does not
+decorate functions without magic docstrings. This is safe and also convenient
+for internal usage of the ``decorated`` function in the ``Decorated``
+metaclass. Nevertheless, advanced users may want to have the ability
+of decorating functions by hand, without invoking `decorators.decorator()``
+and magic docstrings. This is possible, by calling the decorator directly.
+For instance, here is how to convert a lambda function in a staticmethod:
+
+>>> do_nothing=decorators.staticmethod(lambda:None)
+>>> print do_nothing # ``do_nothing`` is a static method
+<staticmethod:<lambda>>
+
+The most convenient usage of this feature is in the composition of decorators.
+For instance, we may compose the just defined ``staticmethod`` with a
+``chattymethod2``:
+
+>>> chattystatic=customdec.chattymethod2(do_nothing)
+>>> print chattystatic
+<chattymethod2staticmethod:<lambda>>
+
+The syntax
+
+ ``decorator1(decorator2(obj))``
+
+automagically creates a composed class ``decorator1decorator2`` in the
+``safetype`` module (or recycle it, if ``decorator1decorator2`` has
+been already created) and it is equivalent to
+
+ ``decorator1decorator2(obj)``
+
+Here is the check that everything works:
+
+>>> class B:
+... chattystatic=chattystatic
+>>> B.chattystatic()
+calling <chattymethod2staticmethod:<lambda>> from <class 'B'>
+
+Notice that ``chattystatic`` does not know the class where it
+is defined:
+
+>>> chattystatic.__klass__
+<class 'safetype.?'>
+
+unless the ``__klass__`` attribute is explicitly set.
+
+This is the hierarchy:
+
+>>> for C in type(chattystatic).mro(): print C
+...
+<class 'safetype.chattymethod2staticmethod'>
+<class 'customdec.chattymethod2'>
+<class 'customdec.chattymethod'>
+<class 'decorators.staticmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.decorator'>
+<type 'object'>
+
+One can also compose classes by hand, by using class decorators:
+
+>>> class C:
+... "[Logged]"
+... def f(): "[staticmethod]"
+...
+<class 'C'> created
+>>> C=customdec.Traced(C)
+<class C[TracedLogged]> created
+
+
+The ``decorators.enhance_classes(<classdecorator>)`` syntax performs
+the composition automagically:
+
+ ::
+
+ #<example8.py>
+
+ from example2 import identity,name
+ import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+ class C1: # automagically converted to a Decorated class
+ identity=identity
+
+ class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+ c1=C1() # C1 instance
+ c2=C2() # C2 instance
+
+ #</example8.py>
+
+In this example the class ``C2`` has already a magic docstring. This means that
+``C2`` has to be enhanced both from ``Logged`` and from ``Decorated``.
+This is done by automagically creating a ``DecoratedLogged`` class
+decorator:
+
+>>> from example8 import C1,C2,c1,c2
+<class C2[DecoratedLogged]> created
+
+The second line is printed because of the logging capabilities of ``C2``.
+Moreover, since ``C2`` is decorated too, the following will work:
+
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2'
+
+Idem for ``C1``:
+
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1
+
+The implementation
+============================================================================
+
+This part can be safely skipped, unless you are a *really* curious and
+you want to know how the implementation works.
+
+The module is rather short (less than 150 lines if you skip comments and
+docstrings) but quite non-trivial, since it is based on extensive
+usage of metaclass wizardry. Moreover,
+it invokes the ``safetype`` module to solve metaclass conflicts, and
+``safetype`` contains 50 lines of *really* deep metaclass magic.
+
+ .. figure:: decorators.png
+
+The main class-metaclass hierarchy is represented in figure, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.
+
+For instance, this is the Method Resolution Order for the ``Decorated``
+metaclass:
+
+>>> for i,c in enumerate(decorators.Decorated.__mro__):
+... print i,c,"[%s]" % type(c).__name__
+0 <class 'decorators.Decorated'> [MetaDecorator]
+1 <class 'decorators.ClassDecorator'> [MetaDecorator]
+2 <class 'safetype.safetype'> [type]
+3 <type 'type'> [type]
+4 <class 'decorators.decorator'> [MetaDecorator]
+5 <type 'object'> [type]
+
+``Decorator`` is the mother of all decorators. Its main purpose it to
+provide the ``MetaDecorator``
+metaclass to all decorators.
+
+The ``safetype`` metaclass, imported from the ``safetype`` module,
+ensures that class decorators can generate classes without incurring
+in conflicts.
+
+Since class decorators are metaclasses,
+``MetaDecorator`` is actually a meta-metaclass with respect to
+instances of decorated classes. For this reason if
+
+>>> C=decorators.ClassDecorator('C',(),{})
+
+then
+
+>>> C.__class__
+<class 'decorators.ClassDecorator'>
+
+but
+
+>>> C.__metaclass__
+<class 'decorators.MetaDecorator'>
+
+since the ``C.__metaclass__`` attribute is inherited from ``Decorator``.
+
+On the other hand, ``MetaDecorator`` is a simple metaclass with
+respect to instances of decorated methods.
+
+The implementation is relatively smart. Consider for instance the case of
+the ``logged`` example. In that example class ``D`` was a subclass
+of a tricked ``object`` class, enhanced by ``ClassDecorator``. Moreover ``D``
+had a ``Logged`` docstring. Therefore it should have been an instance of a
+``ClassDecoratorLogged`` metaclass. But logged was
+a subclass of ``ClassDecorator``, therefore it already had all the features
+of ``ClassDecorator`` and it would have been redundant to create
+``ClassDecoratorLogged``, when``Logged`` would have been enough.
+So ``Logged`` was used and ``ClassDecoratorLogged`` was never created.
+All this magic is in the ``safetype.remove_redundant(*classes)`` function.
+
+The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. Probably, they do not matter unless one has to decorate
+thousands or millions of functions and classes.
+
+Finally, a word about bugs. The ``decorators`` module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~200 tests, most of them extracted from the documentation you are reading),
+I cannot guarantee that it is correct. If somebody finds a bug or an
+unexpected behavior, please let me know and I will fix it or document it.
+
+.. References:
+
+.. _PEP 318:
+ http://www.python.org/pep
+.. _cooperative methods:
+ http://www.python.org/2.3/descrintro.html
+.. _new style classes and old style classes:
+ http://www.python.org/2.3/descrintro.html
+.. _descriptors: http://users.rcn.com/python/download/Descriptor.htm
+.. _cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+.. _first paper on metaclasses:
+ http://www-106.ibm.com/developerworks/library/l-pymeta.html
+.. _metaclasses: http://www-106.ibm.com/developerworks/library/l-pymeta2.html
+
+.. include:: decorators.rst
diff --git a/pypers/pep318/example.py b/pypers/pep318/example.py
new file mode 100755
index 0000000..aedb6f3
--- /dev/null
+++ b/pypers/pep318/example.py
@@ -0,0 +1,16 @@
+# example.py
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+class C1: # automagically converted to a decorated class
+ identity=identity
+
+class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+
diff --git a/pypers/pep318/example1.py b/pypers/pep318/example1.py
new file mode 100755
index 0000000..5e8f7c8
--- /dev/null
+++ b/pypers/pep318/example1.py
@@ -0,0 +1,29 @@
+# example1.py
+
+import decorators
+
+def do_nothing(self):
+ "No magic docstring here"
+dec_do_nothing=decorators.decorator(do_nothing)
+
+def identity(x):
+ "[staticmethod]"
+ return x
+dec_identity=decorators.decorator(identity)
+
+def name(cls):
+ "[classmethod]"
+ return cls.__name__
+dec_name=decorators.decorator(name)
+
+class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+class NewStyle(object):
+ name=dec_name
+
+o=OldStyle() # creates an old style instance
+n=NewStyle() # creates a new style instance
+
+
diff --git a/pypers/pep318/example2.py b/pypers/pep318/example2.py
new file mode 100755
index 0000000..0377c0c
--- /dev/null
+++ b/pypers/pep318/example2.py
@@ -0,0 +1,22 @@
+# example2.py
+
+from decorators import decorator
+from example1 import do_nothing,identity,name
+
+class B(object):
+ "This is a regular class"
+
+B=decorator(B) or B # return the original B
+
+class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ class Inner: # old style class
+ "[Decorated]" # required docstring
+ name=name
+
+C=decorator(C) # regenerate the class converting methods in decorators
+c=C()
+
+
diff --git a/pypers/pep318/example3.py b/pypers/pep318/example3.py
new file mode 100755
index 0000000..5f3ddac
--- /dev/null
+++ b/pypers/pep318/example3.py
@@ -0,0 +1,22 @@
+# example3.py
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ "[Decorated]" # magic docstring here
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+class D(object):
+ "Undecorated" # no magic docstring here
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+c=C(); d=D()
+
+
diff --git a/pypers/pep318/example4.py b/pypers/pep318/example4.py
new file mode 100755
index 0000000..723c7c5
--- /dev/null
+++ b/pypers/pep318/example4.py
@@ -0,0 +1,20 @@
+# example4.py
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+
+assert C.identity(1) == 1
+assert C.Inner.name() == 'Inner'
+
+
diff --git a/pypers/pep318/example5.py b/pypers/pep318/example5.py
new file mode 100755
index 0000000..81ebce9
--- /dev/null
+++ b/pypers/pep318/example5.py
@@ -0,0 +1,20 @@
+# example5.py
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+
+assert C.identity(1) == 1
+assert C.Inner.name() == 'Inner'
+
+
diff --git a/pypers/pep318/example6.py b/pypers/pep318/example6.py
new file mode 100755
index 0000000..145c06a
--- /dev/null
+++ b/pypers/pep318/example6.py
@@ -0,0 +1,14 @@
+# example6.py
+
+"How to trace a class method"
+
+import customdec; customdec.enhance_classes()
+
+class C(object):
+ "[Decorated]"
+ def fact(cls,n): # a traced classmethod
+ "[classmethod,tracedmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+
diff --git a/pypers/pep318/example7.py b/pypers/pep318/example7.py
new file mode 100755
index 0000000..ce12589
--- /dev/null
+++ b/pypers/pep318/example7.py
@@ -0,0 +1,15 @@
+# example7.py
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+class C1(object): # automagically converted to a decorated class
+ identity=identity
+
+class C2: # automagically converted to a decorated class
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+
diff --git a/pypers/pep318/example8.py b/pypers/pep318/example8.py
new file mode 100755
index 0000000..5b4f9df
--- /dev/null
+++ b/pypers/pep318/example8.py
@@ -0,0 +1,16 @@
+# example8.py
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+class C1: # automagically converted to a Decorated class
+ identity=identity
+
+class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+
diff --git a/pypers/pep318/example9.py b/pypers/pep318/example9.py
new file mode 100755
index 0000000..8a6fd33
--- /dev/null
+++ b/pypers/pep318/example9.py
@@ -0,0 +1,19 @@
+# example9.py
+
+import customdec; customdec.enhance_classes()
+
+logfile=file('file3.log','w')
+
+class C(object):
+ "[Decorated]"
+ def fact(self,n):
+ "[tracedmethod] The good old factorial."
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+C().fact(2) # write a message to logfile
+
+logfile.close()
+
+
diff --git a/pypers/pep318/lessmeta/decorators.py b/pypers/pep318/lessmeta/decorators.py
new file mode 100755
index 0000000..b452c24
--- /dev/null
+++ b/pypers/pep318/lessmeta/decorators.py
@@ -0,0 +1,210 @@
+# Simpler implementation with smaller usage of metaclasses
+
+"""
+A module to implement pep318 (decorator syntax) via magic doctrings.
+It defines two new classes, MethodDecorator and ClassDecorator,
+which are meant to be subclassed.
+
+def f(x):
+ "[chattymethod]"
+
+as a shortcut for
+
+def f(x):
+ pass
+f=chattymethod(f)
+
+Decorators can be composed and for instance
+
+def f(x):
+ "[chattymethod,classmethod]"
+
+is a shortcut for
+
+def f(x):
+ pass
+f=chattymethodclassmethod(f)
+
+where 'chattymethodclassmethod' is a decorator class obtained by
+multiple inheriting from chattymethod and classmethod.
+Notice that the built-in classmethod descriptor (idem for
+staticmethod) is non=cooperatice, whereas the custom
+descriptor chattymethod is cooperative: this means that
+the custom descriptor has to be put *first* in the list.
+
+The implementation stores the generated descriptors in a
+dictionary, and avoids creating unneeded subclasses.
+
+The names in the module are quite specific, since I am trying to avoid
+troubles to people using the form "from decorator import *".
+"""
+
+import sys,re,inspect,__builtin__,time,noconflict
+anyTrue=sum
+
+############################ UTILITIES ##############################
+
+class UnknownDecoratorError(Exception):
+ "In case of mistakes"
+
+class StoredDecorators(type):
+ "Metaclass storing its instances in the dictionary dic"
+ dic={}
+
+ def __init__(cls,*args):
+ super(StoredDecorators,cls).__init__(*args)
+ StoredDecorators.dic[cls.__name__]=cls
+ get=cls.__dict__.get('get') # if get
+ if get: cls.__get__=get # set __get__
+
+ def methodlike(mcl):
+ "List of recognized MethodDecorators"
+ return [name for name in mcl.dic
+ if issubclass(mcl.dic[name],MethodDecorator)]
+ methodlike=classmethod(methodlike)
+
+ def classlike(mcl):
+ "List of recognized ClassDecorators"
+ return [name for name in mcl.dic
+ if issubclass(mcl.dic[name],ClassDecorator)]
+ classlike=classmethod(classlike)
+
+def decorate(obj='THISMODULE'):
+ """
+ obj can be:
+ - the string 'THISMODULE'
+ in this case magic docstrings are interpreted in the new
+ style classes of the calling module;
+ - the string 'ALLMODULES'
+ in this case magic docstrings are interpreted in the new
+ style classes of ALL modules;
+ - a dictionary or an object with a dictionary
+ in this case magic docstrings are interpreted in all the
+ functions and classes in the dictionary
+ """
+ dic={}
+ if obj=='THISMODULE':
+ callerglobals=sys._getframe(1).f_globals
+ callerglobals['object']=_EnableMagicDocstrings
+ elif obj=='ALLMODULES':
+ __builtin__.object=_EnableMagicDocstrings
+ elif isinstance(obj,dict):
+ dic=obj
+ elif hasattr(obj,'__dict__'):
+ dic=obj.__dict__
+ else:
+ raise TypeError("Dictionary or object with a __dict__ required")
+ for name,value in dic.items():
+ _set(obj,name,value)
+
+def _decorate(obj):
+ """Given an object with a magic docstrings, returns its decorated
+ version; otherwise, returns None"""
+ docstring=inspect.getdoc(obj) or ''
+ MO=re.match(r'\[([\w_ ,]*)\]',docstring)
+ # for instance [name_1, name_2, name_3] is matched
+ if MO:
+ decorators=MO.group(1).split(',')
+ try: dclasses=[StoredDecorators.dic[n] for n in decorators]
+ except KeyError: raise UnknownDecoratorError(n)
+ dclasses=noconflict.remove_redundant(dclasses)
+ decname=''.join([d.__name__ for d in dclasses])
+ decorator=StoredDecorators.dic.get(decname)
+ if not decorator: decorator=makecls()(decname,dclasses,{})
+ if issubclass(decorator,ClassDecorator): return decorator(obj)()
+ return decorator(obj)
+
+def _set(objdict,name,obj):
+ dec=_decorate(obj)
+ if dec:
+ dec.inside=objdict
+ if isinstance(objdict,dict):
+ objdict[name]=dec
+ else:
+ setattr(objdict,name,dec)
+
+class _MagicDocstrings:
+ def __init__(cls,name,bases,dic):
+ decorate(cls) # both cls and its dictionary
+
+class _EnableMagicDocstrings:
+ __metaclass__=_MagicDocstrings
+
+class Decorator(object):
+ """Instance of StoredDecorators, i.e. each time Decorator is
+ subclassed, StoredDecorators.dic is updated. Provides a setattributes
+ method to recognize magic attributes with ``set_`` prefix.
+ Provides an 'inside' attribute (default='?')"""
+ __metaclass__=StoredDecorators
+ inside=type('?',(),{}) # default placeholder class (to be
+ # replaced by the class that contains the decorated method)
+ def setattributes(self):
+ for a in dir(self):
+ if a.startswith('set_'):
+ setattr(self,a[4:],getattr(self,a))
+
+class MethodDecorator(Decorator):
+ """MethodDecorator objects provide a 'get' method and a 'str' method"""
+ def __init__(self,func):
+ super(MethodDecorator,self).__init__(func)
+ self.func=func; self.setattributes()
+ def get(self,obj,cls=None): # default, to be overridden
+ return self.func.__get__(obj,cls)
+ def __str__(self):
+ return '<%s:%s>' % (self.__class__.__name__,self.func.__name__)
+
+class ClassDecorator(Decorator):
+ """ClassDecorator takes a class as argument and returns a callable
+ object acting as a factory of decorated objects"""
+ def __init__(self,klass):
+ super(ClassDecorator,self).__init__(klass)
+ self.klass=klass; self.setattributes()
+ def __call__(self): # to be cooperatively overridden
+ return self.klass
+ def __str__(self):
+ return '<%s:%s>' % (self.__class__.__name__,self.klass.__name__)
+
+#################### Useful Method Decorators ######################
+
+class staticmethod(MethodDecorator):
+ "Decorator, converts a function in a staticmethod"
+ def get(self,obj,cls=None):
+ super(staticmethod,self).get(obj,cls)
+ return self.func
+
+class classmethod(MethodDecorator):
+ "Decorator, converts a function in a classmethod"
+ def get(self,obj,cls=None):
+ if cls is None: cls=type(obj)
+ return super(classmethod,self).get(cls,cls)
+
+class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a function in a traced method"
+ indent=0; output=sys.stdout # defaults
+ def __init__(self,func):
+ super(tracedmethod,self).__init__(func)
+ self.funcname=self.func.__name__
+ def get(self,obj,cls):
+ clsname=self.inside.__name__
+ boundmethod=super(tracedmethod,self).get(obj,cls)
+ def _(*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s ...\n" % (str(args)+str(kw)))
+ res=boundmethod(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return _
+
+####################### Class Decorators ###############################
+
+class Register(ClassDecorator):
+ output=sys.stdout
+ def __call__(self):
+ cls=super(Register,self).__call__()
+ print >> self.output, "%s: %s created" % (time.asctime(),cls)
+ return cls
diff --git a/pypers/pep318/logged.py b/pypers/pep318/logged.py
new file mode 100755
index 0000000..04be3fd
--- /dev/null
+++ b/pypers/pep318/logged.py
@@ -0,0 +1,8 @@
+# logged.py
+
+import customdec; customdec.enhance_classes()
+
+class D(object): # will print a message at D creation
+ "[Logged]"
+
+
diff --git a/pypers/pep318/mod.py b/pypers/pep318/mod.py
new file mode 100755
index 0000000..4e7073d
--- /dev/null
+++ b/pypers/pep318/mod.py
@@ -0,0 +1,11 @@
+# mod.py
+
+#"[TraceFunctions]"
+
+def f1(): pass
+
+def f2(): pass
+
+def f3(): "-untraced-"
+
+
diff --git a/pypers/pep318/module.py b/pypers/pep318/module.py
new file mode 100755
index 0000000..d22c798
--- /dev/null
+++ b/pypers/pep318/module.py
@@ -0,0 +1,13 @@
+# module.py
+
+"Magically decorated module"
+
+import decorators,sys
+
+thismodule=sys.modules[__name__]
+
+class MyClass: "[Decorated]"
+
+newmod=decorators.decorated(thismodule)
+
+
diff --git a/pypers/pep318/moduledec.py b/pypers/pep318/moduledec.py
new file mode 100755
index 0000000..ccaa73c
--- /dev/null
+++ b/pypers/pep318/moduledec.py
@@ -0,0 +1,51 @@
+
+
+err=file('err','w')
+
+def printerr(*args):
+ "For debugging purposes"
+ for a in args: print >> err, a,
+ print >> err
+
+def importmodule(name,dic):
+ """Given a module name and a dictionary, executes the module in a copy
+ of the dictionary and returns a new module."""
+ already_imported=sys.modules.get(name)
+ if already_imported: return already_imported # do nothing
+ fname=name+'.py'
+ dic=dic.copy()
+ execfile(fname,dic)
+ mod=types.ModuleType(name)
+ for k,v in dic.iteritems():
+ setattr(mod,k,v)
+ sys.modules[name]=mod
+ mod.__name__=name
+
+ mod.__file__=fname
+ return mod
+
+class ModuleDecorator(Decorator,types.ModuleType):
+ def __init__(self,mod): # non-cooperative
+ self.undecorated=mod
+ for k,v in mod.__dict__.iteritems():
+ setattr(self,k,v)
+ decorate(self)
+ def __str__(self):
+ return '<module %s[%s]>' % (self.mod.__name__,self.__class__.__name__)
+
+class DecoratedModule(ModuleDecorator): # defined for readability
+ pass
+
+def callModuleDecorator(dec,*args):
+ if issubclass(dec,ModuleDecorator):
+ nargs=len(args)
+ if nargs==1:
+ mod=args[0]
+ elif nargs==2:
+ name,glob=args # a different object for each module
+ glob['object']=ClassDecorator(object)
+ mod=importmodule(name,glob)
+ else:
+ raise TypeError('%s() takes 1 or 2 arguments' % dec.__name__)
+ return type.__call__(dec,mod)
+
diff --git a/pypers/pep318/moduledec.txt b/pypers/pep318/moduledec.txt
new file mode 100755
index 0000000..602aec4
--- /dev/null
+++ b/pypers/pep318/moduledec.txt
@@ -0,0 +1,97 @@
+
+
+
+Module decorators
+-----------------------------------------------------------------------
+
+Finally, one can decorate entire modules through the concept of
+*module decorators*. Module decorators have the ability of decorating
+modules by changing their dictionary. Custom module decorators
+should be derived from the class ``decorators.ModuleDecorator``, by
+cooperatively overriding its ``__init__(self,mod)`` method. Writing
+a module decorator is a bit tricky, but I do expect only
+expert programmers to play this kind of game.
+For instance, suppose one wants to trace all the functions in a module,
+unless they have the docstring "-untraced-" . This can be done with a
+suitable module decorator which modifies the module dictionary.
+Here is an example
+
+ ::
+
+ #<customdec.py>
+
+ class TraceFunctions(ModuleDecorator):
+ def __init__(self,mod):
+ super(TraceFunctions,self).__init__(mod)
+ for name,func in self.__dict__.items():
+ if inspect.isfunction(func):
+ doc=func.__doc__ or ''
+ if doc.startswith('-untraced-'):
+ pass # do nothing
+ else:
+ def traced(func):
+ def tracedfunc(*args,**kw):
+ print 'called',func.__name__
+ return func(*args,**kw)
+ return tracedfunc
+ setattr(self,name,traced(func))
+
+ #</customdec.py>
+
+There is no way of tinkering with the attribute access in modules (as
+opposed to attribute access in classes) so we cannot used descriptors
+here and we are forced to use closures.
+
+Let me test that the module decorator does its job. Consider the module
+
+ ::
+
+ #<mod.py>
+
+ #"[TraceFunctions]"
+
+ def f1(): pass
+
+ def f2(): pass
+
+ def f3(): "-untraced-"
+
+ #</mod.py>
+
+By importing this module, only the functions ``f1`` and ``f2`` should
+be traced. This is indeed the case:
+
+>>> from customdec import TraceFunctions
+>>> mod=TraceFunctions('mod',{})
+>>> mod.f1()
+called f1
+>>> mod.f2()
+called f2
+>>> mod.f3() # does nothing, correct
+
+One has always access to the original, undecorated module, via the
+``undecorated`` attribute:
+
+>>> orig=mod.undecorated
+>>> orig.f1() # does nothing, correct
+
+ ::
+
+ #<module.py>
+
+ "Magically decorated module"
+
+ import decorators,sys
+
+ thismodule=sys.modules[__name__]
+
+ class MyClass: "[Decorated]"
+
+ newmod=decorators.decorated(thismodule)
+
+ #</module.py>
+
+>>> from module import *
+>>> assert isinstance(newmod.MyClass, decorators.Decorated)
+>>> assert isinstance(newmod,decorators.DecoratedModule)
+
diff --git a/pypers/pep318/mydoc.html b/pypers/pep318/mydoc.html
new file mode 100755
index 0000000..fb70714
--- /dev/null
+++ b/pypers/pep318/mydoc.html
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Documentation of the mydoc module</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="documentation-of-the-mydoc-module">
+<h1 class="title">Documentation of the mydoc module</h1>
+<p>Short utility to extract documentation from a module</p>
+<div class="section" id="documented-functions">
+<h1><a name="documented-functions">Documented functions</a></h1>
+<p><tt class="literal"><span class="pre">publish_cmdline(reader=None,</span> <span class="pre">reader_name='standalone'</span></tt></p>
+<blockquote>
+<p>Set up &amp; run a <cite>Publisher</cite>. For command-line front ends.</p>
+<p>Parameters:</p>
+<ul class="simple">
+<li><cite>reader</cite>: A <cite>docutils.readers.Reader</cite> object.</li>
+<li><cite>reader_name</cite>: Name or alias of the Reader class to be instantiated if
+no <cite>reader</cite> supplied.</li>
+<li><cite>parser</cite>: A <cite>docutils.parsers.Parser</cite> object.</li>
+<li><cite>parser_name</cite>: Name or alias of the Parser class to be instantiated if
+no <cite>parser</cite> supplied.</li>
+<li><cite>writer</cite>: A <cite>docutils.writers.Writer</cite> object.</li>
+<li><cite>writer_name</cite>: Name or alias of the Writer class to be instantiated if
+no <cite>writer</cite> supplied.</li>
+<li><cite>settings</cite>: Runtime settings object.</li>
+<li><cite>settings_spec</cite>: Extra settings specification; a <cite>docutils.SettingsSpec</cite>
+subclass. Used only if no <cite>settings</cite> specified.</li>
+<li><cite>settings_overrides</cite>: A dictionary containing program-specific overrides
+of component settings.</li>
+<li><cite>argv</cite>: Command-line argument list to use instead of <tt class="literal"><span class="pre">sys.argv[1:]</span></tt>.</li>
+<li><cite>usage</cite>: Usage string, output if there's a problem parsing the command
+line.</li>
+<li><cite>description</cite>: Program description, output for the &quot;--help&quot; option
+(along with command-line option descriptions).</li>
+</ul>
+</blockquote>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="mydoc.rst">View document source</a>.
+Generated on: 2003-09-20 11:43 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/nonrecognized.py b/pypers/pep318/nonrecognized.py
new file mode 100755
index 0000000..d8e3be5
--- /dev/null
+++ b/pypers/pep318/nonrecognized.py
@@ -0,0 +1,23 @@
+# nonrecognized.py
+
+"Classes and functions inside a function are not decorated"
+
+import decorators; decorators.enable()
+
+def outer():
+
+ class C(object):
+ def f():
+ "[staticmethod]"
+
+ def g():
+ "[staticmethod]"
+
+ # testing
+
+ assert isinstance(C.__dict__['f'],decorators.staticmethod)
+ assert not isinstance(g,decorators.staticmethod)
+
+outer()
+
+
diff --git a/pypers/pep318/oopp.html b/pypers/pep318/oopp.html
new file mode 100755
index 0000000..948dad1
--- /dev/null
+++ b/pypers/pep318/oopp.html
@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Documentation of the oopp module</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="documentation-of-the-oopp-module">
+<h1 class="title">Documentation of the oopp module</h1>
+<div class="section" id="documented-metaclasses">
+<h1><a name="documented-metaclasses">Documented metaclasses</a></h1>
+<p><tt class="literal"><span class="pre">Final(type):</span> <span class="pre">#</span> <span class="pre">better</span> <span class="pre">derived</span> <span class="pre">from</span> <span class="pre">WithCounter,typ</span></tt></p>
+<blockquote>
+Instances of Final cannot be derived</blockquote>
+<p><tt class="literal"><span class="pre">AutoWrapped(type)</span></tt></p>
+<blockquote>
+Metaclass that looks at the methods declared in the attributes
+builtinlist and wraplist of its instances and wraps them with
+autowrappedmethod.</blockquote>
+<p><tt class="literal"><span class="pre">Wrapped(Customizable,type)</span></tt></p>
+<blockquote>
+<p>A customizable metaclass to wrap methods with a given wrapper and
+a given condition</p>
+<p><tt class="literal"><span class="pre">Reflective(type)</span></tt></p>
+<blockquote>
+Cooperative metaclass that defines the private variable __this in
+its instances. __this contains a reference to the class, therefore
+it allows anonymous cooperative super calls in the class.</blockquote>
+<p><tt class="literal"><span class="pre">wrappedmethod(Customizable)</span></tt></p>
+<blockquote>
+Customizable method factory intended for derivation.
+The wrapper method is overridden in the children.</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">Cooperative(BracketCallable,type)</span></tt></p>
+<blockquote>
+<p>Bracket-callable metaclass implementing cooperative methods. Works
+well for plain methods returning None, such as __init__</p>
+<p><tt class="literal"><span class="pre">coop_method(cls,name,method):</span> <span class="pre">#</span> <span class="pre">method</span> <span class="pre">can</span> <span class="pre">be</span> <span class="pre">Non</span></tt></p>
+<blockquote>
+Calls both the superclass method and the class method (if the
+class has an explicit method). Implemented via a closure</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">Logged(WithLogger,Reflective)</span></tt></p>
+<blockquote>
+Metaclass that reuses the features provided by WithLogger.
+In particular the classes created by Logged are Reflective,
+PrettyPrinted and Customizable.</blockquote>
+<p><tt class="literal"><span class="pre">MagicallyTransformed(type)</span></tt></p>
+<blockquote>
+Metaclass changing the formatstring of its instances</blockquote>
+<p><tt class="literal"><span class="pre">Printable(PrettyPrinted,type)</span></tt></p>
+<blockquote>
+Apparently does nothing, but actually makes PrettyPrinted acting as
+a metaclass.</blockquote>
+</div>
+<div class="section" id="documented-classes">
+<h1><a name="documented-classes">Documented classes</a></h1>
+<p><tt class="literal"><span class="pre">convert2descriptor(object)</span></tt></p>
+<blockquote>
+<p>To all practical means, this class acts as a function that, given an
+object, adds to it a __get__ method if it is not already there. The
+added __get__ method is trivial and simply returns the original object,
+independently from obj and cls.</p>
+<p><tt class="literal"><span class="pre">__get__(self,obj,cls=None)</span></tt></p>
+<blockquote>
+Returns self.a independently from obj and cls</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">Object(object)</span></tt></p>
+<blockquote>
+A convenient Object class</blockquote>
+<p><tt class="literal"><span class="pre">GeometricFigure(object):</span> <span class="pre">#an</span> <span class="pre">example</span> <span class="pre">of</span> <span class="pre">object</span> <span class="pre">factor</span></tt></p>
+<blockquote>
+<p>This class allows to define geometric figures according to their
+equation in the cartesian plane. It will be extended later.</p>
+<p><tt class="literal"><span class="pre">__init__(self,equation,**parameters)</span></tt></p>
+<blockquote>
+Specify the cartesian equation of the object and its parameters</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">UglyDuckling(PrettyPrinted)</span></tt></p>
+<blockquote>
+A plain, regular class</blockquote>
+<p><tt class="literal"><span class="pre">autowrappedmethod(wrappedmethod)</span></tt></p>
+<blockquote>
+Makes the method returning cls instances, by wrapping its
+output with cls</blockquote>
+<p><tt class="literal"><span class="pre">Clock(object)</span></tt></p>
+<blockquote>
+Clock with a staticmethod</blockquote>
+<p><tt class="literal"><span class="pre">AccessError(object)</span></tt></p>
+<blockquote>
+Descriptor raising an AttributeError when the attribute is
+accessed</blockquote>
+<p><tt class="literal"><span class="pre">Frozen(object)</span></tt></p>
+<blockquote>
+Subclasses of Frozen are frozen, i.e. it is impossibile to add
+new attributes to them and their instances</blockquote>
+<p><tt class="literal"><span class="pre">Timer(Clock)</span></tt></p>
+<blockquote>
+Inherits the get_time staticmethod from Clock</blockquote>
+<p><tt class="literal"><span class="pre">Vector(list)</span></tt></p>
+<blockquote>
+Implements finite dimensional vectors as lists. Can be instantiated
+as Vector([a,b,c,..]) or as Vector(a,b,c ..)</blockquote>
+<p><tt class="literal"><span class="pre">Singleton(WithCounter)</span></tt></p>
+<blockquote>
+If you inherit from me, you can only have one instance</blockquote>
+<p><tt class="literal"><span class="pre">Homo(PrettyPrinted)</span></tt></p>
+<blockquote>
+Defines the method 'can', which is intended to be overriden
+in the children classes, and inherits '__str__' from PrettyPrinted,
+ensuring a nice printing representation for all children.</blockquote>
+<p><tt class="literal"><span class="pre">kwdict(dict)</span></tt></p>
+<blockquote>
+<p>Keyword dictionary base class</p>
+<p><tt class="literal"><span class="pre">pretty(dic)</span></tt></p>
+<blockquote>
+Returns a nice string representation for the dictionary</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">WithLogger(WithCounter,PrettyPrinted)</span></tt></p>
+<blockquote>
+WithLogger inherits from WithCounter the 'count' class attribute;
+moreover it inherits '__str__' from PrettyPrinted</blockquote>
+<p><tt class="literal"><span class="pre">Get(object)</span></tt></p>
+<blockquote>
+Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod,
+property, plainmethod, plaindata, returns the corresponding
+attributes as a keyword dictionary. It works by internally calling
+the routine inspect.classify_class_attrs. Notice that special data
+attributes are not retrieved (this is by design).</blockquote>
+<p><tt class="literal"><span class="pre">Makeobj(object)</span></tt></p>
+<blockquote>
+A factory of object factories. Makeobj(cls) returns instances
+of cls</blockquote>
+<p><tt class="literal"><span class="pre">ExampleBaseClass(PrettyPrinted)</span></tt></p>
+<blockquote>
+Contains a regular method 'm', a staticmethod 's', a classmethod
+'c', a property 'p' and a data attribute 'd'.</blockquote>
+<p><tt class="literal"><span class="pre">TransformedUglyDuckling(PrettyPrinted)</span></tt></p>
+<blockquote>
+A class metamagically modified</blockquote>
+<p><tt class="literal"><span class="pre">Customizable(object)</span></tt></p>
+<blockquote>
+Classes inhering from 'Customizable' have a 'with' method acting as
+an object modifier and 'With' classmethod acting as a class factory</blockquote>
+<p><tt class="literal"><span class="pre">WithMultiCounter(WithCounter)</span></tt></p>
+<blockquote>
+Each time a new subclass is derived, the counter is reset</blockquote>
+<p><tt class="literal"><span class="pre">BracketCallable(object)</span></tt></p>
+<blockquote>
+Any subclass C(BracketCallable) can be called with the syntax C[t],
+where t is a tuple of arguments stored in bracket_args; returns the
+class or an instance of it, depending on the flag 'returnclass'.</blockquote>
+<p><tt class="literal"><span class="pre">ClsFactory(BracketCallable)</span></tt></p>
+<blockquote>
+<dl>
+<dt>Bracket callable non-cooperative class acting as </dt>
+<dd>a factory of class factories.
+ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.</dd>
+<dt>. They automatically converts functions to static methods </dt>
+<dd>if the input object is not a class. If an explicit name is not passed
+the name of the created class is obtained by adding an underscore to
+the name of the original object.</dd>
+</dl>
+<p><tt class="literal"><span class="pre">__call__(self,</span> <span class="pre">*args)</span></tt></p>
+<blockquote>
+Generates a new class using self.meta and avoiding conflicts.
+The first metaobject can be a dictionary, an object with a
+dictionary (except a class), or a simple name.</blockquote>
+</blockquote>
+<p><tt class="literal"><span class="pre">WithCounter(object)</span></tt></p>
+<blockquote>
+Mixin class counting the total number of its instances and storing
+it in the class attribute counter.</blockquote>
+</div>
+<div class="section" id="documented-functions">
+<h1><a name="documented-functions">Documented functions</a></h1>
+<p><tt class="literal"><span class="pre">mandelbrot(row,col)</span></tt></p>
+<blockquote>
+Computes the Mandelbrot set in one line</blockquote>
+<p><tt class="literal"><span class="pre">with_tracer(function,namespace='__main__',output=sys.stdout,</span> <span class="pre">indent=[0])</span></tt></p>
+<blockquote>
+Closure returning traced functions. It is typically invoked
+trough an auxiliary function fixing the parameters of with_tracer.</blockquote>
+<p><tt class="literal"><span class="pre">ancestor(C,S=None)</span></tt></p>
+<blockquote>
+Returns the ancestors of the first argument with respect to the
+MRO of the second argument. If the second argument is None, then
+returns the MRO of the first argument.</blockquote>
+<p><tt class="literal"><span class="pre">customize(obj,errfile=None,**kw)</span></tt></p>
+<blockquote>
+Adds attributes to an object, if possible. If not, writes an error
+message on 'errfile'. If errfile is None, skips the exception.</blockquote>
+<p><tt class="literal"><span class="pre">loop_overhead(N)</span></tt></p>
+<blockquote>
+Computes the time spent in empty loop of N iterations</blockquote>
+<p><tt class="literal"><span class="pre">indent(block,n)</span></tt></p>
+<blockquote>
+Indent a block of code by n spaces</blockquote>
+<p><tt class="literal"><span class="pre">totuple(arg)</span></tt></p>
+<blockquote>
+Converts the argument to a tuple, if need there is</blockquote>
+<p><tt class="literal"><span class="pre">attributes(obj,condition=lambda</span> <span class="pre">n,v:</span> <span class="pre">not</span> <span class="pre">special(n))</span></tt></p>
+<blockquote>
+Returns a dictionary containing the accessible attributes of
+an object. By default, returns the non-special attributes only.</blockquote>
+<p><tt class="literal"><span class="pre">generateblocks(regexp,text)</span></tt></p>
+<blockquote>
+Generator splitting text in blocks according to regexp</blockquote>
+<p><tt class="literal"><span class="pre">wrapfunctions(obj,wrapper,err=None,**options)</span></tt></p>
+<blockquote>
+Traces the callable objects in an object with a dictionary</blockquote>
+<p><tt class="literal"><span class="pre">wrap(obj,wrapped,condition=lambda</span> <span class="pre">k,v:</span> <span class="pre">True,</span> <span class="pre">err=None)</span></tt></p>
+<blockquote>
+Retrieves obj's dictionary and wraps it</blockquote>
+<p><tt class="literal"><span class="pre">reflective(*classes)</span></tt></p>
+<blockquote>
+Reflective classes know themselves, i.e. they own a private
+attribute __this containing a reference to themselves. If the class
+name starts with '_', the underscores are stripped. This is needed
+to make the mangling mechanism work.</blockquote>
+<p><tt class="literal"><span class="pre">withmemory(f)</span></tt></p>
+<blockquote>
+This closure invokes the callable object f only if need there is</blockquote>
+<p><tt class="literal"><span class="pre">get_time()</span></tt></p>
+<blockquote>
+Return the time of the system in the format HH:MM:SS</blockquote>
+<p><tt class="literal"><span class="pre">prn(s)</span></tt></p>
+<blockquote>
+Given an evaluable string, print its value and its object reference.
+Notice that the evaluation is done in the __main__ dictionary.</blockquote>
+<p><tt class="literal"><span class="pre">isplaindata(a)</span></tt></p>
+<blockquote>
+A data attribute has no __get__ or __set__ attributes, is not
+a built-in function, nor a built-in method.</blockquote>
+<p><tt class="literal"><span class="pre">Pizza(toppings,**dic)</span></tt></p>
+<blockquote>
+This function produces classes inheriting from GenericPizza and
+WithLogger, using a metaclass inferred from Logged</blockquote>
+<p><tt class="literal"><span class="pre">modulesub(s,r,module)</span></tt></p>
+<blockquote>
+Requires 2.3</blockquote>
+<p><tt class="literal"><span class="pre">memoize(f)</span></tt></p>
+<blockquote>
+This closure remembers all f invocations</blockquote>
+<p><tt class="literal"><span class="pre">with_timer(func,</span> <span class="pre">modulename='__main__',</span> <span class="pre">n=1,</span> <span class="pre">logfile=sys.stdout)</span></tt></p>
+<blockquote>
+Wraps the function func and executes it n times (default n=1).
+The average time spent in one iteration, express in milliseconds,
+is stored in the attributes func.time and func.CPUtime, and saved
+in a log file which defaults to the standard output.</blockquote>
+<p><tt class="literal"><span class="pre">dedent(text)</span></tt></p>
+<blockquote>
+<p>dedent(text : string) -&gt; string</p>
+<blockquote>
+<blockquote>
+<p>Remove any whitespace than can be uniformly removed from the left
+of every line in <cite>text</cite>.</p>
+<p>This can be used e.g. to make triple-quoted strings line up with
+the left edge of screen/whatever, while still presenting it in the
+source code in indented form.</p>
+<p>For example:</p>
+<blockquote>
+<blockquote>
+<dl>
+<dt>def test():</dt>
+<dd><p class="first"># end first line with to avoid the empty line!
+s = ''' hello</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>oopp.rst</tt>, line 337)</p>
+Unexpected indentation.</div>
+<blockquote>
+world</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 338)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p class="last">'''
+print repr(s) # prints ' hello</p>
+</dd>
+</dl>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 340)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>world</p>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 341)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<dl>
+<dt>'</dt>
+<dd>print repr(dedent(s)) # prints 'hello</dd>
+</dl>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 343)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>world</p>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>oopp.rst</tt>, line 344)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>'</p>
+</blockquote>
+<p><tt class="literal"><span class="pre">makecls(*metas,**options)</span></tt></p>
+<blockquote>
+Class factory avoiding metatype conflicts. The invocation syntax is
+makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+metaclasses conflicting within themselves or with the given metaclasses,
+it automatically generates a compatible metaclass and instantiate it.
+If priority is True, the given metaclasses have priority over the
+bases' metaclasses</blockquote>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="oopp.rst">View document source</a>.
+Generated on: 2003-09-20 12:54 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/oopp.tex b/pypers/pep318/oopp.tex
new file mode 100755
index 0000000..e5ffd3c
--- /dev/null
+++ b/pypers/pep318/oopp.tex
@@ -0,0 +1,573 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper,margin=2cm,nohead]{geometry}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
+\newenvironment{optionlist}[1]
+{\begin{list}{}
+ {\setlength{\labelwidth}{#1}
+ \setlength{\rightmargin}{1cm}
+ \setlength{\leftmargin}{\rightmargin}
+ \addtolength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}{\optionlistlabel}}
+}{\end{list}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{/mnt/exp/MyDocs/pypers/style.tex}
+\title{Documentation of the oopp module}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Documentation of the oopp module}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+
+%___________________________________________________________________________
+
+\hypertarget{documented-metaclasses}{}
+\section*{Documented metaclasses}
+\pdfbookmark[0]{Documented metaclasses}{documented-metaclasses}
+
+\texttt{Final(type): {\#} better derived from WithCounter,typ}
+\begin{quote}
+
+Instances of Final cannot be derived
+\end{quote}
+
+\texttt{AutoWrapped(type)}
+\begin{quote}
+
+Metaclass that looks at the methods declared in the attributes
+builtinlist and wraplist of its instances and wraps them with
+autowrappedmethod.
+\end{quote}
+
+\texttt{Wrapped(Customizable,type)}
+\begin{quote}
+
+A customizable metaclass to wrap methods with a given wrapper and
+a given condition
+\begin{quote}
+
+\texttt{Reflective(type)}
+\begin{quote}
+
+Cooperative metaclass that defines the private variable {\_}{\_}this in
+its instances. {\_}{\_}this contains a reference to the class, therefore
+it allows anonymous cooperative super calls in the class.
+\end{quote}
+
+\texttt{wrappedmethod(Customizable)}
+\begin{quote}
+
+Customizable method factory intended for derivation.
+The wrapper method is overridden in the children.
+\end{quote}
+\end{quote}
+\end{quote}
+
+\texttt{Cooperative(BracketCallable,type)}
+\begin{quote}
+
+Bracket-callable metaclass implementing cooperative methods. Works
+well for plain methods returning None, such as {\_}{\_}init{\_}{\_}
+
+\texttt{coop{\_}method(cls,name,method): {\#} method can be Non}
+\begin{quote}
+
+Calls both the superclass method and the class method (if the
+class has an explicit method). Implemented via a closure
+\end{quote}
+\end{quote}
+
+\texttt{Logged(WithLogger,Reflective)}
+\begin{quote}
+
+Metaclass that reuses the features provided by WithLogger.
+In particular the classes created by Logged are Reflective,
+PrettyPrinted and Customizable.
+\end{quote}
+
+\texttt{MagicallyTransformed(type)}
+\begin{quote}
+
+Metaclass changing the formatstring of its instances
+\end{quote}
+
+\texttt{Printable(PrettyPrinted,type)}
+\begin{quote}
+
+Apparently does nothing, but actually makes PrettyPrinted acting as
+a metaclass.
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{documented-classes}{}
+\section*{Documented classes}
+\pdfbookmark[0]{Documented classes}{documented-classes}
+
+\texttt{convert2descriptor(object)}
+\begin{quote}
+
+To all practical means, this class acts as a function that, given an
+object, adds to it a {\_}{\_}get{\_}{\_} method if it is not already there. The
+added {\_}{\_}get{\_}{\_} method is trivial and simply returns the original object,
+independently from obj and cls.
+
+\texttt{{\_}{\_}get{\_}{\_}(self,obj,cls=None)}
+\begin{quote}
+
+Returns self.a independently from obj and cls
+\end{quote}
+\end{quote}
+
+\texttt{Object(object)}
+\begin{quote}
+
+A convenient Object class
+\end{quote}
+
+\texttt{GeometricFigure(object): {\#}an example of object factor}
+\begin{quote}
+
+This class allows to define geometric figures according to their
+equation in the cartesian plane. It will be extended later.
+
+\texttt{{\_}{\_}init{\_}{\_}(self,equation,**parameters)}
+\begin{quote}
+
+Specify the cartesian equation of the object and its parameters
+\end{quote}
+\end{quote}
+
+\texttt{UglyDuckling(PrettyPrinted)}
+\begin{quote}
+
+A plain, regular class
+\end{quote}
+
+\texttt{autowrappedmethod(wrappedmethod)}
+\begin{quote}
+
+Makes the method returning cls instances, by wrapping its
+output with cls
+\end{quote}
+
+\texttt{Clock(object)}
+\begin{quote}
+
+Clock with a staticmethod
+\end{quote}
+
+\texttt{AccessError(object)}
+\begin{quote}
+
+Descriptor raising an AttributeError when the attribute is
+accessed
+\end{quote}
+
+\texttt{Frozen(object)}
+\begin{quote}
+
+Subclasses of Frozen are frozen, i.e. it is impossibile to add
+new attributes to them and their instances
+\end{quote}
+
+\texttt{Timer(Clock)}
+\begin{quote}
+
+Inherits the get{\_}time staticmethod from Clock
+\end{quote}
+
+\texttt{Vector(list)}
+\begin{quote}
+
+Implements finite dimensional vectors as lists. Can be instantiated
+as Vector([a,b,c,..]) or as Vector(a,b,c ..)
+\end{quote}
+
+\texttt{Singleton(WithCounter)}
+\begin{quote}
+
+If you inherit from me, you can only have one instance
+\end{quote}
+
+\texttt{Homo(PrettyPrinted)}
+\begin{quote}
+
+Defines the method 'can', which is intended to be overriden
+in the children classes, and inherits '{\_}{\_}str{\_}{\_}' from PrettyPrinted,
+ensuring a nice printing representation for all children.
+\end{quote}
+
+\texttt{kwdict(dict)}
+\begin{quote}
+
+Keyword dictionary base class
+
+\texttt{pretty(dic)}
+\begin{quote}
+
+Returns a nice string representation for the dictionary
+\end{quote}
+\end{quote}
+
+\texttt{WithLogger(WithCounter,PrettyPrinted)}
+\begin{quote}
+
+WithLogger inherits from WithCounter the 'count' class attribute;
+moreover it inherits '{\_}{\_}str{\_}{\_}' from PrettyPrinted
+\end{quote}
+
+\texttt{Get(object)}
+\begin{quote}
+
+Invoked as Get(cls)(xxx) where xxx = staticmethod, classmethod,
+property, plainmethod, plaindata, returns the corresponding
+attributes as a keyword dictionary. It works by internally calling
+the routine inspect.classify{\_}class{\_}attrs. Notice that special data
+attributes are not retrieved (this is by design).
+\end{quote}
+
+\texttt{Makeobj(object)}
+\begin{quote}
+
+A factory of object factories. Makeobj(cls) returns instances
+of cls
+\end{quote}
+
+\texttt{ExampleBaseClass(PrettyPrinted)}
+\begin{quote}
+
+Contains a regular method 'm', a staticmethod 's', a classmethod
+'c', a property 'p' and a data attribute 'd'.
+\end{quote}
+
+\texttt{TransformedUglyDuckling(PrettyPrinted)}
+\begin{quote}
+
+A class metamagically modified
+\end{quote}
+
+\texttt{Customizable(object)}
+\begin{quote}
+
+Classes inhering from 'Customizable' have a 'with' method acting as
+an object modifier and 'With' classmethod acting as a class factory
+\end{quote}
+
+\texttt{WithMultiCounter(WithCounter)}
+\begin{quote}
+
+Each time a new subclass is derived, the counter is reset
+\end{quote}
+
+\texttt{BracketCallable(object)}
+\begin{quote}
+
+Any subclass C(BracketCallable) can be called with the syntax C[t],
+where t is a tuple of arguments stored in bracket{\_}args; returns the
+class or an instance of it, depending on the flag 'returnclass'.
+\end{quote}
+
+\texttt{ClsFactory(BracketCallable)}
+\begin{quote}
+\begin{description}
+%[visit_definition_list_item]
+\item[Bracket callable non-cooperative class acting as :]
+%[visit_definition]
+
+a factory of class factories.
+ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[. They automatically converts functions to static methods :]
+%[visit_definition]
+
+if the input object is not a class. If an explicit name is not passed
+the name of the created class is obtained by adding an underscore to
+the name of the original object.
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+
+\texttt{{\_}{\_}call{\_}{\_}(self, *args)}
+\begin{quote}
+
+Generates a new class using self.meta and avoiding conflicts.
+The first metaobject can be a dictionary, an object with a
+dictionary (except a class), or a simple name.
+\end{quote}
+\end{quote}
+
+\texttt{WithCounter(object)}
+\begin{quote}
+
+Mixin class counting the total number of its instances and storing
+it in the class attribute counter.
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{documented-functions}{}
+\section*{Documented functions}
+\pdfbookmark[0]{Documented functions}{documented-functions}
+
+\texttt{mandelbrot(row,col)}
+\begin{quote}
+
+Computes the Mandelbrot set in one line
+\end{quote}
+
+\texttt{with{\_}tracer(function,namespace='{\_}{\_}main{\_}{\_}',output=sys.stdout, indent=[0])}
+\begin{quote}
+
+Closure returning traced functions. It is typically invoked
+trough an auxiliary function fixing the parameters of with{\_}tracer.
+\end{quote}
+
+\texttt{ancestor(C,S=None)}
+\begin{quote}
+
+Returns the ancestors of the first argument with respect to the
+MRO of the second argument. If the second argument is None, then
+returns the MRO of the first argument.
+\end{quote}
+
+\texttt{customize(obj,errfile=None,**kw)}
+\begin{quote}
+
+Adds attributes to an object, if possible. If not, writes an error
+message on 'errfile'. If errfile is None, skips the exception.
+\end{quote}
+
+\texttt{loop{\_}overhead(N)}
+\begin{quote}
+
+Computes the time spent in empty loop of N iterations
+\end{quote}
+
+\texttt{indent(block,n)}
+\begin{quote}
+
+Indent a block of code by n spaces
+\end{quote}
+
+\texttt{totuple(arg)}
+\begin{quote}
+
+Converts the argument to a tuple, if need there is
+\end{quote}
+
+\texttt{attributes(obj,condition=lambda n,v: not special(n))}
+\begin{quote}
+
+Returns a dictionary containing the accessible attributes of
+an object. By default, returns the non-special attributes only.
+\end{quote}
+
+\texttt{generateblocks(regexp,text)}
+\begin{quote}
+
+Generator splitting text in blocks according to regexp
+\end{quote}
+
+\texttt{wrapfunctions(obj,wrapper,err=None,**options)}
+\begin{quote}
+
+Traces the callable objects in an object with a dictionary
+\end{quote}
+
+\texttt{wrap(obj,wrapped,condition=lambda k,v: True, err=None)}
+\begin{quote}
+
+Retrieves obj's dictionary and wraps it
+\end{quote}
+
+\texttt{reflective(*classes)}
+\begin{quote}
+
+Reflective classes know themselves, i.e. they own a private
+attribute {\_}{\_}this containing a reference to themselves. If the class
+name starts with '{\_}', the underscores are stripped. This is needed
+to make the mangling mechanism work.
+\end{quote}
+
+\texttt{withmemory(f)}
+\begin{quote}
+
+This closure invokes the callable object f only if need there is
+\end{quote}
+
+\texttt{get{\_}time()}
+\begin{quote}
+
+Return the time of the system in the format HH:MM:SS
+\end{quote}
+
+\texttt{prn(s)}
+\begin{quote}
+
+Given an evaluable string, print its value and its object reference.
+Notice that the evaluation is done in the {\_}{\_}main{\_}{\_} dictionary.
+\end{quote}
+
+\texttt{isplaindata(a)}
+\begin{quote}
+
+A data attribute has no {\_}{\_}get{\_}{\_} or {\_}{\_}set{\_}{\_} attributes, is not
+a built-in function, nor a built-in method.
+\end{quote}
+
+\texttt{Pizza(toppings,**dic)}
+\begin{quote}
+
+This function produces classes inheriting from GenericPizza and
+WithLogger, using a metaclass inferred from Logged
+\end{quote}
+
+\texttt{modulesub(s,r,module)}
+\begin{quote}
+
+Requires 2.3
+\end{quote}
+
+\texttt{memoize(f)}
+\begin{quote}
+
+This closure remembers all f invocations
+\end{quote}
+
+\texttt{with{\_}timer(func, modulename='{\_}{\_}main{\_}{\_}', n=1, logfile=sys.stdout)}
+\begin{quote}
+
+Wraps the function func and executes it n times (default n=1).
+The average time spent in one iteration, express in milliseconds,
+is stored in the attributes func.time and func.CPUtime, and saved
+in a log file which defaults to the standard output.
+\end{quote}
+
+\texttt{dedent(text)}
+\begin{quote}
+
+dedent(text : string) -{$>$} string
+\begin{quote}
+\begin{quote}
+
+Remove any whitespace than can be uniformly removed from the left
+of every line in text.
+
+This can be used e.g. to make triple-quoted strings line up with
+the left edge of screen/whatever, while still presenting it in the
+source code in indented form.
+
+For example:
+\begin{quote}
+\begin{quote}
+\begin{description}
+%[visit_definition_list_item]
+\item[def test()::]
+%[visit_definition]
+
+{\#} end first line with to avoid the empty line!
+s = ''' hello
+
+Unexpected indentation.
+
+\begin{quote}
+
+world
+\end{quote}
+
+Block quote ends without a blank line; unexpected unindent.
+
+
+'''
+print repr(s) {\#} prints ' hello
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+\end{quote}
+
+Block quote ends without a blank line; unexpected unindent.
+
+
+world
+\end{quote}
+
+Block quote ends without a blank line; unexpected unindent.
+
+\begin{description}
+%[visit_definition_list_item]
+\item[':]
+%[visit_definition]
+
+print repr(dedent(s)) {\#} prints 'hello
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+\end{quote}
+
+Block quote ends without a blank line; unexpected unindent.
+
+
+world
+\end{quote}
+
+Block quote ends without a blank line; unexpected unindent.
+
+
+'
+\end{quote}
+
+\texttt{makecls(*metas,**options)}
+\begin{quote}
+
+Class factory avoiding metatype conflicts. The invocation syntax is
+makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+metaclasses conflicting within themselves or with the given metaclasses,
+it automatically generates a compatible metaclass and instantiate it.
+If priority is True, the given metaclasses have priority over the
+bases' metaclasses
+\end{quote}
+
+\end{document}
diff --git a/pypers/pep318/post.txt b/pypers/pep318/post.txt
new file mode 100755
index 0000000..cfbb221
--- /dev/null
+++ b/pypers/pep318/post.txt
@@ -0,0 +1,21 @@
+Instructions:
+
+1. Download the file ``decorators.zip`` from my home-page:
+
+ http://www.phyast.pitt.edu/~micheles/python/decorators.zip
+
+2. Unzip it in a directory, for instance:
+
+ ``unzip decorators -d decorators``
+
+3. Test that everything works:
+
+ ``cd decorators; python doct.py decorators.txt``
+
+4. Add the decorators directory to your Python path or copy the files
+ decorators.py, noconflict.py, debugger.py into your Python distribution.
+
+5. It is always a good idea to give a look to the README.txt file.
+
+Notice: Python 2.3 is required. I have tested that it works (at least for
+me) both under Red Hat 7.3 and under Windows 98SE.
diff --git a/pypers/pep318/printerr.py b/pypers/pep318/printerr.py
new file mode 100755
index 0000000..7de2333
--- /dev/null
+++ b/pypers/pep318/printerr.py
@@ -0,0 +1,7 @@
+err=file('err','w')
+
+def printerr(*args):
+ "For debugging purposes"
+ for a in args: print >> err, a,
+ print >> err
+
diff --git a/pypers/pep318/prnt.py b/pypers/pep318/prnt.py
new file mode 100755
index 0000000..cb56674
--- /dev/null
+++ b/pypers/pep318/prnt.py
@@ -0,0 +1,7 @@
+# prnt.py
+
+import sys
+f=sys.stdout
+
+def hello():
+ print >> f,'hello'
diff --git a/pypers/pep318/pro.py b/pypers/pep318/pro.py
new file mode 100755
index 0000000..d875d55
--- /dev/null
+++ b/pypers/pep318/pro.py
@@ -0,0 +1,28 @@
+"""Funziona solo se e' composto una sola volta, senno' ricorsione
+infinita""" # ??
+
+import customdec,__builtin__
+__builtin__.type=customdec.type
+__builtin__.object=customdec.TraceFunctions(object)
+print __builtin__.object.__mro__
+
+__builtin__.object=customdec.Decorated(customdec.TraceFunctions(object))
+print __builtin__.object.__mro__
+
+#raise SystemExit
+
+#__builtin__.object=customdec.TraceFunctions(object)
+from tracing import E
+e=E()
+print E,type(type(E))
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pypers/pep318/pro1.py b/pypers/pep318/pro1.py
new file mode 100755
index 0000000..a9c2e0f
--- /dev/null
+++ b/pypers/pep318/pro1.py
@@ -0,0 +1,7 @@
+# pro1.py
+import sys
+f=sys.stdout
+a='ciao'
+def prn():
+ print >> f, a
+
diff --git a/pypers/pep318/pro1.txt b/pypers/pep318/pro1.txt
new file mode 100755
index 0000000..c75b12a
--- /dev/null
+++ b/pypers/pep318/pro1.txt
@@ -0,0 +1,10 @@
+#<pro1.py>
+import sys
+f=sys.stdout
+a='ciao'
+def prn():
+ print >> f, a
+#</pro1.py>
+>>> import pro1
+>>> pro1.prn()
+ciao
diff --git a/pypers/pep318/pro2.py b/pypers/pep318/pro2.py
new file mode 100755
index 0000000..58cffb4
--- /dev/null
+++ b/pypers/pep318/pro2.py
@@ -0,0 +1,24 @@
+from customdec import *
+
+def f(self): pass
+
+tracedf=tracedmethod(f)
+tracedtracedf=decorated(tracedmethod(f))
+
+class C: pass
+
+c=C()
+
+C.f=tracedtracedf
+
+c.f()
+
+class Chatty(ClassDecorator):
+ def __init__(cls,*args):
+ print 'Chatty.__init__'
+
+class C:pass
+
+
+
+C=Chatty(Chatty(C))
diff --git a/pypers/pep318/pro2.txt b/pypers/pep318/pro2.txt
new file mode 100755
index 0000000..70dbcd4
--- /dev/null
+++ b/pypers/pep318/pro2.txt
@@ -0,0 +1,3 @@
+>>> import pro1
+>>> pro1.prn()
+ciao
diff --git a/pypers/pep318/psyco.tex b/pypers/pep318/psyco.tex
new file mode 100755
index 0000000..93dcc9f
--- /dev/null
+++ b/pypers/pep318/psyco.tex
@@ -0,0 +1,201 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper,margin=2cm,nohead]{geometry}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
+\newenvironment{optionlist}[1]
+{\begin{list}{}
+ {\setlength{\labelwidth}{#1}
+ \setlength{\rightmargin}{1cm}
+ \setlength{\leftmargin}{\rightmargin}
+ \addtolength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}{\optionlistlabel}}
+}{\end{list}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{/mnt/exp/MyDocs/pypers/style.tex}
+\title{Module psyco}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Module psyco}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+Psyco -- the Python Specializing Compiler.
+
+Typical usage: add the following lines to your application's main module:
+\begin{description}
+%[visit_definition_list_item]
+\item[try::]
+%[visit_definition]
+
+import psyco
+psyco.profile()
+
+%[depart_definition]
+%[depart_definition_list_item]
+%[visit_definition_list_item]
+\item[except::]
+%[visit_definition]
+
+print 'Psyco not found, ignoring it'
+
+%[depart_definition]
+%[depart_definition_list_item]
+\end{description}
+
+
+%___________________________________________________________________________
+
+\hypertarget{functions}{}
+\section*{Functions}
+\pdfbookmark[0]{Functions}{functions}
+
+\texttt{cannotcompile(x):}
+\begin{quote}
+
+Instruct Psyco never to compile the given function, method
+or code object.
+\end{quote}
+
+\texttt{log(logfile='', mode='w', top=10):}
+\begin{quote}
+
+Enable logging to the given file.
+
+If the file name is unspecified, a default name is built by appending
+a 'log-psyco' extension to the main script name.
+
+Mode is 'a' to append to a possibly existing file or 'w' to overwrite
+an existing file. Note that the log file may grow quickly in 'a' mode.
+\end{quote}
+
+\texttt{runonly(memory=None, time=None, memorymax=None, timemax=None):}
+\begin{quote}
+
+Nonprofiler.
+
+XXX check if this is useful and document.
+\end{quote}
+
+\texttt{profile(watermark = default{\_}watermark,}
+\begin{quote}
+
+Turn on profiling.
+
+The 'watermark' parameter controls how easily running functions will
+be compiled. The smaller the value, the more functions are compiled.
+\end{quote}
+
+\texttt{full(memory=None, time=None, memorymax=None, timemax=None):}
+\begin{quote}
+
+Compile as much as possible.
+
+Typical use is for small scripts performing intensive computations
+or string handling.
+\end{quote}
+
+\texttt{dumpcodebuf():}
+\begin{quote}
+
+Write in file psyco.dump a copy of the emitted machine code,
+provided Psyco was compiled with a non-zero CODE{\_}DUMP.
+See py-utils/httpxam.py to examine psyco.dump.
+\end{quote}
+
+\texttt{stop():}
+\begin{quote}
+
+Turn off all automatic compilation. bind() calls remain in effect.
+\end{quote}
+
+\texttt{proxy(x, rec=None):}
+\begin{quote}
+
+Return a Psyco-enabled copy of the function.
+
+The original function is still available for non-compiled calls.
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection.
+\end{quote}
+
+\texttt{background(watermark = default{\_}watermark,}
+\begin{quote}
+
+Turn on passive profiling.
+
+This is a very lightweight mode in which only intensively computing
+functions can be detected. The smaller the 'watermark', the more functions
+are compiled.
+\end{quote}
+
+\texttt{unbind(x):}
+\begin{quote}
+
+Reverse of bind().
+\end{quote}
+
+\texttt{bind(x, rec=None):}
+\begin{quote}
+
+Enable compilation of the given function, method, or class object.
+
+If C is a class (or anything with a '{\_}{\_}dict{\_}{\_}' attribute), bind(C) will
+rebind all functions and methods found in C.{\_}{\_}dict{\_}{\_} (which means, for
+classes, all methods defined in the class but not in its parents).
+
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection.
+\end{quote}
+
+\texttt{{\_}getemulframe(depth=0):}
+\begin{quote}
+
+As {\_}getframe(), but the returned objects are real Python frame objects
+emulating Psyco frames. Some of their attributes can be wrong or missing,
+however.
+\end{quote}
+
+\texttt{unproxy(proxy):}
+\begin{quote}
+
+Return a new copy of the original function of method behind a proxy.
+The result behaves like the original function in that calling it
+does not trigger compilation nor execution of any compiled code.
+\end{quote}
+
+\end{document}
diff --git a/pypers/pep318/pydoc.html b/pypers/pep318/pydoc.html
new file mode 100755
index 0000000..8de6660
--- /dev/null
+++ b/pypers/pep318/pydoc.html
@@ -0,0 +1,508 @@
+
+<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module decorators</title>
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>decorators</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/mnt/exp/MyDocs/pypers/pep318/decorators.py">/mnt/exp/MyDocs/pypers/pep318/decorators.py</a></font></td></tr></table>
+ <p><tt>A&nbsp;module&nbsp;to&nbsp;implement&nbsp;pep318&nbsp;(decorator&nbsp;syntax)&nbsp;via&nbsp;magic&nbsp;doctrings.<br>
+For&nbsp;the&nbsp;documentation&nbsp;see<br>
+&nbsp;<br>
+<a href="http://www.phyast.pitt.edu/~micheles/python/decorators,html">http://www.phyast.pitt.edu/~micheles/python/decorators,html</a><br>
+&nbsp;<br>
+and&nbsp;the&nbsp;on-line&nbsp;help.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#fffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="__builtin__.html">__builtin__</a><br>
+<a href="inspect.html">inspect</a><br>
+</td><td width="25%" valign=top><a href="noconflict.html">noconflict</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#UnknownDecoratorError">UnknownDecoratorError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorator">Decorator</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="decorators.html#MethodDecorator">MethodDecorator</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#classmethod">classmethod</a>
+</font></dt><dt><font face="helvetica, arial"><a href="decorators.html#staticmethod">staticmethod</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#type">__builtin__.type</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="decorators.html#MetaDecorator">MetaDecorator</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ClassDecorator">class <strong>ClassDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;callable&nbsp;with&nbsp;one&nbsp;or&nbsp;three&nbsp;arguments,&nbsp;having&nbsp;its&nbsp;calling<br>
+syntax&nbsp;redefined&nbsp;by&nbsp;the&nbsp;meta-metaclass&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.&nbsp;It&nbsp;redefines<br>
+__str__&nbsp;both&nbsp;on&nbsp;classes&nbsp;and&nbsp;instances.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ClassDecorator-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl>
+
+<dl><dt><a name="ClassDecorator-__str__"><strong>__str__</strong></a>(cls)</dt></dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="ClassDecorator-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__call__">__call__</a>(...)&nbsp;&lt;==&gt;&nbsp;x(...)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;type 'type'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.ClassDecorator'&gt;, &lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ClassDecorator-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Decorated">class <strong>Decorated</strong></a>(<a href="decorators.html#ClassDecorator">ClassDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;which&nbsp;decorates&nbsp;its&nbsp;instances<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#Decorated">Decorated</a></dd>
+<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Decorated-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#ClassDecorator">ClassDecorator</a>:<br>
+<dl><dt><a name="Decorated-__str__"><strong>__str__</strong></a>(cls)</dt></dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="Decorated-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__call__">__call__</a>(...)&nbsp;&lt;==&gt;&nbsp;x(...)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#Decorated-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="Decorated-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#Decorated-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;class 'decorators.ClassDecorator'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;class 'decorators.ClassDecorator'&gt;,)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.Decorated'&gt;, &lt;class 'decorators.ClassDecorator'&gt;, &lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#Decorated-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Decorator">class <strong>Decorator</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Instance&nbsp;of&nbsp;<a href="#MetaDecorator">MetaDecorator</a>&nbsp;and&nbsp;mothers&nbsp;of&nbsp;all&nbsp;decorators.&nbsp;When&nbsp;called<br>
+in&nbsp;the&nbsp;form&nbsp;<a href="#Decorator">Decorator</a>(obj),&nbsp;with&nbsp;obj&nbsp;having&nbsp;a&nbsp;magic&nbsp;docstring,&nbsp;it&nbsp;returns<br>
+an&nbsp;instance&nbsp;of&nbsp;the&nbsp;correct&nbsp;decorator,&nbsp;otherwise&nbsp;it&nbsp;returns&nbsp;None.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Data and other attributes defined here:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MetaDecorator">class <strong>MetaDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#MetaDecorator">MetaDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MetaDecorator-__call__"><strong>__call__</strong></a>(dec, *args)</dt></dl>
+
+<dl><dt><a name="MetaDecorator-__init__"><strong>__init__</strong></a>(dec, *args)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>dic</strong> = {'ClassDecorator': &lt;class 'decorators.ClassDecorator'&gt;, 'Decorated': &lt;class 'decorators.Decorated'&gt;, 'Decorator': &lt;class 'decorators.Decorator'&gt;, 'MethodDecorator': &lt;class 'decorators.MethodDecorator'&gt;, 'classmethod': &lt;class 'decorators.classmethod'&gt;, 'staticmethod': &lt;class 'decorators.staticmethod'&gt;}</dl>
+
+<dl><dt><strong>ls</strong> = [&lt;class 'decorators.Decorator'&gt;, &lt;class 'decorators.MethodDecorator'&gt;, &lt;class 'decorators.ClassDecorator'&gt;, &lt;class 'decorators.Decorated'&gt;, &lt;class 'decorators.staticmethod'&gt;, &lt;class 'decorators.classmethod'&gt;]</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="MetaDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;type 'type'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;type 'type'&gt;,)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.MetaDecorator'&gt;, &lt;type 'type'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MetaDecorator-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MethodDecorator">class <strong>MethodDecorator</strong></a>(<a href="decorators.html#Decorator">Decorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Descriptor&nbsp;class&nbsp;callable&nbsp;with&nbsp;a&nbsp;function&nbsp;as&nbsp;argument.&nbsp;The&nbsp;calling<br>
+syntax&nbsp;is&nbsp;redefined&nbsp;by&nbsp;the&nbsp;meta-metaclass&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.&nbsp;It&nbsp;redefines<br>
+__str__&nbsp;and&nbsp;get&nbsp;(i.e.&nbsp;__get__)&nbsp;on&nbsp;its&nbsp;instances.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MethodDecorator-__get__"><strong>__get__</strong></a> = <a href="#MethodDecorator-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="UnknownDecoratorError">class <strong>UnknownDecoratorError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;name&nbsp;says&nbsp;it&nbsp;all<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="UnknownDecoratorError-__getitem__"><strong>__getitem__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownDecoratorError-__init__"><strong>__init__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownDecoratorError-__str__"><strong>__str__</strong></a>(...)</dt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="classmethod">class <strong>classmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Decorator">Decorator</a>,&nbsp;converts&nbsp;a&nbsp;function&nbsp;in&nbsp;a&nbsp;<a href="#classmethod">classmethod</a><br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#classmethod">classmethod</a></dd>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="classmethod-__get__"><strong>__get__</strong></a> = <a href="#classmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="classmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><a name="classmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="classmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="staticmethod">class <strong>staticmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Decorator">Decorator</a>,&nbsp;converts&nbsp;a&nbsp;function&nbsp;in&nbsp;a&nbsp;<a href="#staticmethod">staticmethod</a><br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#staticmethod">staticmethod</a></dd>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="staticmethod-__get__"><strong>__get__</strong></a> = <a href="#staticmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="staticmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><a name="staticmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="staticmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined&nbsp;in&nbsp;any&nbsp;module,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+2.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+&nbsp;&nbsp;&nbsp;is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>
+3.&nbsp;Decorators&nbsp;calls&nbsp;are&nbsp;dispatched&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_<br>
+&nbsp;&nbsp;&nbsp;<a href="#classmethod">classmethod</a>.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-decorate"><strong>decorate</strong></a>(objdict)</dt><dd><tt>Takes&nbsp;an&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;a&nbsp;dictionary&nbsp;and&nbsp;decorates&nbsp;all&nbsp;its&nbsp;functions<br>
+and&nbsp;classes&nbsp;according&nbsp;to&nbsp;their&nbsp;docstrings.</tt></dd></dl>
+ <dl><dt><a name="-decorated"><strong>decorated</strong></a>(obj)</dt><dd><tt>Returns&nbsp;a&nbsp;new&nbsp;decorated&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;created&nbsp;from&nbsp;obj,&nbsp;if&nbsp;obj&nbsp;is&nbsp;a&nbsp;function<br>
+or&nbsp;a&nbsp;class;&nbsp;otherwise&nbsp;it&nbsp;returns&nbsp;None</tt></dd></dl>
+ <dl><dt><a name="-decorator_from"><strong>decorator_from</strong></a>(docstring, defaultdec<font color="#909090">=None</font>)</dt><dd><tt>Takes&nbsp;a&nbsp;magic&nbsp;docstring&nbsp;and&nbsp;a&nbsp;default&nbsp;decorator<br>
+and&nbsp;returns&nbsp;a&nbsp;decorator&nbsp;class&nbsp;or&nbsp;None.&nbsp;It&nbsp;tries&nbsp;to&nbsp;be&nbsp;smart.</tt></dd></dl>
+ <dl><dt><a name="-enhance_classes"><strong>enhance_classes</strong></a>(docstring<font color="#909090">=''</font>)</dt><dd><tt>Enhance&nbsp;all&nbsp;the&nbsp;classes&nbsp;in&nbsp;the&nbsp;caller&nbsp;module&nbsp;with&nbsp;a&nbsp;metaclass<br>
+built&nbsp;from&nbsp;the&nbsp;given&nbsp;docstring;&nbsp;the&nbsp;default&nbsp;is&nbsp;<a href="#ClassDecorator">ClassDecorator</a>.</tt></dd></dl>
+ <dl><dt><a name="-get"><strong>get</strong></a>(docstring<font color="#909090">=None</font>)</dt><dd><tt>List&nbsp;of&nbsp;recognized&nbsp;decorators</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>MAGICDOC</strong> = &lt;_sre.SRE_Pattern object&gt;</td></tr></table>
+</body></html> \ No newline at end of file
diff --git a/pypers/pep318/safetype.html b/pypers/pep318/safetype.html
new file mode 100755
index 0000000..a3c3cd4
--- /dev/null
+++ b/pypers/pep318/safetype.html
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Module safetype</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="module-safetype">
+<h1 class="title">Module safetype</h1>
+<p>Deep, DEEP magic to remove metaclass conflicts.
+Metaclasses derived from safetype.type by cooperatively overriding __new__
+are safe under conflicts.
+The suggested import syntax for usage in other modules is</p>
+<blockquote>
+from safetype import type</blockquote>
+<div class="section" id="documented-metaclasses">
+<h1><a name="documented-metaclasses">Documented metaclasses</a></h1>
+<p><tt class="literal"><span class="pre">safetype(type)</span></tt></p>
+<blockquote>
+Redefines the 'type' metaclass, making it safe under conflicts.</blockquote>
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="safetype.rst">View document source</a>.
+Generated on: 2003-09-20 16:41 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/safetype.tex b/pypers/pep318/safetype.tex
new file mode 100755
index 0000000..fc80a11
--- /dev/null
+++ b/pypers/pep318/safetype.tex
@@ -0,0 +1,61 @@
+\documentclass[10pt,english]{article}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper,margin=2cm,nohead]{geometry}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
+\newenvironment{optionlist}[1]
+{\begin{list}{}
+ {\setlength{\labelwidth}{#1}
+ \setlength{\rightmargin}{1cm}
+ \setlength{\leftmargin}{\rightmargin}
+ \addtolength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}{\optionlistlabel}}
+}{\end{list}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{/mnt/exp/MyDocs/pypers/style.tex}
+\title{Documentation of the safetype module\\
+\large{Metaclasses}
+}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Documentation of the safetype module}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+
+\texttt{safetype(type)}
+
+Redefines the 'type' metaclass, making it safe under conflicts.
+
+\end{document}
diff --git a/pypers/pep318/tracing.py b/pypers/pep318/tracing.py
new file mode 100755
index 0000000..5ea3552
--- /dev/null
+++ b/pypers/pep318/tracing.py
@@ -0,0 +1,25 @@
+# tracing.py
+
+"""
+This is a pre-existing module not using decorators but using multiple
+inheritance and unsafe metaclasses. We want to trace it for debugging
+purposes.
+"""
+
+class M(type):
+ "There should be some non-trivial code here"
+
+class B(object):
+ def __init__(self):
+ super(B,self).__init__()
+
+class D(object):
+ __metaclass__=M
+ def __init__(self):
+ super(D,self).__init__()
+
+class E(B,D):
+ def __init__(self):
+ super(E,self).__init__()
+
+
diff --git a/pypers/pep318/working/README.txt b/pypers/pep318/working/README.txt
new file mode 100755
index 0000000..31f878e
--- /dev/null
+++ b/pypers/pep318/working/README.txt
@@ -0,0 +1,45 @@
+DECORATORS README
+========================================================================
+
+The ``decorators`` distribution contains the following files:
+
+1. README.txt (your are reading it)
+
+2. decorators.txt (the documentation in ReStructuredText format)
+
+3. decorators.html (the documentation in HTML format)
+
+4. decorators.pdf (the documentation in pdf format)
+
+5. decorators.py (the heart of the distribution)
+
+6. noconflict.py (imported by decorators, resolves metaclass conflicts)
+
+7. doct.py (utility to extract tests from the documentation)
+
+8. decorators.ps (a figure included in decorators.pdf)
+
+9. decorators.png (a figure included in decorators.html)
+
+10. makegraph.dot (DOT script generating the figure)
+
+
+``noconflict`` and ``doct`` can be used as standalone
+modules too. They are documented in the on-line Python cookbook.
+
+After running ``python doct.py decorators.txt`` a number of files will be
+generated, including a module ``customdec.py`` containing the examples
+of custom decorators discussed in the documentation.
+
+If the tests fail, then there is something wrong with your Python
+installation, and I cannot help you since I don't have your machine
+at my disposal :-( It works for me both under Red Hat 7.3 and
+Windows 98SE. Notice that Python 2.3 is required.
+
+If you use the decorators module in your code (of course, you should
+not use it in production software!) and you find some bug of unexpected
+behaviour, please send a bug report to me:
+
+ MicheleSimionato@libero.it
+
+That's all, folks. Enjoy!
diff --git a/pypers/pep318/working/chatty2.py b/pypers/pep318/working/chatty2.py
new file mode 100755
index 0000000..8907abb
--- /dev/null
+++ b/pypers/pep318/working/chatty2.py
@@ -0,0 +1,27 @@
+# chatty2.py
+
+import customdec; customdec.enhance_classes("[Decorated]")
+
+# sets the log files
+log1=file('file1.log','w')
+log2=file('file2.log','w')
+
+class C:
+ def f(self):
+ "[chattymethod2]"
+ f.logfile=log1 # function attribute
+ def g(self):
+ "[chattymethod2]"
+ g.logfile=log2 # function attribute
+
+assert C.__dict__['f'].logfile is log1 # check the conversion
+assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr.
+
+c=C() # C instantiation
+
+c.f() # print a message in file1.log
+c.g() # print a message in file2.log
+
+log1.close(); log2.close() # finally
+
+
diff --git a/pypers/pep318/working/customdec.py b/pypers/pep318/working/customdec.py
new file mode 100755
index 0000000..bebec11
--- /dev/null
+++ b/pypers/pep318/working/customdec.py
@@ -0,0 +1,67 @@
+# customdec.py
+
+from decorators import *
+
+class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+
+
+class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+
+
+class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -> dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+
+
+class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print >> cls.output,"%s created" % cls
+
+
+
+from types import FunctionType
+
+class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType): # modifies the docstring
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+
+
diff --git a/pypers/pep318/working/debugger.py b/pypers/pep318/working/debugger.py
new file mode 100755
index 0000000..30510df
--- /dev/null
+++ b/pypers/pep318/working/debugger.py
@@ -0,0 +1,17 @@
+import sys
+
+def info(type, value, tb):
+ if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \
+ type == SyntaxError:
+ # we are in interactive mode or we don't have a tty-like
+ # device, so we call the default hook
+ sys.__excepthook__(type, value, tb)
+ else:
+ import traceback, pdb
+ # we are NOT in interactive mode, print the exception...
+ traceback.print_exception(type, value, tb)
+ print
+ # ...then start the debugger in post-mortem mode.
+ pdb.pm()
+
+sys.excepthook = info
diff --git a/pypers/pep318/working/decorators.html b/pypers/pep318/working/decorators.html
new file mode 100755
index 0000000..ceac9b6
--- /dev/null
+++ b/pypers/pep318/working/decorators.html
@@ -0,0 +1,1337 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Implementing PEP 318 (decorators)</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="implementing-pep-318-decorators">
+<h1 class="title">Implementing PEP 318 (decorators)</h1>
+<blockquote>
+<table class="field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Module:</th><td class="field-body">decorators</td>
+</tr>
+<tr class="field"><th class="field-name">Version:</th><td class="field-body">0.5</td>
+</tr>
+<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr class="field"><th class="field-name">e-mail:</th><td class="field-body"><a class="reference" href="mailto:MicheleSimionato&#64;libero.it">MicheleSimionato&#64;libero.it</a></td>
+</tr>
+<tr class="field"><th class="field-name">Licence:</th><td class="field-body">Python-like</td>
+</tr>
+<tr class="field"><th class="field-name">Disclaimer:</th><td class="field-body">This is experimental code. Use it at your own risk!</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<div class="contents topic" id="contents">
+<p class="topic-title"><a name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#basics" id="id1" name="id1">Basics</a></li>
+<li><a class="reference" href="#simple-usage-of-decorators" id="id2" name="id2">Simple usage of decorators</a></li>
+<li><a class="reference" href="#decorating-classes" id="id3" name="id3">Decorating classes</a></li>
+<li><a class="reference" href="#adding-magic" id="id4" name="id4">Adding magic</a></li>
+<li><a class="reference" href="#the-dangers-of-magic" id="id5" name="id5">The dangers of magic</a></li>
+<li><a class="reference" href="#defining-method-decorators" id="id6" name="id6">Defining method decorators</a></li>
+<li><a class="reference" href="#composing-decorators" id="id7" name="id7">Composing decorators</a></li>
+<li><a class="reference" href="#defining-class-decorators" id="id8" name="id8">Defining class decorators</a></li>
+<li><a class="reference" href="#advanced-usage" id="id9" name="id9">Advanced usage</a></li>
+<li><a class="reference" href="#the-implementation" id="id10" name="id10">The implementation</a></li>
+</ul>
+</div>
+<p>Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of PEP 318 in pure Python.</p>
+<p>Here is the rationale:</p>
+<ul class="simple">
+<li>some kind of decorator syntax is scheduled to go in Python 2.4,
+therefore it is interesting to play with the concept;</li>
+<li>it is nice to play with decorators now, without having to
+wait for one year or so;</li>
+<li>it is much easier levelto experiment with a pure Python implementation
+than with a C implementation;</li>
+<li>the implementation can be seen as an exercise on modern Python
+programming and may be valuable to people wanting to study the most
+advanced new constructs in Python 2.2 (<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">descriptors</a>, <a class="reference" href="http://www-106.ibm.com/developerworks/library/l-pymeta2.html">metaclasses</a>,
+<a class="reference" href="http://www.python.org/2.3/descrintro.html">cooperative methods</a>, etc.)</li>
+</ul>
+<div class="section" id="basics">
+<h1><a class="toc-backref" href="#id1" name="basics">Basics</a></h1>
+<p>PEP 318 has the goal of providing a nice syntactic sugar for expressions like</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ return x
+identity=staticmethod(identity)
+</pre>
+</blockquote>
+<p>or</p>
+<blockquote>
+<pre class="literal-block">
+def name(cls):
+ return cls.__name__
+name=classmethod(name)
+</pre>
+</blockquote>
+<p>which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x)[staticmethod]:
+ return x
+
+def name(cls)[classmethod]:
+ return cls.__name__
+</pre>
+</blockquote>
+<p>involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+</pre>
+</blockquote>
+<p>The implementation is able to recognize such docstrings
+and to automagically convert those methods in decorators.</p>
+<p>Decorators are nothing else than a sophisticated kind of wrappers.
+The <tt class="literal"><span class="pre">decorators</span></tt> module provides support both for method decorators,
+which wrap functions and class decorator, which wrap classes.
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt> are two examples of already existing
+method decorators (actually my implementation rewrites them, but let me
+pass on this detail). Technically speaking, method decorators are classes
+taking a single function as input and producing a descriptor object
+as output (properties are not decorators according to this definition,
+since they take four functions as input, <tt class="literal"><span class="pre">get,</span> <span class="pre">set,</span> <span class="pre">del_</span></tt> and <tt class="literal"><span class="pre">doc</span></tt>).
+Descriptors are objects with a <tt class="literal"><span class="pre">__get__</span></tt> method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip on this point ;).
+A knowledge of descriptors is not needed in order to use the <tt class="literal"><span class="pre">decorator</span></tt>
+module; however it is welcomed for advanced users wanting to implement
+custom method decorators.
+Class decorators are metaclasses taking a class as imput and returning
+a decorated class as output. A good understanding of metaclasses is needed
+in order to be able to write custom class decorators, but no knowledge
+at all is required in order to use the pre-defined class decorators
+provided by the module.
+Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the module short.</p>
+<p>Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.</p>
+</div>
+<div class="section" id="simple-usage-of-decorators">
+<h1><a class="toc-backref" href="#id2" name="simple-usage-of-decorators">Simple usage of decorators</a></h1>
+<p>Before talking about the implementation details, I will show
+how the <tt class="literal"><span class="pre">decorators</span></tt> module works in practice. The simplest and safest
+usage is by means of the <tt class="literal"><span class="pre">decorators.decorated()</span></tt> function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+is simple but verbose, so magic shortcuts will be discussed in the next
+section.</p>
+<p>Here, let me give an example, showing that method decorators work both for
+<a class="reference" href="http://www.python.org/2.3/descrintro.html">new style classes and old style classes</a>:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example1.py&gt;
+
+import decorators
+
+def do_nothing(self):
+ &quot;No magic docstring here&quot;
+dec_do_nothing=decorators.decorated(do_nothing)
+
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+dec_identity=decorators.decorated(identity)
+
+def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+dec_name=decorators.decorated(name)
+
+class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+class NewStyle(object):
+ name=dec_name
+
+o=OldStyle() # creates an old style instance
+n=NewStyle() # creates a new style instance
+
+#&lt;/example1.py&gt;
+</pre>
+</blockquote>
+<p>In this example, both <tt class="literal"><span class="pre">dec_identity</span></tt> and <tt class="literal"><span class="pre">dec_name</span></tt> are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects, since they have a re-defined printing representation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example1 import *
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; print dec_identity
+&lt;staticmethod:identity&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; print dec_name
+&lt;classmethod:name&gt;
+</pre>
+<p>Now, let me check that <tt class="literal"><span class="pre">dec_</span> <span class="pre">identity</span></tt> works as a staticmethod,</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert OldStyle.identity(1) == 1 # called from the class
+&gt;&gt;&gt; assert o.identity(1) == 1 # called from the instance
+</pre>
+<p>whereas <tt class="literal"><span class="pre">dec_name</span></tt> works as a classmethod:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert NewStyle.name() == 'NewStyle' # called from the class
+&gt;&gt;&gt; assert n.name() == 'NewStyle' # called from the instance
+</pre>
+<p>On the other hand, <tt class="literal"><span class="pre">do_nothing</span></tt> does not have a magic
+docstring, therefore it is not converted to a decorator object;
+actually it is <em>exactly</em> the original function:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert dec_do_nothing is do_nothing # not converted
+</pre>
+<p>Therefore it works without surprises:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; o.do_nothing() # does nothing, correct
+</pre>
+<p>For sake of convenience, I have re-implemented the built-in
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, so</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_identity,staticmethod)
+False
+</pre>
+<p>and</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_name,classmethod)
+False
+</pre>
+<p>but</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_identity,decorators.staticmethod)
+True
+</pre>
+<p>and</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; isinstance(dec_name,decorators.classmethod)
+True
+</pre>
+<p>It is possible to recognize method decorators since they provides
+a couple of special attributes:</p>
+<p><tt class="literal"><span class="pre">__func__</span></tt>, returning the function from which they originated</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert dec_identity.__func__ is identity
+&gt;&gt;&gt; assert dec_name.__func__ is name
+</pre>
+</blockquote>
+<p>and <tt class="literal"><span class="pre">__klass__</span></tt>, returning the class where they where defined</p>
+<blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; dec_identity.__klass__
+&lt;class 'decorators.?'&gt;
+</pre>
+</blockquote>
+<p>The question mark here means that the definition class is unknown.</p>
+</div>
+<div class="section" id="decorating-classes">
+<h1><a class="toc-backref" href="#id3" name="decorating-classes">Decorating classes</a></h1>
+<p>The problem with the approach described in the previous section
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically converting their
+methods to method decorators according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with &quot;[Decorated]&quot;
+and decorating the class.
+Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example2.py&gt;
+
+from decorators import decorated
+from example1 import do_nothing,identity,name
+
+class B(object):
+ &quot;This is a regular class&quot;
+
+B=decorated(B) # does nothing
+
+class C(B):
+ &quot;[Decorated]&quot;
+ do_nothing=do_nothing
+ identity=identity
+ name=name
+
+C=decorated(C) # regenerates the class converting methods in decorators
+c=C()
+
+#&lt;/example2.py&gt;
+</pre>
+</blockquote>
+<p>Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example2 import *
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert C.name() == 'C'
+&gt;&gt;&gt; assert c.identity(1) == 1
+&gt;&gt;&gt; assert c.name() == 'C'
+</pre>
+<p>Notice that adding <tt class="literal"><span class="pre">identity</span></tt> after the class creation with the syntax
+<tt class="literal"><span class="pre">C.identity=identity</span></tt> would not work;
+<tt class="literal"><span class="pre">C.identity=decorators.decorated(identity)</span></tt> is required:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.identity=decorators.decorated(identity)
+&gt;&gt;&gt; C.identity(1) # it works
+1
+</pre>
+<p>If a class misses the magic docstring, nothing happens:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; B # returns the original B
+&lt;class 'example2.B'&gt;
+</pre>
+<p>The mechanism works for old style classes too:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example2.py&gt;
+
+class D: # old style
+ &quot;[Decorated]&quot;
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+D=decorated(D)
+
+d=D()
+
+# test
+assert d.identity(1) == 1
+assert D.identity(1) == 1
+
+#&lt;/example2.py&gt;
+</pre>
+</blockquote>
+<p>Under the hood <tt class="literal"><span class="pre">decorators.decorated()</span></tt> recognizes the class level
+magic docstring &quot;[Decorated]&quot; and creates an instance of the
+<tt class="literal"><span class="pre">decorators.Decorated</span></tt> metaclass; incidentally,
+this converts old style classes in new style classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example2 import D,d
+&gt;&gt;&gt; type(D) # D is an instance of decorator.Decorated
+&lt;class 'decorators.Decorated'&gt;
+</pre>
+<p>Internally the metaclass invokes <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. By the way, if you mispell a
+decorator name you get an helpful error message:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E:
+... &quot;[Decorated]&quot;
+... def f():
+... &quot;[staticmeth]&quot;
+&gt;&gt;&gt; E=decorators.decorated(E)
+Traceback (most recent call last):
+ .. a long and cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+</pre>
+<p>The enhancement provided by the metaclass includes a new default
+printing representation for both the class</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print D # returns the name of D and of its metaclass
+&lt;class D[Decorated]&gt;
+</pre>
+<p>and its instances:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print d
+&lt;D instance&gt;
+</pre>
+<p>One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(D):
+... def name(cls):
+... &quot;[classmethod]&quot;
+... return cls.__name__
+&gt;&gt;&gt; print E.name()
+E
+</pre>
+<p>This approach presents another advantage: the decorated methods know
+the class where they were defined via the special attribute <tt class="literal"><span class="pre">__klass__</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.__dict__['name'].__klass__ # the class where 'name' is defined
+&lt;class 'E'&gt;
+</pre>
+<p>This is useful for introspection and debugging purposes.</p>
+</div>
+<div class="section" id="adding-magic">
+<h1><a class="toc-backref" href="#id4" name="adding-magic">Adding magic</a></h1>
+<p>The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking <tt class="literal"><span class="pre">decorators.decorated()</span></tt>
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt>
+on top of the module. Then all methods in all classes of the module
+with a magic docstring will be checked for magic docstrings and
+automagically decorated if needed.
+For instance, the previous example would be written</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example4.py&gt;
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ &quot;[Decorated]&quot; # magic docstring here
+ def do_nothing(self):
+ &quot;No magic docstring here&quot;
+
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+class D(object):
+ &quot;Undecorated&quot; # no magic docstring here
+ def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+c=C(); d=D()
+
+#&lt;/example4.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">C</span></tt> has a <tt class="literal"><span class="pre">[Decorated]</span></tt> docstring, so its methods
+are automatically decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example4 import *
+&gt;&gt;&gt; assert c.do_nothing() is None
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert c.identity(1) == 1
+</pre>
+<p>On the other hand, since <tt class="literal"><span class="pre">D</span></tt> misses a magic docstring,
+its <tt class="literal"><span class="pre">name</span></tt> method is not decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; hasattr(D.__dict__['name'],'__func__') # not a decorator
+False
+</pre>
+<p>Since <tt class="literal"><span class="pre">D.name</span></tt> is a regular method and not a classmethod, <tt class="literal"><span class="pre">D.name()</span></tt>
+gives an error:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance as first argument (got nothing instead)
+</pre>
+<p>The trick works for classes containing inner classes, too:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example5.py&gt;
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ &quot;[Decorated]&quot; # required docstring
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+ class Inner:
+ &quot;[Decorated]&quot; # required docstring
+ def name(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+
+assert C.identity(1) == 1
+assert C.Inner.name() == 'Inner'
+
+#&lt;/example5.py&gt;
+</pre>
+</blockquote>
+<p>Under the hood, the magic works by enhancing the <tt class="literal"><span class="pre">object</span></tt> class
+of the module with a <tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> metaclass:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import example5
+&gt;&gt;&gt; type(example5.object)
+&lt;class 'decorators.ClassDecorator'&gt;
+</pre>
+<p>Notice that for safety reasons the enhancement is only on the module
+<tt class="literal"><span class="pre">object</span></tt> class, not on the <tt class="literal"><span class="pre">__builtin__.object</span></tt> class. The problem
+is that adding too much magic can be risky.</p>
+</div>
+<div class="section" id="the-dangers-of-magic">
+<h1><a class="toc-backref" href="#id5" name="the-dangers-of-magic">The dangers of magic</a></h1>
+<p>For the sake of metaclass users, in this section I will point out the
+dangers of the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntax. On the other hand, if you
+never use metaclasses, you may safely skip to the following section.</p>
+<p>The problem is that the <tt class="literal"><span class="pre">enhance_classes()</span></tt> syntax is not 100% safe,
+because of possible metaclass conflicts.
+Here is an example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import decorators; decorators.enhance_classes()
+</pre>
+<p>This line enhances the <tt class="literal"><span class="pre">object</span></tt> class in the interpreter namespace:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print object
+&lt;class object[ClassDecorator]&gt;
+</pre>
+<p>This shows that <tt class="literal"><span class="pre">object</span></tt> is an instance of <tt class="literal"><span class="pre">ClassDecorator</span></tt>.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class M(type):
+... &quot;Some non-trivial code here...&quot;
+</pre>
+<p>This line creates a custom metaclass we want to use to enhance our classes.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>The problem is that the previous line tries to create a class <tt class="literal"><span class="pre">D</span></tt>
+which should have both metaclasses <tt class="literal"><span class="pre">ClassDecorator</span></tt> and <tt class="literal"><span class="pre">M</span></tt>:
+a conflict follows.</p>
+<p>Fortunately, the decorators module imports the <tt class="literal"><span class="pre">makecls</span></tt> function from my
+<tt class="literal"><span class="pre">noconflict</span></tt> module (described in the <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a>)
+just to avoid this kind of problems:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(object):
+... __metaclass__=decorators.makecls(M)
+</pre>
+<p>Now the class has been safely created as an instance of the composed
+class <tt class="literal"><span class="pre">ClassDecoratorM</span></tt>.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(D)
+&lt;class 'noconflict.ClassDecoratorM'&gt;
+</pre>
+<p>If we want <tt class="literal"><span class="pre">M</span></tt> to have the priority over <tt class="literal"><span class="pre">ClassDecorator</span></tt>, the
+option <tt class="literal"><span class="pre">priority=True</span></tt> makes the job:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(object):
+... __metaclass__=decorators.makecls(M,priority=True)
+&gt;&gt;&gt; type(D)
+&lt;class 'noconflict.MClassDecorator'&gt;
+</pre>
+<p>The situation for old style classes is worse, since</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D:
+... __metaclass__=M
+... def sm():
+... &quot;[staticmethod]&quot;
+</pre>
+<p>apparently gives no error, but actually the metaclass <tt class="literal"><span class="pre">M</span></tt> overrides
+<tt class="literal"><span class="pre">ClassDecorator</span></tt>, so <tt class="literal"><span class="pre">D</span></tt> will not recognize magic docstrings:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; type(D)
+&lt;class 'M'&gt;
+&gt;&gt;&gt; D.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with D instance as first argument (got nothing instead)
+</pre>
+<p>Using <tt class="literal"><span class="pre">decorators.makecls(M)</span></tt> will not help here (because of the way Python
+assigns metaclasses) and the only solution is to be completely explicit:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D:
+... __metaclass__=decorators.makecls(decorators.ClassDecorator,M)
+... def sm():
+... &quot;[staticmethod]&quot;
+&gt;&gt;&gt; type(D)
+&lt;class 'noconflict.ClassDecoratorM'&gt;
+</pre>
+<p>Now <tt class="literal"><span class="pre">D</span></tt> is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so <tt class="literal"><span class="pre">D</span></tt>
+subclasses with a &quot;[Decorated]&quot; docstring will be decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(D):
+... &quot;[Decorated]&quot;
+... def cm(cls):
+... &quot;[classmethod]&quot;
+... print '.cm() called from',cls
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.cm() # it works
+.cm() called from &lt;class E[ClassDecoratorMDecorated]&gt;
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">sm</span></tt> was defined in <tt class="literal"><span class="pre">D</span></tt>, the undecorated class: therefore
+it is not decorated:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead)
+</pre>
+<p>The error message says clearly that <tt class="literal"><span class="pre">sm</span></tt> is an unbound method and not
+a static method.</p>
+</div>
+<div class="section" id="defining-method-decorators">
+<h1><a class="toc-backref" href="#id6" name="defining-method-decorators">Defining method decorators</a></h1>
+<p>The <tt class="literal"><span class="pre">decorators</span> <span class="pre">module</span></tt> contains two predefinite method decorators,
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The <tt class="literal"><span class="pre">decorators.MethodDecorator</span></tt> class which is here
+exactly for that purpose.</p>
+<p>Custom decorators are expected to be implemented by subclassing
+<tt class="literal"><span class="pre">MethodDecorator</span></tt> and by overriding its <tt class="literal"><span class="pre">get</span></tt> method. The
+<tt class="literal"><span class="pre">get</span></tt> method automagically induces a <tt class="literal"><span class="pre">__get__</span></tt> method, turning the
+class in a descriptor. The machinery is needed since <tt class="literal"><span class="pre">__get__</span></tt> cannot
+be made cooperative using the standard <tt class="literal"><span class="pre">super</span></tt> mechanism because
+there would be a confusion between <tt class="literal"><span class="pre">super.__get__</span></tt> and the decorator
+<tt class="literal"><span class="pre">__get__</span></tt>. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators <em>too</em> easy, since they are potentially dangerous.</p>
+<p>In order to give a simple example, let me show the implementation
+of a <tt class="literal"><span class="pre">chattymethod</span></tt> that prints a message when it is called:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+from decorators import *
+
+class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Notice the usage of the <tt class="literal"><span class="pre">super().get</span></tt> trick. This guarantees that
+<tt class="literal"><span class="pre">chattymethod</span></tt> will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.</p>
+<p>Here is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import customdec # adds chattymethod to the list of known decorators
+&gt;&gt;&gt; customdec.enhance_classes() # automagically enhances classes when needed
+&gt;&gt;&gt; class C:
+... &quot; [Decorated] &quot;
+... def f(self):
+... &quot;&quot;&quot;
+... [ chattymethod ]
+... &quot;&quot;&quot;
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.f()
+calling &lt;chattymethod:f&gt; from &lt;C instance&gt;
+</pre>
+<p>By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.</p>
+<p>One can check that the syntax <tt class="literal"><span class="pre">C.f(c)</span></tt> works too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.f(c)
+calling &lt;chattymethod:f&gt; from &lt;class C[Decorated]&gt;
+</pre>
+<p>A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind</p>
+<blockquote>
+<pre class="literal-block">
+def f(self)[chattymethod(logfile=file('file1.log','w'))]
+</pre>
+</blockquote>
+<p>In my view, there are too many parenthesis in this syntax, and it may
+become rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the decorators module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.</p>
+<p>A simple minded solution is to change the defaults by hand:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from customdec import chattymethod,decorated
+&gt;&gt;&gt; chattymethod.logfile=file('file.log','w')
+&gt;&gt;&gt; def g(self):
+... &quot;[chattymethod]&quot;
+&gt;&gt;&gt; C.g=decorated(g)
+&gt;&gt;&gt; c.g() # will print a message on file.log
+</pre>
+<p>This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; c.f()
+</pre>
+<p>will print a message to <tt class="literal"><span class="pre">file.log</span></tt> too, and not to standard output.
+Here is the confirmation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; chattymethod.logfile.close()
+&gt;&gt;&gt; print file('file.log').read().rstrip()
+calling &lt;chattymethod:g&gt; from &lt;C instance&gt;
+calling &lt;chattymethod:f&gt; from &lt;C instance&gt;
+</pre>
+<p>A much better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the <tt class="literal"><span class="pre">__init__</span></tt> method. Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">__init__</span></tt> method has the signature
+<tt class="literal"><span class="pre">__init__(self,objfunc)</span></tt>, where the <tt class="literal"><span class="pre">objfunc</span></tt> object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding <tt class="literal"><span class="pre">__init__</span></tt> (see also
+<tt class="literal"><span class="pre">help(decorators.MethodDecorator.__init__)</span></tt>).</p>
+<p>Here is the testing:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty2.py&gt;
+
+import customdec; customdec.enhance_classes(&quot;[Decorated]&quot;)
+
+# sets the log files
+log1=file('file1.log','w')
+log2=file('file2.log','w')
+
+class C:
+ def f(self):
+ &quot;[chattymethod2]&quot;
+ f.logfile=log1 # function attribute
+ def g(self):
+ &quot;[chattymethod2]&quot;
+ g.logfile=log2 # function attribute
+
+assert C.__dict__['f'].logfile is log1 # check the conversion
+assert C.__dict__['g'].logfile is log2 # function attr. -&gt; decorator attr.
+
+c=C() # C instantiation
+
+c.f() # print a message in file1.log
+c.g() # print a message in file2.log
+
+log1.close(); log2.close() # finally
+
+#&lt;/chatty2.py&gt;
+</pre>
+</blockquote>
+<p>Let me check the contents of the log files:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import chatty2
+&gt;&gt;&gt; print file('file1.log').read().rstrip()
+calling &lt;chattymethod2:f&gt; from &lt;C instance&gt;
+&gt;&gt;&gt; print file('file2.log').read().rstrip()
+calling &lt;chattymethod2:g&gt; from &lt;C instance&gt;
+</pre>
+<p><tt class="literal"><span class="pre">chattymethod</span></tt> is the poor man version of <tt class="literal"><span class="pre">tracedmethod</span></tt>, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class tracedmethod(MethodDecorator):
+ &quot;Descriptor class, converts a method in a traced method&quot;
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -&gt; dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write(&quot;%sCalling '%s.%s' with arguments &quot; %
+ (i,clsname,self.funcname))
+ self.output.write(&quot;%s%s ...\n&quot; % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write(&quot;%s'%s.%s' called with result: %s\n&quot;
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method wrapper object, so it is
+possible to use <tt class="literal"><span class="pre">im_func</span></tt> to retrieve the internal function
+<tt class="literal"><span class="pre">tracedmeth</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C:
+... &quot;[Decorated]&quot;
+... def f(self):
+... &quot;[tracedmethod]&quot;
+&gt;&gt;&gt; c=C(); c.f()
+Calling 'C.f' with arguments &lt;C instance&gt;(){} ...
+'C.f' called with result: None
+&gt;&gt;&gt; c.f.im_func.__name__
+'tracedmeth'
+</pre>
+<p>As soon as the <tt class="literal"><span class="pre">tracedmethod</span></tt> module is loaded, the <tt class="literal"><span class="pre">tracedmethod</span></tt> class
+is added to the list of know decorators, so one should use the
+&quot;[tracedmethod]&quot; docstring and not something like
+&quot;[customdec.tracedmethod]&quot;.</p>
+<p>Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example9.py&gt;
+
+import customdec; customdec.enhance_classes()
+
+logfile=file('file3.log','w')
+
+class C(object):
+ &quot;[Decorated]&quot;
+ def fact(self,n):
+ &quot;[tracedmethod] The good old factorial.&quot;
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+C().fact(2) # write a message to logfile
+
+logfile.close()
+
+#&lt;/example9.py&gt;
+</pre>
+</blockquote>
+<p>Here is the content of the <tt class="literal"><span class="pre">file3.log</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import example9
+&gt;&gt;&gt; print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments &lt;C instance&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;C instance&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;C instance&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+</pre>
+</div>
+<div class="section" id="composing-decorators">
+<h1><a class="toc-backref" href="#id7" name="composing-decorators">Composing decorators</a></h1>
+<p>Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example6.py&gt;
+
+&quot;How to trace a class method&quot;
+
+import customdec; customdec.enhance_classes()
+
+class C(object):
+ &quot;[Decorated]&quot;
+ def fact(cls,n): # a traced classmethod
+ &quot;[classmethod,tracedmethod]&quot;
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+#&lt;/example6.py&gt;
+</pre>
+</blockquote>
+<p>Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example6 import C
+&gt;&gt;&gt; C.fact(2)
+Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+</pre>
+<p>You may easily check that calling <tt class="literal"><span class="pre">.fact</span></tt> from the instance will work too.</p>
+<p>Under the hood the syntax</p>
+<blockquote>
+<pre class="literal-block">
+[classmethod,tracedmethod]
+</pre>
+</blockquote>
+<p>generates a <tt class="literal"><span class="pre">classmethodtracedmethod</span></tt> class obtained via
+multiple inheritance:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__['fact'].__class__
+&lt;class 'noconflict.classmethodtracedmethod'&gt;
+</pre>
+<p>Notice that the order does matter and using the docstring
+&quot;[tracedmethod,classmethod]&quot; will not work:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D:
+... &quot;[Decorated]&quot;
+... def f(cls):
+... &quot;[tracedmethod,classmethod]&quot;
+&gt;&gt;&gt; D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead)
+</pre>
+<p>The problem here is that <tt class="literal"><span class="pre">tracedmethod.get</span></tt> returns a method-wrapper object
+which expects a D instance as first argument whereas it gets <tt class="literal"><span class="pre">None</span></tt> since
+it is called from the class. On the other hand,</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D().f()
+Calling 'D.f' with arguments &lt;D instance&gt;(){} ...
+'D.f' called with result: None
+</pre>
+<p>will work. When <tt class="literal"><span class="pre">classmethod</span></tt> precedes <tt class="literal"><span class="pre">tracedmethod</span></tt>, then
+<tt class="literal"><span class="pre">classmethod</span></tt> passes to <tt class="literal"><span class="pre">tracedmeth</span></tt> a non-empty first argument,
+i.e. the calling class, even when called from the instance:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C().fact(2)
+Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(2,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(1,){} ...
+ Calling 'C.fact' with arguments &lt;class C[Decorated]&gt;(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+</pre>
+<p>If we try to trace a staticmethod, we will get a different error with
+the order &quot;tracedmethod, staticmethod&quot;:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class F(object):
+... &quot;[Decorated]&quot;
+... def fact(n):
+... &quot;[tracedmethod,staticmethod]&quot;
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+&gt;&gt;&gt; F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead)
+</pre>
+<p>The message is self-explanatory.</p>
+<p>On the other hand, composing the decorators in the other order
+&quot;[tracedmethod,staticmethod]&quot; will work just fine.</p>
+</div>
+<div class="section" id="defining-class-decorators">
+<h1><a class="toc-backref" href="#id8" name="defining-class-decorators">Defining class decorators</a></h1>
+<p>PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a
+class decorator at work, the metaclass <tt class="literal"><span class="pre">Decorated</span></tt>, giving
+to its instances the ability to interpret magic docstrings,
+and converting functions in method decorators.</p>
+<p>To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference from deriving by <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead
+of deriving from <tt class="literal"><span class="pre">type</span></tt>. To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print &gt;&gt; cls.output,&quot;%s created&quot; % cls
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">Logged</span></tt> is derived by the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>). Logging capabilities can be added to a class
+by simply using the magic docstring syntax:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;logged.py&gt;
+
+import customdec; customdec.enhance_classes()
+
+class D(object): # will print a message at D creation
+ &quot;[Logged]&quot;
+
+#&lt;/logged.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; import logged
+&lt;class D[Logged]&gt; created
+</pre>
+<p>Notice that the printing representation of <tt class="literal"><span class="pre">D</span></tt> involves the name
+of <tt class="literal"><span class="pre">D</span></tt> preceded by the name of its metaclass, which in this case
+is <tt class="literal"><span class="pre">Logged</span></tt></p>
+<p>Each time an instance of <tt class="literal"><span class="pre">Logged</span></tt> is created, a similar message is printed:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(logged.D):
+... pass
+&lt;class E[Logged]&gt; created
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">E</span></tt> does not have any magic docstring</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.__doc__ # no docstring
+</pre>
+<p>but still it inherits its magic from <tt class="literal"><span class="pre">D</span></tt>.</p>
+<p>Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;customdec.py&gt;
+
+from types import FunctionType
+
+class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType): # modifies the docstring
+ func.__doc__=&quot;[tracedmethod] &quot; + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+
+#&lt;/customdec.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... &quot;&quot;&quot;[Decorated,Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... to method decorator objects.&quot;&quot;&quot;
+... def f1(self): pass
+... def f2(self): pass
+...
+&gt;&gt;&gt; type(C)
+&lt;class 'noconflict.DecoratedTraced'&gt;
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.f1()
+Calling 'C.f1' with arguments &lt;C instance&gt;(){} ...
+'C.f1' called with result: None
+&gt;&gt;&gt; c.f2()
+Calling 'C.f2' with arguments &lt;C instance&gt;(){} ...
+'C.f2' called with result: None
+</pre>
+<p>By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt>
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example.py&gt;
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes(&quot;[Decorated]&quot;)
+
+class C1: # automagically converted to a decorated class
+ identity=identity
+
+class C2: # automagically converted to a DecoratedLogged class
+ &quot;[Logged]&quot;
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+#&lt;/example.py&gt;
+</pre>
+</blockquote>
+<p>Notice that class <tt class="literal"><span class="pre">C2</span></tt> has already a magic docstring. This means that
+<tt class="literal"><span class="pre">C2</span></tt> has to be enhanced both from <tt class="literal"><span class="pre">Logged</span></tt> and from <tt class="literal"><span class="pre">Decorated</span></tt>.
+This is done by automagically creating a <tt class="literal"><span class="pre">DecoratedLogged</span></tt> class
+decorator:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example import C1,C2,c1,c2
+&lt;class C2[DecoratedLogged]&gt; created
+</pre>
+<p>The second line is printed because of the logging capabilities of <tt class="literal"><span class="pre">C2</span></tt>.
+Moreover, since <tt class="literal"><span class="pre">C2</span></tt> is decorated too, the following will work:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert C2.name() == 'C2'
+&gt;&gt;&gt; assert c2.name() == 'C2'
+</pre>
+<p>Idem for <tt class="literal"><span class="pre">C1</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; assert C1.identity(1) == 1
+&gt;&gt;&gt; assert c1.identity(1) == 1
+</pre>
+<p>You may check that the magic works both for new style classes (decorating
+module <tt class="literal"><span class="pre">object</span></tt> class) and old style classes (by setting the module level
+<tt class="literal"><span class="pre">__metaclass</span></tt> attribute and by implicitly converting the classes
+to new style classes).</p>
+<p>This magical approach is the easiest way to go if you want to trace
+inheritance hierarchies. For instance, here is how to trace cooperative
+methods in complicate (which is useful for debugging):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracing.py&gt;
+
+import customdec; customdec.enhance_classes(&quot;[Decorated]&quot;)
+
+class B(object):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(B,self).__init__()
+
+class D(object):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(D,self).__init__()
+
+class E(B,D):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(E,self).__init__()
+
+ #&lt;/tracing.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracing import E
+&gt;&gt;&gt; e=E()
+Calling 'E.__init__' with arguments &lt;E instance&gt;(){} ...
+ Calling 'B.__init__' with arguments &lt;E instance&gt;(){} ...
+ Calling 'D.__init__' with arguments &lt;E instance&gt;(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None
+</pre>
+</div>
+<div class="section" id="advanced-usage">
+<h1><a class="toc-backref" href="#id9" name="advanced-usage">Advanced usage</a></h1>
+<p>Whereas the average programmer is expected to use the
+<tt class="literal"><span class="pre">decorators.decorated</span></tt> function only, the module provides access to
+its underlining implementation, which may be useful to the advanced
+programmer.</p>
+<p>The module provides an utility functions to retrieve the list of
+recognized decorators: <tt class="literal"><span class="pre">decorators.get(docstring)</span></tt>, where <tt class="literal"><span class="pre">docstring</span></tt>
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance <tt class="literal"><span class="pre">decorators.get('[MethodDecorator]')</span></tt>
+gives the list of all subclasses of <tt class="literal"><span class="pre">MethodDecorator</span></tt>, i.e. all method
+decorators, whereas <tt class="literal"><span class="pre">decorators.get('[ClassDecorator]')</span></tt>
+gives the list of the known class decorators. It is even possible
+to use the comma notation:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; decorators.get(&quot;[classmethod,tracedmethod]&quot;)
+[&lt;class 'noconflict.classmethodtracedmethod'&gt;]
+</pre>
+<p>For instance, it is possible to decorate functions by hand,
+without using magic docstring. Here is an example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; do_nothing=decorators.staticmethod(lambda:None)
+&gt;&gt;&gt; print do_nothing # ``do_nothing`` is a static method
+&lt;staticmethod:&lt;lambda&gt;&gt;
+</pre>
+<p>One can even compose decorators by hand:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B: pass
+...
+&gt;&gt;&gt; B.chattystatic=customdec.chattymethod2(do_nothing)
+&gt;&gt;&gt; B.chattystatic()
+calling &lt;chattymethod2staticmethod:&lt;lambda&gt;&gt; from &lt;class B[ClassDecorator]&gt;
+</pre>
+<p>In other words</p>
+<blockquote>
+<tt class="literal"><span class="pre">decorator1(decorator2(obj))</span></tt></blockquote>
+<p>automagically creates a composed class <tt class="literal"><span class="pre">decorator1decorator2</span></tt> in the
+<tt class="literal"><span class="pre">noconflict</span></tt> module (or recycle it, if <tt class="literal"><span class="pre">decorator1decorator2</span></tt> has
+been already created) and it is equivalent to</p>
+<blockquote>
+<tt class="literal"><span class="pre">decorator1decorator2(obj)</span></tt></blockquote>
+<p>Here is the check:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; decorators.get(&quot;[chattymethod2staticmethod]&quot;)
+[&lt;class 'noconflict.chattymethod2staticmethod'&gt;]
+</pre>
+<p>Here is another example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from customdec import tracedmethod
+&gt;&gt;&gt; class C(object):
+... def fact(self,n):
+... if n==0: return 1
+... else: return n*self.fact(n-1)
+... fact=tracedmethod(fact)
+&gt;&gt;&gt; c=C()
+&gt;&gt;&gt; c.fact(2)
+Calling '?.fact' with arguments &lt;C instance&gt;(2,){} ...
+ Calling '?.fact' with arguments &lt;C instance&gt;(1,){} ...
+ Calling '?.fact' with arguments &lt;C instance&gt;(0,){} ...
+ '?.fact' called with result: 1
+ '?.fact' called with result: 1
+'?.fact' called with result: 2
+2
+</pre>
+<p>In this second syntax <tt class="literal"><span class="pre">fact</span></tt> does not know where it
+is defined, unless the containing class is explicitly set:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C.__dict__['fact'].__klass__=C
+</pre>
+<p>Now the code will work as with the docstring syntax.</p>
+<p>What happens if you try to decorate something which is already
+decorated? You get <tt class="literal"><span class="pre">None</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def f(x): &quot;[tracedmethod]&quot;
+...
+&gt;&gt;&gt; tm=decorators.decorated(f) # creates a tracedmethod from f
+&gt;&gt;&gt; print decorators.decorated(tm) # trying to decorate a tracedmethod
+None
+</pre>
+<p>You can compose decorators in a trivial way:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; cmtm=decorators.classmethod(tm)
+&gt;&gt;&gt; print cmtm
+&lt;classmethodtracedmethod:f&gt;
+</pre>
+<p>whereas you can compose classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C: &quot;[Decorated]&quot;
+...
+&gt;&gt;&gt; print customdec.Traced(C)
+&lt;class C[TracedDecorated]&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D: pass
+...
+&gt;&gt;&gt; print customdec.Decorated(customdec.Traced(D))
+&lt;class D[DecoratedTraced]&gt;
+</pre>
+<p>This is the hierarchy:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for C in type(cmtm).mro(): print C
+...
+&lt;class 'noconflict.classmethodtracedmethod'&gt;
+&lt;class 'decorators.classmethod'&gt;
+&lt;class 'customdec.tracedmethod'&gt;
+&lt;class 'decorators.MethodDecorator'&gt;
+&lt;class 'decorators.Decorator'&gt;
+&lt;type 'object'&gt;
+</pre>
+<p>One can also decorate classes by hand, by using class decorators.
+<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> which converts a regular (both old
+style or new style) class in a class with the ability to recognize
+magic docstrings. Actually, the <tt class="literal"><span class="pre">decorators.enhance_classes()</span></tt>
+trick works by decorating the <tt class="literal"><span class="pre">object</span></tt> class with
+<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and by setting the custom metaclass to
+<tt class="literal"><span class="pre">decorators.ClassDecorator</span></tt> and it is equivalent to write</p>
+<blockquote>
+<pre class="literal-block">
+import decorators
+object=decorators.ClassDecorator(object) # decorates all new style classes
+__metaclass__= decorators.ClassDecorator # decorates all old style classes
+</pre>
+</blockquote>
+<p>on top of your module.
+If you want the magic to work only for new style classes only, you may
+forget the
+second line; if you want the magic to work for old style classes only, you
+may forget the first line.</p>
+<p>If the module contains a magic docstring which is an explicit class decorator,
+such as <tt class="literal"><span class="pre">decorators.Decorated</span></tt>, then <tt class="literal"><span class="pre">decorators.decorated</span></tt> look at the
+magic docstring and executes something like</p>
+<blockquote>
+<pre class="literal-block">
+import decorators
+object=decorators.Decorated(object) # decorates all new style classes
+__metaclass__= decorators.Decorated # decorates all old style classes
+</pre>
+</blockquote>
+<p>It is possible to go even deeper in black magic, and to decorate all
+the new style classes in <em>all</em> modules, by decorating <tt class="literal"><span class="pre">__builtin__.object</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+import decorators,__builtin__
+__builtin.object=decorators.decorated(object)
+</pre>
+</blockquote>
+<p>Still, redefining <tt class="literal"><span class="pre">__builtin__object</span></tt> is not recommended since it
+may induce metaclass conflicts in other modules using metaclasses.
+It will work only if you import modules not using metaclasses, or
+modules using metaclasses safely, i.e. modules taking care of
+possible conflicts by using the <tt class="literal"><span class="pre">makecls</span></tt> function or an equivalent one.</p>
+</div>
+<div class="section" id="the-implementation">
+<h1><a class="toc-backref" href="#id10" name="the-implementation">The implementation</a></h1>
+<p>This part can be safely skipped, unless you are a <em>really</em> curious and
+you want to know how the implementation works.</p>
+<p>The module is rather short (~150 lines) but far from being trivial,
+since it is based on extensive usage of metaclass wizardry.</p>
+<p>The main class-metaclass hierarchy is represented in figure 1, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.</p>
+<div class="figure">
+<p><img alt="decorators.png" src="decorators.png" /></p>
+</div>
+<p>Notice that <tt class="literal"><span class="pre">MetaDecorator</span></tt> (inherited via <tt class="literal"><span class="pre">Decorator</span></tt>) is the
+metaclass of all decorators. Class decorators are already metaclasses,
+so <tt class="literal"><span class="pre">MetaDecorator</span></tt> is actually a meta-metaclass with respect to
+instances of decorated classes, whereas it is a simple metaclass with
+respect to instances of decorated methods. For this reason in the
+presenced of class decorators the unusual relation</p>
+<p>assert object.__metaclass__ != object.__class__</p>
+<p>holds. More specifically</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; object.__class__
+&lt;class 'decorators.ClassDecorator'&gt;
+</pre>
+<p>but</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; object.__metaclass__
+&lt;class 'decorators.MetaDecorator'&gt;
+</pre>
+<p>since <tt class="literal"><span class="pre">object</span></tt> the <tt class="literal"><span class="pre">__metaclass__</span></tt>
+attribute is inherited from <tt class="literal"><span class="pre">Decorator</span></tt>.</p>
+<p>The implementation is relatively smart. Consider for instance the case of
+the logged example. In that example class <tt class="literal"><span class="pre">D</span></tt> was a subclass of a tricked
+<tt class="literal"><span class="pre">object</span></tt> class, enhanced by a metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>. Moreover <tt class="literal"><span class="pre">D</span></tt>
+had a <tt class="literal"><span class="pre">Logged</span></tt> docstring. Therefore it should have been an instance of
+<tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt> metaclass. But logged was
+a subclass of <tt class="literal"><span class="pre">ClassDecorator</span></tt>, therefore it already had all the features
+of <tt class="literal"><span class="pre">ClassDecorator</span></tt> and it would have been redundant to create
+<tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt>, when``Logged`` would have been enough.
+So <tt class="literal"><span class="pre">Logged</span></tt>
+was used and <tt class="literal"><span class="pre">_ClassDecoratorLogged</span></tt> was never created. All the magic is in
+the <tt class="literal"><span class="pre">noconflict</span></tt> module discussed in my <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a> recipe.</p>
+<p>The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. They does not probably matter unless one has to decorate
+thousands or millions of functions and classes.</p>
+<p>Things to do: adding more error checking.</p>
+<p>Finally, a word about bugs. The <tt class="literal"><span class="pre">decorators</span></tt> module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~100 tests automatically extracted from the documentation you are reading),
+I cannot guarantee that it is correct.
+If somebody finds a bug or an unexpected
+behavior, please let me know and I will try to fix it.</p>
+<!-- References: -->
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="decorators.txt">View document source</a>.
+Generated on: 2003-09-17 14:45 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/working/decorators.py b/pypers/pep318/working/decorators.py
new file mode 100755
index 0000000..ddf3ec9
--- /dev/null
+++ b/pypers/pep318/working/decorators.py
@@ -0,0 +1,173 @@
+# implementation with a strong usage of metaclasses
+
+"""
+A module to implement pep318 (decorator syntax) via magic doctrings.
+For the documentation see
+
+http://www.phyast.pitt.edu/~micheles/python/decorators,html
+
+and the on-line help.
+"""
+
+import sys, re, inspect, __builtin__, noconflict
+from types import FunctionType,ClassType
+from noconflict import makecls
+#from printerr import printerr
+
+############################ UTILITIES ##############################
+
+MAGICDOC=re.compile(r'\s*\[([\w_\s,]*)\]')
+# regexp to recognize magic docstrings
+
+class UnknownDecoratorError(Exception):
+ "The name says it all"
+
+class MetaDecorator(type):
+ """Metaclass inducing a certain amount of magic on decorators:
+ 1. Each time a decorator is defined in any module, it is stored in
+ MetaDecorator.dic and MetaDecorator.ls.
+ 2. If the (method) decorator has a 'get' method, a '__get__' method
+ is automagically created as an alias to 'get'.
+ 3. Decorators calls are dispatched to the decorator _call_
+ classmethod."""
+
+ dic,ls={},[]
+
+ def __init__(dec,*args):
+ super(MetaDecorator,dec).__init__(*args)
+ MetaDecorator.dic[dec.__name__]=dec
+ MetaDecorator.ls.append(dec)
+ get=dec.__dict__.get('get') # look at get
+ if get: dec.__get__=get # alias __get__ to get
+
+ def __call__(dec,*args):
+ nargs=len(args)
+ if nargs==1: # simple call of kind dec(obj)
+ obj=args[0]
+ if isinstance(obj,Decorator):
+ dec=composed(dec,obj.__class__) # composed default decorator
+ dec=decorator_from(obj.__doc__,dec)
+ elif nargs==3: # class decorator called with three arguments
+ dec=decorator_from(args[2].get('__doc__'),dec)
+ return dec._call_(*args) # dispatch to the correct _call_ classmethod
+
+def composed(*dclasses):
+ dclasses=noconflict.remove_redundant(dclasses)
+ decname=''.join([d.__name__ for d in dclasses])
+ decorator=MetaDecorator.dic.get(decname)
+ if not decorator: decorator=makecls()(decname,dclasses,{})
+ return decorator
+
+def decorator_from(docstring,defaultdec=None):
+ """Takes a magic docstring and a default decorator
+ and returns a decorator class or None. It tries to be smart."""
+ match=MAGICDOC.match(docstring or '')
+ if not match: return defaultdec
+ decnames=map(str.strip,match.group(1).split(','))
+ try: dclasses=[MetaDecorator.dic[n] for n in decnames] # get the decorator
+ except KeyError: raise UnknownDecoratorError(n) # classes from the names
+ if defaultdec: dclasses.insert(0,defaultdec)
+ return composed(*dclasses)
+
+def decorate(objdict):
+ """Takes an object with a dictionary and decorates all its functions
+ and classes according to their docstrings."""
+ for name,obj in objdict.__dict__.items():
+ if getattr(obj,'__doc__',None): # non-empty docstring
+ dec_obj=decorated(obj) or obj
+ if dec_obj is not obj:
+ setattr(dec_obj,'__klass__',objdict)
+ setattr(objdict,name,dec_obj)
+
+def get(docstring=None):
+ "List of recognized decorators"
+ dec=decorator_from(docstring,Decorator)
+ isdecorator=lambda x: issubclass(x,dec)
+ return filter(isdecorator,MetaDecorator.ls)
+
+#################### functions of the API #################################
+
+def decorated(obj):
+ """If obj is a function or a class, returns a decorated object created
+ according to obj's docstring or the original obj if no magic docstring
+ is found; otherwise it returns None"""
+ if isinstance(obj,(FunctionType,ClassType,type)): # is function or class
+ return Decorator(obj) or obj
+
+def enhance_classes(docstring='[ClassDecorator]'):
+ """Enhances all the classes in the caller module with a metaclass
+ built from the given docstring."""
+ callerglobals=sys._getframe(1).f_globals
+ dec=decorator_from(docstring)
+ callerglobals['__metaclass__']=dec
+ callerglobals['object']=dec('object',(),{})
+
+####################### BASIC DECORATORS ###########################
+
+class Decorator(object):
+ """Instance of MetaDecorator and mothers of all decorators. When called
+ in the form Decorator(obj), with obj having a magic docstring, it returns
+ an instance of the correct decorator, otherwise it returns None."""
+ __metaclass__=MetaDecorator
+ def _call_(dec,obj):
+ "Returns None, all the interesting stuff is in MetaDecorator.__call__"
+ _call_=classmethod(_call_)
+
+class MethodDecorator(Decorator):
+ """Descriptor class callable with a function or a descriptor object
+ as argument. The calling syntax is redefined by the meta-metaclass
+ MetaDecorator. It redefines__str__ and get (i.e. __get__) on its instances.
+ """
+ __klass__=type('?',(),{}) # dummy owner class, to be overridden
+ def __init__(self,objfunc):
+ "objfunc is a decorator object or a function"
+ assert isinstance(objfunc,(FunctionType,Decorator))
+ super(MethodDecorator,self).__init__(objfunc)
+ self.__func__=getattr(objfunc,'__func__',objfunc)
+ def get(self,obj,cls=None):
+ "aliased to __get__, to be overridden"
+ return self.__func__.__get__(obj,cls)
+ def __str__(self):
+ "Printing representation of kind <decoratorclass:functionname>"
+ return '<%s:%s>' % (self.__class__.__name__,self.__func__.__name__)
+ def _call_(dec,obj):
+ "Returns a method decorator object."
+ return type.__call__(dec,obj) # calls __new__ and __init__
+ _call_=classmethod(_call_)
+
+
+class ClassDecorator(type,Decorator):
+ """Metaclass callable with one or three arguments, having its calling
+ syntax redefined by the meta-metaclass MetaDecorator. It redefines
+ __str__ both on classes and instances."""
+ def __init__(cls,name,bases,dic):
+ super(ClassDecorator,cls).__init__(name,bases,dic)
+ if cls.__str__ is object.__str__: # redefine default __str__
+ cls.__str__=lambda self: "<%s instance>" % self.__class__.__name__
+ def __str__(cls):
+ return '<class %s[%s]>' % (cls.__name__,cls.__class__.__name__)
+ def _call_(dec,*args):
+ "Returns a decorated class"
+ a=args[0] # first argument; can be a string or a class
+ if inspect.isclass(a): args=a.__name__,a.__bases__,a.__dict__.copy()
+ return makecls(dec)(*args)
+ _call_=classmethod(_call_)
+
+class Decorated(ClassDecorator):
+ """Metaclass which decorates its instances"""
+ def __init__(cls,name,bases,dic):
+ super(Decorated,cls).__init__(name,bases,dic)
+ decorate(cls)
+
+#################### Built-in Method Decorators ######################
+
+class staticmethod(MethodDecorator):
+ "Decorator, converts a function in a staticmethod"
+ def get(self,obj,cls=None):
+ return super(staticmethod,self).get(obj,cls).im_func
+
+class classmethod(MethodDecorator):
+ "Decorator, converts a function in a classmethod"
+ def get(self,obj,cls=None):
+ if cls is None: cls=type(obj)
+ return super(classmethod,self).get(cls,cls)
diff --git a/pypers/pep318/working/decorators.txt b/pypers/pep318/working/decorators.txt
new file mode 100755
index 0000000..18fb320
--- /dev/null
+++ b/pypers/pep318/working/decorators.txt
@@ -0,0 +1,1346 @@
+Implementing PEP 318 (decorators)
+======================================================================
+
+ :Module: decorators
+ :Version: 0.5
+ :Author: Michele Simionato
+ :e-mail: MicheleSimionato@libero.it
+ :Licence: Python-like
+ :Disclaimer: This is experimental code. Use it at your own risk!
+
+.. contents::
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of PEP 318 in pure Python.
+
+Here is the rationale:
+
+* some kind of decorator syntax is scheduled to go in Python 2.4,
+ therefore it is interesting to play with the concept;
+
+* it is nice to play with decorators now, without having to
+ wait for one year or so;
+
+* it is much easier levelto experiment with a pure Python implementation
+ than with a C implementation;
+
+* the implementation can be seen as an exercise on modern Python
+ programming and may be valuable to people wanting to study the most
+ advanced new constructs in Python 2.2 (descriptors_, metaclasses_,
+ `cooperative methods`_, etc.)
+
+Basics
+--------------------------------------------------------------------
+
+PEP 318 has the goal of providing a nice syntactic sugar for expressions like
+
+ ::
+
+ def identity(x):
+ return x
+ identity=staticmethod(identity)
+
+or
+
+ ::
+
+ def name(cls):
+ return cls.__name__
+ name=classmethod(name)
+
+which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)
+
+ ::
+
+ def identity(x)[staticmethod]:
+ return x
+
+ def name(cls)[classmethod]:
+ return cls.__name__
+
+involves changing the grammar and modifying the interpreter at the
+C level. This means a lot of work. Fortunately, it is possible to
+have the same effect without changing the Python grammar.
+The idea is to use magic docstrings like this:
+
+ ::
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+The implementation is able to recognize such docstrings
+and to automagically convert those methods in decorators.
+
+Decorators are nothing else than a sophisticated kind of wrappers.
+The ``decorators`` module provides support both for method decorators,
+which wrap functions and class decorator, which wrap classes.
+``staticmethod`` and ``classmethod`` are two examples of already existing
+method decorators (actually my implementation rewrites them, but let me
+pass on this detail). Technically speaking, method decorators are classes
+taking a single function as input and producing a descriptor object
+as output (properties are not decorators according to this definition,
+since they take four functions as input, ``get, set, del_`` and ``doc``).
+Descriptors are objects with a ``__get__`` method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip on this point ;).
+A knowledge of descriptors is not needed in order to use the ``decorator``
+module; however it is welcomed for advanced users wanting to implement
+custom method decorators.
+Class decorators are metaclasses taking a class as imput and returning
+a decorated class as output. A good understanding of metaclasses is needed
+in order to be able to write custom class decorators, but no knowledge
+at all is required in order to use the pre-defined class decorators
+provided by the module.
+Finally, the module is meant to be extensible; so one could
+define new kind of decorators. For instance, the original version of
+the module also had the concept of module decorators; however I have cut
+down that part in order to keep the module short.
+
+Admittedly, the implementation
+is not for the faint of heart, nevertheless I have tried to make the
+basic usage easy and simple to understand.
+
+Simple usage of decorators
+------------------------------------------------------------------------
+
+Before talking about the implementation details, I will show
+how the ``decorators`` module works in practice. The simplest and safest
+usage is by means of the ``decorators.decorated()`` function, which
+takes an object (a function or a class) and checks its docstring: if
+a magic docstring is found, it returns a decorated version of the object,
+otherwise it returns the original object. Using ``decorators.decorated()``
+is simple but verbose, so magic shortcuts will be discussed in the next
+section.
+
+Here, let me give an example, showing that method decorators work both for
+`new style classes and old style classes`_:
+
+ ::
+
+ #<example1.py>
+
+ import decorators
+
+ def do_nothing(self):
+ "No magic docstring here"
+ dec_do_nothing=decorators.decorated(do_nothing)
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+ dec_identity=decorators.decorated(identity)
+
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+ dec_name=decorators.decorated(name)
+
+ class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+ class NewStyle(object):
+ name=dec_name
+
+ o=OldStyle() # creates an old style instance
+ n=NewStyle() # creates a new style instance
+
+ #</example1.py>
+
+In this example, both ``dec_identity`` and ``dec_name`` are decorator objects,
+i.e. descriptors modifiying the attribute access. It is easy to recognize
+decorators objects, since they have a re-defined printing representation:
+
+>>> from example1 import *
+
+>>> print dec_identity
+<staticmethod:identity>
+
+>>> print dec_name
+<classmethod:name>
+
+Now, let me check that ``dec_ identity`` works as a staticmethod,
+
+>>> assert OldStyle.identity(1) == 1 # called from the class
+>>> assert o.identity(1) == 1 # called from the instance
+
+whereas ``dec_name`` works as a classmethod:
+
+>>> assert NewStyle.name() == 'NewStyle' # called from the class
+>>> assert n.name() == 'NewStyle' # called from the instance
+
+On the other hand, ``do_nothing`` does not have a magic
+docstring, therefore it is not converted to a decorator object;
+actually it is *exactly* the original function:
+
+>>> assert dec_do_nothing is do_nothing # not converted
+
+Therefore it works without surprises:
+
+>>> o.do_nothing() # does nothing, correct
+
+For sake of convenience, I have re-implemented the built-in
+``staticmethod`` and ``classmethod``, so
+
+>>> isinstance(dec_identity,staticmethod)
+False
+
+and
+
+>>> isinstance(dec_name,classmethod)
+False
+
+but
+
+>>> isinstance(dec_identity,decorators.staticmethod)
+True
+
+and
+
+>>> isinstance(dec_name,decorators.classmethod)
+True
+
+It is possible to recognize method decorators since they provides
+a couple of special attributes:
+
+``__func__``, returning the function from which they originated
+
+ >>> assert dec_identity.__func__ is identity
+ >>> assert dec_name.__func__ is name
+
+and ``__klass__``, returning the class where they where defined
+
+ >>> dec_identity.__klass__
+ <class 'decorators.?'>
+
+The question mark here means that the definition class is unknown.
+
+Decorating classes
+----------------------------------------------------------------------
+
+The problem with the approach described in the previous section
+is that it does not present any significant advantage over
+the already existing mechanism. A real step forward would be to
+have classes with the ability of automatically converting their
+methods to method decorators according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply
+by adding to the class a docstring starting with "[Decorated]"
+and decorating the class.
+Here is an example:
+
+ ::
+
+ #<example2.py>
+
+ from decorators import decorated
+ from example1 import do_nothing,identity,name
+
+ class B(object):
+ "This is a regular class"
+
+ B=decorated(B) # does nothing
+
+ class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ name=name
+
+ C=decorated(C) # regenerates the class converting methods in decorators
+ c=C()
+
+ #</example2.py>
+
+Here is the testing:
+
+>>> from example2 import *
+>>> assert C.identity(1) == 1
+>>> assert C.name() == 'C'
+>>> assert c.identity(1) == 1
+>>> assert c.name() == 'C'
+
+Notice that adding ``identity`` after the class creation with the syntax
+``C.identity=identity`` would not work;
+``C.identity=decorators.decorated(identity)`` is required:
+
+>>> C.identity=decorators.decorated(identity)
+>>> C.identity(1) # it works
+1
+
+If a class misses the magic docstring, nothing happens:
+
+>>> B # returns the original B
+<class 'example2.B'>
+
+The mechanism works for old style classes too:
+
+ ::
+
+ #<example2.py>
+
+ class D: # old style
+ "[Decorated]"
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ D=decorated(D)
+
+ d=D()
+
+ # test
+ assert d.identity(1) == 1
+ assert D.identity(1) == 1
+
+ #</example2.py>
+
+Under the hood ``decorators.decorated()`` recognizes the class level
+magic docstring "[Decorated]" and creates an instance of the
+``decorators.Decorated`` metaclass; incidentally,
+this converts old style classes in new style classes:
+
+>>> from example2 import D,d
+>>> type(D) # D is an instance of decorator.Decorated
+<class 'decorators.Decorated'>
+
+Internally the metaclass invokes ``decorators.decorated()``
+on the methods of its instances: this is why they becomes decorated
+if a suitable docstring is found. By the way, if you mispell a
+decorator name you get an helpful error message:
+
+>>> class E:
+... "[Decorated]"
+... def f():
+... "[staticmeth]"
+>>> E=decorators.decorated(E)
+Traceback (most recent call last):
+ .. a long and cryptic traceback here ..
+UnknownDecoratorError: staticmeth
+
+The enhancement provided by the metaclass includes a new default
+printing representation for both the class
+
+>>> print D # returns the name of D and of its metaclass
+<class D[Decorated]>
+
+and its instances:
+
+>>> print d
+<D instance>
+
+One can even forget the docstring in subclasses of decorated
+classes, since metaclasses are inherited:
+
+>>> class E(D):
+... def name(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print E.name()
+E
+
+This approach presents another advantage: the decorated methods know
+the class where they were defined via the special attribute ``__klass__``:
+
+>>> E.__dict__['name'].__klass__ # the class where 'name' is defined
+<class 'E'>
+
+This is useful for introspection and debugging purposes.
+
+Adding magic
+----------------------------------------------------------------------------
+
+The problem of the previous approach is that one must explicitely
+decorate the classes by hand, by invoking ``decorators.decorated()``
+each time. However, it is possible to add more magic
+and to decorate all the classes automatically.
+It is as easy as writing ``decorators.enhance_classes()``
+on top of the module. Then all methods in all classes of the module
+with a magic docstring will be checked for magic docstrings and
+automagically decorated if needed.
+For instance, the previous example would be written
+
+ ::
+
+ #<example4.py>
+
+ import decorators; decorators.enhance_classes()
+
+ class C:
+ "[Decorated]" # magic docstring here
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ class D(object):
+ "Undecorated" # no magic docstring here
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ c=C(); d=D()
+
+ #</example4.py>
+
+``C`` has a ``[Decorated]`` docstring, so its methods
+are automatically decorated:
+
+>>> from example4 import *
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert c.identity(1) == 1
+
+On the other hand, since ``D`` misses a magic docstring,
+its ``name`` method is not decorated:
+
+>>> hasattr(D.__dict__['name'],'__func__') # not a decorator
+False
+
+Since ``D.name`` is a regular method and not a classmethod, ``D.name()``
+gives an error:
+
+>>> D.name()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method name() must be called with D instance as first argument (got nothing instead)
+
+The trick works for classes containing inner classes, too:
+
+ ::
+
+ #<example5.py>
+
+ import decorators; decorators.enhance_classes()
+
+ class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+
+ assert C.identity(1) == 1
+ assert C.Inner.name() == 'Inner'
+
+ #</example5.py>
+
+Under the hood, the magic works by enhancing the ``object`` class
+of the module with a ``decorators.ClassDecorator`` metaclass:
+
+>>> import example5
+>>> type(example5.object)
+<class 'decorators.ClassDecorator'>
+
+Notice that for safety reasons the enhancement is only on the module
+``object`` class, not on the ``__builtin__.object`` class. The problem
+is that adding too much magic can be risky.
+
+The dangers of magic
+-----------------------------------------------------------------------
+
+For the sake of metaclass users, in this section I will point out the
+dangers of the ``enhance_classes()`` syntax. On the other hand, if you
+never use metaclasses, you may safely skip to the following section.
+
+The problem is that the ``enhance_classes()`` syntax is not 100% safe,
+because of possible metaclass conflicts.
+Here is an example:
+
+>>> import decorators; decorators.enhance_classes()
+
+This line enhances the ``object`` class in the interpreter namespace:
+
+>>> print object
+<class object[ClassDecorator]>
+
+This shows that ``object`` is an instance of ``ClassDecorator``.
+
+>>> class M(type):
+... "Some non-trivial code here..."
+
+This line creates a custom metaclass we want to use to enhance our classes.
+
+>>> class D(object): __metaclass__=M # does not work!
+...
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The problem is that the previous line tries to create a class ``D``
+which should have both metaclasses ``ClassDecorator`` and ``M``:
+a conflict follows.
+
+Fortunately, the decorators module imports the ``makecls`` function from my
+``noconflict`` module (described in the cookbook_)
+just to avoid this kind of problems:
+
+>>> class D(object):
+... __metaclass__=decorators.makecls(M)
+
+Now the class has been safely created as an instance of the composed
+class ``ClassDecoratorM``.
+
+>>> type(D)
+<class 'noconflict.ClassDecoratorM'>
+
+If we want ``M`` to have the priority over ``ClassDecorator``, the
+option ``priority=True`` makes the job:
+
+>>> class D(object):
+... __metaclass__=decorators.makecls(M,priority=True)
+>>> type(D)
+<class 'noconflict.MClassDecorator'>
+
+The situation for old style classes is worse, since
+
+>>> class D:
+... __metaclass__=M
+... def sm():
+... "[staticmethod]"
+
+apparently gives no error, but actually the metaclass ``M`` overrides
+``ClassDecorator``, so ``D`` will not recognize magic docstrings:
+
+>>> type(D)
+<class 'M'>
+>>> D.sm()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with D instance as first argument (got nothing instead)
+
+Using ``decorators.makecls(M)`` will not help here (because of the way Python
+assigns metaclasses) and the only solution is to be completely explicit:
+
+>>> class D:
+... __metaclass__=decorators.makecls(decorators.ClassDecorator,M)
+... def sm():
+... "[staticmethod]"
+>>> type(D)
+<class 'noconflict.ClassDecoratorM'>
+
+Now ``D`` is not decorated since it does miss a magic docstring, but
+it provides the ability to recognizing magic docstrings, so ``D``
+subclasses with a "[Decorated]" docstring will be decorated:
+
+>>> class E(D):
+... "[Decorated]"
+... def cm(cls):
+... "[classmethod]"
+... print '.cm() called from',cls
+
+>>> E.cm() # it works
+.cm() called from <class E[ClassDecoratorMDecorated]>
+
+Notice that ``sm`` was defined in ``D``, the undecorated class: therefore
+it is not decorated:
+
+>>> E.sm() # correctly, does not work
+Traceback (most recent call last):
+ ...
+TypeError: unbound method sm() must be called with E instance as first argument (got nothing instead)
+
+The error message says clearly that ``sm`` is an unbound method and not
+a static method.
+
+Defining method decorators
+-----------------------------------------------------------------------
+
+The ``decorators module`` contains two predefinite method decorators,
+``staticmethod`` and ``classmethod``, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The ``decorators.MethodDecorator`` class which is here
+exactly for that purpose.
+
+Custom decorators are expected to be implemented by subclassing
+``MethodDecorator`` and by overriding its ``get`` method. The
+``get`` method automagically induces a ``__get__`` method, turning the
+class in a descriptor. The machinery is needed since ``__get__`` cannot
+be made cooperative using the standard ``super`` mechanism because
+there would be a confusion between ``super.__get__`` and the decorator
+``__get__``. This is a bit tricky, but the causal programmer is not
+expected to write custom decorators, and actually I don't want to make
+the access to decorators *too* easy, since they are potentially dangerous.
+
+In order to give a simple example, let me show the implementation
+of a ``chattymethod`` that prints a message when it is called:
+
+ ::
+
+ #<customdec.py>
+
+ from decorators import *
+
+ class chattymethod(MethodDecorator):
+ logfile=sys.stdout # default
+ def get(self,obj,cls=None): # same signature as __get__
+ self.logfile.write('calling %s from %s\n' % (self,obj or cls))
+ return super(chattymethod,self).get(obj,cls)
+
+ #</customdec.py>
+
+Notice the usage of the ``super().get`` trick. This guarantees that
+``chattymethod`` will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance). The point will
+be fully discussed in the section on composing decorators.
+
+Here is an example of usage:
+
+>>> import customdec # adds chattymethod to the list of known decorators
+>>> customdec.enhance_classes() # automagically enhances classes when needed
+>>> class C:
+... " [Decorated] "
+... def f(self):
+... """
+... [ chattymethod ]
+... """
+>>> c=C()
+>>> c.f()
+calling <chattymethod:f> from <C instance>
+
+By the way, this shows that one can safely add whitespaces (including
+newlines) to the magic docstring: they are simply ignored.
+
+One can check that the syntax ``C.f(c)`` works too:
+
+>>> C.f(c)
+calling <chattymethod:f> from <class C[Decorated]>
+
+A tricky point of the decorators mechanism is the issue of parameter passing.
+In comp.lang.python there was the proposal of allowing explicit parameter
+passing to decorators, with a syntax of kind
+
+ ::
+
+ def f(self)[chattymethod(logfile=file('file1.log','w'))]
+
+In my view, there are too many parenthesis in this syntax, and it may
+become rapidly unreadable. Moreover, it complicates the implementation
+without any real benefit, so the decorators module does not allow
+this kind of parameter passings. There are however viable
+workarounds, so you should not miss the syntax.
+
+A simple minded solution is to change the defaults by hand:
+
+>>> from customdec import chattymethod,decorated
+>>> chattymethod.logfile=file('file.log','w')
+>>> def g(self):
+... "[chattymethod]"
+>>> C.g=decorated(g)
+>>> c.g() # will print a message on file.log
+
+This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. Therefore
+
+>>> c.f()
+
+will print a message to ``file.log`` too, and not to standard output.
+Here is the confirmation:
+
+>>> chattymethod.logfile.close()
+>>> print file('file.log').read().rstrip()
+calling <chattymethod:g> from <C instance>
+calling <chattymethod:f> from <C instance>
+
+A much better solution is to pass
+parameters to method decorators as function attributes: then the function
+attributes can be converted to attributes of the decorator
+in the ``__init__`` method. Here is an example:
+
+ ::
+
+ #<customdec.py>
+
+ class chattymethod2(chattymethod):
+ logfile=sys.stdout # default
+ def __init__(self,objfunc):
+ super(chattymethod2,self).__init__(objfunc)
+ logfile=getattr(self.__func__,'logfile',None)
+ if logfile: self.logfile=logfile
+
+ #</customdec.py>
+
+Notice that the ``__init__`` method has the signature
+``__init__(self,objfunc)``, where the ``objfunc`` object is a
+decorator object or the function to be converted in the decorator
+object, and that it is cooperative.
+This is the suggested way of overriding ``__init__`` (see also
+``help(decorators.MethodDecorator.__init__)``).
+
+Here is the testing:
+
+ ::
+
+ #<chatty2.py>
+
+ import customdec; customdec.enhance_classes("[Decorated]")
+
+ # sets the log files
+ log1=file('file1.log','w')
+ log2=file('file2.log','w')
+
+ class C:
+ def f(self):
+ "[chattymethod2]"
+ f.logfile=log1 # function attribute
+ def g(self):
+ "[chattymethod2]"
+ g.logfile=log2 # function attribute
+
+ assert C.__dict__['f'].logfile is log1 # check the conversion
+ assert C.__dict__['g'].logfile is log2 # function attr. -> decorator attr.
+
+ c=C() # C instantiation
+
+ c.f() # print a message in file1.log
+ c.g() # print a message in file2.log
+
+ log1.close(); log2.close() # finally
+
+ #</chatty2.py>
+
+Let me check the contents of the log files:
+
+>>> import chatty2
+>>> print file('file1.log').read().rstrip()
+calling <chattymethod2:f> from <C instance>
+>>> print file('file2.log').read().rstrip()
+calling <chattymethod2:g> from <C instance>
+
+``chattymethod`` is the poor man version of ``tracedmethod``, a
+sophisticated decorator for tracing methods.
+Here is the code, given for pedagogical purposes; the lazy reader can
+skip it and go directly to the usage section.
+
+ ::
+
+ #<customdec.py>
+
+ class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+
+ def __init__(self,objfunc):
+ super(tracedmethod,self).__init__(objfunc)
+ self.funcname=self.__func__.__name__
+ output=getattr(self.__func__,'output',None)
+ if output: self.output=output # func.attr. -> dec.attr.
+
+ def get(self,obj,cls):
+ clsname=self.__klass__.__name__ # definition clas
+ def tracedmeth(obj,*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,clsname,self.funcname))
+ self.output.write("%s%s ...\n" % (obj or '',str(args)+str(kw)))
+ res=super(tracedmethod,self).get(obj,cls)(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,clsname,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return tracedmeth.__get__(obj,cls) # method wrapper
+
+ #</customdec.py>
+
+``tracedmethod.get`` returns a method wrapper object, so it is
+possible to use ``im_func`` to retrieve the internal function
+``tracedmeth``:
+
+>>> class C:
+... "[Decorated]"
+... def f(self):
+... "[tracedmethod]"
+>>> c=C(); c.f()
+Calling 'C.f' with arguments <C instance>(){} ...
+'C.f' called with result: None
+>>> c.f.im_func.__name__
+'tracedmeth'
+
+As soon as the ``tracedmethod`` module is loaded, the ``tracedmethod`` class
+is added to the list of know decorators, so one should use the
+"[tracedmethod]" docstring and not something like
+"[customdec.tracedmethod]".
+
+Here is a less trivial example of usage, writing in a log file the
+internal working of a recursive function:
+
+ ::
+
+ #<example9.py>
+
+ import customdec; customdec.enhance_classes()
+
+ logfile=file('file3.log','w')
+
+ class C(object):
+ "[Decorated]"
+ def fact(self,n):
+ "[tracedmethod] The good old factorial."
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+ C().fact(2) # write a message to logfile
+
+ logfile.close()
+
+ #</example9.py>
+
+Here is the content of the ``file3.log``:
+
+>>> import example9
+>>> print file('file3.log').read().rstrip()
+Calling 'C.fact' with arguments <C instance>(2,){} ...
+ Calling 'C.fact' with arguments <C instance>(1,){} ...
+ Calling 'C.fact' with arguments <C instance>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+
+Composing decorators
+---------------------------------------------------------------------
+
+Decorators can be composed by using magic docstrings with comma-separated
+decorator names. For instance, you can trace a classmethod as in this example:
+
+ ::
+
+ #<example6.py>
+
+ "How to trace a class method"
+
+ import customdec; customdec.enhance_classes()
+
+ class C(object):
+ "[Decorated]"
+ def fact(cls,n): # a traced classmethod
+ "[classmethod,tracedmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+ #</example6.py>
+
+Here is the testing:
+
+>>> from example6 import C
+>>> C.fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+You may easily check that calling ``.fact`` from the instance will work too.
+
+Under the hood the syntax
+
+ ::
+
+ [classmethod,tracedmethod]
+
+generates a ``classmethodtracedmethod`` class obtained via
+multiple inheritance:
+
+>>> C.__dict__['fact'].__class__
+<class 'noconflict.classmethodtracedmethod'>
+
+Notice that the order does matter and using the docstring
+"[tracedmethod,classmethod]" will not work:
+
+>>> class D:
+... "[Decorated]"
+... def f(cls):
+... "[tracedmethod,classmethod]"
+>>> D.f()
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with D instance as first argument (got nothing instead)
+
+The problem here is that ``tracedmethod.get`` returns a method-wrapper object
+which expects a D instance as first argument whereas it gets ``None`` since
+it is called from the class. On the other hand,
+
+>>> D().f()
+Calling 'D.f' with arguments <D instance>(){} ...
+'D.f' called with result: None
+
+will work. When ``classmethod`` precedes ``tracedmethod``, then
+``classmethod`` passes to ``tracedmeth`` a non-empty first argument,
+i.e. the calling class, even when called from the instance:
+
+>>> C().fact(2)
+Calling 'C.fact' with arguments <class C[Decorated]>(2,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(1,){} ...
+ Calling 'C.fact' with arguments <class C[Decorated]>(0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+If we try to trace a staticmethod, we will get a different error with
+the order "tracedmethod, staticmethod":
+
+>>> class F(object):
+... "[Decorated]"
+... def fact(n):
+... "[tracedmethod,staticmethod]"
+... if n==0: return 1
+... else: return n*F.fact(n-1)
+>>> F.fact(2)
+Traceback (most recent call last):
+ ...
+TypeError: unbound method tracedmeth() must be called with F instance as first argument (got int instance instead)
+
+The message is self-explanatory.
+
+On the other hand, composing the decorators in the other order
+"[tracedmethod,staticmethod]" will work just fine.
+
+Defining class decorators
+-----------------------------------------------------------------------
+
+PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a
+class decorator at work, the metaclass ``Decorated``, giving
+to its instances the ability to interpret magic docstrings,
+and converting functions in method decorators.
+
+To define a custom class decorator is easy: one defines a custom metaclass
+as usual, with the only difference from deriving by ``ClassDecorator`` instead
+of deriving from ``type``. To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:
+
+ ::
+
+ #<customdec.py>
+
+ class Logged(ClassDecorator):
+ output=sys.stdout
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print >> cls.output,"%s created" % cls
+
+ #</customdec.py>
+
+``Logged`` is derived by the metaclass ``ClassDecorator``,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+metaclass ``MetaDecorator``). Logging capabilities can be added to a class
+by simply using the magic docstring syntax:
+
+ ::
+
+ #<logged.py>
+
+ import customdec; customdec.enhance_classes()
+
+ class D(object): # will print a message at D creation
+ "[Logged]"
+
+ #</logged.py>
+
+>>> import logged
+<class D[Logged]> created
+
+Notice that the printing representation of ``D`` involves the name
+of ``D`` preceded by the name of its metaclass, which in this case
+is ``Logged``
+
+Each time an instance of ``Logged`` is created, a similar message is printed:
+
+>>> class E(logged.D):
+... pass
+<class E[Logged]> created
+
+Notice that ``E`` does not have any magic docstring
+
+>>> E.__doc__ # no docstring
+
+but still it inherits its magic from ``D``.
+
+Another simple example of class decorator is the following metaclass
+which modifies the docstrings of the methods of its instances,
+by magically inducing tracing capabilities on them:
+
+ ::
+
+ #<customdec.py>
+
+ from types import FunctionType
+
+ class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType): # modifies the docstring
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+
+ #</customdec.py>
+
+Here is an example of usage:
+
+>>> class C(object):
+... """[Decorated,Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... to method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> type(C)
+<class 'noconflict.DecoratedTraced'>
+>>> c=C()
+>>> c.f1()
+Calling 'C.f1' with arguments <C instance>(){} ...
+'C.f1' called with result: None
+>>> c.f2()
+Calling 'C.f2' with arguments <C instance>(){} ...
+'C.f2' called with result: None
+
+By default, the decorators module only decorates classes with a magic
+docstring (and they subclasses, even without magic docstrings).
+If all the classes of your module have the same magic docstring,
+it makes sense to decorate them all
+with a single command. It is enough to use ``decorators.enhance_classes()``
+with a magic docstring corresponding to a class decorator as argument,
+as in this example:
+
+ ::
+
+ #<example.py>
+
+ from example2 import identity,name
+ import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+ class C1: # automagically converted to a decorated class
+ identity=identity
+
+ class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+ c1=C1() # C1 instance
+ c2=C2() # C2 instance
+
+ #</example.py>
+
+Notice that class ``C2`` has already a magic docstring. This means that
+``C2`` has to be enhanced both from ``Logged`` and from ``Decorated``.
+This is done by automagically creating a ``DecoratedLogged`` class
+decorator:
+
+>>> from example import C1,C2,c1,c2
+<class C2[DecoratedLogged]> created
+
+The second line is printed because of the logging capabilities of ``C2``.
+Moreover, since ``C2`` is decorated too, the following will work:
+
+>>> assert C2.name() == 'C2'
+>>> assert c2.name() == 'C2'
+
+Idem for ``C1``:
+
+>>> assert C1.identity(1) == 1
+>>> assert c1.identity(1) == 1
+
+You may check that the magic works both for new style classes (decorating
+module ``object`` class) and old style classes (by setting the module level
+``__metaclass`` attribute and by implicitly converting the classes
+to new style classes).
+
+This magical approach is the easiest way to go if you want to trace
+inheritance hierarchies. For instance, here is how to trace cooperative
+methods in complicate (which is useful for debugging):
+
+ ::
+
+ #<tracing.py>
+
+ import customdec; customdec.enhance_classes("[Decorated]")
+
+ class B(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(B,self).__init__()
+
+ class D(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(D,self).__init__()
+
+ class E(B,D):
+ def __init__(self):
+ "[tracedmethod]"
+ super(E,self).__init__()
+
+ #</tracing.py>
+
+>>> from tracing import E
+>>> e=E()
+Calling 'E.__init__' with arguments <E instance>(){} ...
+ Calling 'B.__init__' with arguments <E instance>(){} ...
+ Calling 'D.__init__' with arguments <E instance>(){} ...
+ 'D.__init__' called with result: None
+ 'B.__init__' called with result: None
+'E.__init__' called with result: None
+
+Advanced usage
+---------------------------------------------------------------------------
+
+Whereas the average programmer is expected to use the
+``decorators.decorated`` function only, the module provides access to
+its underlining implementation, which may be useful to the advanced
+programmer.
+
+
+The module provides an utility functions to retrieve the list of
+recognized decorators: ``decorators.get(docstring)``, where ``docstring``
+is a magic docstring, i.e. a bracketed comma-separated list
+of decorator names. For instance ``decorators.get('[MethodDecorator]')``
+gives the list of all subclasses of ``MethodDecorator``, i.e. all method
+decorators, whereas ``decorators.get('[ClassDecorator]')``
+gives the list of the known class decorators. It is even possible
+to use the comma notation:
+
+>>> decorators.get("[classmethod,tracedmethod]")
+[<class 'noconflict.classmethodtracedmethod'>]
+
+For instance, it is possible to decorate functions by hand,
+without using magic docstring. Here is an example:
+
+>>> do_nothing=decorators.staticmethod(lambda:None)
+>>> print do_nothing # ``do_nothing`` is a static method
+<staticmethod:<lambda>>
+
+One can even compose decorators by hand:
+
+>>> class B: pass
+...
+>>> B.chattystatic=customdec.chattymethod2(do_nothing)
+>>> B.chattystatic()
+calling <chattymethod2staticmethod:<lambda>> from <class B[ClassDecorator]>
+
+In other words
+
+ ``decorator1(decorator2(obj))``
+
+automagically creates a composed class ``decorator1decorator2`` in the
+``noconflict`` module (or recycle it, if ``decorator1decorator2`` has
+been already created) and it is equivalent to
+
+ ``decorator1decorator2(obj)``
+
+Here is the check:
+
+>>> decorators.get("[chattymethod2staticmethod]")
+[<class 'noconflict.chattymethod2staticmethod'>]
+
+Here is another example:
+
+>>> from customdec import tracedmethod
+>>> class C(object):
+... def fact(self,n):
+... if n==0: return 1
+... else: return n*self.fact(n-1)
+... fact=tracedmethod(fact)
+>>> c=C()
+>>> c.fact(2)
+Calling '?.fact' with arguments <C instance>(2,){} ...
+ Calling '?.fact' with arguments <C instance>(1,){} ...
+ Calling '?.fact' with arguments <C instance>(0,){} ...
+ '?.fact' called with result: 1
+ '?.fact' called with result: 1
+'?.fact' called with result: 2
+2
+
+In this second syntax ``fact`` does not know where it
+is defined, unless the containing class is explicitly set:
+
+>>> C.__dict__['fact'].__klass__=C
+
+Now the code will work as with the docstring syntax.
+
+
+
+
+What happens if you try to decorate something which is already
+decorated? You get ``None``:
+
+>>> def f(x): "[tracedmethod]"
+...
+>>> tm=decorators.decorated(f) # creates a tracedmethod from f
+>>> print decorators.decorated(tm) # trying to decorate a tracedmethod
+None
+
+You can compose decorators in a trivial way:
+
+>>> cmtm=decorators.classmethod(tm)
+>>> print cmtm
+<classmethodtracedmethod:f>
+
+
+whereas you can compose classes:
+
+>>> class C: "[Decorated]"
+...
+>>> print customdec.Traced(C)
+<class C[TracedDecorated]>
+
+
+
+
+>>> class D: pass
+...
+>>> print customdec.Decorated(customdec.Traced(D))
+<class D[DecoratedTraced]>
+
+
+This is the hierarchy:
+
+>>> for C in type(cmtm).mro(): print C
+...
+<class 'noconflict.classmethodtracedmethod'>
+<class 'decorators.classmethod'>
+<class 'customdec.tracedmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.Decorator'>
+<type 'object'>
+
+
+
+One can also decorate classes by hand, by using class decorators.
+``decorators.ClassDecorator`` which converts a regular (both old
+style or new style) class in a class with the ability to recognize
+magic docstrings. Actually, the ``decorators.enhance_classes()``
+trick works by decorating the ``object`` class with
+``decorators.ClassDecorator`` and by setting the custom metaclass to
+``decorators.ClassDecorator`` and it is equivalent to write
+
+ ::
+
+ import decorators
+ object=decorators.ClassDecorator(object) # decorates all new style classes
+ __metaclass__= decorators.ClassDecorator # decorates all old style classes
+
+on top of your module.
+If you want the magic to work only for new style classes only, you may
+forget the
+second line; if you want the magic to work for old style classes only, you
+may forget the first line.
+
+If the module contains a magic docstring which is an explicit class decorator,
+such as ``decorators.Decorated``, then ``decorators.decorated`` look at the
+magic docstring and executes something like
+
+ ::
+
+ import decorators
+ object=decorators.Decorated(object) # decorates all new style classes
+ __metaclass__= decorators.Decorated # decorates all old style classes
+
+It is possible to go even deeper in black magic, and to decorate all
+the new style classes in *all* modules, by decorating ``__builtin__.object``:
+
+ ::
+
+ import decorators,__builtin__
+ __builtin.object=decorators.decorated(object)
+
+Still, redefining ``__builtin__object`` is not recommended since it
+may induce metaclass conflicts in other modules using metaclasses.
+It will work only if you import modules not using metaclasses, or
+modules using metaclasses safely, i.e. modules taking care of
+possible conflicts by using the ``makecls`` function or an equivalent one.
+
+The implementation
+-----------------------------------------------------------------------
+
+This part can be safely skipped, unless you are a *really* curious and
+you want to know how the implementation works.
+
+The module is rather short (~150 lines) but far from being trivial,
+since it is based on extensive usage of metaclass wizardry.
+
+The main class-metaclass hierarchy is represented in figure 1, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed green lines whereas inheritance is denoted by continuous
+blue lines.
+
+.. figure:: decorators.png
+
+Notice that ``MetaDecorator`` (inherited via ``Decorator``) is the
+metaclass of all decorators. Class decorators are already metaclasses,
+so ``MetaDecorator`` is actually a meta-metaclass with respect to
+instances of decorated classes, whereas it is a simple metaclass with
+respect to instances of decorated methods. For this reason in the
+presenced of class decorators the unusual relation
+
+assert object.__metaclass__ != object.__class__
+
+holds. More specifically
+
+>>> object.__class__
+<class 'decorators.ClassDecorator'>
+
+but
+
+>>> object.__metaclass__
+<class 'decorators.MetaDecorator'>
+
+since ``object`` the ``__metaclass__``
+attribute is inherited from ``Decorator``.
+
+The implementation is relatively smart. Consider for instance the case of
+the logged example. In that example class ``D`` was a subclass of a tricked
+``object`` class, enhanced by a metaclass ``ClassDecorator``. Moreover ``D``
+had a ``Logged`` docstring. Therefore it should have been an instance of
+``_ClassDecoratorLogged`` metaclass. But logged was
+a subclass of ``ClassDecorator``, therefore it already had all the features
+of ``ClassDecorator`` and it would have been redundant to create
+``_ClassDecoratorLogged``, when``Logged`` would have been enough.
+So ``Logged``
+was used and ``_ClassDecoratorLogged`` was never created. All the magic is in
+the ``noconflict`` module discussed in my cookbook_ recipe.
+
+The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. They does not probably matter unless one has to decorate
+thousands or millions of functions and classes.
+
+Things to do: adding more error checking.
+
+Finally, a word about bugs. The ``decorators`` module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~100 tests automatically extracted from the documentation you are reading),
+I cannot guarantee that it is correct.
+If somebody finds a bug or an unexpected
+behavior, please let me know and I will try to fix it.
+
+.. References:
+
+.. _cooperative methods:
+ http://www.python.org/2.3/descrintro.html
+.. _new style classes and old style classes:
+ http://www.python.org/2.3/descrintro.html
+.. _descriptors: http://users.rcn.com/python/download/Descriptor.htm
+.. _cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
+.. _metaclasses: http://www-106.ibm.com/developerworks/library/l-pymeta2.html
diff --git a/pypers/pep318/working/doct.py b/pypers/pep318/working/doct.py
new file mode 100755
index 0000000..7e32291
--- /dev/null
+++ b/pypers/pep318/working/doct.py
@@ -0,0 +1,60 @@
+# doct.py
+
+"""Search text files for examples to run via doctest. Example of usage:
+
+$ doct doc1.txt -v doc2.txt doc3.txt
+
+This runs the tests in doc1.txt and doc3.txt in silent mode and the
+tests in doc2.txt in verbose mode."""
+
+import os,sys,doctest,time,textwrap,re
+
+sys.path[0]=os.getcwd() # explicitly puts the current directory in the path
+
+DOTNAME=r'\b[a-zA-Z_][\w\.]*', # identifier with or without dots
+SCRIPT=re.compile(r'(?s)#<(%s)>(.*?)#</\1>' % DOTNAME)
+# regular expressions to identify code blocks of the form
+#<scriptname.py>
+#...
+#</scriptname.py>
+
+TESTER=doctest.Tester(globs={},verbose=0),doctest.Tester(globs={},verbose=1)
+# regular and verbose tester instances
+
+def extractscript(txt):
+ for MO in SCRIPT.finditer(txt):
+ yield MO.group(1),textwrap.dedent(MO.group(2))
+
+def rundoct(fname,tester):
+ txt=file(fname,'U').read()
+ scriptnames=[]; scriptdict={}
+ for scriptname,script in extractscript(txt): # read scripts
+ if scriptname not in scriptnames:
+ scriptdict[scriptname]=script
+ scriptnames.append(scriptname)
+ else:
+ scriptdict[scriptname]+=script
+ for scriptname in scriptnames: # save scripts
+ code='# '+scriptname+scriptdict[scriptname]
+ print >> file(scriptname,'w'),code
+ t0=time.clock()
+ failed,total = tester.runstring(txt,fname)
+ if failed:
+ return ''
+ else:
+ return "%s: %s tests passed in %s seconds\n" % (
+ fname,total,time.clock()-t0)
+
+def main(args=sys.argv[1:]):
+ args=iter(args)
+ for a in args:
+ if a=='-v': # verbose option
+ v=1; a=args.next()
+ else: # default, non-verbose
+ v=0
+ print rundoct(fname=a,tester=TESTER[v]),
+
+if __name__=='__main__':
+ if sys.argv[1:]: main() # parse arguments
+ else: print __doc__ # no arguments given
+
diff --git a/pypers/pep318/working/example.py b/pypers/pep318/working/example.py
new file mode 100755
index 0000000..aedb6f3
--- /dev/null
+++ b/pypers/pep318/working/example.py
@@ -0,0 +1,16 @@
+# example.py
+
+from example2 import identity,name
+import inspect, decorators; decorators.enhance_classes("[Decorated]")
+
+class C1: # automagically converted to a decorated class
+ identity=identity
+
+class C2: # automagically converted to a DecoratedLogged class
+ "[Logged]"
+ name=name
+
+c1=C1() # C1 instance
+c2=C2() # C2 instance
+
+
diff --git a/pypers/pep318/working/example1.py b/pypers/pep318/working/example1.py
new file mode 100755
index 0000000..d9226d7
--- /dev/null
+++ b/pypers/pep318/working/example1.py
@@ -0,0 +1,29 @@
+# example1.py
+
+import decorators
+
+def do_nothing(self):
+ "No magic docstring here"
+dec_do_nothing=decorators.decorated(do_nothing)
+
+def identity(x):
+ "[staticmethod]"
+ return x
+dec_identity=decorators.decorated(identity)
+
+def name(cls):
+ "[classmethod]"
+ return cls.__name__
+dec_name=decorators.decorated(name)
+
+class OldStyle:
+ do_nothing=dec_do_nothing
+ identity=dec_identity
+
+class NewStyle(object):
+ name=dec_name
+
+o=OldStyle() # creates an old style instance
+n=NewStyle() # creates a new style instance
+
+
diff --git a/pypers/pep318/working/example2.py b/pypers/pep318/working/example2.py
new file mode 100755
index 0000000..6657edd
--- /dev/null
+++ b/pypers/pep318/working/example2.py
@@ -0,0 +1,36 @@
+# example2.py
+
+from decorators import decorated
+from example1 import do_nothing,identity,name
+
+class B(object):
+ "This is a regular class"
+
+B=decorated(B) # does nothing
+
+class C(B):
+ "[Decorated]"
+ do_nothing=do_nothing
+ identity=identity
+ name=name
+
+C=decorated(C) # regenerates the class converting methods in decorators
+c=C()
+
+
+
+class D: # old style
+ "[Decorated]"
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+D=decorated(D)
+
+d=D()
+
+# test
+assert d.identity(1) == 1
+assert D.identity(1) == 1
+
+
diff --git a/pypers/pep318/working/example4.py b/pypers/pep318/working/example4.py
new file mode 100755
index 0000000..25338d1
--- /dev/null
+++ b/pypers/pep318/working/example4.py
@@ -0,0 +1,22 @@
+# example4.py
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ "[Decorated]" # magic docstring here
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+class D(object):
+ "Undecorated" # no magic docstring here
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+c=C(); d=D()
+
+
diff --git a/pypers/pep318/working/example5.py b/pypers/pep318/working/example5.py
new file mode 100755
index 0000000..81ebce9
--- /dev/null
+++ b/pypers/pep318/working/example5.py
@@ -0,0 +1,20 @@
+# example5.py
+
+import decorators; decorators.enhance_classes()
+
+class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def name(cls):
+ "[classmethod]"
+ return cls.__name__
+
+
+assert C.identity(1) == 1
+assert C.Inner.name() == 'Inner'
+
+
diff --git a/pypers/pep318/working/example6.py b/pypers/pep318/working/example6.py
new file mode 100755
index 0000000..145c06a
--- /dev/null
+++ b/pypers/pep318/working/example6.py
@@ -0,0 +1,14 @@
+# example6.py
+
+"How to trace a class method"
+
+import customdec; customdec.enhance_classes()
+
+class C(object):
+ "[Decorated]"
+ def fact(cls,n): # a traced classmethod
+ "[classmethod,tracedmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+
diff --git a/pypers/pep318/working/example9.py b/pypers/pep318/working/example9.py
new file mode 100755
index 0000000..8a6fd33
--- /dev/null
+++ b/pypers/pep318/working/example9.py
@@ -0,0 +1,19 @@
+# example9.py
+
+import customdec; customdec.enhance_classes()
+
+logfile=file('file3.log','w')
+
+class C(object):
+ "[Decorated]"
+ def fact(self,n):
+ "[tracedmethod] The good old factorial."
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact.output=logfile
+
+C().fact(2) # write a message to logfile
+
+logfile.close()
+
+
diff --git a/pypers/pep318/working/logged.py b/pypers/pep318/working/logged.py
new file mode 100755
index 0000000..04be3fd
--- /dev/null
+++ b/pypers/pep318/working/logged.py
@@ -0,0 +1,8 @@
+# logged.py
+
+import customdec; customdec.enhance_classes()
+
+class D(object): # will print a message at D creation
+ "[Logged]"
+
+
diff --git a/pypers/pep318/working/noconflict.py b/pypers/pep318/working/noconflict.py
new file mode 100755
index 0000000..e14a1a7
--- /dev/null
+++ b/pypers/pep318/working/noconflict.py
@@ -0,0 +1,64 @@
+"""
+Avoids metaclass conflicts by providing an additional built-in, makecls,
+to be used as __metaclass__=makecls(*metaclasses). Returns a class factory.
+"""
+
+metadic={}
+
+anyTrue=sum
+
+def remove_redundant(bases):
+ ls=list(bases); nonredundant={}
+ for c in bases:
+ if anyTrue([issubclass(C,c) and c is not C for C in ls],
+ c in nonredundant):
+ ls.remove(c) # if c is less specific or duplicated
+ else:
+ nonredundant[c]=True
+ return tuple(ls)
+
+def _generatemetaclass(bases,metas,priority):
+ metabs=tuple(map(type,bases))
+ metabases=remove_redundant((metabs+metas, metas+metabs)[priority])
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname=''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases metaclasses."""
+
+ priority=options.get('priority',False) # default, no priority
+ def clsfactory(n,b,d):
+ # workaround to allow redefinition of meta-meta.__call__
+ meta=_generatemetaclass(b,metas,priority)
+ return type.__call__(meta,n,b,d) # calls __new__ and __init__
+ return clsfactory
+
+#class Noconflict(type):
+# """Meta-metaclass tweaking the metaclass calling in such a way to
+# avoid conflicts"""
+# def __call__(mcl,n,b,d):
+# return makecls(mcl)(n,b,d)
+
+if __name__=='__main__': # test
+ import __builtin__
+ __builtin__.type=Noconflict('Type',(type,),{})
+ class M1(type): pass
+ class M2(type): pass
+ class C1: __metaclass__=M1
+ class C2: __metaclass__=M2
+ class C(C1,C2): pass
+ #__metaclass__=makecls()
+# assert type(C),__name__=='_M1M2'
diff --git a/pypers/pep318/working/pep318.html b/pypers/pep318/working/pep318.html
new file mode 100755
index 0000000..a3ce15b
--- /dev/null
+++ b/pypers/pep318/working/pep318.html
@@ -0,0 +1,1046 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.9: http://docutils.sourceforge.net/" />
+<title>Implementing PEP 318 (decorators)</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="implementing-pep-318-decorators">
+<h1 class="title">Implementing PEP 318 (decorators)</h1>
+<div class="contents topic" id="contents">
+<p class="topic-title"><a name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#basics" id="id1" name="id1">Basics</a></li>
+<li><a class="reference" href="#simple-usage-of-decorators" id="id2" name="id2">Simple usage of decorators</a></li>
+<li><a class="reference" href="#adding-a-bit-of-magic" id="id3" name="id3">Adding a bit of magic</a></li>
+<li><a class="reference" href="#adding-more-magic" id="id4" name="id4">Adding more magic</a></li>
+<li><a class="reference" href="#defining-method-decorators" id="id5" name="id5">Defining method decorators</a></li>
+<li><a class="reference" href="#tracing-methods" id="id6" name="id6">Tracing methods</a></li>
+<li><a class="reference" href="#composition-of-decorators" id="id7" name="id7">Composition of decorators</a></li>
+<li><a class="reference" href="#class-decorators" id="id8" name="id8">Class decorators</a></li>
+<li><a class="reference" href="#module-decorators" id="id9" name="id9">Module decorators</a></li>
+<li><a class="reference" href="#introspection-features" id="id10" name="id10">Introspection features</a></li>
+<li><a class="reference" href="#the-implementation" id="id11" name="id11">The implementation</a></li>
+</ul>
+</div>
+<p>Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of PEP 318 in pure Python.</p>
+<p>Here is the rationale:</p>
+<ul class="simple">
+<li>some kind of decorator syntax is scheduled to go in Python 2.4,
+therefore it is interesting to play with the concept;</li>
+<li>it is nice to play with decorators now, without having to
+wait for one year or so;</li>
+<li>it is much easier to experiment with the pure Python implementation;</li>
+<li>the implementation can be seen as an exercise on modern Python
+programming and may be valuable to people wanting to study the most
+advanced new constructs in Python 2.2 (descriptors, metaclasses,
+cooperative methods, etc.)</li>
+</ul>
+<div class="section" id="basics">
+<h1><a class="toc-backref" href="#id1" name="basics">Basics</a></h1>
+<p>As people in this list most probably know, PEP 318 has the goal
+of providing a nice syntactic sugar for expressions like</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ return x
+identity=staticmethod(identity)
+</pre>
+</blockquote>
+<p>or</p>
+<blockquote>
+<pre class="literal-block">
+def nameof(cls):
+ return cls.__name__
+nameof=classmethod(nameof)
+</pre>
+</blockquote>
+<p>which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x)[staticmethod]:
+ return x
+
+def nameof(cls)[classmethod]:
+ return cls.__name__
+</pre>
+</blockquote>
+<p>involves changing the grammar and modifying the interpreter at the
+C level. However, it is possible to have the same effect without
+changing the Python grammar. The idea is to use magic docstrings
+like this:</p>
+<blockquote>
+<pre class="literal-block">
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+def nameof(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+</pre>
+</blockquote>
+<p>The implementation is able to recognize such docstrings
+and to automagically convert the functions in (method) decorators.</p>
+<p>Method decorators are nothing else than a sophisticated kind of wrappers.
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt> are two examples of already existing
+decorators (actually my implementation rewrites them, but let me pass on
+this detail). Technically speaking, method decorators are classes
+taking a single function as input and producing a descriptor object
+as output (properties are not decorators according to this definition,
+since they take four functions as input, <tt class="literal"><span class="pre">get,</span> <span class="pre">set,</span> <span class="pre">del_</span></tt> and <tt class="literal"><span class="pre">doc</span></tt>).
+<a class="reference" href="http://users.rcn.com/python/download/Descriptor.htm">Descriptors</a> are objects with a <tt class="literal"><span class="pre">__get__</span></tt> method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip this point ;). A knowledge
+of descriptors is not needed in order to use the <tt class="literal"><span class="pre">decorator</span></tt> module;
+however it is welcomed for advanced users wanting to implement
+custom decorators.</p>
+</div>
+<div class="section" id="simple-usage-of-decorators">
+<h1><a class="toc-backref" href="#id2" name="simple-usage-of-decorators">Simple usage of decorators</a></h1>
+<p>Before talking about the implementation details, I will show
+how the <tt class="literal"><span class="pre">decorators</span></tt> module works in practice. The simplest and
+safest usage is by means of the functions <tt class="literal"><span class="pre">decorators.decorate</span></tt>
+and <tt class="literal"><span class="pre">decorators.decorated</span></tt>:</p>
+<ol class="arabic simple">
+<li><tt class="literal"><span class="pre">decorators.decorated(obj)</span></tt> takes an object and checks its docstring;
+if a magic docstring is found, it returns a decorated version of the
+object, otherwise it returns <tt class="literal"><span class="pre">None</span></tt>;</li>
+<li><tt class="literal"><span class="pre">decorators.decorate(obj)</span></tt> takes a dictionary or an object with a
+<tt class="literal"><span class="pre">.__dict__</span></tt> attribute and returns <tt class="literal"><span class="pre">None</span></tt>. It works by
+invoking <tt class="literal"><span class="pre">decorators.decorated</span></tt> on the elements of the dictionary
+and by modifying them if needed.</li>
+</ol>
+<p>Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example1.py&gt;
+
+&quot;How to use ``decorators.decorate`` and ``decorators.decorated``&quot;
+
+import decorators
+
+def do_nothing(self):
+ &quot;No magic docstring here&quot;
+
+def identity(x):
+ &quot;[staticmethod]&quot; # magic docstring here
+ return x
+
+def nameof(cls):
+ &quot;[classmethod]&quot; # magic docstring here too
+ return cls.__name__
+
+dic={'nameof': nameof, 'do_nothing': do_nothing}
+decorators.decorate(dic) # converts nameof -&gt; classmethod
+
+C=type('C',(),dic) # creates the class with the modified dictionary
+C.identity=decorators.decorated(identity) # converts identity -&gt; staticmethod
+c=C() # creates the instance
+
+#&lt;/example1.py&gt;
+</pre>
+</blockquote>
+<p>and here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example1 import C,c
+&gt;&gt;&gt; assert c.do_nothing() is None
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert C.nameof() == 'C'
+&gt;&gt;&gt; assert c.identity(1) == 1
+&gt;&gt;&gt; assert c.nameof() == 'C'
+</pre>
+<p>One can even pass the <tt class="literal"><span class="pre">globals()</span></tt> dictionary since objects without
+a magic docstring are simply ignored. Therefore the previous example
+can be rewritten as</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example2.py&gt;
+
+import decorators
+
+def do_nothing(self):
+ &quot;No magic docstring here&quot;
+
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+def nameof(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+decorators.decorate(globals()) # decorates the functions
+
+class C(object):
+ identity=identity
+ nameof=nameof
+ do_nothing=do_nothing
+
+c=C()
+
+#&lt;/example2.py&gt;
+</pre>
+</blockquote>
+<p>Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example2 import c,C
+&gt;&gt;&gt; assert c.do_nothing() is None
+&gt;&gt;&gt; assert C.identity(1) == 1
+&gt;&gt;&gt; assert C.nameof() == 'C'
+&gt;&gt;&gt; assert c.identity(1) == 1
+&gt;&gt;&gt; assert c.nameof() == 'C'
+</pre>
+<p>Notice that the call to <tt class="literal"><span class="pre">decorators.decorate(globals())</span></tt> must be done
+<em>after</em> the function definitions, otherwise the functions would
+not converted, since they were not in the global dictionary at the
+time of the call. Moreover, one should not try to pass the <tt class="literal"><span class="pre">locals()</span></tt>
+dictionary, since it will not work when <tt class="literal"><span class="pre">locals()</span> <span class="pre">!=</span> <span class="pre">globals()</span></tt>.</p>
+<p>Alternatively, one can just decorate the class:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example3.py&gt;
+
+import decorators
+
+def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+def nameof(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+class C:
+ identity=identity
+ nameof=nameof
+
+decorators.decorate(C)
+
+c=C()
+
+# testing
+assert C.identity(1) == 1
+assert C.nameof() == 'C'
+assert c.identity(1) == 1
+assert c.nameof() == 'C'
+
+#&lt;/example3.py&gt;
+</pre>
+</blockquote>
+<p>This example also shows that decorators work both for <a class="reference" href="http://www.python.org/2.3/descrintro.html">new style classes
+and old style classes</a>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example3 import *
+&gt;&gt;&gt; type(C) # C is an old style class
+&lt;type 'classobj'&gt;
+</pre>
+<p>At this point the difference between <tt class="literal"><span class="pre">decorators.decorate</span></tt> and
+<tt class="literal"><span class="pre">decorators.decorated</span></tt> should be pointed out. The first syntax
+modifies the class dictionary, whereas the second creates a new
+class with the same name of the first one:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D:
+... identity=identity
+&gt;&gt;&gt; decorators.decorated(D)
+&lt;class 'D'&gt;
+&gt;&gt;&gt; D.identity(1) # this is the old D
+Traceback (most recent call last):
+ ...
+TypeError: unbound method identity() must be called with D instance as first argument (got int instance instead)
+</pre>
+<p>Therefore one has to redefine to old class in order the statement to
+have effect:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; D=decorators.decorated(D)
+&gt;&gt;&gt; D.identity(1) # this is the new D
+1
+</pre>
+</div>
+<div class="section" id="adding-a-bit-of-magic">
+<h1><a class="toc-backref" href="#id3" name="adding-a-bit-of-magic">Adding a bit of magic</a></h1>
+<p>It would be nice to have classes with the ability of automatically
+converting their methods to method decorators according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply by adding
+to the class a docstring starting with &quot;[Decorated]&quot;.
+Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example.py&gt;
+
+import decorators
+
+class C: # works for old style classes too
+ &quot;[Decorated]&quot;
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+
+decorators.decorate(globals())
+
+c=C()
+
+# test
+assert C.identity(1) == 1
+assert c.identity(1) == 1
+
+#&lt;/example.py&gt;
+</pre>
+</blockquote>
+<p>Under the hood, the magic docstring &quot;[Decorated]&quot; creates an instance of the
+<tt class="literal"><span class="pre">decorators.Decorated</span></tt> metaclass and replace the original class <tt class="literal"><span class="pre">C</span></tt>
+in the global namespace with the new class <tt class="literal"><span class="pre">C</span></tt>; incidentally,
+this converts <tt class="literal"><span class="pre">C</span></tt> in a new style class:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example import C
+&gt;&gt;&gt; type(C)
+&lt;class 'decorators.Decorated'&gt;
+</pre>
+<p>On the other hand using <tt class="literal"><span class="pre">decorators.decorate(C)</span></tt> would decorate <tt class="literal"><span class="pre">C</span></tt>, but
+without re-creating it as an instance of &quot;[Decorated]&quot;. One can also
+forget the docstring in subclasses of decorated classes:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(C):
+... def nameof(cls):
+... &quot;[classmethod]&quot;
+... return cls.__name__
+&gt;&gt;&gt; print D.nameof()
+D
+</pre>
+<p>The trick works for classes containing inner classes, too:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example4.py&gt;
+
+import decorators
+
+class C:
+ &quot;[Decorated]&quot; # required docstring
+ def identity(x):
+ &quot;[staticmethod]&quot;
+ return x
+ class Inner:
+ &quot;[Decorated]&quot; # required docstring
+ def nameof(cls):
+ &quot;[classmethod]&quot;
+ return cls.__name__
+
+decorators.decorate(globals())
+
+assert C.identity(1) == 1
+assert C.Inner.nameof() == 'Inner'
+
+#&lt;/example4.py&gt;
+</pre>
+</blockquote>
+</div>
+<div class="section" id="adding-more-magic">
+<h1><a class="toc-backref" href="#id4" name="adding-more-magic">Adding more magic</a></h1>
+<p>There is a neat trick to simplify the usage of decorators: decorating the
+<tt class="literal"><span class="pre">object</span></tt> class. Then all methods in all new style classes of your module
+will be checked for magic docstrings and automagically decorated if needed.
+This can be done by simply writing</p>
+<blockquote>
+<pre class="literal-block">
+import decorators
+object=decorators.decorated(object)
+</pre>
+</blockquote>
+<p>on top of your module. Here is an example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example.py&gt;
+
+import inspect, decorators
+object=decorators.decorated(object)
+
+def identity(x):
+ &quot;[staticmethod] defined outside a class&quot;
+ return x
+
+assert inspect.isfunction(identity) # not yet a decorator
+
+class C1(object):
+ def nameof(cls):
+ &quot;[classmethod] defined inside a class&quot;
+ return cls.__name__
+ identity=identity # automagically converted to a decorator
+
+c1=C1() # C1 instance
+
+# testing
+
+assert C1.identity(1) == 1
+assert C1.nameof() == 'C1'
+assert c1.identity(1) == 1
+assert c1.nameof() == 'C1'
+
+#&lt;/example.py&gt;
+</pre>
+</blockquote>
+<p>Notice that adding <tt class="literal"><span class="pre">identity</span></tt> after the class creation with the syntax
+<tt class="literal"><span class="pre">C.identity=identity</span></tt> would not work. Moreover, the magic only works
+for new style classes, since the implementation operates
+by enhancing the <tt class="literal"><span class="pre">object</span></tt> class in the calling module.
+The enhancement includes providing a new default printing representation
+for instances:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example import c1
+&gt;&gt;&gt; print c1
+&lt;instance of C1&gt;
+</pre>
+<p>The <tt class="literal"><span class="pre">decorated(object)</span></tt> trick (and the &quot;[Decorated]&quot; syntax too)
+is not 100% safe, because of possible metaclass conflicts:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import decorators; object=decorators.decorated(object)
+&gt;&gt;&gt; class M(type): pass
+...
+&gt;&gt;&gt; class D(object):
+... __metaclass__=M
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+</pre>
+<p>The decorators module imports the <tt class="literal"><span class="pre">makecls</span></tt> function from my
+<tt class="literal"><span class="pre">noconflict</span></tt> module just to avoid this kind of problems:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(object):
+... __metaclass__=decorators.makecls(M)
+&gt;&gt;&gt; type(D)
+&lt;class 'noconflict._DecoratedM'&gt;
+</pre>
+<p>It is possible to go even deeper in black magic, and to decorate all
+the new style classes in <em>all</em> modules, by decorating <tt class="literal"><span class="pre">__builtin__.object</span></tt>:</p>
+<blockquote>
+<pre class="literal-block">
+import decorators,__builtin__
+__builtin.object=decorators.decorated(object)
+</pre>
+</blockquote>
+<p>Still, redefining <tt class="literal"><span class="pre">__builtin__object</span></tt> is not recommended since it
+may induce metaclass conflicts in other modules using metaclasses.
+It will work only if you import modules not using metaclasses, or
+modules using metaclasses safely, i.e. modules taking care of
+possible conflicts by using the <tt class="literal"><span class="pre">makecls</span></tt> function or an equivalent one.</p>
+</div>
+<div class="section" id="defining-method-decorators">
+<h1><a class="toc-backref" href="#id5" name="defining-method-decorators">Defining method decorators</a></h1>
+<p>The decorators module contains two predefinite method decorators,
+<tt class="literal"><span class="pre">staticmethod</span></tt> and <tt class="literal"><span class="pre">classmethod</span></tt>, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The <tt class="literal"><span class="pre">decorators</span></tt> module provides a
+<tt class="literal"><span class="pre">MethodDecorator</span></tt> class which is here exactly for that purpose.</p>
+<p>Custom decorators are expected to be implemented by subclassing
+<tt class="literal"><span class="pre">MethodDecorator</span></tt> and by overriding its <tt class="literal"><span class="pre">get</span></tt> method, which
+automagically induces a <tt class="literal"><span class="pre">__get__</span></tt> method, turning the class
+in a descriptor. This
+machinery is needed since <tt class="literal"><span class="pre">__get__</span></tt> cannot be made cooperative
+using the standard <tt class="literal"><span class="pre">super</span></tt> mechanism (there would be a confusion
+between <tt class="literal"><span class="pre">super.__get__</span></tt> and the decorator <tt class="literal"><span class="pre">__get__</span></tt>). This is a bit
+tricky, but the causal programmer is not expected to write custom
+decorators, and actually I don't want to make the access to decorators
+<em>too</em> easy</p>
+<p>For instance, let me show the implementation of a <tt class="literal"><span class="pre">chattymethod</span></tt>
+that prints a message when it is called:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty.py&gt;
+
+from decorators import *
+object=decorated(object)
+
+class chattymethod(MethodDecorator):
+ logfile=file('file1.log','w')
+ def get(self,obj,cls=None): # same signature as __get__
+ print &gt;&gt; self.logfile,'calling %s from %s' % (self,obj or cls)
+ return super(chattymethod,self).get(obj,cls)
+
+#&lt;/chatty.py&gt;
+</pre>
+</blockquote>
+<p>Notice the usage of the <tt class="literal"><span class="pre">super().get</span></tt> trick. This guarantees that
+<tt class="literal"><span class="pre">chattymethod</span></tt> will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance)</p>
+<p>Here is an example of usage</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty.py&gt;
+
+class C(object):
+ def f():
+ &quot;[chattymethod,staticmethod]&quot;
+
+c=C()
+
+c.f()
+C.f()
+
+#&lt;/chatty.py&gt;
+</pre>
+</blockquote>
+<p>The content of the logfile is then</p>
+<blockquote>
+<pre class="literal-block">
+calling &lt;chattymethodstaticmethod:f&gt; from &lt;instance of C&gt;
+calling &lt;chattymethodstaticmethod:f&gt; from &lt;class C[Decorated]&gt;
+</pre>
+</blockquote>
+<p>From this output we see how the &quot;[chattymethod,staticmethod]&quot;
+magic docstring is responsible for the creation of a new decorator class
+<tt class="literal"><span class="pre">chattymethodstaticmethod</span></tt>, obtained via multiple inheritance from
+<tt class="literal"><span class="pre">chattymethod</span></tt> and <tt class="literal"><span class="pre">staticmethod</span></tt> respectively.</p>
+<p>One can easily change the logfile, if need there is</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty.py&gt;
+
+chattymethod.logfile=file('file2.log','w')
+
+def g():
+ &quot;[chattymethod,staticmethod]&quot;
+
+C.g=decorated(g)
+C.g # message written in file2.log
+C.f # message written in file2.log
+
+#&lt;/chatty.py&gt;
+</pre>
+</blockquote>
+<p>Now <tt class="literal"><span class="pre">file2.log</span></tt> will contains the messages</p>
+<blockquote>
+<pre class="literal-block">
+calling &lt;chattymethodstaticmethod:g&gt; from &lt;class C[Decorated]&gt;
+calling &lt;chattymethodstaticmethod:f&gt; from &lt;class C[Decorated]&gt;
+</pre>
+</blockquote>
+<p>This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. This can be avoided by converting <tt class="literal"><span class="pre">logfile</span></tt> from a class variable
+to an instance variable in the <tt class="literal"><span class="pre">__init__</span></tt> method:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;chatty2.py&gt;
+
+import sys
+from chatty import *
+
+class chattymethod2(chattymethod):
+ def __init__(self,func):
+ super(chattymethod2,self).__init__(func)
+ self.logfile=self.logfile # class variable becomes instance variable
+
+class C(object):
+ chattymethod2.logfile=sys.stdout
+ f=chattymethod2(lambda self:None)
+ chattymethod2.logfile=file('file3.log','w')
+ g=chattymethod2(lambda self:None)
+
+c=C()
+
+#&lt;/chatty2.py&gt;
+</pre>
+</blockquote>
+<p>Notice that the <tt class="literal"><span class="pre">__init__</span></tt> method should have the signature
+<tt class="literal"><span class="pre">__init__(self,func)</span></tt>, where <tt class="literal"><span class="pre">func</span></tt> is the function to be
+converted in the decorator object. Here is the testing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from chatty2 import c
+&gt;&gt;&gt; c.f()
+calling &lt;chattymethod2:&lt;lambda&gt;&gt; from &lt;instance of C&gt;
+&gt;&gt;&gt; c.g() # message written in file3.log
+&gt;&gt;&gt; c.f() # message written in stdout, not in file3.log!
+calling &lt;chattymethod2:&lt;lambda&gt;&gt; from &lt;instance of C&gt;
+</pre>
+</div>
+<div class="section" id="tracing-methods">
+<h1><a class="toc-backref" href="#id6" name="tracing-methods">Tracing methods</a></h1>
+<p>In order to show a non-trivial example, I will show how
+decorators can be used to implement traced methods.
+Here is the code (notice: the lazy reader can safely skip the
+implementation details and go directly to the usage section ;)</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracedmethod.py&gt;
+
+import sys,decorators
+
+class tracedmethod(decorators.MethodDecorator):
+ &quot;Descriptor class, converts a method in a traced method&quot;
+ indent=0; output=sys.stdout # defaults
+ def __init__(self,func):
+ super(tracedmethod,self).__init__(func)
+ self.funcname=self.func.__name__
+ def get(self,obj,cls):
+ if obj is None: name=self.inside.__name__ # called from class
+ else: name='&lt;%s&gt;' % self.inside.__name__ # from instance
+ methodwrapper=super(tracedmethod,self).get(obj,cls)
+ def _(*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write(&quot;%sCalling '%s.%s' with arguments &quot; %
+ (i,name,self.funcname))
+ self.output.write(&quot;%s ...\n&quot; % (str(args)+str(kw)))
+ res=methodwrapper(*args,**kw)
+ self.output.write(&quot;%s'%s.%s' called with result: %s\n&quot;
+ % (i,name,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return _
+
+#&lt;/tracedmethod.py&gt;
+</pre>
+</blockquote>
+<p>As soon as the <tt class="literal"><span class="pre">tracedmethod</span></tt> module is loaded, the <tt class="literal"><span class="pre">tracedmethod</span></tt> class
+is added to the list of know decorators, so one should use the &quot;[tracedmethod]&quot;
+docstring instead and not &quot;[tracedmethod.tracedmethod]&quot;.</p>
+<p><tt class="literal"><span class="pre">tracedmethod</span></tt> which is quite useful during
+debugging. Here is an example of usage, in tracing the internal working
+of a recursive function:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example4.py&gt;
+
+import decorators,tracedmethod
+
+class C(object):
+ def fact(self,n):
+ &quot;[tracedmethod]&quot;
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+
+decorators.decorate(C)
+
+c=C()
+
+#&lt;/example4.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example4 import c
+&gt;&gt;&gt; c.fact(2)
+Calling '&lt;C&gt;.fact' with arguments (2,){} ...
+ Calling '&lt;C&gt;.fact' with arguments (1,){} ...
+ Calling '&lt;C&gt;.fact' with arguments (0,){} ...
+ '&lt;C&gt;.fact' called with result: 1
+ '&lt;C&gt;.fact' called with result: 1
+'&lt;C&gt;.fact' called with result: 2
+2
+</pre>
+<p>An alternative spelling, not involving magic docstrings, nor the
+decorators module, is the following:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example5.py&gt;
+
+from tracedmethod import tracedmethod
+
+class C(object):
+ def fact(self,n):
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact=tracedmethod(fact)
+
+c=C()
+
+#&lt;/example5.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example5 import c
+&gt;&gt;&gt; c.fact(2)
+Calling '&lt;?&gt;.fact' with arguments (2,){} ...
+ Calling '&lt;?&gt;.fact' with arguments (1,){} ...
+ Calling '&lt;?&gt;.fact' with arguments (0,){} ...
+ '&lt;?&gt;.fact' called with result: 1
+ '&lt;?&gt;.fact' called with result: 1
+'&lt;?&gt;.fact' called with result: 2
+2
+</pre>
+<p>Notice that in this second syntax <tt class="literal"><span class="pre">fact</span></tt> does not know where it
+is defined; however the containing class can be explicitly set:</p>
+<blockquote>
+<tt class="literal"><span class="pre">C.__dict__['fact'].inside=C</span></tt></blockquote>
+<p>The <tt class="literal"><span class="pre">inside</span></tt> attribute is automagically set if the docstring syntax
+is used.</p>
+<p>Here is how to trace cooperative methods in complicate hierarchies
+(which is useful for debugging):</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracing.py&gt;
+
+import decorators,tracedmethod
+object=decorators.decorated(object)
+
+class B(object):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(B,self).__init__()
+
+class D(object):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(D,self).__init__()
+
+class E(B,D):
+ def __init__(self):
+ &quot;[tracedmethod]&quot;
+ super(E,self).__init__()
+
+ #&lt;/tracing.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracing import E
+&gt;&gt;&gt; e=E()
+Calling '&lt;E&gt;.__init__' with arguments (){} ...
+ Calling '&lt;B&gt;.__init__' with arguments (){} ...
+ Calling '&lt;D&gt;.__init__' with arguments (){} ...
+ '&lt;D&gt;.__init__' called with result: None
+ '&lt;B&gt;.__init__' called with result: None
+'&lt;E&gt;.__init__' called with result: None
+</pre>
+<p>In this example decorating <tt class="literal"><span class="pre">object</span></tt> is the easiest way to go.</p>
+</div>
+<div class="section" id="composition-of-decorators">
+<h1><a class="toc-backref" href="#id7" name="composition-of-decorators">Composition of decorators</a></h1>
+<p>Decorators can be composed: for instance, you can trace a
+classmethod as in this example:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;example6.py&gt;
+
+import tracedmethod
+from decorators import decorated
+object=decorated(object)
+
+class C(object):
+ def fact(cls,n):
+ &quot;[tracedmethod,classmethod]&quot;
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+#&lt;/example6.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example6 import C
+&gt;&gt;&gt; C.fact(2)
+Calling 'C.fact' with arguments (2,){} ...
+ Calling 'C.fact' with arguments (1,){} ...
+ Calling 'C.fact' with arguments (0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+</pre>
+<p>Under the hood the syntax</p>
+<blockquote>
+<pre class="literal-block">
+[tracedmethod,classmethod]
+</pre>
+</blockquote>
+<p>generates a <tt class="literal"><span class="pre">tracedmethodclassmethod</span></tt> class obtained via
+multiple inheritance:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for c in type(C.__dict__['fact']).__mro__: print c
+...
+&lt;class 'noconflict.tracedmethodclassmethod'&gt;
+&lt;class 'tracedmethod.tracedmethod'&gt;
+&lt;class 'decorators.classmethod'&gt;
+&lt;class 'decorators.MethodDecorator'&gt;
+&lt;class 'decorators.Decorator'&gt;
+&lt;type 'object'&gt;
+</pre>
+<p>In this case the order does not matter and using the docstring
+&quot;[classmethod,tracedmethod]&quot; would work too, but
+in general one must pay attention to precedence issues.
+For instance the following will not work:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... def fact(n):
+... &quot;[staticmethod,tracedmethod]&quot;
+... if n==0: return 1
+... else: return n*C.fact(n-1)
+&gt;&gt;&gt; C.fact(2)
+Traceback (most recent call last):
+ ...
+AttributeError: 'function' object has no attribute 'im_func'
+</pre>
+<p>The problem here is that <tt class="literal"><span class="pre">staticmethod.get</span></tt> invokes <tt class="literal"><span class="pre">tracedmethod.get</span></tt>
+which returns a function and not a method-wrapper with an <tt class="literal"><span class="pre">im_func</span></tt> method.
+On the other hand, composing the decorators in the other order
+&quot;[tracedmethod,staticmethod]&quot; will work just fine.</p>
+</div>
+<div class="section" id="class-decorators">
+<h1><a class="toc-backref" href="#id8" name="class-decorators">Class decorators</a></h1>
+<p>PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a
+class decorator at work, the metaclass <tt class="literal"><span class="pre">Decorated</span></tt>, giving
+to its instances the ability to interpret magic docstrings,
+and converting functions in method decorators.</p>
+<p>To define a custom class decorator is easy: one defines a custom metaclasses
+as usual, with the only difference from deriving by <tt class="literal"><span class="pre">ClassDecorator</span></tt> instead
+of deriving from <tt class="literal"><span class="pre">type</span></tt>. To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;logged.py&gt;
+
+from decorators import *
+
+class Logged(ClassDecorator):
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print &quot;%s created&quot; % cls
+
+#&lt;/logged.py&gt;
+</pre>
+</blockquote>
+<p><tt class="literal"><span class="pre">Logged</span></tt> is derived by the metaclass <tt class="literal"><span class="pre">ClassDecorator</span></tt>,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+metaclass <tt class="literal"><span class="pre">MetaDecorator</span></tt>). Logging capabilities can be added to a class
+by simply using the magic docstring syntax:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from logged import *
+&gt;&gt;&gt; object=decorators.decorated(object)
+&gt;&gt;&gt; class D(object):
+... &quot;[Logged]&quot;
+&lt;class D[_DecoratedLogged]&gt; created
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">D</span></tt> inherits the <tt class="literal"><span class="pre">Decorated</span></tt> metaclass from <tt class="literal"><span class="pre">object</span></tt>
+and the <tt class="literal"><span class="pre">Logged</span></tt> metaclass from the docstring; the conflict is
+automagically avoid by the miracolous creation of a <tt class="literal"><span class="pre">_DecoratedLogged</span></tt>
+metaclass, obtained via multiple inheritance from <tt class="literal"><span class="pre">Decorated</span></tt> and
+<tt class="literal"><span class="pre">Logged</span></tt>. All the magic is performed in the <tt class="literal"><span class="pre">noconflict</span></tt> module,
+discussed in a <a class="reference" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197">cookbook</a> recipe of mine.</p>
+<p>Notice that the printing representation of <tt class="literal"><span class="pre">D</span></tt> involves the name
+of <tt class="literal"><span class="pre">D</span></tt> preceded by the name of its metaclass, which in this case
+is <tt class="literal"><span class="pre">_DecoratedLogged</span></tt></p>
+<p>Each time an instance of <tt class="literal"><span class="pre">Logged</span></tt> is created, a similar message is printed:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class E(D):
+... pass
+&lt;class E[_DecoratedLogged]&gt; created
+</pre>
+<p>Notice that <tt class="literal"><span class="pre">E</span></tt> does not have any magic docstring</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; E.__doc__ # no docstring
+</pre>
+<p>but still it inherits its magic from <tt class="literal"><span class="pre">D</span></tt>.</p>
+<p>The <tt class="literal"><span class="pre">decorators</span></tt> module provides the already saw class decorator
+<tt class="literal"><span class="pre">Decorated</span></tt>, which converts methods in method decorators.</p>
+<p>Another example is</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;Traced.py&gt;
+
+import tracedmethod
+from decorators import *
+from types import FunctionType
+object=decorated(object)
+
+class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType):
+ func.__doc__=&quot;[tracedmethod] &quot; + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+class C(object):
+ &quot;&quot;&quot;[Traced] The class decorator adds the magic docstring
+ '[tracedmethod]' to f1 and f2, which are then converted
+ to method decorator objects.&quot;&quot;&quot;
+ def f1(self): pass
+ def f2(self): pass
+
+c=C()
+
+#&lt;/Traced.py&gt;
+</pre>
+</blockquote>
+<p>Here is an example of usage:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from Traced import C; c=C()
+&gt;&gt;&gt; c.f1()
+Calling '&lt;C&gt;.f1' with arguments (){} ...
+'&lt;C&gt;.f1' called with result: None
+&gt;&gt;&gt; c.f2()
+Calling '&lt;C&gt;.f2' with arguments (){} ...
+'&lt;C&gt;.f2' called with result: None
+</pre>
+</div>
+<div class="section" id="module-decorators">
+<h1><a class="toc-backref" href="#id9" name="module-decorators">Module decorators</a></h1>
+<p>Finally, one can decorate entire modules through the concept of
+<em>module decorators</em>. Module decorators have the ability of decorating
+modules by changing their dictionary. Custom module decorators
+should be derived from the class <tt class="literal"><span class="pre">decorators.ModuleDecorator</span></tt>, by
+cooperatively overriding its <tt class="literal"><span class="pre">__init__(self,mod)</span></tt> method. Writing
+a module decorator is a bit tricky, but I do expect only
+expert programmers to play this kind of game.
+For instance, suppose one wants to trace all the functions in a module,
+unless they have the docstring &quot;-untraced-&quot; . This can be done with a
+suitable module decorator which modifies the module dictionary.
+Here is an example</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;tracefunctions.py&gt;
+
+from decorators import *
+
+class TraceFunctions(ModuleDecorator):
+ def __init__(self,mod):
+ super(TraceFunctions,self).__init__(mod)
+ for name,func in self.__dict__.items():
+ if inspect.isfunction(func):
+ doc=func.__doc__ or ''
+ if doc.startswith('-untraced-'):
+ pass # do nothing
+ else:
+ def tracedfunc(func=func): # default argument trick
+ def _(*args,**kw):
+ print 'called',func.__name__
+ return func(*args,**kw)
+ return _
+ setattr(self,name,tracedfunc())
+
+#&lt;/tracefunctions.py&gt;
+</pre>
+</blockquote>
+<p>Let me test that the module decorator does its job. Consider the module</p>
+<blockquote>
+<pre class="literal-block">
+#&lt;mod.py&gt;
+
+#&quot;[TraceFunctions]&quot;
+
+def f1(): pass
+
+def f2(): pass
+
+def f3(): &quot;-untraced-&quot;
+
+#&lt;/mod.py&gt;
+</pre>
+</blockquote>
+<p>By importing this module, only the functions <tt class="literal"><span class="pre">f1</span></tt> and <tt class="literal"><span class="pre">f2</span></tt> should
+be traced. This is indeed the case:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from tracefunctions import TraceFunctions
+&gt;&gt;&gt; mod=TraceFunctions('mod',{})
+&gt;&gt;&gt; mod.f1()
+called f1
+&gt;&gt;&gt; mod.f2()
+called f2
+&gt;&gt;&gt; mod.f3() # does nothing, correct
+</pre>
+<blockquote>
+<pre class="literal-block">
+#&lt;module.py&gt;
+
+&quot;Magically decorated module&quot;
+
+import decorators,sys
+
+thismodule=sys.modules[__name__]
+
+class MyClass: &quot;[Decorated]&quot;
+
+newmod=decorators.decorated(thismodule)
+
+#&lt;/module.py&gt;
+</pre>
+</blockquote>
+<pre class="doctest-block">
+&gt;&gt;&gt; from module import *
+&gt;&gt;&gt; assert isinstance(newmod.MyClass, decorators.Decorated)
+&gt;&gt;&gt; assert isinstance(newmod,decorators.DecoratedModule)
+</pre>
+</div>
+<div class="section" id="introspection-features">
+<h1><a class="toc-backref" href="#id10" name="introspection-features">Introspection features</a></h1>
+<p>The module provides three utilities functions to retrieve the list of
+recognized decorators: <tt class="literal"><span class="pre">decorators.methodlike()</span></tt>, <tt class="literal"><span class="pre">decorators.classlike()</span></tt>
+and <tt class="literal"><span class="pre">decorators.modulelike()</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for d in decorators.methodlike(): print d
+...
+MethodDecorator
+tracedmethodclassmethod
+tracedmethod
+classmethod
+chattymethodstaticmethod
+staticmethodtracedmethod
+staticmethod
+chattymethod2
+chattymethod
+&gt;&gt;&gt; decorators.classlike()
+['Traced', 'Logged', '_DecoratedLogged', 'ClassDecorator', '_DecoratedM', 'Decorated', '_DecoratedTraced']
+&gt;&gt;&gt; decorators.modulelike()
+['ModuleDecorator', 'DecoratedModule', 'TraceFunctions']
+</pre>
+</div>
+<div class="section" id="the-implementation">
+<h1><a class="toc-backref" href="#id11" name="the-implementation">The implementation</a></h1>
+<p>This part can be safely skipped, unless you are a <em>really</em> curious and
+you want to know how the implementation works.</p>
+<p>The module is rather short (~250 lines) but far from being trivial,
+since it is based on extensive usage of metaclass wizardry.</p>
+<p>The main class-metaclass hierarchy is represented in figure 1, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed lines whereas inheritance is denoted by continuous
+lines.</p>
+<div class="figure">
+<p><img alt="decorators.png" src="decorators.png" /></p>
+</div>
+<p>The implementation is relatively smart. Suppose for instance
+that a programmer wrote something like</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from decorators import *
+&gt;&gt;&gt; object=decorated(object)
+&gt;&gt;&gt; class C(object):
+... def f():
+... &quot;[staticmethod,MethodDecorator]&quot;
+</pre>
+<p>to document the fact that <tt class="literal"><span class="pre">staticmethod</span></tt> is a method decorator
+and not the built-in <tt class="literal"><span class="pre">staticmethod</span></tt>. Since <tt class="literal"><span class="pre">staticmethod</span></tt> already
+derives from <tt class="literal"><span class="pre">MethodDecorator</span></tt>, it is redundant to repeat
+<tt class="literal"><span class="pre">MethodDecorator</span></tt>. Apparently, there is the risk of generating
+an useless <tt class="literal"><span class="pre">staticmethodMethodDecorator</span></tt> class, doing the same
+as <tt class="literal"><span class="pre">staticmethod</span></tt>. Fortunately, the implementation is able
+to recognize redundant class. In this case, a class called
+<tt class="literal"><span class="pre">MethodDecoratorDecorator</span></tt> is <em>not</em> created; <tt class="literal"><span class="pre">staticmethod</span></tt>
+is used instead:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print type(C.__dict__['f'])
+&lt;class 'decorators.staticmethod'&gt;
+</pre>
+<p>The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. They does not probably matter unless one has to decorate
+thousands or millions of functions and classes.</p>
+<p>Finally, a word about bugs. The <tt class="literal"><span class="pre">decorators</span></tt> module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which is extracted
+from the documentation that you are reading), I cannot guarantee that it
+is correct. If somebody finds a bug or an unexpected behavior, please let me
+know and I will try to fix it.</p>
+<!-- References: -->
+</div>
+</div>
+<hr class="footer"/>
+<div class="footer">
+<a class="reference" href="pep318.txt">View document source</a>.
+Generated on: 2003-09-09 16:26 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pep318/working/pep318.txt b/pypers/pep318/working/pep318.txt
new file mode 100755
index 0000000..03667bf
--- /dev/null
+++ b/pypers/pep318/working/pep318.txt
@@ -0,0 +1,1048 @@
+Implementing PEP 318 (decorators)
+======================================================================
+
+.. contents::
+
+Having plenty of free time in these days, I have finished an old
+project of mine, the implementation of PEP 318 in pure Python.
+
+Here is the rationale:
+
+* some kind of decorator syntax is scheduled to go in Python 2.4,
+ therefore it is interesting to play with the concept;
+
+* it is nice to play with decorators now, without having to
+ wait for one year or so;
+
+* it is much easier to experiment with the pure Python implementation;
+
+* the implementation can be seen as an exercise on modern Python
+ programming and may be valuable to people wanting to study the most
+ advanced new constructs in Python 2.2 (descriptors, metaclasses,
+ cooperative methods, etc.)
+
+Basics
+--------------------------------------------------------------------
+
+As people in this list most probably know, PEP 318 has the goal
+of providing a nice syntactic sugar for expressions like
+
+ ::
+
+ def identity(x):
+ return x
+ identity=staticmethod(identity)
+
+or
+
+ ::
+
+ def nameof(cls):
+ return cls.__name__
+ nameof=classmethod(nameof)
+
+which are pretty verbose. It is clear that having new syntax (as
+for instance the proposed square bracket notation)
+
+ ::
+
+ def identity(x)[staticmethod]:
+ return x
+
+ def nameof(cls)[classmethod]:
+ return cls.__name__
+
+involves changing the grammar and modifying the interpreter at the
+C level. However, it is possible to have the same effect without
+changing the Python grammar. The idea is to use magic docstrings
+like this:
+
+ ::
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def nameof(cls):
+ "[classmethod]"
+ return cls.__name__
+
+The implementation is able to recognize such docstrings
+and to automagically convert the functions in (method) decorators.
+
+Method decorators are nothing else than a sophisticated kind of wrappers.
+``staticmethod`` and ``classmethod`` are two examples of already existing
+decorators (actually my implementation rewrites them, but let me pass on
+this detail). Technically speaking, method decorators are classes
+taking a single function as input and producing a descriptor object
+as output (properties are not decorators according to this definition,
+since they take four functions as input, ``get, set, del_`` and ``doc``).
+Descriptors_ are objects with a ``__get__`` method; they are quite
+sophisticated, but fortunately they have been wonderfully explained by
+Raymond Hettinger already, so I am allowed to skip this point ;). A knowledge
+of descriptors is not needed in order to use the ``decorator`` module;
+however it is welcomed for advanced users wanting to implement
+custom decorators.
+
+Simple usage of decorators
+------------------------------------------------------------------------
+
+Before talking about the implementation details, I will show
+how the ``decorators`` module works in practice. The simplest and
+safest usage is by means of the functions ``decorators.decorate``
+and ``decorators.decorated``:
+
+1. ``decorators.decorated(obj)`` takes an object and checks its docstring;
+ if a magic docstring is found, it returns a decorated version of the
+ object, otherwise it returns ``None``;
+
+2. ``decorators.decorate(obj)`` takes a dictionary or an object with a
+ ``.__dict__`` attribute and returns ``None``. It works by
+ invoking ``decorators.decorated`` on the elements of the dictionary
+ and by modifying them if needed.
+
+Here is an example:
+
+ ::
+
+ #<example1.py>
+
+ "How to use ``decorators.decorate`` and ``decorators.decorated``"
+
+ import decorators
+
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]" # magic docstring here
+ return x
+
+ def nameof(cls):
+ "[classmethod]" # magic docstring here too
+ return cls.__name__
+
+ dic={'nameof': nameof, 'do_nothing': do_nothing}
+ decorators.decorate(dic) # converts nameof -> classmethod
+
+ C=type('C',(),dic) # creates the class with the modified dictionary
+ C.identity=decorators.decorated(identity) # converts identity -> staticmethod
+ c=C() # creates the instance
+
+ #</example1.py>
+
+and here is the testing:
+
+>>> from example1 import C,c
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert C.nameof() == 'C'
+>>> assert c.identity(1) == 1
+>>> assert c.nameof() == 'C'
+
+One can even pass the ``globals()`` dictionary since objects without
+a magic docstring are simply ignored. Therefore the previous example
+can be rewritten as
+
+ ::
+
+ #<example2.py>
+
+ import decorators
+
+ def do_nothing(self):
+ "No magic docstring here"
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def nameof(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ decorators.decorate(globals()) # decorates the functions
+
+ class C(object):
+ identity=identity
+ nameof=nameof
+ do_nothing=do_nothing
+
+ c=C()
+
+ #</example2.py>
+
+Here is the testing:
+
+>>> from example2 import c,C
+>>> assert c.do_nothing() is None
+>>> assert C.identity(1) == 1
+>>> assert C.nameof() == 'C'
+>>> assert c.identity(1) == 1
+>>> assert c.nameof() == 'C'
+
+Notice that the call to ``decorators.decorate(globals())`` must be done
+*after* the function definitions, otherwise the functions would
+not converted, since they were not in the global dictionary at the
+time of the call. Moreover, one should not try to pass the ``locals()``
+dictionary, since it will not work when ``locals() != globals()``.
+
+Alternatively, one can just decorate the class:
+
+ ::
+
+ #<example3.py>
+
+ import decorators
+
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ def nameof(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ class C:
+ identity=identity
+ nameof=nameof
+
+ decorators.decorate(C)
+
+ c=C()
+
+ # testing
+ assert C.identity(1) == 1
+ assert C.nameof() == 'C'
+ assert c.identity(1) == 1
+ assert c.nameof() == 'C'
+
+ #</example3.py>
+
+This example also shows that decorators work both for `new style classes
+and old style classes`_:
+
+>>> from example3 import *
+>>> type(C) # C is an old style class
+<type 'classobj'>
+
+At this point the difference between ``decorators.decorate`` and
+``decorators.decorated`` should be pointed out. The first syntax
+modifies the class dictionary, whereas the second creates a new
+class with the same name of the first one:
+
+>>> class D:
+... identity=identity
+>>> decorators.decorated(D)
+<class 'D'>
+>>> D.identity(1) # this is the old D
+Traceback (most recent call last):
+ ...
+TypeError: unbound method identity() must be called with D instance as first argument (got int instance instead)
+
+Therefore one has to redefine to old class in order the statement to
+have effect:
+
+
+>>> D=decorators.decorated(D)
+>>> D.identity(1) # this is the new D
+1
+
+Adding a bit of magic
+----------------------------------------------------------------------
+
+It would be nice to have classes with the ability of automatically
+converting their methods to method decorators according to the docstrings.
+This sounds a bit of magic, but actually can be done very simply by adding
+to the class a docstring starting with "[Decorated]".
+Here is an example:
+
+ ::
+
+ #<example.py>
+
+ import decorators
+
+ class C: # works for old style classes too
+ "[Decorated]"
+ def identity(x):
+ "[staticmethod]"
+ return x
+
+ decorators.decorate(globals())
+
+ c=C()
+
+ # test
+ assert C.identity(1) == 1
+ assert c.identity(1) == 1
+
+ #</example.py>
+
+Under the hood, the magic docstring "[Decorated]" creates an instance of the
+``decorators.Decorated`` metaclass and replace the original class ``C``
+in the global namespace with the new class ``C``; incidentally,
+this converts ``C`` in a new style class:
+
+>>> from example import C
+>>> type(C)
+<class 'decorators.Decorated'>
+
+On the other hand using ``decorators.decorate(C)`` would decorate ``C``, but
+without re-creating it as an instance of "[Decorated]". One can also
+forget the docstring in subclasses of decorated classes:
+
+>>> class D(C):
+... def nameof(cls):
+... "[classmethod]"
+... return cls.__name__
+>>> print D.nameof()
+D
+
+The trick works for classes containing inner classes, too:
+
+ ::
+
+ #<example4.py>
+
+ import decorators
+
+ class C:
+ "[Decorated]" # required docstring
+ def identity(x):
+ "[staticmethod]"
+ return x
+ class Inner:
+ "[Decorated]" # required docstring
+ def nameof(cls):
+ "[classmethod]"
+ return cls.__name__
+
+ decorators.decorate(globals())
+
+ assert C.identity(1) == 1
+ assert C.Inner.nameof() == 'Inner'
+
+ #</example4.py>
+
+
+Adding more magic
+----------------------------------------------------------------------------
+
+There is a neat trick to simplify the usage of decorators: decorating the
+``object`` class. Then all methods in all new style classes of your module
+will be checked for magic docstrings and automagically decorated if needed.
+This can be done by simply writing
+
+ ::
+
+ import decorators
+ object=decorators.decorated(object)
+
+on top of your module. Here is an example:
+
+ ::
+
+ #<example.py>
+
+ import inspect, decorators
+ object=decorators.decorated(object)
+
+ def identity(x):
+ "[staticmethod] defined outside a class"
+ return x
+
+ assert inspect.isfunction(identity) # not yet a decorator
+
+ class C1(object):
+ def nameof(cls):
+ "[classmethod] defined inside a class"
+ return cls.__name__
+ identity=identity # automagically converted to a decorator
+
+ c1=C1() # C1 instance
+
+ # testing
+
+ assert C1.identity(1) == 1
+ assert C1.nameof() == 'C1'
+ assert c1.identity(1) == 1
+ assert c1.nameof() == 'C1'
+
+ #</example.py>
+
+Notice that adding ``identity`` after the class creation with the syntax
+``C.identity=identity`` would not work. Moreover, the magic only works
+for new style classes, since the implementation operates
+by enhancing the ``object`` class in the calling module.
+The enhancement includes providing a new default printing representation
+for instances:
+
+>>> from example import c1
+>>> print c1
+<instance of C1>
+
+
+The ``decorated(object)`` trick (and the "[Decorated]" syntax too)
+is not 100% safe, because of possible metaclass conflicts:
+
+>>> import decorators; object=decorators.decorated(object)
+>>> class M(type): pass
+...
+>>> class D(object):
+... __metaclass__=M
+Traceback (most recent call last):
+ ...
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+The decorators module imports the ``makecls`` function from my
+``noconflict`` module just to avoid this kind of problems:
+
+>>> class D(object):
+... __metaclass__=decorators.makecls(M)
+>>> type(D)
+<class 'noconflict._DecoratedM'>
+
+It is possible to go even deeper in black magic, and to decorate all
+the new style classes in *all* modules, by decorating ``__builtin__.object``:
+
+ ::
+
+ import decorators,__builtin__
+ __builtin.object=decorators.decorated(object)
+
+Still, redefining ``__builtin__object`` is not recommended since it
+may induce metaclass conflicts in other modules using metaclasses.
+It will work only if you import modules not using metaclasses, or
+modules using metaclasses safely, i.e. modules taking care of
+possible conflicts by using the ``makecls`` function or an equivalent one.
+
+Defining method decorators
+-----------------------------------------------------------------------
+
+The decorators module contains two predefinite method decorators,
+``staticmethod`` and ``classmethod``, which emulate the built-ins
+with the same names. However, it is possible to write your own
+custom decorators. The ``decorators`` module provides a
+``MethodDecorator`` class which is here exactly for that purpose.
+
+Custom decorators are expected to be implemented by subclassing
+``MethodDecorator`` and by overriding its ``get`` method, which
+automagically induces a ``__get__`` method, turning the class
+in a descriptor. This
+machinery is needed since ``__get__`` cannot be made cooperative
+using the standard ``super`` mechanism (there would be a confusion
+between ``super.__get__`` and the decorator ``__get__``). This is a bit
+tricky, but the causal programmer is not expected to write custom
+decorators, and actually I don't want to make the access to decorators
+*too* easy
+
+For instance, let me show the implementation of a ``chattymethod``
+that prints a message when it is called:
+
+ ::
+
+ #<customdec.py>
+
+ from decorators import *
+
+ class chattymethod(MethodDecorator):
+ logfile=file('file1.log','w')
+ def get(self,obj,cls=None): # same signature as __get__
+ print >> self.logfile,'calling %s from %s' % (self,obj or cls)
+ return super(chattymethod,self).get(obj,cls)
+
+ #</customdec.py>
+
+Notice the usage of the ``super().get`` trick. This guarantees that
+``chattymethod`` will play well with other decorators (i.e. it
+can be nicely composed via multiple inheritance)
+
+Here is an example of usage
+
+ ::
+
+ #<chatty.py>
+
+ from customdec import decorated,chattymethod
+ object=decorated(object)
+
+ class C(object):
+ def f():
+ "[chattymethod,staticmethod]"
+
+ c=C()
+
+ c.f()
+ C.f()
+
+ #</chatty.py>
+
+The content of the logfile is then
+
+ ::
+
+ calling <chattymethodstaticmethod:f> from <instance of C>
+ calling <chattymethodstaticmethod:f> from <class C[Decorated]>
+
+From this output we see how the "[chattymethod,staticmethod]"
+magic docstring is responsible for the creation of a new decorator class
+``chattymethodstaticmethod``, obtained via multiple inheritance from
+``chattymethod`` and ``staticmethod`` respectively.
+
+One can easily change the logfile, if need there is
+
+ ::
+
+ #<chatty.py>
+
+ chattymethod.logfile=file('file2.log','w')
+
+ def g():
+ "[chattymethod,staticmethod]"
+
+ C.g=decorated(g)
+ C.g # message written in file2.log
+ C.f # message written in file2.log
+
+ #</chatty.py>
+
+Now ``file2.log`` will contains the messages
+
+ ::
+
+ calling <chattymethodstaticmethod:g> from <class C[Decorated]>
+ calling <chattymethodstaticmethod:f> from <class C[Decorated]>
+
+This approach has the drawback that chattymethods created before changing
+the logfile will also print to the new logfile, if invoked after the
+change. This can be avoided by converting ``logfile`` from a class variable
+to an instance variable in the ``__init__`` method:
+
+ ::
+
+ #<chatty2.py>
+
+ import sys
+ from chatty import *
+
+ class chattymethod2(chattymethod):
+ def __init__(self,func):
+ super(chattymethod2,self).__init__(func)
+ self.logfile=self.logfile # class variable becomes instance variable
+
+ class C(object):
+ chattymethod2.logfile=sys.stdout
+ f=chattymethod2(lambda self:None)
+ chattymethod2.logfile=file('file3.log','w')
+ g=chattymethod2(lambda self:None)
+
+ c=C()
+
+ #</chatty2.py>
+
+Notice that the ``__init__`` method should have the signature
+``__init__(self,func)``, where ``func`` is the function to be
+converted in the decorator object. Here is the testing:
+
+>>> from chatty2 import c
+>>> c.f()
+calling <chattymethod2:<lambda>> from <instance of C>
+>>> c.g() # message written in file3.log
+>>> c.f() # message written in stdout, not in file3.log!
+calling <chattymethod2:<lambda>> from <instance of C>
+
+Tracing methods
+--------------------------------------------------------------------------
+
+In order to show a non-trivial example, I will show how
+decorators can be used to implement traced methods.
+Here is the code (notice: the lazy reader can safely skip the
+implementation details and go directly to the usage section ;)
+
+ ::
+
+ #<customdec.py>
+
+ class tracedmethod(MethodDecorator):
+ "Descriptor class, converts a method in a traced method"
+ indent=0; output=sys.stdout # defaults
+ def __init__(self,func):
+ super(tracedmethod,self).__init__(func)
+ self.funcname=self.func.__name__
+ def get(self,obj,cls):
+ if obj is None: name=self.inside.__name__ # called from class
+ else: name='<%s>' % self.inside.__name__ # from instance
+ methodwrapper=super(tracedmethod,self).get(obj,cls)
+ def _(*args,**kw):
+ i=' '*self.indent # default indentation
+ self.__class__.indent+=4 # increases indentation
+ self.output.write("%sCalling '%s.%s' with arguments " %
+ (i,name,self.funcname))
+ self.output.write("%s ...\n" % (str(args)+str(kw)))
+ res=methodwrapper(*args,**kw)
+ self.output.write("%s'%s.%s' called with result: %s\n"
+ % (i,name,self.funcname,res))
+ self.__class__.indent-=4 # restores default indentation
+ return res
+ return _
+
+ #</customdec.py>
+
+As soon as the ``tracedmethod`` module is loaded, the ``tracedmethod`` class
+is added to the list of know decorators, so one should use the "[tracedmethod]"
+docstring instead and not "[tracedmethod.tracedmethod]".
+
+``tracedmethod`` which is quite useful during
+debugging. Here is an example of usage, in tracing the internal working
+of a recursive function:
+
+ ::
+
+ #<example4.py>
+
+ import decorators,customdec
+
+ class C(object):
+ def fact(self,n):
+ "[tracedmethod]"
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+
+ decorators.decorate(C)
+
+ c=C()
+
+ #</example4.py>
+
+>>> from example4 import c
+>>> c.fact(2)
+Calling '<C>.fact' with arguments (2,){} ...
+ Calling '<C>.fact' with arguments (1,){} ...
+ Calling '<C>.fact' with arguments (0,){} ...
+ '<C>.fact' called with result: 1
+ '<C>.fact' called with result: 1
+'<C>.fact' called with result: 2
+2
+
+An alternative spelling, not involving magic docstrings, nor the
+decorators module, is the following:
+
+ ::
+
+ #<example5.py>
+
+ from customdec import tracedmethod
+
+ class C(object):
+ def fact(self,n):
+ if n==0: return 1
+ else: return n*self.fact(n-1)
+ fact=tracedmethod(fact)
+
+ c=C()
+
+ #</example5.py>
+
+>>> from example5 import c
+>>> c.fact(2)
+Calling '<?>.fact' with arguments (2,){} ...
+ Calling '<?>.fact' with arguments (1,){} ...
+ Calling '<?>.fact' with arguments (0,){} ...
+ '<?>.fact' called with result: 1
+ '<?>.fact' called with result: 1
+'<?>.fact' called with result: 2
+2
+
+Notice that in this second syntax ``fact`` does not know where it
+is defined; however the containing class can be explicitly set:
+
+ ``C.__dict__['fact'].inside=C``
+
+The ``inside`` attribute is automagically set if the docstring syntax
+is used.
+
+Here is how to trace cooperative methods in complicate hierarchies
+(which is useful for debugging):
+
+ ::
+
+ #<tracing.py>
+
+ import decorators,customdec
+ object=decorators.decorated(object)
+
+ class B(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(B,self).__init__()
+
+ class D(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(D,self).__init__()
+
+ class E(B,D):
+ def __init__(self):
+ "[tracedmethod]"
+ super(E,self).__init__()
+
+ #</tracing.py>
+
+>>> from tracing import E
+>>> e=E()
+Calling '<E>.__init__' with arguments (){} ...
+ Calling '<B>.__init__' with arguments (){} ...
+ Calling '<D>.__init__' with arguments (){} ...
+ '<D>.__init__' called with result: None
+ '<B>.__init__' called with result: None
+'<E>.__init__' called with result: None
+
+In this example decorating ``object`` is the easiest way to go.
+
+Composition of decorators
+--------------------------------------------------------------------
+
+Decorators can be composed: for instance, you can trace a
+classmethod as in this example:
+
+ ::
+
+ #<example6.py>
+
+ import customdec
+ from decorators import decorated
+ object=decorated(object)
+
+ class C(object):
+ def fact(cls,n):
+ "[tracedmethod,classmethod]"
+ if n==0: return 1
+ else: return n*cls.fact(n-1)
+
+ #</example6.py>
+
+>>> from example6 import C
+>>> C.fact(2)
+Calling 'C.fact' with arguments (2,){} ...
+ Calling 'C.fact' with arguments (1,){} ...
+ Calling 'C.fact' with arguments (0,){} ...
+ 'C.fact' called with result: 1
+ 'C.fact' called with result: 1
+'C.fact' called with result: 2
+2
+
+Under the hood the syntax
+
+ ::
+
+ [tracedmethod,classmethod]
+
+generates a ``tracedmethodclassmethod`` class obtained via
+multiple inheritance:
+
+>>> for c in type(C.__dict__['fact']).__mro__: print c
+...
+<class 'noconflict.tracedmethodclassmethod'>
+<class 'customdec.tracedmethod'>
+<class 'decorators.classmethod'>
+<class 'decorators.MethodDecorator'>
+<class 'decorators.Decorator'>
+<type 'object'>
+
+In this case the order does not matter and using the docstring
+"[classmethod,tracedmethod]" would work too, but
+in general one must pay attention to precedence issues.
+For instance the following will not work:
+
+>>> class C(object):
+... def fact(n):
+... "[staticmethod,tracedmethod]"
+... if n==0: return 1
+... else: return n*C.fact(n-1)
+>>> C.fact(2)
+Traceback (most recent call last):
+ ...
+AttributeError: 'function' object has no attribute 'im_func'
+
+The problem here is that ``staticmethod.get`` invokes ``tracedmethod.get``
+which returns a function and not a method-wrapper with an ``im_func`` method.
+On the other hand, composing the decorators in the other order
+"[tracedmethod,staticmethod]" will work just fine.
+
+Class decorators
+-----------------------------------------------------------------------
+
+PEP 318 proposes to decorate methods by using descriptors; it is
+quite natural to extend this idea and to decorate classes by using
+class decorators implemented as metaclasses. We already saw a
+class decorator at work, the metaclass ``Decorated``, giving
+to its instances the ability to interpret magic docstrings,
+and converting functions in method decorators.
+
+To define a custom class decorator is easy: one defines a custom metaclasses
+as usual, with the only difference from deriving by ``ClassDecorator`` instead
+of deriving from ``type``. To understand how this works in practice, let me
+show how to add logging capabilities to a given class. The first
+step is to define a suitable class decorator, such as the following:
+
+ ::
+
+ #<customdec.py>
+
+ class Logged(ClassDecorator):
+ def __init__(cls,name,bases,dic):
+ super(Logged,cls).__init__(name,bases,dic)
+ print "%s created" % cls
+
+ #</customdec.py>
+
+``Logged`` is derived by the metaclass ``ClassDecorator``,
+which provides a certain amount of magic under the hood (in particular
+its printing representation and its calling syntax are redefined by its
+metaclass ``MetaDecorator``). Logging capabilities can be added to a class
+by simply using the magic docstring syntax:
+
+>>> from customdec import Logged
+>>> object=decorators.decorated(object)
+>>> class D(object):
+... "[Logged]"
+<class D[_DecoratedLogged]> created
+
+Notice that ``D`` inherits the ``Decorated`` metaclass from ``object``
+and the ``Logged`` metaclass from the docstring; the conflict is
+automagically avoid by the miracolous creation of a ``_DecoratedLogged``
+metaclass, obtained via multiple inheritance from ``Decorated`` and
+``Logged``. All the magic is performed in the ``noconflict`` module,
+discussed in a cookbook_ recipe of mine.
+
+Notice that the printing representation of ``D`` involves the name
+of ``D`` preceded by the name of its metaclass, which in this case
+is ``_DecoratedLogged``
+
+Each time an instance of ``Logged`` is created, a similar message is printed:
+
+>>> class E(D):
+... pass
+<class E[_DecoratedLogged]> created
+
+Notice that ``E`` does not have any magic docstring
+
+>>> E.__doc__ # no docstring
+
+but still it inherits its magic from ``D``.
+
+The ``decorators`` module provides the already saw class decorator
+``Decorated``, which converts methods in method decorators.
+
+Another example is
+
+ ::
+
+ #<customdec.py>
+
+ from types import FunctionType
+
+ class Traced(ClassDecorator):
+ def __init__(cls,n,b,d):
+ for name,func in d.iteritems():
+ if isinstance(func,FunctionType):
+ func.__doc__="[tracedmethod] " + (func.__doc__ or '')
+ super(Traced,cls).__init__(n,b,d)
+
+
+ #</customdec.py>
+
+Here is an example of usage:
+
+>>> from customdec import *
+>>> object=decorated(object)
+>>> class C(object):
+... """[Traced] The class decorator adds the magic docstring
+... '[tracedmethod]' to f1 and f2, which are then converted
+... to method decorator objects."""
+... def f1(self): pass
+... def f2(self): pass
+...
+>>> c=C()
+>>> c.f1()
+Calling '<C>.f1' with arguments (){} ...
+'<C>.f1' called with result: None
+>>> c.f2()
+Calling '<C>.f2' with arguments (){} ...
+'<C>.f2' called with result: None
+
+Module decorators
+-----------------------------------------------------------------------
+
+Finally, one can decorate entire modules through the concept of
+*module decorators*. Module decorators have the ability of decorating
+modules by changing their dictionary. Custom module decorators
+should be derived from the class ``decorators.ModuleDecorator``, by
+cooperatively overriding its ``__init__(self,mod)`` method. Writing
+a module decorator is a bit tricky, but I do expect only
+expert programmers to play this kind of game.
+For instance, suppose one wants to trace all the functions in a module,
+unless they have the docstring "-untraced-" . This can be done with a
+suitable module decorator which modifies the module dictionary.
+Here is an example
+
+ ::
+
+ #<customdec.py>
+
+ from decorators import *
+
+ class TraceFunctions(ModuleDecorator):
+ def __init__(self,mod):
+ super(TraceFunctions,self).__init__(mod)
+ for name,func in self.__dict__.items():
+ if inspect.isfunction(func):
+ doc=func.__doc__ or ''
+ if doc.startswith('-untraced-'):
+ pass # do nothing
+ else:
+ def tracedfunc(func=func): # default argument trick
+ def _(*args,**kw):
+ print 'called',func.__name__
+ return func(*args,**kw)
+ return _
+ setattr(self,name,tracedfunc())
+
+ #</customdec.py>
+
+Let me test that the module decorator does its job. Consider the module
+
+ ::
+
+ #<mod.py>
+
+ #"[TraceFunctions]"
+
+ def f1(): pass
+
+ def f2(): pass
+
+ def f3(): "-untraced-"
+
+ #</mod.py>
+
+By importing this module, only the functions ``f1`` and ``f2`` should
+be traced. This is indeed the case:
+
+>>> from customdec import TraceFunctions
+>>> mod=TraceFunctions('mod',{})
+>>> mod.f1()
+called f1
+>>> mod.f2()
+called f2
+>>> mod.f3() # does nothing, correct
+
+ ::
+
+ #<module.py>
+
+ "Magically decorated module"
+
+ import decorators,sys
+
+ thismodule=sys.modules[__name__]
+
+ class MyClass: "[Decorated]"
+
+ newmod=decorators.decorated(thismodule)
+
+ #</module.py>
+
+>>> from module import *
+>>> assert isinstance(newmod.MyClass, decorators.Decorated)
+>>> assert isinstance(newmod,decorators.DecoratedModule)
+
+The implementation
+-----------------------------------------------------------------------
+
+This part can be safely skipped, unless you are a *really* curious and
+you want to know how the implementation works.
+
+The module is rather short (~250 lines) but far from being trivial,
+since it is based on extensive usage of metaclass wizardry.
+
+The main class-metaclass hierarchy is represented in figure 1, where
+boxes denote classes and ovals denote metaclasses; instantiation is
+denoted by dashed lines whereas inheritance is denoted by continuous
+lines.
+
+.. figure:: decorators.png
+
+The implementation is relatively smart. Suppose for instance
+that a programmer wrote something like
+
+>>> from decorators import *
+>>> object=decorated(object)
+>>> class C(object):
+... def f():
+... "[staticmethod,MethodDecorator]"
+
+to document the fact that ``staticmethod`` is a method decorator
+and not the built-in ``staticmethod``. Since ``staticmethod`` already
+derives from ``MethodDecorator``, it is redundant to repeat
+``MethodDecorator``. Apparently, there is the risk of generating
+an useless ``staticmethodMethodDecorator`` class, doing the same
+as ``staticmethod``. Fortunately, the implementation is able
+to recognize redundant class. In this case, a class called
+``MethodDecoratorDecorator`` is *not* created; ``staticmethod``
+is used instead:
+
+>>> print type(C.__dict__['f'])
+<class 'decorators.staticmethod'>
+
+The module provides three utilities functions to retrieve the list of
+recognized decorators: ``decorators.methodlike()``, ``decorators.classlike()``
+and ``decorators.modulelike()``:
+
+>>> for d in decorators.methodlike(): print d
+...
+<class 'decorators.MethodDecorator'>
+<class 'decorators.staticmethod'>
+<class 'decorators.classmethod'>
+<class 'customdec.chattymethod'>
+<class 'customdec.tracedmethod'>
+<class 'noconflict.chattymethodstaticmethod'>
+<class 'chatty2.chattymethod2'>
+<class 'noconflict.tracedmethodclassmethod'>
+<class 'noconflict.staticmethodtracedmethod'>
+
+>>> for d in decorators.classlike(): print d
+...
+<class 'decorators.ClassDecorator'>
+<class 'decorators.Decorated'>
+<class 'noconflict._DecoratedM'>
+<class 'customdec.Logged'>
+<class 'customdec.Traced'>
+<class 'noconflict._DecoratedLogged'>
+<class 'noconflict._DecoratedTraced'>
+
+>>> for d in decorators.modulelike(): print d
+...
+<class 'decorators.ModuleDecorator'>
+<class 'decorators.DecoratedModule'>
+<class 'customdec.TraceFunctions'>
+
+The current implementation does not make any attempt of optimization and
+there may be alternative implementations faster or more memory efficient.
+At this experimental level I didn't care to explore about performances
+issues. They does not probably matter unless one has to decorate
+thousands or millions of functions and classes.
+
+Finally, a word about bugs. The ``decorators`` module is fairly sophisticated,
+therefore whereas I can guarantee that it passes my test suite (which involves
+~100 tests automatically extracted from the documentation you are reading),
+I cannot guarantee that it is correct. If somebody finds a bug or an unexpected
+behavior, please let me know and I will try to fix it.
+
+.. References:
+
+.. _new style classes and old style classes:
+ http://www.python.org/2.3/descrintro.html
+.. _Descriptors: http://users.rcn.com/python/download/Descriptor.htm
+.. _cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
diff --git a/pypers/pep318/working/pydoc.html b/pypers/pep318/working/pydoc.html
new file mode 100755
index 0000000..8422868
--- /dev/null
+++ b/pypers/pep318/working/pydoc.html
@@ -0,0 +1,504 @@
+
+<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module decorators</title>
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>decorators</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/mnt/exp/MyDocs/pypers/pep318/decorators.py">/mnt/exp/MyDocs/pypers/pep318/decorators.py</a></font></td></tr></table>
+ <p><tt>A&nbsp;module&nbsp;to&nbsp;implement&nbsp;pep318&nbsp;(decorator&nbsp;syntax)&nbsp;via&nbsp;magic&nbsp;doctrings.<br>
+For&nbsp;the&nbsp;documentation&nbsp;see<br>
+&nbsp;<br>
+<a href="http://www.phyast.pitt.edu/~micheles/python/decorators,html">http://www.phyast.pitt.edu/~micheles/python/decorators,html</a><br>
+&nbsp;<br>
+help&nbsp;and&nbsp;pydoc&nbsp;are&nbsp;useful&nbsp;too.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#fffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="__builtin__.html">__builtin__</a><br>
+<a href="inspect.html">inspect</a><br>
+</td><td width="25%" valign=top><a href="noconflict.html">noconflict</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="types.html">types</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#UnknownDecoratorError">UnknownDecoratorError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorator">Decorator</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="decorators.html#MethodDecorator">MethodDecorator</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#classmethod">classmethod</a>
+</font></dt><dt><font face="helvetica, arial"><a href="decorators.html#staticmethod">staticmethod</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#type">__builtin__.type</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#ClassDecorator">ClassDecorator</a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="decorators.html#Decorated">Decorated</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="decorators.html#MetaDecorator">MetaDecorator</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ClassDecorator">class <strong>ClassDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>, <a href="decorators.html#Decorator">Decorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;callable&nbsp;with&nbsp;one&nbsp;or&nbsp;three&nbsp;arguments,&nbsp;having&nbsp;its&nbsp;__call__<br>
+method&nbsp;redefined&nbsp;by&nbsp;the&nbsp;meta-metaclass&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.&nbsp;It&nbsp;redefines<br>
+__str__&nbsp;both&nbsp;on&nbsp;classes&nbsp;and&nbsp;instances.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ClassDecorator-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl>
+
+<dl><dt><a name="ClassDecorator-__str__"><strong>__str__</strong></a>(cls)</dt></dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="ClassDecorator-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__call__">__call__</a>(...)&nbsp;&lt;==&gt;&nbsp;x(...)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ClassDecorator-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="ClassDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#ClassDecorator-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;type 'type'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.ClassDecorator'&gt;, &lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ClassDecorator-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Decorated">class <strong>Decorated</strong></a>(<a href="decorators.html#ClassDecorator">ClassDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;which&nbsp;decorates&nbsp;its&nbsp;instances<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#Decorated">Decorated</a></dd>
+<dd><a href="decorators.html#ClassDecorator">ClassDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Decorated-__init__"><strong>__init__</strong></a>(cls, name, bases, dic)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#ClassDecorator">ClassDecorator</a>:<br>
+<dl><dt><a name="Decorated-__str__"><strong>__str__</strong></a>(cls)</dt></dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="Decorated-__call__"><strong>__call__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__call__">__call__</a>(...)&nbsp;&lt;==&gt;&nbsp;x(...)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Decorated-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Decorated-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#Decorated-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="Decorated-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#Decorated-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;class 'decorators.ClassDecorator'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;class 'decorators.ClassDecorator'&gt;,)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.Decorated'&gt;, &lt;class 'decorators.ClassDecorator'&gt;, &lt;type 'type'&gt;, &lt;class 'decorators.Decorator'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#Decorated-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Decorator">class <strong>Decorator</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Instance&nbsp;of&nbsp;<a href="#MetaDecorator">MetaDecorator</a>,&nbsp;i.e.&nbsp;each&nbsp;time&nbsp;<a href="#Decorator">Decorator</a>&nbsp;is<br>
+subclassed,&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic&nbsp;is&nbsp;updated.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Data and other attributes defined here:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MetaDecorator">class <strong>MetaDecorator</strong></a>(<a href="__builtin__.html#type">__builtin__.type</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#MetaDecorator">MetaDecorator</a></dd>
+<dd><a href="__builtin__.html#type">__builtin__.type</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MetaDecorator-__call__"><strong>__call__</strong></a>(dec, *args)</dt><dd><tt>Dispatch&nbsp;to&nbsp;the&nbsp;decorator&nbsp;_call_&nbsp;<a href="#classmethod">classmethod</a></tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__init__"><strong>__init__</strong></a>(dec, *args)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>dic</strong> = {'ClassDecorator': &lt;class 'decorators.ClassDecorator'&gt;, 'Decorated': &lt;class 'decorators.Decorated'&gt;, 'Decorator': &lt;class 'decorators.Decorator'&gt;, 'MethodDecorator': &lt;class 'decorators.MethodDecorator'&gt;, 'classmethod': &lt;class 'decorators.classmethod'&gt;, 'staticmethod': &lt;class 'decorators.staticmethod'&gt;}</dl>
+
+<dl><dt><strong>ls</strong> = [&lt;class 'decorators.Decorator'&gt;, &lt;class 'decorators.MethodDecorator'&gt;, &lt;class 'decorators.ClassDecorator'&gt;, &lt;class 'decorators.Decorated'&gt;, &lt;class 'decorators.staticmethod'&gt;, &lt;class 'decorators.classmethod'&gt;]</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><a name="MetaDecorator-__cmp__"><strong>__cmp__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__cmp__">__cmp__</a>(y)&nbsp;&lt;==&gt;&nbsp;cmp(x,y)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MetaDecorator-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-__subclasses__"><strong>__subclasses__</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-__subclasses__">__subclasses__</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;immediate&nbsp;subclasses</tt></dd></dl>
+
+<dl><dt><a name="MetaDecorator-mro"><strong>mro</strong></a>(...)</dt><dd><tt><a href="#MetaDecorator-mro">mro</a>()&nbsp;-&gt;&nbsp;list<br>
+return&nbsp;a&nbsp;<a href="__builtin__.html#type">type</a>'s&nbsp;method&nbsp;resolution&nbsp;order</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="__builtin__.html#type">__builtin__.type</a>:<br>
+<dl><dt><strong>__base__</strong> = &lt;type 'type'&gt;</dl>
+
+<dl><dt><strong>__bases__</strong> = (&lt;type 'type'&gt;,)</dl>
+
+<dl><dt><strong>__basicsize__</strong> = 420</dl>
+
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;</dl>
+
+<dl><dt><strong>__dictoffset__</strong> = 132</dl>
+
+<dl><dt><strong>__flags__</strong> = 22523</dl>
+
+<dl><dt><strong>__itemsize__</strong> = 20</dl>
+
+<dl><dt><strong>__mro__</strong> = (&lt;class 'decorators.MetaDecorator'&gt;, &lt;type 'type'&gt;, &lt;type 'object'&gt;)</dl>
+
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MetaDecorator-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="__builtin__.html#type">type</a>&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<dl><dt><strong>__weakrefoffset__</strong> = 184</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MethodDecorator">class <strong>MethodDecorator</strong></a>(<a href="decorators.html#Decorator">Decorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#MethodDecorator">MethodDecorator</a>&nbsp;objects&nbsp;provide&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'str'&nbsp;method<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MethodDecorator-__get__"><strong>__get__</strong></a> = <a href="#MethodDecorator-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MethodDecorator-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="UnknownDecoratorError">class <strong>UnknownDecoratorError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;name&nbsp;says&nbsp;it&nbsp;all<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="UnknownDecoratorError-__getitem__"><strong>__getitem__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownDecoratorError-__init__"><strong>__init__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownDecoratorError-__str__"><strong>__str__</strong></a>(...)</dt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="classmethod">class <strong>classmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Decorator">Decorator</a>,&nbsp;converts&nbsp;a&nbsp;function&nbsp;in&nbsp;a&nbsp;<a href="#classmethod">classmethod</a><br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#classmethod">classmethod</a></dd>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="classmethod-__get__"><strong>__get__</strong></a> = <a href="#classmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="classmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><a name="classmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="classmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="staticmethod">class <strong>staticmethod</strong></a>(<a href="decorators.html#MethodDecorator">MethodDecorator</a>)</font></td></tr>
+
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Decorator">Decorator</a>,&nbsp;converts&nbsp;a&nbsp;function&nbsp;in&nbsp;a&nbsp;<a href="#staticmethod">staticmethod</a><br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="decorators.html#staticmethod">staticmethod</a></dd>
+<dd><a href="decorators.html#MethodDecorator">MethodDecorator</a></dd>
+<dd><a href="decorators.html#Decorator">Decorator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="staticmethod-__get__"><strong>__get__</strong></a> = <a href="#staticmethod-get">get</a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="staticmethod-get"><strong>get</strong></a>(self, obj, cls<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><a name="staticmethod-__init__"><strong>__init__</strong></a>(self, func)</dt></dl>
+
+<dl><dt><a name="staticmethod-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#MethodDecorator">MethodDecorator</a>:<br>
+<dl><dt><strong>__klass__</strong> = &lt;class 'decorators.?'&gt;</dl>
+
+<hr>
+Data and other attributes inherited from <a href="decorators.html#Decorator">Decorator</a>:<br>
+<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'decorators.MetaDecorator'&gt;<dd><tt>Metaclass&nbsp;inducing&nbsp;a&nbsp;certain&nbsp;amount&nbsp;of&nbsp;magic&nbsp;on&nbsp;decorators:<br>
+1.&nbsp;Guarantees&nbsp;the&nbsp;calling&nbsp;syntax&nbsp;decorator(obj)<br>
+2.&nbsp;Each&nbsp;time&nbsp;a&nbsp;decorator&nbsp;is&nbsp;defined,&nbsp;it&nbsp;is&nbsp;stored&nbsp;in&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.dic<br>
+&nbsp;&nbsp;&nbsp;and&nbsp;<a href="#MetaDecorator">MetaDecorator</a>.ls.<br>
+3.&nbsp;If&nbsp;the&nbsp;(method)&nbsp;decorator&nbsp;has&nbsp;a&nbsp;'get'&nbsp;method,&nbsp;a&nbsp;'__get__'&nbsp;method<br>
+is&nbsp;automagically&nbsp;created&nbsp;as&nbsp;an&nbsp;alias&nbsp;to&nbsp;'get'.</tt></dl>
+
+<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Decorator' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-anyTrue"><strong>anyTrue</strong></a> = sum(...)</dt><dd><tt>sum(sequence,&nbsp;start=0)&nbsp;-&gt;&nbsp;value<br>
+&nbsp;<br>
+Returns&nbsp;the&nbsp;sum&nbsp;of&nbsp;a&nbsp;sequence&nbsp;of&nbsp;numbers&nbsp;(NOT&nbsp;strings)&nbsp;plus&nbsp;the&nbsp;value<br>
+of&nbsp;parameter&nbsp;'start'.&nbsp;&nbsp;When&nbsp;the&nbsp;sequence&nbsp;is&nbsp;empty,&nbsp;returns&nbsp;start.</tt></dd></dl>
+ <dl><dt><a name="-decorate"><strong>decorate</strong></a>(objdict)</dt><dd><tt>takes&nbsp;an&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;a&nbsp;dictionary&nbsp;and&nbsp;decorates&nbsp;all&nbsp;its&nbsp;functions<br>
+and&nbsp;classes&nbsp;according&nbsp;to&nbsp;their&nbsp;docstrings.</tt></dd></dl>
+ <dl><dt><a name="-decorated"><strong>decorated</strong></a>(obj<font color="#909090">=None</font>)</dt><dd><tt>Returns&nbsp;a&nbsp;new&nbsp;decorated&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;created&nbsp;from&nbsp;obj,&nbsp;or&nbsp;None,&nbsp;if&nbsp;obj<br>
+cannot&nbsp;be&nbsp;decorated.</tt></dd></dl>
+ <dl><dt><a name="-decorator_from"><strong>decorator_from</strong></a>(magicstring)</dt><dd><tt>Takes&nbsp;a&nbsp;magic&nbsp;string,&nbsp;i.e.&nbsp;a&nbsp;list&nbsp;of&nbsp;comma-separated&nbsp;decorator&nbsp;names,<br>
+and&nbsp;returns&nbsp;a&nbsp;decorator&nbsp;class&nbsp;or&nbsp;None&nbsp;(if&nbsp;the&nbsp;string&nbsp;is&nbsp;not&nbsp;matched).</tt></dd></dl>
+ <dl><dt><a name="-get"><strong>get</strong></a>(magicstring<font color="#909090">='Decorator'</font>)</dt><dd><tt>List&nbsp;of&nbsp;recognized&nbsp;decorators</tt></dd></dl>
+ <dl><dt><a name="-magicstring"><strong>magicstring</strong></a>(docstring)</dt></dl>
+ <dl><dt><a name="-printerr"><strong>printerr</strong></a>(*args)</dt><dd><tt>For&nbsp;debugging&nbsp;purposes</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>MAGICDOC</strong> = &lt;_sre.SRE_Pattern object&gt;<br>
+<strong>err</strong> = &lt;open file 'err', mode 'w'&gt;</td></tr></table>
+</body></html> \ No newline at end of file
diff --git a/pypers/pep318/working/tracing.py b/pypers/pep318/working/tracing.py
new file mode 100755
index 0000000..05f94b4
--- /dev/null
+++ b/pypers/pep318/working/tracing.py
@@ -0,0 +1,20 @@
+# tracing.py
+
+import customdec; customdec.enhance_classes("[Decorated]")
+
+class B(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(B,self).__init__()
+
+class D(object):
+ def __init__(self):
+ "[tracedmethod]"
+ super(D,self).__init__()
+
+class E(B,D):
+ def __init__(self):
+ "[tracedmethod]"
+ super(E,self).__init__()
+
+
diff --git a/pypers/pep318/x.py b/pypers/pep318/x.py
new file mode 100755
index 0000000..fb6f93d
--- /dev/null
+++ b/pypers/pep318/x.py
@@ -0,0 +1,17 @@
+"[Decorated]"
+
+import decorators,customdec; decorators.decorated()
+
+class desc(object):
+ def __get__(self,obj,cls):
+ print obj,cls
+
+class C(object):
+ def f(cls):
+ "[classmethod]"
+ print cls
+ g=desc()
+
+
+C.g
+C().g
diff --git a/pypers/pep318/x.txt b/pypers/pep318/x.txt
new file mode 100755
index 0000000..870e527
--- /dev/null
+++ b/pypers/pep318/x.txt
@@ -0,0 +1,7 @@
+>>> import customdec; customdec.enhance_classes()
+>>> class C:
+... "[Decorated,Logged]"
+... def f():
+... "[staticmethod]"
+... return 'it works!'
+<class C[DecoratedLogged]> created
diff --git a/pypers/preface.tex b/pypers/preface.tex
new file mode 100755
index 0000000..b298225
--- /dev/null
+++ b/pypers/preface.tex
@@ -0,0 +1,832 @@
+\documentclass[11pt,english]{book}
+\usepackage{babel}
+\usepackage{shortvrb}
+\usepackage[latin1]{inputenc}
+\usepackage{tabularx}
+\usepackage{longtable}
+\setlength{\extrarowheight}{2pt}
+\usepackage{amsmath}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{multirow}
+\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+\usepackage[a4paper,margin=2cm]{geometry}
+%% generator Docutils: http://docutils.sourceforge.net/
+\newlength{\admonitionwidth}
+\setlength{\admonitionwidth}{0.9\textwidth}
+\newlength{\docinfowidth}
+\setlength{\docinfowidth}{0.9\textwidth}
+\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
+\newenvironment{optionlist}[1]
+{\begin{list}{}
+ {\setlength{\labelwidth}{#1}
+ \setlength{\rightmargin}{1cm}
+ \setlength{\leftmargin}{\rightmargin}
+ \addtolength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}{\optionlistlabel}}
+}{\end{list}}
+% begin: floats for footnotes tweaking.
+\setlength{\floatsep}{0.5em}
+\setlength{\textfloatsep}{\fill}
+\addtolength{\textfloatsep}{3em}
+\renewcommand{\textfraction}{0.5}
+\renewcommand{\topfraction}{0.5}
+\renewcommand{\bottomfraction}{0.5}
+\setcounter{totalnumber}{50}
+\setcounter{topnumber}{50}
+\setcounter{bottomnumber}{50}
+% end floats for footnotes
+% some commands, that could be overwritten in the style file.
+\newcommand{\rubric}[1]{\section*{~\hfill {\it #1} \hfill ~}}
+% end of "some commands"
+\input{/mnt/exp/MyDocs/pypers/style.tex}
+\title{OBJECT ORIENTED PROGRAMMING IN PYTHON}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={OBJECT ORIENTED PROGRAMMING IN PYTHON},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Version}: &
+ 0.5 \\
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{E-mail}: &
+ mis6@pitt.edu \\
+\textbf{Home-page}: &
+ http://www.phyast.pitt.edu/~micheles/ \\
+\textbf{Disclaimer}: &
+ I release this book to the general public.
+It can be freely distributed if unchanged.
+As usual, I don't give any warranty: while I have tried hard to ensure the
+correctness of what follows, I disclaim any responsability in case of
+errors . Use it at your own risk and peril ! \\
+\end{tabularx}
+\end{center}
+
+
+
+\tableofcontents
+
+\bigskip
+\setcounter{chapter}{-1}
+
+%___________________________________________________________________________
+
+\hypertarget{preface}{}
+\chapter{Preface}
+\begin{quote}
+\begin{flushleft}
+\emph{There~is~only~one~way~to~learn:~trough~examples}
+\end{flushleft}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-philosophy-of-this-book}{}
+\section{The philosophy of this book}
+
+This book is written with the intent to help the programmer going trough
+the fascinating concepts of Object Oriented Programming (OOP), in their
+Python incarnation. Notice that I say to help, not to teach. Actually,
+I do not think that a book can teach OOP or any other non-trivial matter
+in Computer Science or other disciplines. Only the
+practice can teach: practice, then practice, and practice again.
+You must learn yourself from your experiments, not from the books.
+Nevertheless, books are useful. They cannot teach, but they can help.
+They should give you new ideas that you was not thinking about, they should
+show tricks you do not find in the manual, and in general they should be of
+some guidance in the uphill road to knowledge. That is the philosophy
+of this book. For this reason
+
+1. It is not comprehensive, not systematic;
+it is intended to give ideas and basis: from
+that the reader is expected to cover the missing part on his own,
+browsing the documentation, other sources and other books, and finally
+the definite autority, the source itself.
+
+2. It will not even try to teach the \emph{best} practices. I will show what you can
+do with Python, not what you ``should'' do. Often I will show solutions that are
+not recommended. I am not a mammy saying this is
+good, this is bad, do this do that.
+
+3. You can only learn from your failures. If you think ``it should work, if I do
+X and Y'' and it works, then you have learned nothing new.
+You have merely verified
+that your previous knowledge was correct, but you haven't create a new
+knowledge. On the other hand, when you think ``it should work, if I do
+X and Y'' and it doesn't, then you have learned that your previous knowlegde
+was wrong or incomplete, and you are forced to learn something new to
+overcome the difficulty. For this reason, I think it is useful to report
+not only how to do something, but also to report how not to do something,
+showing the pitfalls of wrong approaches.
+
+That's in my opinion is the goal of a good book. I don't know if have
+reached this goal or not (the decision is up to the reader), but at least
+I have tried to follow these guidelines.
+
+Moreover, this is not a book on OOP,
+it is a book on OOP \emph{in Python}.
+
+In other words, the point of view of this book is not
+to emphasize general topics of OOP that are exportable to other languages,
+but exactly the opposite: I want to emphasize specific techniques that one
+can only use in Python, or that are difficult to translate to other
+languages. Moreover, I will not provide comparisons with other
+languages (except for the section ``Why Python?'' in this introduction and
+in few selected other places),
+in order to keep the discussion focused.
+
+This choice comes from the initial motivation for this book, which was
+to fulfill a gap in the (otherwise excellent) Python documentation.
+The problem is that the available documentation still lacks an accessible
+reference of the new Python 2.2+ object-oriented features.
+Since myself I have learned Python and OOP from scratch,
+I have decided to write this book in order to fill that gap and
+help others.
+
+The emphasis in this book is not in giving
+solutions to specific problems (even if most of the recipes of this book
+can easily be tailored to solve real life concrete problems), it is in
+teaching how does it work, why it does work in some cases and why does
+not work in some other cases. Avoiding too specific problems has an
+additional bonus, since it allows me to use \emph{short} examples (the majority
+of the scripts presented here is under 20-30 lines) which I think are
+best suited to teach a new matter [\hyperlink{id2}{1}] . Notice, however, that whereas
+the majority of the scripts in this book are short, it is also true
+that they are pretty \emph{dense}. The density is due to various reasons:
+\newcounter{listcnt1}
+\begin{list}{\arabic{listcnt1}.}
+{
+\usecounter{listcnt1}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item
+I am defining a lot of helper functions and classes, that are
+reused and enhanced during all the book.
+
+\item
+I am doing a strong use of inheritance, therefore a script at the
+end of the book can inherits from the classes defined through all
+the book;
+
+\item
+A ten line script involving metaclasses can easily perform the equivalent
+of generating hundreds of lines of code in a language without metaclasses
+such as Java or C++.
+
+\end{list}
+
+To my knowledge, there are no other books covering the same topics with
+the same focus (be warned, however, that I haven't read so many Python
+books ;-). The two references that come closest to the present book are
+the \texttt{Python Cookbook} by Alex Martelli and David Ascher, and
+Alex Martelli's \texttt{Python in a Nutshell}. They are quite recent books and
+therefore it covers (in much less detail) some of the 2.2 features that are
+the central topics to this book.
+However, the Cookbook reserves to OOP only one chapter and has a quite
+different philosophy from the present book, therefore there is
+practically no overlapping. Also \texttt{Python in a Nutshell} covers
+metaclasses in few pages, whereas half of this book is essentially
+dedied to them. This means that you can read both ;-)
+\begin{figure}[b]\hypertarget{id2}[1]
+Readers that prefer the opposite philosophy of using longer,
+real life-like, examples, have already the excellent ``Dive into
+Python'' book \href{http://diveintopython.org/}{http://diveintopython.org/} at their disposal. This is
+a very good book that I certainly recommend to any (experienced)
+Python programmer; it is also freely available (just like this ;-).
+However, the choice of arguments is quite different and there is
+essentially no overlap between my book and ``Dive into Python''
+(therefore you can read both ;-).
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{for-who-this-book-in-intended}{}
+\section{For who this book in intended}
+
+I have tried to make this tutorial useful to a large public of Pythonistas,
+i.e. both people with no previous experience of Object Oriented Programming
+and people with experience on OOP, but unfamiliar with the most
+recent Python 2.2-2.3 features (such as attribute descriptors,
+metaclasses, change of the MRO in multiple inheritance, etc).
+However, this is not a book for beginners: the non-experienced reader should
+check (at least) the Internet sites www.python.org/newbies.com and
+www.awaretek.com, that provide a nice collection of resources for Python
+newbies.
+
+These are my recommendations for the reader, according to her/his level:
+\newcounter{listcnt2}
+\begin{list}{\arabic{listcnt2}.}
+{
+\usecounter{listcnt2}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item
+If you are an absolute beginner, with no experience on programming,
+this book is \emph{not} for you (yet ;-). Go to
+\href{http://www.python.org/doc/Newbies.html}{http://www.python.org/doc/Newbies.html} and read one of the introductive
+texts listed there, then come back here. I recommend ``How to Think Like
+a Computer Scientist'', available for free on the net (see
+\href{http://www.ibiblio.org/obp/thinkCSpy/}{http://www.ibiblio.org/obp/thinkCSpy/}); I found it useful myself when
+I started learning Python; be warned, however, that it refers to the rather
+old Python version 1.5.2. There are also excellent books
+on the market (see \href{http://www.awaretek.com/plf.html}{http://www.awaretek.com/plf.html}).
+\href{http://www.uselesspython.com/}{http://www.uselesspython.com/} is a good resource to find recensions
+about available Python books. For free books, look at
+\href{http://www.tcfb.com/freetechbooks/bookphyton.html}{http://www.tcfb.com/freetechbooks/bookphyton.html} .
+This is \emph{not} another Python tutorial.
+
+\item
+If you know already (at least) another programming language, but you don't
+know Python, then this book is \emph{not} for you (again ;-). Read the FAQ, the
+Python Tutorial and play a little with the Standard Library (all this
+material can be downloaded for free from \href{http://www.python.org}{http://www.python.org}), then
+come back here.
+
+\item
+If you have passed steps 1 and 2, and you are confortable with Python
+at the level of simple procedural programming, but have no clue about
+objects and classes, \emph{then} this book is for you. Read this book till
+the end and your knowledge of OOP will pass from zero to a quite advanced
+level (hopefully). Of course, you will have to play with the code in
+this book and write a lot of code on your own, first ;-)
+
+\item
+If you are confortable with Python and you also known OOP from other
+languages or from earlier version of Python, then this book is for
+you, too: you are ready to read the more advanced chapters.
+
+\item
+If you are a Python guru, then you should read the book, too. I expect
+you will find the errors and send me feedback, helping me to improve
+this tutorial.
+
+\end{list}
+
+
+%___________________________________________________________________________
+
+\hypertarget{about-the-scripts-in-this-book}{}
+\section{About the scripts in this book}
+
+All the scripts in this book are free. You are expected to play
+with them, to modify them and to improve them.
+
+In order to facilitate the extraction of the scripts from the main text, both
+visually for the reader and automatically for Python, I use the
+convention of sandwiching the body of the example scripts in blocks like this
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}myfirstscript.py{$>$}}\\
+\mbox{}\\
+\mbox{print~"Here~Starts~the~Python~Way~to~Object~Oriented~Programming~!"}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/myfirstscript.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+You may extract the source of this script with the a Python program
+called ``test.py'' and provided in the distribution. Simply give the
+following command:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python~test.py~myfirstscript.py}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+This will create a file called ``myfirstscript.py'', containing the
+source of \texttt{myfirstscript.py}; moreover it will execute the script
+and write its output in a file called ``output.txt''. I have tested
+all the scripts in this tutorial under Red Hat Linux 7.x and
+Windows 98SE. You should not have any problem in running them,
+but if a problem is there, ``test.py'' will probably discover it,
+even if, unfortunately, it will not provide the solution :-(.
+Notice that test.py requires Python 2.3+ to work, since most of
+the examples in this book heavily depends on the new features
+introduced in Python 2.2-2.3. Since the installation of Python
+2.3 is simple, quick and free, I think I am requiring to my readers
+who haven't upgraded yet a very little effort. This is well worth
+the pain since Python 2.3 fixes few bugs of 2.2 (notably in the subject of
+attribute descriptors and the \texttt{super} built-in) that makes
+
+You may give more arguments to test.py, as in this example:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python~test.py~myfirstscript.py~mysecondscript.py}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output of both scripts will still be placed in the file ``output.txt''.
+Notice that if you give an argument which is not the name of a script in the
+book, it will be simply ignored. Morever, if you will not give any argument,
+``test.py'' will automatically executes all the tutorial scripts, writing their
+output in ``output.txt'' [\hyperlink{id4}{2}] . You may want to give a look at this file, once
+you have finished the tutorial. It also contains the source code of
+the scripts, for better readability.
+
+Many examples of this tutorial depend on utility functions defined
+in a external module called \texttt{oopp} (\texttt{oopp} is an obvious abbreviation
+for the title of the tutorial). The module \texttt{oopp} is automatically generated
+by ``test.py'', which works by extracting from the tutorial
+text blocks of code of the form \texttt{{\#}{$<$}oopp.py{$>$} something {\#}{$<$}/oopp.py{$>$}}
+and saving them in a file called ``oopp.py''.
+Let me give an example. A very recent enhancement to Python (in
+Python 2.3) has been the addition of a built-in boolean type with
+values True and False:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python}\\
+\mbox{Python~2.3a1~({\#}1,~Jan~~6~2003,~10:31:14)}\\
+\mbox{[GCC~2.96~20000731~(Red~Hat~Linux~7.2~2.96-108.7.2)]~on~linux2}\\
+\mbox{Type~"help",~"copyright",~"credits"~or~"license"~for~more~information.}\\
+\mbox{{$>$}{$>$}{$>$}~1+1==2}\\
+\mbox{True}\\
+\mbox{{$>$}{$>$}{$>$}~1+1==3}\\
+\mbox{False}\\
+\mbox{{$>$}{$>$}{$>$}~type(True)}\\
+\mbox{{$<$}type~'bool'{$>$}}\\
+\mbox{{$>$}{$>$}{$>$}~type(False)}\\
+\mbox{{$<$}type~'bool'{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+However, previous version of Python use the integers 1 and 0 for
+True and False respectively.
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\$}~python}\\
+\mbox{Python~2.2~({\#}1,~Apr~12~2002,~15:29:57)}\\
+\mbox{[GCC~2.96~20000731~(Red~Hat~Linux~7.2~2.96-109)]~on~linux2}\\
+\mbox{Type~"help",~"copyright",~"credits"~or~"license"~for~more~information.}\\
+\mbox{{$>$}{$>$}{$>$}~1+1==2}\\
+\mbox{1}\\
+\mbox{{$>$}{$>$}{$>$}~1+1==3~}\\
+\mbox{0}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Following the 2.3 convension, in this tutorial I will use the names
+\texttt{True} and \texttt{False} to denotes the numbers 1 and 0 respectively.
+This is automatic in Python 2.2.1+, but not in Python 2.2. Therefore,
+for sake of compatibility, it is convenient to set the values \texttt{True}
+and \texttt{False} in our utility module:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}oopp.py{$>$}}\\
+\mbox{}\\
+\mbox{import~{\_}{\_}builtin{\_}{\_}}\\
+\mbox{try:~}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.True~~~{\#}look~if~True~is~already~defined}\\
+\mbox{except~AttributeError:~{\#}~if~not~add~True~and~False~to~the~builtins}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.True~=~1}\\
+\mbox{~~~~{\_}{\_}builtin{\_}{\_}.False~=~0}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/oopp.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+Here there is an example of usage:
+\begin{quote}
+\begin{ttfamily}\begin{flushleft}
+\mbox{{\#}{$<$}mysecondscript.py{$>$}}\\
+\mbox{}\\
+\mbox{import~oopp}\\
+\mbox{print~"True~=",True,}\\
+\mbox{print~"False~=",False}\\
+\mbox{}\\
+\mbox{{\#}{$<$}/mysecondscript.py{$>$}}
+\end{flushleft}\end{ttfamily}
+\end{quote}
+
+The output is ``True = 1 False = 0'' under Python 2.2 and
+``True = True False = False'' under Python 2.3+.
+\begin{figure}[b]\hypertarget{id4}[2]
+``test.py'', invoked without arguments, does not create '.py' files,
+since I don't want to kludge the distribution with dozens of ten-line
+scripts. I expect you may want to save only few scripts as standalone
+programs, and cut and paste the others.
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{conventions-used-in-this-book}{}
+\section{Conventions used in this book}
+
+Python expressions are denoted with monospaced fonts when in the text.
+Sections marked with an asterisk can be skipped in a first reading.
+Typically they have the purpose of clarifying some subtle point and
+are not needed for the rest of the book. These sections are intended
+for the advanced reader, but could confuse the beginner.
+An example is the section about the difference between methods and
+functions, or the difference between the inheritance constraint and
+the metaclass constraint.
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\chapter{Introduction}
+\begin{quote}
+\begin{flushleft}
+\emph{A~language~that~doesn't~affect~the~way~you~think~about~programming,~\\
+is~not~worth~knowing.}~--~Alan~Perlis
+\end{flushleft}
+\end{quote}
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-oop}{}
+\section{Why OOP ?}
+
+I guess some of my readers, like me, have started programming in the mid-80's,
+when traditional (i.e. non object-oriented) Basic and Pascal where popular as
+first languages. At the time OOP was not as pervasive in software development
+how it is now, most of the mainstream languages were non-object-oriented and
+C++ was just being released. That was a time when the transition from
+spaghetti-code to structured code was already well accomplished, but
+the transition from structured programming to (the first phase of)
+OOP was at the beginning.
+
+Nowaydays, we live in a similar time of transition . Today, the transition
+to (the first phase of) OOP is well accomplished and essentially all
+mainstream
+languages support some elementary form of OOP. To be clear, when I say
+mainstream langauges, I have in mind Java and C++: C is a remarkable
+exception to the rule, since it is mainstream but not object-oriented.
+
+However, both Java an C++ (I mean standard Java and C++, not special
+extension like DTS C++, that have quite powerful object oriented features)
+are quite poor object-oriented languages: they provides only the most
+elementary aspects of OOP, the features of the \emph{first phase} of OOP.
+
+Hence, today the transition to the \emph{second phase} of OOP is only at the
+beginning, i.e mainstream language are not yet really OO, but they will
+become OOP in the near future.
+
+By second phase of OOP I mean the phase in which the primary
+objects of concern for the programmer are no more the objects, but the
+metaobjects. In elementary OOP one works on objects, which have attributes
+and methods (the evolution of old-fashioned data and functions) defined
+by their classes; in the second phase of OOP one works on classes
+which behavior is described by metaclasses. We no more modify objects
+trough classes: nowadays we modify classes and class hierarchies
+through metaclasses and multiple inheritance.
+
+It would be tempting to represent the history of programming in the last
+quarter of century with an evolutionary table like that:
+
+\begin{longtable}[c]{|p{0.28\linewidth}|p{0.23\linewidth}|p{0.26\linewidth}|p{0.08\linewidth}|}
+\hline
+\textbf{
+{\~{ }}1975
+} & \textbf{
+{\~{ }}1985
+} & \textbf{
+{\~{ }}1995
+} & \textbf{
+{\~{ }}2005
+} \\ \hline
+\endhead
+%[visit_tbody]
+
+procedural programming
+ &
+OOP1
+ &
+OOP2
+ &
+?
+ \\ \hline
+
+data,functions
+ &
+objects,classes
+ &
+classes,metaclasses
+ &
+?
+ \\ \hline
+%[depart_tbody]
+\end{longtable}
+
+The problem is that table would be simply wrong, since in truth
+Smalltalk had metaclasses already 25 years ago! And also Lisp
+had \emph{in nuce} everything a long \emph{long} time ago.
+The truth is that certains languages where too much ahead of their
+time ;-)
+
+Therefore, today we already have all the ideas
+and the conceptual tools to go beyond the first phase of OOP
+(they where invented 20-30 years ago), nevertheless those ideas are
+not yet universally known, nor implemented in mainstream languages.
+
+Fortunately, there are good languages
+where you can access the bonus of the second phase of OOP (Smalltalk, CLOS,
+Dylan, ...): unfortunately
+most of them are academic and/or little known in the real world
+(often for purely commercial reasons, since typically languages are not
+chosen accordingly to their merits, helas!). Python is an exception to this
+rule, in the sense that it is an eminently practical language (it started
+as a scripting language to do Operating System administrative jobs),
+which is relatively known and used in that application niche (even if some
+people \emph{wrongly} think that should not be used for 'serious' things).
+
+There are various reasons why most mainstream languages are rather
+poor languages, i.e. underfeatured languages (as Java) or powerful, but too
+tricky to use, as C++. Some are good reasons (for instance \emph{efficiency}: if
+efficiency is the first concern, then poor languages can be much
+better suited to the goal: for instance Fortran for number crunching
+and C for system programming), some are less good (economical
+monopoly). There is nothing to do against these reasons: if you
+need efficiency, or if you are forced to use a proprietary language
+because it is the language used by your employer. However, if you
+are free from these restrictions, there is another reason why you
+could not choose to use a poweful language. The reason is that,
+till now, programmers working in the industrial world mostly had simple
+problems (I mean conceptually simple problems). In order to solve
+simple problems one does not need a powerful language, and the effort
+spent in learning it is not worth.
+
+However, nowadays the situations has changed. Now, with Internet and graphics
+programming everywhere, and object-oriented languages so widespread,
+now it is the time when actually people \emph{needs} metaprogramming, the
+ability to changing classes and programs. Now everybody is programming
+in the large.
+
+In this situation, it is justified to spend some time to learn better
+way of programming. And of course, it is convenient to start from
+the language with the flattest learning curve of all.
+
+
+%___________________________________________________________________________
+
+\hypertarget{why-python}{}
+\section{Why Python ?}
+\begin{quote}
+\begin{flushleft}
+\emph{In~many~ways,~it's~a~dull~language,~borrowing~solid~old~concepts~from~~\\
+many~other~languages~{\&}~styles:~~boring~syntax,~unsurprising~semantics,~\\
+few~automatic~coercions,~etc~etc.~~But~that's~one~of~the~things~I~like~\\
+about~it.}~~--Tim~Peters~on~Python,~16~Sep~93
+\end{flushleft}
+\end{quote}
+
+If you are reading this book, I assume you already have some experience
+with Python. If this is the case, you already know the obvious advantages
+of Python such as readability, easy of use and short development time.
+Nevertheless, you could only have used Python as a fast and simple
+scripting language. If you are in this situation, then your risk to
+have an incorrect opinion on the language like ``it is a nice little
+language, but too simple to be useful in 'real' applications''. The
+truth is that Python is designed to be \emph{simple}, and actually it
+is; but by no means it is a ``shallow'' language. Actually, it goes
+quite \emph{deep}, but it takes some time to appreciate this fact.
+
+Let me contrast Python with Lisp, for instance. From the beginning,
+Lisp was intended to be a language for experts, for people with difficult
+problems to solve. The first
+users of Lisp were academicians, professors of CS and scientists.
+On the contrary, from the beginning Python
+was intended to be language for everybody (Python predecessor was ABC,
+a language invented to teach CS to children). Python makes great a first
+language for everybody, whereas Lisp would require especially
+clever and motivated students (and we all know that there is lack
+of them ;-)
+
+From this difference of origins, Python inherits an easy to learn syntax,
+whereas Lisp syntax is horrible for the beginner (even if not as
+horrible as C++ syntax ;-)
+\begin{quote}
+\begin{flushleft}
+\emph{Macros~are~a~powerful~extension~to~weak~languages.~\\
+Powerful~languages~don't~need~macros~by~definition.}~~\\
+--~Christian~Tismer~on~c.l.p.~(referring~to~C)
+\end{flushleft}
+\end{quote}
+
+Despite the differences, Python borrows quite a lot from Lisp and it
+is nearly as expressive as it (I say nearly since Python is
+not as powerful as Lisp: by tradition, Lisp has always been on the top of
+hierarchy of programming language with respect to power of abstraction).
+It is true that Python lacks some powerful Lisp features: for instance
+Python object model lacks multiple dispatching (for the time being ;-)
+and the language lacks Lisp macros (but this unlikely to change in the
+near future since Pythonistas see the lack of macro as a Good Thing [\hyperlink{id6}{3}]):
+nevertheless, the point is that Python is much \emph{much} easier to learn.
+You have (nearly) all the power, but without the complexity.
+
+One of the reasons, is that Python
+try to be as \emph{less} innovative as
+possible: it takes the proven good things from others, more innovative
+languages, and avoids their pitfalls. If you are an experienced
+programmer , it will be even easier to you to learn Python, since
+there is more or less nothing which is really original to Python.
+For instance:
+\newcounter{listcnt3}
+\begin{list}{\arabic{listcnt3}.}
+{
+\usecounter{listcnt3}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item
+the object model is took from languages that are good at it, such
+as Smalltalk;
+
+\item
+multiple inheritance has been modeled from languages good in it. such
+as CLOS and Dylan;
+
+\item
+regular expression follows the road opened by Perl;
+
+\item
+functional features are borrowed from functional languages;
+
+\item
+the idea of documentation strings come from Lisp;
+
+\item
+list comprehension come from Haskell;
+
+\item
+iterators and generators come from Icon;
+
+\item
+etc. etc. (many other points here)
+
+\end{list}
+
+I thinks the really distinctive feature of Python with respect to
+any other serious language I know, is that Python is \emph{easy}. You have the
+power (I mean power in conceptual sense, not computational power: in
+the sense of computational power the best languages are
+non-object-oriented ones)
+of the most powerful languages with a very little investement.
+In addition to that, Python has a relatively large user base
+(as compared to Smalltalk or Ruby, or the various fragmented Lisp
+communities). Of course,
+there is quite a difference between the user base of Python with
+respect to the user base of, let say, VisualBasic or Perl. But
+I would never take in consideration VisualBasic for anything serious,
+whereas Perl is too ugly for my taste ;-).
+Finally, Python is \emph{practical}. With this I mean the fact that
+Python has libraries that
+allow the user to do nearly everything, since you can access all the C/C++
+libraries with little or no effort, and all the Java libraries, though the
+Python implementation known as Jython. In particular, one has the choice
+between many excellent GUI's trough PyQt, wxPython, Tkinter, etc.
+
+Python started as an Object Oriented Programming
+Languages from the beginning, nevertheless is was never intended to be
+a \emph{pure} OOPL as SmallTalk or, more recently, Ruby. Python is a
+\emph{multiparadigm}
+language such a Lisp, that you choose your programming style according
+to your problem: spaghetti-code, structured programming, functional
+programming, object-oriented programming are all supported. You can
+even write bad code in Python, even if it is less simple than in other
+languages ;-). Python is a language which has quite evolved in its twelve
+years of life (the first public release was released in February 1991)
+and many new features have been integrated in the language with time.
+In particular, Python 2.2 (released in 2002) was a major breakthrough
+in the history of the language
+for what concerns support to Object Oriented Programming (OOP).
+Before the 2.2 revolution, Python Object
+Orientation was good; now it is \emph{excellent}. All the fundamental features
+of OOP, including pretty sophisticated ones, as metaclasses and multiple
+inheritance, have now a very good support (the only missing thing is
+multiple dispatching).
+\begin{figure}[b]\hypertarget{id6}[3]
+Python lacks macros for an intentional design choice: many people
+in the community (including Guido itself) feel that macros are
+``too powerful''. If you give the user the freedom to create her
+own language, you must face at least three problems: i) the risk
+to split the original language in dozens of different dialects;
+ii) in collaborative projects, the individual programmer must
+spend an huge amount of time and effort would be spent in learning
+macro systems written by others; iii) not all users are good
+language designers: the programmer will have to fight with badly
+designed macro systems. Due to these problems, it seems unlikely
+that macros will be added to Python in the future.
+\end{figure}
+\begin{figure}[b]\hypertarget{id7}[4]
+For a good comparison between Python and Lisp I remind the reader to
+the excellent Peter Norvig's article in
+\href{http://www.norvig.com/python-lisp.html}{http://www.norvig.com/python-lisp.html}
+\end{figure}
+
+
+%___________________________________________________________________________
+
+\hypertarget{further-thoughts}{}
+\section{Further thoughts}
+
+Actually, the principal reasons why I begun studying
+Python was the documentation and the newsgroup: Python has an outstanding
+freely available documentation and an incredibly helpful newsgroup that
+make extremely easy to learn the language. If I had found a comparable
+free documentation/newsgroup for C++ or Lisp, I would have studied that
+languages instead.
+
+Unfortunately, the enormous development at the software level, had no
+correspondence with with an appropriate development of documentation.
+As a consequence, the many beatiful, powerful and extremely \emph{useful}
+new features of Python 2.2+ object orientation are mostly remained
+confined to developers and power users: the average Python programmer
+has remained a little a part from the rapid development and she
+\emph{wrongly} thinks she has no use for the new features. There have
+also been \emph{protestations} of the users against developers of the
+kind ``please, stop adding thousands of complicated new extensions
+to the language for which we have no use'' !
+
+Extending a language is always a delicate thing to do, for a whole
+bunch of reasons:
+\newcounter{listcnt4}
+\begin{list}{\arabic{listcnt4}.}
+{
+\usecounter{listcnt4}
+\setlength{\rightmargin}{\leftmargin}
+}
+\item
+once one extension is done, it is there \emph{forever}.
+
+\end{list}
+
+My experience has been the following.
+
+When I first read about metaclasses, in Guido's essay
+``Unifying types and classes in Python 2.2'', I thought ``Wow,
+classes of classes, cool concept, but how useful is it?
+Are metaclasses really providing some new functionality?
+What can I do with metaclasses that I cannot do without?''
+
+Clearly, in these terms, the question is rather retorical, since in principle
+any Turing-complete programming languages contains all the features provided
+by metaclasses. Python metaclasses themselves are implemented in C, that has
+no metaclasses. Therefore, my real question was not ``What can I do
+with metaclasses that I cannot do without?'' but ``How big is the convenience
+provided by metaclasses, with respect to my typical applications?''.
+
+The answer depends on the kind of problem you are considering. For certain
+classes of problems it can be \emph{very} large, as I will show in this and in
+the next chapters.
+
+I think the biggest advantage of metaclasses is \emph{elegance}. Altough it
+is true that most of what you can do with metaclasses, can be done without
+metaclasses, not using metaclasses can result in a much \emph{uglier} solution.
+
+One needs difficult problems in order to appreciate the advantage
+of powerful methods.
+
+If all you need is to write few scripts for copying two or three files,
+there is no point in learning OOP.On the other hand, if you only
+write simple programs where you define only one of two classes, there
+is no point in using metaclasses. Metaclasses becomes relevant only
+when you have many classes, whole classes of classes with similar
+features that you want to modify.
+
+In this sense, metaprogramming is for experts only, i.e. with people
+with difficult problems. The point however, is that nowaydays,
+many persons have difficult problems.
+
+Finally, let me conclude this preface by recalling the
+gist of Python wisdom.
+\begin{quote}
+\begin{verbatim}>>> import this
+The Zen of Python, by Tim Peters
+.
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it.
+Although that way may not be obvious at first unless you're Dutch.
+Now is better than never.
+Although never is often better than *right* now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!\end{verbatim}
+\end{quote}
+
+\end{document}
+
diff --git a/pypers/preface.txt b/pypers/preface.txt
new file mode 100755
index 0000000..ea48766
--- /dev/null
+++ b/pypers/preface.txt
@@ -0,0 +1,660 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+OBJECT ORIENTED PROGRAMMING IN PYTHON
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:Version: 0.5
+:Author: Michele Simionato
+:E-mail: mis6@pitt.edu
+:Home-page: http://www.phyast.pitt.edu/~micheles/
+
+:Disclaimer: I release this book to the general public.
+ It can be freely distributed if unchanged.
+ As usual, I don't give any warranty: while I have tried hard to ensure the
+ correctness of what follows, I disclaim any responsability in case of
+ errors . Use it at your own risk and peril !
+
+.. contents::
+
+.. raw:: latex
+
+ \setcounter{chapter}{-1}
+
+Preface
+============
+
+ .. line-block::
+
+ *There is only one way to learn: trough examples*
+
+The philosophy of this book
+---------------------------
+
+This book is written with the intent to help the programmer going trough
+the fascinating concepts of Object Oriented Programming (OOP), in their
+Python incarnation. Notice that I say to help, not to teach. Actually,
+I do not think that a book can teach OOP or any other non-trivial matter
+in Computer Science or other disciplines. Only the
+practice can teach: practice, then practice, and practice again.
+You must learn yourself from your experiments, not from the books.
+Nevertheless, books are useful. They cannot teach, but they can help.
+They should give you new ideas that you was not thinking about, they should
+show tricks you do not find in the manual, and in general they should be of
+some guidance in the uphill road to knowledge. That is the philosophy
+of this book. For this reason
+
+1. It is not comprehensive, not systematic;
+it is intended to give ideas and basis: from
+that the reader is expected to cover the missing part on his own,
+browsing the documentation, other sources and other books, and finally
+the definite autority, the source itself.
+
+2. It will not even try to teach the *best* practices. I will show what you can
+do with Python, not what you "should" do. Often I will show solutions that are
+not recommended. I am not a mammy saying this is
+good, this is bad, do this do that.
+
+
+3. You can only learn from your failures. If you think "it should work, if I do
+X and Y" and it works, then you have learned nothing new.
+You have merely verified
+that your previous knowledge was correct, but you haven't create a new
+knowledge. On the other hand, when you think "it should work, if I do
+X and Y" and it doesn't, then you have learned that your previous knowlegde
+was wrong or incomplete, and you are forced to learn something new to
+overcome the difficulty. For this reason, I think it is useful to report
+not only how to do something, but also to report how not to do something,
+showing the pitfalls of wrong approaches.
+
+That's in my opinion is the goal of a good book. I don't know if have
+reached this goal or not (the decision is up to the reader), but at least
+I have tried to follow these guidelines.
+
+Moreover, this is not a book on OOP,
+it is a book on OOP *in Python*.
+
+In other words, the point of view of this book is not
+to emphasize general topics of OOP that are exportable to other languages,
+but exactly the opposite: I want to emphasize specific techniques that one
+can only use in Python, or that are difficult to translate to other
+languages. Moreover, I will not provide comparisons with other
+languages (except for the section "Why Python?" in this introduction and
+in few selected other places),
+in order to keep the discussion focused.
+
+This choice comes from the initial motivation for this book, which was
+to fulfill a gap in the (otherwise excellent) Python documentation.
+The problem is that the available documentation still lacks an accessible
+reference of the new Python 2.2+ object-oriented features.
+Since myself I have learned Python and OOP from scratch,
+I have decided to write this book in order to fill that gap and
+help others.
+
+The emphasis in this book is not in giving
+solutions to specific problems (even if most of the recipes of this book
+can easily be tailored to solve real life concrete problems), it is in
+teaching how does it work, why it does work in some cases and why does
+not work in some other cases. Avoiding too specific problems has an
+additional bonus, since it allows me to use *short* examples (the majority
+of the scripts presented here is under 20-30 lines) which I think are
+best suited to teach a new matter [#]_ . Notice, however, that whereas
+the majority of the scripts in this book are short, it is also true
+that they are pretty *dense*. The density is due to various reasons:
+
+1. I am defining a lot of helper functions and classes, that are
+ reused and enhanced during all the book.
+
+2. I am doing a strong use of inheritance, therefore a script at the
+ end of the book can inherits from the classes defined through all
+ the book;
+
+3. A ten line script involving metaclasses can easily perform the equivalent
+ of generating hundreds of lines of code in a language without metaclasses
+ such as Java or C++.
+
+To my knowledge, there are no other books covering the same topics with
+the same focus (be warned, however, that I haven't read so many Python
+books ;-). The two references that come closest to the present book are
+the ``Python Cookbook`` by Alex Martelli and David Ascher, and
+Alex Martelli's ``Python in a Nutshell``. They are quite recent books and
+therefore it covers (in much less detail) some of the 2.2 features that are
+the central topics to this book.
+However, the Cookbook reserves to OOP only one chapter and has a quite
+different philosophy from the present book, therefore there is
+practically no overlapping. Also ``Python in a Nutshell`` covers
+metaclasses in few pages, whereas half of this book is essentially
+dedied to them. This means that you can read both ;-)
+
+
+.. [#] Readers that prefer the opposite philosophy of using longer,
+ real life-like, examples, have already the excellent "Dive into
+ Python" book http://diveintopython.org/ at their disposal. This is
+ a very good book that I certainly recommend to any (experienced)
+ Python programmer; it is also freely available (just like this ;-).
+ However, the choice of arguments is quite different and there is
+ essentially no overlap between my book and "Dive into Python"
+ (therefore you can read both ;-).
+
+For who this book in intended
+-----------------------------
+
+I have tried to make this tutorial useful to a large public of Pythonistas,
+i.e. both people with no previous experience of Object Oriented Programming
+and people with experience on OOP, but unfamiliar with the most
+recent Python 2.2-2.3 features (such as attribute descriptors,
+metaclasses, change of the MRO in multiple inheritance, etc).
+However, this is not a book for beginners: the non-experienced reader should
+check (at least) the Internet sites www.python.org/newbies.com and
+www.awaretek.com, that provide a nice collection of resources for Python
+newbies.
+
+These are my recommendations for the reader, according to her/his level:
+
+1. If you are an absolute beginner, with no experience on programming,
+ this book is *not* for you (yet ;-). Go to
+ http://www.python.org/doc/Newbies.html and read one of the introductive
+ texts listed there, then come back here. I recommend "How to Think Like
+ a Computer Scientist", available for free on the net (see
+ http://www.ibiblio.org/obp/thinkCSpy/); I found it useful myself when
+ I started learning Python; be warned, however, that it refers to the rather
+ old Python version 1.5.2. There are also excellent books
+ on the market (see http://www.awaretek.com/plf.html).
+ http://www.uselesspython.com/ is a good resource to find recensions
+ about available Python books. For free books, look at
+ http://www.tcfb.com/freetechbooks/bookphyton.html .
+ This is *not* another Python tutorial.
+
+2. If you know already (at least) another programming language, but you don't
+ know Python, then this book is *not* for you (again ;-). Read the FAQ, the
+ Python Tutorial and play a little with the Standard Library (all this
+ material can be downloaded for free from http://www.python.org), then
+ come back here.
+
+3. If you have passed steps 1 and 2, and you are confortable with Python
+ at the level of simple procedural programming, but have no clue about
+ objects and classes, *then* this book is for you. Read this book till
+ the end and your knowledge of OOP will pass from zero to a quite advanced
+ level (hopefully). Of course, you will have to play with the code in
+ this book and write a lot of code on your own, first ;-)
+
+4. If you are confortable with Python and you also known OOP from other
+ languages or from earlier version of Python, then this book is for
+ you, too: you are ready to read the more advanced chapters.
+
+5. If you are a Python guru, then you should read the book, too. I expect
+ you will find the errors and send me feedback, helping me to improve
+ this tutorial.
+
+About the scripts in this book
+-----------------------------------------------------------------------------
+
+All the scripts in this book are free. You are expected to play
+with them, to modify them and to improve them.
+
+In order to facilitate the extraction of the scripts from the main text, both
+visually for the reader and automatically for Python, I use the
+convention of sandwiching the body of the example scripts in blocks like this
+
+ ::
+
+ #<myfirstscript.py>
+
+ print "Here Starts the Python Way to Object Oriented Programming !"
+
+ #</myfirstscript.py>
+
+You may extract the source of this script with the a Python program
+called "test.py" and provided in the distribution. Simply give the
+following command:
+
+ ::
+
+ $ python test.py myfirstscript.py
+
+This will create a file called "myfirstscript.py", containing the
+source of ``myfirstscript.py``; moreover it will execute the script
+and write its output in a file called "output.txt". I have tested
+all the scripts in this tutorial under Red Hat Linux 7.x and
+Windows 98SE. You should not have any problem in running them,
+but if a problem is there, "test.py" will probably discover it,
+even if, unfortunately, it will not provide the solution :-(.
+Notice that test.py requires Python 2.3+ to work, since most of
+the examples in this book heavily depends on the new features
+introduced in Python 2.2-2.3. Since the installation of Python
+2.3 is simple, quick and free, I think I am requiring to my readers
+who haven't upgraded yet a very little effort. This is well worth
+the pain since Python 2.3 fixes few bugs of 2.2 (notably in the subject of
+attribute descriptors and the ``super`` built-in) that makes
+
+You may give more arguments to test.py, as in this example:
+
+ ::
+
+ $ python test.py myfirstscript.py mysecondscript.py
+
+The output of both scripts will still be placed in the file "output.txt".
+Notice that if you give an argument which is not the name of a script in the
+book, it will be simply ignored. Morever, if you will not give any argument,
+"test.py" will automatically executes all the tutorial scripts, writing their
+output in "output.txt" [#]_ . You may want to give a look at this file, once
+you have finished the tutorial. It also contains the source code of
+the scripts, for better readability.
+
+Many examples of this tutorial depend on utility functions defined
+in a external module called ``oopp`` (``oopp`` is an obvious abbreviation
+for the title of the tutorial). The module ``oopp`` is automatically generated
+by "test.py", which works by extracting from the tutorial
+text blocks of code of the form ``#<oopp.py> something #</oopp.py>``
+and saving them in a file called "oopp.py".
+Let me give an example. A very recent enhancement to Python (in
+Python 2.3) has been the addition of a built-in boolean type with
+values True and False:
+
+ ::
+
+ $ python
+ Python 2.3a1 (#1, Jan 6 2003, 10:31:14)
+ [GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> 1+1==2
+ True
+ >>> 1+1==3
+ False
+ >>> type(True)
+ <type 'bool'>
+ >>> type(False)
+ <type 'bool'>
+
+
+However, previous version of Python use the integers 1 and 0 for
+True and False respectively.
+
+ ::
+
+ $ python
+ Python 2.2 (#1, Apr 12 2002, 15:29:57)
+ [GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-109)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> 1+1==2
+ 1
+ >>> 1+1==3
+ 0
+
+Following the 2.3 convension, in this tutorial I will use the names
+``True`` and ``False`` to denotes the numbers 1 and 0 respectively.
+This is automatic in Python 2.2.1+, but not in Python 2.2. Therefore,
+for sake of compatibility, it is convenient to set the values ``True``
+and ``False`` in our utility module:
+
+ ::
+
+ #<oopp.py>
+
+ import __builtin__
+ try:
+ __builtin__.True #look if True is already defined
+ except AttributeError: # if not add True and False to the builtins
+ __builtin__.True = 1
+ __builtin__.False = 0
+
+ #</oopp.py>
+
+
+Here there is an example of usage:
+
+ ::
+
+ #<mysecondscript.py>
+
+ import oopp
+ print "True =",True,
+ print "False =",False
+
+ #</mysecondscript.py>
+
+The output is "True = 1 False = 0" under Python 2.2 and
+"True = True False = False" under Python 2.3+.
+
+.. [#] "test.py", invoked without arguments, does not create '.py' files,
+ since I don't want to kludge the distribution with dozens of ten-line
+ scripts. I expect you may want to save only few scripts as standalone
+ programs, and cut and paste the others.
+
+Conventions used in this book
+----------------------------------------------------------------------
+
+Python expressions are denoted with monospaced fonts when in the text.
+Sections marked with an asterisk can be skipped in a first reading.
+Typically they have the purpose of clarifying some subtle point and
+are not needed for the rest of the book. These sections are intended
+for the advanced reader, but could confuse the beginner.
+An example is the section about the difference between methods and
+functions, or the difference between the inheritance constraint and
+the metaclass constraint.
+
+Introduction
+===========================================================================
+
+ .. line-block::
+
+ *A language that doesn't affect the way you think about programming,
+ is not worth knowing.* -- Alan Perlis
+
+
+Why OOP ?
+----------------------------
+
+I guess some of my readers, like me, have started programming in the mid-80's,
+when traditional (i.e. non object-oriented) Basic and Pascal where popular as
+first languages. At the time OOP was not as pervasive in software development
+how it is now, most of the mainstream languages were non-object-oriented and
+C++ was just being released. That was a time when the transition from
+spaghetti-code to structured code was already well accomplished, but
+the transition from structured programming to (the first phase of)
+OOP was at the beginning.
+
+Nowaydays, we live in a similar time of transition . Today, the transition
+to (the first phase of) OOP is well accomplished and essentially all
+mainstream
+languages support some elementary form of OOP. To be clear, when I say
+mainstream langauges, I have in mind Java and C++: C is a remarkable
+exception to the rule, since it is mainstream but not object-oriented.
+
+However, both Java an C++ (I mean standard Java and C++, not special
+extension like DTS C++, that have quite powerful object oriented features)
+are quite poor object-oriented languages: they provides only the most
+elementary aspects of OOP, the features of the *first phase* of OOP.
+
+Hence, today the transition to the *second phase* of OOP is only at the
+beginning, i.e mainstream language are not yet really OO, but they will
+become OOP in the near future.
+
+By second phase of OOP I mean the phase in which the primary
+objects of concern for the programmer are no more the objects, but the
+metaobjects. In elementary OOP one works on objects, which have attributes
+and methods (the evolution of old-fashioned data and functions) defined
+by their classes; in the second phase of OOP one works on classes
+which behavior is described by metaclasses. We no more modify objects
+trough classes: nowadays we modify classes and class hierarchies
+through metaclasses and multiple inheritance.
+
+It would be tempting to represent the history of programming in the last
+quarter of century with an evolutionary table like that:
+
+======================== ==================== ====================== =======
+ ~1975 ~1985 ~1995 ~2005
+======================== ==================== ====================== =======
+ procedural programming OOP1 OOP2 ?
+ data,functions objects,classes classes,metaclasses ?
+======================== ==================== ====================== =======
+
+The problem is that table would be simply wrong, since in truth
+Smalltalk had metaclasses already 25 years ago! And also Lisp
+had *in nuce* everything a long *long* time ago.
+The truth is that certains languages where too much ahead of their
+time ;-)
+
+Therefore, today we already have all the ideas
+and the conceptual tools to go beyond the first phase of OOP
+(they where invented 20-30 years ago), nevertheless those ideas are
+not yet universally known, nor implemented in mainstream languages.
+
+Fortunately, there are good languages
+where you can access the bonus of the second phase of OOP (Smalltalk, CLOS,
+Dylan, ...): unfortunately
+most of them are academic and/or little known in the real world
+(often for purely commercial reasons, since typically languages are not
+chosen accordingly to their merits, helas!). Python is an exception to this
+rule, in the sense that it is an eminently practical language (it started
+as a scripting language to do Operating System administrative jobs),
+which is relatively known and used in that application niche (even if some
+people *wrongly* think that should not be used for 'serious' things).
+
+There are various reasons why most mainstream languages are rather
+poor languages, i.e. underfeatured languages (as Java) or powerful, but too
+tricky to use, as C++. Some are good reasons (for instance *efficiency*: if
+efficiency is the first concern, then poor languages can be much
+better suited to the goal: for instance Fortran for number crunching
+and C for system programming), some are less good (economical
+monopoly). There is nothing to do against these reasons: if you
+need efficiency, or if you are forced to use a proprietary language
+because it is the language used by your employer. However, if you
+are free from these restrictions, there is another reason why you
+could not choose to use a poweful language. The reason is that,
+till now, programmers working in the industrial world mostly had simple
+problems (I mean conceptually simple problems). In order to solve
+simple problems one does not need a powerful language, and the effort
+spent in learning it is not worth.
+
+However, nowadays the situations has changed. Now, with Internet and graphics
+programming everywhere, and object-oriented languages so widespread,
+now it is the time when actually people *needs* metaprogramming, the
+ability to changing classes and programs. Now everybody is programming
+in the large.
+
+In this situation, it is justified to spend some time to learn better
+way of programming. And of course, it is convenient to start from
+the language with the flattest learning curve of all.
+
+Why Python ?
+-----------------------------------------------------------------------
+
+ .. line-block::
+
+ *In many ways, it's a dull language, borrowing solid old concepts from
+ many other languages & styles: boring syntax, unsurprising semantics,
+ few automatic coercions, etc etc. But that's one of the things I like
+ about it.* --Tim Peters on Python, 16 Sep 93
+
+If you are reading this book, I assume you already have some experience
+with Python. If this is the case, you already know the obvious advantages
+of Python such as readability, easy of use and short development time.
+Nevertheless, you could only have used Python as a fast and simple
+scripting language. If you are in this situation, then your risk to
+have an incorrect opinion on the language like "it is a nice little
+language, but too simple to be useful in 'real' applications". The
+truth is that Python is designed to be *simple*, and actually it
+is; but by no means it is a "shallow" language. Actually, it goes
+quite *deep*, but it takes some time to appreciate this fact.
+
+Let me contrast Python with Lisp, for instance. From the beginning,
+Lisp was intended to be a language for experts, for people with difficult
+problems to solve. The first
+users of Lisp were academicians, professors of CS and scientists.
+On the contrary, from the beginning Python
+was intended to be language for everybody (Python predecessor was ABC,
+a language invented to teach CS to children). Python makes great a first
+language for everybody, whereas Lisp would require especially
+clever and motivated students (and we all know that there is lack
+of them ;-)
+
+From this difference of origins, Python inherits an easy to learn syntax,
+whereas Lisp syntax is horrible for the beginner (even if not as
+horrible as C++ syntax ;-)
+
+
+ .. line-block::
+
+ *Macros are a powerful extension to weak languages.
+ Powerful languages don't need macros by definition.*
+ -- Christian Tismer on c.l.p. (referring to C)
+
+Despite the differences, Python borrows quite a lot from Lisp and it
+is nearly as expressive as it (I say nearly since Python is
+not as powerful as Lisp: by tradition, Lisp has always been on the top of
+hierarchy of programming language with respect to power of abstraction).
+It is true that Python lacks some powerful Lisp features: for instance
+Python object model lacks multiple dispatching (for the time being ;-)
+and the language lacks Lisp macros (but this unlikely to change in the
+near future since Pythonistas see the lack of macro as a Good Thing [#]_):
+nevertheless, the point is that Python is much *much* easier to learn.
+You have (nearly) all the power, but without the complexity.
+
+One of the reasons, is that Python
+try to be as *less* innovative as
+possible: it takes the proven good things from others, more innovative
+languages, and avoids their pitfalls. If you are an experienced
+programmer , it will be even easier to you to learn Python, since
+there is more or less nothing which is really original to Python.
+For instance:
+
+1. the object model is took from languages that are good at it, such
+ as Smalltalk;
+2. multiple inheritance has been modeled from languages good in it. such
+ as CLOS and Dylan;
+3. regular expression follows the road opened by Perl;
+4. functional features are borrowed from functional languages;
+5. the idea of documentation strings come from Lisp;
+6. list comprehension come from Haskell;
+7. iterators and generators come from Icon;
+8. etc. etc. (many other points here)
+
+I thinks the really distinctive feature of Python with respect to
+any other serious language I know, is that Python is *easy*. You have the
+power (I mean power in conceptual sense, not computational power: in
+the sense of computational power the best languages are
+non-object-oriented ones)
+of the most powerful languages with a very little investement.
+In addition to that, Python has a relatively large user base
+(as compared to Smalltalk or Ruby, or the various fragmented Lisp
+communities). Of course,
+there is quite a difference between the user base of Python with
+respect to the user base of, let say, VisualBasic or Perl. But
+I would never take in consideration VisualBasic for anything serious,
+whereas Perl is too ugly for my taste ;-).
+Finally, Python is *practical*. With this I mean the fact that
+Python has libraries that
+allow the user to do nearly everything, since you can access all the C/C++
+libraries with little or no effort, and all the Java libraries, though the
+Python implementation known as Jython. In particular, one has the choice
+between many excellent GUI's trough PyQt, wxPython, Tkinter, etc.
+
+Python started as an Object Oriented Programming
+Languages from the beginning, nevertheless is was never intended to be
+a *pure* OOPL as SmallTalk or, more recently, Ruby. Python is a
+*multiparadigm*
+language such a Lisp, that you choose your programming style according
+to your problem: spaghetti-code, structured programming, functional
+programming, object-oriented programming are all supported. You can
+even write bad code in Python, even if it is less simple than in other
+languages ;-). Python is a language which has quite evolved in its twelve
+years of life (the first public release was released in February 1991)
+and many new features have been integrated in the language with time.
+In particular, Python 2.2 (released in 2002) was a major breakthrough
+in the history of the language
+for what concerns support to Object Oriented Programming (OOP).
+Before the 2.2 revolution, Python Object
+Orientation was good; now it is *excellent*. All the fundamental features
+of OOP, including pretty sophisticated ones, as metaclasses and multiple
+inheritance, have now a very good support (the only missing thing is
+multiple dispatching).
+
+.. [#]
+ Python lacks macros for an intentional design choice: many people
+ in the community (including Guido itself) feel that macros are
+ "too powerful". If you give the user the freedom to create her
+ own language, you must face at least three problems: i) the risk
+ to split the original language in dozens of different dialects;
+ ii) in collaborative projects, the individual programmer must
+ spend an huge amount of time and effort would be spent in learning
+ macro systems written by others; iii) not all users are good
+ language designers: the programmer will have to fight with badly
+ designed macro systems. Due to these problems, it seems unlikely
+ that macros will be added to Python in the future.
+
+.. [#]
+ For a good comparison between Python and Lisp I remind the reader to
+ the excellent Peter Norvig's article in
+ http://www.norvig.com/python-lisp.html
+
+Further thoughts
+---------------------------------------------------------------------------
+
+Actually, the principal reasons why I begun studying
+Python was the documentation and the newsgroup: Python has an outstanding
+freely available documentation and an incredibly helpful newsgroup that
+make extremely easy to learn the language. If I had found a comparable
+free documentation/newsgroup for C++ or Lisp, I would have studied that
+languages instead.
+
+Unfortunately, the enormous development at the software level, had no
+correspondence with with an appropriate development of documentation.
+As a consequence, the many beatiful, powerful and extremely *useful*
+new features of Python 2.2+ object orientation are mostly remained
+confined to developers and power users: the average Python programmer
+has remained a little a part from the rapid development and she
+*wrongly* thinks she has no use for the new features. There have
+also been *protestations* of the users against developers of the
+kind "please, stop adding thousands of complicated new extensions
+to the language for which we have no use" !
+
+Extending a language is always a delicate thing to do, for a whole
+bunch of reasons:
+
+1. once one extension is done, it is there *forever*.
+
+
+My experience has been the following.
+
+When I first read about metaclasses, in Guido's essay
+"Unifying types and classes in Python 2.2", I thought "Wow,
+classes of classes, cool concept, but how useful is it?
+Are metaclasses really providing some new functionality?
+What can I do with metaclasses that I cannot do without?"
+
+Clearly, in these terms, the question is rather retorical, since in principle
+any Turing-complete programming languages contains all the features provided
+by metaclasses. Python metaclasses themselves are implemented in C, that has
+no metaclasses. Therefore, my real question was not "What can I do
+with metaclasses that I cannot do without?" but "How big is the convenience
+provided by metaclasses, with respect to my typical applications?".
+
+The answer depends on the kind of problem you are considering. For certain
+classes of problems it can be *very* large, as I will show in this and in
+the next chapters.
+
+I think the biggest advantage of metaclasses is *elegance*. Altough it
+is true that most of what you can do with metaclasses, can be done without
+metaclasses, not using metaclasses can result in a much *uglier* solution.
+
+
+One needs difficult problems in order to appreciate the advantage
+of powerful methods.
+
+
+If all you need is to write few scripts for copying two or three files,
+there is no point in learning OOP.On the other hand, if you only
+write simple programs where you define only one of two classes, there
+is no point in using metaclasses. Metaclasses becomes relevant only
+when you have many classes, whole classes of classes with similar
+features that you want to modify.
+
+In this sense, metaprogramming is for experts only, i.e. with people
+with difficult problems. The point however, is that nowaydays,
+many persons have difficult problems.
+
+Finally, let me conclude this preface by recalling the
+gist of Python wisdom.
+
+ >>> import this
+ The Zen of Python, by Tim Peters
+ .
+ Beautiful is better than ugly.
+ Explicit is better than implicit.
+ Simple is better than complex.
+ Complex is better than complicated.
+ Flat is better than nested.
+ Sparse is better than dense.
+ Readability counts.
+ Special cases aren't special enough to break the rules.
+ Although practicality beats purity.
+ Errors should never pass silently.
+ Unless explicitly silenced.
+ In the face of ambiguity, refuse the temptation to guess.
+ There should be one-- and preferably only one --obvious way to do it.
+ Although that way may not be obvious at first unless you're Dutch.
+ Now is better than never.
+ Although never is often better than *right* now.
+ If the implementation is hard to explain, it's a bad idea.
+ If the implementation is easy to explain, it may be a good idea.
+ Namespaces are one honking great idea -- let's do more of those!
+
diff --git a/pypers/pro.txt b/pypers/pro.txt
new file mode 100755
index 0000000..b71158f
--- /dev/null
+++ b/pypers/pro.txt
@@ -0,0 +1,106 @@
+Operator overloading is best done with metaclasses:
+
+ ::
+
+ #<autowrap.py>
+
+ import inspect
+
+ class wrappedmethod(Customizable):
+ """Customizable method factory intended for derivation.
+ The wrapper method is overridden in the children."""
+
+ logfile=sys.stdout # default
+ namespace='' # default
+
+ def __new__(cls,meth): # meth is a descriptor
+ if isinstance(meth,FunctionType):
+ kind=0 # regular method
+ func=meth
+ elif isinstance(meth,staticmethod):
+ kind=1 # static method
+ func=meth.__get__('whatever')
+ elif isinstance(meth,classmethod):
+ kind=2 # class method
+ func=meth.__get__('whatever','whatever').im_func
+ elif isinstance(meth,wrappedmethod): # already wrapped
+ return meth # do nothing
+ elif inspect.ismethoddescriptor(meth):
+ kind=0; func=meth # for many builtin methods
+ else:
+ return meth # do nothing
+ self=super(wrappedmethod,cls).__new__(cls)
+ self.kind=kind; self.func=func # pre-initialize
+ return self
+
+ def __init__(self,meth): # meth not used
+ self.logfile=self.logfile # default values
+ self.namespace=self.namespace # copy the current
+
+ def __get__(self,obj,cls): # closure
+ def _(*args,**kw):
+ if obj is None: o=() # unbound method call
+ else: o=(obj,) # bound method call
+ allargs=[o,(),(cls,)][self.kind]+args
+ return self.wrapper()(*allargs,**kw)
+ return _ # the wrapped function
+ # allargs is the only nontrivial line in _; it adds
+ # 0 - obj if meth is a regular method
+ # 1 - nothing if meth is a static method
+ # 2 - cls if meth is a class method
+
+ def wrapper(self): return self.func # do nothing, to be overridden
+
+ class autowrappedmethod(wrappedmethod):
+ """Makes the method returning cls instances, by wrapping its
+ output with cls"""
+ klass=None # has to be fixed dynamically from outside
+ def __init__(self,meth):
+ super(autowrappedmethod,self).__init__(meth) # cooperative
+ self.klass=self.klass # class variable -> instance variable
+ def wrapper(self): # closure
+ return lambda *args,**kw: self.klass(self.func(*args,**kw))
+
+ class AutoWrapped(type):
+ """Metaclass that looks at the methods declared in the attributes
+ builtinlist and wraplist of its instances and wraps them with
+ autowrappedmethod."""
+ def __init__(cls,name,bases,dic):
+ super(AutoWrapped,cls).__init__(name,bases,dic) # cooperative
+ cls.builtinlist=getattr(cls,'builtinlist',[])
+ if not hasattr(cls,'diclist') : # true only at the first call
+ cls.diclist=[(a,vars(bases[0])[a]) for a in cls.builtinlist]
+ if dic.has_key('wraplist'): # can be true at any call
+ cls.diclist+=[(a,dic[a]) for a in cls.wraplist]
+ wrapper=autowrappedmethod.With(klass=cls)
+ d=dict([(a,wrapper(v)) for a,v in cls.diclist])
+ customize(cls,**d)
+
+ class Str(str):
+ __metaclass__=AutoWrapped
+ builtinlist="""__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
+ center expandtabs join ljust lower lstrip replace rjust rstrip strip
+ swapcase title translate upper zfill""".split()
+
+ #</autowrap.py>
+
+Here I show various tests.
+
+ .. doctest
+
+ >>> from autowrap import Str
+ >>> sum=Str('a')+Str('b') # check the sum
+ >>> print sum, type(sum)
+ ab <class 'autowrap.Str'>
+ >>> rprod=Str('a')*2 # check the right product
+ >>> print rprod,type(rprod)
+ aa <class 'autowrap.Str'>
+ >>> lprod=2*Str('a') # check the left product
+ >>> print lprod,type(lprod)
+ aa <class 'autowrap.Str'>
+ >>> r=Str('a').replace('a','b') # check replace
+ >>> print r,type(r)
+ b <class 'autowrap.Str'>
+ >>> r=Str('a').capitalize() # check capitalize
+ >>> print r,type(r)
+ A <class 'autowrap.Str'>
diff --git a/pypers/pro1.py b/pypers/pro1.py
new file mode 100755
index 0000000..2d9e9bc
--- /dev/null
+++ b/pypers/pro1.py
@@ -0,0 +1,28 @@
+"Incredible behaviour of metafunctions"
+
+from oopp import *
+
+def magicallyTransform(name,bases,dic):
+ print "called!"
+ dic['formatstring']="Very beautiful, since I am %s"
+ return type(name,bases,dic)
+ #return TransformedUglyDuckling(name,bases,dic)
+
+class MagicallyTransformed(Class):
+ "Metaclass changing the formatstring of its instances"
+ def __init__(cls,name,bases,dic):
+ print "called!"
+ cls.formatstring="Very beautiful, since I am %s"
+
+class TransformedUglyDuckling(PrettyPrint):
+ "A class metamagically modified"
+ __metaclass__ = MagicallyTransformed
+ __metaclass__ = magicallyTransform
+ formatstring="Not beautiful, I am %s" # will be changed
+
+class Swan(TransformedUglyDuckling):
+ formatstring="Very beautiful, I am %s"
+
+print Swan()
+
+print Swan.__class__,Swan.__metaclass__
diff --git a/pypers/pro2.py b/pypers/pro2.py
new file mode 100755
index 0000000..bbe1074
--- /dev/null
+++ b/pypers/pro2.py
@@ -0,0 +1,13 @@
+
+from oopp import *
+
+wrongcode='''
+r"""Code processing example: replaces 'Print' with 'print' except in
+comments and literal strings"""
+Print "Prints \"Hello World!\"" # look at this line
+'''
+
+fixPrint=lambda s: s.replace('Print','print')
+print codeprocess(wrongcode,fixPrint)
+
+print quotencode(wrongcode)
diff --git a/pypers/pro3.py b/pypers/pro3.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/pro3.py
diff --git a/pypers/pro4.py b/pypers/pro4.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/pro4.py
diff --git a/pypers/pro5.py b/pypers/pro5.py
new file mode 100755
index 0000000..d99fb75
--- /dev/null
+++ b/pypers/pro5.py
@@ -0,0 +1,10 @@
+class M(type):
+ def __init__(cls,name,bases,dic):
+ print 'called!'
+
+class C(object):
+ pass
+
+C.__class__=M
+
+class D(C): pass
diff --git a/pypers/pro6.py b/pypers/pro6.py
new file mode 100755
index 0000000..5039ca7
--- /dev/null
+++ b/pypers/pro6.py
@@ -0,0 +1,18 @@
+"""This script looks at its own source code and extracts dotted names,
+i.e. names containing at least one dot, such as object.attribute or
+more general one, such as obj.attr.subattr."""
+
+# Notice that dotted.names in comments and literal strings are ignored
+
+from oopp import *
+import __main__
+
+text=inspect.getsource(__main__)
+
+dotname=Regexp.CODESEP| Regexp.DOTNAME.named()
+
+print 'Using the regular expression',dotname
+
+print "I have found the following dotted names:\n%s" % [
+ MO.group() for MO in dotname.finditer(text)]
+
diff --git a/pypers/prog.txt b/pypers/prog.txt
new file mode 100755
index 0000000..ef0cf6d
--- /dev/null
+++ b/pypers/prog.txt
@@ -0,0 +1,482 @@
+THE PROGRAMMABLE PROGRAMMING LANGUAGE
+=========================================================================
+
+ *I think that lisp is a better applications language than Python.
+ However, Python is close enough, or at least so much better than the
+ alternatives, that Python's social and glue language advantages are
+ often decisive.* -- Andy Freeman on c.l.p.
+
+I go in *really* DEEP BLACK MAGIC here.
+
+Lisp has been called the *programmable programming language* [#]_
+since its macros allow the programmer to change the *syntax* of
+the language. Python has no macros and the syntax of the language
+cannot be changed. Nevertheless, Python metaclasses allows
+to change the *semantics* of the language. In this sense, they
+are even more powerful and more dangerous than Lisp macros.
+Python metaclass allow the user to customize the language (if not
+its syntax). This is cool enough, however it can make your programs
+unreadable by others. The techniques explained in this
+chapter should be used with care. Nevertheless, I trust the judgement
+of the programmer who has been able to reach this chapter, and I don't
+mind providing him further rope to shoot in his/her foot ;)
+
+.. [#] Paul Graham, 'OnLisp'
+ citing
+
+Enhancing the Python language
+--------------------------------------------------------------------------
+
+Let me start with some minor usage of metaclasses. In this section I
+will show how the user can implement in few lines features that are
+built-in in other languages, through a minimal usage of metaclasses.
+
+For instance, suppose one wants to define a class which cannot be
+derived: in Java this can be done with the "final" keyword.
+In Python there is no need to add a new keyword to the language:
+
+ ::
+
+ #<oopp.py>
+
+ class NonDerivableError(Exception): pass
+
+ class Final(type): # better derived from WithCounter,type
+ "Instances of Final cannot be derived"
+ def __new__(meta,name,bases,dic):
+ try:
+ meta.already_called is True
+ except AttributeError: # not already called
+ meta.already_called=True
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else: #if already called
+ raise NonDerivableError("I cannot derive from %s" % bases)
+
+ #</oopp.py>
+
+Here there is an example of usage:
+
+ >>> from oopp import Final
+ >>> class C:
+ ... __metaclass__=Final
+ ...
+ >>> class D(C): pass #error
+ ...
+ NonDerivableError: D not created from (<class 'oopp.C'>,)
+
+It is interesting to notice that a similar effect can be reached
+with a ``singletonClass`` class factory: a 'MetaSingleton' inherits
+from ``Singleton`` and from 'type' (therefore it is a metaclass):
+
+ ::
+
+ #<oopp.py>
+
+ class S(Singleton,type): pass
+ singletonClass=ClsFactory[S]
+
+ #</oopp.py>
+
+If we write
+
+ >>> from oopp import singletonClass
+ >>> C=singletonClass()
+ >>> class D(C):
+ ... pass
+
+
+we see that actually 'D' is not a new instance of 'Singleton', but
+it coincides with 'C', instead:
+
+ >>> id(C),id(D)
+ (135622140, 135622140)
+ >>> C is D
+ True
+ >>> type(C)
+ <class '__main__._Singleton'>
+ >>> type(C).__bases__
+ (<class 'oopp.Singleton'>, <type 'type'>)
+ >>> c=C(); d=D()
+ >>> id(c),id(d)
+ (1075378028, 1075378924)
+
+Notice the order: 'SingletonClass' must inherit from 'Singleton'
+first and from ``Class`` second, otherwise the ``Class.__new__`` method would
+override the ``Singleton.__new__``, therefore losing the 'Singleton'
+basic property of having only one instance. On the other hand, in
+the correct order, 'Singleton' first and 'Class' second, the inheritance
+diagram is
+
+ ::
+
+
+ object 5
+ (__new__)
+ / \
+ / \
+ 2 WithCounter type 4
+ (__new__) (__new__)
+ | |
+ | |
+ 1 Singleton Class 3
+ (__new__) (__new__)
+ \ /
+ \ /
+ SingletonClass 0
+ (Singleton.__new__)
+
+
+ ::
+
+ object
+ / \
+ / |
+ WithCounter |
+ | |
+ Singleton type
+ \ /
+ \ /
+ MetaSingleton
+ :
+ :
+ : instantiation
+ :
+ :
+ C = D
+
+
+whereas 'SingletonClass' inherits ``Singleton.__new__`` which, trough
+the ``super`` mechanism, calls 'type.__new__' and therefore creates
+the class 'C'. Notice that class 'D' is never created, it is simply
+an alias for 'C'.
+
+I think it is simpler to write down the class 'Final' explicitely
+(explicit is better than implicit) as I did; however a fanatic of code
+reuse could derive it from 'SingletonClass':
+
+ ::
+
+ #<final.py>
+
+ from oopp import *
+
+ class Final(Singleton,type):
+ "Inherits the 'instance' attribute from Singleton (default None)"
+ def __new__(meta,name,bases,dic):
+ if meta.counter==0: # first call
+ return super(Final,meta).__new__(meta,name,bases,dic)
+ else:
+ raise NonDerivableError("I cannot derive from %s" % bases)
+
+ class C: __metaclass__=Final
+
+ try:
+ class D(C): pass
+ except NonDerivableError,e:
+ print e
+
+ #</final.py>
+
+The reader can check that this script has the correct output
+"I cannot derive from <class 'oopp.C'>". I leave to the reader
+to understand the issues with trying to implement 'NonDerivable'
+from 'NonInstantiable'. #And why an inner metaclass would not work.
+
+Restricting Python dynamism
+-----------------------------------------------------------
+
+ ::
+
+ #<oopp.py>
+
+ def frozen(self,name,value):
+ if hasattr(self,name):
+ type(self).__bases__[0].__setattr__(self,name,value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+ class Frozen(object):
+ """Subclasses of Frozen are frozen, i.e. it is impossibile to add
+ new attributes to them and their instances"""
+ __setattr__ = frozen
+ class __metaclass__(type):
+ __setattr__ = frozen
+
+ #</oopp.py>
+
+
+ #<frozen.py>
+
+ from oopp import *
+
+ class C(Frozen):
+ c=1
+ def __init__(self):
+ #self.x=5 # won't work anymore, __new__ will be okay
+ pass
+
+ class D(C):
+ d=2
+
+ C.c=2
+
+ print D().d
+
+ #</frozen.py>
+
+Changing the language without changing the language
+--------------------------------------------------------------------------
+
+In Lisp the user has the possibility of changing the syntax of the
+language to suit her purposes (or simply to fit her taste).
+In Python, the user cannot change the basic grammar of the language,
+nevertheless, to a great extent, metaclasses allows to emulate this effect.
+Notice that using metaclasses to this aim is not necessarely
+a good idea, since once you start
+changing the Python standard behaviour, it will become impossible for
+others to understand your programs (which is what happened to Lisp ;).
+
+Let me show how metaclasses can be used to provide notational convenience
+(i.e. syntactic sugar) for Python.
+
+As first example, I will show how we may use metaclasses to provide some
+convenient notation for staticmethods and classmethods:
+
+ ::
+
+ class MetaSugar(type):
+ def __init__(cls,name,bases,clsdict):
+ for key,value in clsdict.iteritems():
+ if key.startswith("static_"):
+ setattr(cls,key[7:],staticmethod(value))
+ elif key.startwith("class_"):
+ setattr(cls,key[6:],classmethod(value))
+
+The same effect can be obtained trough normal inheritance
+
+ ::
+
+ class SyntacticSugar(object):
+ def __init__(self):
+ for k,v in self.__class__.__dict__.iteritems():
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+ if k.startswith('static_'):
+ self.__class__.__dict__[k[7:]] = staticmethod(v)
+
+Let me now implement some syntactic sugar for the __metaclass__ hook.
+
+ ::
+
+ #<oopp.py> #rewrite without eval, please
+
+ import re
+ squarednames=re.compile('\[([A-Za-z_][\w\., ]*)\]')
+
+ #def inferredfromdocstring(name,bases,dic):
+ # docstring=dic['__doc__']
+ # match=squarednames.match(docstring)
+ # if not match: return ClsFactory[Reflective](name,bases,dic)
+ # metanames=[name.strip() for name in match.group(1).split(',')]
+ # metaname=''.join(metanames)
+ # if len(metanames)>1: # creates a new metaclass
+ # metaclass=type(metaname,tuple(map(eval,metanames,globals())),{})
+ # else:
+ # metaclass=eval(metaname,globals())
+ # return ClsFactory[metaclass](name,bases,dic)
+
+ #</oopp.py>
+
+ #<sugar.py>
+
+ from oopp import *
+ #__metaclass__ = inferredfromdocstring
+ class B:
+ "Do nothing class"
+
+ class C:
+ "[Reflective]"
+ " Do nothing class"
+
+ class D:
+ "[WithLogger,Final]"
+ "Do nothing class"
+
+ class E(C):
+ pass
+
+ #</sugar.py>
+
+With output:
+
+ ::
+
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class Logged_C descending from (),
+ instance of <class 'oopp.Logged'>
+
+ Logged_C dictionary:
+ __doc__ = Do nothing class
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class Logged_Final_D descending from (),
+ instance of <class 'oopp.LoggedFinal'>
+
+ Logged_Final_D dictionary:
+ __doc__ = Do nothing class
+ *****************************************************************************
+ Fri Feb 21 09:35:58 2003
+ Creating class E descending from (<class 'oopp.Logged_C'>,),
+ instance of <class 'oopp.Logged'>
+
+ E dictionary:
+ <EMPTY>
+
+At the end, let me point out few observations:
+Metaclasses can be used to provide syntactic sugar, as I have shown
+in the previous example. However, I have given the previous
+routines as a proof of concept: I do *not* use these routines in
+my actual code for many good reasons:
+
+1. At the end a convenient notation will be provided in Python 2.4
+2. I don't want to use magic tricks on my code, I want others to
+ be able to understand what the code is doing;
+3. I want to be able myself to understand my own code in six months
+ from today ;)
+
+Anyway, I think it is a good thing to know about this potentiality
+of metaclasses, that can turn out to be very convenient in certain
+applications: but this does not mean that should be blindly used
+and/or abused. In other words: with great powers come
+great responsabilities ;)
+
+Recognizing magic comments
+--------------------------------------------------------------------------
+
+In this section, I will begin to unravel the secrets of the black magic art
+of changing Python semantics and I will show that with few lines
+involving metaclasses
+and the standard library 'inspect' module, even comments can be made
+significant! (let me continue with my series "how to do what should not
+be done").
+
+To this aim, I need a brief digression on regular expressions.
+
+ ::
+
+ class RecognizesMagicComments(object):
+ form=r'def %s(NAME)(args):#!\s?staticmethod'
+ class __metaclass__(type):
+ def __new__(meta,name,bases,dic):
+ code=[]
+ for attr in dic:
+ source=inspect.getsource(dic[attr]).splitlines()
+ for line in source:
+ split=line.split('#!')
+ if len(split)==2:
+ descriptor=split[1]; code.append(split[0])
+ else: code.append(line)
+
+ class C(RecognizesMagicComments):
+ #!staticmethod
+ def f(x): #!staticmethod
+ return x
+
+Interpreting Python source code on the fly
+---------------------------------------------------------------------------
+
+At this point, I can really go *DEEP* in black magic.
+
+ ::
+
+ import sys, inspect, linecache, re
+
+ def cls_source(name,module):
+ lines = linecache.getlines(inspect.getsourcefile(module))
+ if not lines: raise IOError, 'could not get source code'
+ pat = re.compile(r'^\s*class\s*' + name + r'\b')
+ for i in range(len(lines)):
+ if pat.match(lines[i]): break
+ else: raise IOError, 'could not find class definition'
+ lines, lnum = inspect.getblock(lines[i:]), i + 1
+ return ''.join(lines)
+
+ class Interpreter(object):
+ def __init__(self,CPO): # possible composition of code processing opers
+ self.repl=CPO
+ def __call__(self,name,bases,dic):
+ try:
+ modulename=dic['__module__'] # module where the class is defined
+ except KeyError: # no __module__ attribute
+ raise IOError("Class %s cannot be defined dynamically or in the\n"
+ "interpreter and the source code cannot came from a pipe"% name)
+ module=sys.modules[modulename]
+ source=self.repl(cls_source(name,module))
+ source=re.sub('__metaclass__=.*','__metaclass__=type',source)
+ #print source
+ loc={}; exec source in vars(module),loc
+ return loc[name]
+
+ regexp_expand=Interpreter(regexp)
+
+Implementing lazy evaluation
+---------------------------------------------------------------------------
+
+At this point of our knowledge, it becomes trivial to implement lazy
+evaluation and then a ternary operator. (My original, simpler, implementation
+is posted on c.l.p.; see the thread 'PEP 312 (and thus 308) implemented
+with a black magic trick')
+
+Implementing a ternary operator
+---------------------------------------------------------------------------
+
+ ::
+
+ # module ternary.py
+
+ "PEP 308 and 312 implemented via a metaclass-powered dirty trick"
+
+ import inspect,__main__
+
+ # the ternary operator:
+
+ def if_(cond,f,g):
+ "Short circuiting ternary operator implemented via lambdas"
+ if cond: return f()
+ else: return g()
+
+ # the metaclass black magic:
+
+ class DirtyTrick(type):
+ """Cooperative metaclass that looks at the source code of its instances
+ and replaces the string '~' with 'lambda :' before the class creation"""
+ def __new__(meta,name,bases,dic):
+ for attr in dic.values():
+ if inspect.isfunction(attr):
+ code=inspect.getsource(attr)
+ if code.find('~')==-1: continue # no '~' found, skip
+ code=code.replace('~','lambda :')
+ code=dedent(code)+'\n'
+ exec code in __main__.__dict__,dic # modifies dic
+ return super(DirtyTrick,meta).__new__(meta,name,bases,dic)
+
+ # a convenient base class:
+
+ class RecognizesImplicitLambdas:
+ "Children of this class do recognize implicit lambdas"
+ __metaclass__=DirtyTrick
+
+Here there is an example of usage:
+
+ ::
+
+ from ternary import if_, RecognizesImplicitLambdas
+ from math import sqrt
+
+ class C(RecognizesImplicitLambdas):
+ def safesqrt(self,x):
+ return if_( x>0, ~sqrt(x), ~0) #short-circuiting ternary operator
+
+ c=C()
+ print c.safesqrt(4), c.safesqrt(-4)
diff --git a/pypers/prog_inter.py b/pypers/prog_inter.py
new file mode 100755
index 0000000..00a2c9d
--- /dev/null
+++ b/pypers/prog_inter.py
@@ -0,0 +1,13 @@
+from oopp import Final
+class C:
+ __metaclass__=Final
+from oopp import singletonClass
+C=singletonClass()
+class D(C):
+ pass
+id(C),id(D)
+C is D
+type(C)
+type(C).__bases__
+c=C(); d=D()
+id(c),id(d)
diff --git a/pypers/prova.txt b/pypers/prova.txt
new file mode 100755
index 0000000..e35912a
--- /dev/null
+++ b/pypers/prova.txt
@@ -0,0 +1,4 @@
+.. image:: fig1.png
+
+
+
diff --git a/pypers/pyj/python-subtilities.html b/pypers/pyj/python-subtilities.html
new file mode 100755
index 0000000..976402f
--- /dev/null
+++ b/pypers/pyj/python-subtilities.html
@@ -0,0 +1,187 @@
+<?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.1: http://docutils.sourceforge.net/" />
+<title>Python gotchas</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="python-gotchas">
+<h1 class="title">Python gotchas</h1>
+<p>One of the strenghts of Python is its ability to be easy to understand and
+unsurprising most of the times. However, easy does not mean trivial and
+there actually situations where Python can surprise the new user (and
+sometimes even the old timer). In this rubric we will discuss few of
+those Python gotchas.</p>
+<p>The choice will be based on questions which are periodically asked on
+the Python newsgroup: the fact that those questions are repeated means
+that i) they are non-trivial and ii) more than few persons are caught
+by those gotchas.</p>
+<p>I will start with a gotcha who got me actually, and was the reason
+for my first post on comp.lang.python.</p>
+<p>The issue has been discussed in many threads on comp.lang.python; the
+one where I understood the issue is this one:</p>
+<p><a class="reference" href="http://groups.google.it/groups?hl=it&amp;lr=&amp;ie=UTF-8&amp;oe=UTF-8&amp;threadm=tyfsmgtbewl.fsf%40lxplus030.cern.ch&amp;rnum=1&amp;prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26q%3Dsimionato%2Blambda%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python">http://groups.google.it/groups?hl=it&amp;lr=&amp;ie=UTF-8&amp;oe=UTF-8&amp;threadm=tyfsmgtbewl.fsf%40lxplus030.cern.ch&amp;rnum=1&amp;prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26q%3Dsimionato%2Blambda%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python</a>.*</p>
+<div class="section" id="statement-of-the-issue">
+<h1><a name="statement-of-the-issue">Statement of the issue</a></h1>
+<p>This gotcha is related to the scope rules of Python in loops: it often
+appears in conjunction with lambda functions, but actually has nothing
+to do with them, since lambda functions are not special at all in
+Python. To show what the surprising behaviour is, consider the
+following code:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def make_adders(n):
+... return [(lambda x: x+i) for i in range(n)]
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; add0,add1=make_adders(n=2)
+</pre>
+<p>Now, it is clear what add1(0) will do:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; add1(0)
+1
+</pre>
+<p>The surprise is with add0(0), which does not return 0, as you
+would probably expect:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; add0(0)
+1
+</pre>
+<p>The issue has nothing to do with lambda functions and list comprehensions,
+since</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def make_adders(n):
+... ls=[]
+... for i in range(n):
+... def f(x):
+... return x+i
+... ls.append(f)
+... return ls
+</pre>
+<p>has the same exact behavior:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; add1(0)
+1
+&gt;&gt;&gt; add0(0)
+1
+</pre>
+<p>The behavior is even more surprising if you think that other languages
+(such as Haskell, the language from which Python stole list comprehensions)
+do the &quot;right&quot; thing, i.e.</p>
+<pre class="literal-block">
+Prelude&gt; let make-adders n = [ \x -&gt; x + i | i &lt;- [0..n-1] ]
+Prelude&gt; let [add0,add1] = make_adders 2
+Prelude&gt; add0 0
+0
+Prelude&gt; add1 0
+1
+</pre>
+<p>(this example was provided to me by a Haskell expert, I never actually used
+the language, but I report it here just to show that there are languages
+where you get what you expect).</p>
+<p>So one could consider this as a Python wart. However, the truth is more
+complex.</p>
+</div>
+<div class="section" id="explanation-of-the-issue">
+<h1><a name="explanation-of-the-issue">Explanation of the issue</a></h1>
+<p>The reason why Python acts as it does, is that <em>by design</em> Python
+loops (both &quot;for&quot; loops and &quot;while&quot; loops) do not introduce a new
+scope.</p>
+<p>So, if I write</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a = 1 # no-new-scope
+&gt;&gt;&gt; for i in range(1,10):
+... a = i
+... if i == 2: break
+&gt;&gt;&gt; a
+2
+</pre>
+<p>the &quot;a&quot; inside the for loop is exactly the same as the &quot;a&quot; defined outside
+the &quot;for&quot; loop. This is what I want: if the &quot;for&quot; loop introduced a new
+scope, then the inner &quot;a&quot; would be a local variable and the result of
+this code would be &quot;a=1&quot; for the original global variable. Since
+Python does not have the ability to rebind variables in outer scopes,
+there would be no way to change &quot;a&quot; from inside the loop <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a>.</p>
+<p>But what exactly happens, when Python encounters a &quot;for&quot; loop?
+The answer is that the form</p>
+<pre class="literal-block">
+for i in iterable:
+ &lt;do_something i&gt;
+</pre>
+<p>is essentially interpreted as</p>
+<pre class="literal-block">
+try:
+ it = iter(iterable)
+ while True:
+ i = it.next()
+ &lt;do_something i&gt;
+except StopIteration:
+ pass
+</pre>
+<p>As you see, this does not introduce any new scope. In order to solve the
+&quot;issue&quot; you have to introduce a new scope. In Python, the natural way
+to introduce a new scope is via a function.
+If the &quot;for&quot; loop was interpreted as follows</p>
+<pre class="literal-block">
+try:
+ it = iter(iterable)
+ def helper(i): # helper function
+ &lt;do_something i&gt;
+ while True:
+ helper(it.next())
+except StopIteration:
+ pass
+</pre>
+<p>then a new &quot;i&quot; would enter in the scope at each iteration.</p>
+<p>For instance, in the example I am talking about,</p>
+<pre class="literal-block">
+def make_adders(n):
+ return [lambda x: x+i for i in range(n)]
+</pre>
+<p>would be interpreted as</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def make_adders(n):
+... try:
+... adders=[]
+... it = iter(range(2))
+... def helper(i):
+... adders.append(lambda x: x+i)
+... while True:
+... helper(it.next())
+... except StopIteration:
+... return adders
+</pre>
+<p>You can check that with this definition <tt class="literal"><span class="pre">make_adders</span></tt> does the right thing:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; add0,add1 = make_adders(n=2)
+&gt;&gt;&gt; add1(0)
+1
+&gt;&gt;&gt; add0(0)
+0
+</pre>
+<p>Essentially, the &quot;i&quot; variable would be passed via the helper function
+call and at each iteration the lambda function would see a different
+value of it.</p>
+<p>Unfortunately, changing Python &quot;for&quot; loops in this way would cause
+an even worse wart, as I discussed in the <tt class="literal"><span class="pre">no-new-scope</span></tt> example.</p>
+<p>That is the reason why the present behavior will
+not change.</p>
+<table class="footnote" frame="void" id="id2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>Unless you use contorsion involving making &quot;a&quot; an element of
+a mutable container.</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+<hr class="footer" />
+<div class="footer">
+<a class="reference" href="python-subtilities.txt">View document source</a>.
+Generated on: 2004-05-03 15:26 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/pyj/python-subtilities.txt b/pypers/pyj/python-subtilities.txt
new file mode 100755
index 0000000..78d249b
--- /dev/null
+++ b/pypers/pyj/python-subtilities.txt
@@ -0,0 +1,187 @@
+Python gotchas
+========================
+
+One of the strenghts of Python is its ability to be easy to understand and
+unsurprising most of the times. However, easy does not mean trivial and
+there actually situations where Python can surprise the new user (and
+sometimes even the old timer). In this rubric we will discuss few of
+those Python gotchas.
+
+The choice will be based on questions which are periodically asked on
+the Python newsgroup: the fact that those questions are repeated means
+that i) they are non-trivial and ii) more than few persons are caught
+by those gotchas.
+
+I will start with a gotcha who got me actually, and was the reason
+for my first post on comp.lang.python.
+
+The issue has been discussed in many threads on comp.lang.python; the
+one where I understood the issue is this one:
+
+http://groups.google.it/groups?hl=it&lr=&ie=UTF-8&oe=UTF-8&threadm=tyfsmgtbewl.fsf%40lxplus030.cern.ch&rnum=1&prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26q%3Dsimionato%2Blambda%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python.*
+
+Statement of the issue
+------------------------
+
+This gotcha is related to the scope rules of Python in loops: it often
+appears in conjunction with lambda functions, but actually has nothing
+to do with them, since lambda functions are not special at all in
+Python. To show what the surprising behaviour is, consider the
+following code:
+
+>>> def make_adders(n):
+... return [(lambda x: x+i) for i in range(n)]
+
+>>> add0,add1=make_adders(n=2)
+
+Now, it is clear what add1(0) will do:
+
+>>> add1(0)
+1
+
+The surprise is with add0(0), which does not return 0, as you
+would probably expect:
+
+>>> add0(0)
+1
+
+The issue has nothing to do with lambda functions and list comprehensions,
+since
+
+>>> def make_adders(n):
+... ls=[]
+... for i in range(n):
+... def f(x):
+... return x+i
+... ls.append(f)
+... return ls
+
+has the same exact behavior:
+
+>>> add1(0)
+1
+>>> add0(0)
+1
+
+The behavior is even more surprising if you think that other languages
+(such as Haskell, the language from which Python stole list comprehensions)
+do the "right" thing, i.e.
+
+::
+
+ Prelude> let make-adders n = [ \x -> x + i | i <- [0..n-1] ]
+ Prelude> let [add0,add1] = make_adders 2
+ Prelude> add0 0
+ 0
+ Prelude> add1 0
+ 1
+
+(this example was provided to me by a Haskell expert, I never actually used
+the language, but I report it here just to show that there are languages
+where you get what you expect).
+
+
+So one could consider this as a Python wart. However, the truth is more
+complex.
+
+Explanation of the issue
+----------------------------------
+
+The reason why Python acts as it does, is that *by design* Python
+loops (both "for" loops and "while" loops) do not introduce a new
+scope.
+
+So, if I write
+
+>>> a = 1 # no-new-scope
+>>> for i in range(1,10):
+... a = i
+... if i == 2: break
+>>> a
+2
+
+the "a" inside the for loop is exactly the same as the "a" defined outside
+the "for" loop. This is what I want: if the "for" loop introduced a new
+scope, then the inner "a" would be a local variable and the result of
+this code would be "a=1" for the original global variable. Since
+Python does not have the ability to rebind variables in outer scopes,
+there would be no way to change "a" from inside the loop [#]_.
+
+But what exactly happens, when Python encounters a "for" loop?
+The answer is that the form
+
+::
+
+ for i in iterable:
+ <do_something i>
+
+is essentially interpreted as
+
+::
+
+ try:
+ it = iter(iterable)
+ while True:
+ i = it.next()
+ <do_something i>
+ except StopIteration:
+ pass
+
+As you see, this does not introduce any new scope. In order to solve the
+"issue" you have to introduce a new scope. In Python, the natural way
+to introduce a new scope is via a function.
+If the "for" loop was interpreted as follows
+
+::
+
+ try:
+ it = iter(iterable)
+ def helper(i): # helper function
+ <do_something i>
+ while True:
+ helper(it.next())
+ except StopIteration:
+ pass
+
+then a new "i" would enter in the scope at each iteration.
+
+For instance, in the example I am talking about,
+
+::
+
+ def make_adders(n):
+ return [lambda x: x+i for i in range(n)]
+
+would be interpreted as
+
+>>> def make_adders(n):
+... try:
+... adders=[]
+... it = iter(range(2))
+... def helper(i):
+... adders.append(lambda x: x+i)
+... while True:
+... helper(it.next())
+... except StopIteration:
+... return adders
+
+You can check that with this definition ``make_adders`` does the right thing:
+
+>>> add0,add1 = make_adders(n=2)
+>>> add1(0)
+1
+>>> add0(0)
+0
+
+Essentially, the "i" variable would be passed via the helper function
+call and at each iteration the lambda function would see a different
+value of it.
+
+Unfortunately, changing Python "for" loops in this way would cause
+an even worse wart, as I discussed in the ``no-new-scope`` example.
+
+That is the reason why the present behavior will
+not change.
+
+.. [#] Unless you use contorsion involving making "a" an element of
+ a mutable container.
diff --git a/pypers/quixote/notes.txt b/pypers/quixote/notes.txt
new file mode 100755
index 0000000..14c4a3e
--- /dev/null
+++ b/pypers/quixote/notes.txt
@@ -0,0 +1,16 @@
+Quixote is a programmer's framework.
+
+The design goals for Quixote were:
+
+ 1.
+
+ To allow easy development of Web applications where the accent is more on complicated programming logic than complicated templating.
+ 2.
+
+ To make the templating language as similar to Python as possible. The aim is to make as many of the skills and techniques learned from writing regular Python code applicable to the task of writing Web applications.
+ 3.
+
+ No magic. When it's not obvious what to do in a certain case, Quixote refuses to guess.
+
+If you view a web site as a program, and web pages as subroutines, Quixote just might be the tool for you. If you view a web site as a graphic design showcase, and each web page as an individual work of art, Quixote is probably not what you're looking for.
+
diff --git a/pypers/recipes/Memoize.txt b/pypers/recipes/Memoize.txt
new file mode 100755
index 0000000..63628ab
--- /dev/null
+++ b/pypers/recipes/Memoize.txt
@@ -0,0 +1,44 @@
+Caching object creation
+-----------------------
+
+This cookbook contains many recipes to memoize functions, however a recipe to
+memoize classes was missing. Using this recipe you can cache object
+creation, i.e. __new__ and __init__ methods are called only when needed.
+For a good use case, see the discussion around
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413609
+
+ #<main.py>
+
+ from ms.lang_utils import Memoize
+
+ if __name__ == "__main__":
+
+ class Object:
+ __metaclass__ = Memoize
+ def __init__(self, *args):
+ print "object created with parameters %s" % str(args)
+
+
+ o1 = Object(1) # create the first object
+ o2 = Object(1) # return the already created object
+ assert o1 is o2
+
+ o3 = Object(1, 2) # create another object
+ o4 = Object(1, 2) # return the already created object
+ assert o3 is o4
+
+ #</main.py>
+
+The test case in the code should be clear: if you pass to class the same
+parameters, you will get the same object. __new__ and __init__ methods
+will NOT be called the second time you pass the same parameters.
+Everything works since __new__ and __init__ are called by the metaclass
+__call__ method which has been memoized.
+
+You may use whatever implementation you like for the memoize decorator, there
+are many availables in this cookbook and in the PythonDecoratorLibrary in
+the Python Wiki. I also have written a pretty cool decorator module that
+allows you to define signature-preserving decorators:
+
+http://www.phyast.pitt.edu/~micheles/python/decorator.zip
+
diff --git a/pypers/recipes/autoclose.py b/pypers/recipes/autoclose.py
new file mode 100644
index 0000000..2704377
--- /dev/null
+++ b/pypers/recipes/autoclose.py
@@ -0,0 +1,35 @@
+import atexit
+
+class AutoCloseMeta(type):
+ _instances = [] # tracks the instances of this metaclass
+
+ def __new__(mcl, name, bases, dic):
+ cls = super(AutoCloseMeta, mcl).__new__(mcl, name, bases, dic)
+ cls._closelst = [] # objects to be closed
+ mcl._instances.append(cls)
+ return cls
+
+ def __call__(cls, *args, **kw):
+ # tracks the instances of the instances
+ if cls is AutoClose: # abstract base class
+ raise TypeError('AutoClose is not meant to be instantiated!')
+ self = super(AutoCloseMeta, cls).__call__(*args, **kw)
+ cls._closelst.append(self)
+ return self
+
+ def closeall(cls):
+ "Recursively close all instances of cls and its subclasses"
+ # 'list' avoids a RuntimeError: dictionary changed size during iteration
+ print 'Closing instances of %s' % cls # you may remove this print
+ for obj in list(cls._closelst):
+ obj.close()
+ for subc in cls.__subclasses__():
+ subc.closeall()
+
+# assume the instantiation order is not important
+class AutoClose(object):
+ __metaclass__ = AutoCloseMeta
+ def close(self):
+ self._closelst.remove(self)
+
+atexit.register(AutoClose.closeall)
diff --git a/pypers/recipes/autoclose.txt b/pypers/recipes/autoclose.txt
new file mode 100644
index 0000000..3334043
--- /dev/null
+++ b/pypers/recipes/autoclose.txt
@@ -0,0 +1,89 @@
+Semi-automatic resource management with AutoClose
+---------------------------------------------------
+
+I have read in my life code where the release of a resource was done
+in the destructor method, relying on Python reference counting garbage
+collector. This kind of code is very fragile, since Python does not
+make any guarantee at all about *when* the destructor will be called.
+Consider for instance this code (coming from a real life case):
+
+<pre>
+$ cat deallocating.py
+import logging
+
+class C(object):
+ def __init__(self):
+ logging.warn('Allocating resource ...')
+
+ def __del__(self):
+ logging.warn('De-allocating resource ...')
+ print 'THIS IS NEVER REACHED!'
+
+if __name__ == '__main__':
+ c = C()
+
+$ python deallocating.py
+WARNING:root:Allocating resource ...
+Exception exceptions.AttributeError: "'NoneType' object has no
+attribute 'warn'" in <bound method C.__del__ of <__main__.C object at
+0xb7b9436c>> ignored
+</pre>
+
+The issue here is that the destructor is called *too late*, when the
+cleanup mechanism (see http://www.python.org/doc/essays/cleanup) has
+already set logging to None. This recipe
+
+1) gets rid of the destructor, and put the responsibility of deallocating
+ the resource in a .close method (which is IMO opinion much clearer);
+2) used in conjunction with the contextlib.closing class (new in Python 2.5)
+ it allows to perform explicit resource deallocation with the 'with'
+ statement, which is the preferred way to do that in Python 2.5;
+3) at the end of the program, it automatically closes the resources which
+ were not closed explicitely before.
+
+The usage is pretty simple. You should just import from the autoclose module
+the AutoClose class and you should add it to the list of your base classes.
+Then you should override the .close method making sure it calls the base
+class .close.
+
+Here is an example:
+
+<pre>
+
+$ cat autoclose_ex.py
+import logging
+from autoclose import AutoClose
+
+class C(AutoClose):
+ def __init__(self, id):
+ self.id = id
+ def close(self):
+ logging.warn('closing object %s' % self.id)
+ super(C, self).close()
+
+class D(C):
+ pass
+
+c1 = C(1)
+c2 = C(2)
+d3 = D(3)
+
+$ python autoclose_ex.py
+Closing instances of <class 'autoclose.AutoClose'>
+Closing instances of <class '__main__.C'>
+WARNING:root:closing object 1
+WARNING:root:closing object 2
+Closing instances of <class '__main__.D'>
+WARNING:root:closing object 3
+
+</pre>
+
+Notice that AutoClose keeps in memory a potentially large lists of instances,
+therefore your are advised to explicitly close your objects as soon as
+possible, to keep the list short. You can remove all the instances of a
+given class (and its subclasses) with the (meta)method <Class>.closeall().
+The implementation below should answer all your questions about how this
+is done exactly. In particular the call to atexit.register(AutoClose.closeall)
+is the one that ensures correct finalization (unlikely destructors methods,
+functions registered in atexit do not rely on the garbage collector and
+are called before the cleanup mechanism).
diff --git a/pypers/recipes/autoclose_ex.py b/pypers/recipes/autoclose_ex.py
new file mode 100644
index 0000000..48d860b
--- /dev/null
+++ b/pypers/recipes/autoclose_ex.py
@@ -0,0 +1,18 @@
+import logging
+from autoclose import AutoClose
+
+class C(AutoClose):
+ def __init__(self, id):
+ self.id = id
+ def close(self):
+ logging.warn('closing object %s' % self.id)
+ super(C, self).close()
+
+class D(C):
+ pass
+
+
+if __name__ == '__main__':
+ c1 = C(1)
+ c2 = C(2)
+ d3 = D(3)
diff --git a/pypers/recipes/chop.py b/pypers/recipes/chop.py
new file mode 100755
index 0000000..aebd254
--- /dev/null
+++ b/pypers/recipes/chop.py
@@ -0,0 +1,12 @@
+# chop.py
+
+def chop(iterable, n):
+ bin = []
+ for i, el in enumerate(iterable):
+ bin.append(el)
+ if i % n == n-1:
+ yield bin; bin = []
+ if bin:
+ yield bin
+
+
diff --git a/pypers/recipes/deallocating.py b/pypers/recipes/deallocating.py
new file mode 100644
index 0000000..2cc819d
--- /dev/null
+++ b/pypers/recipes/deallocating.py
@@ -0,0 +1,12 @@
+import logging
+
+class C(object):
+ def __init__(self):
+ logging.warn('Allocating resource ...')
+
+ def __del__(self):
+ logging.warn('De-allocating resource ...')
+ print 'THIS IS NEVER REACHED!'
+
+if __name__ == '__main__':
+ c = C()
diff --git a/pypers/recipes/deferred.py b/pypers/recipes/deferred.py
new file mode 100755
index 0000000..5c688ad
--- /dev/null
+++ b/pypers/recipes/deferred.py
@@ -0,0 +1,41 @@
+# deferred.py
+
+from twisted.internet import reactor, defer
+from twisted.python import threadable; threadable.init(1)
+import sys, time
+
+# BAD IDEA
+def deferred(func, *args):
+ """Takes a blocking function an converts it into a deferred-valued
+ function running in a separate thread.
+ """
+ de = defer.Deferred()
+ de.addCallback(func)
+ reactor.callInThread(de.callback, *args)
+ return de
+
+def running():
+ sys.stdout.write("."); sys.stdout.flush()
+ reactor.callLater(.1, running)
+
+# GOOD IDEA
+
+from twisted.internet.threads import deferToThread as deferred
+
+@deferred.__get__
+def sleep(sec):
+ print 'start sleep %s' % sec
+ time.sleep(sec)
+ print '\nend sleep %s' % sec
+ return "ok"
+
+def print_(result):
+ print result
+
+if __name__ == "__main__":
+ sleep(2).addBoth(print_)
+ reactor.callLater(.1, running)
+ reactor.callLater(3, reactor.stop)
+ reactor.run()
+
+
diff --git a/pypers/recipes/deferreds.txt b/pypers/recipes/deferreds.txt
new file mode 100755
index 0000000..0f7e529
--- /dev/null
+++ b/pypers/recipes/deferreds.txt
@@ -0,0 +1,70 @@
+Twisted FAQs clearly state that "deferreds do not magically convert blocking
+code into non-blocking code". So, how do you magically convert blocking
+code into non-blocking code?
+
+This recipe is the solution!
+
+::
+
+ #<deferred.py>
+
+ from twisted.internet import reactor, defer
+ from twisted.python import threadable; threadable.init(1)
+ import sys, time
+
+ ## the core trick
+
+ def callInThread(func, *args):
+ """Takes a blocking function an converts it into a deferred-valued
+ function running in a separate thread.
+ """
+ de = defer.Deferred()
+ de.addCallback(func)
+ reactor.callInThread(de.callback, *args)
+ return de
+
+ deferred = callInThread.__get__ # decorator associated to callInThread
+
+ ## example code
+
+ def print_(result):
+ print result
+
+ def running():
+ "Prints a few dots on stdout while the reactor is running."
+ sys.stdout.write("."); sys.stdout.flush()
+ reactor.callLater(.1, running)
+
+ @deferred
+ def sleep(sec):
+ "A blocking function magically converted in a non-blocking one."
+ print 'start sleep %s' % sec
+ time.sleep(sec)
+ print '\nend sleep %s' % sec
+ return "ok"
+
+ if __name__ == "__main__":
+ sleep(2).addBoth(print_)
+ reactor.callLater(.1, running)
+ reactor.callLater(3, reactor.stop)
+ reactor.run()
+
+ #</deferred.py>
+
+<rant>How to make blocking code non-blocking is the obvious question for
+everybody using Twisted, but the Twisted documentation does not make
+easy to find the solution :-( </rant>
+
+The trick is to run the blocking function in a separate thread. Here
+all the magic is performed by the decorator, ``deferred``, which converts
+``sleep``, a blocking function, into a deferred function i.e. a
+non-blocking function that returns a deferred object. The code for
+``callInThread`` is clear, and the ``.__get__`` trick converts
+``callInThread`` in a one-argument function returning a closure,
+i.e. an object suitable to be used as a decorator. I have seen this
+trick in Alex Martelli's lectures at PyCon 2005. If you are confused
+by it, you should read Raymond Hettinger essay on descriptors
+(http://users.rcn.com/python/download/Descriptor.htm).
+
+In short: every time you have a blocking function in your code, wrap
+it with the ``deferred`` decorator and live happy!
diff --git a/pypers/recipes/doct0.py b/pypers/recipes/doct0.py
new file mode 100755
index 0000000..95d6b36
--- /dev/null
+++ b/pypers/recipes/doct0.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python2.4
+# Author: michele.simionato@gmail.com
+"""Run doctest on text files. Examples of usage:
+
+$ doct file.txt # run the tests in file.txt
+$ doct -v file.txt # run the tests in verbose mode
+$ doct -u file.txt # run the tests as unittests
+$ doct directory # run all the tests in all the text files into directory.
+
+For simplicity, this version is not recursive.
+"""
+
+import os, sys, doctest, time, textwrap, re, types, unittest
+
+# 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 code blocks
+def extractscript(txt):
+ for MO in SCRIPT.finditer(txt):
+ yield MO.group(1), textwrap.dedent(MO.group(2))
+
+class TestRunner(object):
+ def __init__(self, verbose=False, unitest=False):
+ self.verbose = verbose
+ self.unitest = unitest
+ if unitest:
+ self.DocTestSuite = doctest.DocTestSuite
+ self.unitTestRunner = unittest.TextTestRunner(verbosity=verbose)
+ else:
+ self.docTestParser = doctest.DocTestParser()
+ self.docTestRunner = doctest.DocTestRunner(verbose=verbose)
+
+ def utest(self, fname, txt):
+ """Converts doctests to unittests."""
+ # works by dynamically generating a module with a suitable docstring
+ suite = self.DocTestSuite(types.ModuleType(fname, txt))
+ self.unitTestRunner.run(suite)
+ return ''
+
+ def dtest(self, fname, txt):
+ """Making dtest similar to utest."""
+ dt = self.docTestParser.get_doctest(txt, {}, fname, fname, 0)
+ t0 = time.clock()
+ failed, total = self.docTestRunner.run(dt)
+ if failed:
+ return ''
+ else:
+ return "%s: %s tests passed in %s seconds\n" % (
+ fname, total, time.clock() - t0)
+
+ def run(self, txt='', fname=''):
+ if fname:
+ txt += file(fname, 'U').read()
+ else:
+ fname = '<current-buffer>'
+ scriptnames = []; scriptdict = {}
+ for scriptname, script in extractscript(txt): # read scripts
+ if scriptname not in scriptnames:
+ scriptdict[scriptname] = script
+ scriptnames.append(scriptname)
+ else:
+ scriptdict[scriptname] += script
+ for scriptname in scriptnames: # save scripts
+ code = '# ' + scriptname + scriptdict[scriptname]
+ print >> file(scriptname, 'w'), code
+ if self.unitest:
+ return self.utest(fname, txt)
+ else:
+ return self.dtest(fname, txt)
+
+if __name__=='__main__':
+ # strip the option arguments
+ args = [arg for arg in sys.argv[1:] if not arg.startswith("-")]
+ if not args: # print usage message
+ print __doc__
+ if len(args) > 1:
+ print "Too many args"
+ else: # run
+ arg = args[0]
+ verbose = "-v" in sys.argv
+ unitest = "-u" in sys.argv
+ if os.path.isdir(arg):
+ for fname in os.listdir(arg):
+ f = os.path.join(arg, fname)
+ print TestRunner(verbose, unitest).run(fname=f)
+ elif os.path.isfile(arg):
+ print TestRunner(verbose, unitest).run(fname=args[0])
+ else:
+ raise IOError("File %s not found" % arg)
diff --git a/pypers/recipes/doct24.py b/pypers/recipes/doct24.py
new file mode 100755
index 0000000..95d6b36
--- /dev/null
+++ b/pypers/recipes/doct24.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python2.4
+# Author: michele.simionato@gmail.com
+"""Run doctest on text files. Examples of usage:
+
+$ doct file.txt # run the tests in file.txt
+$ doct -v file.txt # run the tests in verbose mode
+$ doct -u file.txt # run the tests as unittests
+$ doct directory # run all the tests in all the text files into directory.
+
+For simplicity, this version is not recursive.
+"""
+
+import os, sys, doctest, time, textwrap, re, types, unittest
+
+# 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 code blocks
+def extractscript(txt):
+ for MO in SCRIPT.finditer(txt):
+ yield MO.group(1), textwrap.dedent(MO.group(2))
+
+class TestRunner(object):
+ def __init__(self, verbose=False, unitest=False):
+ self.verbose = verbose
+ self.unitest = unitest
+ if unitest:
+ self.DocTestSuite = doctest.DocTestSuite
+ self.unitTestRunner = unittest.TextTestRunner(verbosity=verbose)
+ else:
+ self.docTestParser = doctest.DocTestParser()
+ self.docTestRunner = doctest.DocTestRunner(verbose=verbose)
+
+ def utest(self, fname, txt):
+ """Converts doctests to unittests."""
+ # works by dynamically generating a module with a suitable docstring
+ suite = self.DocTestSuite(types.ModuleType(fname, txt))
+ self.unitTestRunner.run(suite)
+ return ''
+
+ def dtest(self, fname, txt):
+ """Making dtest similar to utest."""
+ dt = self.docTestParser.get_doctest(txt, {}, fname, fname, 0)
+ t0 = time.clock()
+ failed, total = self.docTestRunner.run(dt)
+ if failed:
+ return ''
+ else:
+ return "%s: %s tests passed in %s seconds\n" % (
+ fname, total, time.clock() - t0)
+
+ def run(self, txt='', fname=''):
+ if fname:
+ txt += file(fname, 'U').read()
+ else:
+ fname = '<current-buffer>'
+ scriptnames = []; scriptdict = {}
+ for scriptname, script in extractscript(txt): # read scripts
+ if scriptname not in scriptnames:
+ scriptdict[scriptname] = script
+ scriptnames.append(scriptname)
+ else:
+ scriptdict[scriptname] += script
+ for scriptname in scriptnames: # save scripts
+ code = '# ' + scriptname + scriptdict[scriptname]
+ print >> file(scriptname, 'w'), code
+ if self.unitest:
+ return self.utest(fname, txt)
+ else:
+ return self.dtest(fname, txt)
+
+if __name__=='__main__':
+ # strip the option arguments
+ args = [arg for arg in sys.argv[1:] if not arg.startswith("-")]
+ if not args: # print usage message
+ print __doc__
+ if len(args) > 1:
+ print "Too many args"
+ else: # run
+ arg = args[0]
+ verbose = "-v" in sys.argv
+ unitest = "-u" in sys.argv
+ if os.path.isdir(arg):
+ for fname in os.listdir(arg):
+ f = os.path.join(arg, fname)
+ print TestRunner(verbose, unitest).run(fname=f)
+ elif os.path.isfile(arg):
+ print TestRunner(verbose, unitest).run(fname=args[0])
+ else:
+ raise IOError("File %s not found" % arg)
diff --git a/pypers/recipes/doctester.html b/pypers/recipes/doctester.html
new file mode 100755
index 0000000..0223832
--- /dev/null
+++ b/pypers/recipes/doctester.html
@@ -0,0 +1,130 @@
+<?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></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<p>I use this little script a lot: to test my posts to
+c.l.py, to test my articles, and to test my libraries.
+Since the script is extremely simple and useful, I thought I
+will share it.</p>
+<p>The first thing you need is a text like this, with
+cut and pasted interpreter sessions:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; 1 + 1
+2
+</pre>
+<p>The doctester will look for snippets of this form and will
+test them. The magic is performed by the doctest module in the
+standard library. I have just added the possibity of inserting
+named modules in the text file, like this one:</p>
+<pre class="literal-block">
+#&lt;example_module.py&gt;
+
+a = 1
+
+#&lt;/example_module.py&gt;
+</pre>
+<p>The doctester will extract code like this and save it in your
+current directory, under the name <tt class="docutils literal"><span class="pre">example_module.py</span></tt>,
+<em>before</em> running the tests. In this way you can import
+the module in your tests:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example_module import a
+&gt;&gt;&gt; print a
+1
+</pre>
+<p>You may define any number of modules in the same way.
+You can also add code to a previously defined module, simply by
+repeating the module name:</p>
+<pre class="literal-block">
+#&lt;example_module.py&gt;
+
+b = 2
+
+#&lt;/example_module.py&gt;
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; from example_module import b
+&gt;&gt;&gt; print b
+2
+</pre>
+<p>Ideally, in future extensions, it will be possible to insert snippets
+of code in other languages (for instance bash).</p>
+<p>The doctester can be used from the command line or called from
+an external program. For instance, you could pass to the doctester
+this text you are reading now: suppose it is stored in a file
+called doctester.txt, you will get</p>
+<pre class="literal-block">
+$ python doctester.py &lt; doctester.txt
+doctest: 0 failed 5 ok
+</pre>
+<p>or, if you prefer a more explicit output,</p>
+<pre class="literal-block">
+$ python doctester.py -v &lt; doctester.txt
+Trying:
+ 1 + 1
+Expecting:
+ 2
+ok
+Trying:
+ from example_module import a
+Expecting nothing
+ok
+Trying:
+ print a
+Expecting:
+ 1
+ok
+Trying:
+ from example_module import b
+Expecting nothing
+ok
+Trying:
+ print b
+Expecting:
+ 2
+ok
+1 items passed all tests:
+ 5 tests in &lt;current-buffer&gt;
+5 tests in 1 items.
+5 passed and 0 failed.
+Test passed.
+</pre>
+<p>The message says that the tests were defined in '&lt;current-buffer&gt;': the
+reason is that I usually call the doctester from Emacs, when I am editing
+the text. If you have Python 2.4 and the doctester in installed in
+your current Python path, you can just put the following in your .emacs:</p>
+<pre class="literal-block">
+;; passing the current buffer to an external tool
+(defun run-doctest ()
+ (interactive)
+ (shell-command-on-region (beginning-of-buffer)
+ (end-of-buffer)
+ &quot;python2.4 -m doctester&quot;
+ current-prefix-arg
+ current-prefix-arg))
+
+(defun run-doctest-verbose ()
+ (interactive)
+ (shell-command-on-region (beginning-of-buffer)
+ (end-of-buffer)
+ &quot;python2.4 -m doctester -v&quot;
+ current-prefix-arg
+ current-prefix-arg))
+
+;; F6 for regular output, SHIFT-F6 for verbose output
+(global-set-key [f6] 'run-doctest)
+(global-set-key [(shift f6)] 'run-doctest-verbose)
+</pre>
+<p>If you have Python 2.3 you may want to work a bit more, or
+just insert the full pathname of the doctester script.
+Obviously you may change the keybindings to whatever you like.</p>
+</div>
+</body>
+</html>
diff --git a/pypers/recipes/doctester.py b/pypers/recipes/doctester.py
new file mode 100755
index 0000000..3fe231a
--- /dev/null
+++ b/pypers/recipes/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/recipes/doctester.txt b/pypers/recipes/doctester.txt
new file mode 100755
index 0000000..c12c360
--- /dev/null
+++ b/pypers/recipes/doctester.txt
@@ -0,0 +1,124 @@
+I use this little script a lot: to test my posts to
+c.l.py, to test my articles, and to test my libraries.
+Since the script is extremely simple and useful, I thought I
+will share it.
+
+The first thing you need is a text like this, with
+cut and pasted interpreter sessions:
+
+>>> 1 + 1
+2
+
+The doctester will look for snippets of this form and will
+test them. The magic is performed by the doctest module in the
+standard library. I have just added the possibity of inserting
+named modules in the text file, like this one::
+
+ #<example_module.py>
+
+ a = 1
+
+ #</example_module.py>
+
+The doctester will extract code like this and save it in your
+current directory, under the name ``example_module.py``,
+*before* running the tests. In this way you can import
+the module in your tests:
+
+>>> from example_module import a
+>>> print a
+1
+
+You may define any number of modules in the same way.
+You can also add code to a previously defined module, simply by
+repeating the module name::
+
+ #<example_module.py>
+
+ b = 2
+
+ #</example_module.py>
+
+>>> from example_module import b
+>>> print b
+2
+
+Ideally, in future extensions, it will be possible to insert snippets
+of code in other languages (for instance bash).
+
+The doctester can be used from the command line or called from
+an external program. For instance, you could pass to the doctester
+this text you are reading now: suppose it is stored in a file
+called doctester.txt, you will get
+
+::
+
+ $ python doctester.py < doctester.txt
+ doctest: 0 failed of 5
+
+or, if you prefer a more explicit output,
+
+::
+
+ $ python doctester.py -v < doctester.txt
+ Trying:
+ 1 + 1
+ Expecting:
+ 2
+ ok
+ Trying:
+ from example_module import a
+ Expecting nothing
+ ok
+ Trying:
+ print a
+ Expecting:
+ 1
+ ok
+ Trying:
+ from example_module import b
+ Expecting nothing
+ ok
+ Trying:
+ print b
+ Expecting:
+ 2
+ ok
+ 1 items passed all tests:
+ 5 tests in <current-buffer>
+ 5 tests in 1 items.
+ 5 passed and 0 failed.
+ Test passed.
+
+The message says that the tests were defined in '<current-buffer>': the
+reason is that I usually call the doctester from Emacs, when I am editing
+the text. If you have Python 2.4 and the doctester in installed in
+your current Python path, you can just put the following in your .emacs::
+
+ ;; passing the current buffer to an external tool
+ (defun run-doctest ()
+ (interactive)
+ (shell-command-on-region (beginning-of-buffer)
+ (end-of-buffer)
+ "python2.4 -m doctester"
+ current-prefix-arg
+ current-prefix-arg))
+
+ (defun run-doctest-verbose ()
+ (interactive)
+ (shell-command-on-region (beginning-of-buffer)
+ (end-of-buffer)
+ "python2.4 -m doctester -v"
+ current-prefix-arg
+ current-prefix-arg))
+
+ ;; F6 for regular output, SHIFT-F6 for verbose output
+ (global-set-key [f6] 'run-doctest)
+ (global-set-key [(shift f6)] 'run-doctest-verbose)
+
+
+If you have Python 2.3 you may have to work a bit more, or
+may just insert the full pathname of the doctester script.
+Obviously you may change the keybindings to whatever you like.
+I am pretty sure you can invoke the doctester from the Other Editor
+(TM) too ;)
diff --git a/pypers/recipes/example_module.py b/pypers/recipes/example_module.py
new file mode 100755
index 0000000..adc89a7
--- /dev/null
+++ b/pypers/recipes/example_module.py
@@ -0,0 +1,9 @@
+# example_module.py
+
+a = 1
+
+
+
+b = 2
+
+
diff --git a/pypers/recipes/frozen.py b/pypers/recipes/frozen.py
new file mode 100755
index 0000000..9770960
--- /dev/null
+++ b/pypers/recipes/frozen.py
@@ -0,0 +1,27 @@
+def frozen(set):
+ "Raise an error when trying to set an undeclared name."
+ def set_attr(self,name,value):
+ if hasattr(self,name):
+ set(self,name,value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+ return set_attr
+
+class Frozen(object):
+ """Subclasses of Frozen are frozen, i.e. it is impossibile to add
+ new attributes to them and their instances."""
+ __setattr__=frozen(object.__setattr__)
+ class __metaclass__(type):
+ __setattr__=frozen(type.__setattr__)
+
+class Person(Frozen):
+ firstname=""
+ lastname=""
+ def __init__(self,firstname,lastname):
+ self.firstname=firstname
+ self.lastname=lastname
+
+Person.firstname=""
+me=Person("Michele","Simionato")
+#Person.add_an_attribute="something"
+me.add_an_attribute="something"
diff --git a/pypers/recipes/frozen.txt b/pypers/recipes/frozen.txt
new file mode 100755
index 0000000..4302896
--- /dev/null
+++ b/pypers/recipes/frozen.txt
@@ -0,0 +1,76 @@
+This recipe is here for a couple of reasons:
+1) discourage a common misuse of __slots__;
+2) show how to restrict Python dynamism.
+
+__slots__ are a Python 2.2 feature intended as a memory optimization:
+however, judging from recent posts in c.l.py, lots of people have misunderstood
+its aim, and think __slots__ is used to introduce declarations in Python.
+The reason why they think so is that it is impossible to add undeclared
+run-time attributes to instances of classes with __slots__.
+This is a limitation of __slots__, not a feature!
+
+Nevertheless there are people who want to restrict Python dynamism,
+for various reasons. The right way to do it is not via __slots__, but
+via __setattr__. Here I show a simple recipe - which maybe expanded
+and customized - to restrict the dynamism of Python classes.
+
+Notice that the recipe inhibits not only the addition of
+runtime attributes to objects, but even to classes.
+
+def frozen(set):
+ "Raise an error when trying to set an undeclared name."
+ def set_attr(self,name,value):
+ if hasattr(self,name):
+ set(self,name,value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+ return set_attr
+
+class Frozen(object):
+ """Subclasses of Frozen are frozen, i.e. it is impossibile to add
+ new attributes to them and their instances."""
+ __setattr__=frozen(object.__setattr__)
+ class __metaclass__(type):
+ __setattr__=frozen(type.__setattr__)
+
+
+Here is an example of usage:
+
+class Person(Frozen):
+ firstname=""
+ lastname=""
+ def __init__(self,firstname,lastname):
+ self.firstname=firstname
+ self.lastname=lastname
+
+me=Person("Michele","Simionato")
+
+Using this "feature" one is forced to declare the attributes of a
+class explicitly since setting an undeclared attribute raises an error:
+
+>>> Person.add_an_attribute="something" # => Attribute Error
+>>> me.add_an_attribute="something" # => Attribute Error
+
+Also, the normal Python idiom "self.somename=something" raises an error
+if "somename" is not explicitely declared in the class. In other words,
+subclasses of "Frozen" behaves as Java/C++ classes, so this limitation
+may be useful in the coding of prototypes to be converted in static
+languages.
+
+Consider for instance the following class, which declares the slots
+"x" and "y":
+
+>>> class C(object):
+... __slots__=["x","y"]
+... x=1
+... y=2
+
+If you try to add a new undeclared attribute to C instances
+
+>>> c=C()
+>>> c.z=3
+Traceback (most recent call last):
+ File "<interactive input>", line 1, in ?
+AttributeError: 'C' object has no attribute 'z'
+
+you get an attribute error.
diff --git a/pypers/recipes/indented_lines.py b/pypers/recipes/indented_lines.py
new file mode 100755
index 0000000..e62ed07
--- /dev/null
+++ b/pypers/recipes/indented_lines.py
@@ -0,0 +1,21 @@
+from inspect import indentsize
+"A simple shortcut"
+
+from itertools import ifilter
+from cStringIO import StringIO
+
+stream=StringIO("""
+This is a trial text.
+It contains a couple of indented lines:
+
+ "This is the first one"
+
+and
+
+ "This is the second one"
+
+""")
+
+print filter(indentsize,stream)
+for line in ifilter(indentsize,stream):
+ print line,
diff --git a/pypers/recipes/noconflict.txt b/pypers/recipes/noconflict.txt
new file mode 100755
index 0000000..ce30dbf
--- /dev/null
+++ b/pypers/recipes/noconflict.txt
@@ -0,0 +1,125 @@
+
+> Infatti, ho passato una serata interessante a farlo. Vorrei mettere
+> nei credits anche David (e magari Phil Eby?), hai loro email
+> "correnti"?
+
+Sì, mertz@gnosis.cx e pje@telecommunity.com. Il criticismo di Eby
+soprattutto e' stato importante. Tutta la storia e' documentata
+in questo thread:
+
+http://groups.google.com/groups?hl=en&lr=&threadm=25b5433d.0306081048.1d9ad5dd%40posting.google.com&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26q%3Deby%2Bmetaclass%2Bconflict%26btnG%3DSearch%26meta%3Dgroup%253Dcomp.lang.python.*
+
+> Sarebbe carino avere qualcosa del genere in Python 2.5, effettivamente.
+> Ma e` da scrivere in C e in una parte gia` complicata, quindi la
+> semplicita` e chiarezza per partire sono cruciali. Poi bisogna vedere
+> se occorre qualche indicatore sintattico, hmmm...
+
+Infatti sarebbe da scrivere in C ma io pensavo ad un prototipo in
+puro Python. Tanto non e' che tipicamente le classi si creino a
+migliaia, l'efficienza non e' un vero problema. Il vero problema
+e' scrivere una soluzione generale che sia nello stesso tempo
+comprensibile e manutenibile :-(.
+
+Ho trovato tra le mie note un'altra versione della ricetta che fa
+uso di una funzione 'remove_redundant', che avevo introdotto
+appunto per migliorare la leggibilita'. Ti mando quella versione piu'
+altri cambiamenti: nota in particolare il cambiamento
+nella segnatura di classmaker.
+
+Sarebbe anche da mettere una nota sul fatto che
+noconflict.classmaker() ritorna una funzione che genera la classe
+usando la metaclasse corretta, ma NON ritorna direttamente la
+metaclasse corretta (insomma il right hand side of __metaclass__
+non deve necessariamente essere una metaclasse).
+
+Tra l'altro, ho visto che l'implementazione di string.Template usa
+metaclasse non banale, e' la prima volta che ne vedo una nella libreria
+standard. Sei a conoscenza di altri esempi?
+
+########################### noconflict.py #################################
+
+import sets
+
+def remove_redundant(classes):
+ """Given a sequence of (meta)classes, removes the redundant ones,
+ i.e. the (meta)classes implied (in the sense of being superclasses
+ or duplicated classes) by the others."""
+ remove_set = sets.Set()
+ ls = list(classes)
+ for c in classes:
+ is_super_class_of_at_least_one = True in [
+ (issubclass(C, c) and c is not C) for C in ls]
+ if c in remove_set or is_super_class_of_at_least_one:
+ ls.remove(c) # if c is less specific or duplicated
+ else:
+ remove_set.add(c)
+ return tuple(ls)
+
+memoized_metaclasses_map = {}
+
+def _get_noconflict_metaclass(bases, left_metas, right_metas):
+ # make the tuple of all needed metaclasses in specified priority order
+ metas = left_metas + tuple(map(type, bases)) + right_metas
+ non_trivial_metas = remove_redundant(metas)
+ # return existing confict-solving meta if any
+ if non_trivial_metas in memoized_metaclasses_map:
+ return memoized_metaclasses_map[non_trivial_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ if not non_trivial_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(non_trivial_metas) == 1: # another trivial case
+ meta = non_trivial_metas[0]
+ else: # nontrivial, gotta work...
+ metaname = '_' + ''.join([m.__name__ for m in non_trivial_metas])
+ meta = classmaker()(metaname, non_trivial_metas, {})
+ memoized_metaclasses_map[non_trivial_metas] = meta
+ return meta
+
+def classmaker(inject_left = (), inject_right = ()):
+ def make_class(name, bases, adict):
+ metaclass = _get_noconflict_metaclass(bases, inject_left, inject_right)
+ return metaclass(name, bases, adict)
+ return make_class
+
+
+__test__ = dict(
+ ex1 = """
+>>> class Meta_A(type): pass
+...
+>>> class Meta_B(type): pass
+...
+>>> class A: __metaclass__ = Meta_A
+...
+>>> class B: __metaclass__ = Meta_B
+...
+>>> class C(A, B): pass
+...
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+>>> import __main__ as noconflict
+>>> class Meta_C(Meta_A, Meta_B): pass
+...
+>>> class C(A, B): __metaclass__ = Meta_C
+...
+
+>>> class C(A, B): __metaclass__ = noconflict.classmaker()
+...
+>>> class D(A):
+... __metaclass__ = noconflict.classmaker(inject_left=(Meta_B,))
+""" ,
+ remove_redundant="""
+ >>> class C1(object): pass
+ ...
+
+ >>> class C2(C1): pass
+ ...
+ >>> from __main__ import remove_redundant
+ >>> remove_redundant((C1, C2, C1))
+ (<class '__main__.C2'>,)
+""")
+
+if __name__ == "__main__":
+ import doctest,__main__
+ doctest.testmod(__main__)
diff --git a/pypers/recipes/noconflict_alex.py b/pypers/recipes/noconflict_alex.py
new file mode 100755
index 0000000..542e086
--- /dev/null
+++ b/pypers/recipes/noconflict_alex.py
@@ -0,0 +1,95 @@
+# noconflict_alex.py
+
+import inspect, types
+try: set
+except NameError:
+ from sets import Set as set
+
+memoized_metaclasses_map = {}
+
+def uniques(sequence, skipset):
+ for item in sequence:
+ if item not in skipset:
+ skipset.add(item)
+ yield item# noconflict.py
+
+def remove_redundant(classes):
+ redundant = set([types.ClassType])
+ for c in classes:
+ redundant.update(inspect.getmro(c)[1:])
+ return tuple(uniques(classes, redundant))
+
+def offending_metaclass_in(metas):
+ for m in metas:
+ if not issubclass(m, type): return True
+ return False
+
+def _get_noconflict_metaclass(bases, left_metas, right_metas):
+ # 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
+ if not needed_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(needed_metas) == 1: # another trivial case
+ meta = needed_metas[0]
+ elif offending_metaclass_in(needed_metas): # es. Zope Extension Classes
+ 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
+
+__test__ = dict(
+ ex1 = """
+>>> class Meta_A(type): pass
+...
+>>> class Meta_B(type): pass
+...
+>>> class A: __metaclass__ = Meta_A
+...
+>>> class B: __metaclass__ = Meta_B
+...
+>>> class C(A, B): pass
+...
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+>>> import __main__ as noconflict
+>>> class Meta_C(Meta_A, Meta_B): pass
+...
+>>> class C(A, B): __metaclass__ = Meta_C
+...
+
+>>> class C(A, B): __metaclass__ = noconflict.classmaker()
+...
+>>> class D(A):
+... __metaclass__ = noconflict.classmaker((Meta_B,))
+""",
+ remove_redundant="""
+ >>> class C1(object): pass
+ ...
+
+ >>> class C2(object): pass
+ ...
+ >>> from __main__ import remove_redundant
+ >>> remove_redundant((C1, C2, C1))
+ (<class '__main__.C1'>, <class '__main__.C2'>)
+""")
+
+if __name__ == "__main__":
+ import doctest,__main__
+ doctest.testmod(__main__)
diff --git a/pypers/recipes/optparse.html b/pypers/recipes/optparse.html
new file mode 100755
index 0000000..1399714
--- /dev/null
+++ b/pypers/recipes/optparse.html
@@ -0,0 +1,94 @@
+<?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.1: http://docutils.sourceforge.net/" />
+<title>Parsing the command line</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="parsing-the-command-line">
+<h1 class="title">Parsing the command line</h1>
+<p>The module optparse was a great addition to Python 2.3, since it is much more
+powerful and easier to use than getopt. Using optparse, writing command-line
+tools is a breeze. However, the power of optparse comes together with a certain
+verbosity. This recipe allows to use optparse with a minimum of boilerplate,
+trading flexibility for easy of use. Still, it covers 95% of my common needs,
+so I think it may be useful to others.</p>
+<p>The following script is an example of how to use the optionparse module.</p>
+<p>&lt;pre&gt;</p>
+<p>&quot;&quot;&quot;An example script invoking optionparse.</p>
+<blockquote>
+usage: %prog [options] args
+-p, --positional: print positional arguments
+-1, --option1=OPTION1: print option1
+-2, --option2=OPTION2: print option2</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>optparse.txt</tt>, line 21)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>&quot;&quot;&quot;</p>
+<p>import optionparse
+opt, args = optionparse.parse(__doc__)
+if not opt and not args:</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt>optparse.txt</tt>, line 26)</p>
+Unexpected indentation.</div>
+<blockquote>
+optionparse.exit()</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt>optparse.txt</tt>, line 27)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<dl>
+<dt>if opt.positional:</dt>
+<dd>print args</dd>
+<dt>if opt.option1:</dt>
+<dd>print opt.option1</dd>
+<dt>if opt.option2:</dt>
+<dd>print opt.option2</dd>
+</dl>
+<p>&lt;/pre&gt;</p>
+<p>The optionparse.parse() function parses the docstring and internally builds
+an option parser object using optparse; then it uses that parser to
+parse the command line arguments. It returns an object containing
+the given options and a list of positional arguments.</p>
+<p>If no options and positional arguments are given, the script
+exits and returns an useful message:</p>
+<p>&lt;pre&gt;</p>
+<p>$ python example.py
+An example script invoking optionparse.</p>
+<blockquote>
+usage: example.py [options] args
+-p, --positional: print positional arguments
+-1, --option1=OPTION1: print option1
+-2, --option2=OPTION2: print option2</blockquote>
+<p>&lt;/pre&gt;</p>
+<p>A similar message is also obtained if the -h or --help option is
+passed.</p>
+<p>If the -p flag is passed, the list of positional arguments is displayed:</p>
+<p>&lt;pre&gt;</p>
+<p>$ python example.py -p <a href="#id1" name="id2"><span class="problematic" id="id2">*</span></a>.txt
+[&lt;the list of text files in the current directory&gt;]</p>
+<div class="system-message" id="id1">
+<p class="system-message-title">System Message: <a name="id1">WARNING/2</a> (<tt>optparse.txt</tt>, line 63); <em><a href="#id2">backlink</a></em></p>
+Inline emphasis start-string without end-string.</div>
+<p>&lt;/pre&gt;</p>
+<p>If the option argument 1 or 2 are passed, they are displayed:</p>
+<p>&lt;pre&gt;</p>
+<p>$ python example.py -1file1.txt -2file2.txt
+file1.txt
+file2.txt</p>
+<p>&lt;/pre&gt;</p>
+<p>I think you get the idea. Notice that with the current implementation
+there are restrictions with the format of the usage block in the
+docstring: for instance it cannot contain blank lines and one must
+be careful with characters such as &quot;:&quot; &quot;,&quot; &quot;=&quot;.</p>
+</div>
+<hr class="footer" />
+<div class="footer">
+<a class="reference" href="optparse.txt">View document source</a>.
+Generated on: 2004-04-18 07:16 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/recipes/optparse.txt b/pypers/recipes/optparse.txt
new file mode 100755
index 0000000..825f128
--- /dev/null
+++ b/pypers/recipes/optparse.txt
@@ -0,0 +1,86 @@
+Parsing the command line
+=========================
+
+The module optparse was a great addition to Python 2.3, since it is much more
+powerful and easier to use than getopt. Using optparse, writing command-line
+tools is a breeze. However, the power of optparse comes together with a certain
+verbosity. This recipe allows to use optparse with a minimum of boilerplate,
+trading flexibility for easy of use. Still, it covers 95% of my common needs,
+so I think it may be useful to others.
+
+The following script is an example of how to use the recipe.
+
+<pre>
+
+"""An example script invoking optionparse, my wrapper around optparse.
+
+ usage: %prog [options] args
+ -p, --positional: print positional arguments
+ -1, --option1=OPTION1: print option1
+ -2, --option2=OPTION2: print option2
+"""
+
+import optionparse
+opt, args = optionparse.parse(__doc__)
+if not opt and not args:
+ optionparse.exit()
+if opt.positional:
+ print args
+if opt.option1:
+ print opt.option1
+if opt.option2:
+ print opt.option2
+
+</pre>
+
+The optionparse.parse() function parses the docstring and internally builds
+an option parser object using optparse; then it uses that parser to
+parse the command line arguments (please do not confuse parsing the
+docstring with parsing the command line!) It returns an object containing
+the given options and a list of positional arguments.
+
+If no options and no positional arguments are given, the script
+exits and returns an helpful message:
+
+<pre>
+
+$ python example.py
+An example script invoking optionparse.
+
+ usage: example.py [options] args
+ -p, --positional: print positional arguments
+ -1, --option1=OPTION1: print option1
+ -2, --option2=OPTION2: print option2
+
+</pre>
+
+A similar message is also obtained if the -h or --help option is
+passed.
+
+If the -p flag is passed, the list of positional arguments is displayed:
+
+<pre>
+
+$ python example.py -p *.txt
+[<the list of text files in the current directory>]
+
+</pre>
+
+
+If the option argument 1 or 2 are passed, they are displayed:
+
+<pre>
+
+$ python example.py -1hello -2world
+hello
+world
+
+</pre>
+
+I think you get the idea. Within the current implementation
+there are restrictions with the format of the usage block in the
+docstring: for instance it cannot contain blank lines and one must
+be careful with characters such as ":" "," "=". It is up to you
+to build up a more sophisticated parser, if you care enough.
+The purpose of this recipe is just to give the idea.
+
diff --git a/pypers/recipes/prova.txt b/pypers/recipes/prova.txt
new file mode 100755
index 0000000..36a2abd
--- /dev/null
+++ b/pypers/recipes/prova.txt
@@ -0,0 +1,3 @@
+>>> a = 1
+>>> print a
+1
diff --git a/pypers/recipes/solving_alex.txt b/pypers/recipes/solving_alex.txt
new file mode 100755
index 0000000..c49e4de
--- /dev/null
+++ b/pypers/recipes/solving_alex.txt
@@ -0,0 +1,209 @@
+
+-- recipe: 204197 #34
+
+-- title: Solving Metaclass Conflicts
+
+-- credits:
+Michele Simionato (mis6@pitt.edu)
+
+-- comments:
++1, hard but crucial, check Eby and Mertz are also credited -- SOLVING THE METACLASS CONFLICT
+
+-- problem:
+You need to need to multiply inherit from several classes that may come
+from several metaclasses; therefore, you need to generate automatically
+a custom metaclass to solve any possible metaclass conflicts.
+
+-- solution
+-- p
+Given a set of metaclasses, adding a further metaclass <r>m</r> to the
+mix will give no problem if if <r>m</r> is already a superclass of any
+of them. In particular, there is no problem if <c>m is type</c>, since
+<t>type</t> is the superclass of all metaclasses, like <t>object</t> is
+the superclass of all classes. Thus, we first need this easy function:
+-- code
+def _problem_m(m, metas):
+ if m is type: return False
+ for M in metas:
+ if issubclass(M, m): return False
+ return True
+-- !code
+Given a set of base classes as well as a set of existing metaclasses,
+then, we can determine the set of metaclasses of those base classes'
+that need to be added as they might otherwise give conflicts. Since
+order of inheritance matters in Python, we need to return a sequence of
+such metas; for convenience, we build and return a list of them.
+-- code
+def _metas_to_add(bases, metas):
+ metas = list(metas)
+ must_add = []
+ for b in bases:
+ m = type(b)
+ if _problem_m(m, metas):
+ must_add.append(m)
+ metas.append(m)
+ return must_add
+-- !code
+We now have the tools to obtain (and generate, if needed) a suitable
+metaclass, given a set of base classes and a set of metaclasses to
+inject explicitly. Since, again, order of inheritance matters in
+Python, we also need a boolean parameter to indicate whether the new or
+injected metaclasses get priority, that is, go to the front of the
+sequence of metaclasses. It's also important to avoid generating more
+than one metaclass to solve the same potential conflicts, so we will
+also keep a <q>memoization</q> dictionary to ensure that:
+-- code
+noconflict_metaclass_for_metaclasses_memo = {}
+def _obtain_noconflict_metaclass(bases, injected_metas, injected_priority):
+ must_add = tuple(_metas_to_add(bases, injected_metas))
+ # make the tuple of all needed metaclasses in specified priority order
+ if injected_priority:
+ all_metas = must_add + tuple(injected_metas)
+ else:
+ all_metas = tuple(injected_metas) + must_add
+ # return existing confict-solving meta if any
+ if all_metas in noconflict_metaclass_for_metaclasses_memo:
+ return noconflict_metaclass_for_metaclasses_memo[all_metas]
+ # nope: compute, memoize and return needed conflict-solving meta
+ if not all_metas: # wee, a trivial case, happy us
+ meta = type
+ elif len(all_metas) == 1: # another trivial case
+ meta = all_metas[0]
+ else: # nontrivial, gotta work...
+ metaname = '_' + ''.join([m.__name__ for m in all_metas])
+ meta = classmaker()(metaname, all_metas, {})
+ noconflict_metaclass_for_metaclasses_memo[all_metas] = meta
+ return meta
+-- !code
+If you're reading this code carefully, you will have noticed towards the
+end of this function <v>_obtain_supermetaclass</v> the appearance of a
+name that is yet unknown, specifically <v>classmaker</v>, called without
+arguments. You may have recognized by the pattern of the code where
+this new name is used that we are generating a class (specifically, a
+conflict-resolving metaclass that inherits from all needed metas) by
+calling <q>something</q> and passing as arguments the name, bases and
+dictionary (here, an empty dictionary). So, why don't we just call the
+<t>type</t> built-in? Answer: because metaclasses could have custom
+metaclasses, too &mdash; and so on, if not ad infinitum, at least for
+more than one metalevel, until we do get to the base class of all
+metaclasses, <t>type</t>. So, we need mutual recursion between the
+function we just wrote, to obtain (possibly build) a metaclass to solve
+conflicts, and the factory-function <v>classmaker</v> that uses this
+conflict-solving metaclass appropriately for class-building purposes.
+Specifically, <v>classmaker</v> needs to be a closure, and here it is:
+-- code
+def classmaker(*metas, **options):
+ injected_priority = options.pop('injected_priority', True)
+ if options:
+ raise TypeError, 'ignored options: %r' % options
+ def make_class(name, bases, adict):
+ metaclass = _obtain_noconflict_metaclass(bases, metas, injected_priority)
+ return metaclass(name, bases, adict)
+ return make_class
+-- !code
+In all of our code outside of this <f>noconflict.py</f> module, we will
+only use <v>noconflict.classmaker</v>, calling it with metaclasses we
+want to inject, and optionally the priority-indicator flag, to obtain a
+callable that we can then use just like a metaclass to build new class
+objects given names, bases and dictionary, but with the assurance that
+metatype conflicts cannot occur. Phew. Now <h>that</h> was worth it,
+wasn't it?!
+
+-- discuss:
+-- p
+Here is the simplest case where we can have a metatype confict: multiply
+inheriting from two classes with independent metaclasses. In a
+pedagogically simplified toy-level examples, that could be, say:
+-- code
+>>> class Meta_A(type): pass
+...
+>>> class Meta_B(type): pass
+...
+>>> class A: __metaclass__ = Meta_A
+...
+>>> class B: __metaclass__ = Meta_B
+...
+>>> class C(A, B): pass
+<o>Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+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</o>
+>>>
+-- !code
+A class normally inherits its metaclass from its bases, but when the
+bases have distinct metaclasses, the metatype constraint that Python
+expresses so tersely in this error message applies. So, we need to
+build a new metaclass, say <r>Meta_C</r>, which inherits from both
+<r>Meta_A</r> and <r>Meta_B</r>. For a demonstration, see the book
+that's rightly considered the Bible of metaclasses, <citetitle>Putting
+Metaclasses to Work: A New Dimension in Object-Oriented
+Programming</citetitle>, by <citation>Ira R. Forman and Scott H.
+Danforth</citation> (Addison Wesley, 1999).
+
+-- p
+Python does not do magic: it does not automatically create
+<r>Meta_C</r>. Rather, it raises a <t>TypeError</t> to ensure the
+programmer is aware of the problem. In simple cases, the programmer can
+solve the metatype conflict by hand, as follows:
+-- code
+>>> class Meta_C(Meta_A, Meta_B): pass
+>>> class C(A, B): __metaclass__ = Meta_C
+-- !code
+In this case, everything works smoothly.
+
+-- p
+The key point of this recipe is to show an automatic way to resolve
+metatype conflicts, rather than having to do it by hand every time.
+Having saved all the code from this recipe's <q>Solution</q> into module
+<f>noconflict.py</f> somewhere along your Python's <t>sys.path</t>, you
+may then make class <v>C</v> with automatic conflict resolution, as
+follows:
+-- code
+>>> import noconflict
+>>> class C(A, B): __metaclass__ = noconflict.classmaker()
+-- !code
+Automating the resolution of the metatype conflict has many pluses, even
+in simple cases. Thanks to the <q>memoizing</q> technique used in
+<v>noconflict.py</v>, the same conflict-resolving metaclass will get
+used for any sequence of conflicting metaclasses. Moreover, with this
+approach you may also inject other metaclasses explicitly, beyond those
+you get from your base classes, and again avoid conflicts. Consider:
+-- code
+>>> class D(A): __metaclass__ = Meta_B
+<o>Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+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</o>
+-- !code
+This metatype conflict is resolved just as easily as the former one:
+-- code
+>>> class D(A): __metaclass__ = noconflict.classmaker(Meta_B)
+-- !code
+If you want the the implicitly inherited <v>Meta_A</v> to take priority
+over the explicitly injected <v>Meta_B</v>, <v>noconflict.classmaker</v>
+allows that easily, too:
+-- code
+>>> class D(A): __metaclass__ = noconflict.classmaker(Meta_B, injected_priority=False)
+-- !code
+
+-- p
+The code presented in this recipe's <q>Solution</q> takes pains to avoid
+any subclassing that is not strictly necessary, and also uses mutual
+recursion to avoid any meta-level of meta-meta-type conflicts. You
+might never meet higher-order-meta conflict anyway, but if you adopt the
+code presented in this recipe you need not even worry about them.
+
+-- p
+I thank David Mertz for help in polishing the original version of the code.
+This version has largerly benefited from discussions with Phillip J.
+Eby, and Alex Martelli did his best to try to make the recipe's code as
+explicit and understandable as he could make it.
+
+-- see_also
+-- p
+<citetitle>Putting Metaclasses to Work: A New Dimension in
+Object-Oriented Programming</citetitle>, by <citation>Ira R. Forman and
+Scott H. Danforth</citation> (Addison Wesley, 1999).
+
diff --git a/pypers/recipes/superattr.py b/pypers/recipes/superattr.py
new file mode 100755
index 0000000..5228057
--- /dev/null
+++ b/pypers/recipes/superattr.py
@@ -0,0 +1,20 @@
+class Super(object):
+ def __init__(self, cls, meth):
+ self.cls = cls
+ self.meth = meth
+ def __call__(self, *args, **kw):
+ return getattr(super(self.cls, obj or objtype), self.meth)
+
+class B(object):
+ def __init__(self):
+ print "B.__init__"
+
+class C(object):
+ def __init__(self):
+ print "C.__init__"
+ print self.__init__.super(self)
+
+C.__dict__["__init__"].super = Super(C, "__init__")
+
+
+c = C()
diff --git a/pypers/recipes/supersugar.py b/pypers/recipes/supersugar.py
new file mode 100755
index 0000000..9ff8be0
--- /dev/null
+++ b/pypers/recipes/supersugar.py
@@ -0,0 +1,42 @@
+"""Implements a nice syntax for cooperative methods.
+Could be made working for staticmethods and classmethods,
+but who needs them anyway? ;)"""
+
+# think how to improve this for generic descriptors, especially
+# for classmethods
+
+import inspect
+
+def second_arg(func):
+ args = inspect.getargspec(func)[0]
+ if len(args) >= 2: return args[1]
+
+class _Cooperative(type):
+ def __init__(cls, name, bases, dic):
+ for n, func in dic.iteritems():
+ setattr(cls, n, func)
+ def __setattr__(cls, name, func):
+ set = super(_Cooperative, cls).__setattr__
+ if inspect.isfunction(func) and second_arg(func) == "super":
+ set(name, lambda self, *args, **kw :
+ func(self, super(cls, self), *args, **kw))
+ else:
+ set(name, func)
+
+class Cooperative:
+ __metaclass__ = _Cooperative
+
+
+if __name__ == "__main__":
+ class B(Cooperative):
+ def print_(self):
+ print "B",
+ class C(B):
+ def print_(self, super):
+ super.print_()
+ print "C",
+ class D(C):
+ def print_(self, super):
+ super.print_()
+ print "D",
+ D().print_() # => B C D
diff --git a/pypers/recipes/test/chop.txt b/pypers/recipes/test/chop.txt
new file mode 100755
index 0000000..127596c
--- /dev/null
+++ b/pypers/recipes/test/chop.txt
@@ -0,0 +1,27 @@
+#<chop.py>
+
+def chop(iterable, n):
+ bin = []
+ for i, el in enumerate(iterable):
+ bin.append(el)
+ if i % n == n-1:
+ yield bin; bin = []
+ if bin:
+ yield bin
+
+#</chop.py>
+
+>>> from chop import chop
+>>> ls = ["a", "b", "c", "d", "e", "f"]
+
+>>> print list(chop(ls, 2))
+[['a', 'b'], ['c', 'd'], ['e', 'f']]
+
+>>> print list(chop(ls, 3))
+[['a', 'b', 'c'], ['d', 'e', 'f']]
+
+>>> print list(chop(ls, 1))
+[['a'], ['b'], ['c'], ['d'], ['e'], ['f']]
+
+>>> print list(chop("abcdefg", 2))
+[['a', 'b'], ['c', 'd'], ['e', 'f'], ['g']]
diff --git a/pypers/recipes/test/chop2.txt b/pypers/recipes/test/chop2.txt
new file mode 100755
index 0000000..127596c
--- /dev/null
+++ b/pypers/recipes/test/chop2.txt
@@ -0,0 +1,27 @@
+#<chop.py>
+
+def chop(iterable, n):
+ bin = []
+ for i, el in enumerate(iterable):
+ bin.append(el)
+ if i % n == n-1:
+ yield bin; bin = []
+ if bin:
+ yield bin
+
+#</chop.py>
+
+>>> from chop import chop
+>>> ls = ["a", "b", "c", "d", "e", "f"]
+
+>>> print list(chop(ls, 2))
+[['a', 'b'], ['c', 'd'], ['e', 'f']]
+
+>>> print list(chop(ls, 3))
+[['a', 'b', 'c'], ['d', 'e', 'f']]
+
+>>> print list(chop(ls, 1))
+[['a'], ['b'], ['c'], ['d'], ['e'], ['f']]
+
+>>> print list(chop("abcdefg", 2))
+[['a', 'b'], ['c', 'd'], ['e', 'f'], ['g']]
diff --git a/pypers/recipes/testnoconflict.py b/pypers/recipes/testnoconflict.py
new file mode 100755
index 0000000..4c405bf
--- /dev/null
+++ b/pypers/recipes/testnoconflict.py
@@ -0,0 +1,245 @@
+# testnoconflict.py
+from noconflict_alex import classmaker
+
+errormsg23 = "multiple bases have instance lay-out conflict"
+errormsg24 = """Error when calling the metaclass bases
+ multiple bases have instance lay-out conflict"""
+
+try:
+ set
+except NameError: # we are using Python 2.3
+ errormsg = errormsg23
+else: # we are using Python 2.4
+ errormsg = errormsg24
+
+# First, the trivial cases
+
+#single old-style-would-be class
+class C:
+ __metaclass__ = classmaker()
+
+# here needed_metas = ()
+assert C.__class__ is type
+
+#single new style class
+class C(object):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (type,)
+assert C.__class__ is type
+
+# class inheriting from old-style
+
+class O: pass # old-style
+
+class O_: pass # old-style
+
+try:
+ class C(O):
+ __metaclass__ = classmaker()
+except TypeError,e:
+ assert str(e) == "a new-style class can't have only classic bases"
+
+try:
+ class C(O, O_):
+ __metaclass__ = classmaker()
+except TypeError,e:
+ assert str(e) == "a new-style class can't have only classic bases"
+
+# inheriting from both old style and new style works
+
+class C(O, object):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (type,)
+assert type(C) is type
+
+# the simplest non-trivial case (M_A and M_B)
+
+class M_A(type): pass
+class M_B(type): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C(A,B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_A from left
+
+class C(B):
+ __metaclass__ = classmaker((M_A,))
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+# injecting M_B from right
+
+class C(A):
+ __metaclass__ = classmaker(right_metas = (M_B,))
+
+# here needed_metas = (M_A, M_B)
+assert C.__class__.__name__ == "_M_AM_B"
+
+
+# redundant injections
+
+class C:
+ __metaclass__ = classmaker (left_metas = (M_B,), right_metas = (M_A, M_B,))
+
+# here needed_metas = (M_B, M_A), the last M_B is correctly removed
+assert C.__class__.__name__ == "_M_BM_A"
+
+# composing an old-style class and a metaclass
+
+class O: pass
+
+try:
+ class C(O, M_A):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+
+# the other way around
+
+try:
+ class C(M_A, O):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing an new-style class and a metaclass
+
+class N(object): pass
+
+try:
+ class C(N, M_A):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+
+# the other way around
+
+try:
+ class C(M_A, N):
+ __metaclass__ = classmaker()
+except TypeError, e:
+ # here needed_metas = (type, )
+ assert str(e) == errormsg
+
+# composing a non-trivial class and a metaclass
+
+class C(B, M_A):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# the other way around
+
+class C(M_A, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+
+# a more bizarre hierarchy
+
+class C(B, M_B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, B, M_B, type, object]
+
+# changing the order
+
+class C(M_B, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_B,)
+assert C.__class__ is M_B
+# C.__mro__ == [C, M_B, type, B, object]
+
+
+# meta-metaclasses
+
+class MM_X(type): pass
+class MM_Y(type): pass
+
+class M_X(type): __metaclass__ = MM_X
+class M_Y(type): __metaclass__ = MM_Y
+
+class X(type): __metaclass__ = M_X
+class Y(type): __metaclass__ = M_Y
+
+class Z(X,Y): __metaclass__ = classmaker()
+
+# here needed_metas = (M_X, M_Y)
+
+assert Z.__class__.__name__ == "_M_XM_Y"
+
+# in order to construct _M_XM_Y classmaker has to
+# construct _MM_XMM_Y first:
+
+assert Z.__class__.__class__.__name__ == "_MM_XMM_Y"
+
+class C(Z, B): __metaclass__ = classmaker()
+
+# here needed_metas = (_M_XM_Y, M_B)
+
+# composition of many metaclasses
+
+class M_A(type): pass
+class M_B(type): pass
+class M_C(M_B): pass
+class A: __metaclass__ = M_A
+class B: __metaclass__ = M_B
+
+class C: __metaclass__ = M_C
+class D: __metaclass__ = M_B
+
+class E(A, B):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+assert E.__class__.__name__ == "_M_AM_B"
+
+class F(C):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (M_A, M_B)
+
+assert F.__class__.__name__ == "M_C"
+
+class G(E, F):
+ __metaclass__ = classmaker()
+
+# here needed_metas = (_M_AM_B, M_C)
+assert G.__class__.__name__ == "__M_AM_BM_C"
+
+## Test for C-coded metaclasses, i.e. Zope ExtensionClass
+
+try: # is Zope installed?
+ from OFS.Folder import Folder # an instance of Zope ExtensionClass
+
+ class C(Folder): # no problem here
+ __metaclass__ = classmaker()
+
+ assert C.__class__.__name__ == "ExtensionClass"
+
+ try:
+ class C(Folder):
+ __metaclass__ = classmaker((M_A,))
+ except TypeError, e:
+ e[1] == 'Incompatible root metatypes'
+
+except ImportError:
+ pass # will skip the ExtensionClass test
diff --git a/pypers/regexp.txt b/pypers/regexp.txt
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/pypers/regexp.txt
diff --git a/pypers/remove.py b/pypers/remove.py
new file mode 100755
index 0000000..ecfe1bb
--- /dev/null
+++ b/pypers/remove.py
@@ -0,0 +1,11 @@
+"Remove unwanted .py files"
+
+import os
+
+def killable(name):
+ preserve='test.py', 'oopp.py', 'remove.py', 'EXECUTEME.py'
+ return name.endswith('.py') and not name.startswith('pro') and \
+ name not in preserve
+
+for f in os.listdir('.'):
+ if killable(f): os.remove(f)
diff --git a/pypers/secret.txt b/pypers/secret.txt
new file mode 100755
index 0000000..0288ed8
--- /dev/null
+++ b/pypers/secret.txt
@@ -0,0 +1,922 @@
+ADVANCED METAPROGRAMMING TECHNIQUES
+========================================================================
+
+In elementary OOP, the programmer works with objects; in advanced OOP,
+the programmer works with classes, taking full advantage of
+(multiple) inheritance and metaclasses. Metaprograming is the activity of
+building, composing and modifying classes.
+
+I will give various examples of metaprogramming techniques
+using run-time class modifications
+multiple inheritance, metaclasses, attribute descriptors and
+even simple functions.
+
+Moreover, I will show show metaclasses can change the
+semantics of Python programs: hence theire reputation of *black* magic.
+That is to say that the techniques explained here are dangerous!
+
+On code processing
+--------------------------------------------------------------------
+
+It is a good programming practice to avoid the direct modification
+of source code. Nevertheless, there are situations where the ability of
+modifying the source code *dynamically* is invaluable. Python has the
+capability of
+
+1) generating new code from scratch;
+
+2) modifying pre-existing source code;
+
+3) executing the newly created/modified code at run-time.
+
+The capability of creating source code and executing it *immediately* has
+no equivalent in static languages such as C/C++/Java and it is maybe the
+most poweful feature of dynamics languages such as Java/Python/Perl.
+This feature has been exploited to its ultimate consequences in the languages
+of the Lisp family, in which one can use incredibly poweful macros, which
+in a broad sense, are programs that write themselves
+
+In this chapter I will discuss how to implement macros in Python and I will
+present some of the miracles you may perform with this technique. To this
+aim, I will discuss various ways of manipulating Python source code, by
+using regular expressions and state machines.
+
+
+Regular expressions
+-------------------------------------------------------------------------
+
+ .. line-block::
+
+ *Some people, when confronted with a problem,
+ think "I know, I'll use regular expressions."
+ Now they have two problems.*
+ -- Jamie Zawinski
+
+
+Python source code is a kind of text and can manipulated with the same
+techniques that are used to manipulate text:
+
+1. the trivial search and replace;
+
+2. regular expressions;
+
+3. state machines;
+
+4. parser
+
+There is not very much to say about the search and replace methods: it
+is fast, efficient and it works. It should always be used whenever
+possible. However, in this chapter I will only be interested in case
+where one has to recur to something more sophisticated than a plain
+search and replace. Something that can be managed with regular expression
+or with something even more sophisticated than them: a state machine or
+even a full featured parser.
+
+
+I will *not* give a primer on regular expression here, since they are
+already well documented in the standard documentation (see Andrew's
+Kuchling 'Howto') as well in many books (for instance Mastering Regular
+Expression and 'Python in a Nutshell'). Instead, I will give various
+practical examples of usage.
+
+ >>> import re
+ >>> reobj=re.compile(r'x')
+
+
+More on metaclasses and subclassing built-in types
+-------------------------------------------------------------------------
+
+Subclassing ``list`` is easy since there are no methods returning lists
+except the methods correspondings to the '+' and '*' operators.
+Subclassing ``str`` is more complicated, since one has many methods
+that return strings. Nevertheless, it can be done with the ``AutoWrapped``
+metaclass, simply by specifying the list of the builtins to be wrapped.
+
+ ::
+
+ #<oopp.py>
+
+ class Str(str):
+ __metaclass__=AutoWrapped
+ builtinlist="""__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
+ center expandtabs join ljust lower lstrip replace rjust rstrip strip
+ swapcase title translate upper zfill""".split()
+
+ #</oopp.py>
+
+Here I show various tests.
+
+ .. doctest
+
+ >>> from oopp import Str
+ >>> sum=Str('a')+Str('b') # check the sum
+ >>> print sum, type(sum)
+ ab <class 'oopp.Str'>
+ >>> rprod=Str('a')*2 # check the right product
+ >>> print rprod,type(rprod)
+ aa <class 'oopp.Str'>
+ >>> lprod=2*Str('a') # check the left product
+ >>> print lprod,type(lprod)
+ aa <class 'oopp.Str'>
+ >>> r=Str('a').replace('a','b') # check replace
+ >>> print r,type(r)
+ b <class 'oopp.Str'>
+ >>> r=Str('a').capitalize() # check capitalize
+ >>> print r,type(r)
+ A <class 'oopp.Str'>
+
+``Str`` acts as a nice base class to built abstractions based on strings.
+In particular, regular expressions can be built on top of strings describing
+their representation (I remind that if ``x`` is a regular expression object,
+``x.pattern`` is its string representation). Then, the sum of two regular
+expressions ``x`` and ``y`` can be defined as the sum of their string
+representation, ``(x+y).pattern=x.pattern+y.pattern``. Moreover, it is
+convenient to define the ``__or__`` method of two regular expression in
+such a way that ``(x | y).pattern=x.pattern+'|'+y.pattern``.
+All this can be achieved trough the following class:
+
+ ::
+
+ #<oopp.py>
+
+ class BaseRegexp(Str):
+
+ builtinlist=['__radd__', '__ror__']
+ wraplist=['__add__','__or__']
+
+ __add__ = lambda self,other: self.pattern + other
+ __or__ = lambda self,other: self.pattern+'|'+other
+
+ def __init__ (self,regexp):
+ "Adds to str methods the regexp methods"
+ if '#' in regexp:
+ reobj=re.compile(regexp,re.VERBOSE)
+ else:
+ reobj=re.compile(regexp) # non-verbose
+ for attr in dir(reobj)+['pattern']:
+ setattr(self,attr,getattr(reobj,attr))
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> aob=BaseRegexp('a')|BaseRegexp('b'); print aob
+ a|b
+ >>> print pretty(attributes(aob))
+ encode = <built-in method encode of BaseRegexp object at 0x401b25cc>
+ endswith = <built-in method endswith of BaseRegexp object at 0x401b25cc>
+ expandtabs = <function _ at 0x401b69cc>
+ find = <built-in method find of BaseRegexp object at 0x401b25cc>
+ findall = <built-in method findall of _sre.SRE_Pattern object at 0x4019b890>
+ finditer = <built-in method finditer of _sre.SRE_Pattern object at
+ 0x4019b890>
+ index = <built-in method index of BaseRegexp object at 0x401b25cc>
+ isalnum = <built-in method isalnum of BaseRegexp object at 0x401b25cc>
+ isalpha = <built-in method isalpha of BaseRegexp object at 0x401b25cc>
+ isdigit = <built-in method isdigit of BaseRegexp object at 0x401b25cc>
+ islower = <built-in method islower of BaseRegexp object at 0x401b25cc>
+ isspace = <built-in method isspace of BaseRegexp object at 0x401b25cc>
+ istitle = <built-in method istitle of BaseRegexp object at 0x401b25cc>
+ isupper = <built-in method isupper of BaseRegexp object at 0x401b25cc>
+ join = <function _ at 0x401b6a74>
+ ljust = <function _ at 0x401b6b1c>
+ lower = <function _ at 0x401b6cdc>
+ lstrip = <function _ at 0x401b6d84>
+ match = <built-in method match of _sre.SRE_Pattern object at 0x4019b890>
+ pattern = ba
+ replace = <function _ at 0x401b6ed4>
+ rfind = <built-in method rfind of BaseRegexp object at 0x401b25cc>
+ rindex = <built-in method rindex of BaseRegexp object at 0x401b25cc>
+ rjust = <function _ at 0x401ba0d4>
+ rstrip = <function _ at 0x401ba10c>
+ scanner = <built-in method scanner of _sre.SRE_Pattern object at 0x4019b890>
+ search = <built-in method search of _sre.SRE_Pattern object at 0x4019b890>
+ split = <built-in method split of _sre.SRE_Pattern object at 0x4019b890>
+ splitlines = <built-in method splitlines of BaseRegexp object at 0x401b25cc>
+ startswith = <built-in method startswith of BaseRegexp object at 0x401b25cc>
+ strip = <function _ at 0x401ba25c>
+ sub = <built-in method sub of _sre.SRE_Pattern object at 0x4019b890>
+ subn = <built-in method subn of _sre.SRE_Pattern object at 0x4019b890>
+ swapcase = <function _ at 0x401ba294>
+ title = <function _ at 0x401ba4fc>
+ translate = <function _ at 0x401ba534>
+ upper = <function _ at 0x401ba5dc>
+ wraplist = ['__add__', '__radd__', '__or__', '__ror__']
+ zfill = <function _ at 0x401ba614>
+
+ #<oopp.py>
+
+ class Regexp(BaseRegexp):
+ class __metaclass__(BaseRegexp.__metaclass__):
+ def __setattr__(cls,name,value):
+ if name==name.upper(): # all caps means regexp constant
+ if not isinstance(value,cls): value=cls(value)
+ value.name=name # set regexp name
+ BaseRegexp.__metaclass__.__setattr__(cls,name,value)
+ # basic setattr
+
+ def __call__(self,name=None):
+ "Convert the regexp to a named group with the right name"
+ name=getattr(self,'name',name)
+ if name is None: raise TypeError('Unnamed regular expression')
+ return self.__class__('(?P<%s>%s)' % (name,self.pattern))
+
+ generateblocks=generateblocks
+
+ #</oopp.py>
+
+The magic of ``Regexp.__metaclass__`` allows to generate a library of
+regular expressions in an elegant way:
+
+ ::
+
+ #<oopp.py>
+
+ r=Regexp
+
+ customize(r,
+ DOTALL =r'(?s)' , # starts the DOTALL mode; must be at the beginning
+ NAME =r'\b[a-zA-Z_]\w*', # plain Python name
+ EXTNAME=r'\b[a-zA-Z_][\w\.]*', # Python name with or without dots
+ DOTNAME=r'\b[a-zA-Z_]\w*\.[\w\.]*',# Python name with dots
+ COMMENT=r"#.*?(?=\n)", # Python comment
+ QUOTED1=r"'.+?'", # single quoted string '
+ QUOTED2=r'".+?"', # single quoted string "
+ TRIPLEQ1=r"'''.+?'''", # triple quoted string '
+ TRIPLEQ2=r'""".+?"""' # triple quoted string "
+ )
+
+ r.STRING=r.TRIPLEQ1|r.TRIPLEQ2|r.QUOTED1|r.QUOTED2
+ r.CODESEP=r.DOTALL+r.COMMENT()|r.STRING()
+
+ #</oopp.py>
+
+The trick is in the redefinition of ``__setattr__``, which magically converts
+all caps attributes in ``Regexp`` objects.
+
+The features of ``Regexp`` can be tested with the following code:
+
+ ::
+
+ #<test_re.py>
+
+ """This script looks at its own source code and extracts dotted names,
+ i.e. names containing at least one dot, such as object.attribute or
+ more general one, such as obj.attr.subattr."""
+
+ # Notice that dotted.names in comments and literal strings are ignored
+
+ from oopp import *
+ import __main__
+
+ text=inspect.getsource(__main__)
+
+ regexp=Regexp.CODESEP| Regexp.DOTNAME()
+
+ print 'Using the regular expression',regexp
+
+ print "I have found the following dotted names:\n%s" % [
+ MO.group() for MO in regexp.finditer(text) if MO.lastgroup=='DOTNAME']
+
+ #</test_re.py>
+
+with output:
+
+ ::
+
+ Using the regular expression (?s)(?P<COMMENT>#.*?(?=\n))|(?P<STRING>
+ '''.+?'''|""".+?"""|'.+?'|".+?")|(?P<DOTNAME>[a-zA-Z_]\w*\.[\w\.]*)
+ I have found the following dotted names:
+ ['inspect.getsource', 'Regexp.CODESEP', 'Regexp.DOTNAME.__call__', 'MO.group',
+ 'dotname.finditer', 'MO.lastgroup']
+
+Now one can define a good ``CodeStr`` class with replacing features
+
+Let me consider for instance the solution to the problem discussed in chapter
+4, i.e. the definition of a ``TextStr`` class able to indent and dedent
+blocks of text.
+
+ ::
+
+ #<oopp.py>
+
+ def codeprocess(code,TPO): # TPO=text processing operator
+ code=code.replace("\\'","\x01").replace('\\"','\x02')
+ genblock,out = Regexp.CODESEP.generateblocks(code),[]
+ for block in genblock:
+ out.append(TPO(block))
+ out.append(genblock.next())
+ return ''.join(out).replace("\x01","\\'").replace('\x02','\\"')
+
+ def quotencode(text):
+ return text.replace("\\'","\x01").replace('\\"','\x02')
+
+ def quotdecode(text):
+ return text.replace("\x01","\\'").replace('\x02','\\"')
+
+ #</oopp.py>
+
+Here is an example of usage: replacing 'Print' with 'print' except in
+comments and literal strings.
+
+ ::
+
+ #<codeproc.py>
+
+ from oopp import codeprocess
+
+ wrongcode=r'''
+ """Code processing example: replaces 'Print' with 'print' except in
+ comments and literal strings"""
+ Print "This program prints \"Hello World!\"" # look at this line!
+ '''
+
+ fixPrint=lambda s: s.replace('Print','print')
+ validcode=codeprocess(wrongcode,fixPrint)
+
+ print 'Source code:\n',validcode
+ print 'Output:\n'; exec validcode
+
+ #</codeproc.py>
+
+with output
+
+ ::
+
+ Source code:
+
+ """Code processing example: replaces 'Print' with 'print' except in
+ comments and literal strings"""
+ print "Prints \"Hello World!\"" # look at this line!
+
+ Output:
+
+ This program prints "Hello World!"
+
+A simple state machine
+---------------------------------------------------------------------------
+
+Regular expression, however powerful, are limited in scope since they
+cannot recognize recursive structures. For instance, they cannot parse
+parenthesized expression.
+
+The simplest way to parse a parenthesized expression is to use a state
+machine.
+
+ ::
+
+ (?:...) non-grouping
+ (?P<name>...)
+
+ (?=...) look-ahead
+ (?!...) negative
+ (?<=...) look-behind
+ (?<!...) negative
+
+ dec=\
+ """
+ paren = ( .. )
+ bracket = [ .. ]
+ brace = { .. }
+ comment = # .. \n
+ """
+
+ actions=\
+ """
+ reobj1 : R' .. (?<!\\)' -> Regexp(r''' .. ''')
+ reobj2 : R" .. (?<!\\)" -> Regexp(r""" .. """)
+ string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
+ string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
+ """
+
+ beg=0; end=1
+
+ string1[beg]=r"(?<!')'(?!')" # an isolated single quote
+ string2[beg]=r'(?<!")"(?!")' # an isolated double quote
+ string1[end]=r"(?<!\\)'(?!')" # ending single quote
+ string2[end]=r'(?<!\\)"(?!")' # ending double quote
+
+ reobj1[beg]=r"R'"
+ reobj2[beg]=r'R"'
+ reobj1[end]=string1[end] # ending single quote
+ reobj2[end]=string2[end] # ending double quote
+
+ actions=\
+ """
+ reobj1 : R' .. (?<!\\)' -> Regexp(r''' .. ''')
+ reobj2 : R" .. (?<!\\)" -> Regexp(r""" .. """)
+ string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
+ string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
+ """
+
+ beg={}; end={}; ls=[]
+ for line in decl.splitlines():
+ mode,rest=line.split(' : ')
+ s,r=rest.split(' -> ')
+
+ beg[mode],end[mode]=s.split(' .. ')
+ ls.append('(?P<beg_%s>%s)' % (mode,beg[mode]))
+ ls.append('(?P<end_%s>%s)' % (mode,end[mode]))
+
+ beg2[mode],end2[mode]=r.split(' .. ')
+ ls.append(beg2[mode])
+ ls.append(end2[mode])
+
+ delimiters='(%s)' % re.compile('|'.join(ls))
+ splitlist=['']+delimiters.split(source)
+ for delim,text in splitlist:
+ delimiters.match(delim).lastgroup
+
+Creating classes
+----------------------------------------------------------------------
+
+TODO
+
+Modifying modules
+-----------------------------------------------------------
+
+Metaclasses are extremely
+useful since they allows to change the behaviour of the code without
+changing the sources. For instance, suppose you have a large library written
+by others that you want to enhance in some way.
+
+Typically, it is always a bad idea to modify the sources, for many reasons:
+
++ touching code written by others, you may introduce new bugs;
++ you may have many scripts that requires the original version
+ of the library, not the modified one;
++ if you change the sources and then you buy the new version of the
+ library, you have to change the sources again!
+
+The solution is to enhance the proprierties of the library at run
+time, when the module is imported, by using metaclasses.
+
+To show a concrete example, let me consider the case of the module
+*commands* in the Standard Library. This module is Unix-specific,
+and cannot be used under Windows. It would be nice to have a
+metaclass able to enhance the module in such a way that
+when it is invoked on a Windows platform, Windows specific replacement
+of the Unix functions provided in the module are used. However,
+for sake of brevity, I will only give a metaclasses that display
+a nice message in the case we are in a Window platform, without
+raising an error (one could easily implement such a behaviour,
+however).
+
+ ::
+
+ #<recognizewindows.py>
+
+ import oopp,sys,commands
+
+ class WindowsAware(type):
+ def __init__(cls,*args):
+ if sys.platform=='win32':
+ for key,val in vars(cls).iteritems():
+ if isinstance(val,staticmethod):
+ setattr(cls,key,staticmethod(lambda *args:
+ "Sorry, you are (or I pretend you are) on Windows,"
+ " you cannot use the %s.module" % cls.__name__))
+
+ sys.platform="win32" #just in case you are not on Windows
+
+ commands=oopp.ClsFactory[WindowsAware](commands)
+
+ print commands.getoutput('date') #cannot be executed on Windows
+
+ #</recognizewindows.py>
+
+The output of this script is
+
+ ::
+
+ Sorry, you are on Windows, you cannot use the commands.module
+
+However, if you are on Linux and you comment out the line
+
+ ::
+
+ sys.platform="win32"
+
+you will see that the script works.
+
+Notice that the line ``commands=WindowsAware(commands)`` actually
+converts the 'commands' module in a 'commands' class, but since
+the usage is the same, this will fool all programs using the
+commands module. In this case the class factory 'WindowsAware'
+can also be thought as a module modifier. In this sense, it is
+very useful to denote the metaclass with an *adjective*.
+
+Metaclasses and attribute descriptors
+----------------------------------------------------------------------
+
+Descriptors are especially useful in conjunction with metaclasses, since
+a custom metaclass can use them as low level tools to modify the methods
+and the attributes of its instances. This allows to implement very
+sophisticated features with few lines of code.
+
+
+Notice, anyway, that
+even plain old function can be thought of as of descriptors.
+
+Descriptors share at least two features with metaclasses:
+
+1. as metaclasses, descriptors are best used as adjectives, since they
+ are intended to modify and enhance standard methods and attributes, in the
+ same sense metaclasses modify and enhance standard classes;
+
+2. as metaclasses, descriptors can change the *semantics* of Python, i.e.
+ what you see is not necessarely what you get. As such, they are a
+ dangerous feature. Use them with judgement!
+
+Now I will show a possible application of properties.
+
+#<readonly.py>
+
+ "Making read-only attributes"
+
+ class Readonly(type):
+ def __init__(cls,name,bases,dic):
+ super(Readonly,cls).__init__(name,bases,dic) # cooperative __init__
+ readonly=dic.get('__readonly__',[])
+ for attr in readonly:
+ get=lambda self,value=getattr(cls,attr): value
+ setattr(cls,attr,property(get))
+
+ class C:
+ __metaclass__=Readonly
+ __readonly__='x','y'
+ x=1
+ y=2
+
+ c=C()
+
+ print c.x,c.y
+ try:
+ c.x=3
+ except Exception,e:
+ print e
+
+ #</readonly.py>
+
+Suppose one has a given class with various kind of
+attributes (plain methods, regular methods, static methods,
+class methods, properties and data attributes) and she wants
+to trace to access to the data attributes (notice that the motivation
+for the following problem come from a real question asked in
+comp.lang.python). Then one needs to retrieve data
+attributes from the class and convert them in properties
+controlling their access syntax. The first problem is solved
+by a simple function
+
+ ::
+
+ #<oopp.py>
+
+ def isplaindata(a):
+ """A data attribute has no __get__ or __set__ attributes, is not
+ a built-in function, nor a built-in method."""
+ return not(hasattr(a,'__get__') or hasattr(a,'__set__')
+ or isinstance(a,BuiltinMethodType) or
+ isinstance(a,BuiltinFunctionType))
+
+ #</oopp.py>
+
+whereas the second problem is elegantly solved by a custom metaclass:
+
+ ::
+
+ #<tracedaccess.py>
+
+ from oopp import isplaindata,inspect
+
+ class TracedAccess(type):
+ "Metaclass converting data attributes to properties"
+ def __init__(cls,name,bases,dic):
+ cls.datadic={}
+ for a in dic:
+ if isplaindata(a):
+ cls.datadic[a]=dic[a]
+ def get(self,a=a):
+ v=cls.datadic[a]
+ print "Accessing %s, value=%s" % (a,v)
+ return v
+ def set(self,v,a=a):
+ print "Setting %s, value=%s" % (a,v)
+ cls.datadic[a]=v
+ setattr(cls,a,property(get,set))
+
+ class C(object):
+ __metaclass__ = TracedAccess
+ a1='x'
+
+ class D(C): # shows that the approach works well with inheritance
+ a2='y'
+
+ i=D()
+ i.a1 # => Accessing a1, value=x
+ i.a2 # => Accessing a2, value=y
+ i.a1='z' # => Setting a1, value=z
+ i.a1 # => Accessing a1, value=z
+
+ #</tracedaccess.py>
+
+In this example the metaclass looks at the plain data attributes (recognized
+thanks ot the ``isplaindata`` function) of its instances and put them
+in the dictionary ``cls.datadic``. Then the original attributes are replaced
+with property objects tracing the access to them. The solution is a 4-line
+custom metaclass doing the boring job for me:
+
+ ::
+
+ #<oopp.py>
+
+ class Wrapped(Customizable,type):
+ """A customizable metaclass to wrap methods with a given wrapper and
+ a given condition"""
+ __metaclass__=Reflective
+ wrapper=wrappedmethod
+ condition=lambda k,v: True # wrap all
+ def __init__(cls,*args):
+ super(cls.__this,cls).__init__(*args)
+ wrap(cls,cls.wrapper,cls.condition.im_func)
+
+ Traced=Wrapped.With(wrapper=tracedmethod,__name__='Traced')
+ Timed=Wrapped.With(wrapper=timedmethod,__name__='Timed')
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> from oopp import *
+ >>> time_=ClsFactory[Traced](time)
+ >>> print time_.asctime()
+ [time_] Calling 'asctime' with arguments
+ (){} ...
+ -> 'time_.asctime' called with result: Sun May 4 07:30:51 2003
+ Sun May 4 07:30:51 2003
+
+Another is
+
+ ::
+
+ #<tracemain.py>
+
+ from oopp import ClsFactory,Traced,Reflective
+
+ def f1(x): return x # nested functions
+ def f2(x): return f1(x) # we want to trace
+
+ f1orf2=lambda k,v : v is f1 or v is f2
+ make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]
+ traced=make('traced',globals())
+
+ traced.f2('hello!') # call traced.f2
+
+ #</tracemain.py>
+
+with output
+
+ ::
+
+ [__main__] Calling 'f2' with arguments
+ ('hello!',){} ...
+ [__main__] Calling 'f1' with arguments
+ ('hello!',){} ...
+ -> '__main__.f1' called with result: hello!
+ -> '__main__.f2' called with result: hello!
+
+Modifying hierarchies
+---------------------------------------------------------
+
+Suppose one wants to enhance a pre-existing class, for instance
+by adding tracing capabilities to it. The problem is non-trivial
+since it is not enough to derive a new class from the original
+class using the 'Traced' metaclass. For instance, we could imagine of
+tracing the 'Pizza' class introduced in chapter 4 by defining
+
+ >>> from oopp import *
+ >>> class TracedTomatoPizza(GenericPizza,WithLogger):
+ ... __metaclass__=ClsFactory[Traced]
+ ... toppinglist=['tomato']
+
+However, this would only trace the methods of the newly defined class,
+not of the original one. Since the new class does not introduce any
+non-trivial method, the addition of 'Traced' is practically without
+any effect:
+
+ >>> marinara=TracedTomatoPizza('small') # nothing happens
+ *****************************************************************************
+ Tue Apr 15 11:00:17 2003
+ 1. Created small pizza with tomato, cost $ 1.5
+
+Tracing hierarchies
+------------------------------------------------------------------------------
+
+ ::
+
+ #<traceH.py>
+
+ from oopp import *
+
+ def wrapMRO(cls,wrapped):
+ for c in cls.__mro__[:-1]:
+ wrap(c,wrapped)
+
+ tracing=tracedmethod.With(logfile=file('trace.txt','w'))
+ wrapMRO(HomoSapiensSapiens,tracing)
+ HomoSapiensSapiens().can()
+
+ #</traceH.py>
+
+with output in trace.txt
+
+ ::
+
+ [HomoSapiensSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [HomoSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [HomoHabilis] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [Homo] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [PrettyPrinted] Calling '__str__' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
+ [PrettyPrinted.__str__] called with result:
+ <HomoSapiensSapiens>
+ [Homo.can] called with result: None
+ [HomoHabilis.can] called with result: None
+ [HomoSapiens.can] called with result: None
+ [HomoSapiensSapiens.can] called with result: None
+
+Modifying source code
+------------------------------------------------------------------------
+
+The real solution would be to derive the original class 'GenericPizza'
+from 'Traced' and not from 'object'. One could imagine of creating
+a new class inhering from 'Traced' and with all the methods of the
+original 'GenericPizza' class; then one should create copies of
+all the classes in the whole multiple inheritance hierarchy.
+This would be a little annoying, but feasable; the real problem is
+that this approach would not work with cooperative methods, since
+cooperative calls in the derived classes would invoked methods in
+the original classes, which are not traced.
+
+This is a case where the modification of the original source code is
+much more appealing and simpler that any other method: it is enough
+to perform a search and replace in the original source code, by adding
+the metaclass 'Traced', to enhance the whole multiple inheritance hierarchy.
+Let me assume that the hierarchy is contained in a module (which is
+typical case). The idea, is to generate *dynamically* a new module from the
+modified source code, with a suitable name to avoid conflicts with the
+original module. Incredibily enough, this can be done in few lines:
+
+ ::
+
+ #<oopp.py>
+
+ def modulesub(s,r,module):
+ "Requires 2.3"
+ name=module.__name__
+ source=inspect.getsource(module).replace(s,r)
+ dic={name: module}; exec source in dic # exec the modified module
+ module2=ModuleType(name+'2') # creates an an empty module
+ customize(module2,**dic) # populates it with dic
+ return module2
+
+ #</oopp.py>
+
+Notice that the ``sub`` function, that modifies the source code of
+a given module and returns a modified module, requires Python 2.3
+to work. This is a due to a subtle bug in ``exec`` in Python 2.2.
+Anyway, the restriction to Python 2.3 allows me to take advantage
+of one of the most elegant convenience of Python 2.3: the name in
+the ``types`` module acts are type factories and in particular
+``ModuleType(s)`` returns an (empty) module named ``s``.
+Here is an example of usage:
+
+ >>> import oopp
+ >>> s='GenericPizza(object):'
+ >>> oopp2=oopp.modulesub(s,s+'\n __metaclass__=oopp.Traced',oopp)
+
+Name clashes are avoided, being 'oopp2' a different module from
+'oopp'; we have simultaneously access to both the original hierarchy
+in 'oopp' (non-traced) and the modified one in 'oopp2' (traced).
+In particular 'oopp2.CustomizablePizza' is traced and therefore
+
+ >>> class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
+ ... __metaclass__=makecls()
+ >>> marinara=PizzaLog.With(toppinglist=['tomato'])('small')
+
+gives the output
+
+ ::
+
+ [PizzaLog] Calling '__init__' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>, 'small'){} ...
+ -> 'PizzaLog.__init__' called with result: None
+
+ *****************************************************************************
+ Thu Mar 27 09:18:28 2003
+ [PizzaLog] Calling '__str__' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ [PizzaLog] Calling 'price' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ [PizzaLog] Calling 'toppings_price' with arguments
+ (<oopp.PizzaLog object at 0x40470dac>,){} ...
+ -> 'PizzaLog.toppings_price' called with result: 0.5
+
+ -> 'PizzaLog.price' called with result: 1.5
+
+ -> 'PizzaLog.__str__' called with result: small pizza with tomato, cost $ 1.5
+
+ 1. Created small pizza with tomato, cost $ 1.5
+
+From that we understand what is happening:
+
+- ``PizzaLog.__init__`` calls ``GenericPizza.__init__`` that defines size and
+ cooperatively calls ``WithLogger.__init__``
+
+- WithLogger.__init__ cooperatively calls ``WithCounter.__init__``
+ that increments the count attribute;
+
+- at this point, the instruction 'print self' in ``WithLogger.__init__`` calls
+ ``PizzaLog.__str__`` (inherited from ``GenericPizza.__str__``);
+
+- ``GenericPizza.__str__`` calls 'price' that in turns calls
+ 'toppings_price'.
+
+On top of that, notice that the metaclass of 'PizzaLog' is
+``_TracedReflective`` that has been automagically generated by
+``makecls`` from the metaclasses of 'CustomizablePizza' (i.e. 'Traced')
+and of 'WithLogger' (i.e. 'Reflective'); the leading underscore helps
+to understand the dynamical origin of '_TracedReflective'.
+It turns out that '_TracedReflective' has a dynamically
+generated (meta-meta)class:
+
+ >>> print type(type(PizzaLog)) #meta-metaclass
+ <class 'oopp._WithWrappingCapabilitiesReflective'>
+
+Therefore this example has a non-trivial class hierarchy
+
+ >>> print oopp.MRO(PizzaLog)
+ MRO of PizzaLog:
+ 0 - PizzaLog(CustomizablePizza,WithLogger)[Traced]
+ 1 - CustomizablePizza(GenericPizza,Customizable)[Traced]
+ 2 - GenericPizza(object)[Traced]
+ 3 - WithLogger(WithCounter,Customizable,PrettyPrinted)
+ 4 - WithCounter(object)
+ 5 - Customizable(object)
+ 6 - PrettyPrinted(object)
+ 7 - object()
+
+a non-trivial metaclass hierarchy,
+
+ >>> print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
+ MRO of Traced:
+ 0 - Traced(Reflective)[WithWrappingCapabilities]
+ 1 - Reflective(type)
+ 2 - type(object)
+ 3 - object()
+
+and a non-trivial meta-metaclass hierarchy:
+
+ >>> print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
+ MRO of WithWrappingCapabilities:
+ 0 - WithWrappingCapabilities(BracketCallable)
+ 1 - CallableWithBrackets(type)
+ 2 - type(object)
+ 3 - object()
+
+Pretty much complicated, isn't it ? ;)
+
+This example is there to show what kind of maintenance one can have
+with programs doing a large use of metaclasses, particularly, when
+they should be understood by somebody else than the autor ...
+
+Metaclass regenerated hierarchies
+--------------------------------------------------------------------------
+
+ ::
+
+ import types
+
+ def hierarchy(self,cls):
+ d=dict([(t.__name__,t) for t in vars(types).itervalues()
+ if isinstance(t,type)])
+ def new(c):
+ bases=tuple([d[b.__name__] for b in c.__bases__])
+ return self(c.__name__, bases, c.__dict__.copy())
+ mro=list(cls.__mro__[:-1])
+ mro.reverse()
+ for c in mro:
+ if not c.__name__ in d:
+ d[c.__name__]=new(c)
+ customize(self,**d)
+
+ ClsFactory.hierarchy=hierarchy
+ traced=ClsFactory[Traced,Reflective]
+
+Unfortunately, this approach does not work if the original hierarchy makes
+named cooperative super calls.
+
+Therefore the source-code run-time modification has its advantages.
diff --git a/pypers/secret_inter.py b/pypers/secret_inter.py
new file mode 100755
index 0000000..46c7838
--- /dev/null
+++ b/pypers/secret_inter.py
@@ -0,0 +1,34 @@
+import re
+reobj=re.compile(r'x')
+from oopp import Str
+sum=Str('a')+Str('b') # check the sum
+print sum, type(sum)
+rprod=Str('a')*2 # check the right product
+print rprod,type(rprod)
+lprod=2*Str('a') # check the left product
+print lprod,type(lprod)
+r=Str('a').replace('a','b') # check replace
+print r,type(r)
+r=Str('a').capitalize() # check capitalize
+print r,type(r)
+from oopp import *
+aob=BaseRegexp('a')|BaseRegexp('b'); print aob
+print pretty(attributes(aob))
+from oopp import *
+time_=ClsFactory[Traced](time)
+print time_.asctime()
+from oopp import *
+class TracedTomatoPizza(GenericPizza,WithLogger):
+ __metaclass__=ClsFactory[Traced]
+ toppinglist=['tomato']
+marinara=TracedTomatoPizza('small') # nothing happens
+import oopp
+s='GenericPizza(object):'
+oopp2=oopp.modulesub(s,s+'\n __metaclass__=oopp.Traced',oopp)
+class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
+ __metaclass__=makecls()
+marinara=PizzaLog.With(toppinglist=['tomato'])('small')
+print type(type(PizzaLog)) #meta-metaclass
+print oopp.MRO(PizzaLog)
+print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
+print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
diff --git a/pypers/simionato_talk/abstract-en.txt b/pypers/simionato_talk/abstract-en.txt
new file mode 100644
index 0000000..844956c
--- /dev/null
+++ b/pypers/simionato_talk/abstract-en.txt
@@ -0,0 +1,12 @@
+As the old saying goes, Python is the only language with more Web frameworks than
+keywords. This is sometimes an advantage, but more often than not, it is an issue.
+In order to improve the interoperability between different frameworks, Phillip
+J. Eby proposed in 2003 a specification, the WSGI or Web Server Gateway
+Interface, a.k.a Whiskey. In my talk I will discuss how you can achieve portability
+of your application by following the WSGI protocol. I will give practical
+examples of how to use the WSGI reference implementation which is part of the
+standard library (wsgiref), of how to supplement it with other WSGI-compliant
+libraries (notably Paste by Ian Bicking) and of how to integrate your WSGI
+application in different frameworks including Zope, Twisted, TurboGears et al.
+The talk is intended as a tutorial and requires only elementary knowledge of
+Web programming, at the level of simple CGI.
diff --git a/pypers/simionato_talk/abstract-scipy.txt b/pypers/simionato_talk/abstract-scipy.txt
new file mode 100644
index 0000000..22fe89f
--- /dev/null
+++ b/pypers/simionato_talk/abstract-scipy.txt
@@ -0,0 +1,14 @@
+SciPy e' il progetto piu' importante per quanto riguarda il calcolo scientifico in Python.
+SciPy fornisce molti vantaggi:
+
+1) la possibilita' di riutilizzare codice scientifico esistente (in C, C++ e Fortran)
+ da Python, tramite degli opportuni wrapper (SWIG, weave, f2py, etc.);
+2) un'ottima libreria per gli array N-dimensionali (Numpy);
+3) la possibilita' di utilizzare la console interattiva di (I)Python;
+4) ottime routine di visualizzazione (Matplotlib, Chaco, etc.);
+5) l'apertura a tutto il mondo Python.
+
+Il mio talk sara' decisamente introduttivo, discutera' le caratteristiche
+piu' semplici di Numpy e mostrera' qualche esempio di uso di altre parti di SciPy,
+in particolare le librerie per l'interpolazione non-lineare (non-linear least
+squares fitting). En passant, parlero' anche di IPython e Matplotlib.
diff --git a/pypers/simionato_talk/abstract.txt b/pypers/simionato_talk/abstract.txt
new file mode 100644
index 0000000..6e88e8a
--- /dev/null
+++ b/pypers/simionato_talk/abstract.txt
@@ -0,0 +1,28 @@
+SciPy on WSGI
+--------------
+
+Nonostante il titolo esoterico, il talk sara' molto concreto ed
+illustrera' come implementare un'interfaccia Web per una semplice
+applicazione scientifica. Prendero' spunto da una mia esperienza
+personale considerando come esmpio uno script per l'interpolazione di
+dati con una curva non lineare. L'enfasi sara' sulla semplicita' d'uso e
+sulla velocita' di implementazione. L'audience ideale e' costituita
+da persone che non sono sviluppatori Web di professione.
+
+Python e' l'unico linguaggio con piu' Web frameworks che keywords.
+Per cercare di dare ordine all'anarchia e permettere una maggiore
+interoperabilita' tra i diversi frameworks, Phillip H. Eby ha
+inventato nel 2003 il protocollo WSGI (Web Server Gateway Interface,
+per gli amici Whiskey) che regola la comunicazione fra Web framework
+e Web server. Il protocollo ha avuto largo successo ed oggigiorno
+praticamente tutti i Web frameworks per Python lo implementano.
+
+Nel talk discutero':
+
+1. Che cosa e' esattamente WSGI;
+2. Come usare l'implementazione di riferimento di WSGI (wsgiref, parte
+ della libreria standard di Python dalla versione 2.5);
+3. Come scrivere un object publisher in 20 righe;
+4. Come implementare un semplice servizio Web per il fitting di dati
+ sperimentali;
+5. Come migrare un'applicazione WSGI da un framework ad un altro.
diff --git a/pypers/simionato_talk/badpricehistory.png b/pypers/simionato_talk/badpricehistory.png
new file mode 100644
index 0000000..33767cf
--- /dev/null
+++ b/pypers/simionato_talk/badpricehistory.png
Binary files differ
diff --git a/pypers/simionato_talk/badpricehistory2.png b/pypers/simionato_talk/badpricehistory2.png
new file mode 100644
index 0000000..c557af7
--- /dev/null
+++ b/pypers/simionato_talk/badpricehistory2.png
Binary files differ
diff --git a/pypers/simionato_talk/bio.txt b/pypers/simionato_talk/bio.txt
new file mode 100644
index 0000000..3adf82e
--- /dev/null
+++ b/pypers/simionato_talk/bio.txt
@@ -0,0 +1,14 @@
+Nota biografica
+---------------
+
+Michele Simionato e' conosciuto nella comunita' internazionale per la sua
+attivita' nei newsgroups di Python, per la sua partecipazione a conferenze,
+per i suoi articoli divulgativi, come autore di ricette pubblicate nel Python
+cookbook e per un modulo open source sui decoratori. Michele e' anche socio
+fondatore dell'associazione Python Italia.
+In una vita precedente Michele si e' occupato di Fisica Teorica, lavorando per
+vari anni in Francia e negli Stati Uniti come ricercatore post-doc. Adesso
+Michele vive e lavora a Milano per StatPro Italia, occupandosi di software
+finanziario e programmazione Web. Nel suo tempo libero, quando non e' impegnato
+con la visione di vecchi cartoni animati giapponesi, Michele cerca sempre di
+imparare qualche cosa di nuovo.
diff --git a/pypers/simionato_talk/cdf-dist.png b/pypers/simionato_talk/cdf-dist.png
new file mode 100644
index 0000000..dcb4b27
--- /dev/null
+++ b/pypers/simionato_talk/cdf-dist.png
Binary files differ
diff --git a/pypers/simionato_talk/delta-cdf.png b/pypers/simionato_talk/delta-cdf.png
new file mode 100644
index 0000000..e1d582d
--- /dev/null
+++ b/pypers/simionato_talk/delta-cdf.png
Binary files differ
diff --git a/pypers/simionato_talk/delta-dist.png b/pypers/simionato_talk/delta-dist.png
new file mode 100644
index 0000000..445e4b6
--- /dev/null
+++ b/pypers/simionato_talk/delta-dist.png
Binary files differ
diff --git a/pypers/simionato_talk/delta_dist.py b/pypers/simionato_talk/delta_dist.py
new file mode 100644
index 0000000..deb0024
--- /dev/null
+++ b/pypers/simionato_talk/delta_dist.py
@@ -0,0 +1,64 @@
+from pylab import *
+from scipy.special import gamma, erf, hyp2f1
+from scipy.optimize import brentq
+
+Cg = 1/sqrt(2*pi)
+SQRT2 = sqrt(2.)
+
+def gauss(x):
+ return Cg*exp(-x*x)
+
+def cdf_gauss(x):
+ return erf(x/SQRT2)/2.+.5
+
+# avg is a global variable
+def delta(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*d2**(1.+d2/2.)/(x*x + d2)**(1.5+d2/2.)
+
+def cdf(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*x/d*hyp2f1(.5, 1.5+d2/2., 1.5, -x*x/d2) + .5
+
+def plot_cdf():
+ x = arange(-5,5,.1)
+ plot(x, cdf_gauss(x), color='black')
+ plot(x, cdf(.2)(x), color='red')
+ plot(x, cdf(1.)(x), color='green')
+ plot(x, cdf(5.)(x), color='blue')
+
+ text(1.5, .3, '$c_{gauss}\ :\ VAR_{95}=1.64\ \sigma$', color='black')
+ text(1.5, .6, '$c_{0.2}\ :\ VAR_{95}=0.40\ \sigma$', color='red')
+ text(1.5, .5, '$c_{1.0}\ :\ VAR_{95}=1.36\ \sigma$', color='green')
+ text(1.5, .4, '$c_{5.0}\ :\ VAR_{95}=1.64\ \sigma$', color='blue')
+
+ savefig('cdf-dist.png', dpi=72)
+ show()
+## you get VAR_{95}/vol from computation like
+# brentq(lambda x: cdf(0.2)(x)-.95,.1,10)
+# brentq(lambda x: cdf(1.0)(x)-.95,.1,10)
+# brentq(lambda x: cdf(5.0)(x)-.95,.1,10)
+
+def plot_delta():
+ x = arange(-5,5,.1)
+ plot(x, gauss(x), color='black')
+ plot(x, delta(.1)(x), color='red')
+ plot(x, delta(1.)(x), color='green')
+ plot(x, delta(5.)(x), color='blue')
+
+ text(4.5, 4, '$f_{0.2}(x)$', color='red')
+ text(4.5, 3, '$f_{1.0}(x)$', color='green')
+ text(4.5, 2, '$f_{5.0}(x)$', color='blue')
+ text(4.5, 1, '$\phi(x)$', color='black')
+
+ savefig('delta-dist.png', dpi=72)
+ show()
+
+if __name__ == '__main__':
+ if '-c' in sys.argv[1:]:
+ plot_cdf()
+ else:
+ plot_delta()
+
diff --git a/pypers/simionato_talk/error_trapper.py b/pypers/simionato_talk/error_trapper.py
new file mode 100644
index 0000000..172b679
--- /dev/null
+++ b/pypers/simionato_talk/error_trapper.py
@@ -0,0 +1,22 @@
+import sys
+from wsgiref.simple_server import make_server
+
+# WSGI app
+def simple_app(env, resp):
+ resp('200 OK', [('Content-type','text/plain')])
+ yield 'Hello world!\n'
+ yield 1/0
+
+def wrapped_app(app, env, resp):
+ try:
+ page = app(env, lambda s, h, e=None: None)
+ print list(page)
+ except:
+ resp('500 ERR', [("content-type","text/plain")], sys.exc_info())
+ return ['err']
+ else:
+ resp('200 OK', [("content-type","text/html")])
+ return page
+
+if __name__ == '__main__':
+ make_server('', 8000, wrapped_app.__get__(simple_app)).serve_forever()
diff --git a/pypers/simionato_talk/evalexception_ex.py b/pypers/simionato_talk/evalexception_ex.py
new file mode 100644
index 0000000..51b70a8
--- /dev/null
+++ b/pypers/simionato_talk/evalexception_ex.py
@@ -0,0 +1,11 @@
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+if __name__ == '__main__':
+ make_server('', 9090, EvalException(app)).serve_forever()
diff --git a/pypers/simionato_talk/formulas.html b/pypers/simionato_talk/formulas.html
new file mode 100644
index 0000000..c98eec7
--- /dev/null
+++ b/pypers/simionato_talk/formulas.html
@@ -0,0 +1,329 @@
+<?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.4.1: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title></title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+<math xmlns='http://www.w3.org/1998/Math/MathML'>
+<mi>&Phi;</mi>
+<mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo><mfrac><mn>12</mn><mo>+</mo></mfrac><mfrac><mn>12</mn><mi>erf</mi></mfrac><mo>(</mo><mi>x</mi><mo>/</mo><msqrt><mo>(</mo></msqrt><mn>2</mn><mo>)</mo><mo>)</mo>
+</math></div>
+</div>
+</body>
+</html>
diff --git a/pypers/simionato_talk/formulas.tex b/pypers/simionato_talk/formulas.tex
new file mode 100644
index 0000000..052405c
--- /dev/null
+++ b/pypers/simionato_talk/formulas.tex
@@ -0,0 +1,48 @@
+\documentclass[]{slides}
+\begin{document}
+
+delta-distribution
+
+$$
+f_\delta(x;\mu,\sigma) =
+\frac{C_\delta(\delta^2\sigma^2)^{1+\delta^2/2}}
+{[(x-\mu)^2+\delta^2\sigma^2]^{3/2+\delta^2/2}}
+$$
+
+$$
+C_\delta = \frac{\Gamma(3/2+\delta^2/2)}{\Gamma(1/2)/\Gamma(1+\delta^2/2)}
+$$
+
+$$\frac12 < C_\delta < \frac1{\sqrt\pi}\simeq 0.5642$$
+
+$$
+\int dx\ f_\delta(x;\mu,\sigma) = 1,\quad \forall \delta
+$$
+
+$$
+\int dx\ (x-\mu)^2f_\delta(x;\mu,\sigma) = \sigma^2,\quad \forall \delta
+$$
+
+$$
+f_\delta(x;\mu,\sigma) \sim \frac1{x^{3+\delta^2}},\quad x\to\infty
+$$
+
+The average absolute deviation of the curve is $\delta\sigma$
+
+$$
+\int dx\ |x-\mu|f_\delta(x;\mu,\sigma) =
+\frac{\Gamma(1/2+\delta^2/2)}{\Gamma(1/2)\Gamma(1+\delta^2/2)}\delta\sigma
+$$
+
+Cumulative distribution function
+
+$$
+F_\delta(x;\mu,\sigma) = \int_{-\infty}^x\ dx'\ f_\delta(x';\mu,\sigma) =
+$$
+
+$$
+\frac12 + C_\delta\frac{x-\mu}{\delta\sigma}
+{}_2F_1\left(\frac12, \frac{\delta^2+3}2, \frac32, -\frac{(x-\mu)^2}
+{\delta^2\sigma^2}\right)
+$$
+\end{document}
diff --git a/pypers/simionato_talk/formulas.txt b/pypers/simionato_talk/formulas.txt
new file mode 100644
index 0000000..ea4a232
--- /dev/null
+++ b/pypers/simionato_talk/formulas.txt
@@ -0,0 +1 @@
+$\Phi(x) = \frac12+\frac12 erf(x/\sqrt(2))$
diff --git a/pypers/simionato_talk/hello.py b/pypers/simionato_talk/hello.py
new file mode 100644
index 0000000..caa7f62
--- /dev/null
+++ b/pypers/simionato_talk/hello.py
@@ -0,0 +1,10 @@
+
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
diff --git a/pypers/simionato_talk/nonblocking.py b/pypers/simionato_talk/nonblocking.py
new file mode 100644
index 0000000..c91d494
--- /dev/null
+++ b/pypers/simionato_talk/nonblocking.py
@@ -0,0 +1,48 @@
+from __future__ import with_statement
+import time, threading
+from easy_async import make_reactor
+from wsgiref.simple_server import make_server
+from ms.debug_utils import printdict
+from paste.auth.basic import AuthBasicHandler
+
+## def blockingcalc(env, resp):
+## resp('200 OK', [('Content-type', 'text/html')])
+## res = 0
+## for i in range(1000):
+## time.sleep(.01)
+## res += i
+## printdict(env)
+## return ['The result is %d' % res]
+
+def _nonblockingcalc(job):
+ res = 0
+ for i in range(1000):
+ time.sleep(.01)
+ res += i
+ yield 'Partial result %d' % res
+ yield 'The result is %d' % res
+
+_cache = {}
+
+def next(gen, env):
+ user = env['REMOTE_USER'] # not null because of the middleware
+ job = _cache.get(user)
+ if job is None: # create a new job
+ job = _cache[user] = reactor.Job(gen)
+ job.tick = 0
+ job.start()
+ return job.yval
+
+def nonblockingcalc(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ yield str(next(_nonblockingcalc, env))
+
+if __name__ == '__main__':
+ app = AuthBasicHandler(
+ nonblockingcalc, 'realm', lambda e, u, p: u=='pippo')
+ reactor = make_reactor('default')
+ job = reactor.Job(_nonblockingcalc)
+ job.start()
+
+ with reactor.in_separated_thread():
+ make_server('', 8000, app).serve_forever()
diff --git a/pypers/simionato_talk/nongaussian.png b/pypers/simionato_talk/nongaussian.png
new file mode 100644
index 0000000..e779e89
--- /dev/null
+++ b/pypers/simionato_talk/nongaussian.png
Binary files differ
diff --git a/pypers/simionato_talk/objectpublisher.py b/pypers/simionato_talk/objectpublisher.py
new file mode 100644
index 0000000..88201f9
--- /dev/null
+++ b/pypers/simionato_talk/objectpublisher.py
@@ -0,0 +1,46 @@
+from wsgiref import util, simple_server
+
+# page is any callable object returning HTML in chunks
+class WSGIObjectPublisher(object):
+ def __init__(self, root):
+ self.root = root
+ def __call__(self, env, resp):
+ return self.getsubpage(self.root, env, resp)()
+ def getsubpage(self, root, env, resp):
+ script_name = util.shift_path_info(env)
+ print script_name, '****', env['PATH_INFO']
+ if not script_name: # We've arrived!
+ resp('200 OK', [('content-type', 'text/html')])
+ return root
+ try:
+ page = getattr(root, script_name)
+ except AttributeError:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['missing page %r' % script_name]
+ exposed = getattr(page, 'exposed', False)
+ if not exposed:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['%r is not exposed!' % script_name]
+ return self.getsubpage(page, env, resp)
+
+if __name__ == '__main__':
+ class Example(object):
+ def __call__(self):
+ yield '<h1>index page</h1>'
+ yield 'goto <a href="./page1">page1</a><br/>'
+ yield 'goto <a href="./page2">page2</a><br/>'
+ yield 'goto <a href="subsite">subsite</a><br/>'
+ def page1(self):
+ yield 'page1'
+ def page2(self):
+ yield 'page2'
+ page1.exposed = page2.exposed = True
+ root = Example()
+ root.subsite = Example()
+ root.subsite.exposed = True
+ app = WSGIObjectPublisher(root)
+ from paste.auth.basic import AuthBasicHandler
+ def authfunc(environ, username, password):
+ return username == 'pippo' and password == 'lippo'
+ app_with_auth = AuthBasicHandler(app, 'Test Realm', authfunc)
+ simple_server.make_server('', 8000, app_with_auth).serve_forever()
diff --git a/pypers/simionato_talk/simpleplotter.py b/pypers/simionato_talk/simpleplotter.py
new file mode 100644
index 0000000..dcdb5ef
--- /dev/null
+++ b/pypers/simionato_talk/simpleplotter.py
@@ -0,0 +1,21 @@
+import os, sys
+from ms.plot_utils import GnuPlotter
+DATADIR = os.path.expanduser('~/sp/equities-histories')
+
+def make_graph(code, batch):
+ gp = GnuPlotter(batch=batch)
+ lines = list(file(os.path.join(DATADIR, code)))[-500:]
+ data = ''.join(lines)
+ png_file='/tmp/%s.png' % code
+ gp.plot(locals())
+ return png_file
+
+if __name__ == '__main__':
+ L = len(sys.argv) - 1
+ if L == 1:
+ batch = False
+ elif L == 2:
+ batch = bool(int(sys.argv[2])) # 0 or 1
+ else:
+ sys.exit('Examples: $ python simpleplotter.py fri-gb;AVE')
+ make_graph(sys.argv[1], batch)
diff --git a/pypers/simionato_talk/talk.html b/pypers/simionato_talk/talk.html
new file mode 100644
index 0000000..925b833
--- /dev/null
+++ b/pypers/simionato_talk/talk.html
@@ -0,0 +1,1207 @@
+<?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.4.1: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title>SciPy on WSGI</title>
+<meta name="organization" content="StatPro Italy" />
+<meta name="date" content="2007-06-09" />
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+<h1>SciPy on WSGI</h1>
+<h2>PyCon Uno 2007 - 09 June 2007</h2>
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+<h1 class="title">SciPy on WSGI</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Talk given at:</th><td class="field-body">PyCon Uno 2007</td>
+</tr>
+<tr class="field"><th class="docinfo-name">By:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr><th class="docinfo-name">Organization:</th>
+<td>StatPro Italy</td></tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>2007-06-09</td></tr>
+</tbody>
+</table>
+<!-- Definitions of interpreted text roles (classes) for S5/HTML data. -->
+<!-- This data file has been placed in the public domain. -->
+<!-- Colours
+======= -->
+<!-- Text Sizes
+========== -->
+<!-- Display in Slides (Presentation Mode) Only
+========================================== -->
+<!-- Display in Outline Mode Only
+============================ -->
+<!-- Display in Print Only
+===================== -->
+<!-- Incremental Display
+=================== -->
+<p class="center"><strong>Subtitle</strong>: <em>Science on the Web for pedestrians</em></p>
+
+</div>
+<div class="slide" id="before-i-start">
+<h1>Before I start</h1>
+<p>What about you?</p>
+<p class="incremental">Are you more of a programmer or more of a scientist/engineer?</p>
+<p class="incremental">What kind of scientific tools are you using, if any?</p>
+<p class="incremental">Have you ever heard of SciPy?</p>
+<p class="incremental">Have you ever heard of WSGI?</p>
+</div>
+<div class="slide" id="ok-now-i-can-begin">
+<h1>Ok, now I can begin ;)</h1>
+<p>The motivation from this talk comes from a real problem at <a class="reference" href="http://www.statpro.com">StatPro</a></p>
+<p class="incremental">we have bad histories for many financial products</p>
+<p class="incremental">wrong prices at some dates in the past</p>
+</div>
+<div class="slide" id="a-picture">
+<h1>A picture</h1>
+<img alt="badpricehistory.png" src="badpricehistory.png" />
+<p class="incremental">(damn data providers!)</p>
+</div>
+<div class="slide" id="discarding-values">
+<h1>Discarding values ...</h1>
+<img alt="badpricehistory2.png" src="badpricehistory2.png" />
+<p>... is tricky!</p>
+</div>
+<div class="slide" id="issues">
+<h1>Issues</h1>
+<p>We cannot use the conventional criterium</p>
+<img alt="nongaussian.png" class="incremental" src="nongaussian.png" />
+</div>
+<div class="slide" id="strategy">
+<h1>Strategy</h1>
+<ul class="incremental simple">
+<li>price distributions (ln p_i/p) are known to decay with power laws</li>
+<li>fit the distributions with a &quot;reasonable&quot; curve and determine
+a suitable criterium for the spikes at some confidence level</li>
+<li>a reasonably simple ansatz gives a family of distributions depending
+on a parameter delta</li>
+<li><a class="reference" href="formulas.pdf">show formulae</a></li>
+</ul>
+</div>
+<div class="slide" id="delta-distribution">
+<h1>delta-distribution</h1>
+<img alt="delta-dist.png" src="delta-dist.png" />
+<p>From Dirac delta (delta -&gt; 0) to Gaussian
+distribution (delta -&gt; oo)</p>
+</div>
+<div class="slide" id="cumulative-dist">
+<h1>Cumulative dist</h1>
+<img alt="cdf-dist.png" src="cdf-dist.png" />
+<p>VAR-XX = Max loss at XX% confidence level [<a class="reference" href="http://integrals.wolfram.com/index.jsp">i</a>]</p>
+</div>
+<div class="slide" id="relation-var-vol">
+<h1>Relation VAR-vol.</h1>
+<p>If you assume a given distribution, there is a fixed relation between
+VAR-XX and volatility</p>
+<ul class="incremental simple">
+<li>for the Gaussian VAR-95 = 1.64 sigma, for a lot of our distributions
+VAR-95 &lt; 1.0 sigma</li>
+<li>we don't want to make assumptions on the distribution function
+for computing the VAR</li>
+<li>but we are willing to make assumptions for the sake of eliminating
+statistically invalid values</li>
+</ul>
+</div>
+<div class="slide" id="the-tool-we-need">
+<h1>The tool we need</h1>
+<pre class="literal-block">
+$ python simpleplotter.py &quot;fri-gb;AVE&quot;
+</pre>
+<p>(not 100% finished yet!)</p>
+</div>
+<div class="slide" id="enter-scipy-co">
+<h1>Enter SciPy &amp; Co.</h1>
+<p>In order to perform our analysis we looked
+at many scientific tools</p>
+<ul class="incremental simple">
+<li>a good plotting tool (matplotlib)</li>
+<li>support for histograms (matplotlib)</li>
+<li>support for special functions (scipy.special)</li>
+<li>support for non-linear fitting (scipy.leastsq)</li>
+<li>good performance (scipy)</li>
+<li>interactive and IPython-friendly (scipy)</li>
+<li>bla-bla</li>
+<li>cheating: I actually used Gnuplot!! ;-)</li>
+</ul>
+</div>
+<div class="slide" id="installation">
+<h1>Installation</h1>
+<p>If you are lucky, it is trivial:</p>
+<pre class="literal-block">
+$ apt-get install ipython
+$ apt-get install python-matplotlib
+$ apt-get install python-numpy
+$ apt-get install python-numpy-ext
+$ apt-get install python-scipy
+</pre>
+<p>If you are unlucky, or if you try to build from sources,
+YMMV ...</p>
+</div>
+<div class="slide" id="what-s-in-scipy">
+<h1>What's in Scipy</h1>
+<ul class="incremental simple">
+<li>support for multi-dimensional array (numpy)</li>
+<li>linear algebra and minimization routines</li>
+<li>solving, integration, interpolation, fitting</li>
+<li>special functions and statistical functions</li>
+<li>etc. etc.</li>
+</ul>
+</div>
+<div class="slide" id="special-functions">
+<h1>Special functions</h1>
+<p>Airy Functions:</p>
+<pre class="literal-block">
+airy
+--Airy functions and their derivatives.
+airye
+--Exponentially scaled Airy functions
+ai_zeros
+--Zeros of Airy functions Ai(x) and Ai'(x)
+bi_zeros
+--Zeros of Airy functions Bi(x) and Bi'(x)
+</pre>
+</div>
+<div class="slide" id="elliptic-functions-and-integrals">
+<h1>Elliptic Functions and Integrals</h1>
+<pre class="literal-block">
+ellipj
+--Jacobian elliptic functions
+ellipk
+--Complete elliptic integral of the first kind.
+ellipkinc
+--Incomplete elliptic integral of the first kind.
+ellipe
+--Complete elliptic integral of the second kind.
+ellipeinc
+--Incomplete elliptic integral of the second kind.
+</pre>
+</div>
+<div class="slide" id="bessel-functions">
+<h1>Bessel Functions</h1>
+<pre class="literal-block">
+jn
+--Bessel function of integer order and real argument.
+jv
+--Bessel function of real-valued order and complex argument.
+jve
+--Exponentially scaled Bessel function.
+yn
+--Bessel function of second kind (integer order).
+yv
+--Bessel function of the second kind (real-valued order).
+yve
+--Exponentially scaled Bessel function of the second kind.
+kn
+--Modified Bessel function of the third kind (integer order).
+kv
+--Modified Bessel function of the third kind (real order).
+kve
+--Exponentially scaled modified Bessel function of the third kind.
+iv
+--Modified Bessel function.
+ive
+--Exponentially scaled modified Bessel function.
+hankel1
+--Hankel function of the first kind.
+hankel1e
+--Exponentially scaled Hankel function of the first kind.
+hankel2
+--Hankel function of the second kind.
+hankel2e
+--Exponentially scaled Hankel function of the second kind.
+
+lmbda
+--Sequence of lambda functions with arbitrary order v.
+</pre>
+</div>
+<div class="slide" id="zeros-of-bessel-functions">
+<h1>Zeros of Bessel Functions</h1>
+<pre class="literal-block">
+jnjnp_zeros
+--Zeros of integer-order Bessel functions and derivatives
+ sorted in order.
+jnyn_zeros
+--Zeros of integer-order Bessel functions and derivatives
+ as separate arrays.
+jn_zeros
+--Zeros of Jn(x)
+jnp_zeros
+--Zeros of Jn'(x)
+yn_zeros
+--Zeros of Yn(x)
+ynp_zeros
+--Zeros of Yn'(x)
+y0_zeros
+--Complex zeros: Y0(z0)=0 and values of Y0'(z0)
+y1_zeros
+--Complex zeros: Y1(z1)=0 and values of Y1'(z1)
+y1p_zeros
+--Complex zeros of Y1'(z1')=0 and values of Y1(z1')
+</pre>
+</div>
+<div class="slide" id="faster-versions">
+<h1>Faster versions</h1>
+<pre class="literal-block">
+j0
+--Bessel function of order 0.
+j1
+--Bessel function of order 1.
+y0
+--Bessel function of second kind of order 0.
+y1
+--Bessel function of second kind of order 1.
+i0
+--Modified Bessel function of order 0.
+i0e
+--Exponentially scaled modified Bessel function of order 0.
+i1
+--Modified Bessel function of order 1.
+i1e
+--Exponentially scaled modified Bessel function of order 1.
+k0
+--Modified Bessel function of the third kind of order 0.
+k0e
+--Exponentially scaled modified Bessel function of the
+ third kind of order 0.
+k1
+--Modified Bessel function of the third kind of order 1.
+k1e
+--Exponentially scaled modified Bessel function of the
+ third kind of order 1.
+</pre>
+</div>
+<div class="slide" id="integrals-of-bessel-functions">
+<h1>Integrals of Bessel Functions</h1>
+<pre class="literal-block">
+itj0y0
+--Basic integrals of j0 and y0 from 0 to x.
+it2j0y0
+--Integrals of (1-j0(t))/t from 0 to x and
+ y0(t)/t from x to inf.
+iti0k0
+--Basic integrals of i0 and k0 from 0 to x.
+it2i0k0
+--Integrals of (i0(t)-1)/t from 0 to x and
+ k0(t)/t from x to inf.
+besselpoly
+--Integral of a bessel function: Jv(2*a*x) * x^lambda
+ from x=0 to 1.
+</pre>
+</div>
+<div class="slide" id="derivatives-of-bessel-functions">
+<h1>Derivatives of Bessel Functions</h1>
+<pre class="literal-block">
+jvp
+--Nth derivative of Jv(v,z)
+yvp
+--Nth derivative of Yv(v,z)
+kvp
+--Nth derivative of Kv(v,z)
+ivp
+--Nth derivative of Iv(v,z)
+h1vp
+--Nth derivative of H1v(v,z)
+h2vp
+--Nth derivative of H2v(v,z)
+</pre>
+</div>
+<div class="slide" id="spherical-bessel-functions">
+<h1>Spherical Bessel Functions</h1>
+<pre class="literal-block">
+sph_jn
+--Sequence of spherical Bessel functions, jn(z)
+sph_yn
+--Sequence of spherical Bessel functions, yn(z)
+sph_jnyn
+--Sequence of spherical Bessel functions, jn(z) and yn(z)
+sph_in
+--Sequence of spherical Bessel functions, in(z)
+sph_kn
+--Sequence of spherical Bessel functions, kn(z)
+sph_inkn
+--Sequence of spherical Bessel functions, in(z) and kn(z)
+</pre>
+</div>
+<div class="slide" id="riccati-bessel-fun">
+<h1>Riccati-Bessel Fun.</h1>
+<pre class="literal-block">
+riccati_jn
+--Sequence of Ricatti-Bessel functions
+ of first kind.
+riccati_yn
+--Sequence of Ricatti-Bessel functions
+ of second kind.
+</pre>
+</div>
+<div class="slide" id="struve-functions">
+<h1>Struve Functions</h1>
+<pre class="literal-block">
+struve
+--Struve function --- Hv(x)
+modstruve
+--Modified struve function --- Lv(x)
+itstruve0
+--Integral of H0(t) from 0 to x
+it2struve0
+--Integral of H0(t)/t from x to Inf.
+itmodstruve0
+--Integral of L0(t) from 0 to x.
+</pre>
+</div>
+<div class="slide" id="statistical-functions">
+<h1>Statistical Functions</h1>
+<pre class="literal-block">
+bdtr
+--Sum of terms 0 through k of of the binomial pdf.
+bdtrc
+--Sum of terms k+1 through n of the binomial pdf.
+bdtri
+--Inverse of bdtr
+btdtr
+--Integral from 0 to x of beta pdf.
+btdtri
+--Quantiles of beta distribution
+fdtr
+--Integral from 0 to x of F pdf.
+fdtrc
+--Integral from x to infinity under F pdf.
+fdtri
+--Inverse of fdtrc
+gdtr
+--Integral from 0 to x of gamma pdf.
+gdtrc
+--Integral from x to infinity under gamma pdf.
+gdtri
+--Quantiles of gamma distribution
+nbdtr
+--Sum of terms 0 through k of the negative binomial pdf.
+nbdtrc
+--Sum of terms k+1 to infinity under negative binomial pdf.
+nbdtri
+--Inverse of nbdtr
+pdtr
+--Sum of terms 0 through k of the Poisson pdf.
+pdtrc
+--Sum of terms k+1 to infinity of the Poisson pdf.
+pdtri
+--Inverse of pdtr
+stdtr
+--Integral from -infinity to t of the Student-t pdf.
+stdtri
+--Inverse of stdtr (quantiles)
+chdtr
+--Integral from 0 to x of the Chi-square pdf.
+chdtrc
+--Integral from x to infnity of Chi-square pdf.
+chdtri
+--Inverse of chdtrc.
+ndtr
+--Integral from -infinity to x of standard normal pdf
+ndtri
+--Inverse of ndtr (quantiles)
+smirnov
+--Kolmogorov-Smirnov complementary CDF for one-sided
+ test statistic (Dn+ or Dn-)
+smirnovi
+--Inverse of smirnov.
+kolmogorov
+--The complementary CDF of the (scaled) two-sided test
+ statistic (Kn*) valid for large n.
+kolmogi
+--Inverse of kolmogorov
+tklmbda
+--Tukey-Lambda CDF
+</pre>
+</div>
+<div class="slide" id="gamma-and-related-functions">
+<h1>Gamma and Related Functions</h1>
+<pre class="literal-block">
+gamma
+--Gamma function.
+gammaln
+--Log of the absolute value of the gamma function.
+gammainc
+--Incomplete gamma integral.
+gammaincinv
+--Inverse of gammainc.
+gammaincc
+--Complemented incomplete gamma integral.
+gammainccinv
+--Inverse of gammaincc.
+beta
+--Beta function.
+betaln
+--Log of the absolute value of the beta function.
+betainc
+--Incomplete beta integral.
+betaincinv
+--Inverse of betainc.
+betaincinva
+--Inverse (in first argument, a) of betainc
+betaincinvb
+--Inverse (in first argument, b) of betainc
+psi(digamma)
+--Logarithmic derivative of the gamma function.
+rgamma
+--One divided by the gamma function.
+polygamma
+--Nth derivative of psi function.
+</pre>
+</div>
+<div class="slide" id="error-function-and-fresnel-int">
+<h1>Error Function and Fresnel Int.</h1>
+<pre class="literal-block">
+erf
+--Error function.
+erfc
+--Complemented error function (1- erf(x))
+erfinv
+--Inverse of error function
+erfcinv
+--Inverse of erfc
+erf_zeros
+--Complex zeros of erf(z)
+fresnel
+--Fresnel sine and cosine integrals.
+fresnel_zeros
+--Complex zeros of both Fresnel integrals
+fresnelc_zeros
+--Complex zeros of fresnel cosine integrals
+fresnels_zeros
+--Complex zeros of fresnel sine integrals
+modfresnelp
+--Modified Fresnel integrals F_+(x) and K_+(x)
+modfresnelm
+--Modified Fresnel integrals F_-(x) and K_-(x)
+</pre>
+</div>
+<div class="slide" id="legendre-functions">
+<h1>Legendre Functions</h1>
+<pre class="literal-block">
+lpn
+--Legendre Functions (polynomials) of the first kind
+lqn
+--Legendre Functions of the second kind.
+lpmn
+--Associated Legendre Function of the first kind.
+lqmn
+--Associated Legendre Function of the second kind.
+lpmv
+--Associated Legendre Function of arbitrary non-negative
+ degree v.
+sph_harm
+--Spherical Harmonics (complex-valued) Y^m_n(theta,phi)
+</pre>
+</div>
+<div class="slide" id="orthogonal-polyn">
+<h1>Orthogonal polyn.</h1>
+<pre class="literal-block">
+legendre
+--Legendre polynomial P_n(x)
+chebyt
+--Chebyshev polynomial T_n(x)
+chebyu
+--Chebyshev polynomial U_n(x)
+chebyc
+--Chebyshev polynomial C_n(x)
+chebys
+--Chebyshev polynomial S_n(x)
+jacobi
+--Jacobi polynomial P^(alpha,beta)_n(x)
+laguerre
+--Laguerre polynomial, L_n(x)
+genlaguerre
+--Generalized (Associated) Laguerre polynomial, L^alpha_n(x)
+hermite
+--Hermite polynomial H_n(x)
+hermitenorm
+--Normalized Hermite polynomial, He_n(x)
+gegenbauer
+--Gegenbauer (Ultraspherical) polynomials, C^(alpha)_n(x)
+sh_legendre
+--shifted Legendre polynomial, P*_n(x)
+sh_chebyt
+--shifted Chebyshev polynomial, T*_n(x)
+sh_chebyu
+--shifted Chebyshev polynomial, U*_n(x)
+sh_jacobi
+--shifted Jacobi polynomial, J*_n(x) = G^(p,q)_n(x)
+</pre>
+</div>
+<div class="slide" id="hypergeometric-functions">
+<h1>HyperGeometric Functions</h1>
+<pre class="literal-block">
+hyp2f1
+--Gauss hypergeometric function (2F1)
+hyp1f1
+--Confluent hypergeometric function (1F1)
+hyperu
+--Confluent hypergeometric function (U)
+hyp0f1
+--Confluent hypergeometric limit function (0F1)
+hyp2f0
+--Hypergeometric function (2F0)
+hyp1f2
+--Hypergeometric function (1F2)
+hyp3f0
+--Hypergeometric function (3F0)
+</pre>
+</div>
+<div class="slide" id="parabolic-cylinder-functions">
+<h1>Parabolic Cylinder Functions</h1>
+<pre class="literal-block">
+pbdv
+--Parabolic cylinder function Dv(x) and derivative.
+pbvv
+--Parabolic cylinder function Vv(x) and derivative.
+pbwa
+--Parabolic cylinder function W(a,x) and derivative.
+pbdv_seq
+--Sequence of parabolic cylinder functions Dv(x)
+pbvv_seq
+--Sequence of parabolic cylinder functions Vv(x)
+pbdn_seq
+--Sequence of parabolic cylinder functions Dn(z), complex z
+</pre>
+</div>
+<div class="slide" id="mathieu-functions">
+<h1>Mathieu functions</h1>
+<pre class="literal-block">
+mathieu_a
+--Characteristic values for even solution (ce_m)
+mathieu_b
+--Characteristic values for odd solution (se_m)
+mathieu_even_coef
+--sequence of expansion coefficients for even solution
+mathieu_odd_coef
+--sequence of expansion coefficients for odd solution
+ ** All the following return both function and first derivative **
+mathieu_cem
+--Even mathieu function
+mathieu_sem
+--Odd mathieu function
+mathieu_modcem1
+--Even modified mathieu function of the first kind
+mathieu_modcem2
+--Even modified mathieu function of the second kind
+mathieu_modsem1
+--Odd modified mathieu function of the first kind
+mathieu_modsem2
+--Odd modified mathieu function of the second kind
+</pre>
+</div>
+<div class="slide" id="spheroidal-wave-functions">
+<h1>Spheroidal Wave Functions</h1>
+<pre class="literal-block">
+pro_ang1
+--Prolate spheroidal angular function of the first kind
+pro_rad1
+--Prolate spheroidal radial function of the first kind
+pro_rad2
+--Prolate spheroidal radial function of the second kind
+obl_ang1
+--Oblate spheroidal angluar function of the first kind
+obl_rad1
+--Oblate spheroidal radial function of the first kind
+obl_rad2
+--Oblate spheroidal radial function of the second kind
+pro_cv
+--Compute characteristic value for prolate functions
+obl_cv
+--Compute characteristic value for oblate functions
+pro_cv_seq
+--Compute sequence of prolate characteristic values
+obl_cv_seq
+--Compute sequence of oblate characteristic values
+ ** The following functions require pre-computed characteristic values **
+pro_ang1_cv
+--Prolate spheroidal angular function of the first kind
+pro_rad1_cv
+--Prolate spheroidal radial function of the first kind
+pro_rad2_cv
+--Prolate spheroidal radial function of the second kind
+obl_ang1_cv
+--Oblate spheroidal angluar function of the first kind
+obl_rad1_cv
+--Oblate spheroidal radial function of the first kind
+obl_rad2_cv
+--Oblate spheroidal radial function of the second kind
+</pre>
+</div>
+<div class="slide" id="kelvin-functions">
+<h1>Kelvin Functions</h1>
+<pre class="literal-block">
+kelvin
+--All Kelvin functions (order 0) and derivatives.
+kelvin_zeros
+--Zeros of All Kelvin functions (order 0) and derivatives
+ber
+--Kelvin function ber x
+bei
+--Kelvin function bei x
+berp
+--Derivative of Kelvin function ber x
+beip
+--Derivative of Kelvin function bei x
+ker
+--Kelvin function ker x
+kei
+--Kelvin function kei x
+kerp
+--Derivative of Kelvin function ker x
+keip
+--Derivative of Kelvin function kei x
+ber_zeros
+--Zeros of Kelvin function bei x
+bei_zeros
+--Zeros of Kelvin function ber x
+berp_zeros
+--Zeros of derivative of Kelvin function ber x
+beip_zeros
+--Zeros of derivative of Kelvin function bei x
+ker_zeros
+--Zeros of Kelvin function kei x
+kei_zeros
+--Zeros of Kelvin function ker x
+kerp_zeros
+--Zeros of derivative of Kelvin function ker x
+keip_zeros
+--Zeros of derivative of Kelvin function kei x
+</pre>
+</div>
+<div class="slide" id="other-special-functions">
+<h1>Other Special Functions</h1>
+<pre class="literal-block">
+expn
+--Exponential integral.
+exp1
+--Exponential integral of order 1 (for complex argument)
+expi
+--Another exponential integral
+--Ei(x)
+wofz
+--Fadeeva function.
+dawsn
+--Dawson's integral.
+shichi
+--Hyperbolic sine and cosine integrals.
+sici
+--Integral of the sinc and &quot;cosinc&quot; functions.
+spence
+--Dilogarithm integral.
+zeta
+--Riemann zeta function of two arguments.
+zetac
+--1.0 - standard Riemann zeta function.
+</pre>
+</div>
+<div class="slide" id="convenience-functions">
+<h1>Convenience Functions</h1>
+<pre class="literal-block">
+cbrt
+--Cube root.
+exp10
+--10 raised to the x power.
+exp2
+--2 raised to the x power.
+radian
+--radian angle given degrees, minutes, and seconds.
+cosdg
+--cosine of the angle given in degrees.
+sindg
+--sine of the angle given in degrees.
+tandg
+--tangent of the angle given in degrees.
+cotdg
+--cotangent of the angle given in degrees.
+log1p
+--log(1+x)
+expm1
+--exp(x)-1
+cosm1
+--cos(x)-1
+round
+--round the argument to the nearest integer. If argument
+ ends in 0.5 exactly, pick the nearest even integer.
+</pre>
+</div>
+<div class="slide" id="and-more">
+<h1>... and more!</h1>
+<p>but let us go back to our problem</p>
+<ul class="incremental simple">
+<li>at the present we are cleaning our histories in production with a
+quick and dirty criterium;</li>
+<li>we want to be able to see the histories case by case in order to take
+specific actions;</li>
+<li>we want to go on the Web (--&gt; next)</li>
+</ul>
+</div>
+<div class="slide" id="going-on-the-web">
+<h1>Going on the Web</h1>
+<ul class="incremental simple">
+<li>we want a simple tool for internal usage on our intranet;</li>
+<li>convenient to integrate with other Web tools;</li>
+<li>usable also for non-techical users;</li>
+<li>avoid installing and mantaining on every machine;</li>
+<li>possibly we may open it to our other offices in the world;</li>
+<li>we like the browser interface.</li>
+</ul>
+</div>
+<div class="slide" id="without-a-framework">
+<h1>Without a framework</h1>
+<ul class="incremental simple">
+<li>no security concerns;</li>
+<li>no scalability concerns;</li>
+<li>no nice-looking concerns;</li>
+<li>it must be <em>EASY</em> to change;</li>
+<li>we want minimal learning curve;</li>
+<li>we want no installation/configuration hassle;</li>
+<li>we want no dependencies;</li>
+<li>we want something even simpler than CGI, if possible!</li>
+</ul>
+</div>
+<div class="slide" id="enter-wsgi">
+<h1>Enter WSGI</h1>
+<ul class="incremental simple">
+<li>WSGI = Web Server Gateway Interface (<em>Whiskey</em> for friends)</li>
+<li>the brainchild of Python guru Phillip J. Eby;</li>
+<li>also input from Ian Bicking (<tt class="docutils literal"><span class="pre">paste</span></tt>) and others;</li>
+<li>starting from Python 2.5, we have a WSGI web server in the standard
+library (<tt class="docutils literal"><span class="pre">wsgiref</span></tt>);</li>
+<li>there are plenty of simple and useful add-ons for WSGI applications
+out there;</li>
+<li>even <tt class="docutils literal"><span class="pre">wsgiref</span></tt> fullfills all of our requirements, let's use it!
+(following the example of <a class="reference" href="http://video.google.com/videoplay?docid=-8502904076440714866">Guido</a> ...)</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-key-concepts">
+<h1>WSGI key concepts</h1>
+<ol class="incremental arabic">
+<li><p class="first">WSGI application:</p>
+<p>(env, resp) -&gt; chunks of text</p>
+<p>env = environment dictionary of the server;
+resp = function sending to the client the HTTP headers;</p>
+</li>
+<li><p class="first">WSGI middleware:</p>
+<p>WSGI app -&gt; enhanced WSGI app</p>
+</li>
+</ol>
+</div>
+<div class="slide" id="ex1-hello-world">
+<h1>Ex1: Hello World</h1>
+<pre class="literal-block">
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['&lt;h1&gt;Hello, World!&lt;/h1&gt;']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
+</pre>
+<p><a class="reference" href="http://localhost:8000">show live example</a></p>
+</div>
+<div class="slide" id="ex2-middleware">
+<h1>Ex2: middleware</h1>
+<p>No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware:</p>
+<pre class="literal-block">
+from paste.auth.basic import AuthBasicHandler
+
+def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+</pre>
+</div>
+<div class="slide" id="other-middleware">
+<h1>Other middleware</h1>
+<pre class="literal-block">
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+make_server('', 9090, EvalException(app)).serve_forever()
+</pre>
+<p>Show <a class="reference" href="http://localhost:9090">evalexception</a></p>
+</div>
+<div class="slide" id="wsgi-vs-cgi">
+<h1>WSGI vs. CGI</h1>
+<ul class="simple">
+<li>WSGI is simpler than CGI<ul>
+<li><span class="incremental">using wsgiref you don't require an external server</span></li>
+<li><span class="incremental">you can keep sessions in memory</span></li>
+</ul>
+</li>
+<li>WSGI scales better than CGI<ul>
+<li><span class="incremental">there is a large choice of wsgi servers (mod_wsgi, Twisted ...)</span></li>
+<li><span class="incremental">there is a large choice of third party middleware</span></li>
+<li><span class="incremental">it is relatively easy to turn a toy application into a serious one</span></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-vs-frameworks">
+<h1>WSGI vs. frameworks</h1>
+<p>Pro:</p>
+<ul class="simple">
+<li><span class="incremental">if you liked playing with Lego, you will be happy</span></li>
+<li><span class="incremental">you have much more control and you are not forced to marry a technology</span></li>
+<li><span class="incremental">you can learn a lot</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="id1">
+<h1>WSGI vs. frameworks</h1>
+<p>Contra:</p>
+<ul class="simple">
+<li><span class="incremental">you can build your own framework with WSGI, but you have to debug it</span></li>
+<li><span class="incremental">the existing WSGI frameworks are newer, there is less experience with them</span></li>
+<li><span class="incremental">WSGI is not particularly Twisted-friendly</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="the-history-plotter">
+<h1>The history plotter</h1>
+<pre class="literal-block">
+$ python webplotter.py
+</pre>
+<p><a class="reference" href="http://localhost:8000">Click here for the live demonstration</a></p>
+</div>
+<div class="slide" id="references">
+<h1>References</h1>
+<p>That's all, folks!</p>
+<ul class="simple">
+<li><a class="reference" href="http://www.scipy.org">http://www.scipy.org</a></li>
+<li><a class="reference" href="http://www.python.org/dev/peps/pep-0333">http://www.python.org/dev/peps/pep-0333</a></li>
+<li><a class="reference" href="http://pythonpaste.org/do-it-yourself-framework.html">http://pythonpaste.org/do-it-yourself-framework.html</a></li>
+</ul>
+<p class="incremental"><strong>(P.S. at StatPro, we are hiring! ;)</strong></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/simionato_talk/talk.txt b/pypers/simionato_talk/talk.txt
new file mode 100644
index 0000000..f3c0a3a
--- /dev/null
+++ b/pypers/simionato_talk/talk.txt
@@ -0,0 +1,962 @@
+SciPy on WSGI
+======================================
+
+:Talk given at: PyCon Uno 2007
+:By: Michele Simionato
+:organization: StatPro Italy
+:date: 2007-06-09
+
+.. include:: <s5defs.txt>
+.. footer:: PyCon Uno 2007 - 09 June 2007
+
+.. class:: center
+
+**Subtitle**: *Science on the Web for pedestrians*
+
+Before I start
+------------------------------------------------
+
+What about you?
+
+.. class:: incremental
+
+ Are you more of a programmer or more of a scientist/engineer?
+
+ What kind of scientific tools are you using, if any?
+
+ Have you ever heard of SciPy?
+
+ Have you ever heard of WSGI?
+
+
+Ok, now I can begin ;)
+----------------------------
+
+The motivation from this talk comes from a real problem at StatPro_
+
+.. _StatPro: http://www.statpro.com
+
+.. class:: incremental
+
+we have bad histories for many financial products
+
+.. class:: incremental
+
+wrong prices at some dates in the past
+
+
+A picture
+-------------------------------------
+
+.. image:: badpricehistory.png
+
+.. class:: incremental
+
+(damn data providers!)
+
+Discarding values ...
+---------------------------------------------------
+
+.. image:: badpricehistory2.png
+
+... is tricky!
+
+Issues
+---------------------------
+
+We cannot use the conventional criterium
+
+.. class:: incremental
+
+.. image:: nongaussian.png
+
+Strategy
+---------------------------------------
+
+.. class:: incremental
+
+- price distributions (ln p_i/p) are known to decay with power laws
+
+- fit the distributions with a "reasonable" curve and determine
+ a suitable criterium for the spikes at some confidence level
+
+- a reasonably simple ansatz gives a family of distributions depending
+ on a parameter delta
+
+- `show formulae`_
+
+.. _show formulae: formulas.pdf
+
+delta-distribution
+-----------------------------------------
+
+.. image:: delta-dist.png
+
+From Dirac delta (delta -> 0) to Gaussian
+distribution (delta -> oo)
+
+Cumulative dist
+------------------------------------------
+
+.. image:: cdf-dist.png
+
+VAR-XX = Max loss at XX% confidence level [i_]
+
+.. _i: http://integrals.wolfram.com/index.jsp
+
+Relation VAR-vol.
+-------------------------------------------
+
+If you assume a given distribution, there is a fixed relation between
+VAR-XX and volatility
+
+.. class:: incremental
+
+- for the Gaussian VAR-95 = 1.64 sigma, for a lot of our distributions
+ VAR-95 < 1.0 sigma
+
+- we don't want to make assumptions on the distribution function
+ for computing the VAR
+
+- but we are willing to make assumptions for the sake of eliminating
+ statistically invalid values
+
+The tool we need
+----------------------------------------
+
+::
+
+ $ python simpleplotter.py "fri-gb;AVE"
+
+
+(not 100% finished yet!)
+
+Enter SciPy & Co.
+----------------------------------------
+
+In order to perform our analysis we looked
+at many scientific tools
+
+.. class:: incremental
+
+- a good plotting tool (matplotlib)
+- support for histograms (matplotlib)
+- support for special functions (scipy.special)
+- support for non-linear fitting (scipy.leastsq)
+- good performance (scipy)
+- interactive and IPython-friendly (scipy)
+- bla-bla
+- cheating: I actually used Gnuplot!! ;-)
+
+Installation
+------------------------------------------
+
+If you are lucky, it is trivial::
+
+ $ apt-get install ipython
+ $ apt-get install python-matplotlib
+ $ apt-get install python-numpy
+ $ apt-get install python-numpy-ext
+ $ apt-get install python-scipy
+
+If you are unlucky, or if you try to build from sources,
+YMMV ...
+
+What's in Scipy
+--------------------------------------
+
+.. class:: incremental
+
+- support for multi-dimensional array (numpy)
+- linear algebra and minimization routines
+- solving, integration, interpolation, fitting
+- special functions and statistical functions
+- etc. etc.
+
+Special functions
+--------------------------------------
+
+Airy Functions::
+
+ airy
+ --Airy functions and their derivatives.
+ airye
+ --Exponentially scaled Airy functions
+ ai_zeros
+ --Zeros of Airy functions Ai(x) and Ai'(x)
+ bi_zeros
+ --Zeros of Airy functions Bi(x) and Bi'(x)
+
+Elliptic Functions and Integrals
+--------------------------------------
+
+::
+
+ ellipj
+ --Jacobian elliptic functions
+ ellipk
+ --Complete elliptic integral of the first kind.
+ ellipkinc
+ --Incomplete elliptic integral of the first kind.
+ ellipe
+ --Complete elliptic integral of the second kind.
+ ellipeinc
+ --Incomplete elliptic integral of the second kind.
+
+Bessel Functions
+--------------------------------------
+
+::
+
+ jn
+ --Bessel function of integer order and real argument.
+ jv
+ --Bessel function of real-valued order and complex argument.
+ jve
+ --Exponentially scaled Bessel function.
+ yn
+ --Bessel function of second kind (integer order).
+ yv
+ --Bessel function of the second kind (real-valued order).
+ yve
+ --Exponentially scaled Bessel function of the second kind.
+ kn
+ --Modified Bessel function of the third kind (integer order).
+ kv
+ --Modified Bessel function of the third kind (real order).
+ kve
+ --Exponentially scaled modified Bessel function of the third kind.
+ iv
+ --Modified Bessel function.
+ ive
+ --Exponentially scaled modified Bessel function.
+ hankel1
+ --Hankel function of the first kind.
+ hankel1e
+ --Exponentially scaled Hankel function of the first kind.
+ hankel2
+ --Hankel function of the second kind.
+ hankel2e
+ --Exponentially scaled Hankel function of the second kind.
+
+ lmbda
+ --Sequence of lambda functions with arbitrary order v.
+
+Zeros of Bessel Functions
+--------------------------------------
+
+::
+
+ jnjnp_zeros
+ --Zeros of integer-order Bessel functions and derivatives
+ sorted in order.
+ jnyn_zeros
+ --Zeros of integer-order Bessel functions and derivatives
+ as separate arrays.
+ jn_zeros
+ --Zeros of Jn(x)
+ jnp_zeros
+ --Zeros of Jn'(x)
+ yn_zeros
+ --Zeros of Yn(x)
+ ynp_zeros
+ --Zeros of Yn'(x)
+ y0_zeros
+ --Complex zeros: Y0(z0)=0 and values of Y0'(z0)
+ y1_zeros
+ --Complex zeros: Y1(z1)=0 and values of Y1'(z1)
+ y1p_zeros
+ --Complex zeros of Y1'(z1')=0 and values of Y1(z1')
+
+Faster versions
+--------------------------------------
+
+::
+
+ j0
+ --Bessel function of order 0.
+ j1
+ --Bessel function of order 1.
+ y0
+ --Bessel function of second kind of order 0.
+ y1
+ --Bessel function of second kind of order 1.
+ i0
+ --Modified Bessel function of order 0.
+ i0e
+ --Exponentially scaled modified Bessel function of order 0.
+ i1
+ --Modified Bessel function of order 1.
+ i1e
+ --Exponentially scaled modified Bessel function of order 1.
+ k0
+ --Modified Bessel function of the third kind of order 0.
+ k0e
+ --Exponentially scaled modified Bessel function of the
+ third kind of order 0.
+ k1
+ --Modified Bessel function of the third kind of order 1.
+ k1e
+ --Exponentially scaled modified Bessel function of the
+ third kind of order 1.
+
+Integrals of Bessel Functions
+--------------------------------------
+
+::
+
+ itj0y0
+ --Basic integrals of j0 and y0 from 0 to x.
+ it2j0y0
+ --Integrals of (1-j0(t))/t from 0 to x and
+ y0(t)/t from x to inf.
+ iti0k0
+ --Basic integrals of i0 and k0 from 0 to x.
+ it2i0k0
+ --Integrals of (i0(t)-1)/t from 0 to x and
+ k0(t)/t from x to inf.
+ besselpoly
+ --Integral of a bessel function: Jv(2*a*x) * x^lambda
+ from x=0 to 1.
+
+Derivatives of Bessel Functions
+--------------------------------------
+
+::
+
+ jvp
+ --Nth derivative of Jv(v,z)
+ yvp
+ --Nth derivative of Yv(v,z)
+ kvp
+ --Nth derivative of Kv(v,z)
+ ivp
+ --Nth derivative of Iv(v,z)
+ h1vp
+ --Nth derivative of H1v(v,z)
+ h2vp
+ --Nth derivative of H2v(v,z)
+
+Spherical Bessel Functions
+--------------------------------------
+
+::
+
+ sph_jn
+ --Sequence of spherical Bessel functions, jn(z)
+ sph_yn
+ --Sequence of spherical Bessel functions, yn(z)
+ sph_jnyn
+ --Sequence of spherical Bessel functions, jn(z) and yn(z)
+ sph_in
+ --Sequence of spherical Bessel functions, in(z)
+ sph_kn
+ --Sequence of spherical Bessel functions, kn(z)
+ sph_inkn
+ --Sequence of spherical Bessel functions, in(z) and kn(z)
+
+Riccati-Bessel Fun.
+--------------------------------------
+
+::
+
+ riccati_jn
+ --Sequence of Ricatti-Bessel functions
+ of first kind.
+ riccati_yn
+ --Sequence of Ricatti-Bessel functions
+ of second kind.
+
+Struve Functions
+--------------------------------------
+
+::
+
+ struve
+ --Struve function --- Hv(x)
+ modstruve
+ --Modified struve function --- Lv(x)
+ itstruve0
+ --Integral of H0(t) from 0 to x
+ it2struve0
+ --Integral of H0(t)/t from x to Inf.
+ itmodstruve0
+ --Integral of L0(t) from 0 to x.
+
+
+Statistical Functions
+--------------------------------------
+
+::
+
+ bdtr
+ --Sum of terms 0 through k of of the binomial pdf.
+ bdtrc
+ --Sum of terms k+1 through n of the binomial pdf.
+ bdtri
+ --Inverse of bdtr
+ btdtr
+ --Integral from 0 to x of beta pdf.
+ btdtri
+ --Quantiles of beta distribution
+ fdtr
+ --Integral from 0 to x of F pdf.
+ fdtrc
+ --Integral from x to infinity under F pdf.
+ fdtri
+ --Inverse of fdtrc
+ gdtr
+ --Integral from 0 to x of gamma pdf.
+ gdtrc
+ --Integral from x to infinity under gamma pdf.
+ gdtri
+ --Quantiles of gamma distribution
+ nbdtr
+ --Sum of terms 0 through k of the negative binomial pdf.
+ nbdtrc
+ --Sum of terms k+1 to infinity under negative binomial pdf.
+ nbdtri
+ --Inverse of nbdtr
+ pdtr
+ --Sum of terms 0 through k of the Poisson pdf.
+ pdtrc
+ --Sum of terms k+1 to infinity of the Poisson pdf.
+ pdtri
+ --Inverse of pdtr
+ stdtr
+ --Integral from -infinity to t of the Student-t pdf.
+ stdtri
+ --Inverse of stdtr (quantiles)
+ chdtr
+ --Integral from 0 to x of the Chi-square pdf.
+ chdtrc
+ --Integral from x to infnity of Chi-square pdf.
+ chdtri
+ --Inverse of chdtrc.
+ ndtr
+ --Integral from -infinity to x of standard normal pdf
+ ndtri
+ --Inverse of ndtr (quantiles)
+ smirnov
+ --Kolmogorov-Smirnov complementary CDF for one-sided
+ test statistic (Dn+ or Dn-)
+ smirnovi
+ --Inverse of smirnov.
+ kolmogorov
+ --The complementary CDF of the (scaled) two-sided test
+ statistic (Kn*) valid for large n.
+ kolmogi
+ --Inverse of kolmogorov
+ tklmbda
+ --Tukey-Lambda CDF
+
+Gamma and Related Functions
+--------------------------------------
+
+::
+
+ gamma
+ --Gamma function.
+ gammaln
+ --Log of the absolute value of the gamma function.
+ gammainc
+ --Incomplete gamma integral.
+ gammaincinv
+ --Inverse of gammainc.
+ gammaincc
+ --Complemented incomplete gamma integral.
+ gammainccinv
+ --Inverse of gammaincc.
+ beta
+ --Beta function.
+ betaln
+ --Log of the absolute value of the beta function.
+ betainc
+ --Incomplete beta integral.
+ betaincinv
+ --Inverse of betainc.
+ betaincinva
+ --Inverse (in first argument, a) of betainc
+ betaincinvb
+ --Inverse (in first argument, b) of betainc
+ psi(digamma)
+ --Logarithmic derivative of the gamma function.
+ rgamma
+ --One divided by the gamma function.
+ polygamma
+ --Nth derivative of psi function.
+
+Error Function and Fresnel Int.
+--------------------------------------
+
+::
+
+ erf
+ --Error function.
+ erfc
+ --Complemented error function (1- erf(x))
+ erfinv
+ --Inverse of error function
+ erfcinv
+ --Inverse of erfc
+ erf_zeros
+ --Complex zeros of erf(z)
+ fresnel
+ --Fresnel sine and cosine integrals.
+ fresnel_zeros
+ --Complex zeros of both Fresnel integrals
+ fresnelc_zeros
+ --Complex zeros of fresnel cosine integrals
+ fresnels_zeros
+ --Complex zeros of fresnel sine integrals
+ modfresnelp
+ --Modified Fresnel integrals F_+(x) and K_+(x)
+ modfresnelm
+ --Modified Fresnel integrals F_-(x) and K_-(x)
+
+Legendre Functions
+--------------------------------------
+
+::
+
+ lpn
+ --Legendre Functions (polynomials) of the first kind
+ lqn
+ --Legendre Functions of the second kind.
+ lpmn
+ --Associated Legendre Function of the first kind.
+ lqmn
+ --Associated Legendre Function of the second kind.
+ lpmv
+ --Associated Legendre Function of arbitrary non-negative
+ degree v.
+ sph_harm
+ --Spherical Harmonics (complex-valued) Y^m_n(theta,phi)
+
+Orthogonal polyn.
+--------------------------------------
+
+::
+
+ legendre
+ --Legendre polynomial P_n(x)
+ chebyt
+ --Chebyshev polynomial T_n(x)
+ chebyu
+ --Chebyshev polynomial U_n(x)
+ chebyc
+ --Chebyshev polynomial C_n(x)
+ chebys
+ --Chebyshev polynomial S_n(x)
+ jacobi
+ --Jacobi polynomial P^(alpha,beta)_n(x)
+ laguerre
+ --Laguerre polynomial, L_n(x)
+ genlaguerre
+ --Generalized (Associated) Laguerre polynomial, L^alpha_n(x)
+ hermite
+ --Hermite polynomial H_n(x)
+ hermitenorm
+ --Normalized Hermite polynomial, He_n(x)
+ gegenbauer
+ --Gegenbauer (Ultraspherical) polynomials, C^(alpha)_n(x)
+ sh_legendre
+ --shifted Legendre polynomial, P*_n(x)
+ sh_chebyt
+ --shifted Chebyshev polynomial, T*_n(x)
+ sh_chebyu
+ --shifted Chebyshev polynomial, U*_n(x)
+ sh_jacobi
+ --shifted Jacobi polynomial, J*_n(x) = G^(p,q)_n(x)
+
+HyperGeometric Functions
+--------------------------------------
+
+::
+
+ hyp2f1
+ --Gauss hypergeometric function (2F1)
+ hyp1f1
+ --Confluent hypergeometric function (1F1)
+ hyperu
+ --Confluent hypergeometric function (U)
+ hyp0f1
+ --Confluent hypergeometric limit function (0F1)
+ hyp2f0
+ --Hypergeometric function (2F0)
+ hyp1f2
+ --Hypergeometric function (1F2)
+ hyp3f0
+ --Hypergeometric function (3F0)
+
+Parabolic Cylinder Functions
+--------------------------------------
+
+::
+
+ pbdv
+ --Parabolic cylinder function Dv(x) and derivative.
+ pbvv
+ --Parabolic cylinder function Vv(x) and derivative.
+ pbwa
+ --Parabolic cylinder function W(a,x) and derivative.
+ pbdv_seq
+ --Sequence of parabolic cylinder functions Dv(x)
+ pbvv_seq
+ --Sequence of parabolic cylinder functions Vv(x)
+ pbdn_seq
+ --Sequence of parabolic cylinder functions Dn(z), complex z
+
+Mathieu functions
+--------------------------------------
+
+::
+
+ mathieu_a
+ --Characteristic values for even solution (ce_m)
+ mathieu_b
+ --Characteristic values for odd solution (se_m)
+ mathieu_even_coef
+ --sequence of expansion coefficients for even solution
+ mathieu_odd_coef
+ --sequence of expansion coefficients for odd solution
+ ** All the following return both function and first derivative **
+ mathieu_cem
+ --Even mathieu function
+ mathieu_sem
+ --Odd mathieu function
+ mathieu_modcem1
+ --Even modified mathieu function of the first kind
+ mathieu_modcem2
+ --Even modified mathieu function of the second kind
+ mathieu_modsem1
+ --Odd modified mathieu function of the first kind
+ mathieu_modsem2
+ --Odd modified mathieu function of the second kind
+
+Spheroidal Wave Functions
+--------------------------------------
+
+::
+
+ pro_ang1
+ --Prolate spheroidal angular function of the first kind
+ pro_rad1
+ --Prolate spheroidal radial function of the first kind
+ pro_rad2
+ --Prolate spheroidal radial function of the second kind
+ obl_ang1
+ --Oblate spheroidal angluar function of the first kind
+ obl_rad1
+ --Oblate spheroidal radial function of the first kind
+ obl_rad2
+ --Oblate spheroidal radial function of the second kind
+ pro_cv
+ --Compute characteristic value for prolate functions
+ obl_cv
+ --Compute characteristic value for oblate functions
+ pro_cv_seq
+ --Compute sequence of prolate characteristic values
+ obl_cv_seq
+ --Compute sequence of oblate characteristic values
+ ** The following functions require pre-computed characteristic values **
+ pro_ang1_cv
+ --Prolate spheroidal angular function of the first kind
+ pro_rad1_cv
+ --Prolate spheroidal radial function of the first kind
+ pro_rad2_cv
+ --Prolate spheroidal radial function of the second kind
+ obl_ang1_cv
+ --Oblate spheroidal angluar function of the first kind
+ obl_rad1_cv
+ --Oblate spheroidal radial function of the first kind
+ obl_rad2_cv
+ --Oblate spheroidal radial function of the second kind
+
+Kelvin Functions
+--------------------------------------
+
+::
+
+ kelvin
+ --All Kelvin functions (order 0) and derivatives.
+ kelvin_zeros
+ --Zeros of All Kelvin functions (order 0) and derivatives
+ ber
+ --Kelvin function ber x
+ bei
+ --Kelvin function bei x
+ berp
+ --Derivative of Kelvin function ber x
+ beip
+ --Derivative of Kelvin function bei x
+ ker
+ --Kelvin function ker x
+ kei
+ --Kelvin function kei x
+ kerp
+ --Derivative of Kelvin function ker x
+ keip
+ --Derivative of Kelvin function kei x
+ ber_zeros
+ --Zeros of Kelvin function bei x
+ bei_zeros
+ --Zeros of Kelvin function ber x
+ berp_zeros
+ --Zeros of derivative of Kelvin function ber x
+ beip_zeros
+ --Zeros of derivative of Kelvin function bei x
+ ker_zeros
+ --Zeros of Kelvin function kei x
+ kei_zeros
+ --Zeros of Kelvin function ker x
+ kerp_zeros
+ --Zeros of derivative of Kelvin function ker x
+ keip_zeros
+ --Zeros of derivative of Kelvin function kei x
+
+Other Special Functions
+--------------------------------------
+
+::
+
+ expn
+ --Exponential integral.
+ exp1
+ --Exponential integral of order 1 (for complex argument)
+ expi
+ --Another exponential integral
+ --Ei(x)
+ wofz
+ --Fadeeva function.
+ dawsn
+ --Dawson's integral.
+ shichi
+ --Hyperbolic sine and cosine integrals.
+ sici
+ --Integral of the sinc and "cosinc" functions.
+ spence
+ --Dilogarithm integral.
+ zeta
+ --Riemann zeta function of two arguments.
+ zetac
+ --1.0 - standard Riemann zeta function.
+
+Convenience Functions
+--------------------------------------
+
+::
+
+ cbrt
+ --Cube root.
+ exp10
+ --10 raised to the x power.
+ exp2
+ --2 raised to the x power.
+ radian
+ --radian angle given degrees, minutes, and seconds.
+ cosdg
+ --cosine of the angle given in degrees.
+ sindg
+ --sine of the angle given in degrees.
+ tandg
+ --tangent of the angle given in degrees.
+ cotdg
+ --cotangent of the angle given in degrees.
+ log1p
+ --log(1+x)
+ expm1
+ --exp(x)-1
+ cosm1
+ --cos(x)-1
+ round
+ --round the argument to the nearest integer. If argument
+ ends in 0.5 exactly, pick the nearest even integer.
+
+... and more!
+------------------------------------------
+
+but let us go back to our problem
+
+.. class:: incremental
+
+- at the present we are cleaning our histories in production with a
+ quick and dirty criterium;
+- we want to be able to see the histories case by case in order to take
+ specific actions;
+- we want to go on the Web (--> next)
+
+Going on the Web
+-----------------------------------
+
+.. class:: incremental
+
+- we want a simple tool for internal usage on our intranet;
+- convenient to integrate with other Web tools;
+- usable also for non-techical users;
+- avoid installing and mantaining on every machine;
+- possibly we may open it to our other offices in the world;
+- we like the browser interface.
+
+Without a framework
+---------------------------------------------
+
+.. class:: incremental
+
+- no security concerns;
+- no scalability concerns;
+- no nice-looking concerns;
+
+- it must be *EASY* to change;
+- we want minimal learning curve;
+- we want no installation/configuration hassle;
+
+- we want no dependencies;
+- we want something even simpler than CGI, if possible!
+
+Enter WSGI
+-------------------------
+
+.. class:: incremental
+
+- WSGI = Web Server Gateway Interface (*Whiskey* for friends)
+- the brainchild of Python guru Phillip J. Eby;
+- also input from Ian Bicking (``paste``) and others;
+- starting from Python 2.5, we have a WSGI web server in the standard
+ library (``wsgiref``);
+- there are plenty of simple and useful add-ons for WSGI applications
+ out there;
+- even ``wsgiref`` fullfills all of our requirements, let's use it!
+ (following the example of Guido_ ...)
+
+.. _Guido: http://video.google.com/videoplay?docid=-8502904076440714866
+
+WSGI key concepts
+------------------------------------------------
+
+.. class:: incremental
+
+1. WSGI application:
+
+ (env, resp) -> chunks of text
+
+ env = environment dictionary of the server;
+ resp = function sending to the client the HTTP headers;
+
+2. WSGI middleware:
+
+ WSGI app -> enhanced WSGI app
+
+Ex1: Hello World
+-------------------------------
+
+::
+
+ from wsgiref import simple_server
+
+ def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+ server=simple_server.make_server('', 8000, app)
+ server.serve_forever()
+
+`show live example`_
+
+.. _`show live example`: http://localhost:8000
+
+Ex2: middleware
+---------------------------
+
+No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware::
+
+ from paste.auth.basic import AuthBasicHandler
+
+ def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+ auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+
+Other middleware
+-----------------------------------------------
+
+::
+
+ from wsgiref.simple_server import make_server
+ from paste.evalexception import EvalException
+
+ a, b = 1,0
+
+ def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+ make_server('', 9090, EvalException(app)).serve_forever()
+
+Show evalexception_
+
+.. _evalexception: http://localhost:9090
+
+WSGI vs. CGI
+--------------------------------------------
+
+- WSGI is simpler than CGI
+
+ + `using wsgiref you don't require an external server`
+ + `you can keep sessions in memory`
+
+- WSGI scales better than CGI
+
+ + `there is a large choice of wsgi servers (mod_wsgi, Twisted ...)`
+ + `there is a large choice of third party middleware`
+ + `it is relatively easy to turn a toy application into a serious one`
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Pro:
+
+- `if you liked playing with Lego, you will be happy`
+- `you have much more control and you are not forced to marry a technology`
+- `you can learn a lot`
+- `others ...`
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Contra:
+
+- `you can build your own framework with WSGI, but you have to debug it`
+- `the existing WSGI frameworks are newer, there is less experience with them`
+- `WSGI is not particularly Twisted-friendly`
+- `others ...`
+
+The history plotter
+----------------------------------------------------
+
+::
+
+ $ python webplotter.py
+
+`Click here for the live demonstration`_
+
+.. _`Click here for the live demonstration`: http://localhost:8000
+
+
+References
+-----------
+
+That's all, folks!
+
+
+
+- http://www.scipy.org
+- http://www.python.org/dev/peps/pep-0333
+- http://pythonpaste.org/do-it-yourself-framework.html
+
+.. class:: incremental
+
+ **(P.S. at StatPro, we are hiring! ;)**
diff --git a/pypers/simionato_talk/test_cdf.py b/pypers/simionato_talk/test_cdf.py
new file mode 100644
index 0000000..110f870
--- /dev/null
+++ b/pypers/simionato_talk/test_cdf.py
@@ -0,0 +1,10 @@
+from scipy.integrate import quad
+from delta_dist import cdf, delta
+
+def cdf(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*x*hyp2f1(.5, 1.5+d2/2., 1.5, -x*x)
+
+print quad(delta(.2), 0., 1.0)
+print cdf(.2)(1.0)
diff --git a/pypers/simionato_talk/ui/default/ex.html b/pypers/simionato_talk/ui/default/ex.html
new file mode 100644
index 0000000..798e3cc
--- /dev/null
+++ b/pypers/simionato_talk/ui/default/ex.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style type="text/css">
+body
+{
+background: url(statpro_logo.gif) no-repeat top
+}
+</style>
+</head>
+<body>
+Prova Background CSS
+</body>
+</html>
diff --git a/pypers/simionato_talk/ui/default/slides.js b/pypers/simionato_talk/ui/default/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/simionato_talk/ui/default/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/simionato_talk/webplotter.py b/pypers/simionato_talk/webplotter.py
new file mode 100644
index 0000000..f2b9a55
--- /dev/null
+++ b/pypers/simionato_talk/webplotter.py
@@ -0,0 +1,36 @@
+import os, cgi, traceback
+from wsgiref import simple_server
+from tempfile import mkstemp
+from simpleplotter import make_graph
+
+def getformdict(env):
+ qs = env.get('QUERY_STRING')
+ if qs:
+ return dict((k, v[0]) for k, v in cgi.parse_qs(qs).iteritems())
+
+def app(env, resp):
+ form = getformdict(env)
+ if form and form.get('submitted'):
+ try:
+ fname = make_graph(form.get('code'), batch=True)
+ except Exception, e:
+ resp('500 ERR', [('Content-type', 'text/plain')])
+ return [traceback.format_exc()]
+ else:
+ resp('200 OK', [('Content-type', 'image/png')])
+ return file(fname)
+ else:
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [
+ 'Try values such as <pre>fri-gb;AVE</pre>',
+ '<pre>fri-gb;TSCO</pre> <pre>fri-us;DELL</pre>',
+ '<form>', 'insert code ',
+ '<input type="text" name="code"/>',
+ '<input type="submit", name="submitted", value="submit" />',
+ '</form>']
+
+if __name__ == '__main__':
+ #from paste.auth.basic import AuthBasicHandler
+ #app = AuthBasicHandler(
+ # app, 'plotter realm', lambda e, u, p: u=='pippo' and p=='lippo')
+ simple_server.make_server('', 8000, app).serve_forever()
diff --git a/pypers/style.tex b/pypers/style.tex
new file mode 100755
index 0000000..b695c98
--- /dev/null
+++ b/pypers/style.tex
@@ -0,0 +1,20 @@
+% donot indent first line.
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{5pt plus 2pt minus 1pt}
+
+% sloppy
+% ------
+% Less strict (opposite to default fussy) space size between words. Therefore
+% less hyphenation.
+\sloppy
+
+% fonts
+% -----
+% times for pdf generation, gives smaller pdf files.
+%
+% But in standard postscript fonts: courier and times/helvetica do not fit.
+% Maybe use pslatex.
+\usepackage{times}
+
+% pagestyle
+\pagestyle{headings}
diff --git a/pypers/super/chapman.txt b/pypers/super/chapman.txt
new file mode 100755
index 0000000..019f766
--- /dev/null
+++ b/pypers/super/chapman.txt
@@ -0,0 +1,103 @@
+``super`` with only one argument ("bound" super) is a mess.
+
+AFAICT ``super(C)`` is intended to be used as an attribute in
+other classes. Then the descriptor magic will automatically convert the
+unbound syntax in the bound syntax. For instance:
+
+>>> class B(object):
+... a = 1
+>>> class C(B):
+... pass
+>>> class D(C):
+... sup = super(C)
+>>> d = D()
+>>> d.sup.a
+1
+
+This works since ``d.sup.a`` calls ``super(C).__get__(d,D).a`` which is
+converted to ``super(C, d).a`` and retrieves ``B.a``.
+
+There is a single use case for the single argument
+syntax of ``super`` that I am aware of, but I think it gives more troubles
+than advantages. The use case is the implementation of "autosuper" made
+by Guido on his essay about new-style classes.
+
+The idea there is to use the unbound super objects as private
+attributes. For instance, in our example, we could define the
+private attribute ``__sup`` in the class ``C`` as the unbound
+super object ``super(C)``:
+
+>>> C._C__sup = super(C)
+
+With this definition inside the methods the syntax
+``self.__sup.meth(arg)`` can be used
+as an alternative to ``super(C, self).meth(arg)``, and the advantage is
+that you avoid to repeat the name of the class in the calling
+syntax, since that name is hidden in the mangling mechanism of
+private names. The creation of the ``__sup`` attributes can be hidden
+in a metaclass and made automatic. So, all this seems to work: but
+actually this is *not* the case.
+
+Things may wrong in various case, for instance for classmethods,
+as in this example::
+
+ #<ex1.py>
+
+ class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ @classmethod
+ def meth(cls):
+ print "B.meth(%s)" % cls
+
+ class C(B):
+ @classmethod
+ def meth(cls):
+ print "C.meth(%s)" % cls
+ cls.__super.meth()
+
+ C._C__super = super(C)
+
+ class D(C):
+ pass
+
+ D._D__super = super(D)
+
+
+ d=D()
+
+ d.meth()
+
+ #</ex1.py>
+
+The last line raises an ``AttributeError: 'super' object has no attribute
+'meth'.``
+
+So, using a ``__super`` unbound super object is not a robust solution
+(notice that everything would work by substituting ``self.__super.meth()``
+with ``super(C,self).meth()``. There are other ways to avoid repeating
+the class name, see for instance my cookbook recipe, which will also be
+in the printed version.
+
+If it was me, I would just remove the single argument syntax of ``super``,
+making it illegal. But this would probably break someone code, so
+I don't think it will ever happen. Another solution would be just to
+deprecate it. There is no need for this syntax, one can always circumvent
+it. In addition to the other issues, the unbound form of ``super`` does
+not play well with pydoc.
+The problems is still there in Python 2.4 (see bug report SF729103)
+
+>>> class B(object): pass
+...
+>>> class C(B):
+... s=super(B)
+...
+>>> help(C)
+Traceback (most recent call last):
+ ...
+ ... lots of stuff here
+ ...
+ File "/usr/lib/python2.4/pydoc.py", line 1290, in docother
+ chop = maxlen - len(line)
+TypeError: unsupported operand type(s) for -: 'type' and 'int'
+
diff --git a/pypers/super/cls_mcl.txt b/pypers/super/cls_mcl.txt
new file mode 100755
index 0000000..5cc6320
--- /dev/null
+++ b/pypers/super/cls_mcl.txt
@@ -0,0 +1,26 @@
+#<ex.py>
+
+ class B(object):
+ def __init__(self, *args):
+ print "B.__init__"
+ super(B, self).__init__(*args)
+
+ class M(B, type):
+ def __init__(self, n, b, d):
+ print "M.__init__"
+ super(M, self).__init__(n, b, d)
+
+ class C(B):
+ __metaclass__ = M
+ def __init__(self):
+ print "C.__init__"
+ super(C, self).__init__()
+
+ #</ex.py>
+
+>>> from ex import C
+M.__init__
+B.__init__
+>>> c = C()
+C.__init__
+B.__init__
diff --git a/pypers/super/descr.py b/pypers/super/descr.py
new file mode 100755
index 0000000..300be52
--- /dev/null
+++ b/pypers/super/descr.py
@@ -0,0 +1,16 @@
+# descr.py
+
+class Descr(object):
+ def __init__(self, a):
+ self.a = a
+ def __get__(self, obj, objtyp):
+ return self.a
+
+class C(object):
+ p=property()
+
+#C.s = super(C, C())
+
+if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
diff --git a/pypers/super/descr_example.py b/pypers/super/descr_example.py
new file mode 100755
index 0000000..41cd6d7
--- /dev/null
+++ b/pypers/super/descr_example.py
@@ -0,0 +1,21 @@
+# descr_example.py
+
+class Descr(object):
+ def __init__(self, attr):
+ self.attr = attr
+ def __get__(self, obj, objtyp = None):
+ return self.attr
+
+class M(type):
+ a = 1 #Descr("I am an attribute descriptor")
+
+class B:
+ __metaclass__=M
+
+class C(B):
+ pass
+
+print super(C,C()).a #=> I am an attribute descriptor
+
+
+
diff --git a/pypers/super/ex.py b/pypers/super/ex.py
new file mode 100755
index 0000000..6a8489a
--- /dev/null
+++ b/pypers/super/ex.py
@@ -0,0 +1,19 @@
+# ex.py
+
+class B(object):
+ def __init__(self, *args):
+ print "B.__init__"
+ super(B, self).__init__(*args)
+
+class M(B, type):
+ def __init__(self, n, b, d):
+ print "M.__init__"
+ super(M, self).__init__(n, b, d)
+
+class C(B):
+ __metaclass__ = M
+ def __init__(self):
+ print "C.__init__"
+ super(C, self).__init__()
+
+
diff --git a/pypers/super/ex1.py b/pypers/super/ex1.py
new file mode 100755
index 0000000..6c68a9d
--- /dev/null
+++ b/pypers/super/ex1.py
@@ -0,0 +1,28 @@
+# ex1.py
+
+class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ def meth(self):
+ print "B.meth(%s)" % self
+ meth = classmethod(meth)
+
+class C(B):
+ def meth(self):
+ print "C.meth(%s)" % self
+ self.__super.meth()
+ meth = classmethod(meth)
+
+C._C__super = super(C)
+
+class D(C):
+ pass
+
+D._D__super = super(D)
+
+
+d=D()
+
+d.meth()
+
+
diff --git a/pypers/super/ex2.py b/pypers/super/ex2.py
new file mode 100755
index 0000000..602631f
--- /dev/null
+++ b/pypers/super/ex2.py
@@ -0,0 +1,12 @@
+# ex2.py# fixed in 2.4
+
+ class C(object):
+ pass
+
+ C.s = super(C)
+
+ if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
+
+
diff --git a/pypers/super/ex3.py b/pypers/super/ex3.py
new file mode 100755
index 0000000..8841d7d
--- /dev/null
+++ b/pypers/super/ex3.py
@@ -0,0 +1,19 @@
+class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ def meth(self):
+ print "B.meth(%s)" % self
+ meth=classmethod(meth)
+
+class C(B):
+ def meth(self):
+ print "C.meth(%s)" % self
+ super(C,self).meth()
+ meth=classmethod(meth)
+
+class D(C):
+ pass
+
+d=D()
+
+d.meth()
diff --git a/pypers/super/ex4.py b/pypers/super/ex4.py
new file mode 100755
index 0000000..827629f
--- /dev/null
+++ b/pypers/super/ex4.py
@@ -0,0 +1,9 @@
+class B(object):
+ pass
+
+class C(B):
+ s=super(B)
+
+from pydoc import help
+
+help(C)
diff --git a/pypers/super/ex5.py b/pypers/super/ex5.py
new file mode 100755
index 0000000..b0087b1
--- /dev/null
+++ b/pypers/super/ex5.py
@@ -0,0 +1,18 @@
+class M(type):
+ pass
+
+class B(object):
+ __metaclass__=M
+ a='class attribute'
+
+
+class C(B):
+ pass
+
+print super(C,C).a
+
+
+print super(M,M).__name__
+
+print super(M,C).a
+
diff --git a/pypers/super/ex_roth.py b/pypers/super/ex_roth.py
new file mode 100755
index 0000000..d709c09
--- /dev/null
+++ b/pypers/super/ex_roth.py
@@ -0,0 +1,12 @@
+class C(object):
+ @staticmethod
+ def f():
+ print "C.f"
+
+class D(C):
+ @staticmethod
+ def f():
+ print "D.f"
+ super(D, D).f()
+
+D.f()
diff --git a/pypers/super/example1.py b/pypers/super/example1.py
new file mode 100755
index 0000000..2265c78
--- /dev/null
+++ b/pypers/super/example1.py
@@ -0,0 +1,18 @@
+# example1.py
+
+class M(type):
+ "A metaclass with a class attribute 'a'."
+ a = 1
+
+class B:
+ "An instance of M with a meta-attribute 'a'."
+ __metaclass__ = M
+
+class C(B):
+ "An instance of M with the same meta-attribute 'a'"
+
+if __name__ == "__main__":
+ print B.a, C.a # => 1 1
+ print super(C,C).a #=> attribute error
+
+
diff --git a/pypers/super/post.txt b/pypers/super/post.txt
new file mode 100755
index 0000000..115c12b
--- /dev/null
+++ b/pypers/super/post.txt
@@ -0,0 +1,14 @@
+"super" is one of the trickiest things in Python. I remember having
+spent a *lot* of time with it in the past. I keep seeing postings from
+people confused about "super" coming in this newsgroup. The current
+documentation about "super" is at the best incomplete, in some parts is
+misleading and in other parts even wrong. Finally, I decided to write a
+paper telling the truth about super. This is not the whole truth,
+however, since I am not enough confident to say that I do understand
+all secrets of "super". I have posted a draft here:
+
+http://www.phyast.pitt.edu/~micheles/super.html,
+
+for review from knowledgeable Pythonistas.
+Ideally, a corrected/expanded/revision of this draft should go somewhere
+in python.org, so people will have a decent reference about "super".
diff --git a/pypers/super/special_meth.py b/pypers/super/special_meth.py
new file mode 100755
index 0000000..66104b0
--- /dev/null
+++ b/pypers/super/special_meth.py
@@ -0,0 +1,21 @@
+"""
+Does bound super have problems with special methods?
+""" # seems not
+
+"""
+certainly problems with __self_class__, __thisclass__
+"""
+class B(object):
+ def __init__(self):
+ print "B.__init__"
+
+class C(B):
+ def __init__(self):
+ print "C.__init__"
+ self.__super.__init__()
+
+C._C__super = super(C)
+
+c = C()
+
+print dir(c._C__super)
diff --git a/pypers/super/super.html b/pypers/super/super.html
new file mode 100755
index 0000000..a970a18
--- /dev/null
+++ b/pypers/super/super.html
@@ -0,0 +1,564 @@
+<?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>The truth about super</title>
+<meta name="author" content="Michele Simionato" />
+<meta name="date" content="June 2004" />
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<h1 class="title">The truth about <tt class="literal"><span class="pre">super</span></tt></h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr class="field"><th class="docinfo-name">Email:</th><td class="field-body"><a class="reference" href="mailto:michele.simionato&#64;libero.it">michele.simionato&#64;libero.it</a></td>
+</tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>June 2004</td></tr>
+<tr><th class="docinfo-name">Status:</th>
+<td>Draft</td></tr>
+</tbody>
+</table>
+<div class="document" id="the-truth-about-super">
+<p><tt class="literal"><span class="pre">super</span></tt> is a new built-in, first introduced in Python 2.2 and slightly
+improved and fixed in Python 2.3, which is little known to the average
+Python programmer. One of the reason for this
+fact is its poor documentation`: at the time of this writing
+(June 2004) <tt class="literal"><span class="pre">super</span></tt> documentation is incomplete and in some parts
+misleading and even wrong. For instance, it was recently pointed out
+on comp.lang.python that the standard library (Python 2.3.4, section 2.1)
+still says:</p>
+<pre class="literal-block">
+super(type[, object-or-type])
+ Return the superclass of type. If the second argument is omitted the
+ super object returned is unbound. If the second argument is an object,
+ isinstance(obj, type) must be true. If the second argument is a type,
+ issubclass(type2, type) must be true. super() only works for new-style
+ classes.
+</pre>
+<p>The first sentence is just plain wrong. <tt class="literal"><span class="pre">super</span></tt> does not return the
+superclass. There is no such a thing as &quot;the&quot; superclass in a Multiple
+Inheritance (MI) language. Also, the sentence about 'unbound' is misleading,
+since it may easily lead the programmer to think about bound and unbound
+methods, whereas it has nothing to do with that concept. Finally, there
+subtle pitfalls of <tt class="literal"><span class="pre">super</span></tt> which are not at all mentioned. IMNSHO
+<tt class="literal"><span class="pre">super</span></tt> is one of the most trickiest and surprising Python constructs,
+so it absolutely needs a document to share light on some of his secrets:
+the present article aims to fix the issues with the current documentation,
+and tell you the &quot;truth&quot; about <tt class="literal"><span class="pre">super</span></tt>. At least the amount of truth
+I have discovered with my experimentations, which is certainly
+not the whole truth ;)</p>
+<p>Here is the plan: first I will discuss the concept of superclass in
+a Multiple Inheritance (MI) world (there is no such a thing as
+&quot;the&quot; superclass!); second, I will show that <tt class="literal"><span class="pre">super</span></tt> is a proxy
+object, able to dispatch to the right methods/attributes in the
+MRO; third,</p>
+<p>recall some background on
+how the resolution of methods works on how
+descriptors work and
+(essentially pointing out to the standard references); then
+I will discuss the more common invocation of <tt class="literal"><span class="pre">super</span></tt> -
+the invocation with two arguments - and finally I will discuss
+the most tricky part, i.e. invoking <tt class="literal"><span class="pre">super</span></tt> with just one
+argument and I will discuss pitfalls.</p>
+<p>Finally, a fair warning: this document is aimed to expert
+Pythonistas. It is not for the faint of heart ;)</p>
+<div class="section" id="first-truth-there-is-no-superclass-in-a-mi-world">
+<h1><a name="first-truth-there-is-no-superclass-in-a-mi-world">First truth: there is no superclass in a MI world</a></h1>
+<p>Readers familiar will single inheritance languages, such as
+Java or Smalltalk, will have a clear concept of superclass
+in mind. This concept, however, has <em>no useful meaning</em> in Python or in
+other Multiple Inheritance languages. This fact was proved to
+me by Bjorn Pettersen in a thread on comp.lang.python in May 2003
+(at that time I was mistakenly thinking that one could define a
+superclass concept in Python). Consider this example from that
+discussion:</p>
+<pre class="literal-block">
+ +-----+
+ | T |
+ |a = 0|
+ +-----+
+ / \
+ / \
++-------+ +-------+
+| A | | B |
+| | | a = 2 |
++-------+ +-------+
+ \ /
+ \ /
+ +-----+
+ | C |
+ +-----+
+ :
+ : instantiation
+ c
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class T(object):
+... a = 0
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class A(T):
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(T):
+... a = 2
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(A,B):
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; c = C()
+</pre>
+<p>Who is the superclass of C? There are two direct superclasses (i.e. bases)
+of C: A and B. A comes before B, so one would naturally think
+that the superclass of C is A. A inherits the attribute 'a' from T,
+so if <tt class="literal"><span class="pre">super(C,c)</span></tt> was returning the superclass of C, then
+it should return 'A'. 'A' inherits its attribute 'a' from T,
+where 'a' has the value 0, so <tt class="literal"><span class="pre">super(C,c).a</span></tt> would return 0. This
+is NOT what happens. Instead, <tt class="literal"><span class="pre">super(C,c).a</span></tt> walks trought the
+method resolution order <a class="footnote-reference" href="#id4" id="id1" name="id1">[1]</a> of the class of <tt class="literal"><span class="pre">c</span></tt> (which is 'C')
+and retrieves the attribute from the first class above 'C' which
+defines it. In this example the MRO of 'C' is [C, A, B, T, object], so
+B is the first class above C which defines 'a' and <tt class="literal"><span class="pre">super(C,c).a</span></tt>
+correctly returns the value 2, not 0:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,c).a
+2
+</pre>
+<p>You may call 'A' the superclass of 'C', but this is not an useful
+concept since the methods are resolved by looking at the classes
+in the MRO of 'C', and not by looking at the classes in the MRO of A
+(which in this case is [A,T, object] and does not contain B).</p>
+<p>So, using the word <em>superclass</em> in the standard doc is completely
+misleading and should be avoided altogether.</p>
+</div>
+<div class="section" id="second-truth-super-returns-proxy-objects">
+<h1><a name="second-truth-super-returns-proxy-objects">Second truth: <tt class="literal"><span class="pre">super</span></tt> returns proxy objects</a></h1>
+<p>Having understood that <tt class="literal"><span class="pre">super</span></tt> cannot return and does not return the
+mythical superclass, we ask ourselves what the hell is returning
+<tt class="literal"><span class="pre">super</span></tt> ;) The truth is that <tt class="literal"><span class="pre">super</span></tt> returns proxy objects.
+Informally speaking, a proxy object is an object with
+the ability to dispatch to methods of other classes via delegation.</p>
+<p>Technically, <tt class="literal"><span class="pre">super</span></tt> works by overriding the <tt class="literal"><span class="pre">__getattribute__</span></tt>
+method in such a way that its instances becomes proxy objects providing
+access to the methods in the MRO. The dispatch is done in such a way
+that</p>
+<p><tt class="literal"><span class="pre">super(cls,</span> <span class="pre">instance-or-subclass).meth(*args,</span> <span class="pre">**kw)</span></tt></p>
+<p>corresponds to</p>
+<p><tt class="literal"><span class="pre">&lt;right</span> <span class="pre">method</span> <span class="pre">in</span> <span class="pre">the</span> <span class="pre">MRO&gt;(instance-or-subclass,</span> <span class="pre">*args,</span> <span class="pre">**kw)</span></tt></p>
+<p>There is a caveat at this point: the second argument can be
+an instance of the first argument, or a subclass of it, but
+in <em>both cases</em> a bound method is returned (one could naivily
+think, instead, that when a subclass is passed one gets an
+unbound method).</p>
+<p>For instance, in this example</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... def __repr__(self):
+... return &quot;&lt;instance of %s&gt;&quot; % self.__class__.__name__
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(B):
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(C):
+... pass
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; d = D()
+</pre>
+<p>both</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C,d).__repr__
+&lt;bound method D.__repr__ of &lt;instance of D&gt;&gt;
+</pre>
+<p>and</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C,D).__repr__
+&lt;bound method D.__repr__ of &lt;class 'D'&gt;&gt;
+</pre>
+<p>returns bound methods. This means that when I call those methods,
+I get</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C,d).__repr__()
+&lt;instance of D&gt;
+</pre>
+<p>(here d, a D instance is being passed to <tt class="literal"><span class="pre">__repr__</span></tt>) and</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C,D).__repr__()
+&lt;instance of type&gt;
+</pre>
+<p>(here D, an instance of the (meta)class <tt class="literal"><span class="pre">type</span></tt> is being passed
+to <tt class="literal"><span class="pre">__repr__</span></tt>).</p>
+<p>Reading the docs,one could think that in order to get unbound methods,
+she needs to switch to the alternative syntax of <tt class="literal"><span class="pre">super</span></tt>, the single
+argument syntax. This is actually completely WRONG.</p>
+</div>
+<div class="section" id="third-truth-the-unbound-syntax-does-not-access-unbound-methods">
+<h1><a name="third-truth-the-unbound-syntax-does-not-access-unbound-methods">Third truth: the 'unbound' syntax does not access unbound methods</a></h1>
+<p>Suppose 'B' has a method called 'meth' like this:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; B.meth = lambda self :'You called B.meth with first argument %s' % self
+</pre>
+<p>Then <tt class="literal"><span class="pre">B.meth</span></tt> is an unbound method and, mislead by the documentation,
+one could expect to be able to access it with the syntax <tt class="literal"><span class="pre">super(C).meth</span></tt>.
+This is not the case. You get an error instead:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C).meth
+Traceback (most recent call last):
+ ...
+AttributeError: 'super' object has no attribute 'meth'
+</pre>
+<p>The truth is that unbound super objects cannot be accessed directly,
+they must be converted to bound objects in order to make them
+to dispatch properly. Unbound super objects can be converted to
+bound super objects via the descriptor protocol. For instance,
+in this example I can convert <tt class="literal"><span class="pre">super(C)</span></tt> in a super object
+bound to the instance 'd' in this way:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; boundsuper = super(C).__get__(d, D) # this is the same as super(C,d)
+</pre>
+<p>Now I can access the bound method 'd.meth':</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print boundsuper.meth
+&lt;bound method D.&lt;lambda&gt; of &lt;instance of D&gt;&gt;
+</pre>
+<p>To really access the unbound method 'D.meth' is tricky; it could be
+done in this way:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print boundsuper.meth.__get__(None,D)
+&lt;unbound method D.&lt;lambda&gt;&gt;
+</pre>
+<p>All those tricks requires a good grasp of descriptors (see next
+paragraph). Here I would like just to point out that in my opinion
+having an unbound syntax for descriptors is generating more
+confusion than needed. First of all, there is a problem of poor
+documentation.</p>
+<p>The distinction bound/unbound
+can easily be confused with the distinction between bound and unbound
+methods. So you could expect <tt class="literal"><span class="pre">super(C).meth</span></tt> to be an unbound method
+of the superclass of C (superclass here is intended in some nebolous way).
+This is not that dangerous when you get an error; it is much more
+dangerous when you don't get an error, but you get a result which is
+not what you expect. This happens for special methods which are
+already defined in <tt class="literal"><span class="pre">super</span></tt>.
+For instance <tt class="literal"><span class="pre">super(C).__repr__</span></tt> does not give any error,
+but it is <em>not</em> returning a proxy to <tt class="literal"><span class="pre">D.__repr__</span></tt>, it is
+returning <tt class="literal"><span class="pre">super.__repr__</span></tt> bound to the (unbound) super object <tt class="literal"><span class="pre">super(C)</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C).__repr__() # same as repr(super(C))
+&lt;super: &lt;class 'C'&gt;, NULL&gt;
+</pre>
+<p>Very tricky. Notice that <tt class="literal"><span class="pre">super</span></tt> also redefines <tt class="literal"><span class="pre">__new__</span></tt>,
+<tt class="literal"><span class="pre">__init</span></tt>, <tt class="literal"><span class="pre">__get__</span></tt>, <tt class="literal"><span class="pre">__getattribute</span></tt>, as well as inheriting
+other special attributes from <tt class="literal"><span class="pre">object</span></tt>. So you will dispatch to
+these methods in <tt class="literal"><span class="pre">super</span></tt> and not to the right methods defined in
+the hierarchy at hand. On the other hand, the two-argument syntax
+does not have this problem. For instance</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(C,C).__repr__()
+&lt;instance of type&gt;
+</pre>
+<p>does the right thing. There is a single use case for the single argument
+syntax of <tt class="literal"><span class="pre">super</span></tt> that I am aware of, but I think it gives more troubles
+than advantages. The use case is the implementation of &quot;autosuper&quot; made
+by Guido on his essay about new-style classes.</p>
+<p>The idea there is to use the unbound super objects as private
+attributes. For instance, in our example, we could define the
+private attribute <tt class="literal"><span class="pre">__sup</span></tt> in the class <tt class="literal"><span class="pre">C</span></tt> as the unbound
+super object <tt class="literal"><span class="pre">super(C)</span></tt>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; C._C__sup = super(C)
+</pre>
+<p>With this definition inside the methods the syntax
+<tt class="literal"><span class="pre">self.__sup.meth(arg)</span></tt> can be used
+as an alternative to <tt class="literal"><span class="pre">super(C,</span> <span class="pre">self).meth(arg)</span></tt>, and the advantage is
+that you avoid to repeat the name of the class in the calling
+syntax, since that name is hidden in the mangling mechanism of
+private names. The creation of the <tt class="literal"><span class="pre">__sup</span></tt> attributes can be hidden
+in a metaclass and made automatic. So, all this seems to work: but
+actually this <em>not</em> the case.
+Things may wrong in various case, for instance for classmethods,
+as in this example:</p>
+<pre class="literal-block">
+#&lt;ex.py&gt;
+
+class B(object):
+ def __repr__(self):
+ return '&lt;instance of %s&gt;' % self.__class__.__name__
+ def meth(self):
+ print &quot;B.meth(%s)&quot; % self
+ meth = classmethod(meth)
+
+class C(B):
+ def meth(self):
+ print &quot;C.meth(%s)&quot; % self
+ self.__super.meth()
+ meth = classmethod(meth)
+
+C._C__super = super(C)
+
+class D(C):
+ pass
+
+D._D__super = super(D)
+
+
+d=D()
+
+d.meth()
+
+#&lt;/ex.py&gt;
+</pre>
+<p>The last line raises an <tt class="literal"><span class="pre">AttributeError:</span> <span class="pre">'super'</span> <span class="pre">object</span> <span class="pre">has</span> <span class="pre">no</span> <span class="pre">attribute</span>
+<span class="pre">'meth'.</span></tt></p>
+<p>So, using a <tt class="literal"><span class="pre">__super</span></tt> unbound super object is not a robust solution
+(notice that everything would work by substituting <tt class="literal"><span class="pre">self.__super.meth()</span></tt>
+with <tt class="literal"><span class="pre">super(C,self).meth()</span></tt>. There are other ways to avoid repeating
+the class name, see for instance my cookbook recipe.</p>
+<p>If it was me, I would just remove the single argument syntax of <tt class="literal"><span class="pre">super</span></tt>,
+making it illegal. But this would probably break someone code, so
+I don't think it will ever happen. Another solution would be just to
+deprecate it. There is no need for this syntax, one can always circumvent it.</p>
+</div>
+<div class="section" id="forth-truth-super-returns-descriptor-objects">
+<h1><a name="forth-truth-super-returns-descriptor-objects">Forth truth: <tt class="literal"><span class="pre">super</span></tt> returns descriptor objects</a></h1>
+<p>Descriptors (more properly I should speak of the descriptor protocol) were
+introduced in Python 2.2 by Guido van Rossum. Their primary motivation
+is technical, since they were needed to implement the new-style object
+system. Descriptors were also used to introduce new standard concepts in
+Python, such as classmethods, staticmethods and properties. Moreover,
+according to the traditional transparency policy of Python, descriptors
+were exposed to the application programmer, giving him/her the freedom
+to write custom descriptors. <a class="footnote-reference" href="#id6" id="id2" name="id2">[2]</a> Any serious Python programmer should have
+a look at descriptors: luckily they are now very well documented (which was
+not the case when I first studied them :-/) thanks to the beautiful essay
+of Raimond Hettinger <a class="footnote-reference" href="#id7" id="id3" name="id3">[3]</a>. You should read it before continuing this article,
+since it explains all the details. However, for the sake of our discussion
+of <tt class="literal"><span class="pre">super</span></tt>, it is enough to say that a <em>descriptor class</em> is just a
+regular new-style class which implements a .``__get__`` method with
+signature <tt class="literal"><span class="pre">__get__(self,</span> <span class="pre">obj,</span> <span class="pre">objtyp</span> <span class="pre">=</span> <span class="pre">None)</span></tt>. A <em>descriptor object</em>
+is just an instance of a descriptor class.</p>
+<p>Descriptor objects are intended to be used as attributes (hence their
+complete name attribute descriptors). Suppose that 'descr' is a
+given descriptor object used as attribute of a given class C.
+Then the syntax <tt class="literal"><span class="pre">C.descr</span></tt> is actually implemented by Python as a
+call to <tt class="literal"><span class="pre">descr.__get__(None,</span> <span class="pre">C)</span></tt>, whereas the same syntax for an
+instance of C corresponds to a call to <tt class="literal"><span class="pre">descr.__get__(c,</span> <span class="pre">type(c))</span></tt>.</p>
+<p>This explanation is relevant in order to understand how the <tt class="literal"><span class="pre">super</span></tt>
+built-in works when it is called with only one argument, i.e. in the
+unbound form. In this case <tt class="literal"><span class="pre">super(C)</span></tt> returns a descriptor which
+is intended to be used as an attribute in other classes. This is
+an example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(C):
+... sup = super(C)
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; d = D()
+&gt;&gt;&gt; d.sup.a
+2
+</pre>
+<p>This works since <tt class="literal"><span class="pre">d.sup.a</span></tt> calls</p>
+<table class="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>Descriptors gives an enormous power to the application
+programmer, especially when combined with metaclasses. For instance,
+using descriptors I did implement a prototype-based object system for
+Python in few lines: see of course this was an hack, only useful as proof
+of concept.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="fifth-truth-super-does-not-work-with-meta-attributes">
+<h1><a name="fifth-truth-super-does-not-work-with-meta-attributes">Fifth truth: <tt class="literal"><span class="pre">super</span></tt> does not work with meta-attributes</a></h1>
+<p>If you start using <tt class="literal"><span class="pre">super</span></tt> intensively, soon or latter you will find
+a number of subtilities. One of these is the fact that <tt class="literal"><span class="pre">super</span></tt> does not
+work well with the <tt class="literal"><span class="pre">__name__</span></tt> special attribute. Consider this example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object):
+... &quot;This is class B&quot;
+...
+&gt;&gt;&gt; class C(B):
+... pass
+...
+</pre>
+<p>Here the special class attribute <tt class="literal"><span class="pre">__doc__</span></tt> is retrieved as you would expect:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C).__doc__ == super(C,C()).__doc__ == B.__doc__
+True
+</pre>
+<p>On the other hand, the special attribute <tt class="literal"><span class="pre">__name__</span></tt> is not
+retrieved correctly:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; super(C,C).__name__ # one would expect it to be 'B'
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+</pre>
+<p>The problem is that <tt class="literal"><span class="pre">__name__</span></tt> is not just a plain class
+attribute: it is actually a &quot;getset descriptor&quot; defined on
+the metaclass &quot;type&quot; (try to run <tt class="literal"><span class="pre">help(type.__dict__['__name__'])</span></tt>
+and you will see for yourself). More in general, <tt class="literal"><span class="pre">super</span></tt> has
+problems with meta-attributes, i.e. class attributes of metaclasses.
+Meta-attributes differs from regular attributes since they are not
+transmitted to the instances of the instances.You can find the
+rationale for this behaviour elsewhere <a class="footnote-reference" href="#id8" id="id5" name="id5">[4]</a>: here I am only interested
+to the issues with <tt class="literal"><span class="pre">super</span></tt>. Consider this example:</p>
+<p>#&lt;example1.py&gt;</p>
+<dl>
+<dt>class M(type):</dt>
+<dd>&quot;A metaclass with a class attribute 'a'.&quot;
+a = 1</dd>
+<dt>class B:</dt>
+<dd>&quot;An instance of M with a meta-attribute 'a'.&quot;
+__metaclass__ = M</dd>
+<dt>class C(B):</dt>
+<dd>&quot;An instance of M with the same meta-attribute 'a'&quot;</dd>
+<dt>if __name__ == &quot;__main__&quot;:</dt>
+<dd>print B.a, C.a # =&gt; 1 1
+print super(C,C).a #=&gt; attribute error</dd>
+</dl>
+<p>#&lt;/example1.py&gt;</p>
+<p>If you run this, you will get an attribute error. This is a case
+where <tt class="literal"><span class="pre">super</span></tt> is doing the <em>right</em> thing, since 'a' is <em>not</em> inherited
+from B, but it comes directly from the metaclass (again, look at my second
+article with David Mertz to understand why it is so), so 'a'
+is <em>not</em> in the MRO of C. A similar thing happens for the &quot;__name__&quot;
+attribute (the fact that it is a descriptor and not a plain
+attribute does not matter), so <tt class="literal"><span class="pre">super</span></tt> is working correctly, but
+still it may seems surprising at first.</p>
+</div>
+<div class="section" id="the-final-truth-there-are-other-bugs-and-pitfalls">
+<h1><a name="the-final-truth-there-are-other-bugs-and-pitfalls">The final truth: there are other bugs and pitfalls</a></h1>
+<p>There are various <tt class="literal"><span class="pre">super</span></tt> pitfalls currently undocumented and
+that experienced Python programmers should be aware of.</p>
+<p>The unbound form of <tt class="literal"><span class="pre">super</span></tt> does not play well with pydoc.
+The problems is still there in Python 2.3.4 (see bug report SF729103)</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class B(object): pass
+...
+&gt;&gt;&gt; class C:
+... s=super(B)
+...
+&gt;&gt;&gt; help(C)
+Traceback (most recent call last):
+ ...
+ ... lots of stuff here
+ ...
+File &quot;/usr/lib/python2.3/pydoc.py&quot;, line 1198, in docother
+ chop = maxlen - len(line)
+TypeError: unsupported operand type(s) for -: 'classobj' and 'int'
+</pre>
+<p>I have not yet clear what the cause is, but it is certainly quite
+tricky.</p>
+<p>There is also another subtle pitfall which is not directly related
+to <tt class="literal"><span class="pre">super</span></tt> but which is often encountered working with <tt class="literal"><span class="pre">super</span></tt>.
+This came up at least three or four times in the newsgroup, and there
+are various independent bug reports on sourceforge about it, so possibly
+you may face it too. Bjorn Pettersen was the first one who pointed out the
+problem to me (see also bug report SF 729913): the issue is that</p>
+<p><tt class="literal"><span class="pre">super(MyCls,</span> <span class="pre">self).__getitem__(5)</span></tt></p>
+<p>works, but not</p>
+<p><tt class="literal"><span class="pre">super(MyCls,</span> <span class="pre">self)[5]</span></tt>.</p>
+<p>The problem is general to all special methods, not only to <tt class="literal"><span class="pre">__getitem__</span></tt>,
+and the explanation for that has to do with the implementation of
+attribute lookup for special methods. Clear explanations of what is
+going on are provided by Michael Hudson as a comment to the bug report:
+SF789262 and by Raymond Hetting as a comment to the bug report SF805304.
+Shortly put, this is not a problem of <tt class="literal"><span class="pre">super</span></tt> per se, the problem is
+that (using <tt class="literal"><span class="pre">__getitem__</span></tt> as example)</p>
+<blockquote>
+<tt class="literal"><span class="pre">x[5]</span> <span class="pre">==</span> <span class="pre">type(x).__getitem__(x,5)</span></tt></blockquote>
+<p><em>only if</em> <tt class="literal"><span class="pre">__getitem__</span></tt> is explicitely defined in <tt class="literal"><span class="pre">type(x)</span></tt>. If
+<tt class="literal"><span class="pre">type(x)</span></tt> does not define <tt class="literal"><span class="pre">__getitem__</span></tt> directly, but only
+indirectly via delegation (i.e. overriding <tt class="literal"><span class="pre">__getattribute__</span></tt>),
+then the second form works but not the first one, i.e. the <tt class="literal"><span class="pre">[]</span></tt> syntax
+is not automatically converted to the second form, the working one.</p>
+<p>This restriction will likely stay in Python because it would involve
+a really big change and a loss of performances, so it has to be
+considered just a documentation bug, since nowhere in
+the docs it is mentioned that special calling syntaxes (such as
+the <tt class="literal"><span class="pre">[]</span></tt> call, the <tt class="literal"><span class="pre">iter</span></tt> call, the <tt class="literal"><span class="pre">repr</span></tt> call, etc. etc.)
+are special and bypass <tt class="literal"><span class="pre">__getattribute__</span></tt>. Guido advice is:
+just use the more explicit form and everything will work.</p>
+<p>Finally, there may be other bugs and pitfalls I am not aware of. Certainly
+there are many other issues and bugs in previous versions of Python that
+I have not mentioned here, since they have been fixed, but that you may
+encounter if you use Python 2.2 (and maybe even in 2.3).</p>
+</div>
+<div class="section" id="the-final-truth-when-it-comes-to-super-don-t-trust-even-guido-himself">
+<h1><a name="the-final-truth-when-it-comes-to-super-don-t-trust-even-guido-himself">The final truth: when it comes to <tt class="literal"><span class="pre">super</span></tt> don't trust even Guido himself!</a></h1>
+<p>In order to explain how <tt class="literal"><span class="pre">super</span></tt> works, Guido describes a
+&quot;fully functional implementation of the super() built-in class in
+pure Python&quot; in &quot;Unifying types and classes in Python 2.2&quot;.
+Unfortunately, that implemenentation is more harmful than helpful, since
+the current <tt class="literal"><span class="pre">super</span></tt> DOES NOT work in the same way :-(
+Take for instance this example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class C(object):
+... f='C.f'
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; class D(C):
+... f='D.f'
+</pre>
+<p>Here the <tt class="literal"><span class="pre">super</span></tt> works fine,
+df</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print super(D,D()).f
+C.f
+</pre>
+<p>but the class <tt class="literal"><span class="pre">Super</span></tt> described by Guido will raise an attribute
+error when invoked as <tt class="literal"><span class="pre">Super(D,D()).f</span></tt>. Therefore Super is NOT
+equivalent to the currently implemented <tt class="literal"><span class="pre">super</span></tt> built-in</p>
+</div>
+<div class="section" id="notes">
+<h1><a name="notes">Notes</a></h1>
+<table class="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="#id2" name="id6">[2]</a></td><td>I wrote an essay on Python 2.3 Method Resolution Order which
+you may find here:</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id7" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3" name="id7">[3]</a></td><td>Raymond Hetting wrote a beautiful essay on descriptors here:</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id8" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id5" name="id8">[4]</a></td><td>David Mertz and me wrote a couple of articles on metaclasses;
+the second one is the relevant one for the issues discussed here:</td></tr>
+</tbody>
+</table>
+<table class="footnote" frame="void" id="id9" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a name="id9">[5]</a></td><td><a class="reference" href="http://groups.google.it/groups?hl=it&amp;lr=&amp;ie=UTF-8&amp;threadm=2259b0e2.0304300625.4e0ebace%40posting.google.com&amp;rnum=3&amp;prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26q%3Dsimionato%2Bpettersen%2Bsuper%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python">http://groups.google.it/groups?hl=it&amp;lr=&amp;ie=UTF-8&amp;threadm=2259b0e2.0304300625.4e0ebace%40posting.google.com&amp;rnum=3&amp;prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26q%3Dsimionato%2Bpettersen%2Bsuper%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python</a>.*</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+<hr class="footer" />
+<div class="footer">
+<a class="reference" href="super.txt">View document source</a>.
+Generated on: 2004-06-13 20:23 UTC.
+Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
+</div>
+</body>
+</html>
diff --git a/pypers/super/super.txt b/pypers/super/super.txt
new file mode 100755
index 0000000..11b39a6
--- /dev/null
+++ b/pypers/super/super.txt
@@ -0,0 +1,564 @@
+The truth about ``super``
+==========================
+
+:Author: Michele Simionato
+:Email: michele.simionato@gmail.com
+:Date: June 2004
+:Status: Draft
+
+``super`` is a new built-in, first introduced in Python 2.2 and slightly
+improved and fixed in Python 2.3, which is little known to the average
+Python programmer. One of the reason for this
+fact is the poor documentation of ``super``: at the time of this writing
+(June 2004) the documentation is incomplete and in some parts
+misleading and even wrong. For instance, it was recently pointed out
+on comp.lang.python that the standard library (Python 2.3.4, section 2.1)
+still says::
+
+ super(type[, object-or-type])
+ Return the superclass of type. If the second argument is omitted the
+ super object returned is unbound. If the second argument is an object,
+ isinstance(obj, type) must be true. If the second argument is a type,
+ issubclass(type2, type) must be true. super() only works for new-style
+ classes.
+
+The first sentence is just plain wrong. ``super`` does not return the
+superclass. There is no such a thing as "the" superclass in a Multiple
+Inheritance (MI) world. Also, the sentence about 'unbound' is misleading,
+since it may easily lead the programmer to think about bound and unbound
+methods, whereas it has nothing to do with that concept. Finally, there are
+subtle pitfalls and dark corners of ``super`` which are not at all mentioned.
+IMNSHO ``super`` is one of the most trickiest and surprising Python
+constructs, and we absolutely needs a document to share light its secrets.
+The present draft is a first step in this direction: it aims to tell you
+the "truth" about ``super``. At least the amount of truth
+I have discovered with my experimentations, which is certainly
+not the whole truth ;)
+
+A fair warning is in order here: this document is aimed to expert
+Pythonistas. It assumes you already know the Method Resolution Order (MRO)
+concept; moreover a good understanding of descriptors would be extremely
+useful in order to grasp this document. Some parts also require good
+familiarity with metaclasses. All in all, this paper is not for the faint
+of heart ;)
+
+First truth: there is no superclass in a MI world
+----------------------------------------------------------
+
+Readers familiar will single inheritance languages, such as
+Java or Smalltalk, will have a clear concept of superclass
+in mind. This concept, however, has *no useful meaning* in Python or in
+other multiple inheritance languages. I became convinced of this fact
+after a discussion with Bjorn Pettersen on comp.lang.python in May 2003
+(at that time I was mistakenly thinking that one could define a
+superclass concept in Python). Consider this example from that
+discussion:
+
+ ::
+
+ +-----+
+ | T |
+ |a = 0|
+ +-----+
+ / \
+ / \
+ +-------+ +-------+
+ | A | | B |
+ | | | a = 2 |
+ +-------+ +-------+
+ \ /
+ \ /
+ +-----+
+ | C |
+ +-----+
+ :
+ : instantiation
+ c
+
+>>> class T(object):
+... a = 0
+
+>>> class A(T):
+... pass
+
+>>> class B(T):
+... a = 2
+
+>>> class C(A,B):
+... pass
+
+>>> c = C()
+
+Who is the superclass of ``C``? There are two direct superclasses (i.e. bases)
+of ``C``: ``A ``and ``B``. ``A`` comes before ``B``, so one would naturally
+think that the superclass of ``C`` is ``A``. ``A`` inherits the attribute
+``a`` from ``T``, so if ``super(C,c)`` was returning the superclass of ``C``,
+then it should return ``A``. ``A`` inherits its attribute ``a`` from ``T``,
+where ``a`` has the value 0, so ``super(C,c).a`` would return 0. This
+is NOT what happens. Instead, ``super(C,c).a`` walks trought the
+method resolution order [#]_ of the class of ``c`` (which is ``C``)
+and retrieves the attribute from the first class above ``C`` which
+defines it. In this example the MRO of ``C`` is ``[C, A, B, T, object]``, so
+``B`` is the first class above ``C`` which defines ``a`` and ``super(C,c).a``
+correctly returns the value 2, not 0:
+
+>>> super(C,c).a
+2
+
+You may call ``A`` the superclass of ``C``, but this is not an useful
+concept since the methods are resolved by looking at the classes
+in the MRO of ``C``, and not by looking at the classes in the MRO of ``A``
+(which in this case is ``[A,T, object]`` and does not contain ``B``).
+The whole MRO is needed, not just the first superclass.
+
+So, using the word *superclass* in the standard doc is completely
+misleading and should be avoided altogether.
+
+Second truth: ``super`` returns proxy objects
+----------------------------------------------------
+
+Having established that ``super`` cannot return and does not return the
+mythical superclass, we may ask ourselves what the hell is returning
+``super`` ;) The truth is that ``super`` returns proxy objects.
+
+Informally speaking, a proxy object is an object with
+the ability to dispatch to methods of other classes via delegation.
+Technically, ``super`` works by overriding the ``__getattribute__``
+method in such a way that its instances becomes proxy objects providing
+access to the methods in the MRO. The dispatch is done in such a way
+that
+
+``super(cls, instance-or-subclass).meth(*args, **kw)``
+
+corresponds to
+
+``right-method-in-the-MRO-applied-to(instance-or-subclass, *args, **kw)``
+
+There is a caveat at this point: the second argument can be
+an instance of the first argument, or a subclass of it, but
+in *both cases* a bound method is returned (one could naivily
+think, instead, that when a subclass is passed one gets an
+unbound method).
+
+For instance, in this example
+
+>>> class B(object):
+... def __repr__(self):
+... return "<instance of %s>" % self.__class__.__name__
+
+>>> class C(B):
+... pass
+
+>>> class D(C):
+... pass
+
+>>> d = D()
+
+both
+
+>>> print super(C,d).__repr__
+<bound method D.__repr__ of <instance of D>>
+
+and
+
+>>> print super(C,D).__repr__
+<bound method D.__repr__ of <class 'D'>>
+
+returns bound methods. This means that when I call those methods,
+I get
+
+>>> print super(C,d).__repr__()
+<instance of D>
+
+(here ``d``, a ``D`` instance, is being passed to ``__repr__``) and
+
+>>> print super(C, D).__repr__()
+<instance of type>
+
+(here ``D``, an instance of the (meta)class ``type``, is being passed
+to ``__repr__``).
+
+Reading the docs,one could think that in order to get unbound methods,
+she needs to switch to the alternative syntax of ``super``, the single
+argument syntax. This is actually untrue. To understand how unbound
+methods can be retrieved we need to talk about descriptors.
+
+Third truth: ``super`` returns descriptor objects
+----------------------------------------------------
+
+Descriptors (more properly I should speak of the descriptor protocol) were
+introduced in Python 2.2 by Guido van Rossum. Their primary motivation
+is technical, since they were needed to implement the new-style object
+system. Descriptors were also used to introduce new standard concepts in
+Python, such as classmethods, staticmethods and properties. Moreover,
+according to the traditional transparency policy of Python, descriptors
+were exposed to the application programmer, giving him/her the freedom
+to write custom descriptors. [#]_ Any serious Python programmer should have
+a look at descriptors: luckily they are now very well documented (which was
+not the case when I first studied them :-/) thanks to the beautiful essay
+of Raimond Hettinger [#]_. You should read it before continuing this article,
+since it explains all the details. However, for the sake of our discussion
+of ``super``, it is enough to say that a *descriptor class* is just a
+regular new-style class which implements a .``__get__`` method with
+signature ``__get__(self, obj, objtyp = None)``. A *descriptor object*
+is just an instance of a descriptor class.
+
+Descriptor objects are intended to be used as attributes (hence their
+complete name attribute descriptors). Suppose that 'descr' is a
+given descriptor object used as attribute of a given class C.
+Then the syntax ``C.descr`` is actually interpreted by Python as a
+call to ``descr.__get__(None, C)``, whereas the same syntax for an
+instance of C corresponds to a call to ``descr.__get__(c, type(c))``.
+
+The unbound method ``__repr__`` can be retrieved as
+
+>>> super(C,d).__repr__.__get__(None,D) # or with D instead of d
+<unbound method D.__repr__>
+
+and we may check that it works correctly:
+
+>>> print _(d)
+<instance of D>
+
+This is cumbersome and tricky, but it is the only way to get
+the unbound method. Using the unbound form of ``super`` does
+*not* return ``D.__repr__``: instead it returns ``super.__repr__``
+bound to the (unbound) super object ``super(C)``:
+
+>>> print super(C).__repr__() # same as repr(super(C))
+<super: <class 'C'>, NULL>
+
+Very tricky. Notice that ``super`` also redefines ``__new__``,
+``__init``, ``__get__``, ``__getattribute``, as well as inheriting
+other special attributes from ``object``. So using the single-argument
+syntax you will dispatch to
+these methods in ``super`` and not to the right methods defined in
+the hierarchy at hand. On the other hand, the two-argument syntax
+does not have this problem. For instance
+
+>>> print super(C,C).__repr__()
+<instance of type>
+
+does the right thing.
+
+This other example should shed further light. Suppose ``B``
+has a method called ``meth`` like this:
+
+>>> B.meth = lambda self :'You called B.meth with first argument %s' % self
+
+Then ``B.meth`` is an unbound method and, mislead by the documentation,
+one could expect to be able to access it with the syntax ``super(C).meth``.
+This is not the case. You get an error instead:
+
+>>> super(C).meth
+Traceback (most recent call last):
+ ...
+AttributeError: 'super' object has no attribute 'meth'
+
+Unbound super objects cannot be accessed directly,
+they must be converted to bound objects in order to make them
+to dispatch properly. Unbound super objects can be converted to
+bound super objects via the descriptor protocol. For instance,
+in this example I can convert ``super(C)`` in a super object
+bound to ``d`` in this way:
+
+>>> boundsuper = super(C).__get__(d, D) # this is the same as super(C,d)
+
+Now I can access the bound method 'd.meth':
+
+>>> print boundsuper.meth
+<bound method D.<lambda> of <instance of D>>
+
+
+
+As a consequence, ``__get__`` cannot be turned into a cooperative method
+just by using ``super``: you would get the wrong ``get``.
+
+.. [#] Descriptors gives an enormous power to the application
+ programmer, especially when combined with metaclasses. For instance,
+ using descriptors I did implement a prototype-based object system for
+ Python in few lines: see of course this was an hack, only useful as proof
+ of concept.
+
+Fourth truth: the 'unbound' syntax is a mess
+------------------------------------------------------------------
+
+Having established that the 'unbound' syntax does not returns unbound methods
+one might ask what its purpose is.
+The answer is that ``super(C)`` is intended to be used as an attribute in
+other classes. Then the descriptor magic will automatically convert the
+unbound syntax in the bound syntax. For instance:
+
+>>> class B(object):
+... a = 1
+>>> class C(B):
+... pass
+>>> class D(C):
+... sup = super(C)
+>>> d = D()
+>>> d.sup.a
+1
+
+This works since ``d.sup.a`` calls ``super(C).__get__(d,D).a`` which is
+converted to ``super(C, d).a`` and retrieves ``B.a``.
+
+There is a single use case for the single argument
+syntax of ``super`` that I am aware of, but I think it gives more troubles
+than advantages. The use case is the implementation of "autosuper" made
+by Guido on his essay about new-style classes.
+
+The idea there is to use the unbound super objects as private
+attributes. For instance, in our example, we could define the
+private attribute ``__sup`` in the class ``C`` as the unbound
+super object ``super(C)``:
+
+>>> C._C__sup = super(C)
+
+With this definition inside the methods the syntax
+``self.__sup.meth(arg)`` can be used
+as an alternative to ``super(C, self).meth(arg)``, and the advantage is
+that you avoid to repeat the name of the class in the calling
+syntax, since that name is hidden in the mangling mechanism of
+private names. The creation of the ``__sup`` attributes can be hidden
+in a metaclass and made automatic. So, all this seems to work: but
+actually this *not* the case.
+
+Things may wrong in various case, for instance for classmethods,
+as in this example::
+
+ #<ex.py>
+
+ class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ def meth(self):
+ print "B.meth(%s)" % self
+ meth = classmethod(meth)
+
+ class C(B):
+ def meth(self):
+ print "C.meth(%s)" % self
+ self.__super.meth()
+ meth = classmethod(meth)
+
+ C._C__super = super(C)
+
+ class D(C):
+ pass
+
+ D._D__super = super(D)
+
+
+ d=D()
+
+ d.meth()
+
+ #</ex.py>
+
+The last line raises an ``AttributeError: 'super' object has no attribute
+'meth'.``
+
+So, using a ``__super`` unbound super object is not a robust solution
+(notice that everything would work by substituting ``self.__super.meth()``
+with ``super(C,self).meth()``. There are other ways to avoid repeating
+the class name, see for instance my cookbook recipe [#]_.
+
+If it was me, I would just remove the single argument syntax of ``super``,
+making it illegal. But this would probably break someone code, so
+I don't think it will ever happen. Another solution would be just to
+deprecate it. There is no need for this syntax, one can always circumvent
+it.
+
+Fifth truth: ``super`` does not work with meta-attributes
+-----------------------------------------------------------------------
+
+If you start using ``super`` intensively, soon or latter you will find
+a number of subtilities. One of these is the fact that ``super`` does not
+work well with the ``__name__`` special attribute. Consider this example:
+
+>>> class B(object):
+... "This is class B"
+...
+>>> class C(B):
+... pass
+...
+
+Here the special (class) attribute ``__doc__`` is retrieved as you would expect:
+
+>>> super(C,C).__doc__ == super(C,C()).__doc__ == B.__doc__
+True
+
+On the other hand, the special attribute ``__name__`` is not
+retrieved correctly:
+
+>>> super(C,C).__name__ # one would expect it to be 'B'
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+
+The problem is that ``__name__`` is not just a plain class
+attribute: it is actually a "getset descriptor" defined on
+the metaclass "type" (try to run ``help(type.__dict__['__name__'])``
+and you will see for yourself). More in general, ``super`` has
+problems with meta-attributes, i.e. class attributes of metaclasses.
+
+Meta-attributes differs from regular attributes since they are not
+transmitted to the instances of the instances.You can find the
+rationale for this behaviour elsewhere [#]_: here I am only interested
+to the issues with ``super``. Consider this example:
+
+#<example1.py>
+
+class M(type):
+ "A metaclass with a class attribute 'a'."
+ a = 1
+
+class B:
+ "An instance of M with a meta-attribute 'a'."
+ __metaclass__ = M
+
+class C(B):
+ "An instance of M with the same meta-attribute 'a'"
+
+if __name__ == "__main__":
+ print B.a, C.a # => 1 1
+ print super(C,C).a #=> attribute error
+
+#</example1.py>
+
+If you run this, you will get an attribute error. This is a case
+where ``super`` is doing the *right* thing, since 'a' is *not* inherited
+from B, but it comes directly from the metaclass (again, look at my second
+article with David Mertz to understand why it is so), so 'a'
+is *not* in the MRO of C. A similar thing happens for the "__name__"
+attribute (the fact that it is a descriptor and not a plain
+attribute does not matter), so ``super`` is working correctly, but
+still it may seems surprising at first.
+
+Sixth truth: ``super`` does not play well with pydoc and doctest
+-----------------------------------------------------------------
+
+There are various ``super`` pitfalls currently undocumented of which
+the experienced Python programmer should be aware of.
+
+The unbound form of ``super`` does not play well with pydoc.
+The problems is still there in Python 2.3.4 (see bug report SF729103)
+
+>>> class B(object): pass
+...
+>>> class C(B):
+... s=super(B)
+...
+>>> help(C)
+Traceback (most recent call last):
+ ...
+ ... lots of stuff here
+ ...
+File "/usr/lib/python2.3/pydoc.py", line 1198, in docother
+ chop = maxlen - len(line)
+TypeError: unsupported operand type(s) for -: 'type' and 'int'
+
+I have not yet clear what the cause is, but it is certainly quite
+tricky. An incompatibility between ``super`` was reported by Christian Tanzer
+(SF902628); if you run the following, you will get a TypeError:
+
+ ::
+
+ #<ex.py>
+
+ class C(object):
+ pass
+
+ C.s = super(C)
+
+ if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
+
+ #<ex.py>
+
+BTW, I don't think this is related to ``super`` only since I have
+found similar problems when playing with descriptors and doctest
+some time ago (but I cannot reproduce the bug right now).
+
+Seventh truth: special attribute access for special attributes is special ;)
+----------------------------------------------------------------------------
+
+There is also another subtle pitfall which is not directly related
+to ``super`` but which is often encountered working with ``super``.
+This came up at least three or four times in the newsgroup, and there
+are various independent bug reports on sourceforge about it, so possibly
+you may face it too. Bjorn Pettersen was the first one who pointed out the
+problem to me (see also bug report SF 729913): the issue is that
+
+``super(MyCls, self).__getitem__(5)``
+
+works, but not
+
+``super(MyCls, self)[5]``.
+
+The problem is general to all special methods, not only to ``__getitem__``,
+and the explanation for that has to do with the implementation of
+attribute lookup for special methods. Clear explanations of what is
+going on are provided by Michael Hudson as a comment to the bug report:
+SF789262 and by Raymond Hettinger as a comment to the bug report SF805304.
+Shortly put, this is not a problem of ``super`` per se, the problem is
+that the special call ``x[5]`` (using ``__getitem__`` as example) is
+converted to ``type(x).__getitem__(x,5)``
+*only if* ``__getitem__`` is explicitely defined in ``type(x)``. If
+``type(x)`` does not define ``__getitem__`` directly, but only
+indirectly via delegation (i.e. overriding ``__getattribute__``),
+then the second form works but not the first one.
+
+This restriction will likely stay in Python because it would involve
+a really big change and a loss of performances, so it has to be
+considered just a documentation bug, since nowhere in
+the docs it is mentioned that special calling syntaxes (such as
+the ``[]`` call, the ``iter`` call, the ``repr`` call, etc. etc.)
+are special and bypass ``__getattribute__``. Guido advice is:
+just use the more explicit form and everything will work.
+
+Finally, there may be other bugs and pitfalls I am not aware of. Certainly
+there are many other issues and bugs in previous versions of Python that
+I have not mentioned here, since they have been fixed, but that you may
+encounter if you use Python 2.2 (and maybe even in 2.3).
+
+The last truth: when it comes to ``super`` don't trust even Guido himself!
+-----------------------------------------------------------------------------
+
+In order to explain how ``super`` works, Guido describes a
+"fully functional implementation of the super() built-in class in
+pure Python" in "Unifying types and classes in Python 2.2".
+Unfortunately, that implemenentation is more harmful than helpful, since
+the current ``super`` DOES NOT work in the same way :-(
+Take for instance this example:
+
+>>> class C(object):
+... f='C.f'
+
+>>> class D(C):
+... f='D.f'
+
+Here the ``super`` works fine,
+df
+
+>>> print super(D,D()).f
+C.f
+
+but the class ``Super`` described by Guido will raise an attribute
+error when invoked as ``Super(D,D()).f``. Therefore Super is NOT
+equivalent to the currently implemented ``super`` built-in.
+
+Notes
+-----------------
+
+
+.. [#] I wrote an essay on Python 2.3 Method Resolution Order which
+ you may find here:
+
+.. [#] Raymond Hetting wrote a beautiful essay on descriptors here:
+
+.. [#] David Mertz and me wrote a couple of articles on metaclasses;
+ the second one is the relevant one for the issues discussed here:
+
+.. [#] http://groups.google.it/groups?hl=it&lr=&ie=UTF-8&threadm=2259b0e2.0304300625.4e0ebace%40posting.google.com&rnum=3&prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26q%3Dsimionato%2Bpettersen%2Bsuper%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python.*
diff --git a/pypers/super/super23.txt b/pypers/super/super23.txt
new file mode 100755
index 0000000..f18332d
--- /dev/null
+++ b/pypers/super/super23.txt
@@ -0,0 +1,556 @@
+The truth about ``super``
+==========================
+
+:Author: Michele Simionato
+:Email: michele.simionato@gmail.com
+:Date: June 2004
+:Status: Draft
+
+``super`` is a new built-in, first introduced in Python 2.2 and slightly
+improved and fixed in Python 2.3, which is little known to the average
+Python programmer. One of the reason for this
+fact is the poor documentation of ``super``: at the time of this writing
+(June 2004) the documentation is incomplete and in some parts
+misleading and even wrong. For instance, it was recently pointed out
+on comp.lang.python that the standard library (Python 2.3.4, section 2.1)
+still says::
+
+ super(type[, object-or-type])
+ Return the superclass of type. If the second argument is omitted the
+ super object returned is unbound. If the second argument is an object,
+ isinstance(obj, type) must be true. If the second argument is a type,
+ issubclass(type2, type) must be true. super() only works for new-style
+ classes.
+
+The first sentence is just plain wrong. ``super`` does not return the
+superclass. There is no such a thing as "the" superclass in a Multiple
+Inheritance (MI) world. Also, the sentence about 'unbound' is misleading,
+since it may easily lead the programmer to think about bound and unbound
+methods, whereas it has nothing to do with that concept. Finally, there are
+subtle pitfalls and dark corners of ``super`` which are not at all mentioned.
+IMNSHO ``super`` is one of the most trickiest and surprising Python
+constructs, and we absolutely needs a document to share light its secrets.
+The present draft is a first step in this direction: it aims to tell you
+the "truth" about ``super``. At least the amount of truth
+I have discovered with my experimentations, which is certainly
+not the whole truth ;)
+
+A fair warning is in order here: this document is aimed to expert
+Pythonistas. It assumes you already know the Method Resolution Order (MRO)
+concept; moreover a good understanding of descriptors would be extremely
+useful in order to grasp this document. Some parts also require good
+familiarity with metaclasses. All in all, this paper is not for the faint
+of heart ;)
+
+First truth: there is no superclass in a MI world
+----------------------------------------------------------
+
+Readers familiar will single inheritance languages, such as
+Java or Smalltalk, will have a clear concept of superclass
+in mind. This concept, however, has *no useful meaning* in Python or in
+other multiple inheritance languages. I became convinced of this fact
+after a discussion with Bjorn Pettersen on comp.lang.python in May 2003
+(at that time I was mistakenly thinking that one could define a
+superclass concept in Python). Consider this example from that
+discussion:
+
+ ::
+
+ +-----+
+ | T |
+ |a = 0|
+ +-----+
+ / \
+ / \
+ +-------+ +-------+
+ | A | | B |
+ | | | a = 2 |
+ +-------+ +-------+
+ \ /
+ \ /
+ +-----+
+ | C |
+ +-----+
+ :
+ : instantiation
+ c
+
+>>> class T(object):
+... a = 0
+
+>>> class A(T):
+... pass
+
+>>> class B(T):
+... a = 2
+
+>>> class C(A,B):
+... pass
+
+>>> c = C()
+
+Who is the superclass of ``C``? There are two direct superclasses (i.e. bases)
+of ``C``: ``A ``and ``B``. ``A`` comes before ``B``, so one would naturally
+think that the superclass of ``C`` is ``A``. ``A`` inherits the attribute
+``a`` from ``T``, so if ``super(C,c)`` was returning the superclass of ``C``,
+then it should return ``A``. ``A`` inherits its attribute ``a`` from ``T``,
+where ``a`` has the value 0, so ``super(C,c).a`` would return 0. This
+is NOT what happens. Instead, ``super(C,c).a`` walks trought the
+method resolution order [#]_ of the class of ``c`` (which is ``C``)
+and retrieves the attribute from the first class above ``C`` which
+defines it. In this example the MRO of ``C`` is ``[C, A, B, T, object]``, so
+``B`` is the first class above ``C`` which defines ``a`` and ``super(C,c).a``
+correctly returns the value 2, not 0:
+
+>>> super(C,c).a
+2
+
+You may call ``A`` the superclass of ``C``, but this is not an useful
+concept since the methods are resolved by looking at the classes
+in the MRO of ``C``, and not by looking at the classes in the MRO of ``A``
+(which in this case is ``[A,T, object]`` and does not contain ``B``).
+The whole MRO is needed, not just the first superclass.
+
+So, using the word *superclass* in the standard doc is completely
+misleading and should be avoided altogether.
+
+Second truth: ``super`` returns proxy objects
+----------------------------------------------------
+
+Having established that ``super`` cannot return and does not return the
+mythical superclass, we may ask ourselves what the hell is returning
+``super`` ;) The truth is that ``super`` returns proxy objects.
+
+Informally speaking, a proxy object is an object with
+the ability to dispatch to methods of other classes via delegation.
+Technically, ``super`` works by overriding the ``__getattribute__``
+method in such a way that its instances becomes proxy objects providing
+access to the methods in the MRO. The dispatch is done in such a way
+that
+
+``super(cls, instance-or-subclass).meth(*args, **kw)``
+
+corresponds to
+
+``right-method-in-the-MRO-applied-to(instance-or-subclass, *args, **kw)``
+
+There is a caveat at this point: the second argument can be
+an instance of the first argument, or a subclass of it; in
+the first case a bound method is returned, in the second case
+an unbound method is returned (this was not true in earlier
+versions of Python).
+
+For instance, in this example
+
+>>> class B(object):
+... def __repr__(self):
+... return "<instance of %s>" % self.__class__.__name__
+
+>>> class C(B):
+... pass
+
+>>> class D(C):
+... pass
+
+>>> d = D()
+
+we have
+
+>>> print super(C,d).__repr__
+<bound method D.__repr__ of <instance of D>>
+
+and
+
+>>> print super(C,D).__repr__
+<unbound method D.__repr__>
+
+So, when we call those methods, we get
+
+>>> print super(C,d).__repr__()
+<instance of D>
+
+(here ``d``, a ``D`` instance, is being passed to ``__repr__``) and
+
+>>> print super(C, D).__repr__(D())
+<instance of D>
+
+
+Third truth: ``super`` returns descriptor objects
+----------------------------------------------------
+
+Descriptors (more properly I should speak of the descriptor protocol) were
+introduced in Python 2.2 by Guido van Rossum. Their primary motivation
+is technical, since they were needed to implement the new-style object
+system. Descriptors were also used to introduce new standard concepts in
+Python, such as classmethods, staticmethods and properties. Moreover,
+according to the traditional transparency policy of Python, descriptors
+were exposed to the application programmer, giving him/her the freedom
+to write custom descriptors. [#]_ Any serious Python programmer should have
+a look at descriptors: luckily they are now very well documented (which was
+not the case when I first studied them :-/) thanks to the beautiful essay
+of Raimond Hettinger [#]_. You should read it before continuing this article,
+since it explains all the details. However, for the sake of our discussion
+of ``super``, it is enough to say that a *descriptor class* is just a
+regular new-style class which implements a .``__get__`` method with
+signature ``__get__(self, obj, objtyp = None)``. A *descriptor object*
+is just an instance of a descriptor class.
+
+Descriptor objects are intended to be used as attributes (hence their
+complete name attribute descriptors). Suppose that 'descr' is a
+given descriptor object used as attribute of a given class C.
+Then the syntax ``C.descr`` is actually interpreted by Python as a
+call to ``descr.__get__(None, C)``, whereas the same syntax for an
+instance of C corresponds to a call to ``descr.__get__(c, type(c))``.
+
+The unbound method ``__repr__`` can be retrieved as
+
+>>> super(C,d).__repr__.__get__(None,D) # or with D instead of d
+<unbound method D.__repr__>
+
+and we may check that it works correctly:
+
+>>> print _(d)
+<instance of D>
+
+This is cumbersome and tricky, but it is the only way to get
+the unbound method. Using the unbound form of ``super`` does
+*not* return ``D.__repr__``: instead it returns ``super.__repr__``
+bound to the (unbound) super object ``super(C)``:
+
+>>> print super(C).__repr__() # same as repr(super(C))
+<super: <class 'C'>, NULL>
+
+Very tricky. Notice that ``super`` also redefines ``__new__``,
+``__init``, ``__get__``, ``__getattribute``, as well as inheriting
+other special attributes from ``object``. So using the single-argument
+syntax you will dispatch to
+these methods in ``super`` and not to the right methods defined in
+the hierarchy at hand. On the other hand, the two-argument syntax
+does not have this problem. For instance
+
+>>> print super(C,C).__repr__(C())
+<instance of C>
+
+does the right thing.
+
+This other example should shed further light. Suppose ``B``
+has a method called ``meth`` like this:
+
+>>> B.meth = lambda self :'You called B.meth with first argument %s' % self
+
+Then ``B.meth`` is an unbound method and, mislead by the documentation,
+one could expect to be able to access it with the syntax ``super(C).meth``.
+This is not the case. You get an error instead:
+
+>>> super(C).meth
+Traceback (most recent call last):
+ ...
+AttributeError: 'super' object has no attribute 'meth'
+
+Unbound super objects cannot be accessed directly,
+they must be converted to bound objects in order to make them
+to dispatch properly. Unbound super objects can be converted to
+bound super objects via the descriptor protocol. For instance,
+in this example I can convert ``super(C)`` in a super object
+bound to ``d`` in this way:
+
+>>> boundsuper = super(C).__get__(d, D) # this is the same as super(C,d)
+
+Now I can access the bound method 'd.meth':
+
+>>> print boundsuper.meth
+<bound method D.<lambda> of <instance of D>>
+
+
+
+As a consequence, ``__get__`` cannot be turned into a cooperative method
+just by using ``super``: you would get the wrong ``get``.
+
+.. [#] Descriptors gives an enormous power to the application
+ programmer, especially when combined with metaclasses. For instance,
+ using descriptors I did implement a prototype-based object system for
+ Python in few lines: see of course this was an hack, only useful as proof
+ of concept.
+
+Fourth truth: the 'unbound' syntax is a mess
+------------------------------------------------------------------
+
+Having established that the 'unbound' syntax does not returns unbound methods
+one might ask what its purpose is.
+The answer is that ``super(C)`` is intended to be used as an attribute in
+other classes. Then the descriptor magic will automatically convert the
+unbound syntax in the bound syntax. For instance:
+
+>>> class B(object):
+... a = 1
+>>> class C(B):
+... pass
+>>> class D(C):
+... sup = super(C)
+>>> d = D()
+>>> d.sup.a
+1
+
+This works since ``d.sup.a`` calls ``super(C).__get__(d,D).a`` which is
+converted to ``super(C, d).a`` and retrieves ``B.a``.
+
+There is a single use case for the single argument
+syntax of ``super`` that I am aware of, but I think it gives more troubles
+than advantages. The use case is the implementation of "autosuper" made
+by Guido on his essay about new-style classes.
+
+The idea there is to use the unbound super objects as private
+attributes. For instance, in our example, we could define the
+private attribute ``__sup`` in the class ``C`` as the unbound
+super object ``super(C)``:
+
+>>> C._C__sup = super(C)
+
+With this definition inside the methods the syntax
+``self.__sup.meth(arg)`` can be used
+as an alternative to ``super(C, self).meth(arg)``, and the advantage is
+that you avoid to repeat the name of the class in the calling
+syntax, since that name is hidden in the mangling mechanism of
+private names. The creation of the ``__sup`` attributes can be hidden
+in a metaclass and made automatic. So, all this seems to work: but
+actually this *not* the case.
+
+Things may wrong in various case, for instance for classmethods,
+as in this example::
+
+ #<ex.py>
+
+ class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ def meth(self):
+ print "B.meth(%s)" % self
+ meth = classmethod(meth)
+
+ class C(B):
+ def meth(self):
+ print "C.meth(%s)" % self
+ self.__super.meth()
+ meth = classmethod(meth)
+
+ C._C__super = super(C)
+
+ class D(C):
+ pass
+
+ D._D__super = super(D)
+
+
+ d=D()
+
+ d.meth()
+
+ #</ex.py>
+
+The last line raises an ``AttributeError: 'super' object has no attribute
+'meth'.``
+
+So, using a ``__super`` unbound super object is not a robust solution
+(notice that everything would work by substituting ``self.__super.meth()``
+with ``super(C,self).meth()``. There are other ways to avoid repeating
+the class name, see for instance my cookbook recipe [#]_.
+
+If it was me, I would just remove the single argument syntax of ``super``,
+making it illegal. But this would probably break someone code, so
+I don't think it will ever happen. Another solution would be just to
+deprecate it. There is no need for this syntax, one can always circumvent
+it.
+
+Fifth truth: ``super`` does not work with meta-attributes
+-----------------------------------------------------------------------
+
+If you start using ``super`` intensively, soon or latter you will find
+a number of subtilities. One of these is the fact that ``super`` does not
+work well with the ``__name__`` special attribute. Consider this example:
+
+>>> class B(object):
+... "This is class B"
+...
+>>> class C(B):
+... pass
+...
+
+Here the special (class) attribute ``__doc__`` is retrieved as you would expect:
+
+>>> super(C,C).__doc__ == super(C,C()).__doc__ == B.__doc__
+True
+
+On the other hand, the special attribute ``__name__`` is not
+retrieved correctly:
+
+>>> super(C,C).__name__ # one would expect it to be 'B'
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+
+The problem is that ``__name__`` is not just a plain class
+attribute: it is actually a "getset descriptor" defined on
+the metaclass "type" (try to run ``help(type.__dict__['__name__'])``
+and you will see for yourself). More in general, ``super`` has
+problems with meta-attributes, i.e. class attributes of metaclasses.
+
+Meta-attributes differs from regular attributes since they are not
+transmitted to the instances of the instances.You can find the
+rationale for this behaviour elsewhere [#]_: here I am only interested
+to the issues with ``super``. Consider this example:
+
+#<example1.py>
+
+class M(type):
+ "A metaclass with a class attribute 'a'."
+ a = 1
+
+class B:
+ "An instance of M with a meta-attribute 'a'."
+ __metaclass__ = M
+
+class C(B):
+ "An instance of M with the same meta-attribute 'a'"
+
+if __name__ == "__main__":
+ print B.a, C.a # => 1 1
+ print super(C,C).a #=> attribute error
+
+#</example1.py>
+
+If you run this, you will get an attribute error. This is a case
+where ``super`` is doing the *right* thing, since 'a' is *not* inherited
+from B, but it comes directly from the metaclass (again, look at my second
+article with David Mertz to understand why it is so), so 'a'
+is *not* in the MRO of C. A similar thing happens for the "__name__"
+attribute (the fact that it is a descriptor and not a plain
+attribute does not matter), so ``super`` is working correctly, but
+still it may seems surprising at first.
+
+Sixth truth: ``super`` does not play well with pydoc and doctest
+-----------------------------------------------------------------
+
+There are various ``super`` pitfalls currently undocumented of which
+the experienced Python programmer should be aware of.
+
+The unbound form of ``super`` does not play well with pydoc.
+The problems is still there in Python 2.3.4 (see bug report SF729103)
+
+>>> class B(object): pass
+...
+>>> class C(B):
+... s=super(B)
+...
+>>> help(C)
+Traceback (most recent call last):
+ ...
+ ... lots of stuff here
+ ...
+File "/usr/lib/python2.3/pydoc.py", line 1198, in docother
+ chop = maxlen - len(line)
+TypeError: unsupported operand type(s) for -: 'type' and 'int'
+
+I have not yet clear what the cause is, but it is certainly quite
+tricky. An incompatibility between ``super`` was reported by Christian Tanzer
+(SF902628); if you run the following, you will get a TypeError:
+
+ ::
+
+ #<ex.py>
+
+ class C(object):
+ pass
+
+ C.s = super(C)
+
+ if __name__ == "__main__":
+ import doctest, __main__
+ doctest.testmod(__main__)
+
+ #<ex.py>
+
+BTW, I don't think this is related to ``super`` only since I have
+found similar problems when playing with descriptors and doctest
+some time ago (but I cannot reproduce the bug right now).
+
+Seventh truth: special attribute access for special attributes is special ;)
+----------------------------------------------------------------------------
+
+There is also another subtle pitfall which is not directly related
+to ``super`` but which is often encountered working with ``super``.
+This came up at least three or four times in the newsgroup, and there
+are various independent bug reports on sourceforge about it, so possibly
+you may face it too. Bjorn Pettersen was the first one who pointed out the
+problem to me (see also bug report SF 729913): the issue is that
+
+``super(MyCls, self).__getitem__(5)``
+
+works, but not
+
+``super(MyCls, self)[5]``.
+
+The problem is general to all special methods, not only to ``__getitem__``,
+and the explanation for that has to do with the implementation of
+attribute lookup for special methods. Clear explanations of what is
+going on are provided by Michael Hudson as a comment to the bug report:
+SF789262 and by Raymond Hettinger as a comment to the bug report SF805304.
+Shortly put, this is not a problem of ``super`` per se, the problem is
+that the special call ``x[5]`` (using ``__getitem__`` as example) is
+converted to ``type(x).__getitem__(x,5)``
+*only if* ``__getitem__`` is explicitely defined in ``type(x)``. If
+``type(x)`` does not define ``__getitem__`` directly, but only
+indirectly via delegation (i.e. overriding ``__getattribute__``),
+then the second form works but not the first one.
+
+This restriction will likely stay in Python because it would involve
+a really big change and a loss of performances, so it has to be
+considered just a documentation bug, since nowhere in
+the docs it is mentioned that special calling syntaxes (such as
+the ``[]`` call, the ``iter`` call, the ``repr`` call, etc. etc.)
+are special and bypass ``__getattribute__``. Guido advice is:
+just use the more explicit form and everything will work.
+
+Finally, there may be other bugs and pitfalls I am not aware of. Certainly
+there are many other issues and bugs in previous versions of Python that
+I have not mentioned here, since they have been fixed, but that you may
+encounter if you use Python 2.2 (and maybe even in 2.3).
+
+The last truth: when it comes to ``super`` don't trust even Guido himself!
+-----------------------------------------------------------------------------
+
+In order to explain how ``super`` works, Guido describes a
+"fully functional implementation of the super() built-in class in
+pure Python" in "Unifying types and classes in Python 2.2".
+Unfortunately, that implemenentation is more harmful than helpful, since
+the current ``super`` DOES NOT work in the same way :-(
+Take for instance this example:
+
+>>> class C(object):
+... f='C.f'
+
+>>> class D(C):
+... f='D.f'
+
+Here the ``super`` works fine,
+df
+
+>>> print super(D,D()).f
+C.f
+
+but the class ``Super`` described by Guido will raise an attribute
+error when invoked as ``Super(D,D()).f``. Therefore Super is NOT
+equivalent to the currently implemented ``super`` built-in
+
+Notes
+-----------------
+
+
+.. [#] I wrote an essay on Python 2.3 Method Resolution Order which
+ you may find here:
+
+.. [#] Raymond Hetting wrote a beautiful essay on descriptors here:
+
+.. [#] David Mertz and me wrote a couple of articles on metaclasses;
+ the second one is the relevant one for the issues discussed here:
+
+.. [#] http://groups.google.it/groups?hl=it&lr=&ie=UTF-8&threadm=2259b0e2.0304300625.4e0ebace%40posting.google.com&rnum=3&prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26q%3Dsimionato%2Bpettersen%2Bsuper%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python.*
diff --git a/pypers/super/super24.txt b/pypers/super/super24.txt
new file mode 100755
index 0000000..e2a8674
--- /dev/null
+++ b/pypers/super/super24.txt
@@ -0,0 +1,536 @@
+The truth about ``super``
+==========================
+
+:Author: Michele Simionato
+:Email: michele.simionato@gmail.com
+:Date: June 2004
+:Status: Draft
+
+``super`` is a new built-in, first introduced in Python 2.2 and slightly
+improved and fixed in Python 2.3, which is little known to the average
+Python programmer. One of the reason for this
+fact is the poor documentation of ``super``: at the time of this writing
+(June 2004) the documentation is incomplete and in some parts
+misleading and even wrong. For instance, it was recently pointed out
+on comp.lang.python that the standard library (Python 2.3.4, section 2.1)
+still says::
+
+ super(type[, object-or-type])
+ Return the superclass of type. If the second argument is omitted the
+ super object returned is unbound. If the second argument is an object,
+ isinstance(obj, type) must be true. If the second argument is a type,
+ issubclass(type2, type) must be true. super() only works for new-style
+ classes.
+
+The first sentence is just plain wrong. ``super`` does not return the
+superclass. There is no such a thing as "the" superclass in a Multiple
+Inheritance (MI) world. Also, the sentence about 'unbound' is misleading,
+since it may easily lead the programmer to think about bound and unbound
+methods, whereas it has nothing to do with that concept. Finally, there are
+subtle pitfalls and dark corners of ``super`` which are not at all mentioned.
+IMNSHO ``super`` is one of the most trickiest and surprising Python
+constructs, and we absolutely needs a document to share light its secrets.
+The present draft is a first step in this direction: it aims to tell you
+the "truth" about ``super``. At least the amount of truth
+I have discovered with my experimentations, which is certainly
+not the whole truth ;)
+
+A fair warning is in order here: this document is aimed to expert
+Pythonistas. It assumes you already know the Method Resolution Order (MRO)
+concept; moreover a good understanding of descriptors would be extremely
+useful in order to grasp this document. Some parts also require good
+familiarity with metaclasses. All in all, this paper is not for the faint
+of heart ;)
+
+First truth: there is no superclass in a MI world
+----------------------------------------------------------
+
+Readers familiar will single inheritance languages, such as
+Java or Smalltalk, will have a clear concept of superclass
+in mind. This concept, however, has *no useful meaning* in Python or in
+other multiple inheritance languages. I became convinced of this fact
+after a discussion with Bjorn Pettersen on comp.lang.python in May 2003
+(at that time I was mistakenly thinking that one could define a
+superclass concept in Python). Consider this example from that
+discussion:
+
+ ::
+
+ +-----+
+ | T |
+ |a = 0|
+ +-----+
+ / \
+ / \
+ +-------+ +-------+
+ | A | | B |
+ | | | a = 2 |
+ +-------+ +-------+
+ \ /
+ \ /
+ +-----+
+ | C |
+ +-----+
+ :
+ : instantiation
+ c
+
+>>> class T(object):
+... a = 0
+
+>>> class A(T):
+... pass
+
+>>> class B(T):
+... a = 2
+
+>>> class C(A,B):
+... pass
+
+>>> c = C()
+
+Who is the superclass of ``C``? There are two direct superclasses (i.e. bases)
+of ``C``: ``A ``and ``B``. ``A`` comes before ``B``, so one would naturally
+think that the superclass of ``C`` is ``A``. ``A`` inherits the attribute
+``a`` from ``T``, so if ``super(C,c)`` was returning the superclass of ``C``,
+then it should return ``A``. ``A`` inherits its attribute ``a`` from ``T``,
+where ``a`` has the value 0, so ``super(C,c).a`` would return 0. This
+is NOT what happens. Instead, ``super(C,c).a`` walks trought the
+method resolution order [#]_ of the class of ``c`` (which is ``C``)
+and retrieves the attribute from the first class above ``C`` which
+defines it. In this example the MRO of ``C`` is ``[C, A, B, T, object]``, so
+``B`` is the first class above ``C`` which defines ``a`` and ``super(C,c).a``
+correctly returns the value 2, not 0:
+
+>>> super(C,c).a
+2
+
+You may call ``A`` the superclass of ``C``, but this is not an useful
+concept since the methods are resolved by looking at the classes
+in the MRO of ``C``, and not by looking at the classes in the MRO of ``A``
+(which in this case is ``[A,T, object]`` and does not contain ``B``).
+The whole MRO is needed, not just the first superclass.
+
+So, using the word *superclass* in the standard doc is completely
+misleading and should be avoided altogether.
+
+Second truth: ``super`` returns proxy objects
+----------------------------------------------------
+
+Having established that ``super`` cannot return and does not return the
+mythical superclass, we may ask ourselves what the hell is returning
+``super`` ;) The truth is that ``super`` returns proxy objects.
+
+Informally speaking, a proxy object is an object with
+the ability to dispatch to methods of other classes via delegation.
+Technically, ``super`` works by overriding the ``__getattribute__``
+method in such a way that its instances becomes proxy objects providing
+access to the methods in the MRO. The dispatch is done in such a way
+that
+
+``super(cls, instance-or-subclass).meth(*args, **kw)``
+
+corresponds to
+
+``right-method-in-the-MRO-applied-to(instance-or-subclass, *args, **kw)``
+
+There is a caveat at this point: the second argument can be
+an instance of the first argument, or a subclass of it; in
+the first case a bound method is returned, in the second case
+an unbound method is returned (this was not true in earlier
+versions of Python).
+
+For instance, in this example
+
+>>> class B(object):
+... def __repr__(self):
+... return "<instance of %s>" % self.__class__.__name__
+
+>>> class C(B):
+... pass
+
+>>> class D(C):
+... pass
+
+>>> d = D()
+
+we have
+
+>>> print super(C,d).__repr__
+<bound method D.__repr__ of <instance of D>>
+
+and
+
+>>> print super(C,D).__repr__
+<unbound method D.__repr__>
+
+So, when we call those methods, we get
+
+>>> print super(C,d).__repr__()
+<instance of D>
+
+(here ``d``, a ``D`` instance, is being passed to ``__repr__``) and
+
+>>> print super(C, D).__repr__(D())
+<instance of D>
+
+
+Third truth: ``super`` returns descriptor objects
+----------------------------------------------------
+
+Descriptors (more properly I should speak of the descriptor protocol) were
+introduced in Python 2.2 by Guido van Rossum. Their primary motivation
+is technical, since they were needed to implement the new-style object
+system. Descriptors were also used to introduce new standard concepts in
+Python, such as classmethods, staticmethods and properties. Moreover,
+according to the traditional transparency policy of Python, descriptors
+were exposed to the application programmer, giving him/her the freedom
+to write custom descriptors. [#]_ Any serious Python programmer should have
+a look at descriptors: luckily they are now very well documented (which was
+not the case when I first studied them :-/) thanks to the beautiful essay
+of Raimond Hettinger [#]_. You should read it before continuing this article,
+since it explains all the details. However, for the sake of our discussion
+of ``super``, it is enough to say that a *descriptor class* is just a
+regular new-style class which implements a .``__get__`` method with
+signature ``__get__(self, obj, objtyp = None)``. A *descriptor object*
+is just an instance of a descriptor class.
+
+Descriptor objects are intended to be used as attributes (hence their
+complete name attribute descriptors). Suppose that 'descr' is a
+given descriptor object used as attribute of a given class C.
+Then the syntax ``C.descr`` is actually interpreted by Python as a
+call to ``descr.__get__(None, C)``, whereas the same syntax for an
+instance of C corresponds to a call to ``descr.__get__(c, type(c))``.
+
+The unbound method ``__repr__`` can be retrieved as
+
+>>> super(C,D).__repr__
+<unbound method D.__repr__>
+
+and we may check that it works correctly:
+
+>>> print _(d)
+<instance of D>
+
+This is cumbersome and tricky, but it is the only way to get
+the unbound method. Using the unbound form of ``super`` does
+*not* return ``D.__repr__``: instead it returns ``super.__repr__``
+bound to the (unbound) super object ``super(C)``:
+
+>>> print super(C).__repr__() # same as repr(super(C))
+<super: <class 'C'>, NULL>
+
+Very tricky. Notice that ``super`` also redefines ``__new__``,
+``__init``, ``__get__``, ``__getattribute``, as well as inheriting
+other special attributes from ``object``. So using the single-argument
+syntax you will dispatch to
+these methods in ``super`` and not to the right methods defined in
+the hierarchy at hand. On the other hand, the two-argument syntax
+does not have this problem. For instance
+
+>>> print super(C,C).__repr__(C())
+<instance of C>
+
+does the right thing.
+
+This other example should shed further light. Suppose ``B``
+has a method called ``meth`` like this:
+
+>>> B.meth = lambda self :'You called B.meth with first argument %s' % self
+
+Then ``B.meth`` is an unbound method and, mislead by the documentation,
+one could expect to be able to access it with the syntax ``super(C).meth``.
+This is not the case. You get an error instead:
+
+>>> super(C).meth
+Traceback (most recent call last):
+ ...
+AttributeError: 'super' object has no attribute 'meth'
+
+Unbound super objects cannot be accessed directly,
+they must be converted to bound objects in order to make them
+to dispatch properly. Unbound super objects can be converted to
+bound super objects via the descriptor protocol. For instance,
+in this example I can convert ``super(C)`` in a super object
+bound to ``d`` in this way:
+
+>>> boundsuper = super(C).__get__(d, D) # this is the same as super(C,d)
+
+Now I can access the bound method 'd.meth':
+
+>>> print boundsuper.meth
+<bound method D.<lambda> of <instance of D>>
+
+
+
+As a consequence, ``__get__`` cannot be turned into a cooperative method
+just by using ``super``: you would get the wrong ``get``.
+
+.. [#] Descriptors gives an enormous power to the application
+ programmer, especially when combined with metaclasses. For instance,
+ using descriptors I did implement a prototype-based object system for
+ Python in few lines: see of course this was an hack, only useful as proof
+ of concept.
+
+Fourth truth: the 'unbound' syntax is a mess
+------------------------------------------------------------------
+
+Having established that the 'unbound' syntax does not returns unbound methods
+one might ask what its purpose is.
+The answer is that ``super(C)`` is intended to be used as an attribute in
+other classes. Then the descriptor magic will automatically convert the
+unbound syntax in the bound syntax. For instance:
+
+>>> class B(object):
+... a = 1
+>>> class C(B):
+... pass
+>>> class D(C):
+... sup = super(C)
+>>> d = D()
+>>> d.sup.a
+1
+
+This works since ``d.sup.a`` calls ``super(C).__get__(d,D).a`` which is
+converted to ``super(C, d).a`` and retrieves ``B.a``.
+
+There is a single use case for the single argument
+syntax of ``super`` that I am aware of, but I think it gives more troubles
+than advantages. The use case is the implementation of "autosuper" made
+by Guido on his essay about new-style classes.
+
+The idea there is to use the unbound super objects as private
+attributes. For instance, in our example, we could define the
+private attribute ``__sup`` in the class ``C`` as the unbound
+super object ``super(C)``:
+
+>>> C._C__sup = super(C)
+
+With this definition inside the methods the syntax
+``self.__sup.meth(arg)`` can be used
+as an alternative to ``super(C, self).meth(arg)``, and the advantage is
+that you avoid to repeat the name of the class in the calling
+syntax, since that name is hidden in the mangling mechanism of
+private names. The creation of the ``__sup`` attributes can be hidden
+in a metaclass and made automatic. So, all this seems to work: but
+actually this is *not* the case.
+
+Things may wrong in various case, for instance for classmethods,
+as in this example::
+
+ #<ex1.py>
+
+ class B(object):
+ def __repr__(self):
+ return '<instance of %s>' % self.__class__.__name__
+ def meth(self):
+ print "B.meth(%s)" % self
+ meth = classmethod(meth)
+
+ class C(B):
+ def meth(self):
+ print "C.meth(%s)" % self
+ self.__super.meth()
+ meth = classmethod(meth)
+
+ C._C__super = super(C)
+
+ class D(C):
+ pass
+
+ D._D__super = super(D)
+
+
+ d=D()
+
+ d.meth()
+
+ #</ex1.py>
+
+The last line raises an ``AttributeError: 'super' object has no attribute
+'meth'.``
+
+So, using a ``__super`` unbound super object is not a robust solution
+(notice that everything would work by substituting ``self.__super.meth()``
+with ``super(C,self).meth()``. There are other ways to avoid repeating
+the class name, see for instance my cookbook recipe [#]_.
+
+If it was me, I would just remove the single argument syntax of ``super``,
+making it illegal. But this would probably break someone code, so
+I don't think it will ever happen. Another solution would be just to
+deprecate it. There is no need for this syntax, one can always circumvent
+it.
+
+Fifth truth: ``super`` does not work with meta-attributes
+-----------------------------------------------------------------------
+
+If you start using ``super`` intensively, soon or latter you will find
+a number of subtilities. One of these is the fact that ``super`` does not
+work well with the ``__name__`` special attribute. Consider this example:
+
+>>> class B(object):
+... "This is class B"
+...
+>>> class C(B):
+... pass
+...
+
+Here the special (class) attribute ``__doc__`` is retrieved as you would expect:
+
+>>> super(C,C).__doc__ == super(C,C()).__doc__ == B.__doc__
+True
+
+On the other hand, the special attribute ``__name__`` is not
+retrieved correctly:
+
+>>> super(C,C).__name__ # one would expect it to be 'B'
+Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+AttributeError: 'super' object has no attribute '__name__'
+
+The problem is that ``__name__`` is not just a plain class
+attribute: it is actually a "getset descriptor" defined on
+the metaclass "type" (try to run ``help(type.__dict__['__name__'])``
+and you will see for yourself). More in general, ``super`` has
+problems with meta-attributes, i.e. class attributes of metaclasses.
+
+Meta-attributes differs from regular attributes since they are not
+transmitted to the instances of the instances.You can find the
+rationale for this behaviour elsewhere [#]_: here I am only interested
+to the issues with ``super``. Consider this example:
+
+#<example1.py>
+
+class M(type):
+ "A metaclass with a class attribute 'a'."
+ a = 1
+
+class B:
+ "An instance of M with a meta-attribute 'a'."
+ __metaclass__ = M
+
+class C(B):
+ "An instance of M with the same meta-attribute 'a'"
+
+if __name__ == "__main__":
+ print B.a, C.a # => 1 1
+ print super(C,C).a #=> attribute error
+
+#</example1.py>
+
+If you run this, you will get an attribute error. This is a case
+where ``super`` is doing the *right* thing, since 'a' is *not* inherited
+from B, but it comes directly from the metaclass (again, look at my second
+article with David Mertz to understand why it is so), so 'a'
+is *not* in the MRO of C. A similar thing happens for the "__name__"
+attribute (the fact that it is a descriptor and not a plain
+attribute does not matter), so ``super`` is working correctly, but
+still it may seems surprising at first.
+
+Sixth truth: ``super`` does not play well with pydoc and doctest
+-----------------------------------------------------------------
+
+There are various ``super`` pitfalls currently undocumented of which
+the experienced Python programmer should be aware of.
+
+The unbound form of ``super`` does not play well with pydoc.
+The problems is still there in Python 2.4 (see bug report SF729103)
+
+>>> class B(object): pass
+...
+>>> class C(B):
+... s=super(B)
+...
+>>> help(C)
+Traceback (most recent call last):
+ ...
+ ... lots of stuff here
+ ...
+ File "/usr/lib/python2.4/pydoc.py", line 1290, in docother
+ chop = maxlen - len(line)
+TypeError: unsupported operand type(s) for -: 'type' and 'int'
+
+
+I have not yet clear what the cause is, but it is certainly quite
+tricky.
+
+Seventh truth: special attribute access for special attributes is special ;)
+----------------------------------------------------------------------------
+
+There is also another subtle pitfall which is not directly related
+to ``super`` but which is often encountered working with ``super``.
+This came up at least three or four times in the newsgroup, and there
+are various independent bug reports on sourceforge about it, so possibly
+you may face it too. Bjorn Pettersen was the first one who pointed out the
+problem to me (see also bug report SF 729913): the issue is that
+
+``super(MyCls, self).__getitem__(5)``
+
+works, but not
+
+``super(MyCls, self)[5]``.
+
+The problem is general to all special methods, not only to ``__getitem__``,
+and the explanation for that has to do with the implementation of
+attribute lookup for special methods. Clear explanations of what is
+going on are provided by Michael Hudson as a comment to the bug report:
+SF789262 and by Raymond Hettinger as a comment to the bug report SF805304.
+Shortly put, this is not a problem of ``super`` per se, the problem is
+that the special call ``x[5]`` (using ``__getitem__`` as example) is
+converted to ``type(x).__getitem__(x,5)``
+*only if* ``__getitem__`` is explicitely defined in ``type(x)``. If
+``type(x)`` does not define ``__getitem__`` directly, but only
+indirectly via delegation (i.e. overriding ``__getattribute__``),
+then the second form works but not the first one.
+
+This restriction will likely stay in Python because it would involve
+a really big change and a loss of performances, so it has to be
+considered just a documentation bug, since nowhere in
+the docs it is mentioned that special calling syntaxes (such as
+the ``[]`` call, the ``iter`` call, the ``repr`` call, etc. etc.)
+are special and bypass ``__getattribute__``. Guido advice is:
+just use the more explicit form and everything will work.
+
+Finally, there may be other bugs and pitfalls I am not aware of. Certainly
+there are many other issues and bugs in previous versions of Python that
+I have not mentioned here, since they have been fixed, but that you may
+encounter if you use Python 2.2 (and maybe even in 2.3).
+
+The last truth: when it comes to ``super`` don't trust even Guido himself!
+-----------------------------------------------------------------------------
+
+In order to explain how ``super`` works, Guido describes a
+"fully functional implementation of the super() built-in class in
+pure Python" in "Unifying types and classes in Python 2.2".
+Unfortunately, that implemenentation is more harmful than helpful, since
+the current ``super`` DOES NOT work in the same way :-(
+Take for instance this example:
+
+>>> class C(object):
+... f='C.f'
+
+>>> class D(C):
+... f='D.f'
+
+Here ``super`` works fine,
+
+>>> print super(D,D()).f
+C.f
+
+but the class ``Super`` described by Guido will raise an attribute
+error when invoked as ``Super(D,D()).f``. Therefore Super is NOT
+equivalent to the currently implemented ``super`` built-in
+
+Notes
+-----------------
+
+
+.. [#] I wrote an essay on Python 2.3 Method Resolution Order which
+ you may find here:
+
+.. [#] Raymond Hetting wrote a beautiful essay on descriptors here:
+
+.. [#] David Mertz and me wrote a couple of articles on metaclasses;
+ the second one is the relevant one for the issues discussed here:
+
+.. [#] http://groups.google.it/groups?hl=it&lr=&ie=UTF-8&threadm=2259b0e2.0304300625.4e0ebace%40posting.google.com&rnum=3&prev=/groups%3Fhl%3Dit%26lr%3D%26ie%3DUTF-8%26q%3Dsimionato%2Bpettersen%2Bsuper%26btnG%3DCerca%26meta%3Dgroup%253Dcomp.lang.python.*
diff --git a/pypers/test_oopp.py b/pypers/test_oopp.py
new file mode 100755
index 0000000..7951ca5
--- /dev/null
+++ b/pypers/test_oopp.py
@@ -0,0 +1,122 @@
+"""Check the Python scripts contained in the OOPP tutorial.
+In addition, it automatically create the module oopp containing
+many useful routines.
+
+Notes
+-----
+
+1. A very cryptic error message of this kind
+
+Traceback (most recent call last):
+ File "test.py", line 82, in ?
+ if __name__=='__main__': main()
+ File "test.py", line 74, in main
+ check(chapter,scriptname,code); n=n+1
+ File "test.py", line 62, in check
+ open(scriptname,'w').write(code)
+IOError: invalid argument: w
+
+means that some of your text files is DOS style. Working solution:
+
+$dos2unix *.txt
+---
+
+This script is 1.5.2 backward compatible.
+exec and execfile have a bug in 2.2: executed code does not recognize
+correctly descriptors! It would work running in a separate interpreter.
+
+"""
+
+#check Python version
+import sys,os,string
+version=string.split(sys.version)[0]
+msg=('\nSorry, your current Python version is %s.\n' % version+
+ 'You must use Python 2.2+ to run this program.\n')
+if version <'2.2': raise SystemExit('*'*45+msg+'*'*45)
+
+#open files for append
+file("output.txt","w").close() #erase previous content
+sys.stdout=file("output.txt","a") #append mode
+file("oopp.py","w").close() #erase previous content
+ooppmodule=file("oopp.py","a") #append mode
+
+#global lists
+err=[]; arglist=sys.argv[1:]
+
+def extract(chapter,condition):
+ """This function collects interpreter lines, oopp lines and script lines
+ and returns them is a list of string tuples with the form
+ [(chaptername,scriptname,code)]"""
+ code=[]; ooppls=[]; codels=[]; inter=[]
+ for line in file(chapter):
+ l=line.lstrip()
+ if ((l.startswith('>>>') or l.startswith('...'))
+ and not (l.endswith('error\n') or l.endswith('.\n')) ):
+ inter.append(line[6:]) #valid interprer line
+ elif l.startswith('#</oopp'): #end oopp routine
+ ooppmodule.write(''.join(ooppls)+'\n')
+ ooppls=[]
+ elif l.startswith('#<oopp'):#start oopp routine
+ ooppls=['']
+ elif ooppls: #read oopp routine lines
+ ooppls.append(line[2:])
+ elif l.startswith('#</'): #end script
+ if condition(scriptname):
+ codels.append((chapter,scriptname,''.join(code)))
+ code=[]
+ elif l.startswith('#<'): #start script
+ scriptname=l[2:-2]
+ code=['']
+ elif code: #read script lines
+ code.append(line[2:])
+ #Add all the lines of interactive code in this chapter
+ icode=''.join(inter) # interactive code
+ iname=chapter[:-4]+'_inter.py'
+ if icode: codels.append((chapter,iname,icode))
+ return codels
+
+def check(chapter,scriptname,code):
+ # write the code in scriptname
+ # print >> sys.stderr,scriptname
+ f=open(scriptname,'w'); f.write(code); f.close()
+ print '\n'+'='*77+'\n'
+ print '--- Script "%s"' % scriptname,'in',chapter,':\n\n'+code
+ print '--- Output of "%s":\n' % scriptname; error=0 # default
+ inter=scriptname.endswith('inter.py')
+ if inter: sys.stderr.write('-')
+ else: sys.stderr.write('+')
+ try:
+ #os.system('%s %s > /dev/null' % (sys.executable,scriptname))#very safe
+ namespace={}; execfile(scriptname,namespace)
+ except Exception,e:
+ err.append('Error found in %s [%s]:\n %s' %
+ (scriptname,chapter,e)); error=1
+ else:
+ error=0
+ if not arglist and not error: os.remove(scriptname)
+
+def main():
+ if arglist: #check only the scripts in the arglist
+ condition=lambda scriptname: (scriptname in arglist)
+ else: #check all scripts
+ condition=lambda scriptname: 1
+ print "CHECK OF THE SCRIPTS IN 'OBJECT ORIENTED PROGRAMMING IN PYTHON'"
+ chapters="preface.txt first.txt functions.txt objects.txt classes.txt "\
+ "descr.txt MI.txt meta.txt magic.txt secret.txt prog.txt app1.txt"\
+ .split()
+ codels=[]; n=0
+ for chapter in chapters: # first pass; creates oopp.py
+ #if not arglist: sys.stderr.write('\nChecking %s ...\n' % chapter)
+ codels = codels + extract(chapter,condition)
+ ooppmodule.close() # needed !
+ for chapter,scriptname,code in codels:
+ check(chapter,scriptname,code)
+ if not scriptname.endswith('inter.py'): n=n+1
+ if err:
+ sys.stderr.write('\n'+'\n'.join(err))
+ sys.stderr.write('\nThe debug is left to you.\n')
+ else:
+ ok='\nGood: executed %s examples with success.\n' % n
+ sys.stderr.write(ok+'The output can be found in file output.txt\n')
+
+if __name__=='__main__': main()
diff --git a/pypers/test_re.py b/pypers/test_re.py
new file mode 100755
index 0000000..1839d46
--- /dev/null
+++ b/pypers/test_re.py
@@ -0,0 +1,13 @@
+"""This script looks at its own source code and extracts dotted names,
+i.e. names containing at least one dot, such as object.attribute or
+more general one, such as obj.attr.subattr."""
+# Notice that dotted.names in comments and literal strings are ignored
+if __name__ == "__main__":
+ from oopp import *
+ import __main__
+ text = inspect.getsource(__main__)
+ regexp = Regexp.CODESEP| Regexp.DOTNAME()
+ print 'Using the regular expression',regexp
+ print "I have found the following dotted names:\n%s" % [
+ MO.group() for MO in regexp.finditer(text) if MO.lastgroup=='DOTNAME']
+
diff --git a/pypers/trace.txt b/pypers/trace.txt
new file mode 100755
index 0000000..e5161b3
--- /dev/null
+++ b/pypers/trace.txt
@@ -0,0 +1,15 @@
+[HomoSapiensSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4048132c>,){} ...
+ [HomoSapiens] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4048132c>,){} ...
+ [HomoHabilis] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4048132c>,){} ...
+ [Homo] Calling 'can' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4048132c>,){} ...
+ [PrettyPrinted] Calling '__str__' with arguments
+ (<oopp.HomoSapiensSapiens object at 0x4048132c>,){} ...
+ [PrettyPrinted.__str__] called with result: <HomoSapiensSapiens>
+ [Homo.can] called with result: None
+ [HomoHabilis.can] called with result: None
+ [HomoSapiens.can] called with result: None
+[HomoSapiensSapiens.can] called with result: None
diff --git a/pypers/tracedaccess.py b/pypers/tracedaccess.py
new file mode 100755
index 0000000..de27f58
--- /dev/null
+++ b/pypers/tracedaccess.py
@@ -0,0 +1,26 @@
+from oopp import isplaindata,inspect
+class TracedAccess(type):
+ "Metaclass converting data attributes to properties"
+ def __init__(cls,name,bases,dic):
+ cls.datadic={}
+ for a in dic:
+ if isplaindata(a):
+ cls.datadic[a]=dic[a]
+ def get(self,a=a):
+ v=cls.datadic[a]
+ print "Accessing %s, value=%s" % (a,v)
+ return v
+ def set(self,v,a=a):
+ print "Setting %s, value=%s" % (a,v)
+ cls.datadic[a]=v
+ setattr(cls,a,property(get,set))
+class C(object):
+ __metaclass__ = TracedAccess
+ a1='x'
+class D(C): # shows that the approach works well with inheritance
+ a2='y'
+i=D()
+i.a1 # => Accessing a1, value=x
+i.a2 # => Accessing a2, value=y
+i.a1='z' # => Setting a1, value=z
+i.a1 # => Accessing a1, value=z
diff --git a/pypers/tracemain.py b/pypers/tracemain.py
new file mode 100755
index 0000000..9bf1068
--- /dev/null
+++ b/pypers/tracemain.py
@@ -0,0 +1,7 @@
+from oopp import ClsFactory,Traced,Reflective
+def f1(x): return x # nested functions
+def f2(x): return f1(x) # we want to trace
+f1orf2=lambda k,v : v is f1 or v is f2
+make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]
+traced=make('traced',globals())
+traced.f2('hello!') # call traced.f2
diff --git a/pypers/twill/abstract.txt b/pypers/twill/abstract.txt
new file mode 100755
index 0000000..8046946
--- /dev/null
+++ b/pypers/twill/abstract.txt
@@ -0,0 +1,10 @@
+Title: Testing Web Applications with Python
+Author: Michele Simionato, Ph. D.
+Abstract:
+
+In this article Michele writes about his experience in testing Web
+applications and gives a few tips and tricks to do it yourself.
+He briefly discusses the Python libraries to interact with the
+HTTP protocol and he shows the virtues of twill, a little
+command interpreter written in Python with the specific purpose
+of testing Web applications.
diff --git a/pypers/twill/chromatic1.txt b/pypers/twill/chromatic1.txt
new file mode 100755
index 0000000..fae6797
--- /dev/null
+++ b/pypers/twill/chromatic1.txt
@@ -0,0 +1,29 @@
+Hello, chromatic!
+
+Last year we exchanged a few emails and OnLamp published my paper on
+GraphViz and dot (http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html).
+
+Since then, I wanted to write something else, but I got involved in many
+other things and I could not find the time. However, it seems that
+now I have some spare time, and also an interesting subject to write on:
+automatic testing of Web applications.
+
+I did some work on the subject, and I was very happy when, as an aftermath
+of the ACCU conference in Oxford, I discovered that there is already a
+nice little language written with the purpose of testing Web application,
+and actually two Python implementations of it:
+PBP, the Python Browser poseur (http://pbp.berlios.de) and twill
+(http://darcs.idyll.org/~t/projects/twill/README.html). They are
+based on the mechanize library by John J. Lee, which in turns
+is inspired by a Perl package with the same name.
+
+I think these tools are pretty useful (they can test *any* web
+application, not just Python-based applications) and not enough
+well known, so I would like to popularize them. And what best of
+an O'Reilly article? ;)
+
+If you are interested, I can send you a draft in a few days.
+Best,
+
+
+ Michele Simionato
diff --git a/pypers/twill/errata.txt b/pypers/twill/errata.txt
new file mode 100755
index 0000000..03be607
--- /dev/null
+++ b/pypers/twill/errata.txt
@@ -0,0 +1,18 @@
+I scanned the paper and found a few misprints. You should be able to do
+the changes with a search and replace:
+
+
+'the app calls' -> 'the application calls'
+'even if have used' -> 'even if you have used'
+'in order togenerate' -> 'in order to generate'
+'is not that difficilt' -> 'is not that difficult'
+'--. small languages' -> '-- small languages'
+'convert it in a twill script' -> 'convert it into a twill script'
+'previous code had to fill it' -> 'previously we had to fill it'
+'sometimes don't need' -> 'sometimes you don't need'
+'at highest possible level' -> 'at the highest possible level'
+
+For the rest, it looks fine.
+Cheers,
+
+ Michele
diff --git a/pypers/twill/test_qdemo.txt b/pypers/twill/test_qdemo.txt
new file mode 100755
index 0000000..7e0df2e
--- /dev/null
+++ b/pypers/twill/test_qdemo.txt
@@ -0,0 +1,9 @@
+>>> from urllib import urlopen
+>>> PAGE = "http://localhost:8080/extras/form"
+>>> page = urlopen(PAGE, "name=michele&password=SECRET")
+
+>>> print page.read()
+
+>>> page = urlopen("http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets",
+... "name=michele&password=SECRET&time=50")
+>>> print page.read()
diff --git a/pypers/twill/testing_web_app.html b/pypers/twill/testing_web_app.html
new file mode 100755
index 0000000..1425b8e
--- /dev/null
+++ b/pypers/twill/testing_web_app.html
@@ -0,0 +1,584 @@
+<?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.9: http://docutils.sourceforge.net/" />
+<title>Testing Web Applications with Python</title>
+<meta name="author" content="Michele Simionato" />
+<meta name="date" content="June 2005" />
+</head>
+<body>
+<div class="document" id="testing-web-applications-with-python">
+<h1 class="title">Testing Web Applications with Python</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>June 2005</td></tr>
+</tbody>
+</table>
+<div class="section" id="introduction">
+<h1><a name="introduction">Introduction</a></h1>
+<p>You have just finished your beautiful Web application, with
+lots of pages, links, forms and buttons; you have spent weeks
+making sure that everything works fine,
+that the special cases are handled correctly, that the user cannot
+crash your system whatever she does.</p>
+<p>Now you are happy and you are ready to ship. But at the last minute
+the customer ask for a change: you have the time to apply the change,
+but not the time - nor the will - to pass trough another testing
+ordalia. So you ship anyway, hoping that your last little fix did not break
+some other part of the application. The result is that the hidden bug
+shows up at the first day of usage.</p>
+<p>If you recognized yourself in this situation then this paper
+is for you, keep reading. If not, well, keep reading anyway,
+I am sure you will find something interesting, among the following topics:</p>
+<ul class="simple">
+<li>how to separate unit tests from functional tests;</li>
+<li>how to test web applications (written in any language) using
+standard Python libraries;</li>
+<li>how to use twill, a nice and easy to learn web testing tool.</li>
+</ul>
+</div>
+<div class="section" id="to-test-or-not-to-test-this-is-the-problem">
+<h1><a name="to-test-or-not-to-test-this-is-the-problem">To test or not to test, this is the problem</a></h1>
+<p>Let me begin with a brief personal recollection of how I became
+interested in testing methodologies, and of what I have learned in
+the last couple of years.</p>
+<p>I have been aware of the importance of testing from the beginning, and
+I have heard about automatic testing for years. However, having
+heard about automatic testing is not the same as doing automatic testing,
+and not the same as doing automatic testing well.
+It takes some time and experience to get into the testing mood, as well
+as the ability to challenge some widespread misconceptions.</p>
+<p>For instance, when I began studying test driven methodologies,
+I had gathered two wrong ideas:</p>
+<ul class="simple">
+<li>that testing was all about unit testing;</li>
+<li>that the more you test, the better.</li>
+</ul>
+<p>After some experience I quickly realized myself that unit tests were
+not the only tool, nor the best tool to effectively test my application <a class="footnote-reference" href="#id11" id="id1" name="id1">[1]</a>.
+But to overcome the second misconception, I needed some help.</p>
+<p>The help come from an XP seminar I attended last year, were I
+actually asked the question &quot;how do I test the user interface of
+a Web application, i.e. that when the user click on a given page she gets
+the expected result?&quot;.</p>
+<p>The answer was: &quot;You don't. Why do you want to test that your browser is
+working?&quot;</p>
+<div class="section" id="the-case-for-not-testing-everything">
+<h2><a name="the-case-for-not-testing-everything">The case for not testing everything</a></h2>
+<p>The answer made me rethink many things. Obviously I was well aware
+from the beginning that full test coverage is a myth, still I thought
+one programmer should try to test as much as he can.</p>
+<p>But this is not the right approach. Instead, it is important to
+discriminate about the infinite amount of things that could be
+tested, and focus on the things that are of your responsability.</p>
+<p>If your customer wants functionality X, you must be sure functionality X
+is there. But if in order to get functionality X you need to rely on
+functionalities X1, X2 ,... XN, you don't need to test for all
+of them. You test only the functionality you are payed to
+implement. You don't test that the browser is working, it is
+not your job.</p>
+<p>For instance, in the case of a Web application,
+you can interact with it <em>indirectly</em>, via the HTTP protocol, or <em>directly</em>,
+via the internal API. If you check that when the user clicks
+on button B method M is called and the result R is displayed, you are
+testing both your application <em>and</em> the correctness of the
+HTTP protocol implementation both in the browser and in the server. This is
+way too much. You may rely on the HTTP protocol and just test the API, i.e
+just test that if method M is called the right result R is returned.</p>
+<p>Of course, a similar viewpoint is applicable to GUIs. In the same
+vein, you must test that the interface to the DB you wrote is working, but
+you don't need to test that the database itself is working, this
+is not your responsability.</p>
+<p>The basic point is to separate the indirect testing of the user
+interface - via the HTTP protocol - from the testing of the inner API.
+To this aim, it is important to write your
+application in such a way that you can test the logic independently
+from the user interface. Working in this way you also have the additional
+bonus that you can change the user interface later, without having to
+change a single tests for the logic part.</p>
+<p>The problem is that typically the customer will give his specifications
+in terms of the user interface. He will tell you &quot;There must be a page where
+the user will enter her order, then she will enter her credit card number,
+then the system must send a confirmation email, ...&quot;</p>
+<p>This kind of specification is a kind of very high level test - a
+functional test - which has to be converted into a low-level test:
+for instance you may have unit testing telling you that the ordered
+item has been registered in the database, that the
+<tt class="docutils literal"><span class="pre">send_confirmation_email</span> <span class="pre">method</span></tt> has been called etc.</p>
+<p>The conversion requires some thinking and practice and it an art more
+than a science. Actually I think that the art of testing is not in <em>how</em>
+to test, but in <em>what</em> to test. The best advice and best answer to somebody
+asking about &quot;how do I test a Web application?&quot; is probably &quot;make a priority
+lists of the things you would like to test and test as little as possible&quot;.</p>
+<p>For instance, one should never tests the details of the
+implementation. If you make this mistake (as I did at the beginning)
+your tests will get in your way at refactoring time, i.e. they will
+have exactly the opposite of the intended effect. Generally speaking,
+good advices are: don't spend time testing third party software, don't
+waste time testing code which API is likely to change, split
+the UI testing from the application logic testing.</p>
+<p>Ideally you should be able to determine what is the minimal set
+of tests needed to make your customer happy, and restrict yourself to
+those tests.</p>
+</div>
+<div class="section" id="the-case-for-testing-everything">
+<h2><a name="the-case-for-testing-everything">The case for testing everything</a></h2>
+<p>The previous advice is nice and reasonable, especially in an ideal world
+where third party software is bug free and everything is configured correctly.
+Unfortunately, the real world is a bit different.</p>
+<p>For instance you must be aware that your application
+does not work on some buggy browser, or that it cannot work in specific
+circumstances with some database. Also, you may have a nice and
+comprehensive test suite which runs flawlessly
+on your development machine, but still
+that the application may not work correctly when installed on a different
+machine, because the database could be installed improperly, or
+the mail server settings could be incorrect, or the Internet
+connection could be down, etc. In the same vein, if you want
+to really be sure that if the user - using a specifing browser in a
+specific environment - clicks on that button she gets that result,
+you have to emulate exactly that situation.</p>
+<p>It looks like we are back to square one, i.e. the need of testing everything.
+But we have learned something in the process: whereas <em>in principle</em>
+you would like to test everything, <em>in practice</em> you can effectively
+prioritize your tests, focusing on some more than on others,and
+splitting them in separate categories to be run separately at
+different times.</p>
+<p>You definitely need to test that the
+application is working as intended when deployed on a different
+machine: and from the failures to these installation tests you may also infer
+what is wrong and correct the problem. These installation tests
+- tests of the environment where your software is running - must
+be kept decoupled from the unit tests checking the
+application logic. If you are sure that the logic is right, then you are
+sure also sure that the problems are in the environment, and you can
+focus your debugging skills in the right direction.</p>
+<p>In any case, you need to have both high level (functional, integration,
+installation) tests and low level tests (unit tests, doctests). High level
+tests include tests of the user interface. In particular, you need a test to
+make sure that if an user click X he gets Y, so you are sure that the
+Internet connection, the web server, the database, the mail
+server, your application, the browser, all work nicely
+together. But you should not focus on these global kind
+of tests. You don't need to write a thousands of these
+high level tests, if you already have many specific low-level
+tests checking that the logic and the various components
+of your application are working.</p>
+</div>
+</div>
+<div class="section" id="how-to-test-the-user-interface">
+<h1><a name="how-to-test-the-user-interface">How to test the user interface</a></h1>
+<p>Having structured your application properly, you will need a smaller
+number of user interface tests, but still you will need at
+least a few. How do you write these tests then?</p>
+<p>There are two possibilities: the hard way and the easy way.</p>
+<p>The hard way is just doing everything by hand, by using your
+favorite programming language Web libraries to perform GET and POST
+requests and to verify the results. The easy way is to leverage on
+tools built by others. Of course,internally these tools work just by calling
+the low level libraries, so it is convenient to say a couple of words on the
+hard way, just to understand what is going on, in case the high level tool
+give you some problem. Moreover, there is always the possibility than
+you need something more customized, and knowledge of
+the low level libraries can be precious.</p>
+<p>The interaction between the user and a Web application
+passes through the HTTP protocol, so it is perfectly possible
+to simulate the action of an user clicking on a browser
+just by sending to the server an equivalent HTTP request (let me
+ignore the existence of Javascript for the moment).</p>
+<p>Any modern programming language has libraries to interact with the
+HTTP protocol, but here I will give my examples in Python, since Python
+is both a common language for Web programming and a readable one. In Python
+the interaction with the Web is managed via the urllib libraries <a class="footnote-reference" href="#id12" id="id2" name="id2">[2]</a>.
+You have two of them: urllib, which can be used in absence of authentication,
+and urllib2 which can also manage cookie-based authentication. A complete
+discussion of these two libraries would take a long
+time, but explaining the basics is pretty simple. I will just give
+a couple of recipes based on urllib2, the newest and most powerful library.</p>
+<p>I will notice here that the support for cookies in Python 2.4
+has improved (essentially by including the third party ClientCookie
+library) so you may not be aware of the trick I am going to explain, even
+if have used the urllib libraries in the past. So, don't skip the
+next two sections ;)</p>
+<div class="section" id="recipe-1-how-to-send-get-and-post-requests">
+<h2><a name="recipe-1-how-to-send-get-and-post-requests">Recipe 1: how to send GET and POST requests</a></h2>
+<p>Suppose you want to access a site which does not require authentication.
+Then making a GET request is pretty easy, just type at the intepreter
+prompt</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from urllib2 import urlopen
+&gt;&gt;&gt; page = urlopen(&quot;http://www.example.com&quot;)
+</pre>
+<p>Now you have a file like-object which contains the HTML code
+of the page <a class="reference" href="http://www.example.com">http://www.example.com</a>:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; for line in page: print line,
+&lt;HTML&gt;
+&lt;HEAD&gt;
+ &lt;TITLE&gt;Example Web Page&lt;/TITLE&gt;
+&lt;/HEAD&gt;
+&lt;body&gt;
+&lt;p&gt;You have reached this web page by typing &amp;quot;example.com&amp;quot;,
+&amp;quot;example.net&amp;quot;,
+ or &amp;quot;example.org&amp;quot; into your web browser.&lt;/p&gt;
+&lt;p&gt;These domain names are reserved for use in documentation and are not available
+ for registration. See &lt;a href=&quot;http://www.rfc-editor.org/rfc/rfc2606.txt&quot;&gt;RFC
+ 2606&lt;/a&gt;, Section 3.&lt;/p&gt;
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+<p>If you try to access a non-existent page, or if your Internet
+connection is down, you will get an <tt class="docutils literal"><span class="pre">urllib2.URLError</span></tt> instead.
+Incidentally, this is why the <tt class="docutils literal"><span class="pre">urllib2.urlopen</span></tt> function is better
+than the older <tt class="docutils literal"><span class="pre">urllib.urlopen</span></tt>, which would just silently retrieve
+a page containing the error message.</p>
+<p>You can easily imagine how to use urlopen to check your Web application:
+for instance, you could retrieve a page, extract all the links and
+check that they refer to existing pages; or you can verify that
+the retrieved page contains the right information, for instance
+by matching it with a regular expression. In practice, <tt class="docutils literal"><span class="pre">urlopen</span></tt>
+(possibly coupled with a third party HTML parsing tool, such as
+BeautifulSoup <a class="footnote-reference" href="#id13" id="id3" name="id3">[3]</a>) gives you all the fine granted control you may wish for.</p>
+<p>Moreover, <tt class="docutils literal"><span class="pre">urlopen</span></tt> gives you the possibility to make a POST:
+just pass the query
+string as second argument to urlopen. As an example, I will make a POST
+to <a class="reference" href="http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets">http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets</a>, which is a
+page containing the example form coming with Quixote, a nice small
+Pythonic Web Framework <a class="footnote-reference" href="#id14" id="id4" name="id4">[4]</a>.</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; page = urlopen(&quot;http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets&quot;,
+... &quot;name=MICHELE&amp;password=SECRET&amp;time=1118766328.56&quot;)
+&gt;&gt;&gt; print page.read()
+&lt;html&gt;
+&lt;head&gt;&lt;title&gt;Quixote Widget Demo&lt;/title&gt;&lt;/head&gt;
+&lt;body&gt;
+&lt;h2&gt;You entered the following values:&lt;/h2&gt;
+&lt;table&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;name&lt;/th&gt;&lt;td&gt;MICHELE&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;password&lt;/th&gt;&lt;td&gt;SECRET&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;confirmation&lt;/th&gt;&lt;td&gt;False&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;eye colour&lt;/th&gt;&lt;td&gt;&lt;i&gt;nothing&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;pizza size&lt;/th&gt;&lt;td&gt;&lt;i&gt;nothing&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;pizza toppings&lt;/th&gt;&lt;td&gt;&lt;i&gt;nothing&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
+&lt;/table&gt;
+&lt;p&gt;It took you 163.0 sec to fill out and submit the form&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+<p>Now <tt class="docutils literal"><span class="pre">page</span></tt> will contain the result of your POST. Notice that I had to
+pass explicitly a value for <tt class="docutils literal"><span class="pre">time</span></tt>, which is an hidden widget in
+the form.</p>
+<p>That was easy, isn't it?</p>
+<p>If the site requires authentication, things are slightly more complicated,
+but not much, at least if you have Python 2.4 installed.</p>
+</div>
+<div class="section" id="recipe-2-managing-authentication">
+<h2><a name="recipe-2-managing-authentication">Recipe 2: managing authentication</a></h2>
+<p>In order to manage cookie-based authentication procedures,
+you need to import a few utilities from urllib2:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from urllib2 import build_opener, HTTPCookieProcessor, Request
+</pre>
+<p>Notice that <tt class="docutils literal"><span class="pre">HTTPCookieProcessor</span></tt> is new in Python 2.4: if you have
+an older version of Python you need third party libraries
+such as <cite>ClientCookie</cite> <a class="footnote-reference" href="#id15" id="id5" name="id5">[5]</a>.</p>
+<p><tt class="docutils literal"><span class="pre">build_opener</span></tt> and <tt class="docutils literal"><span class="pre">HTTPCookieProcessor</span></tt> are used to create an opener
+object that can manage the cookies sent by the Web server:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; opener = build_opener(HTTPCookieProcessor)
+</pre>
+<p>The opener object has an <tt class="docutils literal"><span class="pre">open</span></tt> method that can be used to retrieve
+the Web page corresponding to a given request. The request itself is
+encapsulated in a <tt class="docutils literal"><span class="pre">Request</span></tt> object, which is built from the
+URL address, the query string, and some HTTP headers information.
+In order togenerate the query string, it is pretty convenient
+to use the <tt class="docutils literal"><span class="pre">urlencode</span></tt> function defined in <tt class="docutils literal"><span class="pre">urllib</span></tt> (<em>not</em> in <tt class="docutils literal"><span class="pre">urllib2</span></tt>):</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from urllib import urlencode
+</pre>
+<p><tt class="docutils literal"><span class="pre">urlencode</span></tt> generates the query string from a dictionary
+or a list of pairs, taking care of the quoting and escaping
+rules required by the HTTP protocol. For instance</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; urlencode(dict(user=&quot;MICHELE&quot;, password=&quot;SECRET&quot;))
+'password=SECRET&amp;user=MICHELE'
+</pre>
+<p>Notice that the order is not preserved when you use a dictionary
+(quite obviously), but this is usually
+not an issue. Now, let me define a helper function:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def urlopen2(url, data=None, user_agent='urlopen2'):
+... &quot;&quot;&quot;Can be used to retrieve cookie-enabled Web pages (when 'data' is
+... None) and to post Web forms (when 'data' is a list, tuple or dictionary
+... containing the parameters of the form).
+... &quot;&quot;&quot;
+... if hasattr(data, &quot;__iter__&quot;):
+... data = urllib.urlencode(data)
+... headers = {'User-Agent' : user_agent}
+... return opener.open(urllib2.Request(url, data, headers))
+</pre>
+<p>With <tt class="docutils literal"><span class="pre">urlopen2</span></tt>, you can POST your form in just one line.
+On the other hand, if the page you are posting to does not contain a form,
+you will get an HTTPError:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; urlopen2(&quot;http://www.example.com&quot;, dict(user=&quot;MICHELE&quot;, password=&quot;SECRET&quot;))
+Traceback (most recent call last):
+ ...
+HTTPError: HTTP Error 405: Method Not Allowed
+</pre>
+<p>If you just need to perform a GET, simply forget about the second argument
+to <tt class="docutils literal"><span class="pre">urlopen2</span></tt>, or use an empty dictionary or tuple. You can even fake a
+browser by passing a convenient user agent string, such as
+&quot;Mozilla&quot;, &quot;Internet Explorer&quot;, etc. This is pretty useful if you want
+to make sure that your application works with different browsers.</p>
+<p>Using these two recipes it is not that difficilt to write your own web
+testing framework. But you may be better off by leveraging the work
+of somebody else</p>
+</div>
+</div>
+<div class="section" id="testing-web-applications-the-easy-way-twill">
+<h1><a name="testing-web-applications-the-easy-way-twill">Testing web applications the easy way: twill</a></h1>
+<p>I am a big fan of mini languages, i.e. small languages
+written to perform a specific task (see for instance my O'Reilly article
+on the graph-generation language &quot;dot&quot; <a class="footnote-reference" href="#id16" id="id6" name="id6">[6]</a>). I was very happy when I
+discovered that there a nice little language expressely designed to test
+Web applications. Actually there are two implementations of it: Titus Brown's
+twill <a class="footnote-reference" href="#id17" id="id7" name="id7">[7]</a> and Cory Dodt's Python Browser Poseur, PBP <a class="footnote-reference" href="#id18" id="id8" name="id8">[8]</a>.</p>
+<p>PBP came first, but twill seems to be developing faster. At the time
+of this writing, twill is still pretty young (I am using
+version 0.7.1), but it already works pretty well in most situations.
+Both PBP and twill are based on tools by
+John J. Lee, i.e. mechanize (inspired by Perl), ClientForm and ClientCookie,
+that you may find at <a class="reference" href="http://wwwsearch.sourceforge.net">http://wwwsearch.sourceforge.net</a>. twill also use
+Paul McGuire's PyParsing <a class="footnote-reference" href="#id19" id="id9" name="id9">[9]</a>. However, you don't need to install these
+libraries: twill includes them as zipped libraries (leveraging on the
+new Python 2.3 <tt class="docutils literal"><span class="pre">zipimport</span></tt> module). As a consequence twill installation
+is absolutely obvious and painless (nothing more than the usual
+<tt class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">install</span></tt>).</p>
+<p>The simplest way to use twill is interactively from the command line.
+Let me show a simple session example:</p>
+<pre class="literal-block">
+$ twill-sh
+ -= Welcome to twill! =-
+
+current page: *empty page*
+
+&gt;&gt; go http://www.example.com
+==&gt; at http://www.example.com
+
+&gt;&gt; show
+&lt;HTML&gt;
+&lt;HEAD&gt;
+ &lt;TITLE&gt;Example Web Page&lt;/TITLE&gt;
+&lt;/HEAD&gt;
+&lt;body&gt;
+&lt;p&gt;You have reached this web page by typing &amp;quot;example.com&amp;quot;,
+&amp;quot;example.net&amp;quot;,
+ or &amp;quot;example.org&amp;quot; into your web browser.&lt;/p&gt;
+&lt;p&gt;These domain names are reserved for use in documentation and are not available
+ for registration. See &lt;a href=&quot;http://www.rfc-editor.org/rfc/rfc2606.txt&quot;&gt;RFC
+ 2606&lt;/a&gt;, Section 3.&lt;/p&gt;
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+<p>twill recognizes a few intuitive commands, such as</p>
+<blockquote>
+<pre class="literal-block">
+go, show, find, notfind, echo, code, back, reload, agent, follow
+</pre>
+</blockquote>
+<p>and few others. The example shows how you can access
+a particular HTML page and display its content.</p>
+<p>The <tt class="docutils literal"><span class="pre">find</span></tt> command matches the page against a regular
+expression: thus</p>
+<pre class="literal-block">
+&gt;&gt; find(&quot;Example Web Page&quot;)
+</pre>
+<p>is a test asserting that the current page contains what we expect.
+Similarly, the <tt class="docutils literal"><span class="pre">notfind</span></tt> command asserts that the current page does
+not match the given regular expression.</p>
+<p>The other twill commands are pretty obvious: <tt class="docutils literal"><span class="pre">echo</span> <span class="pre">&lt;message&gt;</span></tt>
+prints a message on standard output, <tt class="docutils literal"><span class="pre">code</span> <span class="pre">&lt;http_error_code&gt;</span></tt> checks
+that you are getting the right HTTP error code (200 if everything is
+alright), <tt class="docutils literal"><span class="pre">back</span></tt> allows you to go back to the previously visited page,
+<tt class="docutils literal"><span class="pre">reload</span></tt> reloads the current page, <tt class="docutils literal"><span class="pre">agent</span> <span class="pre">&lt;user-agent&gt;</span></tt> allows you
+to change the current user agent, thus faking different browsers,
+<tt class="docutils literal"><span class="pre">follow</span> <span class="pre">&lt;regex&gt;</span></tt> finds the first matching link on the page and visit it.</p>
+<p>The full lists of the commands can be obtained
+by giving <tt class="docutils literal"><span class="pre">help</span></tt> at the prompt; <tt class="docutils literal"><span class="pre">EOF</span></tt> of <tt class="docutils literal"><span class="pre">CTRL-D</span></tt> allows you to exit.</p>
+<p>Once you have tested your application interactively, it is pretty easy
+to cut &amp; paste your twill session and convert it in a twill script.
+Then, you can run your twill script in a batch process:</p>
+<pre class="literal-block">
+$ twill-sh mytests.twill
+</pre>
+<p>As you may imagine, you can put more than one script in the command line and
+test many of them at the same time. Since twill is written in Python, you
+can control it from Python entirely, and you can even extends its command
+set just by adding new commands in the <tt class="docutils literal"><span class="pre">commands.py</span></tt> module.</p>
+<p>At the moment, twill is pretty young and it does not have the
+capability to convert scripts in unit tests automatically, so that
+you can easily run entire suites of regression tests. However,
+it is not that difficult to implement that capability yourself,
+and it is not unlikely that twill will gain good integration with
+unittest and doctest in the future.</p>
+<div class="section" id="retrieving-and-submitting-web-forms">
+<h2><a name="retrieving-and-submitting-web-forms">Retrieving and submitting web forms</a></h2>
+<p>twill is especially good at retrieving and submitting web forms. The
+form-related functionality is implemented with the following commands:</p>
+<ul class="simple">
+<li>showforms</li>
+<li>formvalue &lt;form_id&gt; &lt;name&gt; &lt;value&gt;</li>
+<li>submit &lt;button_id&gt;</li>
+<li>formclear &lt;form_id&gt;</li>
+</ul>
+<p>Explaining the commands is pretty straightforward.</p>
+<p><tt class="docutils literal"><span class="pre">showforms</span></tt> shows the forms contained in a web page. For instance, try
+the following:</p>
+<pre class="literal-block">
+&gt;&gt; go http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+&gt;&gt; showforms
+Form #1
+## __Name______ __Type___ __ID________ __Value__________________
+ name text (None)
+ password password (None)
+ confirm checkbox (None) [] of ['yes']
+ colour radio (None) [] of ['green', 'blue', 'brown', 'ot ...
+ size select (None) ['Medium (10&quot;)'] of ['Tiny (4&quot;)', 'S ...
+ toppings select (None) ['cheese'] of ['cheese', 'pepperoni' ...
+ time hidden (None) 1118768019.17
+1 submit (None) Submit
+current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+</pre>
+<p>Notice that <tt class="docutils literal"><span class="pre">twill</span></tt> makes a good job at emulating a browser, so it fills
+the hidden <tt class="docutils literal"><span class="pre">time</span></tt> widget automatically, whereas
+we had to fill it explicitely with <tt class="docutils literal"><span class="pre">urlopen</span></tt>.</p>
+<p>Unnamed forms get an ordinal number to be used as form id
+in the <tt class="docutils literal"><span class="pre">formvalue</span></tt> command, which fill a field
+of the specified form with a given value. You can give many
+formvalue commands in succession; if you are a lazy typist
+you can also use <tt class="docutils literal"><span class="pre">fv</span></tt> as an alias for <tt class="docutils literal"><span class="pre">formvalue</span></tt>:</p>
+<pre class="literal-block">
+&gt;&gt; fv 1 name MICHELES
+current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+&gt;&gt; fv 1 password SECRET
+current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+</pre>
+<p><tt class="docutils literal"><span class="pre">formclear</span></tt> reset all the fields in a form and <tt class="docutils literal"><span class="pre">submit</span></tt> allows
+you to press a <tt class="docutils literal"><span class="pre">submit</span></tt> botton, thus submitting the form:</p>
+<pre class="literal-block">
+&gt;&gt; submit 1
+current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+</pre>
+<p>A simple <tt class="docutils literal"><span class="pre">show</span></tt> will convince you that the forms has been submitted.
+The best way to understand how does it work is just experimenting
+on your own. The base distribution contains a few examples you may
+play with.</p>
+</div>
+</div>
+<div class="section" id="enlarging-the-horizon">
+<h1><a name="enlarging-the-horizon">Enlarging the horizon</a></h1>
+<p>In this article I have shown two easy ways to test your web
+application: by hand, using urllib, or with a simple tool such as twill.
+There is more under the sun. Much more. There are many sophisticated
+Web testing frameworks out there, including enterprise-oriented ones,
+with lots of functionalities and a steep learning curve. Here, on purpose,
+I have decided to start from the small, and to discuss the topic from a
+do-it-yourself attitude, since sometimes the simplest things works best: or
+because you don't need the sophistication, or because your preferred
+testing framework lacks the functionality you wish for, or because
+it is just buggy. If you need something more sophisticated, a great source for
+everything testing-related is Grig Gheorghiu's blog:</p>
+<p><a class="reference" href="http://agiletesting.blogspot.com/2005/02/articles-and-tutorials.html">http://agiletesting.blogspot.com/2005/02/articles-and-tutorials.html</a></p>
+<p>A new framework which is especially interesting is Selenium, which
+is also used to test Plone applications. Selenium is <em>really</em> spectacular,
+since it is Javascript based and it really tests your browser, clicking
+on links, submitting forms, opening popup windows, all in real time. It
+completely emulates the user experience, at highest possible level.
+It also gives
+you all kind of bells and whistles, eye candies and colored HTML output
+(which you may like or not, but that surely will impress your customer
+if you are going to demonstrate him that the application is conform to
+the specifications).
+I cannot render justice to Selenium in a few lines and maybe I should write
+a whole new paper on it, when I find the time. For the moment, however I make
+no promises and I refer you to the available documentation <a class="footnote-reference" href="#id20" id="id10" name="id10">[10]</a>.</p>
+</div>
+<div class="section" id="references">
+<h1><a name="references">References</a></h1>
+<table class="docutils footnote" frame="void" id="id11" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1" name="id11">[1]</a></td><td>I am an early adopter and supporter of doctests, see for
+instance my talk at the ACCU conference,
+<a class="reference" href="http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_doctest.zip">http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_doctest.zip</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id12" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2" name="id12">[2]</a></td><td>For the <tt class="docutils literal"><span class="pre">urllib</span></tt> libraries see the Python docs
+<a class="reference" href="http://docs.python.org/lib/module-urllib2.html">http://docs.python.org/lib/module-urllib2.html</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id13" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3" name="id13">[3]</a></td><td>For <cite>BeautifulSoup``</cite> see <a class="reference" href="http://www.crummy.com/software/BeautifulSoup">http://www.crummy.com/software/BeautifulSoup</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id14" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id4" name="id14">[4]</a></td><td>For Quixote see <a class="reference" href="http://www.mems-exchange.org/software/quixote">http://www.mems-exchange.org/software/quixote</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id15" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id5" name="id15">[5]</a></td><td>For <tt class="docutils literal"><span class="pre">mechanize</span></tt> and <tt class="docutils literal"><span class="pre">ClientCookie</span></tt> see
+<a class="reference" href="http://wwwsearch.sourceforge.net">http://wwwsearch.sourceforge.net</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id16" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id6" name="id16">[6]</a></td><td><a class="reference" href="http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html">http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id17" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id7" name="id17">[7]</a></td><td>For <tt class="docutils literal"><span class="pre">twill</span></tt> see
+<a class="reference" href="http://darcs.idyll.org/%7Et/projects/twill/README.html">http://darcs.idyll.org/%7Et/projects/twill/README.html</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id18" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id8" name="id18">[8]</a></td><td>For the <tt class="docutils literal"><span class="pre">Python</span> <span class="pre">Browser</span> <span class="pre">Poseur</span></tt> see <a class="reference" href="http://pbp.berlios.de">http://pbp.berlios.de</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id19" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id9" name="id19">[9]</a></td><td>For <tt class="docutils literal"><span class="pre">PyParsing</span></tt> see <a class="reference" href="http://pyparsing.sourceforge.net">http://pyparsing.sourceforge.net</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id20" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id10" name="id20">[10]</a></td><td>For <tt class="docutils literal"><span class="pre">Selenium</span></tt> see <a class="reference" href="http://selenium.thoughtworks.com/index.html">http://selenium.thoughtworks.com/index.html</a></td></tr>
+</tbody>
+</table>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/twill/testing_web_app.tex b/pypers/twill/testing_web_app.tex
new file mode 100755
index 0000000..3822551
--- /dev/null
+++ b/pypers/twill/testing_web_app.tex
@@ -0,0 +1,725 @@
+\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{Testing Web Applications with Python}
+\author{}
+\date{}
+\hypersetup{
+pdftitle={Testing Web Applications with Python},
+pdfauthor={Michele Simionato}
+}
+\raggedbottom
+\begin{document}
+\maketitle
+
+%___________________________________________________________________________
+\begin{center}
+\begin{tabularx}{\docinfowidth}{lX}
+\textbf{Author}: &
+ Michele Simionato \\
+\textbf{Date}: &
+ June 2005 \\
+\end{tabularx}
+\end{center}
+
+\setlength{\locallinewidth}{\linewidth}
+
+
+%___________________________________________________________________________
+
+\hypertarget{introduction}{}
+\pdfbookmark[0]{Introduction}{introduction}
+\section*{Introduction}
+
+You have just finished your beautiful Web application, with
+lots of pages, links, forms and buttons; you have spent weeks
+making sure that everything works fine,
+that the special cases are handled correctly, that the user cannot
+crash your system whatever she does.
+
+Now you are happy and you are ready to ship. But at the last minute
+the customer ask for a change: you have the time to apply the change,
+but not the time - nor the will - to pass trough another testing
+ordalia. So you ship anyway, hoping that your last little fix did not break
+some other part of the application. The result is that the hidden bug
+shows up at the first day of usage.
+
+If you recognized yourself in this situation then this paper
+is for you, keep reading. If not, well, keep reading anyway,
+I am sure you will find something interesting, among the following topics:
+\begin{itemize}
+\item {}
+how to separate unit tests from functional tests;
+
+\item {}
+how to test web applications (written in any language) using
+standard Python libraries;
+
+\item {}
+how to use twill, a nice and easy to learn web testing tool.
+
+\end{itemize}
+
+
+%___________________________________________________________________________
+
+\hypertarget{to-test-or-not-to-test-this-is-the-problem}{}
+\pdfbookmark[0]{To test or not to test, this is the problem}{to-test-or-not-to-test-this-is-the-problem}
+\section*{To test or not to test, this is the problem}
+
+Let me begin with a brief personal recollection of how I became
+interested in testing methodologies, and of what I have learned in
+the last couple of years.
+
+I have been aware of the importance of testing from the beginning, and
+I have heard about automatic testing for years. However, having
+heard about automatic testing is not the same as doing automatic testing,
+and not the same as doing automatic testing well.
+It takes some time and experience to get into the testing mood, as well
+as the ability to challenge some widespread misconceptions.
+
+For instance, when I began studying test driven methodologies,
+I had gathered two wrong ideas:
+\begin{itemize}
+\item {}
+that testing was all about unit testing;
+
+\item {}
+that the more you test, the better.
+
+\end{itemize}
+
+After some experience I quickly realized myself that unit tests were
+not the only tool, nor the best tool to effectively test my application [\hyperlink{id11}{1}].
+But to overcome the second misconception, I needed some help.
+
+The help come from an XP seminar I attended last year, were I
+actually asked the question ``how do I test the user interface of
+a Web application, i.e. that when the user click on a given page she gets
+the expected result?''.
+
+The answer was: ``You don't. Why do you want to test that your browser is
+working?''
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-case-for-not-testing-everything}{}
+\pdfbookmark[1]{The case for not testing everything}{the-case-for-not-testing-everything}
+\subsection*{The case for not testing everything}
+
+The answer made me rethink many things. Obviously I was well aware
+from the beginning that full test coverage is a myth, still I thought
+one programmer should try to test as much as he can.
+
+But this is not the right approach. Instead, it is important to
+discriminate about the infinite amount of things that could be
+tested, and focus on the things that are of your responsability.
+
+If your customer wants functionality X, you must be sure functionality X
+is there. But if in order to get functionality X you need to rely on
+functionalities X1, X2 ,... XN, you don't need to test for all
+of them. You test only the functionality you are payed to
+implement. You don't test that the browser is working, it is
+not your job.
+
+For instance, in the case of a Web application,
+you can interact with it \emph{indirectly}, via the HTTP protocol, or \emph{directly},
+via the internal API. If you check that when the user clicks
+on button B method M is called and the result R is displayed, you are
+testing both your application \emph{and} the correctness of the
+HTTP protocol implementation both in the browser and in the server. This is
+way too much. You may rely on the HTTP protocol and just test the API, i.e
+just test that if method M is called the right result R is returned.
+
+Of course, a similar viewpoint is applicable to GUIs. In the same
+vein, you must test that the interface to the DB you wrote is working, but
+you don't need to test that the database itself is working, this
+is not your responsability.
+
+The basic point is to separate the indirect testing of the user
+interface - via the HTTP protocol - from the testing of the inner API.
+To this aim, it is important to write your
+application in such a way that you can test the logic independently
+from the user interface. Working in this way you also have the additional
+bonus that you can change the user interface later, without having to
+change a single tests for the logic part.
+
+The problem is that typically the customer will give his specifications
+in terms of the user interface. He will tell you ``There must be a page where
+the user will enter her order, then she will enter her credit card number,
+then the system must send a confirmation email, ...''
+
+This kind of specification is a kind of very high level test - a
+functional test - which has to be converted into a low-level test:
+for instance you may have unit testing telling you that the ordered
+item has been registered in the database, that the
+\texttt{send{\_}confirmation{\_}email method} has been called etc.
+
+The conversion requires some thinking and practice and it an art more
+than a science. Actually I think that the art of testing is not in \emph{how}
+to test, but in \emph{what} to test. The best advice and best answer to somebody
+asking about ``how do I test a Web application?'' is probably ``make a priority
+lists of the things you would like to test and test as little as possible''.
+
+For instance, one should never tests the details of the
+implementation. If you make this mistake (as I did at the beginning)
+your tests will get in your way at refactoring time, i.e. they will
+have exactly the opposite of the intended effect. Generally speaking,
+good advices are: don't spend time testing third party software, don't
+waste time testing code which API is likely to change, split
+the UI testing from the application logic testing.
+
+Ideally you should be able to determine what is the minimal set
+of tests needed to make your customer happy, and restrict yourself to
+those tests.
+
+
+%___________________________________________________________________________
+
+\hypertarget{the-case-for-testing-everything}{}
+\pdfbookmark[1]{The case for testing everything}{the-case-for-testing-everything}
+\subsection*{The case for testing everything}
+
+The previous advice is nice and reasonable, especially in an ideal world
+where third party software is bug free and everything is configured correctly.
+Unfortunately, the real world is a bit different.
+
+For instance you must be aware that your application
+does not work on some buggy browser, or that it cannot work in specific
+circumstances with some database. Also, you may have a nice and
+comprehensive test suite which runs flawlessly
+on your development machine, but still
+that the application may not work correctly when installed on a different
+machine, because the database could be installed improperly, or
+the mail server settings could be incorrect, or the Internet
+connection could be down, etc. In the same vein, if you want
+to really be sure that if the user - using a specifing browser in a
+specific environment - clicks on that button she gets that result,
+you have to emulate exactly that situation.
+
+It looks like we are back to square one, i.e. the need of testing everything.
+But we have learned something in the process: whereas \emph{in principle}
+you would like to test everything, \emph{in practice} you can effectively
+prioritize your tests, focusing on some more than on others,and
+splitting them in separate categories to be run separately at
+different times.
+
+You definitely need to test that the
+application is working as intended when deployed on a different
+machine: and from the failures to these installation tests you may also infer
+what is wrong and correct the problem. These installation tests
+- tests of the environment where your software is running - must
+be kept decoupled from the unit tests checking the
+application logic. If you are sure that the logic is right, then you are
+sure also sure that the problems are in the environment, and you can
+focus your debugging skills in the right direction.
+
+In any case, you need to have both high level (functional, integration,
+installation) tests and low level tests (unit tests, doctests). High level
+tests include tests of the user interface. In particular, you need a test to
+make sure that if an user click X he gets Y, so you are sure that the
+Internet connection, the web server, the database, the mail
+server, your application, the browser, all work nicely
+together. But you should not focus on these global kind
+of tests. You don't need to write a thousands of these
+high level tests, if you already have many specific low-level
+tests checking that the logic and the various components
+of your application are working.
+
+
+%___________________________________________________________________________
+
+\hypertarget{how-to-test-the-user-interface}{}
+\pdfbookmark[0]{How to test the user interface}{how-to-test-the-user-interface}
+\section*{How to test the user interface}
+
+Having structured your application properly, you will need a smaller
+number of user interface tests, but still you will need at
+least a few. How do you write these tests then?
+
+There are two possibilities: the hard way and the easy way.
+
+The hard way is just doing everything by hand, by using your
+favorite programming language Web libraries to perform GET and POST
+requests and to verify the results. The easy way is to leverage on
+tools built by others. Of course,internally these tools work just by calling
+the low level libraries, so it is convenient to say a couple of words on the
+hard way, just to understand what is going on, in case the high level tool
+give you some problem. Moreover, there is always the possibility than
+you need something more customized, and knowledge of
+the low level libraries can be precious.
+
+The interaction between the user and a Web application
+passes through the HTTP protocol, so it is perfectly possible
+to simulate the action of an user clicking on a browser
+just by sending to the server an equivalent HTTP request (let me
+ignore the existence of Javascript for the moment).
+
+Any modern programming language has libraries to interact with the
+HTTP protocol, but here I will give my examples in Python, since Python
+is both a common language for Web programming and a readable one. In Python
+the interaction with the Web is managed via the urllib libraries [\hyperlink{id12}{2}].
+You have two of them: urllib, which can be used in absence of authentication,
+and urllib2 which can also manage cookie-based authentication. A complete
+discussion of these two libraries would take a long
+time, but explaining the basics is pretty simple. I will just give
+a couple of recipes based on urllib2, the newest and most powerful library.
+
+I will notice here that the support for cookies in Python 2.4
+has improved (essentially by including the third party ClientCookie
+library) so you may not be aware of the trick I am going to explain, even
+if have used the urllib libraries in the past. So, don't skip the
+next two sections ;)
+
+
+%___________________________________________________________________________
+
+\hypertarget{recipe-1-how-to-send-get-and-post-requests}{}
+\pdfbookmark[1]{Recipe 1: how to send GET and POST requests}{recipe-1-how-to-send-get-and-post-requests}
+\subsection*{Recipe 1: how to send GET and POST requests}
+
+Suppose you want to access a site which does not require authentication.
+Then making a GET request is pretty easy, just type at the intepreter
+prompt
+\begin{verbatim}>>> from urllib2 import urlopen
+>>> page = urlopen("http://www.example.com")\end{verbatim}
+
+Now you have a file like-object which contains the HTML code
+of the page \href{http://www.example.com}{http://www.example.com}:
+\begin{verbatim}>>> for line in page: print line,
+<HTML>
+<HEAD>
+ <TITLE>Example Web Page</TITLE>
+</HEAD>
+<body>
+<p>You have reached this web page by typing &quot;example.com&quot;,
+&quot;example.net&quot;,
+ or &quot;example.org&quot; into your web browser.</p>
+<p>These domain names are reserved for use in documentation and are not available
+ for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC
+ 2606</a>, Section 3.</p>
+</BODY>
+</HTML>\end{verbatim}
+
+If you try to access a non-existent page, or if your Internet
+connection is down, you will get an \texttt{urllib2.URLError} instead.
+Incidentally, this is why the \texttt{urllib2.urlopen} function is better
+than the older \texttt{urllib.urlopen}, which would just silently retrieve
+a page containing the error message.
+
+You can easily imagine how to use urlopen to check your Web application:
+for instance, you could retrieve a page, extract all the links and
+check that they refer to existing pages; or you can verify that
+the retrieved page contains the right information, for instance
+by matching it with a regular expression. In practice, \texttt{urlopen}
+(possibly coupled with a third party HTML parsing tool, such as
+BeautifulSoup [\hyperlink{id13}{3}]) gives you all the fine granted control you may wish for.
+
+Moreover, \texttt{urlopen} gives you the possibility to make a POST:
+just pass the query
+string as second argument to urlopen. As an example, I will make a POST
+to \href{http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets}{http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets}, which is a
+page containing the example form coming with Quixote, a nice small
+Pythonic Web Framework [\hyperlink{id14}{4}].
+\begin{verbatim}>>> page = urlopen("http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets",
+... "name=MICHELE&password=SECRET&time=1118766328.56")
+>>> print page.read()
+<html>
+<head><title>Quixote Widget Demo</title></head>
+<body>
+<h2>You entered the following values:</h2>
+<table>
+ <tr><th align="left">name</th><td>MICHELE</td></tr>
+ <tr><th align="left">password</th><td>SECRET</td></tr>
+ <tr><th align="left">confirmation</th><td>False</td></tr>
+ <tr><th align="left">eye colour</th><td><i>nothing</i></td></tr>
+ <tr><th align="left">pizza size</th><td><i>nothing</i></td></tr>
+ <tr><th align="left">pizza toppings</th><td><i>nothing</i></td></tr>
+</table>
+<p>It took you 163.0 sec to fill out and submit the form</p>
+</body>
+</html>\end{verbatim}
+
+Now \texttt{page} will contain the result of your POST. Notice that I had to
+pass explicitly a value for \texttt{time}, which is an hidden widget in
+the form.
+
+That was easy, isn't it?
+
+If the site requires authentication, things are slightly more complicated,
+but not much, at least if you have Python 2.4 installed.
+
+
+%___________________________________________________________________________
+
+\hypertarget{recipe-2-managing-authentication}{}
+\pdfbookmark[1]{Recipe 2: managing authentication}{recipe-2-managing-authentication}
+\subsection*{Recipe 2: managing authentication}
+
+In order to manage cookie-based authentication procedures,
+you need to import a few utilities from urllib2:
+\begin{verbatim}>>> from urllib2 import build_opener, HTTPCookieProcessor, Request\end{verbatim}
+
+Notice that \texttt{HTTPCookieProcessor} is new in Python 2.4: if you have
+an older version of Python you need third party libraries
+such as \titlereference{ClientCookie} [\hyperlink{id15}{5}].
+
+\texttt{build{\_}opener} and \texttt{HTTPCookieProcessor} are used to create an opener
+object that can manage the cookies sent by the Web server:
+\begin{verbatim}>>> opener = build_opener(HTTPCookieProcessor)\end{verbatim}
+
+The opener object has an \texttt{open} method that can be used to retrieve
+the Web page corresponding to a given request. The request itself is
+encapsulated in a \texttt{Request} object, which is built from the
+URL address, the query string, and some HTTP headers information.
+In order togenerate the query string, it is pretty convenient
+to use the \texttt{urlencode} function defined in \texttt{urllib} (\emph{not} in \texttt{urllib2}):
+\begin{verbatim}>>> from urllib import urlencode\end{verbatim}
+
+\texttt{urlencode} generates the query string from a dictionary
+or a list of pairs, taking care of the quoting and escaping
+rules required by the HTTP protocol. For instance
+\begin{verbatim}>>> urlencode(dict(user="MICHELE", password="SECRET"))
+'password=SECRET&user=MICHELE'\end{verbatim}
+
+Notice that the order is not preserved when you use a dictionary
+(quite obviously), but this is usually
+not an issue. Now, let me define a helper function:
+\begin{verbatim}>>> def urlopen2(url, data=None, user_agent='urlopen2'):
+... """Can be used to retrieve cookie-enabled Web pages (when 'data' is
+... None) and to post Web forms (when 'data' is a list, tuple or dictionary
+... containing the parameters of the form).
+... """
+... if hasattr(data, "__iter__"):
+... data = urllib.urlencode(data)
+... headers = {'User-Agent' : user_agent}
+... return opener.open(urllib2.Request(url, data, headers))\end{verbatim}
+
+With \texttt{urlopen2}, you can POST your form in just one line.
+On the other hand, if the page you are posting to does not contain a form,
+you will get an HTTPError:
+\begin{verbatim}>>> urlopen2("http://www.example.com", dict(user="MICHELE", password="SECRET"))
+Traceback (most recent call last):
+ ...
+HTTPError: HTTP Error 405: Method Not Allowed\end{verbatim}
+
+If you just need to perform a GET, simply forget about the second argument
+to \texttt{urlopen2}, or use an empty dictionary or tuple. You can even fake a
+browser by passing a convenient user agent string, such as
+``Mozilla'', ``Internet Explorer'', etc. This is pretty useful if you want
+to make sure that your application works with different browsers.
+
+Using these two recipes it is not that difficilt to write your own web
+testing framework. But you may be better off by leveraging the work
+of somebody else
+
+
+%___________________________________________________________________________
+
+\hypertarget{testing-web-applications-the-easy-way-twill}{}
+\pdfbookmark[0]{Testing web applications the easy way: twill}{testing-web-applications-the-easy-way-twill}
+\section*{Testing web applications the easy way: twill}
+
+I am a big fan of mini languages, i.e. small languages
+written to perform a specific task (see for instance my O'Reilly article
+on the graph-generation language ``dot'' [\hyperlink{id16}{6}]). I was very happy when I
+discovered that there a nice little language expressely designed to test
+Web applications. Actually there are two implementations of it: Titus Brown's
+twill [\hyperlink{id17}{7}] and Cory Dodt's Python Browser Poseur, PBP [\hyperlink{id18}{8}].
+
+PBP came first, but twill seems to be developing faster. At the time
+of this writing, twill is still pretty young (I am using
+version 0.7.1), but it already works pretty well in most situations.
+Both PBP and twill are based on tools by
+John J. Lee, i.e. mechanize (inspired by Perl), ClientForm and ClientCookie,
+that you may find at \href{http://wwwsearch.sourceforge.net}{http://wwwsearch.sourceforge.net}. twill also use
+Paul McGuire's PyParsing [\hyperlink{id19}{9}]. However, you don't need to install these
+libraries: twill includes them as zipped libraries (leveraging on the
+new Python 2.3 \texttt{zipimport} module). As a consequence twill installation
+is absolutely obvious and painless (nothing more than the usual
+\texttt{python setup.py install}).
+
+The simplest way to use twill is interactively from the command line.
+Let me show a simple session example:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}~twill-sh~\\
+~-=~Welcome~to~twill!~=-~\\
+~\\
+current~page:~~*empty~page*~\\
+~~~~~~~~~~~~~~~~~~\\
+>{}>~go~http://www.example.com~\\
+==>~at~http://www.example.com~\\
+~\\
+>{}>~show~\\
+<HTML>~\\
+<HEAD>~\\
+~~<TITLE>Example~Web~Page</TITLE>~\\
+</HEAD>~\\
+<body>~\\
+<p>You~have~reached~this~web~page~by~typing~{\&}quot;example.com{\&}quot;,~\\
+{\&}quot;example.net{\&}quot;,~\\
+~~or~{\&}quot;example.org{\&}quot;~into~your~web~browser.</p>~\\
+<p>These~domain~names~are~reserved~for~use~in~documentation~and~are~not~available~\\
+~~for~registration.~See~<a~href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC~\\
+~~2606</a>,~Section~3.</p>~\\
+</BODY>~\\
+</HTML>
+}\end{quote}
+
+twill recognizes a few intuitive commands, such as
+\begin{quote}
+\begin{quote}{\ttfamily \raggedright \noindent
+go,~show,~find,~notfind,~echo,~code,~back,~reload,~agent,~follow
+}\end{quote}
+\end{quote}
+
+and few others. The example shows how you can access
+a particular HTML page and display its content.
+
+The \texttt{find} command matches the page against a regular
+expression: thus
+\begin{quote}{\ttfamily \raggedright \noindent
+>{}>~find("Example~Web~Page")
+}\end{quote}
+
+is a test asserting that the current page contains what we expect.
+Similarly, the \texttt{notfind} command asserts that the current page does
+not match the given regular expression.
+
+The other twill commands are pretty obvious: \texttt{echo <message>}
+prints a message on standard output, \texttt{code <http{\_}error{\_}code>} checks
+that you are getting the right HTTP error code (200 if everything is
+alright), \texttt{back} allows you to go back to the previously visited page,
+\texttt{reload} reloads the current page, \texttt{agent <user-agent>} allows you
+to change the current user agent, thus faking different browsers,
+\texttt{follow <regex>} finds the first matching link on the page and visit it.
+
+The full lists of the commands can be obtained
+by giving \texttt{help} at the prompt; \texttt{EOF} of \texttt{CTRL-D} allows you to exit.
+
+Once you have tested your application interactively, it is pretty easy
+to cut {\&} paste your twill session and convert it in a twill script.
+Then, you can run your twill script in a batch process:
+\begin{quote}{\ttfamily \raggedright \noindent
+{\$}~twill-sh~mytests.twill
+}\end{quote}
+
+As you may imagine, you can put more than one script in the command line and
+test many of them at the same time. Since twill is written in Python, you
+can control it from Python entirely, and you can even extends its command
+set just by adding new commands in the \texttt{commands.py} module.
+
+At the moment, twill is pretty young and it does not have the
+capability to convert scripts in unit tests automatically, so that
+you can easily run entire suites of regression tests. However,
+it is not that difficult to implement that capability yourself,
+and it is not unlikely that twill will gain good integration with
+unittest and doctest in the future.
+
+
+%___________________________________________________________________________
+
+\hypertarget{retrieving-and-submitting-web-forms}{}
+\pdfbookmark[1]{Retrieving and submitting web forms}{retrieving-and-submitting-web-forms}
+\subsection*{Retrieving and submitting web forms}
+
+twill is especially good at retrieving and submitting web forms. The
+form-related functionality is implemented with the following commands:
+\begin{itemize}
+\item {}
+showforms
+
+\item {}
+formvalue {\textless}form{\_}id{\textgreater} {\textless}name{\textgreater} {\textless}value{\textgreater}
+
+\item {}
+submit {\textless}button{\_}id{\textgreater}
+
+\item {}
+formclear {\textless}form{\_}id{\textgreater}
+
+\end{itemize}
+
+Explaining the commands is pretty straightforward.
+
+\texttt{showforms} shows the forms contained in a web page. For instance, try
+the following:
+\begin{quote}{\ttfamily \raggedright \noindent
+>{}>~go~http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets~\\
+>{}>~showforms~\\
+Form~{\#}1~\\
+{\#}{\#}~{\_}{\_}Name{\_}{\_}{\_}{\_}{\_}{\_}~{\_}{\_}Type{\_}{\_}{\_}~{\_}{\_}ID{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~{\_}{\_}Value{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}{\_}~\\
+~~~name~~~~~~~~~text~~~~~~(None)~\\
+~~~password~~~~~password~~(None)~\\
+~~~confirm~~~~~~checkbox~~(None)~~~~~~~{[}]~of~{[}'yes']~\\
+~~~colour~~~~~~~radio~~~~~(None)~~~~~~~{[}]~of~{[}'green',~'blue',~'brown',~'ot~...~\\
+~~~size~~~~~~~~~select~~~~(None)~~~~~~~{[}'Medium~(10")']~of~{[}'Tiny~(4")',~'S~...~\\
+~~~toppings~~~~~select~~~~(None)~~~~~~~{[}'cheese']~of~{[}'cheese',~'pepperoni'~...~\\
+~~~time~~~~~~~~~hidden~~~~(None)~~~~~~~1118768019.17~\\
+1~~~~~~~~~~~~~~~submit~~~~(None)~~~~~~~Submit~\\
+current~page:~http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets
+}\end{quote}
+
+Notice that \texttt{twill} makes a good job at emulating a browser, so it fills
+the hidden \texttt{time} widget automatically, whereas
+we had to fill it explicitely with \texttt{urlopen}.
+
+Unnamed forms get an ordinal number to be used as form id
+in the \texttt{formvalue} command, which fill a field
+of the specified form with a given value. You can give many
+formvalue commands in succession; if you are a lazy typist
+you can also use \texttt{fv} as an alias for \texttt{formvalue}:
+\begin{quote}{\ttfamily \raggedright \noindent
+>{}>~fv~1~name~MICHELES~\\
+current~page:~http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets~\\
+>{}>~fv~1~password~SECRET~\\
+current~page:~http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets
+}\end{quote}
+
+\texttt{formclear} reset all the fields in a form and \texttt{submit} allows
+you to press a \texttt{submit} botton, thus submitting the form:
+\begin{quote}{\ttfamily \raggedright \noindent
+>{}>~submit~1~\\
+current~page:~http://issola.caltech.edu/{\textasciitilde}t/qwsgi/qwsgi-demo.cgi/widgets
+}\end{quote}
+
+A simple \texttt{show} will convince you that the forms has been submitted.
+The best way to understand how does it work is just experimenting
+on your own. The base distribution contains a few examples you may
+play with.
+
+
+%___________________________________________________________________________
+
+\hypertarget{enlarging-the-horizon}{}
+\pdfbookmark[0]{Enlarging the horizon}{enlarging-the-horizon}
+\section*{Enlarging the horizon}
+
+In this article I have shown two easy ways to test your web
+application: by hand, using urllib, or with a simple tool such as twill.
+There is more under the sun. Much more. There are many sophisticated
+Web testing frameworks out there, including enterprise-oriented ones,
+with lots of functionalities and a steep learning curve. Here, on purpose,
+I have decided to start from the small, and to discuss the topic from a
+do-it-yourself attitude, since sometimes the simplest things works best: or
+because you don't need the sophistication, or because your preferred
+testing framework lacks the functionality you wish for, or because
+it is just buggy. If you need something more sophisticated, a great source for
+everything testing-related is Grig Gheorghiu's blog:
+
+\href{http://agiletesting.blogspot.com/2005/02/articles-and-tutorials.html}{http://agiletesting.blogspot.com/2005/02/articles-and-tutorials.html}
+
+A new framework which is especially interesting is Selenium, which
+is also used to test Plone applications. Selenium is \emph{really} spectacular,
+since it is Javascript based and it really tests your browser, clicking
+on links, submitting forms, opening popup windows, all in real time. It
+completely emulates the user experience, at highest possible level.
+It also gives
+you all kind of bells and whistles, eye candies and colored HTML output
+(which you may like or not, but that surely will impress your customer
+if you are going to demonstrate him that the application is conform to
+the specifications).
+I cannot render justice to Selenium in a few lines and maybe I should write
+a whole new paper on it, when I find the time. For the moment, however I make
+no promises and I refer you to the available documentation [\hyperlink{id20}{10}].
+
+
+%___________________________________________________________________________
+
+\hypertarget{references}{}
+\pdfbookmark[0]{References}{references}
+\section*{References}
+\begin{figure}[b]\hypertarget{id11}[1]
+I am an early adopter and supporter of doctests, see for
+instance my talk at the ACCU conference,
+\href{http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_doctest.zip}{http://www.reportlab.org/{\textasciitilde}andy/accu2005/pyuk2005{\_}simionato{\_}doctest.zip}
+\end{figure}
+\begin{figure}[b]\hypertarget{id12}[2]
+For the \texttt{urllib} libraries see the Python docs
+\href{http://docs.python.org/lib/module-urllib2.html}{http://docs.python.org/lib/module-urllib2.html}
+\end{figure}
+\begin{figure}[b]\hypertarget{id13}[3]
+For \titlereference{BeautifulSoup``} see \href{http://www.crummy.com/software/BeautifulSoup}{http://www.crummy.com/software/BeautifulSoup}
+\end{figure}
+\begin{figure}[b]\hypertarget{id14}[4]
+For Quixote see \href{http://www.mems-exchange.org/software/quixote}{http://www.mems-exchange.org/software/quixote}
+\end{figure}
+\begin{figure}[b]\hypertarget{id15}[5]
+For \texttt{mechanize} and \texttt{ClientCookie} see
+\href{http://wwwsearch.sourceforge.net}{http://wwwsearch.sourceforge.net}
+\end{figure}
+\begin{figure}[b]\hypertarget{id16}[6]
+\href{http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html}{http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz{\_}dot.html}
+\end{figure}
+\begin{figure}[b]\hypertarget{id17}[7]
+For \texttt{twill} see
+\href{http://darcs.idyll.org/%7Et/projects/twill/README.html}{http://darcs.idyll.org/{\%}7Et/projects/twill/README.html}
+\end{figure}
+\begin{figure}[b]\hypertarget{id18}[8]
+For the \texttt{Python Browser Poseur} see \href{http://pbp.berlios.de}{http://pbp.berlios.de}
+\end{figure}
+\begin{figure}[b]\hypertarget{id19}[9]
+For \texttt{PyParsing} see \href{http://pyparsing.sourceforge.net}{http://pyparsing.sourceforge.net}
+\end{figure}
+\begin{figure}[b]\hypertarget{id20}[10]
+For \texttt{Selenium} see \href{http://selenium.thoughtworks.com/index.html}{http://selenium.thoughtworks.com/index.html}
+\end{figure}
+
+\end{document}
+
diff --git a/pypers/twill/testing_web_app.txt b/pypers/twill/testing_web_app.txt
new file mode 100755
index 0000000..54060b7
--- /dev/null
+++ b/pypers/twill/testing_web_app.txt
@@ -0,0 +1,579 @@
+Testing Web Applications with Python
+=========================================
+
+:author: Michele Simionato
+:date: June 2005
+
+Introduction
+-----------------------
+
+You have just finished your beautiful Web application, with
+lots of pages, links, forms and buttons; you have spent weeks
+making sure that everything works fine,
+that the special cases are handled correctly, that the user cannot
+crash your system whatever she does.
+
+Now you are happy and you are ready to ship. But at the last minute
+the customer ask for a change: you have the time to apply the change,
+but not the time - nor the will - to pass trough another testing
+ordalia. So you ship anyway, hoping that your last little fix did not break
+some other part of the application. The result is that the hidden bug
+shows up at the first day of usage.
+
+If you recognized yourself in this situation then this paper
+is for you, keep reading. If not, well, keep reading anyway,
+I am sure you will find something interesting, among the following topics:
+
+* how to separate unit tests from functional tests;
+* how to test web applications (written in any language) using
+ standard Python libraries;
+* how to use twill, a nice and easy to learn web testing tool.
+
+To test or not to test, this is the problem
+----------------------------------------------------
+
+Let me begin with a brief personal recollection of how I became
+interested in testing methodologies, and of what I have learned in
+the last couple of years.
+
+I have been aware of the importance of testing from the beginning, and
+I have heard about automatic testing for years. However, having
+heard about automatic testing is not the same as doing automatic testing,
+and not the same as doing automatic testing well.
+It takes some time and experience to get into the testing mood, as well
+as the ability to challenge some widespread misconceptions.
+
+For instance, when I began studying test driven methodologies,
+I had gathered two wrong ideas:
+
+- that testing was all about unit testing;
+- that the more you test, the better.
+
+After some experience I quickly realized myself that unit tests were
+not the only tool, nor the best tool to effectively test my application [#]_.
+But to overcome the second misconception, I needed some help.
+
+The help come from an XP seminar I attended last year, were I
+actually asked the question "how do I test the user interface of
+a Web application, i.e. that when the user click on a given page she gets
+the expected result?".
+
+The answer was: "You don't. Why do you want to test that your browser is
+working?"
+
+The case for not testing everything
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The answer made me rethink many things. Obviously I was well aware
+from the beginning that full test coverage is a myth, still I thought
+one programmer should try to test as much as he can.
+
+But this is not the right approach. Instead, it is important to
+discriminate about the infinite amount of things that could be
+tested, and focus on the things that are of your responsability.
+
+If your customer wants functionality X, you must be sure functionality X
+is there. But if in order to get functionality X you need to rely on
+functionalities X1, X2 ,... XN, you don't need to test for all
+of them. You test only the functionality you are payed to
+implement. You don't test that the browser is working, it is
+not your job.
+
+For instance, in the case of a Web application,
+you can interact with it *indirectly*, via the HTTP protocol, or *directly*,
+via the internal API. If you check that when the user clicks
+on button B method M is called and the result R is displayed, you are
+testing both your application *and* the correctness of the
+HTTP protocol implementation both in the browser and in the server. This is
+way too much. You may rely on the HTTP protocol and just test the API, i.e
+just test that if method M is called the right result R is returned.
+
+Of course, a similar viewpoint is applicable to GUIs. In the same
+vein, you must test that the interface to the DB you wrote is working, but
+you don't need to test that the database itself is working, this
+is not your responsability.
+
+The basic point is to separate the indirect testing of the user
+interface - via the HTTP protocol - from the testing of the inner API.
+To this aim, it is important to write your
+application in such a way that you can test the logic independently
+from the user interface. Working in this way you also have the additional
+bonus that you can change the user interface later, without having to
+change a single tests for the logic part.
+
+The problem is that typically the customer will give his specifications
+in terms of the user interface. He will tell you "There must be a page where
+the user will enter her order, then she will enter her credit card number,
+then the system must send a confirmation email, ..."
+
+This kind of specification is a kind of very high level test - a
+functional test - which has to be converted into a low-level test:
+for instance you may have unit testing telling you that the ordered
+item has been registered in the database, that the
+``send_confirmation_email method`` has been called etc.
+
+The conversion requires some thinking and practice and it an art more
+than a science. Actually I think that the art of testing is not in *how*
+to test, but in *what* to test. The best advice and best answer to somebody
+asking about "how do I test a Web application?" is probably "make a priority
+lists of the things you would like to test and test as little as possible".
+
+For instance, one should never tests the details of the
+implementation. If you make this mistake (as I did at the beginning)
+your tests will get in your way at refactoring time, i.e. they will
+have exactly the opposite of the intended effect. Generally speaking,
+good advices are: don't spend time testing third party software, don't
+waste time testing code which API is likely to change, split
+the UI testing from the application logic testing.
+
+Ideally you should be able to determine what is the minimal set
+of tests needed to make your customer happy, and restrict yourself to
+those tests.
+
+The case for testing everything
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The previous advice is nice and reasonable, especially in an ideal world
+where third party software is bug free and everything is configured correctly.
+Unfortunately, the real world is a bit different.
+
+For instance you must be aware that your application
+does not work on some buggy browser, or that it cannot work in specific
+circumstances with some database. Also, you may have a nice and
+comprehensive test suite which runs flawlessly
+on your development machine, but still
+that the application may not work correctly when installed on a different
+machine, because the database could be installed improperly, or
+the mail server settings could be incorrect, or the Internet
+connection could be down, etc. In the same vein, if you want
+to really be sure that if the user - using a specifing browser in a
+specific environment - clicks on that button she gets that result,
+you have to emulate exactly that situation.
+
+It looks like we are back to square one, i.e. the need of testing everything.
+But we have learned something in the process: whereas *in principle*
+you would like to test everything, *in practice* you can effectively
+prioritize your tests, focusing on some more than on others,and
+splitting them in separate categories to be run separately at
+different times.
+
+You definitely need to test that the
+application is working as intended when deployed on a different
+machine: and from the failures to these installation tests you may also infer
+what is wrong and correct the problem. These installation tests
+- tests of the environment where your software is running - must
+be kept decoupled from the unit tests checking the
+application logic. If you are sure that the logic is right, then you are
+sure also sure that the problems are in the environment, and you can
+focus your debugging skills in the right direction.
+
+In any case, you need to have both high level (functional, integration,
+installation) tests and low level tests (unit tests, doctests). High level
+tests include tests of the user interface. In particular, you need a test to
+make sure that if an user click X he gets Y, so you are sure that the
+Internet connection, the web server, the database, the mail
+server, your application, the browser, all work nicely
+together. But you should not focus on these global kind
+of tests. You don't need to write a thousands of these
+high level tests, if you already have many specific low-level
+tests checking that the logic and the various components
+of your application are working.
+
+How to test the user interface
+---------------------------------------------------------
+
+Having structured your application properly, you will need a smaller
+number of user interface tests, but still you will need at
+least a few. How do you write these tests then?
+
+There are two possibilities: the hard way and the easy way.
+
+The hard way is just doing everything by hand, by using your
+favorite programming language Web libraries to perform GET and POST
+requests and to verify the results. The easy way is to leverage on
+tools built by others. Of course,internally these tools work just by calling
+the low level libraries, so it is convenient to say a couple of words on the
+hard way, just to understand what is going on, in case the high level tool
+give you some problem. Moreover, there is always the possibility than
+you need something more customized, and knowledge of
+the low level libraries can be precious.
+
+The interaction between the user and a Web application
+passes through the HTTP protocol, so it is perfectly possible
+to simulate the action of an user clicking on a browser
+just by sending to the server an equivalent HTTP request (let me
+ignore the existence of Javascript for the moment).
+
+Any modern programming language has libraries to interact with the
+HTTP protocol, but here I will give my examples in Python, since Python
+is both a common language for Web programming and a readable one. In Python
+the interaction with the Web is managed via the urllib libraries [#]_.
+You have two of them: urllib, which can be used in absence of authentication,
+and urllib2 which can also manage cookie-based authentication. A complete
+discussion of these two libraries would take a long
+time, but explaining the basics is pretty simple. I will just give
+a couple of recipes based on urllib2, the newest and most powerful library.
+
+I will notice here that the support for cookies in Python 2.4
+has improved (essentially by including the third party ClientCookie
+library) so you may not be aware of the trick I am going to explain, even
+if have used the urllib libraries in the past. So, don't skip the
+next two sections ;)
+
+Recipe 1: how to send GET and POST requests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Suppose you want to access a site which does not require authentication.
+Then making a GET request is pretty easy, just type at the intepreter
+prompt
+
+>>> from urllib2 import urlopen
+>>> page = urlopen("http://www.example.com")
+
+Now you have a file like-object which contains the HTML code
+of the page http://www.example.com:
+
+>>> for line in page: print line,
+<HTML>
+<HEAD>
+ <TITLE>Example Web Page</TITLE>
+</HEAD>
+<body>
+<p>You have reached this web page by typing &quot;example.com&quot;,
+&quot;example.net&quot;,
+ or &quot;example.org&quot; into your web browser.</p>
+<p>These domain names are reserved for use in documentation and are not available
+ for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC
+ 2606</a>, Section 3.</p>
+</BODY>
+</HTML>
+
+
+If you try to access a non-existent page, or if your Internet
+connection is down, you will get an ``urllib2.URLError`` instead.
+Incidentally, this is why the ``urllib2.urlopen`` function is better
+than the older ``urllib.urlopen``, which would just silently retrieve
+a page containing the error message.
+
+You can easily imagine how to use urlopen to check your Web application:
+for instance, you could retrieve a page, extract all the links and
+check that they refer to existing pages; or you can verify that
+the retrieved page contains the right information, for instance
+by matching it with a regular expression. In practice, ``urlopen``
+(possibly coupled with a third party HTML parsing tool, such as
+BeautifulSoup [#]_) gives you all the fine granted control you may wish for.
+
+Moreover, ``urlopen`` gives you the possibility to make a POST:
+just pass the query
+string as second argument to urlopen. As an example, I will make a POST
+to http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets, which is a
+page containing the example form coming with Quixote, a nice small
+Pythonic Web Framework [#]_.
+
+>>> page = urlopen("http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets",
+... "name=MICHELE&password=SECRET&time=1118766328.56")
+>>> print page.read()
+<html>
+<head><title>Quixote Widget Demo</title></head>
+<body>
+<h2>You entered the following values:</h2>
+<table>
+ <tr><th align="left">name</th><td>MICHELE</td></tr>
+ <tr><th align="left">password</th><td>SECRET</td></tr>
+ <tr><th align="left">confirmation</th><td>False</td></tr>
+ <tr><th align="left">eye colour</th><td><i>nothing</i></td></tr>
+ <tr><th align="left">pizza size</th><td><i>nothing</i></td></tr>
+ <tr><th align="left">pizza toppings</th><td><i>nothing</i></td></tr>
+</table>
+<p>It took you 163.0 sec to fill out and submit the form</p>
+</body>
+</html>
+
+Now ``page`` will contain the result of your POST. Notice that I had to
+pass explicitly a value for ``time``, which is an hidden widget in
+the form.
+
+That was easy, isn't it?
+
+If the site requires authentication, things are slightly more complicated,
+but not much, at least if you have Python 2.4 installed.
+
+Recipe 2: managing authentication
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order to manage cookie-based authentication procedures,
+you need to import a few utilities from urllib2:
+
+>>> from urllib2 import build_opener, HTTPCookieProcessor, Request
+
+Notice that ``HTTPCookieProcessor`` is new in Python 2.4: if you have
+an older version of Python you need third party libraries
+such as `ClientCookie` [#]_.
+
+``build_opener`` and ``HTTPCookieProcessor`` are used to create an opener
+object that can manage the cookies sent by the Web server:
+
+>>> opener = build_opener(HTTPCookieProcessor)
+
+The opener object has an ``open`` method that can be used to retrieve
+the Web page corresponding to a given request. The request itself is
+encapsulated in a ``Request`` object, which is built from the
+URL address, the query string, and some HTTP headers information.
+In order togenerate the query string, it is pretty convenient
+to use the ``urlencode`` function defined in ``urllib`` (*not* in ``urllib2``):
+
+>>> from urllib import urlencode
+
+``urlencode`` generates the query string from a dictionary
+or a list of pairs, taking care of the quoting and escaping
+rules required by the HTTP protocol. For instance
+
+>>> urlencode(dict(user="MICHELE", password="SECRET"))
+'password=SECRET&user=MICHELE'
+
+Notice that the order is not preserved when you use a dictionary
+(quite obviously), but this is usually
+not an issue. Now, let me define a helper function:
+
+
+>>> def urlopen2(url, data=None, user_agent='urlopen2'):
+... """Can be used to retrieve cookie-enabled Web pages (when 'data' is
+... None) and to post Web forms (when 'data' is a list, tuple or dictionary
+... containing the parameters of the form).
+... """
+... if hasattr(data, "__iter__"):
+... data = urllib.urlencode(data)
+... headers = {'User-Agent' : user_agent}
+... return opener.open(urllib2.Request(url, data, headers))
+
+With ``urlopen2``, you can POST your form in just one line.
+On the other hand, if the page you are posting to does not contain a form,
+you will get an HTTPError:
+
+>>> urlopen2("http://www.example.com", dict(user="MICHELE", password="SECRET"))
+Traceback (most recent call last):
+ ...
+HTTPError: HTTP Error 405: Method Not Allowed
+
+If you just need to perform a GET, simply forget about the second argument
+to ``urlopen2``, or use an empty dictionary or tuple. You can even fake a
+browser by passing a convenient user agent string, such as
+"Mozilla", "Internet Explorer", etc. This is pretty useful if you want
+to make sure that your application works with different browsers.
+
+Using these two recipes it is not that difficilt to write your own web
+testing framework. But you may be better off by leveraging the work
+of somebody else
+
+Testing web applications the easy way: twill
+----------------------------------------------------
+
+I am a big fan of mini languages, i.e. small languages
+written to perform a specific task (see for instance my O'Reilly article
+on the graph-generation language "dot" [#]_). I was very happy when I
+discovered that there a nice little language expressely designed to test
+Web applications. Actually there are two implementations of it: Titus Brown's
+twill [#]_ and Cory Dodt's Python Browser Poseur, PBP [#]_.
+
+PBP came first, but twill seems to be developing faster. At the time
+of this writing, twill is still pretty young (I am using
+version 0.7.1), but it already works pretty well in most situations.
+Both PBP and twill are based on tools by
+John J. Lee, i.e. mechanize (inspired by Perl), ClientForm and ClientCookie,
+that you may find at http://wwwsearch.sourceforge.net. twill also use
+Paul McGuire's PyParsing [#]_. However, you don't need to install these
+libraries: twill includes them as zipped libraries (leveraging on the
+new Python 2.3 ``zipimport`` module). As a consequence twill installation
+is absolutely obvious and painless (nothing more than the usual
+``python setup.py install``).
+
+The simplest way to use twill is interactively from the command line.
+Let me show a simple session example::
+
+ $ twill-sh
+ -= Welcome to twill! =-
+
+ current page: *empty page*
+
+ >> go http://www.example.com
+ ==> at http://www.example.com
+
+ >> show
+ <HTML>
+ <HEAD>
+ <TITLE>Example Web Page</TITLE>
+ </HEAD>
+ <body>
+ <p>You have reached this web page by typing &quot;example.com&quot;,
+ &quot;example.net&quot;,
+ or &quot;example.org&quot; into your web browser.</p>
+ <p>These domain names are reserved for use in documentation and are not available
+ for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC
+ 2606</a>, Section 3.</p>
+ </BODY>
+ </HTML>
+
+twill recognizes a few intuitive commands, such as
+
+ ::
+
+ go, show, find, notfind, echo, code, back, reload, agent, follow
+
+and few others. The example shows how you can access
+a particular HTML page and display its content.
+
+The ``find`` command matches the page against a regular
+expression: thus
+
+::
+
+ >> find("Example Web Page")
+
+is a test asserting that the current page contains what we expect.
+Similarly, the ``notfind`` command asserts that the current page does
+not match the given regular expression.
+
+The other twill commands are pretty obvious: ``echo <message>``
+prints a message on standard output, ``code <http_error_code>`` checks
+that you are getting the right HTTP error code (200 if everything is
+alright), ``back`` allows you to go back to the previously visited page,
+``reload`` reloads the current page, ``agent <user-agent>`` allows you
+to change the current user agent, thus faking different browsers,
+``follow <regex>`` finds the first matching link on the page and visit it.
+
+The full lists of the commands can be obtained
+by giving ``help`` at the prompt; ``EOF`` of ``CTRL-D`` allows you to exit.
+
+Once you have tested your application interactively, it is pretty easy
+to cut & paste your twill session and convert it in a twill script.
+Then, you can run your twill script in a batch process::
+
+ $ twill-sh mytests.twill
+
+As you may imagine, you can put more than one script in the command line and
+test many of them at the same time. Since twill is written in Python, you
+can control it from Python entirely, and you can even extends its command
+set just by adding new commands in the ``commands.py`` module.
+
+At the moment, twill is pretty young and it does not have the
+capability to convert scripts in unit tests automatically, so that
+you can easily run entire suites of regression tests. However,
+it is not that difficult to implement that capability yourself,
+and it is not unlikely that twill will gain good integration with
+unittest and doctest in the future.
+
+Retrieving and submitting web forms
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+twill is especially good at retrieving and submitting web forms. The
+form-related functionality is implemented with the following commands:
+
+* showforms
+* formvalue <form_id> <name> <value>
+* submit <button_id>
+* formclear <form_id>
+
+Explaining the commands is pretty straightforward.
+
+``showforms`` shows the forms contained in a web page. For instance, try
+the following::
+
+ >> go http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+ >> showforms
+ Form #1
+ ## __Name______ __Type___ __ID________ __Value__________________
+ name text (None)
+ password password (None)
+ confirm checkbox (None) [] of ['yes']
+ colour radio (None) [] of ['green', 'blue', 'brown', 'ot ...
+ size select (None) ['Medium (10")'] of ['Tiny (4")', 'S ...
+ toppings select (None) ['cheese'] of ['cheese', 'pepperoni' ...
+ time hidden (None) 1118768019.17
+ 1 submit (None) Submit
+ current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+
+Notice that ``twill`` makes a good job at emulating a browser, so it fills
+the hidden ``time`` widget automatically, whereas
+we had to fill it explicitely with ``urlopen``.
+
+Unnamed forms get an ordinal number to be used as form id
+in the ``formvalue`` command, which fill a field
+of the specified form with a given value. You can give many
+formvalue commands in succession; if you are a lazy typist
+you can also use ``fv`` as an alias for ``formvalue``::
+
+ >> fv 1 name MICHELES
+ current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+ >> fv 1 password SECRET
+ current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+
+
+``formclear`` reset all the fields in a form and ``submit`` allows
+you to press a ``submit`` botton, thus submitting the form::
+
+ >> submit 1
+ current page: http://issola.caltech.edu/~t/qwsgi/qwsgi-demo.cgi/widgets
+
+A simple ``show`` will convince you that the forms has been submitted.
+The best way to understand how does it work is just experimenting
+on your own. The base distribution contains a few examples you may
+play with.
+
+Enlarging the horizon
+----------------------------
+
+In this article I have shown two easy ways to test your web
+application: by hand, using urllib, or with a simple tool such as twill.
+There is more under the sun. Much more. There are many sophisticated
+Web testing frameworks out there, including enterprise-oriented ones,
+with lots of functionalities and a steep learning curve. Here, on purpose,
+I have decided to start from the small, and to discuss the topic from a
+do-it-yourself attitude, since sometimes the simplest things works best: or
+because you don't need the sophistication, or because your preferred
+testing framework lacks the functionality you wish for, or because
+it is just buggy. If you need something more sophisticated, a great source for
+everything testing-related is Grig Gheorghiu's blog:
+
+http://agiletesting.blogspot.com/2005/02/articles-and-tutorials.html
+
+A new framework which is especially interesting is Selenium, which
+is also used to test Plone applications. Selenium is *really* spectacular,
+since it is Javascript based and it really tests your browser, clicking
+on links, submitting forms, opening popup windows, all in real time. It
+completely emulates the user experience, at highest possible level.
+It also gives
+you all kind of bells and whistles, eye candies and colored HTML output
+(which you may like or not, but that surely will impress your customer
+if you are going to demonstrate him that the application is conform to
+the specifications).
+I cannot render justice to Selenium in a few lines and maybe I should write
+a whole new paper on it, when I find the time. For the moment, however I make
+no promises and I refer you to the available documentation [#]_.
+
+References
+------------------------------------
+
+.. [#] I am an early adopter and supporter of doctests, see for
+ instance my talk at the ACCU conference,
+ http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_doctest.zip
+
+
+.. [#] For the ``urllib`` libraries see the Python docs
+ http://docs.python.org/lib/module-urllib2.html
+
+.. [#] For `BeautifulSoup``` see http://www.crummy.com/software/BeautifulSoup
+
+.. [#] For Quixote see http://www.mems-exchange.org/software/quixote
+
+.. [#] For ``mechanize`` and ``ClientCookie`` see
+ http://wwwsearch.sourceforge.net
+
+.. [#] http://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html
+
+.. [#] For ``twill`` see
+ http://darcs.idyll.org/%7Et/projects/twill/README.html
+
+.. [#] For the ``Python Browser Poseur`` see http://pbp.berlios.de
+
+.. [#] For ``PyParsing`` see http://pyparsing.sourceforge.net
+
+.. [#] For ``Selenium`` see http://selenium.thoughtworks.com/index.html
diff --git a/pypers/twisted/Makefile b/pypers/twisted/Makefile
new file mode 100644
index 0000000..cad83fc
--- /dev/null
+++ b/pypers/twisted/Makefile
@@ -0,0 +1,2 @@
+talk:
+ python2.4 maketalk.py talk.txt
diff --git a/pypers/twisted/P01.html b/pypers/twisted/P01.html
new file mode 100644
index 0000000..eb3ae60
--- /dev/null
+++ b/pypers/twisted/P01.html
@@ -0,0 +1,93 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P16.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>An Introduction to Twisted</h1><br/>
+
+<center>
+Seminar given at StatPro Italia<br/>
+15 September 2005 <br/>
+Michele Simionato<br/>
+
+</center></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P02.html b/pypers/twisted/P02.html
new file mode 100644
index 0000000..10ff392
--- /dev/null
+++ b/pypers/twisted/P02.html
@@ -0,0 +1,92 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P02.html'>P02</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Three things</h1><br/>
+
+<ul>
+ <li> What's Twisted <em>(life in an asynchronous world)</em> </li>
+ <li> Using deferreds <em>(the Hollywood principle)</em> </li>
+ <li> Writing servers and clients with Twisted </li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P03.html b/pypers/twisted/P03.html
new file mode 100644
index 0000000..04ec67a
--- /dev/null
+++ b/pypers/twisted/P03.html
@@ -0,0 +1,95 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P03.html'>P03</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>What's Twisted?</h1><br/>
+
+Asynchronous Framework for Network Programming
+
+<ul>
+ <li> Synchronous </li>
+ <li> Threaded </li>
+ <li> Multiprocess </li>
+ <li> Asynchronous </li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P04.html b/pypers/twisted/P04.html
new file mode 100644
index 0000000..603a3d3
--- /dev/null
+++ b/pypers/twisted/P04.html
@@ -0,0 +1,101 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P04.html'>P04</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Example: a file downloader</h1><br/>
+
+<pre>
+class Downloader(cmd.Cmd):
+ out = TkWindow()
+ def postloop(self):
+ self.out.close()
+ def do_quit(self, arg):
+ return True
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ self.out.write(" END %s\n" % fname)
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P05.html b/pypers/twisted/P05.html
new file mode 100644
index 0000000..072b18c
--- /dev/null
+++ b/pypers/twisted/P05.html
@@ -0,0 +1,97 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P05.html'>P05</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Threaded file downloader</h1><br/>
+
+<pre>
+class ThreadedDownloader(Downloader):
+ def do_download(self, arg):
+ self.thread = threading.Thread(
+ target=self.download, args=(arg,))
+ self.thread.start()
+ def postloop(self):
+ self.thread.join() # wait before closing self.out
+ Downloader.postloop(self)
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P06.html b/pypers/twisted/P06.html
new file mode 100644
index 0000000..0760355
--- /dev/null
+++ b/pypers/twisted/P06.html
@@ -0,0 +1,98 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P06.html'>P06</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Issues with threads</h1><br/>
+
+<ul>
+ <li>Platform bugs </li>
+ <li>Your own bugs</li>
+ <li>You <em>cannot kill threads</em></li>
+ <ul>
+ <li>unhandled exceptions do not stop the program</li>
+ <li>sys.exit does not work </li>
+ <li>CTRL-C does not work</li>
+ </ul>
+ <li>Performance and scalability issues</li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P07.html b/pypers/twisted/P07.html
new file mode 100644
index 0000000..326bd4e
--- /dev/null
+++ b/pypers/twisted/P07.html
@@ -0,0 +1,91 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P07.html'>P07</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Solution: split the computation in steps.</h1><br/>
+
+<ul>
+<li>Need a way to switch between multiple tasks</li>
+<li>Need a non-blocking mainloop</li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P08.html b/pypers/twisted/P08.html
new file mode 100644
index 0000000..3b7a213
--- /dev/null
+++ b/pypers/twisted/P08.html
@@ -0,0 +1,101 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P08.html'>P08</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Multi iterator</h1><br/>
+
+<pre>
+def multi_iter(iterlist, terminate=True):
+ while True:
+ for it in iterlist:
+ try:
+ yield it.next()
+ except StopIteration:
+ iterlist.remove(it)
+ if not iterlist:
+ if terminate:
+ break
+ else:
+ yield Nothing
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P09.html b/pypers/twisted/P09.html
new file mode 100644
index 0000000..fb5b514
--- /dev/null
+++ b/pypers/twisted/P09.html
@@ -0,0 +1,92 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P09.html'>P09</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Non-blocking mainloops</h1><br/>
+
+<ul>
+<li> Hand-made command loop </li>
+<li> Twisted mainloop </li>
+<li> Tkinter mainloop </li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P10.html b/pypers/twisted/P10.html
new file mode 100644
index 0000000..1c80f9c
--- /dev/null
+++ b/pypers/twisted/P10.html
@@ -0,0 +1,88 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P10.html'>P10</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Performance</h1><br/>
+
+show <em>million.py</em></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P11.html b/pypers/twisted/P11.html
new file mode 100644
index 0000000..e7dde82
--- /dev/null
+++ b/pypers/twisted/P11.html
@@ -0,0 +1,92 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P11.html'>P11</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Deferreds</h1><br/>
+
+<ul>
+ <li> getProcessOutput </li>
+ <li> deferToThread </li>
+ <li> dbpool.runQuery </li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P12.html b/pypers/twisted/P12.html
new file mode 100644
index 0000000..9405fb9
--- /dev/null
+++ b/pypers/twisted/P12.html
@@ -0,0 +1,91 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P12.html'>P12</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Writing servers</h1><br/>
+
+<ul>
+ <li>A server spawning processes</li>
+ <li>The server version of the file downloader</li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P13.html b/pypers/twisted/P13.html
new file mode 100644
index 0000000..049184b
--- /dev/null
+++ b/pypers/twisted/P13.html
@@ -0,0 +1,113 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P13.html'>P13</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>A server spawning processes</h1><br/>
+
+<pre>
+class FakeTelnet(protocol.Protocol):
+ dirToRunIn = os.getcwd()
+ def connectionMade(self):
+ print 'connection made'
+ self.pp = ProcessProtocol(self)
+ cmd = self.factory.cmd_list
+ reactor.spawnProcess(self.pp, cmd[0], cmd, os.environ,
+ self.dirToRunIn, usePTY=True)
+ def dataReceived(self, data):
+ self.pp.transport.write(data)
+ def connectionLost(self, reason=None):
+ print 'connection lost'
+ self.pp.transport.loseConnection()
+
+class ProcessProtocol(protocol.ProcessProtocol):
+ def __init__(self, telnet):
+ self.telnet = telnet
+ def outReceived(self, data):
+ self.telnet.transport.write(data)
+ def errReceived(self, data):
+ self.telnet.transport.write("! " + data)
+ def processEnded(self, status):
+ print 'protocol connection lost'
+ self.telnet.transport.loseConnection()
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P14.html b/pypers/twisted/P14.html
new file mode 100644
index 0000000..c3334a6
--- /dev/null
+++ b/pypers/twisted/P14.html
@@ -0,0 +1,95 @@
+<!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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P14.html'>P14</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>How to run it</h1><br/>
+
+<pre>
+def run(cmd_list, port=1025):
+ factory = protocol.ServerFactory()
+ factory.protocol = FakeTelnet
+ factory.cmd_list = cmd_list
+ reactor.listenTCP(port, factory)
+ reactor.run()
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P15.html b/pypers/twisted/P15.html
new file mode 100644
index 0000000..a36f1d8
--- /dev/null
+++ b/pypers/twisted/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"><img src = "cjlogo.jpg" alt = "logo"></td>
+</tr>
+</table>
+</td>
+</tr>
+<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='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P15.html'>P15</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Writing clients</h1><br/>
+
+<pre>
+from twisted.internet.protocol import ClientFactory
+from twisted.protocols.basic import LineReceiver
+from twisted.internet import reactor
+
+class EchoClient(LineReceiver):
+ end="Bye-bye!"
+ def connectionMade(self):
+ self.sendLine("Hello, world!")
+ self.sendLine("What a fine day it is.")
+ self.sendLine(self.end)
+ def lineReceived(self, line):
+ print "receive:", line
+ if line == self.end:
+ self.transport.loseConnection()
+
+class EchoClientFactory(ClientFactory):
+ protocol = EchoClient
+ def clientConnectionFailed(self, connector, reason):
+ print 'connection failed:', reason.getErrorMessage()
+ reactor.stop()
+ def clientConnectionLost(self, connector, reason):
+ print 'connection lost:', reason.getErrorMessage()
+ reactor.stop()
+
+reactor.connectTCP('localhost', 8000, EchoClientFactory())
+reactor.run()
+</pre></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/P16.html b/pypers/twisted/P16.html
new file mode 100644
index 0000000..f55310f
--- /dev/null
+++ b/pypers/twisted/P16.html
@@ -0,0 +1,91 @@
+<!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"><img src = "cjlogo.jpg" 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='P15.html'>Prev</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P01.html'>First</a></td> <td bgcolor="lightblue"><a href='P16.html'>Last</a></td>
+</tr>
+<tr>
+ <td bgcolor="lightblue"><a href='P16.html'>P16</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 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"></td> <td bgcolor="lightblue"></td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</small></td> <td bgcolor="lightblue"><h1>Not only Twisted ...</h1><br/>
+
+<ul>
+ <li>greenlets</li>
+ <li>back button</li>
+</ul></td>
+</tr>
+</table>
+
+ </body>
+
+</html>
diff --git a/pypers/twisted/config.py b/pypers/twisted/config.py
new file mode 100644
index 0000000..d62928c
--- /dev/null
+++ b/pypers/twisted/config.py
@@ -0,0 +1,6 @@
+import sys
+if sys.platform == "win32":
+ root = "C:/"
+else:
+ root = "/mnt/hda2/"
+MUSICDIR = root + "Documents and Settings/micheles/Desktop/Music"
diff --git a/pypers/twisted/connect_mysql.py b/pypers/twisted/connect_mysql.py
new file mode 100644
index 0000000..948d67c
--- /dev/null
+++ b/pypers/twisted/connect_mysql.py
@@ -0,0 +1,16 @@
+"""
+Trivial example of using MySQLdb from twisted.
+"""
+from twisted.internet import reactor
+from twisted.enterprise import adbapi
+from ms.debug_utils import printf
+
+def prnt(ls):
+ for el in ls:
+ print el
+
+if __name__ == "__main__":
+ dbpool = adbapi.ConnectionPool("MySQLdb", db='test')
+ dbpool.runQuery("select * from books where genre='FS'").addCallbacks(
+ prnt, printf)
+ reactor.run()
diff --git a/pypers/twisted/downloader1.py b/pypers/twisted/downloader1.py
new file mode 100644
index 0000000..4c41ceb
--- /dev/null
+++ b/pypers/twisted/downloader1.py
@@ -0,0 +1,62 @@
+"""
+A model for a file downloader: Synchronous, Threaded and Asynchronous.
+This version does not require an asynchronous command loop, so it works
+on Windows too.
+"""
+
+import os, sys, cmd, time, threading
+from ms.debug_utils import FifoWindow, TkWindow
+from ms.async_utils import ThreadedLoopingCall, multi_iter
+
+class Downloader(cmd.Cmd):
+ intro = "You may download the following files:\n%s" % \
+ "\n".join(f for f in os.listdir(".") if f.endswith(".html"))
+
+ out = TkWindow() # class level, since there is only one
+
+ def postloop(self):
+ self.out.close()
+ def do_quit(self, arg):
+ return True
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ self.out.write(" END %s\n" % fname)
+
+class SynchronousDownloader(Downloader):
+ def do_download(self, arg):
+ self.download(arg)
+
+class ThreadedDownloader(Downloader):
+ def do_download(self, arg):
+ self.thread = threading.Thread(target=self.download, args=(arg,))
+ self.thread.start()
+ def postloop(self):
+ self.thread.join() # wait for the thread to end before closing self.out
+ Downloader.postloop(self)
+
+class QuasiAsynchronousDownloader(Downloader):
+ def preloop(self):
+ self.downloads = []
+ mi = multi_iter(self.downloads, terminate=False)
+ self.lc = ThreadedLoopingCall(mi.next)
+ self.lc.start()
+ def postloop(self):
+ self.lc.stop()
+ Downloader.postloop(self)
+ def do_download(self, arg):
+ self.downloads.append(self.download(arg))
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ yield None
+ self.out.write(" END %s\n" % fname)
+
+if __name__ == "__main__":
+ #SynchronousDownloader().cmdloop()
+ ThreadedDownloader().cmdloop()
+ #QuasiAsynchronousDownloader().cmdloop()
diff --git a/pypers/twisted/downloader2.py b/pypers/twisted/downloader2.py
new file mode 100644
index 0000000..4a36ad1
--- /dev/null
+++ b/pypers/twisted/downloader2.py
@@ -0,0 +1,57 @@
+"""
+A model for a file downloader: Synchronous, Threaded and Asynchronous.
+"""
+
+import os, sys, cmd, time, select
+from ms.debug_utils import TkWindow
+from ms.async_utils import multi_iter
+from downloader1 import Downloader
+
+class AsynchronousDownloader(Downloader):
+ """Works completely without threads, using an asynchronous command loop."""
+ def preloop(self):
+ self.downloads = []
+ self.mi = multi_iter(self.downloads, terminate=False)
+
+ def do_download(self, arg):
+ self.downloads.append(self.download(arg))
+
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ yield None
+ self.out.write(" END %s\n" % fname)
+
+ def cmdloop(self, intro=None):
+ """Select-based asynchronous command loop. It only works on Unix and
+ it does not use the readline library. The advantage is that the command
+ loop is nonblocking. The loop runs the multi iterator self.mi.
+ """
+ self.preloop()
+ if intro is not None:
+ self.intro = intro
+ if self.intro:
+ self.stdout.write(str(self.intro) + "\n")
+ self.stdout.write(self.prompt)
+ self.stdout.flush()
+ stop = None
+ while not stop:
+ i, o, e = select.select([self.stdin], [], [], 0)
+ if i:
+ line = i[0].readline()
+ if not len(line):
+ line = 'EOF'
+ else:
+ line = line[:-1] # chop \n
+ line = self.precmd(line)
+ stop = self.onecmd(line)
+ stop = self.postcmd(stop, line)
+ self.stdout.write(self.prompt)
+ self.stdout.flush()
+ self.mi.next()
+ self.postloop()
+
+if __name__ == "__main__":
+ AsynchronousDownloader().cmdloop()
diff --git a/pypers/twisted/downloader3.py b/pypers/twisted/downloader3.py
new file mode 100644
index 0000000..b5c0d38
--- /dev/null
+++ b/pypers/twisted/downloader3.py
@@ -0,0 +1,37 @@
+"""
+A model for a file downloader: Synchronous, Threaded and Asynchronous.
+"""
+
+import os, sys, cmd, time, threading
+from ms.debug_utils import TkWindow
+from ms.async_utils import MultiIter, run_iter
+
+from twisted.internet import reactor
+from downloader1 import Downloader
+
+class AsynchronousDownloader(Downloader):
+ "Twisted based."
+ mi = MultiIter()
+ def postloop(self):
+ reactor.stop()
+ while reactor.running: pass # until the stop message is received
+ Downloader.postloop(self)
+ def do_download(self, arg):
+ self.mi.append(self.download(arg))
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ yield None
+ self.out.write(" END %s\n" % fname)
+
+def ended(_):
+ print _, "ended"
+ #d.out.close()
+
+if __name__ == "__main__":
+ d = AsynchronousDownloader()
+ threading.Thread(None, d.cmdloop).start()
+ run_iter(d.mi, reactor.callLater, 0)
+ reactor.run()
diff --git a/pypers/twisted/downloader4.py b/pypers/twisted/downloader4.py
new file mode 100644
index 0000000..1c8a491
--- /dev/null
+++ b/pypers/twisted/downloader4.py
@@ -0,0 +1,65 @@
+"""
+Twisted based downloader server.
+"""
+
+import os, sys, cmd, time
+
+from twisted.internet import reactor, protocol, task
+from twisted.protocols.basic import LineReceiver
+
+from ms.async_utils import multi_iter
+from downloader1 import Downloader
+
+class DownloaderProtocol(LineReceiver, Downloader):
+
+ def do_download (self, arg):
+ self.downloads.append(self.download(arg))
+
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ yield None
+ self.out.write(" END %s\n" % fname)
+
+ def postloop(self):
+ pass # don't close self.out too early
+
+ def connectionMade(self):
+ self.stdout = self.transport
+ self.preloop()
+ intro = getattr(self, "intro", None)
+ if intro:
+ self.stdout.write(intro.replace("\n", "\r\n") + "\r\n")
+ self.stdout.write(self.prompt)
+ self.downloads = []
+ mi = multi_iter(self.downloads, terminate=False)
+ self.iterloop = task.LoopingCall(mi.next)
+ self.iterloop.start(0)
+
+ def lineReceived(self, line):
+ line = self.precmd(line)
+ stop = self.onecmd(line)
+ stop = self.postcmd(stop, line)
+ if stop:
+ self.transport.loseConnection()
+ else:
+ self.stdout.write(self.prompt)
+
+ def connectionLost(self, reason):
+ print "connection lost"
+ self.iterloop.stop()
+ self.postloop()
+
+def run_server(port):
+ factory = protocol.ServerFactory()
+ factory.protocol = DownloaderProtocol
+ reactor.listenTCP(port, factory)
+ reactor.run()
+
+if __name__ == "__main__":
+ run_server(1025)
+ #from twisted.internet.stdio import StandardIO
+ #StandardIO(DownloaderProtocol()); reactor.run() # does NOT work
+
diff --git a/pypers/twisted/echoclient.py b/pypers/twisted/echoclient.py
new file mode 100644
index 0000000..2be5e2a
--- /dev/null
+++ b/pypers/twisted/echoclient.py
@@ -0,0 +1,32 @@
+from twisted.internet.protocol import ClientFactory
+from twisted.protocols.basic import LineReceiver
+from twisted.internet import reactor
+
+class EchoClient(LineReceiver):
+ end="Bye-bye!"
+ def connectionMade(self):
+ self.sendLine("Hello, world!")
+ self.sendLine("What a fine day it is.")
+ self.sendLine(self.end)
+
+ def lineReceived(self, line):
+ print "receive:", line
+ if line == self.end:
+ self.transport.loseConnection()
+
+class EchoClientFactory(ClientFactory):
+ protocol = EchoClient
+
+ def clientConnectionFailed(self, connector, reason):
+ print 'connection failed:', reason.getErrorMessage()
+ reactor.stop()
+
+ def clientConnectionLost(self, connector, reason):
+ print 'connection lost:', reason.getErrorMessage()
+ reactor.stop()
+
+if __name__ == '__main__':
+ reactor.connectTCP('localhost', 8000, EchoClientFactory())
+ reactor.run()
+
+
diff --git a/pypers/twisted/ex.py b/pypers/twisted/ex.py
new file mode 100644
index 0000000..09d3bcf
--- /dev/null
+++ b/pypers/twisted/ex.py
@@ -0,0 +1,15 @@
+from quixote.server import simple_server
+from quixote.publish import Publisher
+from quixote.directory import Directory
+
+class MySite(Directory):
+ _q_exports = ["hello"]
+ def hello(self):
+ return "hello"
+
+
+def factory():
+ return Publisher(MySite())
+
+if __name__ == "__main__":
+ simple_server.run(factory, "localhost", 7080)
diff --git a/pypers/twisted/ex_thread.py b/pypers/twisted/ex_thread.py
new file mode 100644
index 0000000..070e072
--- /dev/null
+++ b/pypers/twisted/ex_thread.py
@@ -0,0 +1,20 @@
+from twisted.internet import reactor
+from twisted.internet.threads import deferToThread
+import time, sys
+
+@deferToThread.__get__
+def longrunning(proc):
+ for i in range(100):
+ time.sleep(.1)
+ sys.stdout.write(proc)
+ sys.stdout.flush()
+ return "ended %r" % proc
+
+def print_(arg):
+ print arg
+
+longrunning("+").addBoth(print_)
+longrunning("-").addBoth(print_)
+longrunning("*").addBoth(print_)
+longrunning("/").addBoth(print_)
+reactor.run()
diff --git a/pypers/twisted/hello_twisted.py b/pypers/twisted/hello_twisted.py
new file mode 100644
index 0000000..2d5e551
--- /dev/null
+++ b/pypers/twisted/hello_twisted.py
@@ -0,0 +1,17 @@
+from twisted.internet import reactor
+import sys
+
+def printdots():
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ reactor.callLater(.1, printdots)
+
+def print_hello_and_exit():
+ print "Hello!"
+ reactor.stop()
+
+reactor.callLater(0, printdots)
+reactor.callLater(2, print_hello_and_exit)
+print "Starting mainloop",
+reactor.run()
+
diff --git a/pypers/twisted/maketalk.py b/pypers/twisted/maketalk.py
new file mode 100644
index 0000000..eaeb3de
--- /dev/null
+++ b/pypers/twisted/maketalk.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+import webbrowser
+from ms.html_utils import makelink, TableList
+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.first, "First"),
+ makelink(self.last, "Last"),
+ makelink(self.namext, self.name),
+ '',
+ border = 0,
+ color = BGCOLOR)
+ logo = TableList.col(
+ '<img src = "cjlogo.jpg" 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(logo, box, index,
+ border = 0, color = BGCOLOR),
+ self.txt, border = 0, color = BGCOLOR))
+ return self.html()
+
+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/twisted/million.py b/pypers/twisted/million.py
new file mode 100644
index 0000000..9ece621
--- /dev/null
+++ b/pypers/twisted/million.py
@@ -0,0 +1,56 @@
+from Tkinter import *
+from ms.async_utils import TkinterLoopingCall, multi_iter
+import itertools
+
+nline = list(enumerate(file(__file__)))
+
+def print_a(i):
+ for n, line in nline:
+ if i % 100 == 0:
+ print "serving client #%6d, sending line #%s" % (i, n+1)
+ yield None
+
+def print_t(i):
+ for n, line in nline:
+ if i % 10 == 0 and n % 100 == 0:
+ print "serving client #%6d, sending line #%s" % (i, n+1)
+
+def test_a(N=1000*1000):
+ print "Have patience ..."
+ root = Tk()
+ mi = multi_iter([print_a(i) for i in xrange(1, N+1)], terminate=False)
+ lc = TkinterLoopingCall(mi.next)
+ lc.start()
+ root.after(10*60*1000, root.quit) # 10 minutes
+ root.mainloop()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+import threading
+
+def test_t(N=1000*1000):
+ global nline
+ from ms.debug_utils import killme; killme()
+ nline = enumerate(itertools.count(1))
+ for i in xrange(1, N+1):
+ t = threading.Thread(target=print_t, args=(i,))
+ t.start(); print t
+
+if __name__ == "__main__":
+ test_a()
diff --git a/pypers/twisted/process_out.py b/pypers/twisted/process_out.py
new file mode 100644
index 0000000..a61e14d
--- /dev/null
+++ b/pypers/twisted/process_out.py
@@ -0,0 +1,24 @@
+import os, sys
+
+if sys.platform == "win32":
+ base = "C:/cygwin/bin/"
+ from twisted.internet import win32eventreactor
+ win32eventreactor.install()
+else:
+ base = "/usr/bin/"
+
+from twisted.internet import reactor
+from twisted.internet.utils import getProcessOutput
+
+def print_(result):
+ print result
+ return "ok"
+
+def stop(arg):
+ print arg
+ reactor.stop()
+
+de = getProcessOutput(base + "wc", ("tlk.txt",))
+#print dir(de)
+de.addErrback(print_).addErrback(stop).addCallback(print_).addCallback(stop)
+reactor.run()
diff --git a/pypers/twisted/talk.txt b/pypers/twisted/talk.txt
new file mode 100644
index 0000000..6670118
--- /dev/null
+++ b/pypers/twisted/talk.txt
@@ -0,0 +1,231 @@
+An Introduction to Twisted
+=======================================
+
+<center>
+Seminar given at StatPro Italia<br/>
+15 September 2005 <br/>
+Michele Simionato<br/>
+
+</center>
+
+
+Three things
+------------------
+
+<ul>
+ <li> What's Twisted <em>(life in an asynchronous world)</em> </li>
+ <li> Using deferreds <em>(the Hollywood principle)</em> </li>
+ <li> Writing servers and clients with Twisted </li>
+</ul>
+
+
+What's Twisted?
+---------------------
+
+Asynchronous Framework for Network Programming
+
+<ul>
+ <li> Synchronous </li>
+ <li> Threaded </li>
+ <li> Multiprocess </li>
+ <li> Asynchronous </li>
+</ul>
+
+
+Example: a file downloader
+-----------------------------
+
+<pre>
+class Downloader(cmd.Cmd):
+ out = TkWindow()
+ def postloop(self):
+ self.out.close()
+ def do_quit(self, arg):
+ return True
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ self.out.write(" END %s\n" % fname)
+</pre>
+
+
+Threaded file downloader
+--------------------------------------
+
+<pre>
+class ThreadedDownloader(Downloader):
+ def do_download(self, arg):
+ self.thread = threading.Thread(
+ target=self.download, args=(arg,))
+ self.thread.start()
+ def postloop(self):
+ self.thread.join() # wait before closing self.out
+ Downloader.postloop(self)
+</pre>
+
+
+Issues with threads
+-----------------------------------
+
+<ul>
+ <li>Platform bugs </li>
+ <li>Your own bugs</li>
+ <li>You <em>cannot kill threads</em></li>
+ <ul>
+ <li>unhandled exceptions do not stop the program</li>
+ <li>sys.exit does not work </li>
+ <li>CTRL-C does not work</li>
+ </ul>
+ <li>Performance and scalability issues</li>
+</ul>
+
+
+Solution: split the computation in steps.
+------------------------------------------
+
+<ul>
+<li>Need a way to switch between multiple tasks</li>
+<li>Need a non-blocking mainloop</li>
+</ul>
+
+
+Multi iterator
+------------------------------
+
+<pre>
+def multi_iter(iterlist, terminate=True):
+ while True:
+ for it in iterlist:
+ try:
+ yield it.next()
+ except StopIteration:
+ iterlist.remove(it)
+ if not iterlist:
+ if terminate:
+ break
+ else:
+ yield Nothing
+</pre>
+
+
+Non-blocking mainloops
+------------------------------
+
+<ul>
+<li> Hand-made command loop </li>
+<li> Twisted mainloop </li>
+<li> Tkinter mainloop </li>
+</ul>
+
+
+Performance
+---------------------------------
+
+show <em>million.py</em>
+
+
+Deferreds
+----------------------
+
+<ul>
+ <li> getProcessOutput </li>
+ <li> deferToThread </li>
+ <li> dbpool.runQuery </li>
+</ul>
+
+
+Writing servers
+---------------------------------
+
+<ul>
+ <li>A server spawning processes</li>
+ <li>The server version of the file downloader</li>
+</ul>
+
+
+A server spawning processes
+------------------------------------
+
+<pre>
+class FakeTelnet(protocol.Protocol):
+ dirToRunIn = os.getcwd()
+ def connectionMade(self):
+ print 'connection made'
+ self.pp = ProcessProtocol(self)
+ cmd = self.factory.cmd_list
+ reactor.spawnProcess(self.pp, cmd[0], cmd, os.environ,
+ self.dirToRunIn, usePTY=True)
+ def dataReceived(self, data):
+ self.pp.transport.write(data)
+ def connectionLost(self, reason=None):
+ print 'connection lost'
+ self.pp.transport.loseConnection()
+
+class ProcessProtocol(protocol.ProcessProtocol):
+ def __init__(self, telnet):
+ self.telnet = telnet
+ def outReceived(self, data):
+ self.telnet.transport.write(data)
+ def errReceived(self, data):
+ self.telnet.transport.write("! " + data)
+ def processEnded(self, status):
+ print 'protocol connection lost'
+ self.telnet.transport.loseConnection()
+</pre>
+
+
+How to run it
+------------------------
+
+<pre>
+def run(cmd_list, port=1025):
+ factory = protocol.ServerFactory()
+ factory.protocol = FakeTelnet
+ factory.cmd_list = cmd_list
+ reactor.listenTCP(port, factory)
+ reactor.run()
+</pre>
+
+
+Writing clients
+---------------------------------
+
+<pre>
+from twisted.internet.protocol import ClientFactory
+from twisted.protocols.basic import LineReceiver
+from twisted.internet import reactor
+
+class EchoClient(LineReceiver):
+ end="Bye-bye!"
+ def connectionMade(self):
+ self.sendLine("Hello, world!")
+ self.sendLine("What a fine day it is.")
+ self.sendLine(self.end)
+ def lineReceived(self, line):
+ print "receive:", line
+ if line == self.end:
+ self.transport.loseConnection()
+
+class EchoClientFactory(ClientFactory):
+ protocol = EchoClient
+ def clientConnectionFailed(self, connector, reason):
+ print 'connection failed:', reason.getErrorMessage()
+ reactor.stop()
+ def clientConnectionLost(self, connector, reason):
+ print 'connection lost:', reason.getErrorMessage()
+ reactor.stop()
+
+reactor.connectTCP('localhost', 8000, EchoClientFactory())
+reactor.run()
+</pre>
+
+
+Not only Twisted ...
+--------------------------------------
+
+<ul>
+ <li>greenlets</li>
+ <li>back button</li>
+</ul>
diff --git a/pypers/twisted/tester.py b/pypers/twisted/tester.py
new file mode 100644
index 0000000..f99b477
--- /dev/null
+++ b/pypers/twisted/tester.py
@@ -0,0 +1,119 @@
+import os, cmd, config
+from ms.file_utils import ifiles
+from ms.concurrency import Popen
+from operator import attrgetter
+import sys, re
+
+def strip_number(song, rx=re.compile(r"\s*\d\d?\. ")):
+ return rx.sub("", os.path.basename(song))
+
+class TestProcess(object):
+ def __init__(self, number, name, popencmd):
+ self.name = name
+ self.number = number
+ self.popencmd = popencmd
+ def start(self):
+ self.proc = Popen(self.popencmd)
+ def stop(self):
+ if self.is_running():
+ self.proc.kill()
+ def is_running(self):
+ return hasattr(self, "proc") and self.proc.is_running()
+
+def popencmd(song):
+ if sys.platform == "win32":
+ return "mplay32", "/play", "/close", song
+ else:
+ return "xterm", "-geometry", "70x14", "-e", "mpg123", song
+
+def str2int(args):
+ for arg in args.split():
+ try:
+ i = int(arg)
+ except ValueError:
+ print "Warning: argument %s is not an integer, ignored" % arg
+ else:
+ yield i
+
+class Tester(cmd.Cmd):
+ use_rawinput = False
+ prompt = ">>" + chr(0)
+ def preloop(self):
+ self.songs = list(ifiles(config.MUSICDIR,
+ lambda f: f.endswith(".mp3")))
+ self.tests = dict(
+ [i, TestProcess(i, strip_number(song), popencmd(song))]
+ for i, song in enumerate(self.songs))
+ self.started_tests = set()
+ self.do_show()
+
+ def running_tests(self):
+ for test in self.started_tests:
+ if test.is_running():
+ yield test
+
+ def run_test(self, testnumber):
+ if testnumber in self.tests:
+ test = self.tests[testnumber]
+ if test.is_running():
+ print "Test #%s is already running!" % test.number
+ else:
+ test.start()
+ self.started_tests.add(test)
+ print "test #%s started" % test.number
+ else:
+ print "There is no test #%s" % testnumber
+
+ def stop_test(self, testnumber):
+ if testnumber in (test.number for test in self.running_tests()):
+ self.tests[testnumber].stop()
+ print "Test #%s stopped." % testnumber
+ else:
+ print "There is no process #%s running" % testnumber
+
+ def do_show(self, arg=""):
+ if arg == "runnable":
+ self.show_runnable()
+ elif arg == "already_run":
+ self.show_already_run()
+ elif arg is "":
+ self.show_already_run()
+ self.show_runnable()
+ else:
+ print "Unknown argument %s" % arg
+
+ def show_runnable(self):
+ print "Runnable tests:"
+ if set(self.tests.itervalues()) != self.started_tests:
+ for test in self.tests.itervalues():
+ if not test in self.started_tests:
+ print test.number, test.name
+ else: # all tests have been started
+ print "None"
+
+ def show_already_run(self):
+ print "Already run tests:"
+ if not self.started_tests:
+ print "None"
+ else:
+ for test in sorted(self.started_tests, key=attrgetter("number")):
+ print test.number, test.name
+
+ def do_run(self, args):
+ run = map(self.run_test, str2int(args))
+ if not run: map(self.run_test, range(len(self.tests))) # run all
+
+ def do_stop(self, args):
+ stop = map(self.stop_test, str2int(args))
+ if not stop: self.postloop() # stop all
+
+ def do_quit(self, arg):
+ print "exiting ..."
+ return True
+
+ def postloop(self):
+ for test in self.running_tests():
+ self.stop_test(test.number)
+
+if __name__ == "__main__":
+ Tester().cmdloop()
diff --git a/pypers/twisted/tester_server.py b/pypers/twisted/tester_server.py
new file mode 100644
index 0000000..c5ac9c9
--- /dev/null
+++ b/pypers/twisted/tester_server.py
@@ -0,0 +1,6 @@
+import sys
+from ms.twisted_utils import run
+
+if __name__ == "__main__":
+ print "listening on localhost 1025 ..."
+ run([sys.executable, "tester.py"], 1025)
diff --git a/pypers/twisted/tk_mainloop.py b/pypers/twisted/tk_mainloop.py
new file mode 100644
index 0000000..15afc10
--- /dev/null
+++ b/pypers/twisted/tk_mainloop.py
@@ -0,0 +1,54 @@
+import sys, time
+from ms.async_utils import \
+ TkControlPanel, TkinterTask, ThreadedTask, TwistedTask
+from ms.debug_utils import killme; killme()
+
+def print_(sym):
+ for i in range(50):
+ time.sleep(.1)
+ sys.stdout.write(sym)
+ sys.stdout.flush()
+ yield i
+ print "task %r ended" % sym
+
+def main_threaded(gen):
+ t = ThreadedTask.generator(gen)
+ for s in "+-*/":
+ task = t(s)
+ task.start()
+ # print task
+ raw_input("Press return to stop\n")
+ ThreadedTask.stopAll()
+ raw_input("\nPress return to resume")
+ ThreadedTask.startAll()
+ raw_input("Press return to exit\n")
+ ThreadedTask.stopAll()
+
+def main_tkinter(gen):
+ from Tkinter import Tk
+ t = TkinterTask.generator(gen)
+ root = Tk()
+ for s in "+-*/":
+ t(s).start()
+ root.mainloop()
+
+def main_twisted(gen):
+ from twisted.internet import reactor
+ t = TwistedTask.generator(gen)
+ for s in "+-*/":
+ t(s).start()
+ reactor.run()
+
+def main_control_panel(func):
+ t = TkinterTask.generator(func)
+ root = TkControlPanel()
+ for s in "1234":
+ task = t(s)
+ task.start()
+ root.mainloop()
+
+if __name__ == "__main__":
+ #main_threaded(print_)
+ #main_tkinter(print_)
+ #main_twisted(print_)
+ main_control_panel(print_)
diff --git a/pypers/twisted/web_downloader.py b/pypers/twisted/web_downloader.py
new file mode 100644
index 0000000..8e3275e
--- /dev/null
+++ b/pypers/twisted/web_downloader.py
@@ -0,0 +1,68 @@
+"""
+It is not so easy to tinker with the HTTPServer mainloop
+(handle_request is blocking). One should use the Twisted server.
+"""
+
+import os, sys, time
+sys.path.append(os.environ["HOME"] + "/md/scripts")
+from quixote.publish import Publisher
+from quixote.directory import Directory
+from twisted.internet import task, reactor
+from fakemodal import multiform
+from fakemodal.controller import Controller
+from downloader1 import Downloader, multi_iter
+
+class Interaction(Directory, Downloader):
+ _q_exports = ["downloader"]
+ downloads = []
+ mi = multi_iter(downloads, terminate=False)
+ lc = task.LoopingCall(mi.next)
+ lc.start(0)
+
+ downloader_html = """\
+ <html>
+ <body>
+ <h3>Web downloader</h3>
+ <form action="$nextname">
+ %s
+ <input type="submit" name="exit" value="quit">
+ </form>
+ </body>
+ </html>
+ """ % "<br/>\n".join(
+ 'download <input type="submit" name="download" value="%s">' % f
+ for f in os.listdir(".") if f.endswith(".html"))
+
+ @multiform
+ def downloader(self, fdict, fd):
+ arg = fdict.get("download")
+ if arg: self.downloads.append(self.download(arg))
+ return fdict
+
+ def download(self, fname):
+ self.out.write("BEGIN %s" % fname)
+ for line in file(fname):
+ time.sleep(.1)
+ self.out.write(".")
+ yield None
+ self.out.write(" END %s\n" % fname)
+
+ def quit(self, fd):
+ self.lc.stop()
+ reactor.callLater(1, self.stop) # shutdown in 1 second
+ return "shutdown in 1 second ..."
+
+ def stop(self):
+ self.out.close()
+ reactor.stop()
+
+class Home(Directory):
+ _q_exports = ["micheles", "pippo"]
+ micheles = Interaction() # in principle reserved to user micheles
+ pippo = Interaction() # in principle reserved to user pippo
+
+if __name__ == '__main__':
+ from quixote.server import twisted_server
+ c = Controller('localhost', 7080, twisted_server.run)
+ reactor.callLater(1, c.open_browser, "micheles/downloader")
+ c.run(lambda : Publisher(Home()))
diff --git a/pypers/unicode/howto.txt b/pypers/unicode/howto.txt
new file mode 100755
index 0000000..751d8d0
--- /dev/null
+++ b/pypers/unicode/howto.txt
@@ -0,0 +1,26 @@
+Welcome to the Unicode world! (a Python-centric view)
+======================================================
+
+quote::
+
+ Here's the stark simple recipe: when you use Unicode, you *MUST*
+ switch to a Unicode-centric view of the universe. Therefore you
+ encode *FROM* Unicode and you decode *TO* Unicode. Period. It's
+ similar to the way floating point contaminates ints.
+
+ Aahz on comp.lang.python
+
+u.encode -> str
+s.decode -> str
+
+Why do Unicode objects have a decode method?
+--------------------------------------------
+
+The u.decode method first encodes in the default encoding and then
+decodes the result with the specified encoding, so if u is a unicode
+object ``u.decode("utf-16")`` is an abbreviation of
+``u.encode().decode("utf-16")``.
+
+In the same way str has an encode method, so ``s.encode("utf-16")``
+is an abbreviation of ``s.decode().encode("utf-16")``.
+
diff --git a/pypers/wsgi/abstract-en.txt b/pypers/wsgi/abstract-en.txt
new file mode 100644
index 0000000..844956c
--- /dev/null
+++ b/pypers/wsgi/abstract-en.txt
@@ -0,0 +1,12 @@
+As the old saying goes, Python is the only language with more Web frameworks than
+keywords. This is sometimes an advantage, but more often than not, it is an issue.
+In order to improve the interoperability between different frameworks, Phillip
+J. Eby proposed in 2003 a specification, the WSGI or Web Server Gateway
+Interface, a.k.a Whiskey. In my talk I will discuss how you can achieve portability
+of your application by following the WSGI protocol. I will give practical
+examples of how to use the WSGI reference implementation which is part of the
+standard library (wsgiref), of how to supplement it with other WSGI-compliant
+libraries (notably Paste by Ian Bicking) and of how to integrate your WSGI
+application in different frameworks including Zope, Twisted, TurboGears et al.
+The talk is intended as a tutorial and requires only elementary knowledge of
+Web programming, at the level of simple CGI.
diff --git a/pypers/wsgi/abstract-scipy.txt b/pypers/wsgi/abstract-scipy.txt
new file mode 100644
index 0000000..22fe89f
--- /dev/null
+++ b/pypers/wsgi/abstract-scipy.txt
@@ -0,0 +1,14 @@
+SciPy e' il progetto piu' importante per quanto riguarda il calcolo scientifico in Python.
+SciPy fornisce molti vantaggi:
+
+1) la possibilita' di riutilizzare codice scientifico esistente (in C, C++ e Fortran)
+ da Python, tramite degli opportuni wrapper (SWIG, weave, f2py, etc.);
+2) un'ottima libreria per gli array N-dimensionali (Numpy);
+3) la possibilita' di utilizzare la console interattiva di (I)Python;
+4) ottime routine di visualizzazione (Matplotlib, Chaco, etc.);
+5) l'apertura a tutto il mondo Python.
+
+Il mio talk sara' decisamente introduttivo, discutera' le caratteristiche
+piu' semplici di Numpy e mostrera' qualche esempio di uso di altre parti di SciPy,
+in particolare le librerie per l'interpolazione non-lineare (non-linear least
+squares fitting). En passant, parlero' anche di IPython e Matplotlib.
diff --git a/pypers/wsgi/abstract.txt b/pypers/wsgi/abstract.txt
new file mode 100644
index 0000000..6e88e8a
--- /dev/null
+++ b/pypers/wsgi/abstract.txt
@@ -0,0 +1,28 @@
+SciPy on WSGI
+--------------
+
+Nonostante il titolo esoterico, il talk sara' molto concreto ed
+illustrera' come implementare un'interfaccia Web per una semplice
+applicazione scientifica. Prendero' spunto da una mia esperienza
+personale considerando come esmpio uno script per l'interpolazione di
+dati con una curva non lineare. L'enfasi sara' sulla semplicita' d'uso e
+sulla velocita' di implementazione. L'audience ideale e' costituita
+da persone che non sono sviluppatori Web di professione.
+
+Python e' l'unico linguaggio con piu' Web frameworks che keywords.
+Per cercare di dare ordine all'anarchia e permettere una maggiore
+interoperabilita' tra i diversi frameworks, Phillip H. Eby ha
+inventato nel 2003 il protocollo WSGI (Web Server Gateway Interface,
+per gli amici Whiskey) che regola la comunicazione fra Web framework
+e Web server. Il protocollo ha avuto largo successo ed oggigiorno
+praticamente tutti i Web frameworks per Python lo implementano.
+
+Nel talk discutero':
+
+1. Che cosa e' esattamente WSGI;
+2. Come usare l'implementazione di riferimento di WSGI (wsgiref, parte
+ della libreria standard di Python dalla versione 2.5);
+3. Come scrivere un object publisher in 20 righe;
+4. Come implementare un semplice servizio Web per il fitting di dati
+ sperimentali;
+5. Come migrare un'applicazione WSGI da un framework ad un altro.
diff --git a/pypers/wsgi/badpricehistory.png b/pypers/wsgi/badpricehistory.png
new file mode 100644
index 0000000..33767cf
--- /dev/null
+++ b/pypers/wsgi/badpricehistory.png
Binary files differ
diff --git a/pypers/wsgi/badpricehistory2.png b/pypers/wsgi/badpricehistory2.png
new file mode 100644
index 0000000..c557af7
--- /dev/null
+++ b/pypers/wsgi/badpricehistory2.png
Binary files differ
diff --git a/pypers/wsgi/cdf-dist.png b/pypers/wsgi/cdf-dist.png
new file mode 100644
index 0000000..dcb4b27
--- /dev/null
+++ b/pypers/wsgi/cdf-dist.png
Binary files differ
diff --git a/pypers/wsgi/delta-cdf.png b/pypers/wsgi/delta-cdf.png
new file mode 100644
index 0000000..e1d582d
--- /dev/null
+++ b/pypers/wsgi/delta-cdf.png
Binary files differ
diff --git a/pypers/wsgi/delta-dist.png b/pypers/wsgi/delta-dist.png
new file mode 100644
index 0000000..445e4b6
--- /dev/null
+++ b/pypers/wsgi/delta-dist.png
Binary files differ
diff --git a/pypers/wsgi/delta_dist.py b/pypers/wsgi/delta_dist.py
new file mode 100644
index 0000000..deb0024
--- /dev/null
+++ b/pypers/wsgi/delta_dist.py
@@ -0,0 +1,64 @@
+from pylab import *
+from scipy.special import gamma, erf, hyp2f1
+from scipy.optimize import brentq
+
+Cg = 1/sqrt(2*pi)
+SQRT2 = sqrt(2.)
+
+def gauss(x):
+ return Cg*exp(-x*x)
+
+def cdf_gauss(x):
+ return erf(x/SQRT2)/2.+.5
+
+# avg is a global variable
+def delta(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*d2**(1.+d2/2.)/(x*x + d2)**(1.5+d2/2.)
+
+def cdf(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*x/d*hyp2f1(.5, 1.5+d2/2., 1.5, -x*x/d2) + .5
+
+def plot_cdf():
+ x = arange(-5,5,.1)
+ plot(x, cdf_gauss(x), color='black')
+ plot(x, cdf(.2)(x), color='red')
+ plot(x, cdf(1.)(x), color='green')
+ plot(x, cdf(5.)(x), color='blue')
+
+ text(1.5, .3, '$c_{gauss}\ :\ VAR_{95}=1.64\ \sigma$', color='black')
+ text(1.5, .6, '$c_{0.2}\ :\ VAR_{95}=0.40\ \sigma$', color='red')
+ text(1.5, .5, '$c_{1.0}\ :\ VAR_{95}=1.36\ \sigma$', color='green')
+ text(1.5, .4, '$c_{5.0}\ :\ VAR_{95}=1.64\ \sigma$', color='blue')
+
+ savefig('cdf-dist.png', dpi=72)
+ show()
+## you get VAR_{95}/vol from computation like
+# brentq(lambda x: cdf(0.2)(x)-.95,.1,10)
+# brentq(lambda x: cdf(1.0)(x)-.95,.1,10)
+# brentq(lambda x: cdf(5.0)(x)-.95,.1,10)
+
+def plot_delta():
+ x = arange(-5,5,.1)
+ plot(x, gauss(x), color='black')
+ plot(x, delta(.1)(x), color='red')
+ plot(x, delta(1.)(x), color='green')
+ plot(x, delta(5.)(x), color='blue')
+
+ text(4.5, 4, '$f_{0.2}(x)$', color='red')
+ text(4.5, 3, '$f_{1.0}(x)$', color='green')
+ text(4.5, 2, '$f_{5.0}(x)$', color='blue')
+ text(4.5, 1, '$\phi(x)$', color='black')
+
+ savefig('delta-dist.png', dpi=72)
+ show()
+
+if __name__ == '__main__':
+ if '-c' in sys.argv[1:]:
+ plot_cdf()
+ else:
+ plot_delta()
+
diff --git a/pypers/wsgi/err.txt b/pypers/wsgi/err.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pypers/wsgi/err.txt
diff --git a/pypers/wsgi/error_trapper.py b/pypers/wsgi/error_trapper.py
new file mode 100644
index 0000000..172b679
--- /dev/null
+++ b/pypers/wsgi/error_trapper.py
@@ -0,0 +1,22 @@
+import sys
+from wsgiref.simple_server import make_server
+
+# WSGI app
+def simple_app(env, resp):
+ resp('200 OK', [('Content-type','text/plain')])
+ yield 'Hello world!\n'
+ yield 1/0
+
+def wrapped_app(app, env, resp):
+ try:
+ page = app(env, lambda s, h, e=None: None)
+ print list(page)
+ except:
+ resp('500 ERR', [("content-type","text/plain")], sys.exc_info())
+ return ['err']
+ else:
+ resp('200 OK', [("content-type","text/html")])
+ return page
+
+if __name__ == '__main__':
+ make_server('', 8000, wrapped_app.__get__(simple_app)).serve_forever()
diff --git a/pypers/wsgi/evalexception_ex.py b/pypers/wsgi/evalexception_ex.py
new file mode 100644
index 0000000..51b70a8
--- /dev/null
+++ b/pypers/wsgi/evalexception_ex.py
@@ -0,0 +1,11 @@
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+if __name__ == '__main__':
+ make_server('', 9090, EvalException(app)).serve_forever()
diff --git a/pypers/wsgi/formulas.html b/pypers/wsgi/formulas.html
new file mode 100644
index 0000000..c98eec7
--- /dev/null
+++ b/pypers/wsgi/formulas.html
@@ -0,0 +1,329 @@
+<?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.4.1: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title></title>
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+<math xmlns='http://www.w3.org/1998/Math/MathML'>
+<mi>&Phi;</mi>
+<mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo><mfrac><mn>12</mn><mo>+</mo></mfrac><mfrac><mn>12</mn><mi>erf</mi></mfrac><mo>(</mo><mi>x</mi><mo>/</mo><msqrt><mo>(</mo></msqrt><mn>2</mn><mo>)</mo><mo>)</mo>
+</math></div>
+</div>
+</body>
+</html>
diff --git a/pypers/wsgi/formulas.tex b/pypers/wsgi/formulas.tex
new file mode 100644
index 0000000..052405c
--- /dev/null
+++ b/pypers/wsgi/formulas.tex
@@ -0,0 +1,48 @@
+\documentclass[]{slides}
+\begin{document}
+
+delta-distribution
+
+$$
+f_\delta(x;\mu,\sigma) =
+\frac{C_\delta(\delta^2\sigma^2)^{1+\delta^2/2}}
+{[(x-\mu)^2+\delta^2\sigma^2]^{3/2+\delta^2/2}}
+$$
+
+$$
+C_\delta = \frac{\Gamma(3/2+\delta^2/2)}{\Gamma(1/2)/\Gamma(1+\delta^2/2)}
+$$
+
+$$\frac12 < C_\delta < \frac1{\sqrt\pi}\simeq 0.5642$$
+
+$$
+\int dx\ f_\delta(x;\mu,\sigma) = 1,\quad \forall \delta
+$$
+
+$$
+\int dx\ (x-\mu)^2f_\delta(x;\mu,\sigma) = \sigma^2,\quad \forall \delta
+$$
+
+$$
+f_\delta(x;\mu,\sigma) \sim \frac1{x^{3+\delta^2}},\quad x\to\infty
+$$
+
+The average absolute deviation of the curve is $\delta\sigma$
+
+$$
+\int dx\ |x-\mu|f_\delta(x;\mu,\sigma) =
+\frac{\Gamma(1/2+\delta^2/2)}{\Gamma(1/2)\Gamma(1+\delta^2/2)}\delta\sigma
+$$
+
+Cumulative distribution function
+
+$$
+F_\delta(x;\mu,\sigma) = \int_{-\infty}^x\ dx'\ f_\delta(x';\mu,\sigma) =
+$$
+
+$$
+\frac12 + C_\delta\frac{x-\mu}{\delta\sigma}
+{}_2F_1\left(\frac12, \frac{\delta^2+3}2, \frac32, -\frac{(x-\mu)^2}
+{\delta^2\sigma^2}\right)
+$$
+\end{document}
diff --git a/pypers/wsgi/formulas.txt b/pypers/wsgi/formulas.txt
new file mode 100644
index 0000000..ea4a232
--- /dev/null
+++ b/pypers/wsgi/formulas.txt
@@ -0,0 +1 @@
+$\Phi(x) = \frac12+\frac12 erf(x/\sqrt(2))$
diff --git a/pypers/wsgi/hello.py b/pypers/wsgi/hello.py
new file mode 100644
index 0000000..caa7f62
--- /dev/null
+++ b/pypers/wsgi/hello.py
@@ -0,0 +1,10 @@
+
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
diff --git a/pypers/wsgi/limit999.png b/pypers/wsgi/limit999.png
new file mode 100644
index 0000000..644a564
--- /dev/null
+++ b/pypers/wsgi/limit999.png
Binary files differ
diff --git a/pypers/wsgi/limit999.py b/pypers/wsgi/limit999.py
new file mode 100644
index 0000000..67f556d
--- /dev/null
+++ b/pypers/wsgi/limit999.py
@@ -0,0 +1,37 @@
+from pylab import *
+from scipy.special import gamma, hyp2f1
+from scipy.integrate import quad, Inf
+from scipy.optimize import brentq
+
+def hyp(x, a):
+ return x*hyp2f1(.5, a, 1.5, -x*x)
+
+Cg = 1/sqrt(2*pi)
+
+def gauss(x):
+ return Cg*exp(-x*x)
+
+def delta(d2):
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*d2**(1.+d2/2.)/(x*x + d2)**(1.5+d2/2.)
+
+def getsol(x):
+ def f(d2):
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return .999-2*Cd*hyp(x, (3.+d2)/2.)
+ return math.sqrt(brentq(f, 0., 100.))
+
+x = arange(.01,5.,.1)
+sols = [getsol(d) for d in x]
+plot(x, sols, color='red')
+#plot(x, hyp(x, 1.7), color='blue')
+#plot(x, quad(delta(.1), x, Inf), color='red')
+#plot(x, quad(delta(1.), x, Inf), color='green')
+#plot(x, quad(delta(5.), x, Inf), color='blue')
+
+#text(4.5, 1.4, '$f_{0.1}(x)$', color='red')
+#text(4.5, 1.3, '$f_{1.0}(x)$', color='green')
+#text(4.5, 1.2, '$f_{5.0}(x)$', color='blue')
+
+savefig('limit999.png', dpi=72)
+show()
diff --git a/pypers/wsgi/m_distribution.py b/pypers/wsgi/m_distribution.py
new file mode 100644
index 0000000..0d3ab65
--- /dev/null
+++ b/pypers/wsgi/m_distribution.py
@@ -0,0 +1,6 @@
+rom pylab import *
+#title(r'$\alpha > \sqrt(\beta)$')
+
+title(r'''$m_{peak} = \Gamma(m-1/2)\sigma\Gamma(m)/\sqrt[(2m-3)\pi]$''')
+
+show()
diff --git a/pypers/wsgi/nonblocking.py b/pypers/wsgi/nonblocking.py
new file mode 100644
index 0000000..c91d494
--- /dev/null
+++ b/pypers/wsgi/nonblocking.py
@@ -0,0 +1,48 @@
+from __future__ import with_statement
+import time, threading
+from easy_async import make_reactor
+from wsgiref.simple_server import make_server
+from ms.debug_utils import printdict
+from paste.auth.basic import AuthBasicHandler
+
+## def blockingcalc(env, resp):
+## resp('200 OK', [('Content-type', 'text/html')])
+## res = 0
+## for i in range(1000):
+## time.sleep(.01)
+## res += i
+## printdict(env)
+## return ['The result is %d' % res]
+
+def _nonblockingcalc(job):
+ res = 0
+ for i in range(1000):
+ time.sleep(.01)
+ res += i
+ yield 'Partial result %d' % res
+ yield 'The result is %d' % res
+
+_cache = {}
+
+def next(gen, env):
+ user = env['REMOTE_USER'] # not null because of the middleware
+ job = _cache.get(user)
+ if job is None: # create a new job
+ job = _cache[user] = reactor.Job(gen)
+ job.tick = 0
+ job.start()
+ return job.yval
+
+def nonblockingcalc(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ yield str(next(_nonblockingcalc, env))
+
+if __name__ == '__main__':
+ app = AuthBasicHandler(
+ nonblockingcalc, 'realm', lambda e, u, p: u=='pippo')
+ reactor = make_reactor('default')
+ job = reactor.Job(_nonblockingcalc)
+ job.start()
+
+ with reactor.in_separated_thread():
+ make_server('', 8000, app).serve_forever()
diff --git a/pypers/wsgi/nongaussian.png b/pypers/wsgi/nongaussian.png
new file mode 100644
index 0000000..e779e89
--- /dev/null
+++ b/pypers/wsgi/nongaussian.png
Binary files differ
diff --git a/pypers/wsgi/notes.py b/pypers/wsgi/notes.py
new file mode 100644
index 0000000..ac0fbd1
--- /dev/null
+++ b/pypers/wsgi/notes.py
@@ -0,0 +1,32 @@
+from wsgiref.simple_server import make_server
+
+# WSGI app
+def simple_app(env, resp):
+ resp('200 OK', [('Content-type','text/plain')])
+ return ['Hello world!\n']
+
+# error management
+def robust_app(env, resp): # the page has to be created fully *before* sending the response
+ try:
+ status = "200 OK"
+ response_headers = [("content-type","text/plain")]
+ resp(status, response_headers)
+ return ["normal body goes here"]
+ except KeyboardError:
+ pass
+ except:
+ status = "500 ERR"
+ response_headers = [("content-type","text/plain")]
+ resp(status, response_headers, sys.exc_info())
+ return ["error body goes here"]
+
+# Middleware
+upper = lambda x: (e.upper() for e in x)
+def upper_middleware(app):
+ return lambda env, resp: upper(app(env, resp))
+
+
+
+
+if __name__ == '__main__':
+ make_server('', 8000, upper_middleware(simple_app)).serve_forever()
diff --git a/pypers/wsgi/objectpublisher.py b/pypers/wsgi/objectpublisher.py
new file mode 100644
index 0000000..88201f9
--- /dev/null
+++ b/pypers/wsgi/objectpublisher.py
@@ -0,0 +1,46 @@
+from wsgiref import util, simple_server
+
+# page is any callable object returning HTML in chunks
+class WSGIObjectPublisher(object):
+ def __init__(self, root):
+ self.root = root
+ def __call__(self, env, resp):
+ return self.getsubpage(self.root, env, resp)()
+ def getsubpage(self, root, env, resp):
+ script_name = util.shift_path_info(env)
+ print script_name, '****', env['PATH_INFO']
+ if not script_name: # We've arrived!
+ resp('200 OK', [('content-type', 'text/html')])
+ return root
+ try:
+ page = getattr(root, script_name)
+ except AttributeError:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['missing page %r' % script_name]
+ exposed = getattr(page, 'exposed', False)
+ if not exposed:
+ resp('404 Not Found', [('content-type', 'text/plain')])
+ return lambda : ['%r is not exposed!' % script_name]
+ return self.getsubpage(page, env, resp)
+
+if __name__ == '__main__':
+ class Example(object):
+ def __call__(self):
+ yield '<h1>index page</h1>'
+ yield 'goto <a href="./page1">page1</a><br/>'
+ yield 'goto <a href="./page2">page2</a><br/>'
+ yield 'goto <a href="subsite">subsite</a><br/>'
+ def page1(self):
+ yield 'page1'
+ def page2(self):
+ yield 'page2'
+ page1.exposed = page2.exposed = True
+ root = Example()
+ root.subsite = Example()
+ root.subsite.exposed = True
+ app = WSGIObjectPublisher(root)
+ from paste.auth.basic import AuthBasicHandler
+ def authfunc(environ, username, password):
+ return username == 'pippo' and password == 'lippo'
+ app_with_auth = AuthBasicHandler(app, 'Test Realm', authfunc)
+ simple_server.make_server('', 8000, app_with_auth).serve_forever()
diff --git a/pypers/wsgi/rst2s5_math.py b/pypers/wsgi/rst2s5_math.py
new file mode 100644
index 0000000..3554824
--- /dev/null
+++ b/pypers/wsgi/rst2s5_math.py
@@ -0,0 +1,29 @@
+import os, re, subprocess
+
+LATEX_EXPR = re.compile(r'\$.*\$')
+
+def indent(text):
+ return '\n ' + '\n '.join(text.splitlines())
+
+def latex2mathml(latex):
+ itex2MML = subprocess.Popen(
+ ['./itex2MML'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ out, err = itex2MML.communicate(latex)
+ assert not err, err
+ return '\n.. raw:: html\n%s ' % indent(out)
+
+def rst2s5(rst):
+ converter = subprocess.Popen(
+ ['rst2s5'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ out, err = converter.communicate(rst)
+ assert not err, err
+ return out
+
+def convertfile(fname):
+ rst = LATEX_EXPR.sub(
+ lambda m: latex2mathml(m.group()), file('%s.txt' % fname).read())
+ print >> file('%s.html' % fname, 'w'), rst2s5(rst),
+
+if __name__ == '__main__':
+ convertfile('formulas')
+
diff --git a/pypers/wsgi/simpleplotter.py b/pypers/wsgi/simpleplotter.py
new file mode 100644
index 0000000..dcdb5ef
--- /dev/null
+++ b/pypers/wsgi/simpleplotter.py
@@ -0,0 +1,21 @@
+import os, sys
+from ms.plot_utils import GnuPlotter
+DATADIR = os.path.expanduser('~/sp/equities-histories')
+
+def make_graph(code, batch):
+ gp = GnuPlotter(batch=batch)
+ lines = list(file(os.path.join(DATADIR, code)))[-500:]
+ data = ''.join(lines)
+ png_file='/tmp/%s.png' % code
+ gp.plot(locals())
+ return png_file
+
+if __name__ == '__main__':
+ L = len(sys.argv) - 1
+ if L == 1:
+ batch = False
+ elif L == 2:
+ batch = bool(int(sys.argv[2])) # 0 or 1
+ else:
+ sys.exit('Examples: $ python simpleplotter.py fri-gb;AVE')
+ make_graph(sys.argv[1], batch)
diff --git a/pypers/wsgi/talk.html b/pypers/wsgi/talk.html
new file mode 100644
index 0000000..925b833
--- /dev/null
+++ b/pypers/wsgi/talk.html
@@ -0,0 +1,1207 @@
+<?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.4.1: http://docutils.sourceforge.net/" />
+<meta name="version" content="S5 1.1" />
+<title>SciPy on WSGI</title>
+<meta name="organization" content="StatPro Italy" />
+<meta name="date" content="2007-06-09" />
+<style type="text/css">
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ 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 ;
+ margin-right: 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 }
+
+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.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 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+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>
+<!-- configuration parameters -->
+<meta name="defaultView" content="slideshow" />
+<meta name="controlVis" content="hidden" />
+<!-- style sheet links -->
+<script src="ui/default/slides.js" type="text/javascript"></script>
+<link rel="stylesheet" href="ui/default/slides.css"
+ type="text/css" media="projection" id="slideProj" />
+<link rel="stylesheet" href="ui/default/outline.css"
+ type="text/css" media="screen" id="outlineStyle" />
+<link rel="stylesheet" href="ui/default/print.css"
+ type="text/css" media="print" id="slidePrint" />
+<link rel="stylesheet" href="ui/default/opera.css"
+ type="text/css" media="projection" id="operaFix" />
+
+<style type="text/css">
+#currentSlide {display: none;}
+</style>
+</head>
+<body>
+<div class="layout">
+<div id="controls"></div>
+<div id="currentSlide"></div>
+<div id="header">
+
+</div>
+<div id="footer">
+<h1>SciPy on WSGI</h1>
+<h2>PyCon Uno 2007 - 09 June 2007</h2>
+</div>
+</div>
+<div class="presentation">
+<div class="slide" id="slide0">
+<h1 class="title">SciPy on WSGI</h1>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="field"><th class="docinfo-name">Talk given at:</th><td class="field-body">PyCon Uno 2007</td>
+</tr>
+<tr class="field"><th class="docinfo-name">By:</th><td class="field-body">Michele Simionato</td>
+</tr>
+<tr><th class="docinfo-name">Organization:</th>
+<td>StatPro Italy</td></tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>2007-06-09</td></tr>
+</tbody>
+</table>
+<!-- Definitions of interpreted text roles (classes) for S5/HTML data. -->
+<!-- This data file has been placed in the public domain. -->
+<!-- Colours
+======= -->
+<!-- Text Sizes
+========== -->
+<!-- Display in Slides (Presentation Mode) Only
+========================================== -->
+<!-- Display in Outline Mode Only
+============================ -->
+<!-- Display in Print Only
+===================== -->
+<!-- Incremental Display
+=================== -->
+<p class="center"><strong>Subtitle</strong>: <em>Science on the Web for pedestrians</em></p>
+
+</div>
+<div class="slide" id="before-i-start">
+<h1>Before I start</h1>
+<p>What about you?</p>
+<p class="incremental">Are you more of a programmer or more of a scientist/engineer?</p>
+<p class="incremental">What kind of scientific tools are you using, if any?</p>
+<p class="incremental">Have you ever heard of SciPy?</p>
+<p class="incremental">Have you ever heard of WSGI?</p>
+</div>
+<div class="slide" id="ok-now-i-can-begin">
+<h1>Ok, now I can begin ;)</h1>
+<p>The motivation from this talk comes from a real problem at <a class="reference" href="http://www.statpro.com">StatPro</a></p>
+<p class="incremental">we have bad histories for many financial products</p>
+<p class="incremental">wrong prices at some dates in the past</p>
+</div>
+<div class="slide" id="a-picture">
+<h1>A picture</h1>
+<img alt="badpricehistory.png" src="badpricehistory.png" />
+<p class="incremental">(damn data providers!)</p>
+</div>
+<div class="slide" id="discarding-values">
+<h1>Discarding values ...</h1>
+<img alt="badpricehistory2.png" src="badpricehistory2.png" />
+<p>... is tricky!</p>
+</div>
+<div class="slide" id="issues">
+<h1>Issues</h1>
+<p>We cannot use the conventional criterium</p>
+<img alt="nongaussian.png" class="incremental" src="nongaussian.png" />
+</div>
+<div class="slide" id="strategy">
+<h1>Strategy</h1>
+<ul class="incremental simple">
+<li>price distributions (ln p_i/p) are known to decay with power laws</li>
+<li>fit the distributions with a &quot;reasonable&quot; curve and determine
+a suitable criterium for the spikes at some confidence level</li>
+<li>a reasonably simple ansatz gives a family of distributions depending
+on a parameter delta</li>
+<li><a class="reference" href="formulas.pdf">show formulae</a></li>
+</ul>
+</div>
+<div class="slide" id="delta-distribution">
+<h1>delta-distribution</h1>
+<img alt="delta-dist.png" src="delta-dist.png" />
+<p>From Dirac delta (delta -&gt; 0) to Gaussian
+distribution (delta -&gt; oo)</p>
+</div>
+<div class="slide" id="cumulative-dist">
+<h1>Cumulative dist</h1>
+<img alt="cdf-dist.png" src="cdf-dist.png" />
+<p>VAR-XX = Max loss at XX% confidence level [<a class="reference" href="http://integrals.wolfram.com/index.jsp">i</a>]</p>
+</div>
+<div class="slide" id="relation-var-vol">
+<h1>Relation VAR-vol.</h1>
+<p>If you assume a given distribution, there is a fixed relation between
+VAR-XX and volatility</p>
+<ul class="incremental simple">
+<li>for the Gaussian VAR-95 = 1.64 sigma, for a lot of our distributions
+VAR-95 &lt; 1.0 sigma</li>
+<li>we don't want to make assumptions on the distribution function
+for computing the VAR</li>
+<li>but we are willing to make assumptions for the sake of eliminating
+statistically invalid values</li>
+</ul>
+</div>
+<div class="slide" id="the-tool-we-need">
+<h1>The tool we need</h1>
+<pre class="literal-block">
+$ python simpleplotter.py &quot;fri-gb;AVE&quot;
+</pre>
+<p>(not 100% finished yet!)</p>
+</div>
+<div class="slide" id="enter-scipy-co">
+<h1>Enter SciPy &amp; Co.</h1>
+<p>In order to perform our analysis we looked
+at many scientific tools</p>
+<ul class="incremental simple">
+<li>a good plotting tool (matplotlib)</li>
+<li>support for histograms (matplotlib)</li>
+<li>support for special functions (scipy.special)</li>
+<li>support for non-linear fitting (scipy.leastsq)</li>
+<li>good performance (scipy)</li>
+<li>interactive and IPython-friendly (scipy)</li>
+<li>bla-bla</li>
+<li>cheating: I actually used Gnuplot!! ;-)</li>
+</ul>
+</div>
+<div class="slide" id="installation">
+<h1>Installation</h1>
+<p>If you are lucky, it is trivial:</p>
+<pre class="literal-block">
+$ apt-get install ipython
+$ apt-get install python-matplotlib
+$ apt-get install python-numpy
+$ apt-get install python-numpy-ext
+$ apt-get install python-scipy
+</pre>
+<p>If you are unlucky, or if you try to build from sources,
+YMMV ...</p>
+</div>
+<div class="slide" id="what-s-in-scipy">
+<h1>What's in Scipy</h1>
+<ul class="incremental simple">
+<li>support for multi-dimensional array (numpy)</li>
+<li>linear algebra and minimization routines</li>
+<li>solving, integration, interpolation, fitting</li>
+<li>special functions and statistical functions</li>
+<li>etc. etc.</li>
+</ul>
+</div>
+<div class="slide" id="special-functions">
+<h1>Special functions</h1>
+<p>Airy Functions:</p>
+<pre class="literal-block">
+airy
+--Airy functions and their derivatives.
+airye
+--Exponentially scaled Airy functions
+ai_zeros
+--Zeros of Airy functions Ai(x) and Ai'(x)
+bi_zeros
+--Zeros of Airy functions Bi(x) and Bi'(x)
+</pre>
+</div>
+<div class="slide" id="elliptic-functions-and-integrals">
+<h1>Elliptic Functions and Integrals</h1>
+<pre class="literal-block">
+ellipj
+--Jacobian elliptic functions
+ellipk
+--Complete elliptic integral of the first kind.
+ellipkinc
+--Incomplete elliptic integral of the first kind.
+ellipe
+--Complete elliptic integral of the second kind.
+ellipeinc
+--Incomplete elliptic integral of the second kind.
+</pre>
+</div>
+<div class="slide" id="bessel-functions">
+<h1>Bessel Functions</h1>
+<pre class="literal-block">
+jn
+--Bessel function of integer order and real argument.
+jv
+--Bessel function of real-valued order and complex argument.
+jve
+--Exponentially scaled Bessel function.
+yn
+--Bessel function of second kind (integer order).
+yv
+--Bessel function of the second kind (real-valued order).
+yve
+--Exponentially scaled Bessel function of the second kind.
+kn
+--Modified Bessel function of the third kind (integer order).
+kv
+--Modified Bessel function of the third kind (real order).
+kve
+--Exponentially scaled modified Bessel function of the third kind.
+iv
+--Modified Bessel function.
+ive
+--Exponentially scaled modified Bessel function.
+hankel1
+--Hankel function of the first kind.
+hankel1e
+--Exponentially scaled Hankel function of the first kind.
+hankel2
+--Hankel function of the second kind.
+hankel2e
+--Exponentially scaled Hankel function of the second kind.
+
+lmbda
+--Sequence of lambda functions with arbitrary order v.
+</pre>
+</div>
+<div class="slide" id="zeros-of-bessel-functions">
+<h1>Zeros of Bessel Functions</h1>
+<pre class="literal-block">
+jnjnp_zeros
+--Zeros of integer-order Bessel functions and derivatives
+ sorted in order.
+jnyn_zeros
+--Zeros of integer-order Bessel functions and derivatives
+ as separate arrays.
+jn_zeros
+--Zeros of Jn(x)
+jnp_zeros
+--Zeros of Jn'(x)
+yn_zeros
+--Zeros of Yn(x)
+ynp_zeros
+--Zeros of Yn'(x)
+y0_zeros
+--Complex zeros: Y0(z0)=0 and values of Y0'(z0)
+y1_zeros
+--Complex zeros: Y1(z1)=0 and values of Y1'(z1)
+y1p_zeros
+--Complex zeros of Y1'(z1')=0 and values of Y1(z1')
+</pre>
+</div>
+<div class="slide" id="faster-versions">
+<h1>Faster versions</h1>
+<pre class="literal-block">
+j0
+--Bessel function of order 0.
+j1
+--Bessel function of order 1.
+y0
+--Bessel function of second kind of order 0.
+y1
+--Bessel function of second kind of order 1.
+i0
+--Modified Bessel function of order 0.
+i0e
+--Exponentially scaled modified Bessel function of order 0.
+i1
+--Modified Bessel function of order 1.
+i1e
+--Exponentially scaled modified Bessel function of order 1.
+k0
+--Modified Bessel function of the third kind of order 0.
+k0e
+--Exponentially scaled modified Bessel function of the
+ third kind of order 0.
+k1
+--Modified Bessel function of the third kind of order 1.
+k1e
+--Exponentially scaled modified Bessel function of the
+ third kind of order 1.
+</pre>
+</div>
+<div class="slide" id="integrals-of-bessel-functions">
+<h1>Integrals of Bessel Functions</h1>
+<pre class="literal-block">
+itj0y0
+--Basic integrals of j0 and y0 from 0 to x.
+it2j0y0
+--Integrals of (1-j0(t))/t from 0 to x and
+ y0(t)/t from x to inf.
+iti0k0
+--Basic integrals of i0 and k0 from 0 to x.
+it2i0k0
+--Integrals of (i0(t)-1)/t from 0 to x and
+ k0(t)/t from x to inf.
+besselpoly
+--Integral of a bessel function: Jv(2*a*x) * x^lambda
+ from x=0 to 1.
+</pre>
+</div>
+<div class="slide" id="derivatives-of-bessel-functions">
+<h1>Derivatives of Bessel Functions</h1>
+<pre class="literal-block">
+jvp
+--Nth derivative of Jv(v,z)
+yvp
+--Nth derivative of Yv(v,z)
+kvp
+--Nth derivative of Kv(v,z)
+ivp
+--Nth derivative of Iv(v,z)
+h1vp
+--Nth derivative of H1v(v,z)
+h2vp
+--Nth derivative of H2v(v,z)
+</pre>
+</div>
+<div class="slide" id="spherical-bessel-functions">
+<h1>Spherical Bessel Functions</h1>
+<pre class="literal-block">
+sph_jn
+--Sequence of spherical Bessel functions, jn(z)
+sph_yn
+--Sequence of spherical Bessel functions, yn(z)
+sph_jnyn
+--Sequence of spherical Bessel functions, jn(z) and yn(z)
+sph_in
+--Sequence of spherical Bessel functions, in(z)
+sph_kn
+--Sequence of spherical Bessel functions, kn(z)
+sph_inkn
+--Sequence of spherical Bessel functions, in(z) and kn(z)
+</pre>
+</div>
+<div class="slide" id="riccati-bessel-fun">
+<h1>Riccati-Bessel Fun.</h1>
+<pre class="literal-block">
+riccati_jn
+--Sequence of Ricatti-Bessel functions
+ of first kind.
+riccati_yn
+--Sequence of Ricatti-Bessel functions
+ of second kind.
+</pre>
+</div>
+<div class="slide" id="struve-functions">
+<h1>Struve Functions</h1>
+<pre class="literal-block">
+struve
+--Struve function --- Hv(x)
+modstruve
+--Modified struve function --- Lv(x)
+itstruve0
+--Integral of H0(t) from 0 to x
+it2struve0
+--Integral of H0(t)/t from x to Inf.
+itmodstruve0
+--Integral of L0(t) from 0 to x.
+</pre>
+</div>
+<div class="slide" id="statistical-functions">
+<h1>Statistical Functions</h1>
+<pre class="literal-block">
+bdtr
+--Sum of terms 0 through k of of the binomial pdf.
+bdtrc
+--Sum of terms k+1 through n of the binomial pdf.
+bdtri
+--Inverse of bdtr
+btdtr
+--Integral from 0 to x of beta pdf.
+btdtri
+--Quantiles of beta distribution
+fdtr
+--Integral from 0 to x of F pdf.
+fdtrc
+--Integral from x to infinity under F pdf.
+fdtri
+--Inverse of fdtrc
+gdtr
+--Integral from 0 to x of gamma pdf.
+gdtrc
+--Integral from x to infinity under gamma pdf.
+gdtri
+--Quantiles of gamma distribution
+nbdtr
+--Sum of terms 0 through k of the negative binomial pdf.
+nbdtrc
+--Sum of terms k+1 to infinity under negative binomial pdf.
+nbdtri
+--Inverse of nbdtr
+pdtr
+--Sum of terms 0 through k of the Poisson pdf.
+pdtrc
+--Sum of terms k+1 to infinity of the Poisson pdf.
+pdtri
+--Inverse of pdtr
+stdtr
+--Integral from -infinity to t of the Student-t pdf.
+stdtri
+--Inverse of stdtr (quantiles)
+chdtr
+--Integral from 0 to x of the Chi-square pdf.
+chdtrc
+--Integral from x to infnity of Chi-square pdf.
+chdtri
+--Inverse of chdtrc.
+ndtr
+--Integral from -infinity to x of standard normal pdf
+ndtri
+--Inverse of ndtr (quantiles)
+smirnov
+--Kolmogorov-Smirnov complementary CDF for one-sided
+ test statistic (Dn+ or Dn-)
+smirnovi
+--Inverse of smirnov.
+kolmogorov
+--The complementary CDF of the (scaled) two-sided test
+ statistic (Kn*) valid for large n.
+kolmogi
+--Inverse of kolmogorov
+tklmbda
+--Tukey-Lambda CDF
+</pre>
+</div>
+<div class="slide" id="gamma-and-related-functions">
+<h1>Gamma and Related Functions</h1>
+<pre class="literal-block">
+gamma
+--Gamma function.
+gammaln
+--Log of the absolute value of the gamma function.
+gammainc
+--Incomplete gamma integral.
+gammaincinv
+--Inverse of gammainc.
+gammaincc
+--Complemented incomplete gamma integral.
+gammainccinv
+--Inverse of gammaincc.
+beta
+--Beta function.
+betaln
+--Log of the absolute value of the beta function.
+betainc
+--Incomplete beta integral.
+betaincinv
+--Inverse of betainc.
+betaincinva
+--Inverse (in first argument, a) of betainc
+betaincinvb
+--Inverse (in first argument, b) of betainc
+psi(digamma)
+--Logarithmic derivative of the gamma function.
+rgamma
+--One divided by the gamma function.
+polygamma
+--Nth derivative of psi function.
+</pre>
+</div>
+<div class="slide" id="error-function-and-fresnel-int">
+<h1>Error Function and Fresnel Int.</h1>
+<pre class="literal-block">
+erf
+--Error function.
+erfc
+--Complemented error function (1- erf(x))
+erfinv
+--Inverse of error function
+erfcinv
+--Inverse of erfc
+erf_zeros
+--Complex zeros of erf(z)
+fresnel
+--Fresnel sine and cosine integrals.
+fresnel_zeros
+--Complex zeros of both Fresnel integrals
+fresnelc_zeros
+--Complex zeros of fresnel cosine integrals
+fresnels_zeros
+--Complex zeros of fresnel sine integrals
+modfresnelp
+--Modified Fresnel integrals F_+(x) and K_+(x)
+modfresnelm
+--Modified Fresnel integrals F_-(x) and K_-(x)
+</pre>
+</div>
+<div class="slide" id="legendre-functions">
+<h1>Legendre Functions</h1>
+<pre class="literal-block">
+lpn
+--Legendre Functions (polynomials) of the first kind
+lqn
+--Legendre Functions of the second kind.
+lpmn
+--Associated Legendre Function of the first kind.
+lqmn
+--Associated Legendre Function of the second kind.
+lpmv
+--Associated Legendre Function of arbitrary non-negative
+ degree v.
+sph_harm
+--Spherical Harmonics (complex-valued) Y^m_n(theta,phi)
+</pre>
+</div>
+<div class="slide" id="orthogonal-polyn">
+<h1>Orthogonal polyn.</h1>
+<pre class="literal-block">
+legendre
+--Legendre polynomial P_n(x)
+chebyt
+--Chebyshev polynomial T_n(x)
+chebyu
+--Chebyshev polynomial U_n(x)
+chebyc
+--Chebyshev polynomial C_n(x)
+chebys
+--Chebyshev polynomial S_n(x)
+jacobi
+--Jacobi polynomial P^(alpha,beta)_n(x)
+laguerre
+--Laguerre polynomial, L_n(x)
+genlaguerre
+--Generalized (Associated) Laguerre polynomial, L^alpha_n(x)
+hermite
+--Hermite polynomial H_n(x)
+hermitenorm
+--Normalized Hermite polynomial, He_n(x)
+gegenbauer
+--Gegenbauer (Ultraspherical) polynomials, C^(alpha)_n(x)
+sh_legendre
+--shifted Legendre polynomial, P*_n(x)
+sh_chebyt
+--shifted Chebyshev polynomial, T*_n(x)
+sh_chebyu
+--shifted Chebyshev polynomial, U*_n(x)
+sh_jacobi
+--shifted Jacobi polynomial, J*_n(x) = G^(p,q)_n(x)
+</pre>
+</div>
+<div class="slide" id="hypergeometric-functions">
+<h1>HyperGeometric Functions</h1>
+<pre class="literal-block">
+hyp2f1
+--Gauss hypergeometric function (2F1)
+hyp1f1
+--Confluent hypergeometric function (1F1)
+hyperu
+--Confluent hypergeometric function (U)
+hyp0f1
+--Confluent hypergeometric limit function (0F1)
+hyp2f0
+--Hypergeometric function (2F0)
+hyp1f2
+--Hypergeometric function (1F2)
+hyp3f0
+--Hypergeometric function (3F0)
+</pre>
+</div>
+<div class="slide" id="parabolic-cylinder-functions">
+<h1>Parabolic Cylinder Functions</h1>
+<pre class="literal-block">
+pbdv
+--Parabolic cylinder function Dv(x) and derivative.
+pbvv
+--Parabolic cylinder function Vv(x) and derivative.
+pbwa
+--Parabolic cylinder function W(a,x) and derivative.
+pbdv_seq
+--Sequence of parabolic cylinder functions Dv(x)
+pbvv_seq
+--Sequence of parabolic cylinder functions Vv(x)
+pbdn_seq
+--Sequence of parabolic cylinder functions Dn(z), complex z
+</pre>
+</div>
+<div class="slide" id="mathieu-functions">
+<h1>Mathieu functions</h1>
+<pre class="literal-block">
+mathieu_a
+--Characteristic values for even solution (ce_m)
+mathieu_b
+--Characteristic values for odd solution (se_m)
+mathieu_even_coef
+--sequence of expansion coefficients for even solution
+mathieu_odd_coef
+--sequence of expansion coefficients for odd solution
+ ** All the following return both function and first derivative **
+mathieu_cem
+--Even mathieu function
+mathieu_sem
+--Odd mathieu function
+mathieu_modcem1
+--Even modified mathieu function of the first kind
+mathieu_modcem2
+--Even modified mathieu function of the second kind
+mathieu_modsem1
+--Odd modified mathieu function of the first kind
+mathieu_modsem2
+--Odd modified mathieu function of the second kind
+</pre>
+</div>
+<div class="slide" id="spheroidal-wave-functions">
+<h1>Spheroidal Wave Functions</h1>
+<pre class="literal-block">
+pro_ang1
+--Prolate spheroidal angular function of the first kind
+pro_rad1
+--Prolate spheroidal radial function of the first kind
+pro_rad2
+--Prolate spheroidal radial function of the second kind
+obl_ang1
+--Oblate spheroidal angluar function of the first kind
+obl_rad1
+--Oblate spheroidal radial function of the first kind
+obl_rad2
+--Oblate spheroidal radial function of the second kind
+pro_cv
+--Compute characteristic value for prolate functions
+obl_cv
+--Compute characteristic value for oblate functions
+pro_cv_seq
+--Compute sequence of prolate characteristic values
+obl_cv_seq
+--Compute sequence of oblate characteristic values
+ ** The following functions require pre-computed characteristic values **
+pro_ang1_cv
+--Prolate spheroidal angular function of the first kind
+pro_rad1_cv
+--Prolate spheroidal radial function of the first kind
+pro_rad2_cv
+--Prolate spheroidal radial function of the second kind
+obl_ang1_cv
+--Oblate spheroidal angluar function of the first kind
+obl_rad1_cv
+--Oblate spheroidal radial function of the first kind
+obl_rad2_cv
+--Oblate spheroidal radial function of the second kind
+</pre>
+</div>
+<div class="slide" id="kelvin-functions">
+<h1>Kelvin Functions</h1>
+<pre class="literal-block">
+kelvin
+--All Kelvin functions (order 0) and derivatives.
+kelvin_zeros
+--Zeros of All Kelvin functions (order 0) and derivatives
+ber
+--Kelvin function ber x
+bei
+--Kelvin function bei x
+berp
+--Derivative of Kelvin function ber x
+beip
+--Derivative of Kelvin function bei x
+ker
+--Kelvin function ker x
+kei
+--Kelvin function kei x
+kerp
+--Derivative of Kelvin function ker x
+keip
+--Derivative of Kelvin function kei x
+ber_zeros
+--Zeros of Kelvin function bei x
+bei_zeros
+--Zeros of Kelvin function ber x
+berp_zeros
+--Zeros of derivative of Kelvin function ber x
+beip_zeros
+--Zeros of derivative of Kelvin function bei x
+ker_zeros
+--Zeros of Kelvin function kei x
+kei_zeros
+--Zeros of Kelvin function ker x
+kerp_zeros
+--Zeros of derivative of Kelvin function ker x
+keip_zeros
+--Zeros of derivative of Kelvin function kei x
+</pre>
+</div>
+<div class="slide" id="other-special-functions">
+<h1>Other Special Functions</h1>
+<pre class="literal-block">
+expn
+--Exponential integral.
+exp1
+--Exponential integral of order 1 (for complex argument)
+expi
+--Another exponential integral
+--Ei(x)
+wofz
+--Fadeeva function.
+dawsn
+--Dawson's integral.
+shichi
+--Hyperbolic sine and cosine integrals.
+sici
+--Integral of the sinc and &quot;cosinc&quot; functions.
+spence
+--Dilogarithm integral.
+zeta
+--Riemann zeta function of two arguments.
+zetac
+--1.0 - standard Riemann zeta function.
+</pre>
+</div>
+<div class="slide" id="convenience-functions">
+<h1>Convenience Functions</h1>
+<pre class="literal-block">
+cbrt
+--Cube root.
+exp10
+--10 raised to the x power.
+exp2
+--2 raised to the x power.
+radian
+--radian angle given degrees, minutes, and seconds.
+cosdg
+--cosine of the angle given in degrees.
+sindg
+--sine of the angle given in degrees.
+tandg
+--tangent of the angle given in degrees.
+cotdg
+--cotangent of the angle given in degrees.
+log1p
+--log(1+x)
+expm1
+--exp(x)-1
+cosm1
+--cos(x)-1
+round
+--round the argument to the nearest integer. If argument
+ ends in 0.5 exactly, pick the nearest even integer.
+</pre>
+</div>
+<div class="slide" id="and-more">
+<h1>... and more!</h1>
+<p>but let us go back to our problem</p>
+<ul class="incremental simple">
+<li>at the present we are cleaning our histories in production with a
+quick and dirty criterium;</li>
+<li>we want to be able to see the histories case by case in order to take
+specific actions;</li>
+<li>we want to go on the Web (--&gt; next)</li>
+</ul>
+</div>
+<div class="slide" id="going-on-the-web">
+<h1>Going on the Web</h1>
+<ul class="incremental simple">
+<li>we want a simple tool for internal usage on our intranet;</li>
+<li>convenient to integrate with other Web tools;</li>
+<li>usable also for non-techical users;</li>
+<li>avoid installing and mantaining on every machine;</li>
+<li>possibly we may open it to our other offices in the world;</li>
+<li>we like the browser interface.</li>
+</ul>
+</div>
+<div class="slide" id="without-a-framework">
+<h1>Without a framework</h1>
+<ul class="incremental simple">
+<li>no security concerns;</li>
+<li>no scalability concerns;</li>
+<li>no nice-looking concerns;</li>
+<li>it must be <em>EASY</em> to change;</li>
+<li>we want minimal learning curve;</li>
+<li>we want no installation/configuration hassle;</li>
+<li>we want no dependencies;</li>
+<li>we want something even simpler than CGI, if possible!</li>
+</ul>
+</div>
+<div class="slide" id="enter-wsgi">
+<h1>Enter WSGI</h1>
+<ul class="incremental simple">
+<li>WSGI = Web Server Gateway Interface (<em>Whiskey</em> for friends)</li>
+<li>the brainchild of Python guru Phillip J. Eby;</li>
+<li>also input from Ian Bicking (<tt class="docutils literal"><span class="pre">paste</span></tt>) and others;</li>
+<li>starting from Python 2.5, we have a WSGI web server in the standard
+library (<tt class="docutils literal"><span class="pre">wsgiref</span></tt>);</li>
+<li>there are plenty of simple and useful add-ons for WSGI applications
+out there;</li>
+<li>even <tt class="docutils literal"><span class="pre">wsgiref</span></tt> fullfills all of our requirements, let's use it!
+(following the example of <a class="reference" href="http://video.google.com/videoplay?docid=-8502904076440714866">Guido</a> ...)</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-key-concepts">
+<h1>WSGI key concepts</h1>
+<ol class="incremental arabic">
+<li><p class="first">WSGI application:</p>
+<p>(env, resp) -&gt; chunks of text</p>
+<p>env = environment dictionary of the server;
+resp = function sending to the client the HTTP headers;</p>
+</li>
+<li><p class="first">WSGI middleware:</p>
+<p>WSGI app -&gt; enhanced WSGI app</p>
+</li>
+</ol>
+</div>
+<div class="slide" id="ex1-hello-world">
+<h1>Ex1: Hello World</h1>
+<pre class="literal-block">
+from wsgiref import simple_server
+
+def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['&lt;h1&gt;Hello, World!&lt;/h1&gt;']
+
+server=simple_server.make_server('', 8000, app)
+server.serve_forever()
+</pre>
+<p><a class="reference" href="http://localhost:8000">show live example</a></p>
+</div>
+<div class="slide" id="ex2-middleware">
+<h1>Ex2: middleware</h1>
+<p>No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware:</p>
+<pre class="literal-block">
+from paste.auth.basic import AuthBasicHandler
+
+def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+</pre>
+</div>
+<div class="slide" id="other-middleware">
+<h1>Other middleware</h1>
+<pre class="literal-block">
+from wsgiref.simple_server import make_server
+from paste.evalexception import EvalException
+
+a, b = 1,0
+
+def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+make_server('', 9090, EvalException(app)).serve_forever()
+</pre>
+<p>Show <a class="reference" href="http://localhost:9090">evalexception</a></p>
+</div>
+<div class="slide" id="wsgi-vs-cgi">
+<h1>WSGI vs. CGI</h1>
+<ul class="simple">
+<li>WSGI is simpler than CGI<ul>
+<li><span class="incremental">using wsgiref you don't require an external server</span></li>
+<li><span class="incremental">you can keep sessions in memory</span></li>
+</ul>
+</li>
+<li>WSGI scales better than CGI<ul>
+<li><span class="incremental">there is a large choice of wsgi servers (mod_wsgi, Twisted ...)</span></li>
+<li><span class="incremental">there is a large choice of third party middleware</span></li>
+<li><span class="incremental">it is relatively easy to turn a toy application into a serious one</span></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="slide" id="wsgi-vs-frameworks">
+<h1>WSGI vs. frameworks</h1>
+<p>Pro:</p>
+<ul class="simple">
+<li><span class="incremental">if you liked playing with Lego, you will be happy</span></li>
+<li><span class="incremental">you have much more control and you are not forced to marry a technology</span></li>
+<li><span class="incremental">you can learn a lot</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="id1">
+<h1>WSGI vs. frameworks</h1>
+<p>Contra:</p>
+<ul class="simple">
+<li><span class="incremental">you can build your own framework with WSGI, but you have to debug it</span></li>
+<li><span class="incremental">the existing WSGI frameworks are newer, there is less experience with them</span></li>
+<li><span class="incremental">WSGI is not particularly Twisted-friendly</span></li>
+<li><span class="incremental">others ...</span></li>
+</ul>
+</div>
+<div class="slide" id="the-history-plotter">
+<h1>The history plotter</h1>
+<pre class="literal-block">
+$ python webplotter.py
+</pre>
+<p><a class="reference" href="http://localhost:8000">Click here for the live demonstration</a></p>
+</div>
+<div class="slide" id="references">
+<h1>References</h1>
+<p>That's all, folks!</p>
+<ul class="simple">
+<li><a class="reference" href="http://www.scipy.org">http://www.scipy.org</a></li>
+<li><a class="reference" href="http://www.python.org/dev/peps/pep-0333">http://www.python.org/dev/peps/pep-0333</a></li>
+<li><a class="reference" href="http://pythonpaste.org/do-it-yourself-framework.html">http://pythonpaste.org/do-it-yourself-framework.html</a></li>
+</ul>
+<p class="incremental"><strong>(P.S. at StatPro, we are hiring! ;)</strong></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/pypers/wsgi/talk.txt b/pypers/wsgi/talk.txt
new file mode 100644
index 0000000..f3c0a3a
--- /dev/null
+++ b/pypers/wsgi/talk.txt
@@ -0,0 +1,962 @@
+SciPy on WSGI
+======================================
+
+:Talk given at: PyCon Uno 2007
+:By: Michele Simionato
+:organization: StatPro Italy
+:date: 2007-06-09
+
+.. include:: <s5defs.txt>
+.. footer:: PyCon Uno 2007 - 09 June 2007
+
+.. class:: center
+
+**Subtitle**: *Science on the Web for pedestrians*
+
+Before I start
+------------------------------------------------
+
+What about you?
+
+.. class:: incremental
+
+ Are you more of a programmer or more of a scientist/engineer?
+
+ What kind of scientific tools are you using, if any?
+
+ Have you ever heard of SciPy?
+
+ Have you ever heard of WSGI?
+
+
+Ok, now I can begin ;)
+----------------------------
+
+The motivation from this talk comes from a real problem at StatPro_
+
+.. _StatPro: http://www.statpro.com
+
+.. class:: incremental
+
+we have bad histories for many financial products
+
+.. class:: incremental
+
+wrong prices at some dates in the past
+
+
+A picture
+-------------------------------------
+
+.. image:: badpricehistory.png
+
+.. class:: incremental
+
+(damn data providers!)
+
+Discarding values ...
+---------------------------------------------------
+
+.. image:: badpricehistory2.png
+
+... is tricky!
+
+Issues
+---------------------------
+
+We cannot use the conventional criterium
+
+.. class:: incremental
+
+.. image:: nongaussian.png
+
+Strategy
+---------------------------------------
+
+.. class:: incremental
+
+- price distributions (ln p_i/p) are known to decay with power laws
+
+- fit the distributions with a "reasonable" curve and determine
+ a suitable criterium for the spikes at some confidence level
+
+- a reasonably simple ansatz gives a family of distributions depending
+ on a parameter delta
+
+- `show formulae`_
+
+.. _show formulae: formulas.pdf
+
+delta-distribution
+-----------------------------------------
+
+.. image:: delta-dist.png
+
+From Dirac delta (delta -> 0) to Gaussian
+distribution (delta -> oo)
+
+Cumulative dist
+------------------------------------------
+
+.. image:: cdf-dist.png
+
+VAR-XX = Max loss at XX% confidence level [i_]
+
+.. _i: http://integrals.wolfram.com/index.jsp
+
+Relation VAR-vol.
+-------------------------------------------
+
+If you assume a given distribution, there is a fixed relation between
+VAR-XX and volatility
+
+.. class:: incremental
+
+- for the Gaussian VAR-95 = 1.64 sigma, for a lot of our distributions
+ VAR-95 < 1.0 sigma
+
+- we don't want to make assumptions on the distribution function
+ for computing the VAR
+
+- but we are willing to make assumptions for the sake of eliminating
+ statistically invalid values
+
+The tool we need
+----------------------------------------
+
+::
+
+ $ python simpleplotter.py "fri-gb;AVE"
+
+
+(not 100% finished yet!)
+
+Enter SciPy & Co.
+----------------------------------------
+
+In order to perform our analysis we looked
+at many scientific tools
+
+.. class:: incremental
+
+- a good plotting tool (matplotlib)
+- support for histograms (matplotlib)
+- support for special functions (scipy.special)
+- support for non-linear fitting (scipy.leastsq)
+- good performance (scipy)
+- interactive and IPython-friendly (scipy)
+- bla-bla
+- cheating: I actually used Gnuplot!! ;-)
+
+Installation
+------------------------------------------
+
+If you are lucky, it is trivial::
+
+ $ apt-get install ipython
+ $ apt-get install python-matplotlib
+ $ apt-get install python-numpy
+ $ apt-get install python-numpy-ext
+ $ apt-get install python-scipy
+
+If you are unlucky, or if you try to build from sources,
+YMMV ...
+
+What's in Scipy
+--------------------------------------
+
+.. class:: incremental
+
+- support for multi-dimensional array (numpy)
+- linear algebra and minimization routines
+- solving, integration, interpolation, fitting
+- special functions and statistical functions
+- etc. etc.
+
+Special functions
+--------------------------------------
+
+Airy Functions::
+
+ airy
+ --Airy functions and their derivatives.
+ airye
+ --Exponentially scaled Airy functions
+ ai_zeros
+ --Zeros of Airy functions Ai(x) and Ai'(x)
+ bi_zeros
+ --Zeros of Airy functions Bi(x) and Bi'(x)
+
+Elliptic Functions and Integrals
+--------------------------------------
+
+::
+
+ ellipj
+ --Jacobian elliptic functions
+ ellipk
+ --Complete elliptic integral of the first kind.
+ ellipkinc
+ --Incomplete elliptic integral of the first kind.
+ ellipe
+ --Complete elliptic integral of the second kind.
+ ellipeinc
+ --Incomplete elliptic integral of the second kind.
+
+Bessel Functions
+--------------------------------------
+
+::
+
+ jn
+ --Bessel function of integer order and real argument.
+ jv
+ --Bessel function of real-valued order and complex argument.
+ jve
+ --Exponentially scaled Bessel function.
+ yn
+ --Bessel function of second kind (integer order).
+ yv
+ --Bessel function of the second kind (real-valued order).
+ yve
+ --Exponentially scaled Bessel function of the second kind.
+ kn
+ --Modified Bessel function of the third kind (integer order).
+ kv
+ --Modified Bessel function of the third kind (real order).
+ kve
+ --Exponentially scaled modified Bessel function of the third kind.
+ iv
+ --Modified Bessel function.
+ ive
+ --Exponentially scaled modified Bessel function.
+ hankel1
+ --Hankel function of the first kind.
+ hankel1e
+ --Exponentially scaled Hankel function of the first kind.
+ hankel2
+ --Hankel function of the second kind.
+ hankel2e
+ --Exponentially scaled Hankel function of the second kind.
+
+ lmbda
+ --Sequence of lambda functions with arbitrary order v.
+
+Zeros of Bessel Functions
+--------------------------------------
+
+::
+
+ jnjnp_zeros
+ --Zeros of integer-order Bessel functions and derivatives
+ sorted in order.
+ jnyn_zeros
+ --Zeros of integer-order Bessel functions and derivatives
+ as separate arrays.
+ jn_zeros
+ --Zeros of Jn(x)
+ jnp_zeros
+ --Zeros of Jn'(x)
+ yn_zeros
+ --Zeros of Yn(x)
+ ynp_zeros
+ --Zeros of Yn'(x)
+ y0_zeros
+ --Complex zeros: Y0(z0)=0 and values of Y0'(z0)
+ y1_zeros
+ --Complex zeros: Y1(z1)=0 and values of Y1'(z1)
+ y1p_zeros
+ --Complex zeros of Y1'(z1')=0 and values of Y1(z1')
+
+Faster versions
+--------------------------------------
+
+::
+
+ j0
+ --Bessel function of order 0.
+ j1
+ --Bessel function of order 1.
+ y0
+ --Bessel function of second kind of order 0.
+ y1
+ --Bessel function of second kind of order 1.
+ i0
+ --Modified Bessel function of order 0.
+ i0e
+ --Exponentially scaled modified Bessel function of order 0.
+ i1
+ --Modified Bessel function of order 1.
+ i1e
+ --Exponentially scaled modified Bessel function of order 1.
+ k0
+ --Modified Bessel function of the third kind of order 0.
+ k0e
+ --Exponentially scaled modified Bessel function of the
+ third kind of order 0.
+ k1
+ --Modified Bessel function of the third kind of order 1.
+ k1e
+ --Exponentially scaled modified Bessel function of the
+ third kind of order 1.
+
+Integrals of Bessel Functions
+--------------------------------------
+
+::
+
+ itj0y0
+ --Basic integrals of j0 and y0 from 0 to x.
+ it2j0y0
+ --Integrals of (1-j0(t))/t from 0 to x and
+ y0(t)/t from x to inf.
+ iti0k0
+ --Basic integrals of i0 and k0 from 0 to x.
+ it2i0k0
+ --Integrals of (i0(t)-1)/t from 0 to x and
+ k0(t)/t from x to inf.
+ besselpoly
+ --Integral of a bessel function: Jv(2*a*x) * x^lambda
+ from x=0 to 1.
+
+Derivatives of Bessel Functions
+--------------------------------------
+
+::
+
+ jvp
+ --Nth derivative of Jv(v,z)
+ yvp
+ --Nth derivative of Yv(v,z)
+ kvp
+ --Nth derivative of Kv(v,z)
+ ivp
+ --Nth derivative of Iv(v,z)
+ h1vp
+ --Nth derivative of H1v(v,z)
+ h2vp
+ --Nth derivative of H2v(v,z)
+
+Spherical Bessel Functions
+--------------------------------------
+
+::
+
+ sph_jn
+ --Sequence of spherical Bessel functions, jn(z)
+ sph_yn
+ --Sequence of spherical Bessel functions, yn(z)
+ sph_jnyn
+ --Sequence of spherical Bessel functions, jn(z) and yn(z)
+ sph_in
+ --Sequence of spherical Bessel functions, in(z)
+ sph_kn
+ --Sequence of spherical Bessel functions, kn(z)
+ sph_inkn
+ --Sequence of spherical Bessel functions, in(z) and kn(z)
+
+Riccati-Bessel Fun.
+--------------------------------------
+
+::
+
+ riccati_jn
+ --Sequence of Ricatti-Bessel functions
+ of first kind.
+ riccati_yn
+ --Sequence of Ricatti-Bessel functions
+ of second kind.
+
+Struve Functions
+--------------------------------------
+
+::
+
+ struve
+ --Struve function --- Hv(x)
+ modstruve
+ --Modified struve function --- Lv(x)
+ itstruve0
+ --Integral of H0(t) from 0 to x
+ it2struve0
+ --Integral of H0(t)/t from x to Inf.
+ itmodstruve0
+ --Integral of L0(t) from 0 to x.
+
+
+Statistical Functions
+--------------------------------------
+
+::
+
+ bdtr
+ --Sum of terms 0 through k of of the binomial pdf.
+ bdtrc
+ --Sum of terms k+1 through n of the binomial pdf.
+ bdtri
+ --Inverse of bdtr
+ btdtr
+ --Integral from 0 to x of beta pdf.
+ btdtri
+ --Quantiles of beta distribution
+ fdtr
+ --Integral from 0 to x of F pdf.
+ fdtrc
+ --Integral from x to infinity under F pdf.
+ fdtri
+ --Inverse of fdtrc
+ gdtr
+ --Integral from 0 to x of gamma pdf.
+ gdtrc
+ --Integral from x to infinity under gamma pdf.
+ gdtri
+ --Quantiles of gamma distribution
+ nbdtr
+ --Sum of terms 0 through k of the negative binomial pdf.
+ nbdtrc
+ --Sum of terms k+1 to infinity under negative binomial pdf.
+ nbdtri
+ --Inverse of nbdtr
+ pdtr
+ --Sum of terms 0 through k of the Poisson pdf.
+ pdtrc
+ --Sum of terms k+1 to infinity of the Poisson pdf.
+ pdtri
+ --Inverse of pdtr
+ stdtr
+ --Integral from -infinity to t of the Student-t pdf.
+ stdtri
+ --Inverse of stdtr (quantiles)
+ chdtr
+ --Integral from 0 to x of the Chi-square pdf.
+ chdtrc
+ --Integral from x to infnity of Chi-square pdf.
+ chdtri
+ --Inverse of chdtrc.
+ ndtr
+ --Integral from -infinity to x of standard normal pdf
+ ndtri
+ --Inverse of ndtr (quantiles)
+ smirnov
+ --Kolmogorov-Smirnov complementary CDF for one-sided
+ test statistic (Dn+ or Dn-)
+ smirnovi
+ --Inverse of smirnov.
+ kolmogorov
+ --The complementary CDF of the (scaled) two-sided test
+ statistic (Kn*) valid for large n.
+ kolmogi
+ --Inverse of kolmogorov
+ tklmbda
+ --Tukey-Lambda CDF
+
+Gamma and Related Functions
+--------------------------------------
+
+::
+
+ gamma
+ --Gamma function.
+ gammaln
+ --Log of the absolute value of the gamma function.
+ gammainc
+ --Incomplete gamma integral.
+ gammaincinv
+ --Inverse of gammainc.
+ gammaincc
+ --Complemented incomplete gamma integral.
+ gammainccinv
+ --Inverse of gammaincc.
+ beta
+ --Beta function.
+ betaln
+ --Log of the absolute value of the beta function.
+ betainc
+ --Incomplete beta integral.
+ betaincinv
+ --Inverse of betainc.
+ betaincinva
+ --Inverse (in first argument, a) of betainc
+ betaincinvb
+ --Inverse (in first argument, b) of betainc
+ psi(digamma)
+ --Logarithmic derivative of the gamma function.
+ rgamma
+ --One divided by the gamma function.
+ polygamma
+ --Nth derivative of psi function.
+
+Error Function and Fresnel Int.
+--------------------------------------
+
+::
+
+ erf
+ --Error function.
+ erfc
+ --Complemented error function (1- erf(x))
+ erfinv
+ --Inverse of error function
+ erfcinv
+ --Inverse of erfc
+ erf_zeros
+ --Complex zeros of erf(z)
+ fresnel
+ --Fresnel sine and cosine integrals.
+ fresnel_zeros
+ --Complex zeros of both Fresnel integrals
+ fresnelc_zeros
+ --Complex zeros of fresnel cosine integrals
+ fresnels_zeros
+ --Complex zeros of fresnel sine integrals
+ modfresnelp
+ --Modified Fresnel integrals F_+(x) and K_+(x)
+ modfresnelm
+ --Modified Fresnel integrals F_-(x) and K_-(x)
+
+Legendre Functions
+--------------------------------------
+
+::
+
+ lpn
+ --Legendre Functions (polynomials) of the first kind
+ lqn
+ --Legendre Functions of the second kind.
+ lpmn
+ --Associated Legendre Function of the first kind.
+ lqmn
+ --Associated Legendre Function of the second kind.
+ lpmv
+ --Associated Legendre Function of arbitrary non-negative
+ degree v.
+ sph_harm
+ --Spherical Harmonics (complex-valued) Y^m_n(theta,phi)
+
+Orthogonal polyn.
+--------------------------------------
+
+::
+
+ legendre
+ --Legendre polynomial P_n(x)
+ chebyt
+ --Chebyshev polynomial T_n(x)
+ chebyu
+ --Chebyshev polynomial U_n(x)
+ chebyc
+ --Chebyshev polynomial C_n(x)
+ chebys
+ --Chebyshev polynomial S_n(x)
+ jacobi
+ --Jacobi polynomial P^(alpha,beta)_n(x)
+ laguerre
+ --Laguerre polynomial, L_n(x)
+ genlaguerre
+ --Generalized (Associated) Laguerre polynomial, L^alpha_n(x)
+ hermite
+ --Hermite polynomial H_n(x)
+ hermitenorm
+ --Normalized Hermite polynomial, He_n(x)
+ gegenbauer
+ --Gegenbauer (Ultraspherical) polynomials, C^(alpha)_n(x)
+ sh_legendre
+ --shifted Legendre polynomial, P*_n(x)
+ sh_chebyt
+ --shifted Chebyshev polynomial, T*_n(x)
+ sh_chebyu
+ --shifted Chebyshev polynomial, U*_n(x)
+ sh_jacobi
+ --shifted Jacobi polynomial, J*_n(x) = G^(p,q)_n(x)
+
+HyperGeometric Functions
+--------------------------------------
+
+::
+
+ hyp2f1
+ --Gauss hypergeometric function (2F1)
+ hyp1f1
+ --Confluent hypergeometric function (1F1)
+ hyperu
+ --Confluent hypergeometric function (U)
+ hyp0f1
+ --Confluent hypergeometric limit function (0F1)
+ hyp2f0
+ --Hypergeometric function (2F0)
+ hyp1f2
+ --Hypergeometric function (1F2)
+ hyp3f0
+ --Hypergeometric function (3F0)
+
+Parabolic Cylinder Functions
+--------------------------------------
+
+::
+
+ pbdv
+ --Parabolic cylinder function Dv(x) and derivative.
+ pbvv
+ --Parabolic cylinder function Vv(x) and derivative.
+ pbwa
+ --Parabolic cylinder function W(a,x) and derivative.
+ pbdv_seq
+ --Sequence of parabolic cylinder functions Dv(x)
+ pbvv_seq
+ --Sequence of parabolic cylinder functions Vv(x)
+ pbdn_seq
+ --Sequence of parabolic cylinder functions Dn(z), complex z
+
+Mathieu functions
+--------------------------------------
+
+::
+
+ mathieu_a
+ --Characteristic values for even solution (ce_m)
+ mathieu_b
+ --Characteristic values for odd solution (se_m)
+ mathieu_even_coef
+ --sequence of expansion coefficients for even solution
+ mathieu_odd_coef
+ --sequence of expansion coefficients for odd solution
+ ** All the following return both function and first derivative **
+ mathieu_cem
+ --Even mathieu function
+ mathieu_sem
+ --Odd mathieu function
+ mathieu_modcem1
+ --Even modified mathieu function of the first kind
+ mathieu_modcem2
+ --Even modified mathieu function of the second kind
+ mathieu_modsem1
+ --Odd modified mathieu function of the first kind
+ mathieu_modsem2
+ --Odd modified mathieu function of the second kind
+
+Spheroidal Wave Functions
+--------------------------------------
+
+::
+
+ pro_ang1
+ --Prolate spheroidal angular function of the first kind
+ pro_rad1
+ --Prolate spheroidal radial function of the first kind
+ pro_rad2
+ --Prolate spheroidal radial function of the second kind
+ obl_ang1
+ --Oblate spheroidal angluar function of the first kind
+ obl_rad1
+ --Oblate spheroidal radial function of the first kind
+ obl_rad2
+ --Oblate spheroidal radial function of the second kind
+ pro_cv
+ --Compute characteristic value for prolate functions
+ obl_cv
+ --Compute characteristic value for oblate functions
+ pro_cv_seq
+ --Compute sequence of prolate characteristic values
+ obl_cv_seq
+ --Compute sequence of oblate characteristic values
+ ** The following functions require pre-computed characteristic values **
+ pro_ang1_cv
+ --Prolate spheroidal angular function of the first kind
+ pro_rad1_cv
+ --Prolate spheroidal radial function of the first kind
+ pro_rad2_cv
+ --Prolate spheroidal radial function of the second kind
+ obl_ang1_cv
+ --Oblate spheroidal angluar function of the first kind
+ obl_rad1_cv
+ --Oblate spheroidal radial function of the first kind
+ obl_rad2_cv
+ --Oblate spheroidal radial function of the second kind
+
+Kelvin Functions
+--------------------------------------
+
+::
+
+ kelvin
+ --All Kelvin functions (order 0) and derivatives.
+ kelvin_zeros
+ --Zeros of All Kelvin functions (order 0) and derivatives
+ ber
+ --Kelvin function ber x
+ bei
+ --Kelvin function bei x
+ berp
+ --Derivative of Kelvin function ber x
+ beip
+ --Derivative of Kelvin function bei x
+ ker
+ --Kelvin function ker x
+ kei
+ --Kelvin function kei x
+ kerp
+ --Derivative of Kelvin function ker x
+ keip
+ --Derivative of Kelvin function kei x
+ ber_zeros
+ --Zeros of Kelvin function bei x
+ bei_zeros
+ --Zeros of Kelvin function ber x
+ berp_zeros
+ --Zeros of derivative of Kelvin function ber x
+ beip_zeros
+ --Zeros of derivative of Kelvin function bei x
+ ker_zeros
+ --Zeros of Kelvin function kei x
+ kei_zeros
+ --Zeros of Kelvin function ker x
+ kerp_zeros
+ --Zeros of derivative of Kelvin function ker x
+ keip_zeros
+ --Zeros of derivative of Kelvin function kei x
+
+Other Special Functions
+--------------------------------------
+
+::
+
+ expn
+ --Exponential integral.
+ exp1
+ --Exponential integral of order 1 (for complex argument)
+ expi
+ --Another exponential integral
+ --Ei(x)
+ wofz
+ --Fadeeva function.
+ dawsn
+ --Dawson's integral.
+ shichi
+ --Hyperbolic sine and cosine integrals.
+ sici
+ --Integral of the sinc and "cosinc" functions.
+ spence
+ --Dilogarithm integral.
+ zeta
+ --Riemann zeta function of two arguments.
+ zetac
+ --1.0 - standard Riemann zeta function.
+
+Convenience Functions
+--------------------------------------
+
+::
+
+ cbrt
+ --Cube root.
+ exp10
+ --10 raised to the x power.
+ exp2
+ --2 raised to the x power.
+ radian
+ --radian angle given degrees, minutes, and seconds.
+ cosdg
+ --cosine of the angle given in degrees.
+ sindg
+ --sine of the angle given in degrees.
+ tandg
+ --tangent of the angle given in degrees.
+ cotdg
+ --cotangent of the angle given in degrees.
+ log1p
+ --log(1+x)
+ expm1
+ --exp(x)-1
+ cosm1
+ --cos(x)-1
+ round
+ --round the argument to the nearest integer. If argument
+ ends in 0.5 exactly, pick the nearest even integer.
+
+... and more!
+------------------------------------------
+
+but let us go back to our problem
+
+.. class:: incremental
+
+- at the present we are cleaning our histories in production with a
+ quick and dirty criterium;
+- we want to be able to see the histories case by case in order to take
+ specific actions;
+- we want to go on the Web (--> next)
+
+Going on the Web
+-----------------------------------
+
+.. class:: incremental
+
+- we want a simple tool for internal usage on our intranet;
+- convenient to integrate with other Web tools;
+- usable also for non-techical users;
+- avoid installing and mantaining on every machine;
+- possibly we may open it to our other offices in the world;
+- we like the browser interface.
+
+Without a framework
+---------------------------------------------
+
+.. class:: incremental
+
+- no security concerns;
+- no scalability concerns;
+- no nice-looking concerns;
+
+- it must be *EASY* to change;
+- we want minimal learning curve;
+- we want no installation/configuration hassle;
+
+- we want no dependencies;
+- we want something even simpler than CGI, if possible!
+
+Enter WSGI
+-------------------------
+
+.. class:: incremental
+
+- WSGI = Web Server Gateway Interface (*Whiskey* for friends)
+- the brainchild of Python guru Phillip J. Eby;
+- also input from Ian Bicking (``paste``) and others;
+- starting from Python 2.5, we have a WSGI web server in the standard
+ library (``wsgiref``);
+- there are plenty of simple and useful add-ons for WSGI applications
+ out there;
+- even ``wsgiref`` fullfills all of our requirements, let's use it!
+ (following the example of Guido_ ...)
+
+.. _Guido: http://video.google.com/videoplay?docid=-8502904076440714866
+
+WSGI key concepts
+------------------------------------------------
+
+.. class:: incremental
+
+1. WSGI application:
+
+ (env, resp) -> chunks of text
+
+ env = environment dictionary of the server;
+ resp = function sending to the client the HTTP headers;
+
+2. WSGI middleware:
+
+ WSGI app -> enhanced WSGI app
+
+Ex1: Hello World
+-------------------------------
+
+::
+
+ from wsgiref import simple_server
+
+ def app(env, resp):
+ resp(
+ '200 OK', [('Content-type', 'text/html')])
+ return ['<h1>Hello, World!</h1>']
+
+ server=simple_server.make_server('', 8000, app)
+ server.serve_forever()
+
+`show live example`_
+
+.. _`show live example`: http://localhost:8000
+
+Ex2: middleware
+---------------------------
+
+No middleware in the standard library, but lots of useful middleware
+from third party sources. For instance, authentication middleware::
+
+ from paste.auth.basic import AuthBasicHandler
+
+ def only_for_pippo(env, user, passwd):
+ return user == 'pippo'
+
+ auth_app = AuthBasicHandler(
+ app, 'app realm', only_for_pippo)
+
+Other middleware
+-----------------------------------------------
+
+::
+
+ from wsgiref.simple_server import make_server
+ from paste.evalexception import EvalException
+
+ a, b = 1,0
+
+ def app(env, resp):
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [str(a/b)]
+
+ make_server('', 9090, EvalException(app)).serve_forever()
+
+Show evalexception_
+
+.. _evalexception: http://localhost:9090
+
+WSGI vs. CGI
+--------------------------------------------
+
+- WSGI is simpler than CGI
+
+ + `using wsgiref you don't require an external server`
+ + `you can keep sessions in memory`
+
+- WSGI scales better than CGI
+
+ + `there is a large choice of wsgi servers (mod_wsgi, Twisted ...)`
+ + `there is a large choice of third party middleware`
+ + `it is relatively easy to turn a toy application into a serious one`
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Pro:
+
+- `if you liked playing with Lego, you will be happy`
+- `you have much more control and you are not forced to marry a technology`
+- `you can learn a lot`
+- `others ...`
+
+WSGI vs. frameworks
+------------------------------------------------
+
+Contra:
+
+- `you can build your own framework with WSGI, but you have to debug it`
+- `the existing WSGI frameworks are newer, there is less experience with them`
+- `WSGI is not particularly Twisted-friendly`
+- `others ...`
+
+The history plotter
+----------------------------------------------------
+
+::
+
+ $ python webplotter.py
+
+`Click here for the live demonstration`_
+
+.. _`Click here for the live demonstration`: http://localhost:8000
+
+
+References
+-----------
+
+That's all, folks!
+
+
+
+- http://www.scipy.org
+- http://www.python.org/dev/peps/pep-0333
+- http://pythonpaste.org/do-it-yourself-framework.html
+
+.. class:: incremental
+
+ **(P.S. at StatPro, we are hiring! ;)**
diff --git a/pypers/wsgi/test_cdf.py b/pypers/wsgi/test_cdf.py
new file mode 100644
index 0000000..110f870
--- /dev/null
+++ b/pypers/wsgi/test_cdf.py
@@ -0,0 +1,10 @@
+from scipy.integrate import quad
+from delta_dist import cdf, delta
+
+def cdf(d):
+ d2 = d*d
+ Cd = gamma(1.5+d2/2.)/gamma(.5)/gamma(1.+d2/2.)
+ return lambda x: Cd*x*hyp2f1(.5, 1.5+d2/2., 1.5, -x*x)
+
+print quad(delta(.2), 0., 1.0)
+print cdf(.2)(1.0)
diff --git a/pypers/wsgi/thread_log.py b/pypers/wsgi/thread_log.py
new file mode 100644
index 0000000..850f221
--- /dev/null
+++ b/pypers/wsgi/thread_log.py
@@ -0,0 +1,13 @@
+import logging, threading, sys
+
+class C(object):
+ def __init__(self):
+ print threading.currentThread()
+ def __del__(self):
+ logger.warn('%s deleted %r' % (threading.currentThread(), self))
+
+if __name__ == '__main__':
+ logger = logging.getLogger()
+ logger.basicConfig(file='/tmp/x.log')
+ c = C()
+
diff --git a/pypers/wsgi/ui/default-orig/ex.html b/pypers/wsgi/ui/default-orig/ex.html
new file mode 100644
index 0000000..798e3cc
--- /dev/null
+++ b/pypers/wsgi/ui/default-orig/ex.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style type="text/css">
+body
+{
+background: url(statpro_logo.gif) no-repeat top
+}
+</style>
+</head>
+<body>
+Prova Background CSS
+</body>
+</html>
diff --git a/pypers/wsgi/ui/default-orig/slides.js b/pypers/wsgi/ui/default-orig/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/wsgi/ui/default-orig/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/wsgi/ui/default/blank.gif b/pypers/wsgi/ui/default/blank.gif
new file mode 100644
index 0000000..75b945d
--- /dev/null
+++ b/pypers/wsgi/ui/default/blank.gif
Binary files differ
diff --git a/pypers/wsgi/ui/default/ex.html b/pypers/wsgi/ui/default/ex.html
new file mode 100644
index 0000000..798e3cc
--- /dev/null
+++ b/pypers/wsgi/ui/default/ex.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style type="text/css">
+body
+{
+background: url(statpro_logo.gif) no-repeat top
+}
+</style>
+</head>
+<body>
+Prova Background CSS
+</body>
+</html>
diff --git a/pypers/wsgi/ui/default/slides.js b/pypers/wsgi/ui/default/slides.js
new file mode 100644
index 0000000..81e04e5
--- /dev/null
+++ b/pypers/wsgi/ui/default/slides.js
@@ -0,0 +1,558 @@
+// S5 v1.1 slides.js -- released into the Public Domain
+// Modified for Docutils (http://docutils.sf.net) by David Goodger
+//
+// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for
+// information about all the wonderful and talented contributors to this code!
+
+var undef;
+var slideCSS = '';
+var snum = 0;
+var smax = 1;
+var slideIDs = new Array();
+var incpos = 0;
+var number = undef;
+var s5mode = true;
+var defaultView = 'slideshow';
+var controlVis = 'visible';
+
+var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0;
+var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0;
+var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0;
+
+function hasClass(object, className) {
+ if (!object.className) return false;
+ return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1);
+}
+
+function hasValue(object, value) {
+ if (!object) return false;
+ return (object.search('(^|\\s)' + value + '(\\s|$)') != -1);
+}
+
+function removeClass(object,className) {
+ if (!object) return;
+ object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2);
+}
+
+function addClass(object,className) {
+ if (!object || hasClass(object, className)) return;
+ if (object.className) {
+ object.className += ' '+className;
+ } else {
+ object.className = className;
+ }
+}
+
+function GetElementsWithClassName(elementName,className) {
+ var allElements = document.getElementsByTagName(elementName);
+ var elemColl = new Array();
+ for (var i = 0; i< allElements.length; i++) {
+ if (hasClass(allElements[i], className)) {
+ elemColl[elemColl.length] = allElements[i];
+ }
+ }
+ return elemColl;
+}
+
+function isParentOrSelf(element, id) {
+ if (element == null || element.nodeName=='BODY') return false;
+ else if (element.id == id) return true;
+ else return isParentOrSelf(element.parentNode, id);
+}
+
+function nodeValue(node) {
+ var result = "";
+ if (node.nodeType == 1) {
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; ++i) {
+ result += nodeValue(children[i]);
+ }
+ }
+ else if (node.nodeType == 3) {
+ result = node.nodeValue;
+ }
+ return(result);
+}
+
+function slideLabel() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var list = document.getElementById('jumplist');
+ smax = slideColl.length;
+ for (var n = 0; n < smax; n++) {
+ var obj = slideColl[n];
+
+ var did = 'slide' + n.toString();
+ if (obj.getAttribute('id')) {
+ slideIDs[n] = obj.getAttribute('id');
+ }
+ else {
+ obj.setAttribute('id',did);
+ slideIDs[n] = did;
+ }
+ if (isOp) continue;
+
+ var otext = '';
+ var menu = obj.firstChild;
+ if (!menu) continue; // to cope with empty slides
+ while (menu && menu.nodeType == 3) {
+ menu = menu.nextSibling;
+ }
+ if (!menu) continue; // to cope with slides with only text nodes
+
+ var menunodes = menu.childNodes;
+ for (var o = 0; o < menunodes.length; o++) {
+ otext += nodeValue(menunodes[o]);
+ }
+ list.options[list.length] = new Option(n + ' : ' + otext, n);
+ }
+}
+
+function currentSlide() {
+ var cs;
+ var footer_nodes;
+ var vis = 'visible';
+ if (document.getElementById) {
+ cs = document.getElementById('currentSlide');
+ footer_nodes = document.getElementById('footer').childNodes;
+ } else {
+ cs = document.currentSlide;
+ footer = document.footer.childNodes;
+ }
+ cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' +
+ '<span id="csSep">\/<\/span> ' +
+ '<span id="csTotal">' + (smax-1) + '<\/span>';
+ if (snum == 0) {
+ vis = 'hidden';
+ }
+ cs.style.visibility = vis;
+ for (var i = 0; i < footer_nodes.length; i++) {
+ if (footer_nodes[i].nodeType == 1) {
+ footer_nodes[i].style.visibility = vis;
+ }
+ }
+}
+
+function go(step) {
+ if (document.getElementById('slideProj').disabled || step == 0) return;
+ var jl = document.getElementById('jumplist');
+ var cid = slideIDs[snum];
+ var ce = document.getElementById(cid);
+ if (incrementals[snum].length > 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ removeClass(incrementals[snum][i], 'current');
+ removeClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (step != 'j') {
+ snum += step;
+ lmax = smax - 1;
+ if (snum > lmax) snum = lmax;
+ if (snum < 0) snum = 0;
+ } else
+ snum = parseInt(jl.value);
+ var nid = slideIDs[snum];
+ var ne = document.getElementById(nid);
+ if (!ne) {
+ ne = document.getElementById(slideIDs[0]);
+ snum = 0;
+ }
+ if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;}
+ if (incrementals[snum].length > 0 && incpos == 0) {
+ for (var i = 0; i < incrementals[snum].length; i++) {
+ if (hasClass(incrementals[snum][i], 'current'))
+ incpos = i + 1;
+ else
+ addClass(incrementals[snum][i], 'incremental');
+ }
+ }
+ if (incrementals[snum].length > 0 && incpos > 0)
+ addClass(incrementals[snum][incpos - 1], 'current');
+ ce.style.visibility = 'hidden';
+ ne.style.visibility = 'visible';
+ jl.selectedIndex = snum;
+ currentSlide();
+ number = 0;
+}
+
+function goTo(target) {
+ if (target >= smax || target == snum) return;
+ go(target - snum);
+}
+
+function subgo(step) {
+ if (step > 0) {
+ removeClass(incrementals[snum][incpos - 1],'current');
+ removeClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos],'current');
+ incpos++;
+ } else {
+ incpos--;
+ removeClass(incrementals[snum][incpos],'current');
+ addClass(incrementals[snum][incpos], 'incremental');
+ addClass(incrementals[snum][incpos - 1],'current');
+ }
+}
+
+function toggle() {
+ var slideColl = GetElementsWithClassName('*','slide');
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ if (!slides.disabled) {
+ slides.disabled = true;
+ outline.disabled = false;
+ s5mode = false;
+ fontSize('1em');
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'visible';
+ }
+ } else {
+ slides.disabled = false;
+ outline.disabled = true;
+ s5mode = true;
+ fontScale();
+ for (var n = 0; n < smax; n++) {
+ var slide = slideColl[n];
+ slide.style.visibility = 'hidden';
+ }
+ slideColl[snum].style.visibility = 'visible';
+ }
+}
+
+function showHide(action) {
+ var obj = GetElementsWithClassName('*','hideme')[0];
+ switch (action) {
+ case 's': obj.style.visibility = 'visible'; break;
+ case 'h': obj.style.visibility = 'hidden'; break;
+ case 'k':
+ if (obj.style.visibility != 'visible') {
+ obj.style.visibility = 'visible';
+ } else {
+ obj.style.visibility = 'hidden';
+ }
+ break;
+ }
+}
+
+// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/)
+function keys(key) {
+ if (!key) {
+ key = event;
+ key.which = key.keyCode;
+ }
+ if (key.which == 84) {
+ toggle();
+ return;
+ }
+ if (s5mode) {
+ switch (key.which) {
+ case 10: // return
+ case 13: // enter
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ if(number != undef) {
+ goTo(number);
+ break;
+ }
+ case 32: // spacebar
+ case 34: // page down
+ case 39: // rightkey
+ case 40: // downkey
+ if(number != undef) {
+ go(number);
+ } else if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ break;
+ case 33: // page up
+ case 37: // leftkey
+ case 38: // upkey
+ if(number != undef) {
+ go(-1 * number);
+ } else if (!incrementals[snum] || incpos <= 0) {
+ go(-1);
+ } else {
+ subgo(-1);
+ }
+ break;
+ case 36: // home
+ goTo(0);
+ break;
+ case 35: // end
+ goTo(smax-1);
+ break;
+ case 67: // c
+ showHide('k');
+ break;
+ }
+ if (key.which < 48 || key.which > 57) {
+ number = undef;
+ } else {
+ if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return;
+ if (key.target && isParentOrSelf(key.target, 'controls')) return;
+ number = (((number != undef) ? number : 0) * 10) + (key.which - 48);
+ }
+ }
+ return false;
+}
+
+function clicker(e) {
+ number = undef;
+ var target;
+ if (window.event) {
+ target = window.event.srcElement;
+ e = window.event;
+ } else target = e.target;
+ if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true;
+ if (!e.which || e.which == 1) {
+ if (!incrementals[snum] || incpos >= incrementals[snum].length) {
+ go(1);
+ } else {
+ subgo(1);
+ }
+ }
+}
+
+function findSlide(hash) {
+ var target = document.getElementById(hash);
+ if (target) {
+ for (var i = 0; i < slideIDs.length; i++) {
+ if (target.id == slideIDs[i]) return i;
+ }
+ }
+ return null;
+}
+
+function slideJump() {
+ if (window.location.hash == null || window.location.hash == '') {
+ currentSlide();
+ return;
+ }
+ if (window.location.hash == null) return;
+ var dest = null;
+ dest = findSlide(window.location.hash.slice(1));
+ if (dest == null) {
+ dest = 0;
+ }
+ go(dest - snum);
+}
+
+function fixLinks() {
+ var thisUri = window.location.href;
+ thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length);
+ var aelements = document.getElementsByTagName('A');
+ for (var i = 0; i < aelements.length; i++) {
+ var a = aelements[i].href;
+ var slideID = a.match('\#.+');
+ if ((slideID) && (slideID[0].slice(0,1) == '#')) {
+ var dest = findSlide(slideID[0].slice(1));
+ if (dest != null) {
+ if (aelements[i].addEventListener) {
+ aelements[i].addEventListener("click", new Function("e",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "if (e.preventDefault) e.preventDefault();"), true);
+ } else if (aelements[i].attachEvent) {
+ aelements[i].attachEvent("onclick", new Function("",
+ "if (document.getElementById('slideProj').disabled) return;" +
+ "go("+dest+" - snum); " +
+ "event.returnValue = false;"));
+ }
+ }
+ }
+ }
+}
+
+function externalLinks() {
+ if (!document.getElementsByTagName) return;
+ var anchors = document.getElementsByTagName('a');
+ for (var i=0; i<anchors.length; i++) {
+ var anchor = anchors[i];
+ if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) {
+ anchor.target = '_blank';
+ addClass(anchor,'external');
+ }
+ }
+}
+
+function createControls() {
+ var controlsDiv = document.getElementById("controls");
+ if (!controlsDiv) return;
+ var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"';
+ var hideDiv, hideList = '';
+ if (controlVis == 'hidden') {
+ hideDiv = hider;
+ } else {
+ hideList = hider;
+ }
+ controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' +
+ '<div id="navLinks">' +
+ '<a accesskey="t" id="toggle" href="javascript:toggle();">&#216;<\/a>' +
+ '<a accesskey="z" id="prev" href="javascript:go(-1);">&laquo;<\/a>' +
+ '<a accesskey="x" id="next" href="javascript:go(1);">&raquo;<\/a>' +
+ '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' +
+ '<\/div><\/form>';
+ if (controlVis == 'hidden') {
+ var hidden = document.getElementById('navLinks');
+ } else {
+ var hidden = document.getElementById('jumplist');
+ }
+ addClass(hidden,'hideme');
+}
+
+function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers
+ if (!s5mode) return false;
+ var vScale = 22; // both yield 32 (after rounding) at 1024x768
+ var hScale = 32; // perhaps should auto-calculate based on theme's declared value?
+ if (window.innerHeight) {
+ var vSize = window.innerHeight;
+ var hSize = window.innerWidth;
+ } else if (document.documentElement.clientHeight) {
+ var vSize = document.documentElement.clientHeight;
+ var hSize = document.documentElement.clientWidth;
+ } else if (document.body.clientHeight) {
+ var vSize = document.body.clientHeight;
+ var hSize = document.body.clientWidth;
+ } else {
+ var vSize = 700; // assuming 1024x768, minus chrome and such
+ var hSize = 1024; // these do not account for kiosk mode or Opera Show
+ }
+ var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale));
+ fontSize(newSize + 'px');
+ if (isGe) { // hack to counter incremental reflow bugs
+ var obj = document.getElementsByTagName('body')[0];
+ obj.style.display = 'none';
+ obj.style.display = 'block';
+ }
+}
+
+function fontSize(value) {
+ if (!(s5ss = document.getElementById('s5ss'))) {
+ if (!isIE) {
+ document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style'));
+ s5ss.setAttribute('media','screen, projection');
+ s5ss.setAttribute('id','s5ss');
+ } else {
+ document.createStyleSheet();
+ document.s5ss = document.styleSheets[document.styleSheets.length - 1];
+ }
+ }
+ if (!isIE) {
+ while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild);
+ s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}'));
+ } else {
+ document.s5ss.addRule('body','font-size: ' + value + ' !important;');
+ }
+}
+
+function notOperaFix() {
+ slideCSS = document.getElementById('slideProj').href;
+ var slides = document.getElementById('slideProj');
+ var outline = document.getElementById('outlineStyle');
+ slides.setAttribute('media','screen');
+ outline.disabled = true;
+ if (isGe) {
+ slides.setAttribute('href','null'); // Gecko fix
+ slides.setAttribute('href',slideCSS); // Gecko fix
+ }
+ if (isIE && document.styleSheets && document.styleSheets[0]) {
+ document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)');
+ document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)');
+ }
+}
+
+function getIncrementals(obj) {
+ var incrementals = new Array();
+ if (!obj)
+ return incrementals;
+ var children = obj.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (hasClass(child, 'incremental')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'incremental');
+ for (var j = 0; j < child.childNodes.length; j++) {
+ if (child.childNodes[j].nodeType == 1) {
+ addClass(child.childNodes[j], 'incremental');
+ }
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ removeClass(child,'incremental');
+ }
+ }
+ if (hasClass(child, 'show-first')) {
+ if (child.nodeName == 'OL' || child.nodeName == 'UL') {
+ removeClass(child, 'show-first');
+ if (child.childNodes[isGe].nodeType == 1) {
+ removeClass(child.childNodes[isGe], 'incremental');
+ }
+ } else {
+ incrementals[incrementals.length] = child;
+ }
+ }
+ incrementals = incrementals.concat(getIncrementals(child));
+ }
+ return incrementals;
+}
+
+function createIncrementals() {
+ var incrementals = new Array();
+ for (var i = 0; i < smax; i++) {
+ incrementals[i] = getIncrementals(document.getElementById(slideIDs[i]));
+ }
+ return incrementals;
+}
+
+function defaultCheck() {
+ var allMetas = document.getElementsByTagName('meta');
+ for (var i = 0; i< allMetas.length; i++) {
+ if (allMetas[i].name == 'defaultView') {
+ defaultView = allMetas[i].content;
+ }
+ if (allMetas[i].name == 'controlVis') {
+ controlVis = allMetas[i].content;
+ }
+ }
+}
+
+// Key trap fix, new function body for trap()
+function trap(e) {
+ if (!e) {
+ e = event;
+ e.which = e.keyCode;
+ }
+ try {
+ modifierKey = e.ctrlKey || e.altKey || e.metaKey;
+ }
+ catch(e) {
+ modifierKey = false;
+ }
+ return modifierKey || e.which == 0;
+}
+
+function startup() {
+ defaultCheck();
+ if (!isOp) createControls();
+ slideLabel();
+ fixLinks();
+ externalLinks();
+ fontScale();
+ if (!isOp) {
+ notOperaFix();
+ incrementals = createIncrementals();
+ slideJump();
+ if (defaultView == 'outline') {
+ toggle();
+ }
+ document.onkeyup = keys;
+ document.onkeypress = trap;
+ document.onclick = clicker;
+ }
+}
+
+window.onload = startup;
+window.onresize = function(){setTimeout('fontScale()', 50);}
diff --git a/pypers/wsgi/var_vol.py b/pypers/wsgi/var_vol.py
new file mode 100644
index 0000000..070a867
--- /dev/null
+++ b/pypers/wsgi/var_vol.py
@@ -0,0 +1,91 @@
+"""
+Compare the approximated var-volatility relation with the exact
+var-volatility relation for the delta-distribution. The result
+is
+
+var95 = 1.0-1.2 sigma
+var99 = 2.0-2.4 sigma
+"""
+
+from scipy import sqrt, pi
+from scipy.special import gamma
+from scipy.integrate import quad
+from scipy.optimize import brentq
+from pylab import plot, show, arange, text
+
+class deltadist(object):
+ def __init__(self, delta, sigma):
+ self.d = delta
+ self.s = sigma
+ self.d2 = delta * delta
+ self.s2 = sigma *sigma
+ self.ds2 = self.d2 * self.s2
+ self.a = (2.+ self.d2)/2.
+ self.b = (3. + self.d2)/2.
+ self.C = gamma(self.b)/gamma(self.a)/gamma(.5)*self.ds2**self.a
+ def __call__(self, x):
+ return self.C/(x*x + self.ds2)**self.b
+
+class deltacumdist(object):
+ def __init__(self, delta, sigma):
+ self.delta = deltadist(delta, sigma)
+ def __call__(self, x):
+ return quad(self.delta, 0., x)[0]+.5
+
+class largepdist(object):
+ def __init__(self, delta, sigma):
+ self.d = delta
+ self.s = sigma
+ self.d2 = delta * delta
+ self.s2 = sigma *sigma
+ self.ds = self.d * self.s
+ self.a = (2.+ self.d2)/2.
+ self.b = (3. + self.d2)/2.
+ self.c = 1./(2.+ self.d2)
+ self.C = gamma(self.b)/gamma(self.a)/gamma(.5)
+
+ def __call__(self, p):
+ return self.ds*(2.*self.a*(1-p)/self.C)**(-self.c)
+
+class largeXdist(object):
+ def __init__(self, delta, sigma):
+ self.d = delta
+ self.s = sigma
+ self.d2 = delta * delta
+ self.s2 = sigma *sigma
+ self.ds = self.d * self.s
+ self.a = (2.+ self.d2)/2.
+ self.b = (3. + self.d2)/2.
+ self.c = 2.+ self.d2
+ self.C = gamma(self.b)/gamma(self.a)/gamma(.5)/self.c
+ def __call__(self, X):
+ return 1.-self.C*(self.ds/X)**self.c
+
+
+d61 = deltadist(.6, 1.)
+c61 = deltacumdist(.6, 1.)
+s61 = largepdist(.6, 1.)
+l61 = largeXdist(.6, 1.)
+
+
+print s61(.95)
+print s61(.99)
+
+x = arange(10, 40, .1)
+
+plot(x, map(deltacumdist(0.1, 1.),x), color='red')
+plot(x, map(deltacumdist(1.0, 1.),x), color='green')
+plot(x, map(deltacumdist(1.0, 1.),x), color='blue')
+plot(x, map(deltacumdist(5.0, 1.),x), color='violet')
+
+#plot(x, , color='black')
+
+text(30, .9999, '$F_{0.1}(x)$', color='red')
+text(30, .99985, '$F_{1.0}(x)$', color='green')
+text(30, .9998, '$F_{5.0}(x)$', color='blue')
+
+print '***'
+print brentq(lambda x: c61(x)-.95, .1, 4.)
+print brentq(lambda x: c61(x)-.99, .1, 4.)
+
+show()
diff --git a/pypers/wsgi/webplotter.py b/pypers/wsgi/webplotter.py
new file mode 100644
index 0000000..f2b9a55
--- /dev/null
+++ b/pypers/wsgi/webplotter.py
@@ -0,0 +1,36 @@
+import os, cgi, traceback
+from wsgiref import simple_server
+from tempfile import mkstemp
+from simpleplotter import make_graph
+
+def getformdict(env):
+ qs = env.get('QUERY_STRING')
+ if qs:
+ return dict((k, v[0]) for k, v in cgi.parse_qs(qs).iteritems())
+
+def app(env, resp):
+ form = getformdict(env)
+ if form and form.get('submitted'):
+ try:
+ fname = make_graph(form.get('code'), batch=True)
+ except Exception, e:
+ resp('500 ERR', [('Content-type', 'text/plain')])
+ return [traceback.format_exc()]
+ else:
+ resp('200 OK', [('Content-type', 'image/png')])
+ return file(fname)
+ else:
+ resp('200 OK', [('Content-type', 'text/html')])
+ return [
+ 'Try values such as <pre>fri-gb;AVE</pre>',
+ '<pre>fri-gb;TSCO</pre> <pre>fri-us;DELL</pre>',
+ '<form>', 'insert code ',
+ '<input type="text" name="code"/>',
+ '<input type="submit", name="submitted", value="submit" />',
+ '</form>']
+
+if __name__ == '__main__':
+ #from paste.auth.basic import AuthBasicHandler
+ #app = AuthBasicHandler(
+ # app, 'plotter realm', lambda e, u, p: u=='pippo' and p=='lippo')
+ simple_server.make_server('', 8000, app).serve_forever()
diff --git a/pypers/x.txt b/pypers/x.txt
new file mode 100755
index 0000000..35f3ffe
--- /dev/null
+++ b/pypers/x.txt
@@ -0,0 +1,213 @@
+
+ ..
+
+ M_A M_B
+ : \ / :
+ : \ / :
+ A M_C B
+ \ : /
+ \ : /
+ C
+
+ co_firstlineno : int(x[, base]) -> integer
+
+ Convert a string or number to an integer, if possible. A floating point
+ argument will be truncated towards zero (this does not include a string
+ representation of a floating point number!) When converting a string, use
+ the optional base. It is an error to supply a base when converting a
+ non-string. If the argument is outside the integer range a long object
+ will be returned instead.
+
+ co_code : str(object) -> string
+
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+ co_freevars : tuple() -> an empty tuple
+ tuple(sequence) -> tuple initialized from sequence's items
+
+ If the argument is a tuple, the return value is the same object.
+
+ co_consts : tuple() -> an empty tuple
+ tuple(sequence) -> tuple initialized from sequence's items
+
+ If the argument is a tuple, the return value is the same object.
+
+ co_name : str(object) -> string
+
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+ co_filename : str(object) -> string
+
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+ co_lnotab : str(object) -> string
+
+ Return a nice string representation of the object.
+ If the argument is a string, the return value is the same object.
+
+ co_flags : int(x[, base]) -> integer
+
+ Convert a string or number to an integer, if possible. A floating point
+ argument will be truncated towards zero (this does not include a string
+ representation of a floating point number!) When converting a string, use
+ the optional base. It is an error to supply a base when converting a
+ non-string. If the argument is outside the integer range a long object
+ will be returned instead.
+
+ co_nlocals : int(x[, base]) -> integer
+
+ Convert a string or number to an integer, if possible. A floating point
+ argument will be truncated towards zero (this does not include a string
+ representation of a floating point number!) When converting a string, use
+ the optional base. It is an error to supply a base when converting a
+ non-string. If the argument is outside the integer range a long object
+ will be returned instead.
+
+ co_stacksize : int(x[, base]) -> integer
+
+ Convert a string or number to an integer, if possible. A floating point
+ argument will be truncated towards zero (this does not include a string
+ representation of a floating point number!) When converting a string, use
+ the optional base. It is an error to supply a base when converting a
+ non-string. If the argument is outside the integer range a long object
+ will be returned instead.
+
+ co_argcount : int(x[, base]) -> integer
+
+ Convert a string or number to an integer, if possible. A floating point
+ argument will be truncated towards zero (this does not include a string
+ representation of a floating point number!) When converting a string, use
+ the optional base. It is an error to supply a base when converting a
+ non-string. If the argument is outside the integer range a long object
+ will be returned instead.
+
+ co_cellvars : tuple() -> an empty tuple
+ tuple(sequence) -> tuple initialized from sequence's items
+
+ If the argument is a tuple, the return value is the same object.
+
+ co_varnames : tuple() -> an empty tuple
+ tuple(sequence) -> tuple initialized from sequence's items
+
+ If the argument is a tuple, the return value is the same object.
+
+ co_names : tuple() -> an empty tuple
+ tuple(sequence) -> tuple initialized from sequence's items
+
+ If the argument is a tuple, the return value is the same object.
+
+
+ class Class(type):
+ """Non-cooperative metaclass acting as a class factory. It accepts
+ various kinds of objects and metaobjects as input (dictionaries, modules,
+ classes etc). It automatically converts functions to static methods if
+ the input object is not a class. If the first argument is a named object,
+ the name of the created class is obtained by propending an underscore."""
+
+ defaultname="InstanceOfClass"
+ defaultbases=()
+ defaultdic={}
+
+ def __new__(meta, *args):
+ metas=meta.__bases__[:-1]
+ name=meta.defaultname
+ bases=meta.defaultbases
+ dic=meta.defaultdic
+ # parse the arguments
+ if not args:
+ pass # do nothing
+ elif len(args)==1:
+ first=args[0]
+ if isinstance(first,str): # is a name
+ name=first
+ elif hasattr(first,'__name__'): # has a name
+ name=first.__name__+'_'
+ if isinstance(first,tuple): # is a tuple
+ bases+=first
+ elif hasattr(first,'__bases__'): # is a class
+ bases+=first.__bases__
+ metas=type(first)+metas
+ if isinstance(first,dict): # is a dict
+ dic.update(first)
+ elif hasattr(first,"__dict__"): # has a dict
+ dic.update(first.__dict__)
+ elif len(args)==2: # must be name,dic
+ name,dic=args
+ elif len(args)==3: # must be name,bases,dic
+ name,bases,dic=args
+ if not bases: # creating a class from a non-class
+ condition=lambda name,attr: inspect.isfunction(attr)
+ wrapdic(dic,staticmethod,condition)
+ cls=child(*bases,**{'name':name,'dic':dic,'metas':metas})
+ cls.__class__=meta; return cls
+
+ class ClsFactory(object):
+ "Bracket-callable non-cooperative class acting as a metaclass factory"
+ class __metaclass__(type):
+ def __getitem__(cls,*meta):
+ meta+=(Class,) # base metaclasses
+ name=''.join([m.__name__ for m in meta])
+ return type(name,meta,{})
+
+ #<oopp.py>
+
+ class ClsFactory(object):
+ """Bracket callable non-cooperative class acting as a factory of class
+ factories.
+
+ ClsFactory instances are class factories accepting 0,1,2 or 3 arguments.
+ . It automatically converts functions to static methods
+ if the input object is not a class. If an explicit name is not passed
+ the name of the created class is obtained by adding an underscore to
+ the name of the original object."""
+
+ class __metaclass__(type):
+ "Makes ClsFactory and its children bracket callable"
+ def __getitem__(cls,metas):
+ "Sets the default metaclass and returns a ClsFactory object"
+ ClsFactory=cls() # a callable ClsFactory object
+ ClsFactory.metas=metas
+ return ClsFactory
+
+ # customizable default attributes
+ name="CreatedFromClsFactory"
+ bases=()
+ dict={}
+
+ def __call__(self, *args):
+ """Generates a new class using self.meta and avoiding conflicts.
+ The first metaobject can be a dictionary, an object with a
+ dictionary (except a class), or a simple name."""
+ if len(args)==1:
+ arg0=args[0]
+ if isinstance(arg0,str): # is a name
+ self.name=arg0
+ elif hasattr(arg0,'__name__'): # has a name
+ self.name=arg0.__name__+'_'
+ self.setbasesdic(arg0)
+ elif len(args)==2:
+ self.name=args[0] # must be a name
+ assert isinstance(name,str)
+ self.setbasesdic(args[1])
+ elif len(args)==3: # must be name,bases,dic
+ self.name,self.bases,self,dic=args
+ if len(args)<3 and not self.bases: # creating class from a non-class
+ condition=lambda name,attr: inspect.isfunction(attr)
+ wrapdic(self.dic,staticmethod,condition)
+ return child(*bases,**vars(self))
+
+ def setbasesdic(self,obj):
+ if isinstance(obj,tuple): # is a tuple
+ self.bases+=obj
+ elif hasattr(obj,'__bases__'): # is a class
+ self.bases+=obj.__bases__
+ if isinstance(obj,dict): # is a dict
+ self.dic.update(obj)
+ elif hasattr(obj,"__dict__"): # has a dict
+ self.dic.update(obj.__dict__)
+
+
+ #</oopp.py>
diff --git a/pypers/xx.txt b/pypers/xx.txt
new file mode 100755
index 0000000..a6582b0
--- /dev/null
+++ b/pypers/xx.txt
@@ -0,0 +1 @@
+>>> execfile('psyco1.py',{})
diff --git a/pypers/yet-another-comparison-of-web-frameworks.txt b/pypers/yet-another-comparison-of-web-frameworks.txt
new file mode 100644
index 0000000..6badf1d
--- /dev/null
+++ b/pypers/yet-another-comparison-of-web-frameworks.txt
@@ -0,0 +1,292 @@
+Yet Another Comparison of Python Web Frameworks
+=======================================================================
+
+:author: Michele Simionato
+:date: October 2007
+
+.. contents::
+
+You can find on the Net hundreds of reviews/comparisons of
+Python Web Frameworks, yet they are never enough, since new
+frameworks continue to appear every day and old frameworks
+continue to change at an even faster rate. At work we are now shopping
+for a new Web framework, so I am looking at the available options
+and I have decided to write this document which may be of help to
+others in the same situation.
+
+Let me start with a word about me: I have already experience with many
+Web frameworks (Zope/Plone, Twisted, Quixote, CherryPy, QP and Paste) and
+I am probably competent enough to write my own, if I wished [#]_. My
+colleagues at work are all competent Pythonistas but we are not really
+into Web programming. We actually do financial software and we just need
+a Web interface to manage the requests of our customers. In other
+words, we are NOT looking into a framework to write e-Commerce sites,
+community sites, blogs, etc. We need a building block on top of which
+to write a very customized application which fits our very specific
+needs. So my evaluation below (this framework is "good"/"bad") must
+be read in the light of our requirements, not in absolute terms.
+
+.. [#] Notice that I claim that I am competent enough to write a Web
+ framework, but I do *not* claim that I am competent enough to
+ write a *good* Web framework.
+
+Of Plone, Quixote, QP, CherryPy and Twisted
+---------------------------------------------------------
+
+The framework we use at work now is Plone and I have been using it for
+four years; I never liked it, even at the beginning (it was my first Web
+framework) and every day I dislike it even more. My colleagues are not fond of
+it either and this is why for new projects we are looking at a
+different framework. Of all the frameworks I cited before, the one I
+like the most for its design and philosophy is Quixote (perhaps not
+coincidentally, since it was designed from the beginning to be an
+anti-Zope framework). Some (Ian Bicking IIRC) labelled Quixote as
+"the framework for hackers", since it is really designed for old
+school boys used to the Unix way of thinking, and also because its
+marketing totally sucks [#]_.
+
+Quixote makes the uncommon choice
+of using Python as template language (well, actually they hacked a bit
+Python to make what they call PTL (Python Template Language) [#]_, but the
+differences with pure Python are minimal) which I wholeheartedly
+support. Templates makes sense if you have mostly static pages and if
+you work with graphical designers, but neither is our case.
+
+I think Quixote can still be recommended for some users (for instance,
+if you want an absolutely stable framework you should go with it,
+since its development virtually stopped years ago and it will probably
+never resume) but most people probably will not want to start
+with a "dead" framework. I looked at the successor of Quixote,
+QP, which shares the same philosophy. It is fine but it is nearly
+completely undocumented, it is less simple than Quixote, and it is
+even less known and used.
+
+I looked at CherryPy, but I never did anything serious with it,
+because at each new major version (1, 2, 3) they completely broke
+backward compatibility and this was a showstopper for me. I used
+Twisted Web, and it is fine, however:
+
+1. Twisted Web is going to be replaced with Twisted Web2, so there is
+ no point to use it for new projects;
+2. Twisted Web2 has been in development for ages and who knows when
+ it will be finished;
+3. In any case using Twisted means that we have to rethink various
+ applications we have (which are blocking) and that we must use
+ Twisted.enterprise and not the support for database we already
+ have and know well.
+
+In short, Twisted Web is cool but is seems a risky choice at the
+moment. Twisted developers themselves basically say "don't use it
+unless you feel adventurous" [#]_ .
+
+At EuroPython 2006 I grokked WSGI and after that I am
+convinced that WSGI is the one obvious way to go. So I looked at
+Paste and I was happy with it. Actually, the more I look at Paste
+the more I like it. That does not means that I like Paste in full
+(for one, I don't
+care at all about the thread support, I am convinced the right
+way to go is to write single-threaded programs and to run them in
+multiple processes, for instance via Apache and mod_wsgi)
+but it is well written in the
+sense that I can extract from Paste what I need and write my own
+framework with almost no effort. This was the design goal of Paste
+from the beginning and I think Ian Bicking succeded; this is not
+a minor feat.
+
+OTOH, we asked ourselves "why should we write our own framework on top of
+Paste where there is an already written framework built on top of
+Paste called Pylons"? So we evaluated Pylons on the field,
+by writing a couple of test applications and
+by porting a pre-existing Paste application. A detailed summary of our
+finding follows.
+
+.. [#] which is one of its strongest points IMO.
+
+.. [#] nowadays PTLs have been superseded by a "templating" system
+ called qpy which is a newer and improved version of PTLs,
+ practically backward-compatible and used in the framework QP; I
+ still say "templating" with quotes, since in pratice this is not a
+ real templating language, it is just pure Python with minimal
+ changes.
+.. [#] http://twistedmatrix.com/trac/wiki/WebDevelopmentWithTwisted
+
+Of Pylons
+-----------------------------------------------------
+
+Pylons has got some good press recently and people I trust spoke well
+of it in c.l.py, so I started with good hopes. At the present,
+however, I am not as happy as when I started. The impression it that
+the framework is still new, still fragile, too much cutting edge
+and with too many dependencies. This was not entirely unexpected,
+though. However I did expect porting a Paste application to Pylons to
+be a breeze: it turned out it was not the case. Probably most
+of the problems we encountered are due to our ignorance with the
+framework, but still I would have expect to encounter much less issues
+[#]_.
+
+Pylons is basically a
+collection of third party eggs with some glue in it.
+The Pylons-related eggs I have in my site-packages are the following
+(in alphabetical order):
+
+1. Beaker-0.7.5-py2.5.egg
+2. decorator-2.2.0-py2.5.egg
+3. FormEncode-0.7.1-py2.5.egg
+4. Mako-0.1.8-py2.5.egg
+5. nose-0.10.0b1-py2.5.egg
+6. Paste-1.4.2-py2.5.egg
+7. PasteDeploy-1.3.1-py2.5.egg
+8. PasteScript-1.3.6-py2.5.egg
+9. Pylons-0.9.6-py2.5.egg
+10. Routes-1.7-py2.5.egg
+11. SQLAlchemy-0.3.10-py2.5.egg
+12. WebHelpers-0.3.2-py2.5.egg
+
+Here is my evaluation of each of them, using the Pythonic score of
++1, 0, -1 (+1 means "good for the kind of applications we are
+interested in", -1 means "not good", 0 means I have not strong
+opinion).
+
+1. Beaker [+1]
+
+It is the session management mechanism. It looks okay.
+
+2. decorator [0]
+
+I am the author of this module and it may surprise people that I am
+not +1 on it. But the reason is easy to explain, once you understand why
+I wrote the decorator module in the first place. The decorator module
+comes out from a shortcoming of the standard library: as of now, it is
+very difficult to write signature-preserving decorators unless you use
+the decorator module or the DecoratorTools by P.J. Eby. I wrote the
+module to make people aware of this shortcoming and hoping from a
+better solution at the core Python level. The better solution is now
+forthcoming (PEP 362 Function Signature Object [#]_) and in future
+versions of Python it will be
+possible to fiddle with the signature of functions directly. Then the
+decorator module will become obsolete and I don't want to use in
+production a technology which I am already know is going to be
+obsolete. On the other hand, it may take years before we switch to a
+Python version with a proper signature object so the decorator module
+can be useful meanwhile. Finally, I do not use many decorators in
+practice (I use them only if there are no better alternatives).
+This is why my score is 0 for the moment.
+
+3. FormEncode [+1]
+
+I have not tried it in person but my coworker Lawrence Oluyede
+says it is okay.
+
+4. Mako [-1]
+
+As I said before, as a matter of principle I am against templating
+language and I prefer to write Python code. Template languages makes
+sense if you pass them to a graphic designer, but then they should be
+valid (X)HTML, compatible with the tools used by the designers and this
+is not the case of Mako. Besides, nowadays everything graphic should
+be done with CSS and I see nothing bad in generating the page from
+Python. Separation of logic and presentation is a matter of discipline
+and has nothing to do with the existence of a templating language.
+Besided, in our firms everybody understand Python better than HTML.
+
+5. nose [+1]
+
+The unittest framework in the standard library sucks and we absolutely
+need something better, compatibile with the past, easy to install and
+that works. I think *nose* fits the bill. I especially like the *py.test*
+inspired (a better word would be stolen) features.
+Incidentally, the change form nose-0.9 to nose-0.10 once broke my
+Pylons installation, this is one of the reasons I say Pylons is
+fragile. Also I would
+recommend to use just the basic features of *nose* because I am sure
+these will stay, where other things like the plugin system have
+already changed.
+
+6. Paste [+1]
+
+As I said I like Paste. It is well written, well documented and with a
+design philosophy I agree with. As I said the only thing I am skeptical about
+is the thread support. Notice that I do not mean that Paste
+support for threads is of of low quality (far from it!): it is just
+that I am on the anti-thread camp and that by choice I
+avoid them when I can. The thread support is making Paste much more
+complicated that it would be without it. Anyway, I am sure the thread
+support will make happy the fans of threads and the poor guys
+condamned to work on platforms without fork.
+
+7. PasteDeploy [0]
+8. PasteScript [0]
+
+I am a big fan of .INI configuration files for simple applications.
+However, the configuration of a Web application is complicated
+enough to stretch the limit of .INI files. So I don't like
+particularly the Paste configuration mechanism. I think the
+configuration file should be in a format like JSON or in pure
+Python. At work we already use a configuration mechanism based
+on configuration classes (so that we may inherit from configurations)
+and it is easier for us to build on it than to use Paste mechanism.
+Moreover I have already equivalents of the paster utility, which I wrote
+by stealing ideas from QP; it is not surpring in retrospect to
+discover, by looking at the comments in the source code, that Ian
+Bicking went further and even took code from QP.
+
+9. Pylons [-1]
+
+I don't think Pylons is adding much value to the other components.
+I think it would take less time to write myself the glue code
+than to understand how Pylons did it. As a proof of concept yesterday I
+took the two test Pylons application we wrote and I removed the
+dependency from Pylons in four hours. I just had to write a few lines
+of code for the SQLAlchemy support and a few lines for the javascript
+support. Then I removed the mako templates, I converted the Pylons
+controllers into pure WSGI applications and I removed Routes
+support by using paste.URLMap instead. I think now the code is
+much simpler, less coupled and it does the same with smaller effort
+and exactly as I want it to do it.
+
+10. Routes [-1]
+
+I have no Ruby On Rails background, so I don't see the advantages of
+routes.
+
+11. SQLAlchemy [?]
+
+SQLAlchemy is big and should be evaluated on its own. One thing to say
+is that the change form 0.3.9 to 0.3.10 broke our test application, and
+that SQLAlchemy did not cooperate well with pymssql. So, I would still
+wait until the framework will stabilize.
+
+12. WebHelpers [?]
+
+I still have to evaluate it.
+
+
+.. [#] for instance we had a Paste application making plots, saving them in
+ a temporary file and returning them as 'image/png'; in Pylons,
+ to make Routes and the Controller happy, my coworked Lawrence
+ had to copy the temporary file in the public directory and to remove the
+ start_response call in the original code.
+
+.. [#] http://www.python.org/dev/peps/pep-0362
+
+What about Diango?
+---------------------
+
+I can't say anything about Django, since I never used it. However my
+coworker Lawrence who has used it says it is this more stable and
+better thought than Pylons and I usually believe Lawrence.
+
+Conclusions
+--------------------------------------
+
+All in all, at the moment I am not much in favor of Pylons over Paste. OTOH,
+it may happen that a killer application will appear for Pylons, which
+will help enormously with our business, so we may still want Pylons
+with all its dependencies. As of now, I would do our things in Paste
+and wait and see for the new developments in the Pylons word.
+
+**Disclaimer to the reader**: since I only spent a couple of days
+for this evaluation, in the future
+I may well realize that I was wrong and change my mind.
+So take this evaluation (as any evaluation) *cum grano salis*.
+You and only you can decide what it is good for your situation.