Anonymous avatar Anonymous committed 747d58a

Initial move from Launchpad

Comments (0)

Files changed (78)

CanonicalScript.rst

+..  index::
+    triple: script; command-line; canonical form
+
+****************************************************************************
+A Canonical Form for Command-Line Programs
+****************************************************************************
+
+Creating Python programs for command-line use involves a certain amount of
+repetitious coding, which can often be left off or forgotten. Here is a form
+which includes everthing.
+
+Note that if you are using Windows, you can add Python programs to your "File
+New" menu and automatically include the above text in the new file. `This
+article <http://articles.techrepublic.com.com/5100-10878_11-5034852.html>`_
+shows you how. Other operating systems have their own automation features.
+# CodeManager.py
+"""
+Extracts, checks and updates code examples in ReST files.
+
+You can just put in the codeMarker and the (indented) first line (containing the
+file path) into your ReST file, then run the update program to automatically
+insert the rest of the file.
+"""
+import os, re, sys, shutil, inspect, difflib
+
+restFiles = [os.path.join(d[0], f) for d in os.walk(".") if not "_test" in d[0]
+             for f in d[2] if f.endswith(".rst")]
+
+class Languages:
+    "Strategy design pattern"
+
+    class Python:
+        codeMarker = "::\n\n"
+        commentTag = "#"
+        listings = re.compile("::\n\n( {4}#.*(?:\n+ {4}.*)*)")
+
+    class Java:
+        codeMarker = "..  code-block:: java\n\n"
+        commentTag = "//"
+        listings = \
+            re.compile(".. *code-block:: *java\n\n( {4}//.*(?:\n+ {4}.*)*)")
+
+def shift(listing):
+    "Shift the listing left by 4 spaces"
+    return [x[4:] if x.startswith("    ") else x for x in listing.splitlines()]
+
+# TEST makes duplicates of the rst files in a test directory to test update():
+dirs = set([os.path.join("_test", os.path.dirname(f)) for f in restFiles])
+if [os.makedirs(d) for d in dirs if not os.path.exists(d)]:
+    [shutil.copy(f, os.path.join("_test", f)) for f in restFiles]
+testFiles = [os.path.join(d[0], f) for d in os.walk("_test")
+             for f in d[2] if f.endswith(".rst")]
+
+class Commands:
+    """
+    Each static method can be called from the command line. Add a new static
+    method here to add a new command to the program.
+    """
+
+    @staticmethod
+    def display(language):
+        "Print all the code listings"
+        for f in restFiles:
+            listings = language.listings.findall(open(f).read())
+            if not listings: continue
+            print '=' * 60 + "\n" + f + "\n" + '=' * 60
+            for n, l in enumerate(listings):
+                print "\n".join(shift(l))
+                if n < len(listings) - 1:
+                    print '-' * 60
+
+    @staticmethod
+    def extract(language):
+        """Pull the code listings from the ReST files and write each
+        listing into its own file"""
+        for f in restFiles:
+            for listing in language.listings.findall(open(f).read()):
+                listing = shift(listing)
+                path = listing[0][len(language.commentTag):].strip()
+                path = os.path.join("_code", path)
+                dirname = os.path.dirname(path)
+                if dirname:
+                    if not os.path.exists(dirname):
+                        os.makedirs(dirname)
+                file(path, 'w').write("\n".join(listing))
+
+    @staticmethod
+    def check(language):
+        "Ensure that external code files exist"
+        missing = []
+        for path in [code.splitlines()[0] for f in restFiles for code in
+                     language.listings.findall(open(f).read())]:
+            path = path.strip()[len(language.commentTag):].strip()
+            path = os.path.normpath(os.path.join("_code", path))
+            if not os.path.exists(path):
+                missing.append(path)
+        if missing:
+            print "Missing", language.__name__, "files:"
+            for p in missing:
+                print p
+        return missing
+
+    @staticmethod
+    def update(language): # Test until it is trustworthy
+        "Refresh external code files into ReST files"
+        if Commands.check(language):
+            print language.__name__, "update aborted"
+            return
+        def _update(matchobj):
+            listing = shift(matchobj.group(1))
+            path = listing[0].strip()[len(language.commentTag):].strip()
+            filename = os.path.basename(path).split('.')[0]
+            path = os.path.join("_code", path)
+            code = open(path).read().splitlines()
+            #diff = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK)
+            #for delta in diff.compare(listing, code):
+            #    print delta
+            #d = difflib.Differ()
+            d = difflib.HtmlDiff()
+            if not os.path.exists("_deltas"):
+                os.makedirs("_deltas")
+            open(os.path.join("_deltas", filename + ".html"), 'w'). write(
+                d.make_file(listing, code) + d.make_table(listing,code))
+            #print '\n'.join(list(diff))
+            #sys.exit()
+            return language.codeMarker + "\n".join(["    " + line for line in listing])
+        for f in testFiles:
+            updated = language.listings.sub(_update, open(f).read())
+            #open(f, 'w').write(updated)
+
+if __name__ == "__main__":
+    commands = dict(inspect.getmembers(Commands, inspect.isfunction))
+    if len(sys.argv) < 2 or sys.argv[1] not in commands:
+        print "Command line options:"
+        for name in commands:
+            print name + ":", commands[name].__doc__
+    else:
+        for language in inspect.getmembers(Languages, inspect.isclass):
+            commands[sys.argv[1]](language[1])

Comprehensions.rst

+.. index::
+   pair: list; comprehension
+   pair: generator; comprehension
+
+********************************************************************************
+Comprehensions
+********************************************************************************
+
+History: where did they come from?
+
+They require a mind shift.
+
+What makes them so compelling (once you 'get it')?
+
+A two-level list comprehension using ``os.walk()``::
+
+    # Comprehensions/os_walk_comprehension.py
+    import os
+    restFiles = [os.path.join(d[0], f) for d in os.walk(".")
+                 for f in d[2] if f.endswith(".rst")]
+    for r in restFiles:
+        print(r)
+
+*******************************************************************************
+Contributors
+*******************************************************************************
+
+Contributors ordered by the number of Launchpad.net Karma points.
+
+.. rubric:: Karma List
+
+..  note:: Launchpad.net now has a web services interface; we need to figure
+    out how to automatically download the Karma list.
+
+Thanks To
+===============================================================================
+
+  * Launchpad.net
+
+  * Creator(s) of Sphinx
+
+  * And of course, Guido and the team for their incessant improvement of Python,
+    especially for taking the risk in breaking backward compatibility in Python
+    3.0 to refactor the language.

CoroutinesAndConcurrency.rst

+.. index::
+   coroutines
+   concurrency
+   threads
+   parallelism
+   multiprocessing
+   GIL: Global Interpreter Lock
+
+********************************************************************************
+Coroutines & Concurrency
+********************************************************************************
+
+Primary focus should be on:
+
+1) Using ``yield`` to create coroutines
+
+2) Using the new ``multiprocessing`` module
+
+and then showing some alternative techniques.
+
+foo bar :func:`input` baz.

DeveloperGuide.rst

+*******************************************************************************
+Developer Guide
+*******************************************************************************
+
+Details for people participating in the book development process.
+
+For Windows Users
+===============================================================================
+
+You need to install Cygwin; go to:
+
+    http://www.cygwin.com
+
+You need to install at least the ``make`` utility, but I find that ``chere``
+(command prompt here) is also very useful.
+
+Also install ``openssh`` (under **Net**), so you can create your RSA key
+for Bazaar.
+
+I've discovered that it's best if you *don't* install Python as part of
+Cygwin; instead use a single Python installation under windows. Cygwin will
+find the installation if it is on your Windows PATH.
+
+Because of this, you shouldn't select "mercurial" or "bazaar" when you're
+installing Cygwin because that will cause Python to be installed. Instead,
+install them as Windows applications (see below).
+
+Installing Sphinx
+===============================================================================
+
+Because we are sometimes pushing the boundaries of Sphinx, you'll need to get
+the very latest development version (a.k.a. the "tip").
+
+#. Get mercurial:
+
+    http://www.selenic.com/mercurial
+
+    Avoid installing the tortoiseHG part - it has caused trouble w/ Python
+    debuggers.
+
+#. To get the Sphinx trunk, start with:
+
+    ``$ hg clone http://www.bitbucket.org/birkenfeld/sphinx/``
+
+    and to update, use:
+
+    ``$ hg pull``
+
+    Once you update, run
+
+    ``$ python setup.py install``
+
+    We may talk about minimum version numbers to process the book. Check your
+    version with:
+
+    ``$ hg identify -n``
+
+The full anouncement from Georg (Sphinx creator) is here:
+
+    http://groups.google.com/group/sphinx-dev/browse_thread/thread/6dd415847e5cbf7c
+
+Mercurial Cheat sheets & quick starts should be enough to answer your questions:
+
+    - http://edong.net/2008v1/docs/dongwoo-Hg-120dpi.png
+    - http://www.ivy.fr/mercurial/ref/v1.0/
+
+Getting the Development Branch of the Book
+===============================================================================
+
+This book uses Launchpad.net tools, and additional tools if necessary.
+
+#.  Sign up for an account at http://Launchpad.net.
+
+#.  Install the Bazaar distributed version control system (DVCS):
+
+    https://launchpad.net/bzr
+
+    It's helpful to follow this Bazaar mini-tutorial:
+
+    http://doc.bazaar-vcs.org/latest/en/mini-tutorial/index.html
+
+    And here's the complete Bazaar user guide:
+
+    http://doc.bazaar-vcs.org/latest/en/user-guide/index.html
+
+#.  You must create an rsa key. Under OSX and Linux, and if you installed
+    ``openssh with`` Cygwin under windows, you run ``ssh-keygen`` to generate
+    the key, and then add it to your Launchpad account.
+
+#.  Go to https://code.launchpad.net/python3patterns, and you'll see
+    instructions for getting a branch for development.
+
+#.  Work on your branch and make local commits.
+
+Building the Book
+===============================================================================
+
+To ensure you have Cygwin installed correctly (if you're using windows) and
+to see what the options are, type:
+
+    ``make``
+
+at a shell prompt. Then you can use ``make html`` to build the HTML version of
+the book, or ``make htmlhelp`` to make the windows help version, etc.
+
+You can also use the ``build`` system I've created (as a book example; it is
+part of the distribution). This will call ``make`` and it simplifies many of the
+tasks involved. Type:
+
+    ``build help``
+
+to see the options.
+
+Working with Launchpad and Bazaar
+===============================================================================
+
+(Adapted from a posting by Yarko T)
+
+This assumes that you have created a local branch on your private machine where
+you do work, and keep it merged with the trunk.
+
+That is, you've done:
+
+   - ``bzr branch lp:python3patterns``
+   - ``bzr commit -m 'initial checkout'``
+   - (hack, hack, hack....)
+   - ``bzr merge``   (pull new updates)
+   - ``bzr commit -m 'checkin after merge...'``
+   - ... and so on...
+
+When you have a new function idea, or think you've found a bug, ask Bruce
+on the group.
+
+   -  If you have a new feature, register a blueprint on Launchpad and
+      describe what you're going to do.
+   -  If you have found a bug, make a bug report on Launchpad (later assign
+      it to yourself, and link your branch to it);
+   -  If you want to work on a project, look for an unassigned bug and try to
+      work it out - then proceed as below...
+
+When you are ready to share your work have others review, register a branch.
+
+.. note:: Once you create a branch, there is no easy way to remove it.
+
+.. note:: You can re-use one branch for multiple bug fixes.
+
+1.  Sign up for an account on launchpad.net
+
+2.  Go to the project and select "register branch"
+    (``https://code.launchpad.net/python3patterns/+addbranch``). Suggest you
+    create a hosted branch, then you can work locally, and pull/push as you make
+    progress (see
+    http://doc.bazaar-vcs.org/latest/en/user-guide/index.html#organizing).
+
+3.  Once you have registered your branch, launchpad will provide you with
+    instructions on how to pull and push to your personal development copy.
+
+4.  Link your bug report or blueprint to your branch.
+
+5.  Merge from your "parent" (the trunk, or others you are working with) as needed.
+
+6.  Push your working copy to launchpad as your work is ready for others to
+    review or  test.
+
+7.  Once you are done making your changes, have completed testing, and are
+    ready for the project team to inspect & test, please select "propose for
+    merging"
+
+8.  Somebody on the core team will make a test merge (it may include
+    merging with other patches). Once tests pass, and your branch is accepted,
+    it will be merged into the trunk.
+
+
+Branch Registration Details
+===============================================================================
+Register a branch on http://code.launchpad.net/python3patterns/.  Since your login
+will identify your branch, ``devel`` is a good choice for a general development branch.
+If you're unsure, select ``Hosted`` branching.
+
+You must submit a public key - this will get you there:
+
+    https://launchpad.net/people/+me/+editsshkeys
+
+When you create your branch, Launchpad will show you the command to push to your branch.
+It will look something like this.
+
+.. image:: _images/pushPath.*
+
+A good local setup is to keep an updated copy of the current trunk
+and a separate local development branch.
+
+Tips for Merging
+===============================================================================
+
+..  note:: I want to eventually move to the mode where submissions are
+    reviewed by another member of the team, rather than me reviewing everything.
+
+-   Make a merge using a local test branch
+
+-   Consider using kdiff3: http://kdiff3.sourceforge.net/
+
+-   You can use ``bzr diff --using=kdiff3`` to browse differences, and use a bazaar
+    plugin to merge conflicts:
+
+       ``bzr extmerge --all``
+
+-    More here:  http://dev.mysql.com/tech-resources/articles/advanced-bazaar.html
+

GeneratorsIterators.rst

+.. index::
+   generators
+   iterators
+   itertools
+
+********************************************************************************
+Generators, Iterators, and Itertools
+********************************************************************************
+
+*******************************************************************************
+Introduction
+*******************************************************************************
+
+.. raw:: html
+
+   <font size="+1">The vision for this book.</font>
+
+A Team Effort
+===============================================================================
+
+This book is an experiment to see if we can combine everyone's best efforts to
+create something great.
+
+You can find the contributors right before this introduction. They are listed
+in order of *Karma Points*, a system Launchpad.net uses to keep track of
+contributions from everyone working on an open-source project.
+
+In my case, I will write new material, as well as rewriting other contributions
+to clarify and give voice, setting up the architecture and being the *Benevolent
+Dictator* for the book. But I definitely won't be doing everything; my goal is
+that this is a team project and that everyone who wants to will have something
+useful to contribute.
+
+We'll be using Launchpad.net's "Blueprints" facility to add "features" to the
+book, so that's where you'll find the main repository of things to do.
+
+What can you contribute? Anything as small as spelling and grammatical
+correctons, and as large as a whole chapter. Research into new topics and
+creating examples is what takes me the most time, so if you know something
+already or are willing to figure it out, don't worry if your writing or
+programming isn't perfect -- contribute it and I and the rest of the group will
+improve it.
+
+You may also have talents in figuring things out. Sphinx formatting, for
+example, or how to produce camera-ready formatting. These are all very useful
+things which will not only benefit this book but also any future book built on
+this template (every bit of the build system for the book will be out in the
+open, so if you want to take what we've done here and start your own book, you
+can).
+
+Of course, not everything can make it into the final print book, but things that
+don't fit into the main book can be moved into an "appendix book" or a "volume
+2" book or something like that.
+
+Not an Introductory Book
+===============================================================================
+
+Although there is an introduction for programmers, this book is not intended to
+be introductory. There are already lots of good introductory books out there.
+
+You can think of it as an "intermediate" or "somewhat advanced" book, but the
+"somewhat" modifier is very important here. Because it is not introductory, two
+difficult constraints are removed.
+
+#.  In an introductory book you are forced to describe everything in lock step,
+    never mentioning anything before it has been thoroughly introduced. That's
+    still a good goal, but we don't have to agonize over it when it doesn't
+    happen (just cross-reference the material).
+
+#.  In addition, the topics are not restricted; in this book topics are chosen
+    based on whether they are interesting and/or useful, not on whether they are
+    introductory or not.
+
+That said, people will still be coming to a topic without knowing about it and
+it will need to be introduced, as much as possible, as if they have never seen
+it before.
+
+The License
+===============================================================================
+
+Unless otherwise specified, the material in this book is published under a
+`Creative Commons Attribution-Share Alike 3.0 license
+<http://creativecommons.org/licenses/by-sa/3.0/>`_.
+
+If you make contributions, you must own the rights to your material and be able
+to place them under this license. Please don't contribute something unless you
+are sure this is the case (read your company's employment contract -- these
+often specify that anything you think of or create at any time of day or night
+belongs to the company).
+
+The Printed Book
+===============================================================================
+
+Because of the creative commons license, the electronic version of the book as
+well as all the sources are available, can be reproduced on other web sites,
+etc. (again, as long as you attribute it).
+
+You can print your own version of the book. I will be creating a printed version
+of the book for sale, with a nice cover and binding. Many people do like to have
+a print version of the book, and part of the motivation for doing a print
+version is to make some income off the effort I put into the book.
+
+But buying my particular print version of the book is optional. All of the tools
+will be downloadable so that you can print it yourself, or send it to a copy
+shop and have it bound, etc. The only thing you won't get is my cover and
+binding.
+
+Translations
+===============================================================================
+
+Launchpad.net, where this project is hosted, has support for doing translations
+and this gave me an idea. I had just come back from speaking at the Python
+conference in Brazil, and was thinking about the user group there and how I
+might support them. (We had done a seminar while I was there in order to help
+pay for my trip and support the organization).
+
+If the book can be kept in a Sphinx restructured text format that can be turned
+directly into camera-ready PDF (the basic form is there but it will take
+somebody messing about with it to get it into camera-ready layout), then the job
+of translation can be kept to something that could be done by user groups during
+sprints. The user group could then use a print-on-demand service to print the
+book, and get the group members to take them to local bookstores and do other
+kinds of promotions. The profits from the book could go to the user group (who
+knows, just like the Brazillian group, your group may end up using some of those
+profits to bring me to speak at your conference!).
+
+If possible, I would like a royalty from these translations. To me, 5% of the
+cover price sounds reasonable. If the user group would like to use my cover,
+then they could pay this royalty. If they wanted to go their own way, it's
+creative commons so as long as the book is attributed that's their choice.
+
+My Motives
+===============================================================================
+
+Just so it's clear, I have the following motives for creating this book:
+
+#.  Learn more about Python and contribute to the Python community, to help
+    create more and better Python programmers.
+
+#.  Develop more Python consulting and training clients through the publicity
+    generated by the book (see
+    `here <http://www.mindviewinc.com/Consulting/Index.php>`_).
+
+#.  Experiment with group creation of teaching materials for the book, which
+    will benefit me in my own training (see the previous point) but will also
+    benefit anyone choosing to use the book as a text in a course or training
+    seminar. (See :ref:`TeachingSupport`).
+
+#.  Generate profits by selling printed books. (But see above about the ability
+    to print the book yourself).
+
+#.  Help raise money for non-U.S. Python user groups via translations, from
+    which I might gain a small percentage.

LanguageChanges.rst

+.. index::
+   pair: Language differences; Python 3
+
+********************************************************************************
+Python 3 Language Changes
+********************************************************************************
+
+Covers language features that don't require their own chapters.
+
+.. note:: If a section in this chapter grows too large it may require its own chapter.

MachineDiscovery.rst

+********************************************************************************
+Discovering the Details About Your Platform
+********************************************************************************
+
+The Python library XXX will give you some information about your machine,
+but it falls short. Here's a rather messy, but useful way to figure out
+everything else.
+
+Just a starting point::
+
+    def detectCPUs():
+        """
+        Detects the number of CPUs on a system. Cribbed from pp.
+        """
+        # Linux, Unix and MacOS:
+        if hasattr(os, "sysconf"):
+            if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
+                # Linux & Unix:
+                ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
+                if isinstance(ncpus, int) and ncpus > 0:
+                    return ncpus
+            else: # OSX:
+                return int(os.popen2("sysctl -n hw.ncpu")[1].read())
+        # Windows:
+        if os.environ.has_key("NUMBER_OF_PROCESSORS"):
+                ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
+                if ncpus > 0:
+                    return ncpus
+        return 1 # Default
+        
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files (usable by e.g. sphinx-web)"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf _build/*
+
+html:
+	mkdir -p _build/html _build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
+	@echo
+	@echo "Build finished. The HTML pages are in _build/html."
+
+pickle:
+	mkdir -p _build/pickle _build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files or run"
+	@echo "  sphinx-web _build/pickle"
+	@echo "to start the sphinx-web server."
+
+web: pickle
+
+htmlhelp:
+	mkdir -p _build/htmlhelp _build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in _build/htmlhelp."
+
+latex:
+	mkdir -p _build/latex _build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in _build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p _build/changes _build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
+	@echo
+	@echo "The overview file is in _build/changes."
+
+linkcheck:
+	mkdir -p _build/linkcheck _build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in _build/linkcheck/output.txt."

NoteToReaders.rst

+*******************************************************************************
+A Note To Readers
+*******************************************************************************
+
+What you see here is an early version of the book. We have yet to get everything
+working right and rewritten for Python. Or even to get the book testing systems
+in place.
+
+If you're here because you're curious, that's great. But please don't expect too
+much from the book just yet. When we get it to a point where everything compiles
+and all the Java references, etc. have been rewritten out, then this note will
+disappear. Until then, *caveat emptor*.
+###############################################################################
+Part I: Foundations
+###############################################################################
+
+###############################################################################
+Part II: Idioms
+###############################################################################
+###############################################################################
+Part III: Patterns
+###############################################################################

PythonDecorators.rst

+.. index::
+   decorator: Python decorators
+   @: Python decorators
+
+************************************************************************************
+Decorators
+************************************************************************************
+
+.. note:: This chapter is a work in progress; it's probably better if you don't
+          begin making changes until I've finished the original version, which
+          is being posted as a series on my weblog.
+
+This amazing feature appeared in the language almost apologetically and with
+concern that it might not be that useful.
+
+I predict that in time it will be seen as one of the more powerful features in
+the language. The problem is that all the introductions to decorators that I
+have seen have been rather confusing, so I will try to rectify that here.
+
+Decorators vs. the Decorator Pattern
+=============================================
+
+First, you need to understand that the word "decorator" was used with some
+trepidation in Python, because there was concern that it would be completely
+confused with the *Decorator* pattern from the `Design Patterns book
+<http://www.amazon.com/gp/product/0201633612/ref=ase_bruceeckelA/>`_. At one
+point other terms were considered for the feature, but "decorator" seems to be
+the one that sticks.
+
+Indeed, you can use Python decorators to implement the *Decorator* pattern, but
+that's an extremely limited use of it. Python decorators, I think, are best
+equated to macros.
+
+History of Macros
+==========================
+
+The macro has a long history, but most people will probably have had experience
+with C preprocessor macros. The problems with C macros were (1) they were in a
+different language (not C) and (2) the behavior was sometimes bizarre, and often
+inconsistent with the behavior of the rest of C.
+
+Both Java and C# have added *annotations*, which allow you to do some things to
+elements of the language. Both of these have the problems that (1) to do what
+you want, you sometimes have to jump through some enormous and untenable hoops,
+which follows from (2) these annotation features have their hands tied by the
+bondage-and-discipline (or as `Martin Fowler gently puts it: "Directing"
+<http://martinfowler.com/bliki/SoftwareDevelopmentAttitude.html>`_) nature of
+those languages.
+
+In a slightly different vein, many C++ programmers (myself included) have noted
+the generative abilities of C++ templates and have used that feature in a macro-
+like fashion.
+
+Many other languages have incorporated macros, but without knowing much about it
+I will go out on a limb and say that Python decorators are similar to Lisp
+macros in power and possibility.
+
+The Goal of Macros
+============================
+
+I think it's safe to say that the goal of macros in a language is to provide a
+way to modify elements of the language. That's what decorators do in Python --
+they modify functions, and in the case of *class decorators*, entire classes.
+This is why they usually provide a simpler alternative to metaclasses.
+
+The major failings of most language's self-modification approaches are that they
+are too restrictive and that they require a different language (I'm going to say
+that Java annotations with all the hoops you must jump through to produce an
+interesting annotation comprises a "different language").
+
+Python falls into Fowler's category of "enabling" languages, so if you want to
+do modifications, why create a different or restricted language? Why not just
+use Python itself? And that's what Python decorators do.
+
+What Can You Do With Decorators?
+===================================
+
+Decorators allow you to inject or modify code in functions or classes. Sounds a
+bit like *Aspect-Oriented Programming* (AOP) in Java, doesn't it? Except that
+it's both much simpler and (as a result) much more powerful. For example,
+suppose you'd like to do something at the entry and exit points of a function
+(such as perform some kind of security, tracing, locking, etc. -- all the
+standard arguments for AOP). With decorators, it looks like this::
+
+    @entryExit
+    def func1():
+        print("inside func1()")
+
+    @entryExit
+    def func2():
+        print("inside func2()")
+
+The ``@`` indicates the application of the decorator.
+
+Function Decorators
+==============================
+
+A function decorator is applied to a function definition by placing it on the
+line before that function definition begins. For example::
+
+    @myDecorator
+    def aFunction():
+        print("inside aFunction")
+
+When the compiler passes over this code, ``aFunction()`` is compiled and the
+resulting function object is passed to the ``myDecorator`` code, which does
+something to produce a function-like object that is then substituted for the
+original ``aFunction()``.
+
+What does the ``myDecorator`` code look like? Well, most introductory examples
+show this as a function, but I've found that it's easier to start understanding
+decorators by using classes as decoration mechanisms instead of functions. In
+addition, it's more powerful.
+
+The only constraint upon the object returned by the decorator is that it can be
+used as a function -- which basically means it must be callable. Thus, any
+classes we use as decorators must implement ``__call__``.
+
+What should the decorator do? Well, it can do anything but usually you expect
+the original function code to be used at some point. This is not required,
+however::
+
+    class myDecorator(object):
+
+        def __init__(self, f):
+            print("inside myDecorator.__init__()")
+            f() # Prove that function definition has completed
+
+        def __call__(self):
+            print("inside myDecorator.__call__()")
+
+    @myDecorator
+    def aFunction():
+        print("inside aFunction()")
+
+    print("Finished decorating aFunction()")
+
+    aFunction()
+
+
+When you run this code, you see::
+
+    inside myDecorator.__init__()
+    inside aFunction()
+    Finished decorating aFunction()
+    inside myDecorator.__call__()
+
+Notice that the constructor for ``myDecorator`` is executed at the point of
+decoration of the function. Since we can call ``f()`` inside ``__init__()``, it
+shows that the creation of ``f()`` is complete before the decorator is called.
+Note also that the decorator constructor receives the function object being
+decorated. Typically, you'll capture the function object in the constructor and
+later use it in the ``__call__()`` method (the fact that decoration and calling
+are two clear phases when using classes is why I argue that it's easier and more
+powerful this way).
+
+When ``aFunction()`` is called after it has been decorated, we get completely
+different behavior; the ``myDecorator.__call__()`` method is called instead of
+the original code. That's because the act of decoration *replaces* the original
+function object with the result of the decoration -- in our case, the
+``myDecorator`` object replaces ``aFunction``. Indeed, before decorators were
+added you had to do something much less elegant to achieve the same thing::
+
+    def foo(): pass
+    foo = staticmethod(foo)
+
+With the addition of the ``@`` decoration operator, you now get the same result
+by saying::
+
+    @staticmethod
+    def foo(): pass
+
+This is the reason why people argued against decorators, because the ``@`` is
+just a little syntax sugar meaning "pass a function object through another
+function and assign the result to the original function."
+
+The reason I think decorators will have such a big impact is because this little
+bit of syntax sugar changes the way you think about programming. Indeed, it
+brings the idea of "applying code to other code" (i.e.: macros) into mainstream
+thinking by formalizing it as a language construct.
+
+Slightly More Useful
+========================
+
+Now let's go back and implement the first example. Here, we'll do the more
+typical thing and actually use the code in the decorated functions::
+
+    class entryExit(object):
+
+        def __init__(self, f):
+            self.f = f
+
+        def __call__(self):
+            print("Entering", self.f.__name__)
+            self.f()
+            print("Exited", self.f.__name__)
+
+    @entryExit
+    def func1():
+        print("inside func1()")
+
+    @entryExit
+    def func2():
+        print("inside func2()")
+
+    func1()
+    func2()
+
+The output is::
+
+    Entering func1
+    inside func1()
+    Exited func1
+    Entering func2
+    inside func2()
+    Exited func2
+
+You can see that the decorated functions now have the "Entering" and "Exited"
+trace statements around the call.
+
+The constructor stores the argument, which is the function object. In the call,
+we use the ``__name__`` attribute of the function to display that function's
+name, then call the function itself.
+
+Using Functions as Decorators
+=====================================
+
+The only constraint on the result of a decorator is that it be callable, so it
+can properly replace the decorated function. In the above examples, I've
+replaced the original function with an object of a class that has a
+``__call__()`` method. But a function object is also callable, so we can rewrite
+the previous example using a function instead of a class, like this::
+
+    def entryExit(f):
+        def new_f():
+            print("Entering", f.__name__)
+            f()
+            print("Exited", f.__name__)
+        return new_f
+
+    @entryExit
+    def func1():
+        print("inside func1()")
+
+    @entryExit
+    def func2():
+        print("inside func2()")
+
+    func1()
+    func2()
+    print(func1.__name__)
+
+``new_f()`` is defined within the body of ``entryExit()``, so it is created and
+returned when ``entryExit()`` is called. Note that ``new_f()`` is a *closure*,
+because it captures the actual value of ``f``.
+
+Once ``new_f()`` has been defined, it is returned from ``entryExit()`` so that
+the decorator mechanism can assign the result as the decorated function.
+
+The output of the line ``print(func1.__name__)`` is ``new_f``, because the
+``new_f`` function has been substituted for the original function during
+decoration. If this is a problem you can change the name of the decorator
+function before you return it::
+
+    def entryExit(f):
+        def new_f():
+            print("Entering", f.__name__)
+            f()
+            print("Exited", f.__name__)
+        new_f.__name__ = f.__name__
+        return new_f
+
+The information you can dynamically get about functions, and the modifications
+you can make to those functions, are quite powerful in Python.
+
+Review: Decorators without Arguments
+=========================================
+
+If we create a decorator without arguments, the function to be decorated is
+passed to the constructor, and the ``__call__()`` method is called whenever the
+decorated function is invoked::
+
+    class decoratorWithoutArguments(object):
+
+        def __init__(self, f):
+            """
+            If there are no decorator arguments, the function
+            to be decorated is passed to the constructor.
+            """
+            print("Inside __init__()")
+            self.f = f
+
+        def __call__(self, *args):
+            """
+            The __call__ method is not called until the
+            decorated function is called.
+            """
+            print("Inside __call__()")
+            self.f(*args)
+            print("After self.f(*args)")
+
+    @decoratorWithoutArguments
+    def sayHello(a1, a2, a3, a4):
+        print('sayHello arguments:', a1, a2, a3, a4)
+
+    print("After decoration")
+
+    print("Preparing to call sayHello()")
+    sayHello("say", "hello", "argument", "list")
+    print("After first sayHello() call")
+    sayHello("a", "different", "set of", "arguments")
+    print("After second sayHello() call")
+
+Any arguments for the decorated function are just passed to ``__call__()``. The
+output is::
+
+    Inside __init__()
+    After decoration
+    Preparing to call sayHello()
+    Inside __call__()
+    sayHello arguments: say hello argument list
+    After self.f(*args)
+    After first sayHello() call
+    Inside __call__()
+    sayHello arguments: a different set of arguments
+    After self.f(*args)
+    After second sayHello() call
+
+Notice that ``__init__()`` is the only method called to perform decoration, and
+``__call__()`` is called every time you call the decorated ``sayHello()``.
+
+
+Decorators with Arguments
+====================================
+
+The decorator mechanism behaves quite differently when you pass arguments to the
+decorator.
+
+Let's modify the above example to see what happens when we add arguments to the
+decorator::
+
+    class decoratorWithArguments(object):
+
+        def __init__(self, arg1, arg2, arg3):
+            """
+            If there are decorator arguments, the function
+            to be decorated is not passed to the constructor!
+            """
+            print("Inside __init__()")
+            self.arg1 = arg1
+            self.arg2 = arg2
+            self.arg3 = arg3
+
+        def __call__(self, f):
+            """
+            If there are decorator arguments, __call__() is only called
+            once, as part of the decoration process! You can only give
+            it a single argument, which is the function object.
+            """
+            print("Inside __call__()")
+            def wrapped_f(*args):
+                print("Inside wrapped_f()")
+                print("Decorator arguments:", self.arg1, self.arg2, self.arg3)
+                f(*args)
+                print("After f(*args)")
+            return wrapped_f
+
+    @decoratorWithArguments("hello", "world", 42)
+    def sayHello(a1, a2, a3, a4):
+        print('sayHello arguments:', a1, a2, a3, a4)
+
+    print("After decoration")
+
+    print("Preparing to call sayHello()")
+    sayHello("say", "hello", "argument", "list")
+    print("after first sayHello() call")
+    sayHello("a", "different", "set of", "arguments")
+    print("after second sayHello() call")
+
+From the output, we can see that the behavior changes quite significantly::
+
+    Inside __init__()
+    Inside __call__()
+    After decoration
+    Preparing to call sayHello()
+    Inside wrapped_f()
+    Decorator arguments: hello world 42
+    sayHello arguments: say hello argument list
+    After f(*args)
+    after first sayHello() call
+    Inside wrapped_f()
+    Decorator arguments: hello world 42
+    sayHello arguments: a different set of arguments
+    After f(*args)
+    after second sayHello() call
+
+Now the process of decoration calls the constructor and then immediately invokes
+``__call__()``, which can only take a single argument (the function object) and
+must return the decorated function object that replaces the original. Notice
+that ``__call__()`` is now only invoked once, during decoration, and after that
+the decorated function that you return from ``__call__()`` is used for the
+actual calls.
+
+Although this behavior makes sense -- the constructor is now used to capture the
+decorator arguments, but the object ``__call__()`` can no longer be used as the
+decorated function call, so you must instead use ``__call__()`` to perform the
+decoration -- it is nonetheless surprising the first time you see it because
+it's acting so much differently than the no-argument case, and you must code the
+decorator very differently from the no-argument case.
+
+Decorator Functions with Decorator Arguments
+==================================================
+
+Finally, let's look at the more complex decorator function implementation, where
+you have to do everything all at once::
+
+    def decoratorFunctionWithArguments(arg1, arg2, arg3):
+        def wrap(f):
+            print("Inside wrap()")
+            def wrapped_f(*args):
+                print("Inside wrapped_f()")
+                print("Decorator arguments:", arg1, arg2, arg3)
+                f(*args)
+                print("After f(*args)")
+            return wrapped_f
+        return wrap
+
+    @decoratorFunctionWithArguments("hello", "world", 42)
+    def sayHello(a1, a2, a3, a4):
+        print('sayHello arguments:', a1, a2, a3, a4)
+
+    print("After decoration")
+
+    print("Preparing to call sayHello()")
+    sayHello("say", "hello", "argument", "list")
+    print("after first sayHello() call")
+    sayHello("a", "different", "set of", "arguments")
+    print("after second sayHello() call")
+
+Here's the output::
+
+    Inside wrap()
+    After decoration
+    Preparing to call sayHello()
+    Inside wrapped_f()
+    Decorator arguments: hello world 42
+    sayHello arguments: say hello argument list
+    After f(*args)
+    after first sayHello() call
+    Inside wrapped_f()
+    Decorator arguments: hello world 42
+    sayHello arguments: a different set of arguments
+    After f(*args)
+    after second sayHello() call
+
+The return value of the decorator function must be a function used to wrap the
+function to be decorated. That is, Python will take the returned function and
+call it at decoration time, passing the function to be decorated. That's why we
+have three levels of functions; the inner one is the actual replacement
+function.
+
+Because of closures, ``wrapped_f()`` has access to the decorator arguments
+``arg1``, ``arg2`` and ``arg3``, *without* having to explicitly store them as in
+the class version. However, this is a case where I find "explicit is better than
+implicit," so even though the function version is more succinct I find the class
+version easier to understand and thus to modify and maintain.
+
+.. seealso::
+
+    http://wiki.python.org/moin/PythonDecoratorLibrary
+        More examples of decorators. Note the number of these examples that
+        use classes rather than functions as decorators.
+
+    http://scratch.tplus1.com/decoratortalk
+        Matt Wilson's *Decorators Are Fun*.
+
+    http://loveandtheft.org/2008/09/22/python-decorators-explained
+        Another introduction to decorators
+
+    http://www.phyast.pitt.edu/~micheles/python/documentation.html
+        Michele Simionato's decorator module wraps functions for you. The page
+        includes an introduction and some examples.
+
+*******************************************************************************
+Book Development Rules
+*******************************************************************************
+
+Guidelines for the creation process.
+
+.. note:: This is just a start. This document will evolve as more issues appear.
+
+Contribute What You Can
+===============================================================================
+
+One of the things I've learned by holding open-spaces conferences is that
+everyone has something useful to contribute, although they often don't know it.
+
+Maybe you're don't feel expert enough at Python to contribute anything yet.
+But you're in this field, so you've got some useful abilities.
+
+- Maybe you're good at admin stuff, figuring out how things work and configuring
+  things.
+
+- If you have a flair for design and can figure out Sphinx templating,
+  that will be useful.
+
+- Perhaps you are good at Latex. We need to figure out how to format the
+  PDF version of the book from the Sphinx sources, so that we can produce
+  the print version of the book without going through hand work in a layout
+  program.
+
+- You probably have ideas about things you'd like to understand, that haven't
+  been covered elsewhere.
+
+- And sometimes people are particularly good at spotting typos.
+
+Don't Get Attached
+===============================================================================
+
+Writing is rewriting. Your stuff will get rewritten, probably multiple times.
+This makes it better for the reader. It doesn't mean it was bad. Everything can
+be made better.
+
+By the same measure, don't worry if your examples or prose aren't perfect. Don't
+let that keep you from contributing them early. They'll get rewritten anyway, so
+don't worry too much -- do the best you can, but don't get blocked.
+
+There's also an editor who will be rewriting for clarity and active voice. He
+doesn't necessarily understand the technology but he will still be able to
+improve the flow. If you discover he has introduced technical errors in the
+prose, then feel free to fix it but try to maintain any clarity that he has
+added.
+
+If something is moved to Volume 2 or the electronic-only appendices, it's just
+a decision about the material, not about you.
+
+Credit
+===============================================================================
+
+As much as possible, I want to give credit to contributions. Much of this will
+be taken care of automatically by the Launchpad.net "Karma" system. However, if
+you contribute something significant, for example the bulk of a new chapter,
+then you should put "contributed by" at the beginning of that chapter, and if
+you make significant improvements and changes to a chapter you should say
+"further contributions by" or "further changes by", accordingly.
+
+Mechanics
+===============================================================================
+
+- Automate everything. Everything should be in the build script; nothing should
+  be done by hand.
+
+- All documents will be in Sphinx restructured text format. Here's the
+  `link to the Sphinx documentation <http://sphinx.pocoo.org/contents.html>`_.
+
+- Everything goes through Launchpad.net and uses Launchpad's Bazzar distributed
+  version control system.
+
+- Follow PEP8 for style. That way we don't have to argue about it.
+
+- Camelcasing for naming. PEP8 suggests underscores as a preference rather than
+  a hard-and fast rule, and camelcasing *feels* more like OO to me, as if we are
+  emphasizing the design here (which I want to do) and putting less focus on the
+  C-ish nature that *can* be expressed in Python.
+
+::
+
+    The above point is still being debated.
+
+- Four space indents.
+
+- We're not using chapter numbers because we'll be moving chapters around.
+  If you need to cross-reference a chapter, use the chapter name and a
+  link.
+
+- Index as you go. Indexing will happen throughout the project. Although
+  finalizing the index is a task in itself, it will be very helpful if everyone
+  adds index entries anytime they occur to you. You can find example index
+  entries by going to the index, clicking on one of the entries, then selecting
+  "view source" in the left-side bar (Sphinx cleverly shows you the Sphinx
+  source so you can use it as an example).
+
+- Don't worry about chapter length. Some chapters may be very small, others may
+  be quite significant. It's just the nature of this book. Trying to make the
+  chapters the same length will end up fluffing some up which will not benefit
+  the reader. Make the chapters however long they need to be, but no longer.
+
+Diagrams
+===============================================================================
+
+Create diagrams using whatever tool is convenient for you, as long as it produces
+formats that Sphinx can use.
+
+It doesn't matter if your diagram is imperfect. Even if you just sketch something
+by hand and scan it in, it will help readers visualize what's going on.
+
+At some point, diagrams will be redone for consistency using a single tool,
+with print publication in mind. This tool may be a commercial product. However,
+if you need to change the diagram you can replace it with your new version using
+your tool of choice. The important thing is to get the diagram right; at some
+point it will be redone to look good.
+
+Note that all image tags should use a ``*`` at the end, not the file extension
+name. For example ``..image:: _images/foo.*``. This way the tag will work for
+both the HTML output and the Latex output. Also, all images should be placed
+in the ``_images`` directory.
+
+Here's an example which was done with the free online service Gliffy.com, then
+modified using the free Windows program Paint.NET (note, however, that we should
+not use color because it won't translate well to the print book):
+
+.. image:: _images/DistributedSystem.*
+
+# SanityCheck.py
+#! /usr/bin/env python
+import string, glob, os
+# Do not include the following in the automatic
+# tests:
+exclude = ("SanityCheck.py", "BoxObserver.py",)
+
+def visitor(arg, dirname, names):
+    dir = os.getcwd()
+    os.chdir(dirname)
+    try:
+        pyprogs = [p for p in glob.glob('*.py')
+                   if p not in exclude ]
+        if not pyprogs: return
+        print '[' + os.getcwd() + ']'
+        for program in pyprogs:
+            print '\t', program
+            os.system("python %s > tmp" % program)
+            file = open(program).read()
+            output = open('tmp').read()
+            # Append output if it's not already there:
+            if file.find("output = '''") == -1 and \
+              len(output) > 0:
+                divider = '#' * 50 + '\n'
+                file = file.replace('#' + ':~', '#<hr>\n')
+                file += "output = '''\n" + \
+                  open('tmp').read() + "'''\n"
+                open(program,'w').write(file)
+    finally:
+        os.chdir(dir)
+
+if __name__ == "__main__":
+    os.path.walk('.', visitor, None)

TeachingSupport.rst

+.. _TeachingSupport:
+
+*******************************************************************************
+Teaching Support
+*******************************************************************************
+
+Teachers and lecturers often need support material to help them use a book for
+teaching. I have put some exercises in, and I hope we can add more.
+
+I'd also like to create teaching materials like slides and exercises as a group
+effort with a creative commons license, especially by allying with those people
+who are in the teaching professions. I'd like to make it something that everyone
+would like to teach from -- including myself.
+
+Here are some places to get ideas for exercises and projects:
+
+  - `For coding dojos <http://www.dtsato.com/blog/2008/10/21/source-of-problems-for-your-coding-dojo/>`_
+
+  - `Rosetta Code <http://www.rosettacode.org/wiki/Category:Python>`_
+Refine "Printed Book" and "Translations"
+Code extractor for rst files (maybe part of intro chapter?)
+Code updater to put code in/refresh code into book.
+
+Move frontmatter into its own directory
+
+<!> Seems to be a warning sign (but initial tests didn't work)
+
+Idea: decorator on a dictionary object, to turn it into an ordered dictionary.
+
+"Other resources" at the end of each chapter
+
+For print version, convert hyperlinks into footnotes.
+(build tool for this)
Add a comment to this file

_images/DistributedSystem.pdf

Binary file added.

Add a comment to this file

_images/DistributedSystem.png

Added
New image
Add a comment to this file

_images/coffeeExplosion.gif

Added
New image
Add a comment to this file

_images/coffeeExplosion.pdf

Binary file added.

Add a comment to this file

_images/compromiseDecoration.gif

Added
New image
Add a comment to this file

_images/compromiseDecoration.pdf

Binary file added.

Add a comment to this file

_images/decoratedCoffee.gif

Added
New image
Add a comment to this file

_images/decoratedCoffee.pdf

Binary file added.

Added
New image

Binary file added.

Binary file added.

Added
New image
Add a comment to this file

_images/stateMachine.gif

Added
New image
Add a comment to this file

_images/stateMachine.pdf

Binary file added.

Added
New image

Binary file added.

Add a comment to this file

_images/trashSorter1.gif

Added
New image
Add a comment to this file

_images/trashSorter1.pdf

Binary file added.

Add a comment to this file

_images/trashSorter2.gif

Added
New image
Add a comment to this file

_images/trashSorter2.pdf

Binary file added.

Add a comment to this file

_images/trashSorter3.gif

Added
New image
Add a comment to this file

_images/trashSorter3.pdf

Binary file added.

Add a comment to this file

_images/trashVisitor.gif

Added
New image
Add a comment to this file

_images/trashVisitor.pdf

Binary file added.

Add a comment to this file

_static/LaunchpadBrand.png

Added
New image
Add a comment to this file

_static/LaunchpadIcon.png

Added
New image
Add a comment to this file

_static/LaunchpadLogo.png

Added
New image

Binary file added.

Added
New image

_templates/layout.html

+{% extends "!layout.html" %}
+{% block sidebarsearch %}
+    {{ super() }}
+    <h4><a href="http://www.mindviewinc.com/Books/Python3Patterns/Index.php">Project Homepage</a></h4>
+    <h4><a href="https://bugs.launchpad.net/python3patterns">Corrections/Suggestions</a></h4>
+    <h4><a href="http://www.mindviewinc.com/Consulting/Index.php">Consulting &amp; Training</a></h4><br><br>
+{% endblock %}

appFrameworks.rst

+
+*******************************************************************************
+Building Application Frameworks
+*******************************************************************************
+
+An application framework allows you to inherit from a class or set of classes
+and create a new application, reusing most of the code in the existing classes
+and overriding one or more methods in order to customize the application to your
+needs. A fundamental concept in the application framework is the *Template
+Method* which is typically hidden beneath the covers and drives the application
+by calling the various methods in the base class (some of which you have
+overridden in order to create the application).
+
+For example, whenever you create an applet you're using an application
+framework: you inherit from **JApplet** and then override **init( )**. The
+applet mechanism (which is a *Template Method*) does the rest by drawing the
+screen, handling the event loop, resizing, etc.
+
+Template Method
+=======================================================================
+
+An important characteristic of the *Template Method* is that it is defined in
+the base class and cannot be changed. It's sometimes a **private** method but
+it's virtually always **final**. It calls other base-class methods (the ones you
+override) in order to do its job, but it is usually called only as part of an
+initialization process (and thus the client programmer isn't necessarily able to
+call it directly)::
+
+    # appFrameworks/TemplateMethod.py
+    # Simple demonstration of Template Method.
+
+    class ApplicationFramework:
+        def __init__(self):
+            self.__templateMethod()
+        def __templateMethod(self):
+            for i in range(5):
+                self.customize1()
+                self.customize2()
+
+    # Create an "application":
+    class MyApp(ApplicationFramework):
+        def customize1(self):
+            print("Nudge, nudge, wink, wink! ",)
+        def customize2(self):
+            print("Say no more, Say no more!")
+
+    MyApp()
+
+
+
+The base-class constructor is responsible for performing the necessary
+initialization and then starting the "engine" (the template method) that runs
+the application (in a GUI application, this "engine" would be the main event
+loop). The client programmer simply provides definitions for **customize1( )**
+and **customize2( )** and the "application" is ready to run.
+
+We'll see *Template Method* numerous other times throughout the book.
+
+Exercises
+=======================================================================
+
+#.  Create a framework that takes a list of file names on the command line. It
+    opens each file except the last for reading, and the last for writing. The
+    framework will process each input file using an undetermined policy and
+    write the output to the last file. Inherit to customize this framework to
+    create two separate applications:
+
+        #. Converts all the letters in each file to uppercase.
+        #. Searches the files for words given in the first file.
+
+
+
+python build.py $*
+python build.py %1 %2 %3 %4 %5 %6 %7
+from builder import rule
+import os, shutil
+# for a complete distribution build from scratch, say 'build clean package'
+
+@rule()
+def html():
+    "Create html files"
+    os.system("make html")
+
+@rule("_build/htmlhelp")
+def winhelp():
+    "Create Windows Help file"
+    os.system("make htmlhelp")
+    os.chdir("_build/htmlhelp")
+    os.system(r"hhc Python3PatternsIdiomsdoc.hhp")
+    os.chdir("../..")
+
+@rule()
+def clean():
+    "Remove files that have been built"
+    os.system("make clean")
+    if os.path.exists("_test"):
+        shutil.rmtree("_test")
+    if os.path.exists("_code"):
+        shutil.rmtree("_code")
+
+# Bug: This needs to work:
+#@rule(["_build/Python3PatternsAndIdioms-html.zip",
+#       "_build/Python3PatternsAndIdioms-htmlhelp.zip"], html, winhelp)
+
+@rule(None, html, winhelp)
+def package():
+    "Create Zip files for upload to Launchpad; for end-user downloads"
+    os.chdir("_build")
+    os.system("zip -r Python3PatternsAndIdioms-html.zip html")
+    os.system("zip -r Python3PatternsAndIdioms-htmlhelp.zip htmlhelp/Python3PatternsIdiomsdoc.*")
+    os.chdir("..")
+
+@rule(None, html, winhelp)
+def all():
+    "Build both html and windows help files"
+
+rule.default = all
+rule.main() # Does the build, handles command-line arguments
+# builder.py
+import sys, os, stat
+"""
+Adds build rules atop Python, to replace make, etc.
+by Bruce Eckel
+License: Creative Commons with Attribution.
+"""
+
+def reportError(msg):
+    print >> sys.stderr, "Error:", msg
+    sys.exit(1)
+
+class Dependency(object):
+    "Created by the decorator to represent a single dependency relation"
+
+    changed = True
+    unchanged = False
+
+    @staticmethod
+    def show(flag):
+        if flag: return "Updated"
+        return "Unchanged"
+
+    def __init__(self, target, dependency):
+        self.target = target
+        self.dependency = dependency
+
+    def __str__(self):
+        return "target: %s, dependency: %s" % (self.target, self.dependency)
+
+    @staticmethod
+    def create(target, dependency): # Simple Factory
+        if target == None:
+            return NoTarget(dependency)
+        if type(target) == str: # String means file name
+            if dependency == None:
+                return FileToNone(target, None)
+            if type(dependency) == str:
+                return FileToFile(target, dependency)
+            if type(dependency) == Dependency:
+                return FileToDependency(target, dependency)
+        reportError("No match found in create() for target: %s, dependency: %s"
+            % (target,  dependency))
+
+    def updated(self):
+        """
+        Call to determine whether this is up to date.
+        Returns 'changed' if it had to update itself.
+        """
+        assert False, "Must override Dependency.updated() in derived class"
+
+class NoTarget(Dependency): # Always call updated() on dependency
+    def __init__(self, dependency):
+        Dependency.__init__(self, None, dependency)
+    def updated(self):
+        if not self.dependency:
+            return Dependency.changed # (None, None) -> always run rule
+        return self.dependency.updated() # Must be a Dependency or subclass
+
+class FileToNone(Dependency): # Run rule if file doesn't exist
+    def updated(self):
+        if not os.path.exists(self.target):
+            return Dependency.changed
+        return Dependency.unchanged
+
+class FileToFile(Dependency): # Compare file datestamps
+    def updated(self):
+        if not os.path.exists(self.dependency):
+            reportError("%s does not exist" % self.dependency)
+        if not os.path.exists(self.target):
+            return Dependency.changed # If it doesn't exist it needs to be made
+        if os.path.getmtime(self.dependency) > os.path.getmtime(self.target):
+            return Dependency.changed
+        return Dependency.unchanged
+
+class FileToDependency(Dependency): # Update if dependency object has changed
+    def updated(self):
+        if self.dependency.updated():
+            return Dependency.changed
+        if not os.path.exists(self.target):
+            return Dependency.changed # If it doesn't exist it needs to be made
+        return Dependency.unchanged
+
+class rule(object):
+    """
+    Decorator that turns a function into a build rule. First file or object in
+    decorator arglist is the target, remainder are dependencies.
+    """
+    rules = []
+    default = None
+
+    class _Rule(object):
+        """
+        Command pattern. name, dependencies, ruleUpdater and description are
+        all injected by class rule.
+        """
+
+        def updated(self):
+            if Dependency.changed in [d.updated() for d in self.dependencies]:
+                self.ruleUpdater()
+                return Dependency.changed
+            return Dependency.unchanged
+
+        def __str__(self): return self.description
+
+    def __init__(self, *decoratorArgs):
+        """
+        This constructor is called first when the decorated function is
+        defined, and captures the arguments passed to the decorator itself.
+        (Note Builder pattern)
+        """
+        self._rule = rule._Rule()
+        decoratorArgs = list(decoratorArgs)
+        if decoratorArgs:
+            if len(decoratorArgs) == 1:
+                decoratorArgs.append(None)
+            target = decoratorArgs.pop(0)
+            if type(target) != list:
+                target = [target]
+            self._rule.dependencies = [Dependency.create(targ, dep)
+                for targ in target for dep in decoratorArgs]
+        else: # No arguments
+            self._rule.dependencies = [Dependency.create(None, None)]
+
+    def __call__(self, func):
+        """
+        This is called right after the constructor, and is passed the function
+        object being decorated. The returned _rule object replaces the original
+        function.
+        """
+        if func.__name__ in [r.name for r in rule.rules]:
+            reportError("@rule name %s must be unique" % func.__name__)
+        self._rule.name = func.__name__
+        self._rule.description = func.__doc__ or ""
+        self._rule.ruleUpdater = func
+        rule.rules.append(self._rule)
+        return self._rule # This is substituted as the decorated function
+
+    @staticmethod
+    def update(x):
+        if x == 0:
+            if rule.default:
+                return rule.default.updated()
+            else:
+                return rule.rules[0].updated()
+        # Look up by name
+        for r in rule.rules:
+            if x == r.name:
+                return r.updated()
+        raise KeyError
+
+    @staticmethod
+    def main():
+        """
+        Produce command-line behavior
+        """
+        if len(sys.argv) == 1:
+            print Dependency.show(rule.update(0))
+        try:
+            for arg in sys.argv[1:]:
+                print Dependency.show(rule.update(arg))
+        except KeyError:
+            print "Available rules are:\n"
+            for r in rule.rules:
+                if r == rule.default:
+                    newline = " (Default if no rule is specified)\n"
+                else:
+                    newline = "\n"
+                print "%s:%s\t%s\n" % (r.name, newline, r)
+            print "(Multiple targets will be updated in order)"
+        # Create "build" commands for Windows and Unix:
+        if not os.path.exists("build.bat"):
+            file("build.bat", 'w').write("python build.py %1 %2 %3 %4 %5 %6 %7")
+        if not os.path.exists("build"):
+            # Unless you can detect cygwin independently of Windows
+            file("build", 'w').write("python build.py $*")
+            os.chmod("build", stat.S_IEXEC)
+
+############### Test/Usage Examples ###############
+
+if __name__ == "__main__":
+    if not os.path.exists("build.py"):
+        file("build.py", 'w').write('''\
+# Use cases: both test code and usage examples
+from builder import rule
+import os
+
+@rule("file1.txt")
+def file1():
+    "File doesn't exist; run rule"
+    file("file1.txt", 'w')
+
+def touchOrCreate(f): # Ordinary function
+    "Bring file up to date; creates it if it doesn't exist"
+    if os.path.exists(f):
+        os.utime(f, None)
+    else:
+        file(f, 'w')
+
+dependencies = ["dependency1.txt", "dependency2.txt",
+                "dependency3.txt", "dependency4.txt"]
+
+targets = ["file1.txt", "target1.txt", "target2.txt"]
+
+allFiles = targets + dependencies
+
+@rule(allFiles)
+def multipleTargets():
+    "Multiple files don't exist; run rule"
+    [file(f, 'w') for f in allFiles if not os.path.exists(f)]
+
+@rule(["target1.txt", "target2.txt"], "dependency1.txt", "dependency2.txt")
+def multipleBoth():
+    "Multiple targets and dependencies"
+    [touchOrCreate(f) for f in ["target1.txt", "target2.txt"]]
+
+@rule("target1.txt","dependency1.txt","dependency2.txt","dependency3.txt")
+def target1():
+    "Brings target1.txt up to date with its dependencies"
+    touchOrCreate("target1.txt")
+
+@rule()
+def updateDependency():
+    "Updates the timestamp on all dependency.* files"
+    [touchOrCreate(f) for f in allFiles if f.startswith("dependency")]
+
+@rule()
+def clean():
+    "Remove all created files"
+    [os.remove(f) for f in allFiles if os.path.exists(f)]
+
+@rule()
+def cleanTargets():
+    "Remove all target files"
+    [os.remove(f) for f in targets if os.path.exists(f)]
+
+@rule("target2.txt", "dependency2.txt", "dependency4.txt")
+def target2():
+    "Brings target2.txt up to date with its dependencies, or creates it"
+    touchOrCreate("target2.txt")
+
+@rule(None, target1, target2)
+def target3():
+    "Always brings target1 and target2 up to date"
+    print target3
+
+@rule(None, clean, file1, multipleTargets, multipleBoth, target1,
+      updateDependency, target2, target3)
+def all():
+    "Brings everything up to date"
+    print all
+
+rule.default = all
+rule.main() # Does the build, handles command-line arguments
+''')

Binary file added.

changeInterface.rst

+
+********************************************************************************
+Changing the Interface
+********************************************************************************
+
+Sometimes the problem that you're solving is as simple as "I don't have the
+interface that I want." Two of the patterns in *Design Patterns* solve this
+problem: *Adapter* takes one type and produces an interface to some other type.
+*Façade* creates an interface to a set of classes, simply to provide a more
+comfortable way to deal with a library or bundle of resources.
+
+Adapter
+=======================================================================
+
+When you've got *this*, and you need *that*, *Adapter* solves the problem. The
+only requirement is to produce a *that*, and there are a number of ways you can
+accomplish this adaptation::
+
+    # changeInterface/Adapter.py
+    # Variations on the Adapter pattern.
+
+    class WhatIHave:
+        def g(self): pass
+        def h(self): pass
+
+    class WhatIWant:
+        def f(self): pass
+
+    class ProxyAdapter(WhatIWant):
+        def __init__(self, whatIHave):
+            self.whatIHave = whatIHave
+
+        def f(self):
+            # Implement behavior using
+            # methods in WhatIHave:
+            self.whatIHave.g()
+            self.whatIHave.h()
+
+    class WhatIUse:
+        def op(self, whatIWant):
+            whatIWant.f()
+
+    # Approach 2: build adapter use into op():
+    class WhatIUse2(WhatIUse):
+        def op(self, whatIHave):
+            ProxyAdapter(whatIHave).f()
+
+    # Approach 3: build adapter into WhatIHave:
+    class WhatIHave2(WhatIHave, WhatIWant):
+        def f(self):
+            self.g()
+            self.h()
+
+    # Approach 4: use an inner class:
+    class WhatIHave3(WhatIHave):
+        class InnerAdapter(WhatIWant):
+            def __init__(self, outer):
+                self.outer = outer
+            def f(self):
+                self.outer.g()
+                self.outer.h()
+
+        def whatIWant(self):
+            return WhatIHave3.InnerAdapter(self)
+
+    whatIUse = WhatIUse()
+    whatIHave = WhatIHave()
+    adapt= ProxyAdapter(whatIHave)
+    whatIUse2 = WhatIUse2()
+    whatIHave2 = WhatIHave2()
+    whatIHave3 = WhatIHave3()
+    whatIUse.op(adapt)
+    # Approach 2:
+    whatIUse2.op(whatIHave)
+    # Approach 3:
+    whatIUse.op(whatIHave2)
+    # Approach 4:
+    whatIUse.op(whatIHave3.whatIWant())
+
+I'm taking liberties with the term "proxy" here, because in *Design Patterns*
+they assert that a proxy must have an identical interface with the object that
+it is a surrogate for. However, if you have the two words together: "proxy
+adapter," it is perhaps more reasonable.
+
+Façade
+=======================================================================
+
+A general principle that I apply when I'm casting about trying to mold
+requirements into a first-cut object is "If something is ugly, hide it inside an
+object." This is basically what *Façade* accomplishes. If you have a rather
+confusing collection of classes and interactions that the client programmer
+doesn't really need to see, then you can create an interface that is useful for
+the client programmer and that only presents what's necessary.
+
+Façade is often implemented as singleton abstract factory. Of course, you can
+easily get this effect by creating a class containing **static** factory
+methods::
+
+    # changeInterface/Facade.py
+    class A:
+        def __init__(self, x): pass
+    class B:
+        def __init__(self, x): pass
+    class C:
+        def __init__(self, x): pass
+
+    # Other classes that aren't exposed by the
+    # facade go here ...
+
+    class Facade:
+        def makeA(x): return A(x)
+        makeA = staticmethod(makeA)
+        def makeB(x): return B(x)
+        makeB = staticmethod(makeB)
+        def makeC(x): return C(x)
+        makeC = staticmethod(makeC)
+
+    # The client programmer gets the objects
+    # by calling the static methods:
+    a = Facade.makeA(1);
+    b = Facade.makeB(1);
+    c = Facade.makeC(1.0);
+
+[rewrite this section using research from Larman's book]
+
+Example for Facade (?): my "nicer" version of the XML library.
+
+Exercises
+=======================================================================
+
+#.  Create an adapter class that automatically loads a two-dimensional array of
+    objects into a dictionary as key-value pairs.
+
+
+
+=== added file '.bzrignore'
+--- .bzrignore	1970-01-01 00:00:00 +0000
++++ .bzrignore	2008-11-07 04:55:02 +0000
+@@ -0,0 +1,9 @@
++*.pyc
++*.pyo
++*~
++*-
++#*
++.db
++.svn
++.swp
++_build
+
+=== modified file 'Comprehensions.rst'
+--- Comprehensions.rst	2008-11-05 20:42:20 +0000
++++ Comprehensions.rst	2008-11-07 04:55:02 +0000
+@@ -19,5 +19,5 @@
+     restFiles = [os.path.join(d[0], f) for d in os.walk(".")
+                  for f in d[2] if f.endswith(".rst")]
+     for r in restFiles:
+-        print r
++        print(r)
+ 
+
+=== modified file 'DeveloperGuide.rst'
+--- DeveloperGuide.rst	2008-11-07 00:21:35 +0000
++++ DeveloperGuide.rst	2008-11-07 04:55:02 +0000
+@@ -46,6 +46,14 @@
+ 
+     ``$ hg pull``
+ 
++    Once you update, run
++
++    ``$ python setup.py install``
++
++    We may talk about minimum version numbers to process the book. Check your version with:
++
++    ``$ hg identify -n``
++
+ The full anouncement from Georg (Sphinx creator) is here:
+ 
+     http://groups.google.com/group/sphinx-dev/browse_thread/thread/6dd415847e5cbf7c
+@@ -107,6 +115,7 @@
+ Working with Launchpad and Bazaar
+ ===============================================================================
+ 
++<<<<<<< TREE
+ (Adapted from a posting by Yarko T)
+ 
+ This assumes that you have created a local branch on your private machine where
+@@ -163,4 +172,22 @@
+     it will be merged into the trunk.
+ 
+ 
++=======
++Register a branch on http://code.launchpad.net/python3patterns/.  Since your login
++will identify your branch, ``devel`` is a good choice for a general development branch.
++If you're unsure, select ``Hosted`` branching.
++
++You must submit a public key - this will get you there: 
++
++    https://launchpad.net/people/+me/+editsshkeys
++
++When you create your branch, Launchpad will show you the command to push to your branch.
++It will look something like this.
++
++.. image:: _images/pushPath.*
++
++