[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}