[Zodb-checkins] CVS: StandaloneZODB/Doc/guide - .cvsignore:1.2 Makefile:1.2 README:1.2 TODO:1.2 admin.tex:1.2 chatter.py:1.2 gfdl.tex:1.2 indexing.tex:1.2 introduction.tex:1.2 links.tex:1.2 modules.tex:1.2 prog-zodb.tex:1.2 storages.tex:1.2 transactions.tex:1.2 zeo.tex:1.2 zodb.dvi:1.2 zodb.tex:1.2

Guido van Rossum guido@python.org
Mon, 11 Feb 2002 18:34:11 -0500


Update of /cvs-repository/StandaloneZODB/Doc/guide
In directory cvs.zope.org:/tmp/cvs-serv18412

Added Files:
	.cvsignore Makefile README TODO admin.tex chatter.py gfdl.tex 
	indexing.tex introduction.tex links.tex modules.tex 
	prog-zodb.tex storages.tex transactions.tex zeo.tex zodb.dvi 
	zodb.tex 
Log Message:
Adding AMK's guide (merge)

=== StandaloneZODB/Doc/guide/.cvsignore 1.1 => 1.2 ===


=== StandaloneZODB/Doc/guide/Makefile 1.1 => 1.2 ===
+PYTHONSRC=$$HOME/projects/python
+
+TEX_FILES = gfdl.tex introduction.tex  modules.tex  prog-zodb.tex  \
+            storages.tex  transactions.tex  zeo.tex  zodb.tex 
+
+MKHOWTO=$(PYTHONSRC)/Doc/tools/mkhowto
+
+zodb.dvi: $(TEX_FILES)
+	$(MKHOWTO) --dvi zodb.tex
+
+html:
+	-rm -rf zodb
+	$(MKHOWTO) --html --iconserver=/python/writing/icons zodb.tex
+#	rm -f /home/amk/www/zodb/guide/*
+#	cp -p zodb/* /home/amk/www/zodb/guide/
+
+
+


=== StandaloneZODB/Doc/guide/README 1.1 => 1.2 ===
+and ZEO.  It is taken from Andrew's zodb.sf.net project on
+SourceForge.  The SF version should be considered the canonical
+version, so don't make edits here for now.
+
+Eventually, we'll finish merging the two projects and then the version
+here will be the canonical one.  For now, we have to manually keep the
+two directories in sync.


=== StandaloneZODB/Doc/guide/TODO 1.1 => 1.2 ===
+Write section on __setstate__
+Connection.sync seems to work now; note this
+Continue working on it
+Suppress the full GFDL text in the PDF/PS versions
+


=== StandaloneZODB/Doc/guide/admin.tex 1.1 => 1.2 ===
+%  Administration
+%    Importing and exporting data
+%    Disaster recovery/avoidance
+%    Security
+


=== StandaloneZODB/Doc/guide/chatter.py 1.1 => 1.2 ===
+import sys, time, os, random
+
+from ZEO import ClientStorage
+import ZODB
+from ZODB.POSException import ConflictError
+from Persistence import Persistent
+import BTree
+
+class ChatSession(Persistent):
+
+    """Class for a chat session.
+    Messages are stored in a B-tree, indexed by the time the message
+    was created.  (Eventually we'd want to throw messages out,
+
+    add_message(message) -- add a message to the channel
+    new_messages()       -- return new messages since the last call to
+                            this method
+    
+    
+    """
+
+    def __init__(self, name):
+        """Initialize new chat session.
+        name -- the channel's name
+        """
+
+        self.name = name
+
+        # Internal attribute: _messages holds all the chat messages.        
+        self._messages = BTree.BTree()
+        
+
+    def new_messages(self):
+        "Return new messages."
+
+        # self._v_last_time is the time of the most recent message
+        # returned to the user of this class. 
+        if not hasattr(self, '_v_last_time'):
+            self._v_last_time = 0
+
+        new = []
+        T = self._v_last_time
+
+        for T2, message in self._messages.items():
+            if T2 > T:
+                new.append( message )
+                self._v_last_time = T2
+
+        return new
+    
+    def add_message(self, message):
+        """Add a message to the channel.
+        message -- text of the message to be added
+        """
+
+        while 1:
+            try:
+                now = time.time()
+                self._messages[ now ] = message
+                get_transaction().commit()
+            except ConflictError:
+                # Conflict occurred; this process should pause and
+                # wait for a little bit, then try again.
+                time.sleep(.2)
+                pass
+            else:
+                # No ConflictError exception raised, so break
+                # out of the enclosing while loop.
+                break
+        # end while
+
+def get_chat_session(conn, channelname):
+    """Return the chat session for a given channel, creating the session
+    if required."""
+
+    # We'll keep a B-tree of sessions, mapping channel names to
+    # session objects.  The B-tree is stored at the ZODB's root under
+    # the key 'chat_sessions'.
+    root = conn.root()
+    if not root.has_key('chat_sessions'):
+        print 'Creating chat_sessions B-tree'
+        root['chat_sessions'] = BTree.BTree()
+        get_transaction().commit()
+        
+    sessions = root['chat_sessions']
+
+    # Get a session object corresponding to the channel name, creating
+    # it if necessary.
+    if not sessions.has_key( channelname ):
+        print 'Creating new session:', channelname
+        sessions[ channelname ] = ChatSession(channelname)
+        get_transaction().commit()
+
+    session = sessions[ channelname ]
+    return session
+    
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print 'Usage: %s <channelname>' % sys.argv[0]
+        sys.exit(0)
+
+    storage = ClientStorage.ClientStorage( ('localhost', 9672) )
+    db = ZODB.DB( storage )
+    conn = db.open()
+
+    s = session = get_chat_session(conn, sys.argv[1])
+
+    messages = ['Hi.', 'Hello', 'Me too', "I'M 3L33T!!!!"]
+
+    while 1:
+        # Send a random message
+        msg = random.choice(messages)
+        session.add_message( '%s: pid %i' % (msg,os.getpid() ))
+
+        # Display new messages
+        for msg in session.new_messages():
+            print msg
+
+        # Wait for a few seconds
+        pause = random.randint( 1, 4 ) 
+        time.sleep( pause )
+        


=== StandaloneZODB/Doc/guide/gfdl.tex 1.1 => 1.2 ===
+% This file is a chapter.  It must be included in a larger document to work
+% properly.
+
+\section{GNU Free Documentation License}
+
+Version 1.1, March 2000\\
+
+ Copyright $\copyright$ 2000  Free Software Foundation, Inc.\\
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\\
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+\subsection*{Preamble}
+
+The purpose of this License is to make a manual, textbook, or other
+written document ``free'' in the sense of freedom: to assure everyone
+the effective freedom to copy and redistribute it, with or without
+modifying it, either commercially or noncommercially.  Secondarily,
+this License preserves for the author and publisher a way to get
+credit for their work, while not being considered responsible for
+modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense.  It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does.  But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book.  We recommend this License
+principally for works whose purpose is instruction or reference.
+
+\subsection{Applicability and Definitions}
+
+This License applies to any manual or other work that contains a
+notice placed by the copyright holder saying it can be distributed
+under the terms of this License.  The ``Document'', below, refers to any
+such manual or work.  Any member of the public is a licensee, and is
+addressed as ``you''.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject.  (For example, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.)  The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, whose contents can be viewed and edited directly and
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters.  A copy made in an otherwise Transparent file
+format whose markup has been designed to thwart or discourage
+subsequent modification by readers is not Transparent.  A copy that is
+not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, \LaTeX~input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML designed for human modification.  Opaque formats include
+PostScript, PDF, proprietary formats that can be read and edited only
+by proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML produced by some word processors for output
+purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page.  For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+
+\subsection{Verbatim Copying}
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License.  You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute.  However, you may accept
+compensation in exchange for copies.  If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+\subsection{Copying in Quantity}
+
+If you publish printed copies of the Document numbering more than 100,
+and the Document's license notice requires Cover Texts, you must enclose
+the copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover.  Both covers must also clearly and legibly identify
+you as the publisher of these copies.  The front cover must present
+the full title with all words of the title equally prominent and
+visible.  You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a publicly-accessible computer-network location containing a complete
+Transparent copy of the Document, free of added material, which the
+general network-using public has access to download anonymously at no
+charge using public-standard network protocols.  If you use the latter
+option, you must take reasonably prudent steps, when you begin
+distribution of Opaque copies in quantity, to ensure that this
+Transparent copy will remain thus accessible at the stated location
+until at least one year after the last time you distribute an Opaque
+copy (directly or through your agents or retailers) of that edition to
+the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+\subsection{Modifications}
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it.  In addition, you must do these things in the Modified Version:
+
+\begin{itemize}
+
+\item Use in the Title Page (and on the covers, if any) a title distinct
+   from that of the Document, and from those of previous versions
+   (which should, if there were any, be listed in the History section
+   of the Document).  You may use the same title as a previous version
+   if the original publisher of that version gives permission.
+\item List on the Title Page, as authors, one or more persons or entities
+   responsible for authorship of the modifications in the Modified
+   Version, together with at least five of the principal authors of the
+   Document (all of its principal authors, if it has less than five).
+\item State on the Title page the name of the publisher of the
+   Modified Version, as the publisher.
+\item Preserve all the copyright notices of the Document.
+\item Add an appropriate copyright notice for your modifications
+   adjacent to the other copyright notices.
+\item Include, immediately after the copyright notices, a license notice
+   giving the public permission to use the Modified Version under the
+   terms of this License, in the form shown in the Addendum below.
+\item Preserve in that license notice the full lists of Invariant Sections
+   and required Cover Texts given in the Document's license notice.
+\item Include an unaltered copy of this License.
+\item Preserve the section entitled ``History'', and its title, and add to
+   it an item stating at least the title, year, new authors, and
+   publisher of the Modified Version as given on the Title Page.  If
+   there is no section entitled ``History'' in the Document, create one
+   stating the title, year, authors, and publisher of the Document as
+   given on its Title Page, then add an item describing the Modified
+   Version as stated in the previous sentence.
+\item Preserve the network location, if any, given in the Document for
+   public access to a Transparent copy of the Document, and likewise
+   the network locations given in the Document for previous versions
+   it was based on.  These may be placed in the ``History'' section.
+   You may omit a network location for a work that was published at
+   least four years before the Document itself, or if the original
+   publisher of the version it refers to gives permission.
+\item In any section entitled ``Acknowledgements'' or ``Dedications'',
+   preserve the section's title, and preserve in the section all the
+   substance and tone of each of the contributor acknowledgements
+   and/or dedications given therein.
+\item Preserve all the Invariant Sections of the Document,
+   unaltered in their text and in their titles.  Section numbers
+   or the equivalent are not considered part of the section titles.
+\item Delete any section entitled ``Endorsements''.  Such a section
+   may not be included in the Modified Version.
+\item Do not retitle any existing section as ``Endorsements''
+   or to conflict in title with any Invariant Section.
+
+\end{itemize}
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant.  To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties -- for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version.  Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity.  If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+\subsection{Combining Documents}
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy.  If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections entitled ``History''
+in the various original documents, forming one section entitled
+``History''; likewise combine any sections entitled ``Acknowledgements'',
+and any sections entitled ``Dedications''.  You must delete all sections
+entitled ``Endorsements.''
+
+
+\subsection{Collections of Documents}
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+
+\subsection{Aggregation With Independent Works}
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, does not as a whole count as a Modified Version
+of the Document, provided no compilation copyright is claimed for the
+compilation.  Such a compilation is called an ``aggregate'', and this
+License does not apply to the other self-contained works thus compiled
+with the Document, on account of their being thus compiled, if they
+are not themselves derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one quarter
+of the entire aggregate, the Document's Cover Texts may be placed on
+covers that surround only the Document within the aggregate.
+Otherwise they must appear on covers around the whole aggregate.
+
+
+\subsection{Translation}
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections.  You may include a
+translation of this License provided that you also include the
+original English version of this License.  In case of a disagreement
+between the translation and the original English version of this
+License, the original English version will prevail.
+
+
+\subsection{Termination}
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License.  Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License.  However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+\subsection{Future Revisions of This Licence}
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+\url{http://www.gnu.org/copyleft/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation.  If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+\subsection*{ADDENDUM: How to use this License for your documents}
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+\begin{quote}
+
+      Copyright $\copyright$  YEAR  YOUR NAME.
+      Permission is granted to copy, distribute and/or modify this document
+      under the terms of the GNU Free Documentation License, Version 1.1
+      or any later version published by the Free Software Foundation;
+      with the Invariant Sections being LIST THEIR TITLES, with the
+      Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+      A copy of the license is included in the section entitled ``GNU
+      Free Documentation License''.
+
+\end{quote}
+
+If you have no Invariant Sections, write ``with no Invariant Sections''
+instead of saying which ones are invariant.  If you have no
+Front-Cover Texts, write ``no Front-Cover Texts'' instead of
+``Front-Cover Texts being LIST''; likewise for Back-Cover Texts.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+


=== StandaloneZODB/Doc/guide/indexing.tex 1.1 => 1.2 ===
+% Indexing Data
+%    BTrees
+%    Full-text indexing
+


=== StandaloneZODB/Doc/guide/introduction.tex 1.1 => 1.2 ===
+%Introduction
+%   What is ZODB?
+%   What is ZEO?
+%   OODBs vs. Relational DBs
+%   Other OODBs
+
+\section{Introduction}
+
+This guide explains how to write Python programs that use the Z Object
+Database (ZODB) and Zope Enterprise Objects (ZEO).  The latest version
+of the guide is always available at
+\url{http://www.amk.ca/zodb/guide/}.
+
+\subsection{What is the ZODB?}
+
+The ZODB is a persistence system for Python objects.  Persistent
+programming languages provide facilities that automatically write
+objects to disk and read them in again when they're required by a
+running program.  By installing the ZODB, you add such facilities to
+Python.
+
+It's certainly possible to build your own system for making Python
+objects persistent.  The usual starting points are the \module{pickle}
+module, for converting objects into a string representation, and
+various database modules, such as the \module{gdbm} or \module{bsddb}
+modules, that provide ways to write strings to disk and read them
+back.  It's straightforward to combine the \module{pickle} module and
+a database module to store and retrieve objects, and in fact the
+\module{shelve} module, included in Python's standard library, does
+this.
+
+The downside is that the programmer has to explicitly manage objects,
+reading an object when it's needed and writing it out to disk when the
+object is no longer required.  The ZODB manages objects for you,
+keeping them in a cache and writing them out if they haven't been 
+accessed in a while.  
+
+
+\subsection{OODBs vs. Relational DBs}
+
+Another way to look at it is that the ZODB is a Python-specific
+object-oriented database (OODB).  Commercial object databases for C++
+or Java often require that you jump through some hoops, such as using
+a special preprocessor or avoiding certain data types.  As we'll see,
+the ZODB has some hoops of its own to jump through, but in comparison
+the naturalness of the ZODB is astonishing.
+
+Relational databases (RDBs) are far more common than OODBs.
+Relational databases store information in tables; a table consists of
+any number of rows, each row containing several columns of
+information.  (Rows are more formally called relations, which is where
+the term ``relational database'' originates.)
+
+Let's look at a concrete example.  The example comes from my day job
+working for the MEMS Exchange, in a greatly simplified version.  The
+job is to track process runs, which are lists of manufacturing steps
+to be performed in a semiconductor fab.  A run is owned by a
+particular user, and has a name and assigned ID number.  Runs consist
+of a number of operations; an operation is a single step to be
+performed, such as depositing something on a wafer or etching
+something off it.
+
+Operations may have parameters, which are additional information
+required to perform an operation.  For example, if you're depositing
+something on a wafer, you need to know two things: 1) what you're
+depositing, and 2) how much should be deposited.  You might deposit
+100 microns of silicon oxide, or 1 micron of copper.
+
+Mapping these structures to a relational database is straightforward:
+
+\begin{verbatim}
+CREATE TABLE runs (
+  int      run_id,
+  varchar  owner,
+  varchar  title,
+  int      acct_num,
+  primary key(run_id)
+);
+
+CREATE TABLE operations (
+  int      run_id,
+  int      step_num, 
+  varchar  process_id,
+  PRIMARY KEY(run_id, step_num),
+  FOREIGN KEY(run_id) REFERENCES runs(run_id),
+);
+
+CREATE TABLE parameters (
+  int      run_id,
+  int      step_num, 
+  varchar  param_name, 
+  varchar  param_value,
+  PRIMARY KEY(run_id, step_num, param_name)
+  FOREIGN KEY(run_id, step_num) 
+     REFERENCES operations(run_id, step_num),
+);  
+\end{verbatim}
+
+In Python, you would write three classes named \class{Run},
+\class{Operation}, and \class{Parameter}.  I won't present code for
+defining these classes, since that code is uninteresting at this
+point. Each class would contain a single method to begin with, an
+\method{__init__} method that assigns default values, such as 0 or
+\code{None}, to each attribute of the class.
+
+It's not difficult to write Python code that will create a \class{Run}
+instance and populate it with the data from the relational tables;
+with a little more effort, you can build a straightforward tool,
+usually called an object-relational mapper, to do this automatically.
+(See
+\url{http://www.amk.ca/python/unmaintained/ordb.html} for a quick hack
+at a Python object-relational mapper, and
+\url{http://www.python.org/workshops/1997-10/proceedings/shprentz.html}
+for Joel Shprentz's more successful implementation of the same idea;
+Unlike mine, Shprentz's system has been used for actual work.)
+
+However, it is difficult to make an object-relational mapper
+reasonably quick; a simple-minded implementation like mine is quite
+slow because it has to do several queries to access all of an object's
+data.  Higher performance object-relational mappers cache objects to
+improve performance, only performing SQL queries when they actually
+need to.
+
+That helps if you want to access run number 123 all of a sudden.  But
+what if you want to find all runs where a step has a parameter named
+'thickness' with a value of 2.0?  In the relational version, you have
+two unappealing choices:
+
+\begin{enumerate}
+ \item Write a specialized SQL query for this case: \code{SELECT run_id
+  FROM operations WHERE param_name = 'thickness' AND param_value = 2.0}
+  
+  If such queries are common, you can end up with lots of specialized
+  queries.  When the database tables get rearranged, all these queries
+  will need to be modified.
+
+ \item An object-relational mapper doesn't help much.  Scanning
+  through the runs means that the the mapper will perform the required
+  SQL queries to read run \#1, and then a simple Python loop can check
+  whether any of its steps have the parameter you're looking for.
+  Repeat for run \#2, 3, and so forth.  This does a vast
+  number of SQL queries, and therefore is incredibly slow.
+
+\end{enumerate}
+
+An object database such as ZODB simply stores internal pointers from
+object to object, so reading in a single object is much faster than
+doing a bunch of SQL queries and assembling the results. Scanning all
+runs, therefore, is still inefficient, but not grossly inefficient.
+
+\subsection{What is ZEO?}
+
+The ZODB comes with a few different classes that implement the
+\class{Storage} interface.  Such classes handle the job of
+writing out Python objects to a physical storage medium, which can be
+a disk file (the \class{FileStorage} class), a BerkeleyDB file
+(\class{BerkeleyStorage}), a relational database
+(\class{DCOracleStorage}), or some other medium.  ZEO adds
+\class{ClientStorage}, a new \class{Storage} that doesn't write to
+physical media but just forwards all requests across a network to a
+server.  The server, which is running an instance of the
+\class{StorageServer} class, simply acts as a front-end for some
+physical \class{Storage} class.  It's a fairly simple idea, but as
+we'll see later on in this document, it opens up many possibilities.
+
+\subsection{About this guide}
+
+The primary author of this guide works on a project which uses the
+ZODB and ZEO as its primary storage technology.  We use the ZODB to
+store process runs and operations, a catalog of available processes,
+user information, accounting information, and other data.  Part of the
+goal of writing this document is to make our experience more widely
+available.  A few times we've spent hours or even days trying to
+figure out a problem, and this guide is an attempt to gather up the
+knowledge we've gained so that others don't have to make the same
+mistakes we did while learning.
+
+This document will always be a work in progress.  If you wish to
+suggest clarifications or additional topics, please send your comments to
+\email{akuchlin@mems-exchange.org}.  
+
+\subsection{Acknowledgements}
+
+I'd like to thank the people who've pointed out inaccuracies and bugs,
+offered suggestions on the text, or proposed new topics that should be
+covered: Jeff Bauer, Willem Broekema, Thomas Guettler,
+Chris McDonough, George Runyan.


=== StandaloneZODB/Doc/guide/links.tex 1.1 => 1.2 ===
+% Collection of relevant links
+
+\section{Resources}
+
+ZODB HOWTO, by Michel Pelletier:
+\\
+Goes into slightly more detail about the rules for writing applications using the ZODB.
+\\
+\url{http://www.zope.org/Members/michel/HowTos/ZODB-How-To}
+
+
+Introduction to the Zope Object Database, by Jim Fulton:
+\\
+Goes into much greater detail, explaining advanced uses of the ZODB and 
+how it's actually implemented.  A definitive reference, and highly recommended.
+\\
+\url{http://www.python.org/workshops/2000-01/proceedings/papers/fulton/zodb3.html}
+
+Download link for ZEO: \\
+\url{http://www.zope.org/Products/ZEO/}
+
+


=== StandaloneZODB/Doc/guide/modules.tex 1.1 => 1.2 ===
+% Related Modules
+%    PersistentMapping
+%    PersistentList
+%    BTree
+%    Catalog
+
+\section{Related Modules}
+
+The ZODB package includes a number of related modules that provide
+useful data types such as BTrees or full-text indexes.
+
+\subsection{\module{ZODB.PersistentMapping}}
+
+The \class{PersistentMapping} class is a wrapper for mapping objects
+that will set the dirty bit when the mapping is modified by setting or
+deleting a key.  
+
+\begin{funcdesc}{PersistentMapping}{container = \{\}}
+Create a \class{PersistentMapping} object that wraps the 
+mapping object \var{container}.  If you don't specify a
+value for \var{container}, a regular Python dictionary is used.
+\end{funcdesc}
+
+\class{PersistentMapping} objects support all the same methods as 
+Python dictionaries do.
+
+\subsection{\module{ZODB.PersistentList}}
+
+The \class{PersistentList} class is a wrapper for mutable sequence objects, 
+much as \class{PersistentMapping} is a wrapper for mappings.  
+
+\begin{funcdesc}{PersistentList}{initlist = []}
+Create a \class{PersistentList} object that wraps the 
+mutable sequence object \var{initlist}.  If you don't specify a
+value for \var{initlist}, a regular Python list is used.
+\end{funcdesc}
+
+\class{PersistentList} objects support all the same methods as 
+Python lists do.
+
+
+\subsection{B-tree Modules}
+
+%here's one: how does one implement searching? i would have expected the
+%btree objects to have ``find key nearest to this'' and ``next'' methods,
+%(like bsddb's set_location)...
+%
+%  -- erno
+
+When programming with the ZODB, Python dictionaries aren't always what
+you need.  The most important case is where you want to store a very
+large mapping.  When a Python dictionary is accessed in a ZODB, the
+whole dictionary has to be unpickled and brought into memory.  If
+you're storing something very large, such as a 100,000-entry user
+database, unpickling such a large object will be slow.  B-trees are a
+balanced tree data structure that behave like a mapping but distribute
+keys throughout a number of tree nodes.  Nodes are then only unpickled
+and brought into memory as they're accessed, so the entire tree
+doesn't have to occupy memory (unless you really are touching every
+single key).
+
+There are four different BTree modules provided.  One of them, the
+\module{BTree} module, provides the most general data type; the keys
+and values in the B-tree can be any Python object.  Some specialized B-tree
+modules require that the keys, and perhaps even the values, to be of a
+certain type, and provide faster performance because of this limitation.
+
+\begin{itemize}
+\item[ \module{IOBTree} ] requires the keys to be integers.
+The module name reminds you of this; the \module{IOBTree} module
+maps Integers to Objects.
+
+\item[ \module{OIBTree} ] requires the values to be integers,
+mapping Objects to Integers.
+
+\item[ \module{IIBTree} ] is strictest, requiring that both keys and values must be integers.
+
+\end{itemize}
+
+To use a B-tree, simply import the desired module and call the
+constructor, always named \function{BTree()}, to get a B-tree
+instance, and then use it like any other mapping:
+
+\begin{verbatim}
+import IIBTree
+iimap = IIBTree.BTree()
+iimap[1972] = 27
+\end{verbatim}
+


=== StandaloneZODB/Doc/guide/prog-zodb.tex 1.1 => 1.2 === (464/564 lines abridged)
+%ZODB Programming
+%   How ZODB works (ExtensionClass, dirty bits)
+%   Installing ZODB
+%   Rules for Writing Persistent Classes
+   
+
+\section{ZODB Programming}
+
+\subsection{Installing ZODB}
+
+The ZODB forms part of Zope, but it's difficult and somewhat painful
+to extract the bits from Zope needed to support just the ZODB.
+Therefore I've assembled a distribution containing only the packages
+required to use the ZODB and ZEO, so you can install it and start
+programming right away.
+
+To download the distribution, go to my ZODB page at 
+\url{http://www.amk.ca/zodb/}.  
+The distribution is still experimental, so don't be surprised if the
+installation process runs into problems.  Please inform me of any
+difficulties you encounter.
+
+\subsubsection{Requirements}
+
+You'll need Python, of course; version 1.5.2 works with some fixes,
+and it also works with Python 2.0, which is what I primarily use.
+
+The code is packaged using Distutils, the new distribution tools for
+Python introduced in Python 2.0.  If you're using 1.5.2, first you'll
+have to get the latest Distutils release from the Distutils SIG page
+at \url{http://www.python.org/sigs/distutils-sig/download.html} and
+install it.  This is simply a matter of untarring or unzipping the
+release package, and then running \code{python setup.py install}.
+
+If you're using 1.5.2 and have installed previous versions of the
+Distutils, be sure to get the very latest version, since developing
+the ZODB distribution turned up some bugs along the way.  If you
+encounter problems compiling \file{ZODB/TimeStamp.c} or your compiler reports
+an error like ``Can't create build/temp.linux2/ExtensionClass.o: No
+such file or directory'', you need an updated version.  Old versions of
+Distutils have two bugs which affect the setup scripts.  First, for a
+long time the \code{define_macros} keyword in setup.py files didn't work due
+to a Distutils bug, so I hacked TimeStamp.c in earlier releases.  The
+Distutils have since been fixed, and the hack became unnecessary, so I
+removed it.  Second, the code that creates directories tries to be
+smart and caches them to save time by not trying to create a directory
+twice, but this code was broken in old versions.
+
+You'll need a C compiler to build the packages, because there are
+various C extension modules.  At the moment no one is making Windows

[-=- -=- -=- 464 lines omitted -=- -=- -=-]

+automatically detecting changes to the object is disabled while the
+\method{__getattr__}, \method{__delattr__}, or \method{__setattr__}
+method is executing.  This means that if the object is modified, the
+object should be marked as dirty by setting the object's
+\member{_p_changed} method to true.
+
+\subsection{Writing Persistent Classes}
+
+Now that we've looked at the basics of programming using the ZODB,
+we'll turn to some more subtle tasks that are likely to come up for
+anyone using the ZODB in a production system.
+
+\subsubsection{Changing Instance Attributes}
+
+Ideally, before making a class persistent you would get its interface
+right the first time, so that no attributes would ever need to be
+added, removed, or have their interpretation change over time.  It's a
+worthy goal, but also an impractical one unless you're gifted with
+perfect knowledge of the future.  Such unnatural foresight can't be
+required of any person, so you therefore have to be prepared to handle
+such structural changes gracefully.  In object-oriented database
+terminology, this is a schema update.  The ZODB doesn't have an actual
+schema specification, but you're changing the software's expectations
+of the data contained by an object, so you're implicitly changing the
+schema.
+
+One way to handle such a change is to write a one-time conversion
+program that will loop over every single object in the database and
+update them to match the new schema.  This can be easy if your network
+of object references is quite structured, making it easy to find all
+the instances of the class being modified.  For example, if all
+\class{User} objects can be found inside a single dictionary or
+B-tree, then it would be a simple matter to loop over every
+\class{User} instance with a \keyword{for} statement.
+This is more difficult if your object graph is less structured; if
+\class{User} objects can be found as attributes of any number of
+different class instances, then there's no longer any easy way to find
+them all, short of writing a generalized object traversal function
+that would walk over every single object in a ZODB, checking each one
+to see if it's an instance of \class{User}.  
+\footnote{XXX is there a convenience method for walking the object graph hiding
+somewhere inside DC's code?  Should there be a utility method for
+doing this?  Should I write one and include it in this section?}
+Some OODBs support a feature called extents, which allow quickly
+finding all the instances of a given class, no matter where they are
+in the object graph; unfortunately the ZODB doesn't offer extents as a
+feature.
+
+XXX Rest of section not written yet: __getstate__/__setstate__
+


=== StandaloneZODB/Doc/guide/storages.tex 1.1 => 1.2 ===
+% Storages
+%    FileStorage
+%    BerkeleyStorage
+%    OracleStorage
+
+\section{Storages}
+
+This chapter will examine the different \class{Storage} subclasses
+that are considered stable, discuss their varying characteristics, and
+explain how to administer them.
+
+\subsection{Using Multiple Storages}
+
+XXX explain mounting substorages
+
+\subsection{FileStorage}
+
+\subsection{BerkeleyStorage}
+
+\subsection{OracleStorage}
+


=== StandaloneZODB/Doc/guide/transactions.tex 1.1 => 1.2 ===
+%   Committing and Aborting
+%   Subtransactions
+%   Undoing
+%   Versions
+%   Multithreaded ZODB Programs
+   
+
+\section{Transactions and Versioning}
+
+%\subsection{Committing and Aborting}
+% XXX There seems very little to say about commit/abort...
+
+\subsection{Subtransactions}
+
+Subtransactions can be created within a transaction.  Each
+subtransaction can be individually committed and aborted, but the
+changes within a subtransaction are not truly committed until the
+containing transaction is committed.
+
+The primary purpose of subtransactions is to decrease the memory usage
+of transactions that touch a very large number of objects.  Consider a
+transaction during which 200,000 objects are modified.  All the
+objects that are modified in a single transaction have to remain in
+memory until the transaction is committed, because the ZODB can't
+discard them from the object cache.  This can potentially make the
+memory usage quite large.  With subtransactions, a commit can be be
+performed at intervals, say, every 10,000 objects.  Those 10,000
+objects are then written to permanent storage and can be purged from
+the cache to free more space.
+
+To commit a subtransaction instead of a full transaction, 
+pass a true value to the \method{commit()}
+or \method{abort()} method of the \class{Transaction} object.
+
+\begin{verbatim}
+# Commit a subtransaction
+get_transaction().commit(1)	
+
+# Abort a subtransaction
+get_transaction().abort(1)   
+\end{verbatim}
+
+A new subtransaction is automatically started on committing or
+aborting the previous subtransaction.
+
+
+\subsection{Undoing Changes}
+
+Some types of \class{Storage} support undoing a transaction even after
+it's been committed.  You can tell if this is the case by calling the
+\method{supportsUndo()} method of the \class{DB} instance, which
+returns true if the underlying storage supports undo.  Alternatively
+you can call the \method{supportsUndo()} method on the underlying
+storage instance.
+
+If a database supports undo, then the \method{undoLog(\var{start},
+\var{end}\optional{, func})} method on the \class{DB} instance returns
+the log of past transactions, returning transactions between the times
+\var{start} and \var{end}, measured in seconds from the epoch.   
+If present, \var{func} is a function that acts as a filter on the
+transactions to be returned; it's passed a dictionary representing
+each transaction, and only transactions for which \var{func} returns
+true will be included in the list of transactions returned to the
+caller of \method{undoLog()}.  The dictionary contains keys for
+various properties of the transaction.  The most important keys are
+\samp{id}, for the transaction ID, and \samp{time}, for the time at
+which the transaction was committed.  
+
+\begin{verbatim}
+>>> print storage.undoLog(0, sys.maxint)
+[{'description': '',
+  'id': 'AzpGEGqU/0QAAAAAAAAGMA',
+  'time': 981126744.98,
+  'user_name': ''},
+ {'description': '',
+  'id': 'AzpGC/hUOKoAAAAAAAAFDQ',
+  'time': 981126478.202,
+  'user_name': ''}
+  ...
+\end{verbatim}
+
+To store a description and a user name on a commit, get the current
+transaction and call the \method{note(\var{text})} method to store a
+description, and the
+\method{setUser(\var{user_name})} method to store the user name.  
+While \method{setUser()} overwrites the current user name and replaces
+it with the new value, the \method{note()} method always adds the text
+to the transaction's description, so it can be called several times to
+log several different changes made in the course of a single
+transaction.
+
+\begin{verbatim}
+get_transaction().setUser('amk')
+get_transaction().note('Change ownership')
+\end{verbatim}
+
+To undo a transaction, call the \method{DB.undo(\var{id})} method,
+passing it the ID of the transaction to undo.  If the transaction
+can't be undone, a \exception{ZODB.POSException.UndoError} exception
+will be raised, with the message ``non-undoable
+transaction''.  Usually this will happen because later transactions
+modified the objects affected by the transaction you're trying to
+undo.
+
+\subsection{Versions}
+
+While many subtransactions can be contained within a single regular
+transaction, it's also possible to contain many regular transactions
+within a long-running transaction, called a version in ZODB
+terminology.  Inside a version, any number of transactions can be
+created and committed or rolled back, but the changes within a version
+are not made visible to other connections to the same ZODB.
+
+Not all storages support versions, but you can test for versioning
+ability by calling \method{supportsVersions()} method of the
+\class{DB} instance, which returns true if the underlying storage
+supports versioning.
+
+A version can be selected when creating the \class{Connection}
+instance using the \method{DB.open(\optional{\var{version}})} method.
+The \var{version} argument must be a string that will be used as the
+name of the version.
+
+\begin{verbatim}
+vers_conn = db.open(version='Working version')
+\end{verbatim}
+
+Transactions can then be committed and aborted using this versioned
+connection.  Other connections that don't specify a version, or
+provide a different version name, will not see changes committed
+within the version named \samp{Working~version}.  To commit or abort a
+version, which will either make the changes visible to all clients or
+roll them back, call the \method{DB.commitVersion()} or
+\method{DB.abortVersion()} methods.
+XXX what are the source and dest arguments for?
+
+The ZODB makes no attempt to reconcile changes between different
+versions.  Instead, the first version which modifies an object will
+gain a lock on that object.  Attempting to modify the object from a
+different version or from an unversioned connection will cause a
+\exception{ZODB.POSException.VersionLockError} to be raised:
+
+\begin{verbatim}
+from ZODB.POSException import VersionLockError
+
+try:
+    get_transaction().commit()
+except VersionLockError, (obj_id, version):
+    print ('Cannot commit; object %s '
+           'locked by version %s' % (obj_id, version))
+\end{verbatim}
+
+The exception provides the ID of the locked object, and the name of
+the version having a lock on it.
+
+\subsection{Multithreaded ZODB Programs}
+
+ZODB databases can be accessed from multithreaded Python programs.
+The \class{Storage} and \class{DB} instances can be shared among
+several threads, as long as individual \class{Connection} instances
+are created for each thread.  
+
+XXX I can't think of anything else to say about multithreaded ZODB
+programs.  Suggestions?  An example program?
+


=== StandaloneZODB/Doc/guide/zeo.tex 1.1 => 1.2 ===
+% ZEO
+%    Installing ZEO
+%    How ZEO works (ClientStorage)
+%    Configuring ZEO
+   
+\section{ZEO}
+\label{zeo}
+
+\subsection{How ZEO Works}
+
+The ZODB, as I've described it so far, can only be used within a
+single Python process (though perhaps with multiple threads).  ZEO,
+Zope Enterprise Objects, extends the ZODB machinery to provide access
+to objects over a network.  The name "Zope Enterprise Objects" is a
+bit misleading; ZEO can be used to store Python objects and access
+them in a distributed fashion without Zope ever entering the picture.
+The combination of ZEO and ZODB is essentially a Python-specific
+object database.
+
+ZEO consists of about 1400 lines of Python code.  The code is
+relatively small because it contains only code for a TCP/IP server,
+and for a new type of Storage, \class{ClientStorage}.
+\class{ClientStorage} doesn't use disk files at all; it simply
+makes remote procedure calls to the server, which then passes them on
+a regular \class{Storage} class such as \class{FileStorage}.  The
+following diagram lays out the system:
+
+XXX insert diagram here later
+
+Any number of processes can create a \class{ClientStorage}
+instance, and any number of threads in each process can be using that
+instance.  \class{ClientStorage} aggressively caches objects
+locally, so in order to avoid using stale data, the ZEO server sends
+an invalidate message to all the connected \class{ClientStorage}
+instances on every write operation.  The invalidate message contains
+the object ID for each object that's been modified, letting the
+\class{ClientStorage} instances delete the old data for the
+given object from their caches.
+
+This design decision has some consequences you should be aware of.
+First, while ZEO isn't tied to Zope, it was first written for use with
+Zope, which stores HTML, images, and program code in the database.  As
+a result, reads from the database are \emph{far} more frequent than
+writes, and ZEO is therefore better suited for read-intensive
+applications.  If every \class{ClientStorage} is writing to the
+database all the time, this will result in a storm of invalidate
+messages being sent, and this might take up more processing time than
+the actual database operations themselves.
+
+On the other hand, for applications that have few writes in comparison
+to the number of read accesses, this aggressive caching can be a major
+win.  Consider a Slashdot-like discussion forum that divides the load
+among several Web servers.  If news items and postings are represented
+by objects and accessed through ZEO, then the most heavily accessed
+objects -- the most recent or most popular postings -- will very
+quickly wind up in the caches of the
+\class{ClientStorage} instances on the front-end servers.  The
+back-end ZEO server will do relatively little work, only being called
+upon to return the occasional older posting that's requested, and to
+send the occasional invalidate message when a new posting is added.
+The ZEO server isn't going to be contacted for every single request,
+so its workload will remain manageable.
+
+\subsection{Installing ZEO}
+
+This section covers how to install the ZEO package, and how to 
+configure and run a ZEO Storage Server on a machine. 
+
+\subsubsection{Requirements}
+
+To run a ZEO server, you'll need Python 1.5.2 or 2.0, and the ZODB
+packages from \url{http://www.amk.ca/files/zodb/}
+have to be installed.  
+
+\emph{Note for Python 1.5.2 users}: ZEO requires updated versions
+of the \module{asyncore.py} and \module{asynchat.py} modules that are
+included in 1.5.2's standard library.  Current versions of the ZODB
+distribution install private versions of these modules, so you
+shouldn't need to grab updated versions yourself.  (The symptom of
+this problem is a traceback on attempting to run a ZEO client program:
+the traceback is ``TypeError: too many arguments; expected 2, got 3''
+around line 100 of \file{smac.py}.
+
+\subsubsection{Installation}
+
+Installing the ZEO package is easy.  Just run \code{python setup.py
+install}.  This will install the ZEO/ package into your Python
+installation, and copy various files into their proper locations:
+\file{zeo.conf} will be put into \file{/usr/local/etc/}, a \file{zeo} startup
+script will be put in \file{/etc/rc.d/init.d/}, and the \file{zeod}
+daemon program will be placed in \file{/usr/local/bin}.
+
+\subsection{Configuring and Running a ZEO Server}
+
+Edit \code{/usr/local/etc/zeo.conf} appropriately for your desired
+setup.  This configuration file controls the port on which ZEO will
+listen for connections, the user and group IDs under which the server
+will be executed, and the location of the concrete \class{Storage}
+object that will be made network-accessible.
+ 
+\subsection{Testing the ZEO Installation}
+
+Once a ZEO server is up and running, using it is just like using ZODB
+with a more conventional disk-based storage; no new programming
+details are introduced by using a remote server.  The only difference
+is that programs must create a \class{ClientStorage} instance instead
+of a \class{FileStorage} instance.  From that point onward, ZODB-based
+code is happily unaware that objects are being retrieved from a ZEO
+server, and not from the local disk.
+
+As an example, and to test whether ZEO is working correctly, try
+running the following lines of code, which will connect to the server,
+add some bits of data to the root of the ZODB, and commits the
+transaction:
+
+\begin{verbatim}
+from ZEO import ClientStorage
+from ZODB import DB
+
+# Change next line to connect to your ZEO server
+addr = ('kronos.example.com', 1975)
+storage = ClientStorage.ClientStorage(addr)
+db = DB(storage)
+conn = db.open()
+root = conn.root()
+
+# Store some things in the root
+root['list'] = ['a', 'b', 1.0, 3]
+root['dict'] = {'a':1, 'b':4}
+
+# Commit the transaction
+get_transaction().commit()
+\end{verbatim}
+
+If this code runs properly, then your ZEO server is working correctly.
+
+\subsection{ZEO Programming Notes}
+
+XXX The Connection.sync() method and its necessity (if it works at all!) 
+
+% That doesn't work.  I tested it.  sync() doesn't seem to get into
+% the asyncore loop.  One of us should probably look into adding an
+% API for this when we have some free time.  It would be a nice
+% small project that would get into ZODB's guts.
+
+
+
+
+\subsection{Sample Application: chatter.py}
+
+For an example application, we'll build a little chat application.
+What's interesting is that none of the application's code deals with
+network programming at all; instead, an object will hold chat
+messages, and be magically shared between all the clients through ZEO.
+I won't present the complete script here; it's included in my ZODB
+distribution, and you can download it from
+\url{http://www.amk.ca/zodb/demos/}.  Only the interesting portions of
+the code will be covered here.
+
+The basic data structure is the \class{ChatSession} object,
+which provides an \method{add_message()} method that adds a
+message, and a \method{new_messages()} method that returns a list
+of new messages that have accumulated since the last call to
+\method{new_messages()}.  Internally, \class{ChatSession}
+maintains a B-tree that uses the time as the key, and stores the
+message as the corresponding value.
+
+The constructor for \class{ChatSession} is pretty simple; it simply
+creates an attribute containing a B-tree:
+
+\begin{verbatim}
+class ChatSession(Persistent):
+    def __init__(self, name):
+        self.name = name
+        # Internal attribute: _messages holds all the chat messages.        
+        self._messages = BTree.BTree()        
+\end{verbatim}
+
+\method{add_message()} has to add a message to the
+\code{_messages} B-tree.  A complication is that it's possible
+that some other client is trying to add a message at the same time;
+when this happens, the client that commits first wins, and the second
+client will get a \exception{ConflictError} exception when it tries to
+commit.  For this application, \exception{ConflictError} isn't serious
+but simply means that the operation has to be retried; other
+applications might treat it as a fatal error.  The code uses
+\code{try...except...else} inside a \code{while} loop,
+breaking out of the loop when the commit works without raising an
+exception.
+
+\begin{verbatim}
+    def add_message(self, message):
+        """Add a message to the channel.
+        message -- text of the message to be added
+        """
+
+        while 1:
+            try:
+                now = time.time()
+                self._messages[now] = message
+                get_transaction().commit()
+            except ConflictError:
+                # Conflict occurred; this process should pause and
+                # wait for a little bit, then try again.
+                time.sleep(.2)
+                pass
+            else:
+                # No ConflictError exception raised, so break
+                # out of the enclosing while loop.
+                break
+        # end while
+\end{verbatim}
+
+\method{new_messages()} introduces the use of \textit{volatile}
+attributes.  Attributes of a persistent object that begin with
+\code{_v_} are considered volatile and are never stored in the
+database.  \method{new_messages()} needs to store the last time
+the method was called, but if the time was stored as a regular
+attribute, its value would be committed to the database and shared
+with all the other clients.  \method{new_messages()} would then
+return the new messages accumulated since any other client called
+\method{new_messages()}, which isn't what we want.
+
+\begin{verbatim}
+    def new_messages(self):
+        "Return new messages."
+
+        # self._v_last_time is the time of the most recent message
+        # returned to the user of this class. 
+        if not hasattr(self, '_v_last_time'):
+            self._v_last_time = 0
+
+        new = []
+        T = self._v_last_time
+
+        for T2, message in self._messages.items():
+            if T2 > T:
+                new.append(message)
+                self._v_last_time = T2
+
+        return new
+\end{verbatim}
+
+This application is interesting because it uses ZEO to easily share a
+data structure; ZEO and ZODB are being used for their networking
+ability, not primarily for their data storage ability.  I can foresee
+many interesting applications using ZEO in this way:
+
+\begin{itemize}
+  \item With a Tkinter front-end, and a cleverer, more scalable data
+  structure, you could build a shared whiteboard using the same
+  technique.
+
+  \item A shared chessboard object would make writing a networked chess
+  game easy.  
+
+  \item You could create a Python class containing a CD's title and
+  track information.  To make a CD database, a read-only ZEO server
+  could be opened to the world, or an HTTP or XML-RPC interface could
+  be written on top of the ZODB.
+
+  \item A program like Quicken could use a ZODB on the local disk to
+  store its data.  This avoids the need to write and maintain
+  specialized I/O code that reads in your objects and writes them out;
+  instead you can concentrate on the problem domain, writing objects
+  that represent cheques, stock portfolios, or whatever.
+
+\end{itemize}
+


=== StandaloneZODB/Doc/guide/zodb.dvi 1.1 => 1.2 ===


=== StandaloneZODB/Doc/guide/zodb.tex 1.1 => 1.2 ===
+
+\title{ZODB/ZEO Programming Guide}
+\release{0.03}
+\date{\today}
+
+\author{A.M.\ Kuchling}
+\authoraddress{akuchlin@mems-exchange.org}
+
+\begin{document}
+\maketitle
+\tableofcontents
+
+\copyright{Copyright 2002 A.M. Kuchling.
+      Permission is granted to copy, distribute and/or modify this document
+      under the terms of the GNU Free Documentation License, Version 1.1
+      or any later version published by the Free Software Foundation;
+      with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+      A copy of the license is included in the appendix entitled ``GNU
+      Free Documentation License''.}
+
+\input{introduction}
+\input{prog-zodb}
+\input{zeo}
+\input{transactions}
+%\input{storages}
+%\input{indexing}
+%\input{admin}
+\input{modules}
+
+\appendix
+\input links.tex
+\input gfdl.tex
+
+\end{document}