[Zodb-checkins] SVN: ZODB/branches/blob-merge-branch/ - Merged to ZODB trunk again

Christian Theune ct at gocept.com
Fri Feb 17 17:01:45 EST 2006


Log message for revision 41650:
   - Merged to ZODB trunk again
  

Changed:
  U   ZODB/branches/blob-merge-branch/NEWS.txt
  U   ZODB/branches/blob-merge-branch/PACKAGE.cfg
  U   ZODB/branches/blob-merge-branch/README.txt
  U   ZODB/branches/blob-merge-branch/doc/guide/zodb.tex
  U   ZODB/branches/blob-merge-branch/setup.py
  _U  ZODB/branches/blob-merge-branch/src/
  A   ZODB/branches/blob-merge-branch/src/BTrees/Development.txt
  D   ZODB/branches/blob-merge-branch/src/BTrees/Maintainer.txt
  U   ZODB/branches/blob-merge-branch/src/BTrees/_fsBTree.c
  U   ZODB/branches/blob-merge-branch/src/ZEO/Exceptions.py
  U   ZODB/branches/blob-merge-branch/src/ZEO/StorageServer.py
  U   ZODB/branches/blob-merge-branch/src/ZEO/__init__.py
  U   ZODB/branches/blob-merge-branch/src/ZEO/tests/forker.py
  U   ZODB/branches/blob-merge-branch/src/ZEO/version.txt
  U   ZODB/branches/blob-merge-branch/src/ZEO/zrpc/connection.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/BaseStorage.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/Connection.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/DB.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/DemoStorage.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/FileStorage/FileStorage.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/POSException.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/__init__.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/collaborations.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/component.xml
  U   ZODB/branches/blob-merge-branch/src/ZODB/config.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/cross-database-references.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/persistentclass.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/serialize.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/subtransactions.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/dbopen.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/multidb.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/synchronizers.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnection.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnectionSavepoint.txt
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testDB.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testZODB.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testcrossdatabasereferences.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/tests/testmvcc.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/transact.py
  U   ZODB/branches/blob-merge-branch/src/ZODB/utils.py
  U   ZODB/branches/blob-merge-branch/src/ZopeUndo/Prefix.py
  U   ZODB/branches/blob-merge-branch/src/ZopeUndo/tests/testPrefix.py
  U   ZODB/branches/blob-merge-branch/src/persistent/README.txt
  U   ZODB/branches/blob-merge-branch/src/persistent/TimeStamp.c
  U   ZODB/branches/blob-merge-branch/src/persistent/dict.py
  U   ZODB/branches/blob-merge-branch/src/persistent/interfaces.py
  U   ZODB/branches/blob-merge-branch/src/persistent/mapping.py
  U   ZODB/branches/blob-merge-branch/src/persistent/tests/persistent.txt
  U   ZODB/branches/blob-merge-branch/src/persistent/tests/persistenttestbase.py
  A   ZODB/branches/blob-merge-branch/src/persistent/tests/test_mapping.py
  U   ZODB/branches/blob-merge-branch/src/scripts/zeoup.py
  U   ZODB/branches/blob-merge-branch/src/transaction/README.txt
  U   ZODB/branches/blob-merge-branch/src/transaction/__init__.py
  U   ZODB/branches/blob-merge-branch/src/transaction/_transaction.py
  U   ZODB/branches/blob-merge-branch/src/transaction/interfaces.py
  U   ZODB/branches/blob-merge-branch/src/transaction/savepoint.txt
  U   ZODB/branches/blob-merge-branch/src/transaction/tests/test_transaction.py
  _U  ZODB/branches/blob-merge-branch/src/zope/
  U   ZODB/branches/blob-merge-branch/test.py

-=-
Modified: ZODB/branches/blob-merge-branch/NEWS.txt
===================================================================
--- ZODB/branches/blob-merge-branch/NEWS.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/NEWS.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,2987 +1,72 @@
-What's new in ZODB3 3.6a4?
+What's new in ZODB3 3.7a1?
 ==========================
-Release date: DD-MMM-2005
+Release date: DD-MMM-200Y
 
-Following is combined news from internal releases (to support ongoing
-Zope3 development).  These are the dates of the internal releases:
 
-- 3.6a4 DD-MMM-2005
-- 3.6a3 07-Sep-2005
-- 3.6a2 06-Sep-2005
-- 3.6a1 04-Sep-2005
-
-Persistent
-----------
-
-- (3.6a4) ZODB 3.6 introduces a change to the basic behavior of Persistent
-  objects in a particular end case.  Before ZODB 3.6, setting
-  ``obj._p_changed`` to a true value when ``obj`` was a ghost was ignored:
-  ``obj`` remained a ghost, and getting ``obj._p_changed`` continued to
-  return ``None``.  Starting with ZODB 3.6, ``obj`` is activated instead
-  (unghostified), and its state is changed from the ghost state to the
-  changed state.  The new behavior is less surprising and more robust.
-
-Commit hooks
-------------
-
-- (3.6a1) The ``beforeCommitHook()`` method has been replaced by the new
-  ``addBeforeCommitHook()`` method, with a more-robust signature.
-  ``beforeCommitHook()`` is now deprecated, and will be removed in ZODB 3.8.
-  Thanks to Julien Anguenot for contributing code and tests.
-
-PersistentMapping
------------------
-
-- (3.6a1) The ``PersistentMapping`` class has an ``__iter__()`` method
-  now, so that objects of this type work well with Python's iteration
-  protocol.  For example, if ``x`` is a ``PersistentMapping`` (or
-  Python dictionary, or BTree, or ``PersistentDict``, ...), then
-  ``for key in x:`` iterates over the keys of ``x``, ``list(x)`` creates
-  a list containing ``x``'s keys, ``iter(x)`` creates an iterator for
-  ``x``'s keys, and so on.
-
-BTrees
-------
-
-- (3.6a1) BTrees and Buckets now implement the ``setdefault()`` and ``pop()``
-  methods. These are exactly like Python's dictionary methods of the same
-  names, except that ``setdefault()`` requires both arguments (and Python is
-  likely to change to require both arguments too -- defaulting the
-  ``default`` argument to ``None`` has no viable use cases).  Thanks to
-  Ruslan Spivak for contributing code, tests, and documentation.
-
-- (3.6a1) Collector 1873.  It wasn't possible to construct a BTree or Bucket
-  from, or apply their update() methods to, a PersistentMapping or
-  PersistentDict.  This works now.
-
-Development
------------
-
-- (3.6a1) The source code for the old ExtensionClass-based Persistence
-  package moved, from ZODB to the Zope 2.9 development tree.  ZODB 3.5
-  makes no use of Persistence, and, indeed, the Persistence package could
-  not be compiled from a ZODB release, since some of the C header files
-  needed appear only in Zope.
-
-- (3.6a3) Re-added the ``zeoctl`` module, for the same reasons
-  ``mkzeoinst`` was re-added (see below).
-
-- (3.6a2) The ``mkzeoinst`` module was re-added to ZEO, because Zope3
-  has a script that expects to import it from there.  ZODB's ``mkzeoinst``
-  script was rewritten to invoke the ``mkzeoinst`` module.
-
-
-What's new in ZODB3 3.5.1b3?
-============================
-Release date: DD-MMM-2005
-
 Following is combined news from internal releases (to support ongoing
-Zope3 development).  These are the dates of the internal releases:
+Zope development).  These are the dates of the internal releases:
 
-- 3.5.1b3 DD-MMM-2005
-- 3.5.1b2 07-Sep-2005
-- 3.5.1b1 06-Sep-2005
+- 3.7a1 DD-MMM-200Y
 
-Build
------
+Connection management
+---------------------
 
-- (3.5.1b2) Re-added the ``zeoctl`` module, for the same reasons
-  ``mkzeoinst`` was re-added (see below).
+- (3.7a1) When more than ``pool_size`` connections have been closed,
+  ``DB`` forgets the excess (over ``pool_size``) connections closed first.
+  Python's cyclic garbage collection can take "a long time" to reclaim them
+  (and may in fact never reclaim them if application code keeps strong
+  references to them), but such forgotten connections can never be opened
+  again, so their caches are now cleared at the time ``DB`` forgets them.
+  Most applications won't notice a difference, but applications that open
+  many connections, and/or store many large objects in connection caches,
+  and/or store limited resources (such as RDB connections) in connection
+  caches may benefit.
 
-- (3.5.1b1) The ``mkzeoinst`` module was re-added to ZEO, because Zope3
-  has a script that expects to import it from there.  ZODB's ``mkzeoinst``
-  script was rewritten to invoke the ``mkzeoinst`` module.
+Documentation
+-------------
 
+- (3.7a1) Thanks to Stephan Richter for converting many of the doctest
+  files to ReST format.  These are now chapters in the Zope 3 apidoc too.
 
-What's new in ZODB3 3.5.0?
-==========================
-Release date: 31-Aug-2005
-
-Following is combined news from internal releases (to support ongoing
-Zope3 development).  These are the dates of the internal releases:
-
-- 3.5a7 11-Aug-2005
-- 3.5a6 04-Aug-2005
-- 3.5a5 19-Jul-2005
-- 3.5a4 14-Jul-2005
-- 3.5a3 17-Jun-2005
-- 3.5a2 16-Jun-2005
-- 3.5a1 10-Jun-2005
-
-Savepoints
-----------
-
-- (3.5.0) As for deprecated subtransaction commits, the intent was
-  that making a savepoint would invoke incremental garbage collection on
-  Connection memory caches, to try to reduce the number of objects in
-  cache to the configured cache size.  Due to an oversight, this didn't
-  happen, and stopped happening for subtransaction commits too.  Making a
-  savepoint (or doing a subtransaction commit) does invoke cache gc now.
-
-- (3.5a3) When a savepoint is made, the states of objects modified so far
-  are saved to a temporary storage (an instance of class ``TmpStore``,
-  although that's an internal implementation detail).  That storage needs
-  to implement the full storage API too, but was missing the ``loadBefore()``
-  method needed for MVCC to retrieve non-current revisions of objects.  This
-  could cause spurious errors if a transaction with a pending savepoint
-  needed to fetch an older revision of some object.
-
-- (3.5a4) The ``ISavepoint`` interface docs said you could roll back to a
-  given savepoint any number of times (until the transaction ends, or until
-  you roll back to an earlier savepoint's state), but the implementation
-  marked a savepoint as invalid after its first use.  The implementation has
-  been repaired, to match the docs.
-
-ZEO client cache
-----------------
-
-- (3.5a6) Two memory leaks in the ZEO client cache were repaired, a
-  major one involving ``ZEO.cache.Entry`` objects, and a minor one involving
-  empty lists.
-
-Subtransactions are deprecated
-------------------------------
-
-- (3.5a4) Subtransactions are deprecated, and will be removed in ZODB 3.7.
-  Use savepoints instead.  Savepoints are more powerful, and code using
-  subtransactions does not mix well with code using savepoints (a
-  subtransaction commit forces all current savepoints to become unusable, so
-  code using subtransactions can hurt newer code trying to use savepoints).
-  In general, a subtransaction commit done just to free memory can be changed
-  from::
-
-      transaction.commit(1)
-
-  to::
-
-      transaction.savepoint(True)
-
-  That is, make a savepoint, and forget it.  As shown, it's best to pass
-  ``True`` for the optional ``optimistic`` argument in this case:  because
-  there's no possibility of asking for a rollback later, there's no need
-  to insist that all data managers support rollback.
-
-  In rarer cases, a subtransaction commit is followed later by a
-  subtransaction abort.  In that case, change the initial::
-
-      transaction.commit(1)
-
-  to::
-
-      sp = transaction.savepoint()
-
-  and in place of the subtransaction abort::
-
-      transaction.abort(1)
-
-  roll back the savepoint instead::
-
-      sp.rollback()
-
-- (3.5a4) Internal uses of subtransactions (transaction ``commit()`` or
-  ``abort()`` passing a true argument) were rewritten to use savepoints
-  instead.
-
-Multi-database
---------------
-
-- (3.5a1) Preliminary support for persistent cross-database references has
-  been added.  See ``ZODB/cross-database-references.txt`` for an
-  introduction.
-
-Tools
------
-
-- (3.5a6, 3.5a7) Collector #1847.  The ZEO client cache tracing and simulation
-  tools weren't updated to work with ZODB 3.3, and the introduction of
-  MVCC required major reworking of the tracing and simulation code.  These
-  tools are in a working state again, although so far lightly tested on
-  just a few applications.  In ``doc/ZEO/``, see the heavily revised
-  ``trace.txt`` and ``cache.txt``.
-
-- (3.5a5) Collector #1846:  If an uncommitted transaction was found,
-  fsrecover.py fell into an infinite loop.
-
-Windows
--------
-
-- (3.5a6) As developed in a long thread starting at
-  http://mail.zope.org/pipermail/zope/2005-July/160433.html
-  there appears to be a race bug in the Microsoft Windows socket
-  implementation, rarely visible in ZEO when multiple processes try to
-  create an "asyncore trigger" simultaneously.  Windows-specific code in
-  ``ZEO/zrpc/trigger.py`` changed to work around this bug when it occurs.
-
-ThreadedAsync.LoopCallback
---------------------------
-
-- (3.5a5) This once again physically replaces Python's ``asyncore.loop``
-  function with its own loop function, because it turns out Zope relied on
-  the seemingly unused ``LoopCallback.exit_status`` global, which was
-  removed in the change described below.  Python's ``asyncore.loop`` is again
-  not invoked, so any breakpoints or debugging prints added to that are again
-  "lost".
-
-- (3.5a4) This replaces Python's ``asyncore.loop`` function with its own, in
-  order to get notified when ``loop()`` is first called.  The signature of
-  ``asyncore.loop`` changed in Python 2.4, but ``LoopCallback.loop``'s
-  signature didn't change to match.  The code here was repaired to be
-  compatible with both old and new signatures, and also repaired to invoke
-  Python's ``asyncore.loop()`` instead of replacing it entirely (so, for
-  example, debugging prints added to Python's ``asyncore.loop`` won't be
-  lost anymore).
-
-
-FileStorage
+IPersistent
 -----------
 
-- (3.5a4) Collector #1830.  In some error cases when reading a FileStorage
-  index, the code referenced an undefined global.
+- (3.7a1) The documentation for ``_p_oid`` now specifies the concrete
+  type of oids (in short, an oid is either None or a non-empty string).
 
-- (3.5a4) Collector #1822.  The ``undoLog()`` and ``undoInfo()`` methods
-  were changed in 3.4a9 to return the documented results.  Alas, some pieces
-  of (non-ZODB) code relied on the actual behavior.  When the ``first`` and
-  ``last`` arguments are both >= 0, these methods now treat them as if they
-  were Python slice indices, including the `first` index but excluding the
-  ``last`` index.  This matches former behavior, although it contradicts older
-  ZODB UML documentation.  The documentation in
-  ``ZODB.interfaces.IStorageUndoable`` was changed to match the new intent.
-
-- (3.5a2) The ``_readnext()`` method now returns the transaction size as
-  the value of the "size" key.  Thanks to Dieter Maurer for the patch, from
-  http://mail.zope.org/pipermail/zodb-dev/2003-October/006157.html. "This is
-  very valuable when you want to spot strange transaction sizes via Zope's
-  'Undo' tab".
-
-BTrees
-------
-
-- (3.5.a5) Collector 1843.  When a non-integer was passed to a method like
-  ``keys()`` of a Bucket or Set with integer keys, an internal error code
-  was overlooked, leading to everything from "delayed errors" to segfaults.
-  Such cases raise TypeError now, as intended.
-
-- (3.5a4) Collector 1831.  The BTree ``minKey()`` and ``maxKey()`` methods
-  gave a misleading message if no key satisfying the constraints existed in a
-  non-empty tree.
-
-- (3.5a4) Collector 1829.  Clarified that the ``minKey()`` and ``maxKey()``
-  methods raise an exception if no key exists satsifying the constraints.
-
-- (3.5a4) The ancient ``convert.py`` script was removed.  It was intended to
-  convert "old" BTrees to "new" BTrees, but the "old" BTree implementation
-  was removed from ZODB years ago.
-
-
-What's new in ZODB3 3.4.2a1?
-============================
-Release date: DD-MMM-2005
-
-Following are dates of internal releases (to support ongoing Zope 2
-development) since ZODB 3.4's last public release:
-
-- 3.4.2a1 DD-MMM-2005
-
-Savepoints
-----------
-
-- (3.4.2a1) As for deprecated subtransaction commits, the intent was
-  that making a savepoint would invoke incremental garbage collection on
-  Connection memory caches, to try to reduce the number of objects in
-  cache to the configured cache size.  Due to an oversight, this didn't
-  happen, and stopped happening for subtransaction commits too.  Making a
-  savepoint (or doing a subtransaction commit) does invoke cache gc now.
-
-PersistentMapping
------------------
-
-- (3.4.2a1) The ``PersistentMapping`` class has an ``__iter__()`` method
-  now, so that objects of this type work well with Python's iteration
-  protocol.  For example, if ``x`` is a ``PersistentMapping`` (or
-  Python dictionary, or BTree, or ``PersistentDict``, ...), then
-  ``for key in x:`` iterates over the keys of ``x``, ``list(x)`` creates
-  a list containing ``x``'s keys, ``iter(x)`` creates an iterator for
-  ``x``'s keys, and so on.
-
-BTrees
-------
-
-- (3.4.2a1) Collector 1873.  It wasn't possible to construct a BTree or Bucket
-  from, or apply their update() methods to, a PersistentMapping or
-  PersistentDict.  This works now.
-
-
-What's new in ZODB3 3.4.1?
-==========================
-Release date: 09-Aug-2005
-
-Following are dates of internal releases (to support ongoing Zope 2
-development) since ZODB 3.4's last public release:
-
-- 3.4.1b5 08-Aug-2005
-- 3.4.1b4 07-Aug-2005
-- 3.4.1b3 04-Aug-2005
-- 3.4.1b2 02-Aug-2005
-- 3.4.1b1 26-Jul-2005
-- 3.4.1a6 19-Jul-2005
-- 3.4.1a5 12-Jul-2005
-- 3.4.1a4 08-Jul-2005
-- 3.4.1a3 02-Jul-2005
-- 3.4.1a2 29-Jun-2005
-- 3.4.1a1 27-Jun-2005
-
-Savepoints
-----------
-
-- (3.4.1a1) When a savepoint is made, the states of objects modified so far
-  are saved to a temporary storage (an instance of class ``TmpStore``,
-  although that's an internal implementation detail).  That storage needs
-  to implement the full storage API too, but was missing the ``loadBefore()``
-  method needed for MVCC to retrieve non-current revisions of objects.  This
-  could cause spurious errors if a transaction with a pending savepoint
-  needed to fetch an older revision of some object.
-
-- (3.4.1a5) The ``ISavepoint`` interface docs said you could roll back to a
-  given savepoint any number of times (until the transaction ends, or until
-  you roll back to an earlier savepoint's state), but the implementation
-  marked a savepoint as invalid after its first use.  The implementation has
-  been repaired, to match the docs.
-
-- (3.4.1b4) Collector 1860:  use an optimistic savepoint in ExportImport
-  (there's no possiblity of rollback here, so no need to insist that the
-  data manager support rollbacks).
-
-ZEO client cache
-----------------
-
-- (3.4.1b3) Two memory leaks in the ZEO client cache were repaired, a
-  major one involving ``ZEO.cache.Entry`` objects, and a minor one involving
-  empty lists.
-
-Subtransactions
----------------
-
-- (3.4.1a5) Internal uses of subtransactions (transaction ``commit()`` or
-  ``abort()`` passing a true argument) were rewritten to use savepoints
-  instead.  Application code is strongly encouraged to do this too:
-  subtransactions are weaker, will be deprecated soon, and do not mix well
-  with savepoints (when you do a subtransaction commit, all current
-  savepoints are made unusable).  In general, a subtransaction commit
-  done just to free memory can be changed from::
-
-      transaction.commit(1)
-
-  to::
-
-      transaction.savepoint(True)
-
-  That is, make a savepoint, and forget it.  As shown, it's best to pass
-  ``True`` for the optional ``optimistic`` argument in this case:  because
-  there's no possibility of asking for a rollback later, there's no need
-  to insist that all data managers support rollback.
-
-  In rarer cases, a subtransaction commit is followed later by a
-  subtransaction abort.  In that case, change the initial::
-
-      transaction.commit(1)
-
-  to::
-
-      sp = transaction.savepoint()
-
-  and in place of the subtransaction abort::
-
-      transaction.abort(1)
-
-  roll back the savepoint instead::
-
-      sp.rollback()
-
-FileStorage
------------
-
-- (3.4.1a3) Collector #1830.  In some error cases when reading a FileStorage
-  index, the code referenced an undefined global.
-
-- (3.4.1a2) Collector #1822.  The ``undoLog()`` and ``undoInfo()`` methods
-  were changed in 3.4a9 to return the documented results.  Alas, some pieces
-  of (non-ZODB) code relied on the actual behavior.  When the `first` and
-  `last` arguments are both >= 0, these methods now treat them as if they
-  were Python slice indices, including the `first` index but excluding the
-  `last` index.  This matches former behavior, although it contradicts older
-  ZODB UML documentation.  The documentation in
-  ``ZODB.interfaces.IStorageUndoable`` was changed to match the new intent.
-
-- (3.4.1a1) The ``UndoSearch._readnext()`` method now returns the transaction
-  size as the value of the "size" key.  Thanks to Dieter Maurer for the
-  patch, from
-  http://mail.zope.org/pipermail/zodb-dev/2003-October/006157.html. "This is
-  very valuable when you want to spot strange transaction sizes via Zope's
-  'Undo' tab".
-
-ThreadedAsync.LoopCallback
---------------------------
-
-- (3.4.1a6) This once again physically replaces Python's ``asyncore.loop``
-  function with its own loop function, because it turns out Zope relied on
-  the seemingly unused ``LoopCallback.exit_status`` global, which was
-  removed in the change described below.  Python's ``asyncore.loop`` is again
-  not invoked, so any breakpoints or debugging prints added to that are again
-  "lost".
-
-- (3.4.1a1) This replaces Python's ``asyncore.loop`` function with its own,
-  in order to get notified when ``loop()`` is first called.  The signature of
-  ``asyncore.loop`` changed in Python 2.4, but ``LoopCallback.loop``'s
-  signature didn't change to match.  The code here was repaired to be
-  compatible with both old and new signatures, and also repaired to invoke
-  Python's ``asyncore.loop()`` instead of replacing it entirely (so, for
-  example, debugging prints added to Python's ``asyncore.loop`` won't be lost
-  anymore).
-
-Windows
--------
-
-- (3.4.1b2) As developed in a long thread starting at
-  http://mail.zope.org/pipermail/zope/2005-July/160433.html
-  there appears to be a race bug in the Microsoft Windows socket
-  implementation, rarely visible in ZEO when multiple processes try to
-  create an "asyncore trigger" simultaneously.  Windows-specific code in
-  ``ZEO/zrpc/trigger.py`` changed to work around this bug when it occurs.
-
-
 Tools
 -----
 
-- (3.4.1b1 thru 3.4.1b5) Collector #1847.  The ZEO client cache tracing and
-  simulation tools weren't updated to work with ZODB 3.3, and the
-  introduction of MVCC required major reworking of the tracing and simulation
-  code.  These tools are in a working state again, although so far lightly
-  tested on just a few applications.  In ``doc/ZEO/``, see the heavily revised
-  ``trace.txt`` and ``cache.txt``.
+- (3.7a1) The changeover from zLOG to the logging module means that some
+  tools need to perform minimal logging configuration themselves. Changed
+  the zeoup script to do so and thus enable it to emit error messages.
 
-- (3.4.1a6) Collector #1846:  If an uncommitted transaction was found,
-  fsrecover.py fell into an infinite loop.
-
-
-DemoStorage
------------
-
-- (3.4.1a1) The implementation of ``undoLog()`` was wrong in several ways;
-  repaired.
-
 BTrees
 ------
 
-- (3.4.1a6) Collector 1843.  When a non-integer was passed to a method like
-  ``keys()`` of a Bucket or Set with integer keys, an internal error code
-  was overlooked, leading to everything from "delayed errors" to segfaults.
-  Such cases raise TypeError now, as intended.
+- (3.7a1) Suppressed warnings about signedness of characters when
+  compiling under GCC 4.0.x.  See http://www.zope.org/Collectors/Zope/2027.
 
-- (3.4.1a4) Collector 1831.  The BTree ``minKey()`` and ``maxKey()`` methods
-  gave a misleading message if no key satisfying the constraints existed in a
-  non-empty tree.
-
-- (3.4.1a3) Collector 1829.  Clarified that the ``minKey()`` and ``maxKey()``
-  methods raise an exception if no key exists satsifying the constraints.
-
-
-What's new in ZODB3 3.4?
-========================
-Release date: 09-Jun-2005
-
-Following is combined news from the "internal releases" (to support
-ongoing Zope 2.8 and Zope3 development) since the last public ZODB 3.4
-release.  These are the dates of the internal releases:
-
-- 3.4c2 06-Jun-2005
-- 3.4c1 03-Jun-2005
-- 3.4b3 27-May-2005
-- 3.4b2 26-May-2005
-
-Connection, DB
---------------
-
-- (3.4b3) ``.transaction_manager`` is now a public attribute of
-  IDataManager, and is the instance of ITransactionManager used by the
-  data manager as its transaction manager.  There was previously no way
-  to ask a data manager which transaction manager it was using.  It's
-  intended that ``transaction_manager`` be treated as read-only.
-
-- (3.4b3) For sanity, the ``txn_mgr`` argument to ``DB.open()``,
-  ``Connection.__init__()``, and ``Connection._setDB()`` has been renamed
-  to ``transaction_manager``.  ``txn_mgr`` is still accepted, but is
-  deprecated and will be removed in ZODB 3.6.  Any code that was using
-  the private ``._txn_mgr`` attribute of ``Connection`` will break
-  immediately.
-
-Development
------------
-
-- (3.4b2) ZODB's ``test.py`` is now a small driver for the shared
-  ``zope.testing.testrunner``.  See the latter's documentation
-  for command-line arguments.
-
-Error reporting
----------------
-
-- (3.4c1) In the unlikely event that ``referencesf()`` reports an unpickling
-  error (for example, a corrupt database can cause this), the message it
-  produces no longer contains unprintable characters.
-
-Tests
------
-
-- (3.4c2) ``checkCrossDBInvalidations`` suffered spurious failures too often
-  on slow and/or busy machines.  The test is willing to wait longer for
-  success now.
-
-
-What's new in ZODB3 3.4b1?
-==========================
-Release date: 19-May-2005
-
-What follows is combined news from the "internal releases" (to support
-ongoing Zope 2.8 and Zope3 development) since the last public ZODB 3.4
-release.  These are the dates of the internal releases:
-
-- 3.4b1 19-May-2005
-- 3.4a9 12-May-2005
-- 3.4a8 09-May-2005
-- 3.4a7 06-May-2005
-- 3.4a6 05-May-2005
-- 3.4a5 25-Apr-2005
-- 3.4a4 23-Apr-2005
-- 3.4a3 13-Apr-2005
-- 3.4a2 03-Apr-2005
-
-
-transaction
------------
-
-- (3.4a7) If the first activity seen by a new ``ThreadTransactionManager`` was
-  an explicit ``begin()`` call, then synchronizers registered after that (but
-  still during the first transaction) were not communicated to the
-  transaction object.  As a result, the ``afterCompletion()`` methods of
-  registered synchronizers weren't called when the first transaction ended.
-
-- (3.4a6) Doing a subtransaction commit erroneously processed invalidations,
-  which could lead to an inconsistent view of the database.  For example, let
-  T be the transaction of which the subtransaction commit was a part.  If T
-  read a persistent object O's state before the subtransaction commit, did not
-  commit new state of its own for O during its subtransaction commit, and O
-  was modified before the subtransaction commit by a different transaction,
-  then the subtransaction commit processed an invalidation for O, and the
-  state T read for O originally was discarded in T.  If T went on to access O
-  again, it saw the newly committed (by a different transaction) state for O::
-
-      o_attr = O.some_attribute
-      get_transaction().commit(True)
-      assert o_attr == O.some_attribute
-
-  could fail, and despite that T never modifed O.
-
-- (3.4a4) Transactions now support savepoints.  Savepoints allow changes to be
-  periodically checkpointed within a transaction.  You can then rollback to a
-  previously created savepoint.  See ``transaction/savepoint.txt``.
-
-- (3.4a6) A ``getBeforeCommitHooks()`` method was added.  It returns an
-  iterable producing the registered beforeCommit hooks.
-
-- (3.4a6) The ``ISynchronizer`` interface has a new ``newTransaction()``
-  method. This is invoked whenever a transaction manager's ``begin()`` method
-  is called.  (Note that a transaction object's (as opposed to a transaction
-  manager's) ``begin()`` method is deprecated, and ``newTransaction()`` is
-  not called when using the deprecated method.)
-
-- (3.4a6) Relatedly, ``Connection`` implements ``ISynchronizer``, and
-  ``Connection``'s ``afterCompletion()`` and ``newTransaction()`` methods now
-  call ``sync()`` on the underlying storage (if the underlying storage has
-  such a method), in addition to processing invalidations.  The practical
-  implication is that storage synchronization will be done automatically now,
-  whenever a transaction is explicitly started, and after top-level
-  transaction commit or abort.  As a result, ``Connection.sync()`` should
-  virtually never be needed anymore, and will eventually be deprecated.
-
-- (3.4a3) Transaction objects have a new method, ``beforeCommitHook(hook,
-  *args, **kws)``.  Hook functions registered with a transaction are called
-  at the start of a top-level commit, before any of the work is begun, so a
-  hook function can perform any database operations it likes.  See
-  ``test_beforeCommitHook()`` in ``transaction/tests/test_transaction.py``
-  for a tutorial doctest, and the ``ITransaction`` interface for details.
-  Thanks to Florent Guillaume for contributing code and tests.
-
-- (3.4a3) Clarifications were made to transaction interfaces.
-
-Support for ZODB4 savepoint-aware data managers has been dropped
-----------------------------------------------------------------
-
-- (3.4a4) In adding savepoint support, we dropped the attempted support for
-  ZODB4 data managers that support savepoints.  We don't think that this will
-  affect anyone.
-
-ZEO
----
-
-- (3.4a4) The ZODB and ZEO version numbers are now the same.  Concretely::
-
-      import ZODB, ZEO
-      assert ZODB.__version__ == ZEO.version
-
-  no longer fails.  If interested, see the README file for details about
-  earlier version numbering schemes.
-
-- (3.4b1) ZConfig version 2.3 adds new socket address types, for smoother
-  default behavior across platforms.  The hostname portion of
-  socket-binding-address defaults to an empty string, which acts like
-  INADDR_ANY on Windows and Linux (bind to any interface).  The hostname
-  portion of socket-connection-address defaults to "127.0.0.1" (aka
-  "localhost").  In config files, the types of ``zeo`` section keys
-  ``address`` and ``monitor-address`` changed to socket-binding-address,
-  and the type of the ``zeoclient`` section key ``server`` changed to
-  socket-connection-address.
-
-- (3.4a4) The default logging setup in ``runzeo.py`` was broken.  It was
-  changed so that running ``runzeo.py`` from a command line now, and without
-  using a config file, prints output to the console much as ZODB 3.2 did.
-
-ZEO on Windows
---------------
-
-Thanks to Mark Hammond for these ``runzeo.py`` enhancements on Windows:
-
-- (3.4b1) Collector 1788:  Repair one of the new features below.
-
-- (3.4a4) A pid file (containing the process id as a decimal string) is
-  created now for a ZEO server started via ``runzeo.py``.  External programs
-  can read the pid from this file and derive a "signal name" used in a new
-  signal-emulation scheme for Windows.  This is only necessary on Windows,
-  but the pid file is created on all platforms that implement
-  ``os.getpid()``, as long as the ``pid-filename`` option is set, or
-  environment variable ``INSTANCE_HOME`` is defined.  The ``pid-filename``
-  option can be set in a ZEO config file, or passed as the new ``--pid-file``
-  argument to ``runzeo.py``.
-
-- (3.4a4) If available, ``runzeo.py`` now uses Zope's new 'Signal' mechanism
-  for Windows, to implement clean shutdown and log rotation handlers for
-  Windows. Note that the Python in use on the ZEO server must also have the
-  Python Win32 extensions installed for this to be useful.
-
-Tools
------
-
-- (3.4a4) ``fsdump.py`` now displays the size (in bytes) of data records.
-  This actually went in several months go, but wasn't noted here at the time.
-  Thanks to Dmitry Vasiliev for contributing code and tests.
-
-FileStorage
------------
-
-- (3.4a9) The ``undoLog()`` and ``undoInfo()`` methods almost always returned
-  a wrong number of results, one too many if ``last < 0`` (the default is
-  such a case), or one too few if ``last >= 0``.  These have been repaired,
-  new tests were added, and these methods are now documented in
-  ``ZODB.interfaces.IStorageUndoable``.
-
-- (3.4a2) A ``pdb.set_trace()`` call was mistakenly left in method
-  ``FileStorage.modifiedInVersion()``.
-
-ZConfig
--------
-
-- (3.4b1) The "standalone" release of ZODB now includes ZConfig version 2.3.
-
-DemoStorage
------------
-
-- (3.4a4) Appropriate implementations of the storage API's ``registerDB()``
-  and ``new_oid()`` methods were added, delegating to the base storage.  This
-  was needed to support wrapping a ZEO client storage as a ``DemoStorage``
-  base storage, as some new Zope tests want to do.
-
-BaseStorage
------------
-
-- (3.4a4) ``new_oid()``'s undocumented ``last=`` argument was removed.  It
-  was used only for internal recursion, and injured code sanity elsewhere
-  because not all storages included it in their ``new_oid()``'s signature.
-  Straightening this out required adding ``last=`` everywhere, or removing it
-  everywhere. Since recursion isn't actually needed, and there was no other
-  use for ``last=``, removing it everywhere was the obvious choice.
-
-Tests
------
-
-- (3.4a3) The various flavors of the ``check2ZODBThreads`` and
-  ``check7ZODBThreads`` tests are much less likely to suffer sproadic
-  failures now.
-
-- (3.4a2) The test ``checkOldStyleRoot`` failed in Zope3, because of an
-  obscure dependence on the ``Persistence`` package (which Zope3 doesn't use).
-
-ZApplication
-------------
-
-- (3.4a8) The file ``ZApplication.py`` was moved, from ZODB to Zope(2).  ZODB
-  and Zope3 don't use it, but Zope2 does.
-
-- (3.4a7) The ``__call__`` method didn't work if a non-None ``connection``
-  string argument was passed.  Thanks to Stefan Holek for noticing.
-
-
-What's new in ZODB3 3.4a1?
-==========================
-Release date: 01-Apr-2005
-
-transaction
------------
-
-- ``get_transaction()`` is officially deprecated now, and will be removed
-  in ZODB 3.6.  Use the ``transaction`` package instead.   For example,
-  instead of::
-
-      import ZODB
-      ...
-      get_transaction().commit()
-
-  do::
-
-      import transaction
-      ...
-      transaction.commit()
-
-DB
---
-
-- There is no longer a hard limit on the number of connections that
-  ``DB.open()`` will create.  In other words, ``DB.open()`` never blocks
-  anymore waiting for an earlier connection to close, and ``DB.open()``
-  always returns a connection now (while it wasn't documented, it was
-  possible for ``DB.open()`` to return ``None`` before).
-
-  ``pool_size`` continues to default to 7, but its meaning has changed:
-  if more than ``pool_size`` connections are obtained from ``DB.open()``
-  and not closed, a warning is logged; if more than twice ``pool_size``, a
-  critical problem is logged.  ``pool_size`` should be set to the maximum
-  number of connections from the ``DB`` instance you expect to have open
-  simultaneously.
-
-  In addition, if a connection obtained from ``DB.open()`` becomes
-  unreachable without having been explicitly closed, when Python's garbage
-  collection reclaims that connection it no longer counts against the
-  ``pool_size`` thresholds for logging messages.
-
-  The following optional arguments to ``DB.open()`` are deprecated:
-  ``transaction``, ``waitflag``, ``force`` and ``temporary``.  If one
-  is specified, its value is ignored, and ``DeprecationWarning`` is
-  raised.  In ZODB 3.6, these optional arguments will be removed.
-
-- Lightweight support for "multi-databases" is implemented.  These are
-  collections of named DB objects and associated open Connections, such
-  that the Connection for any DB in the collection can be obtained from
-  a Connection from any other DB in the collection.  See the new test
-  file ZODB/tests/multidb.txt for a tutorial doctest.  Thanks to Christian
-  Theune for his work on this during the PyCon 2005 ZODB sprint.
-
-ZEO compatibility
------------------
-
-There are severe restrictions on using ZEO servers and clients at or after
-ZODB 3.3 with ZEO servers and clients from ZODB versions before 3.3.  See the
-reworked ``Compatibility`` section in ``README.txt`` for details.  If
-possible, it will be easiest to move clients and servers to 3.3+
-simultaneously.  With care, it's possible to use a 3.3+ ZEO server with
-pre-3.3 ZEO clients, but not possible to use a pre-3.3 ZEO server with 3.3+
-ZEO clients.
-
-BTrees
-------
-
-- A new family of BTree types, in the ``IFBTree`` module, map
-  signed integers (32 bits) to C floats (also 32 bits).  The
-  intended use is to help construct search indices, where, e.g.,
-  integer word or document identifiers map to scores of some
-  kind.  This is easier than trying to work with scaled integer
-  scores in an ``IIBTree``, and Zope3 has moved to ``IFBTrees``
-  for these purposes in its search code.
-
-FileStorage
------------
-
-- Addded a record iteration protocol to FileStorage.  You can use the
-  record iterator to iterate over all current revisions of data
-  pickles in the storage.
-
-  In order to support calling via ZEO, we don't implement this as an
-  actual iterator.  An example of using the record iterator protocol
-  is as follows::
-
-      storage = FileStorage('anexisting.fs')
-      next_oid = None
-      while True:
-          oid, tid, data, next_oid = storage.record_iternext(next_oid)
-          # do something with oid, tid and data
-          if next_oid is None:
-              break
-
-  The behavior of the iteration protocol is now to iterate over all
-  current records in the database in ascending oid order, although
-  this is not a promise to do so in the future.
-
-
-Tools
------
-
-New tool fsoids.py, for heavy debugging of FileStorages; shows all
-uses of specified oids in the entire database (e.g., suppose oid 0x345620
-is missing -- did it ever exist?  if so, when?  who referenced it?  when
-was the last transaction that modified an object that referenced it?
-which objects did it reference?  what kind of object was it?).
-ZODB/test/testfsoids.py is a tutorial doctest.
-
-
-fsIndex
--------
-
-Efficient, general implementations of ``minKey()`` and ``maxKey()`` methods
-were added.  ``fsIndex`` is a special hybrid kind of BTree used to implement
-FileStorage indices.  Thanks to Chris McDonough for code and tests.
-
-
-What's new in ZODB3 3.3.1?
-==========================
-Release date: DD-MMM-2005
-
-Tests
------
-
-The various flavors of the ``check2ZODBThreads`` and ``check7ZODBThreads``
-tests are much less likely to suffer sproadic failures now.
-
-
-What's new in ZODB3 3.3.1c1?
-============================
-Release date: 01-Apr-2005
-
-BTrees
-------
-
-Collector #1734: BTrees conflict resolution leads to index inconsistencies.
-
-Silent data loss could occur due to BTree conflict resolution when one
-transaction T1 added a new key to a BTree containing at least three buckets,
-and a concurrent transaction T2 deleted all keys in the bucket to which the
-new key was added.  Conflict resolution then created a bucket containing the
-newly added key, but the bucket remained isolated, disconnected from the
-BTree. In other words, the committed BTree didn't contain the new key added by
-T1.  Conflict resolution doesn't have enough information to repair this,
-so ``ConflictError`` is now raised in such cases.
-
-
-ZEO
----
-
-Repaired subtle race conditions in establishing ZEO connections, both client-
-and server-side.  These account for intermittent cases where ZEO failed
-to make a connection (or reconnection), accompanied by a log message showing
-an error caught in ``asyncore`` and having a traceback ending with:
-
-    ``UnpicklingError: invalid load key, 'Z'.``
-
-or:
-
-    ``ZRPCError: bad handshake '(K\x00K\x00U\x0fgetAuthProtocol)t.'``
-
-or:
-
-    ``error: (9, 'Bad file descriptor')``
-
-or an ``AttributeError``.
-
-These were exacerbated when running the test suite, because of an unintended
-busy loop in the test scaffolding, which could starve the thread trying to
-make a connection.  The ZEO reconnection tests may run much faster now,
-depending on platform, and should suffer far fewer (if any) intermittent
-"timed out waiting for storage to connect" failures.
-
-ZEO protocol and compatibility
-------------------------------
-
-ZODB 3.3 introduced multiversion concurrency control (MVCC), which required
-changes to the ZEO protocol.  The first 3.3 release should have increased
-the internal ZEO protocol version number (used by ZEO protocol negotiation
-when a client connects), but neglected to.  This has been repaired.
-
-Compatibility between pre-3.3 and post-3.3 ZEO clients and servers remains
-very limited.  See the newly updated ``Compatibility`` section in
-``README.txt`` for details.
-
-FileStorage
------------
-
-- The ``.store()`` and ``.restore()`` methods didn't update the storage's
-  belief about the largest oid in use when passed an oid larger than the
-  largest oid the storage already knew about.  Because ``.restore()`` in
-  particular is used  by ``copyTransactionsFrom()``, and by the first stage
-  of ZRS recovery, a large database could be created that believed the only
-  oid in use was oid 0 (the special oid reserved for the root object).  In
-  rare cases, it could go on from there assigning duplicate oids to new
-  objects, starting over from oid 1 again.  This has been repaired.  A
-  new ``set_max_oid()`` method was added to the ``BaseStorage`` class so
-  that derived storages can update the largest oid in use in a threadsafe
-  way.
-
-- A FileStorage's index file tried to maintain the index's largest oid as a
-  separate piece of data, incrementally updated over the storage's lifetime.
-  This scheme was more complicated than necessary, so was also more brittle
-  and slower than necessary.  It indirectly participated in a rare but
-  critical bug:  when a FileStorage was created via
-  ``copyTransactionsFrom()``, the "maximum oid" saved in the index file was
-  always 0.  Use that FileStorage, and it could then create "new" oids
-  starting over at 0 again, despite that those oids were already in use by
-  old objects in the database.  Packing a FileStorage has no reason to
-  try to update the maximum oid in the index file either, so this kind of
-  damage could (and did) persist even across packing.
-
-  The index file's maximum-oid data is ignored now, but is still written
-  out so that ``.index`` files can be read by older versions of ZODB.
-  Finding the true maximum oid is done now by exploiting that the main
-  index is really a kind of BTree (long ago, this wasn't true), and finding
-  the largest key in a BTree is inexpensive.
-
-- A FileStorage's index file could be updated on disk even if the storage
-  was opened in read-only mode.  That bug has been repaired.
-
-- An efficient ``maxKey()`` implementation was added to class ``fsIndex``.
-
-
-Pickle (in-memory Connection) Cache
------------------------------------
-
-You probably never saw this exception:
-
-    ``ValueError: Can not re-register object under a different oid``
-
-It's been changed to say what it meant:
-
-    ``ValueError: A different object already has the same oid``
-
-This happens if an attempt is made to add distinct objects to the cache
-that have the same oid (object identifier).  ZODB should never do this,
-but it's possible for application code to force such an attempt.
-
-PersistentMapping and PersistentList
-------------------------------------
-
-Backward compatibility code has been added so that the sanest of the
-ZODB 3.2 dotted paths for ``PersistentMapping`` and ``PersistentList``
-resolve.  These are still preferred:
-
-- ``from persistent.list import PersistentList``
-- ``from persistent.mapping import PersistentMapping``
-
-but these work again too:
-
-- ``from ZODB.PersistentList import PersistentList``
-- ``from ZODB.PersistentMapping import PersistentMapping``
-
-BTrees
-------
-
-The BTrees interface file neglected to document the optional
-``excludemin`` and ``excludemax`` arguments to the ``keys()``, ``values()``
-and ``items()`` methods.  Appropriate changes were merged in from the
-ZODB4 BTrees interface file.
-
-Tools
------
-
-- ``mkzeoinst.py``'s default port number changed from to 9999 to 8100, to
-  match the example in Zope's ``zope.conf``.
-
-fsIndex
--------
-
-An efficient ``maxKey()`` method was implemented for the ``fsIndex`` class.
-This makes it possible to determine the largest oid in a ``FileStorage``
-index efficiently, directly, and reliably, replacing a more delicate scheme
-that tried to keep track of this by saving an oid high water mark in the
-index file and incrementally updating it.
-
-
-What's new in ZODB3 3.3.1a1?
-============================
-Release date: 11-Jan-2005
-
-ZEO client cache
-----------------
-
-- Collector 1536:  The ``cache-size`` configuration option for ZEO clients
-  was being ignored.  Worse, the client cache size was only one megabyte,
-  much smaller than the advertised default of 20MB.  Note that the default
-  is carried over from a time when gigabyte disks were expensive and rare;
-  20MB is also too small on most modern machines.
-
-- Fixed a nasty bug in cache verification.  A persistent ZEO cache uses a
-  disk file, and, when active, has some in-memory data structures too to
-  speed operation.  Invalidations processed as part of startup cache
-  verification were reflected in the in-memory data structures, but not
-  correctly in the disk file.  So if an object revision was invalidated as
-  part of verification, the object wasn't loaded again before the connection
-  was closed, and the object revision remained in the cache file until the
-  connection was closed, then the next time the cache file was opened it
-  could believe that the stale object revision in the file was actually
-  current.
-
-- Fixed a bug wherein an object removed from the client cache didn't
-  properly mark the file slice it occupied as being available for reuse.
-
-ZEO
----
-
-Collector 1503:  excessive logging.  It was possible for a ZEO client to
-log "waiting for cache verification to finish" messages at a very high
-rate, producing gigabytes of such messages in short order.
-``ClientStorage._wait_sync()`` was changed to log no more than one
-such message per 5 minutes.
-
-persistent
-----------
-
-Collector #1350:  ZODB has a default one-thread-per-connection model, and
-two threads should never do operations on a single connection
-simultaneously.  However, ZODB can't detect violations, and this happened
-in an early stage of Zope 2.8 development.  The low-level ``ghostify()``
-and ``unghostify()`` routines in ``cPerisistence.c`` were changed to give
-some help in detecting this when it happens.  In a debug build, both abort
-the process if thread interference is detected.  This is extreme, but
-impossible to overlook.  In a release build, ``unghostify()`` raises
-``SystemError`` if thread damage is detected; ``ghostify()`` ignores the
-problem in a release build (``ghostify()`` is supposed to be so simple that
-it "can't fail").
-
-ConflictError
--------------
-
-New in 3.3, a ``ConflictError`` exception may attempt to insert the path to
-the object's class in its message.  However, a ZEO server may not have
-access to application class implementations, and then the attempt by the
-server to raise ``ConflictError`` could raise ``ImportError`` instead while
-trying to determine the object's class path.  This was confusing.  The code
-has been changed to obtain the class path from the object's pickle, without
-trying to import application modules or classes.
-
-FileStorage
------------
-
-Collector 1581:  When an attempt to pack a corrupted ``Data.fs`` file was
-made, it was possible for the pack routine to die with a reference to an
-undefined global while it was trying to raise ``CorruptedError``.  It
-raises ``CorruptedError``, as it always intended, in these cases now.
-
-Install
--------
-
-The C header file ``ring.h`` is now installed.
-
-Tools
------
-
-- ``BTrees.check.display()`` now displays the oids (if any) of the
-  BTree's or TreeSet's constituent objects.
-
-
-What's new in ZODB3 3.3?
-========================
-Release date: 06-Oct-2004
-
-ZEO
----
-
-The encoding of RPC calls between server and client was being done
-with protocol 0 ("text mode") pickles, which could require sending
-four times as many bytes as necessary.  Protocol 1 pickles are used
-now.  Thanks to Andreas Jung for the diagnosis and cure.
-
-ZODB/component.xml
-------------------
-
-``cache-size`` parameters were changed from type ``integer`` to
-type ``byte-size``.  This allows you to specify, for example,
-"``cache-size 20MB``" to get a 20 megabyte cache.
-
-transaction
------------
-
-The deprecation warning for ``Transaction.begin()`` was changed to
-point to the caller, instead of to ``Transaction.begin()`` itself.
-
 Connection
 ----------
 
-Restored Connection's private ``_opened`` attribute.  This was still
-referenced by ``DB.connectionDebugInfo()``, and Zope 2 calls the latter.
+- (3.7a1) An optimization for loading non-current data (MVCC) was
+  inadvertently disabled in ``_setstate()``; this has been repaired.
 
-FileStorage
------------
-
-Collector #1517: History tab for ZPT does not work. ``FileStorage.history()``
-was reading the user, description, and extension fields out of the object
-pickle, due to starting the read at a wrong location.  Looked like
-cut-and-paste repetition of the same bug in ``FileStorage.FileIterator``
-noted in the news for 3.3c1.
-
-What's new in ZODB3 3.3 release candidate 1?
-============================================
-Release date: 14-Sep-2004
-
-Connection
-----------
-
-ZODB intends to raise ``ConnnectionStateError`` if an attempt is made to
-close a connection while modifications are pending (the connection is
-involved in a transaction that hasn't been ``abort()``'ed or
-``commit()``'ed).  It was missing the case where the only pending
-modifications were made in subtransactions.  This has been fixed.  If an
-attempt to close a connection with pending subtransactions is made now::
-
-    ConnnectionStateError: Cannot close a connection with a pending subtransaction
-
-is raised.
-
-transaction
------------
-
-- Transactions have new, backward-incompatible behavior in one respect:
-  if a ``Transaction.commit()``, ``Transaction.commit(False)``, or
-  ``Transaction.commit(True)`` raised an exception, prior behavior was that
-  the transaction effectively aborted, and a new transaction began.
-  A primary bad consequence was that, if in a sequence of subtransaction
-  commits, one of the commits failed but the exception was suppressed,
-  all changes up to and including the failing commit were lost, but
-  later subtransaction commits in the sequence got no indication that
-  something had gone wrong, nor did the final (top level) commit.  This
-  could easily lead to inconsistent data being committed, from the
-  application's point of view.
-
-  The new behavior is that a failing commit "sticks" until explicitly
-  cleared.  Now if an exception is raised by a ``commit()`` call (whether
-  subtransaction or top level) on a Transaction object ``T``:
-
-    - Pending changes are aborted, exactly as they were for a failing
-      commit before.
-
-    - But ``T`` remains the current transaction object (if ``tm`` is ``T``'s
-      transaction manger, ``tm.get()`` continues to return ``T``).
-
-    - All subsequent attempts to do ``T.commit()``, ``T.join()``, or
-      ``T.register()`` raise the new ``TransactionFailedError`` exception.
-      Note that if you try to modify a persistent object, that object's
-      resource manager (usually a ``Connection`` object) will attempt to
-      ``join()`` the failed transaction, and ``TransactionFailedError``
-      will be raised right away.
-
-  So after a transaction or subtransaction commit fails, that must be
-  explicitly cleared now, either by invoking ``abort()`` on the transaction
-  object, or by invoking ``begin()`` on its transaction manager.
-
-- Some explanations of new transaction features in the 3.3a3 news
-  were incorrect, and this news file has been retroactively edited to
-  repair that.  See news for 3.3a3 below.
-
-- If ReadConflictError was raised by an attempt to load an object with a
-  ``_p_independent()`` method that returned false, attempting to commit the
-  transaction failed to (re)raise ReadConflictError for that object.  Note
-  that ZODB intends to prevent committing a transaction in which a
-  ReadConflictError occurred; this was an obscure case it missed.
-
-- Growing pains:  ZODB 3.2 had a bug wherein ``Transaction.begin()`` didn't
-  abort the current transaction if the only pending changes were in a
-  subtransaction.  In ZODB 3.3, it's intended that a transaction manager be
-  used to effect ``begin()`` (instead of invoking ``Transaction.begin()``),
-  and calling ``begin()`` on a transaction manager didn't have this old
-  bug.  However, ``Transaction.begin()`` still exists in 3.3, and it had a
-  worse bug:  it never aborted the transaction (not even if changes were
-  pending outside of subtransactions). ``Transaction.begin()`` has been
-  changed to abort the transaction. ``Transaction.begin()`` is also
-  deprecated.  Don't use it.  Use ``begin()`` on the relevant transaction
-  manager instead.  For example,
-
-      >>> import transaction
-      >>> txn = transaction.begin()  # start a txn using the default TM
-
-  if using the default ``ThreadTransactionManager`` (see news for 3.3a3
-  below). In 3.3, it's intended that a single ``Transaction`` object is
-  used for exactly one transaction.  So, unlike as in 3.2, when somtimes
-  ``Transaction`` objects were reused across transactions, but sometimes
-  weren't, when you do ``Transaction.begin()`` in 3.3 a brand new
-  transaction object is created.  That's why this use is deprecated.  Code
-  of the form:
-
-      >>> txn = transaction.get()
-      >>> ...
-      >>> txn.begin()
-      >>> ...
-      >>> txn.commit()
-
-  can't work as intended in 3.3, because ``txn`` is no longer the current
-  ``Transaction`` object the instant ``txn.begin()`` returns.
-
-BTrees
-------
-
-The BTrees __init__.py file is now just a comment.  It had been trying
-to set up support for (long gone) "int sets", and to import an old
-version of Zope's Interface package, which doesn't even ship with ZODB.
-The latter in particular created problems, at least clashing with
-PythonCAD's Interface package.
-
-POSException
-------------
-
-Collector #1488 (TemporaryStorage -- going backward in time).  This
-confusion was really due to that the detail on a ConflictError exception
-didn't make sense.  It called the current revision "was", and the old
-revision "now".  The detail is much more informative now.  For example,
-if the exception said::
-
-    ConflictError: database conflict error (oid 0xcb22,
-    serial was 0x03441422948b4399, now 0x034414228c3728d5)
-
-before, it now says::
-
-    ConflictError: database conflict error (oid 0xcb22,
-    serial this txn started with 0x034414228c3728d5 2002-04-14 20:50:32.863000,
-    serial currently committed 0x03441422948b4399 2002-04-14 20:50:34.815000)
-
-ConflictError
--------------
-
-The undocumented ``get_old_serial()`` and ``get_new_serial()`` methods
-were swapped (the first returned the new serial, and the second returned
-the old serial).
-
-Tools
------
-
-``FileStorage.FileIterator`` was confused about how to read a transaction's
-user and description fields, which caused several tools to display
-binary gibberish for these values.
-
-``ZODB.utils.oid_repr()`` changed to add a leading "0x", and to strip
-leading zeroes.  This is used, e.g., in the detail of a ``POSKeyError``
-exception, to identify the missing oid.  Before, the output was ambiguous.
-For example, oid 17 was displayed as 0000000000000011.  As a Python
-integer, that's octal 9.  Or was it meant to be decimal 11?  Or was it
-meant to be hex? Now it displays as 0x11.
-
-fsrefs.py:
-
-    When run with ``-v``, produced tracebacks for objects whose creation was
-    merely undone.  This was confusing.  Tracebacks are now produced only
-    if there's "a real" problem loading an oid.
-
-    If the current revision of object O refers to an object P whose
-    creation has been undone, this is now identified as a distinct case.
-
-    Captured and ignored most attempts to stop it via Ctrl+C.  Repaired.
-
-    Now makes two passes, so that an accurate report can be given of all
-    invalid references.
-
-``analyze.py`` produced spurious "len of unsized object" messages when
-finding a data record for an object uncreation or version abort.  These
-no longer appear.
-
-``fsdump.py``'s ``get_pickle_metadata()`` function (which is used by several
-tools) was confused about what to do when the ZODB pickle started with
-a pickle ``GLOBAL`` opcode.  It actually loaded the class then, which it
-intends never to do, leading to stray messages on stdout when the class
-wasn't available, and leading to a strange return value even when it was
-available (the repr of the type object was returned as "the module name",
-and an empty string was returned as "the class name").  This has been
-repaired.
-
-
-What's new in ZODB3 3.3 beta 2
-==============================
-Release date: 13-Aug-2004
-
-Transaction Managers
---------------------
-
-Zope3-dev Collector #139: Memory leak involving buckets and connections
-
-The transaction manager internals effectively made every Connection
-object immortal, except for those explicitly closed.  Since typical
-practice is not to close connections explicitly (and closing a DB
-happens not to close the connections to it -- although that may
-change), this caused massive memory leaks when many connections were
-opened.  The transaction manager internals were reworked to use weak
-references instead, so that connection memory (and other registered
-synch objects) now get cleaned up when nothing other than the
-transaction manager knows about them.
-
-Storages
---------
-
-Collector #1327: FileStorage init confused by time travel
-
-If the system clock "went backwards" a long time between the times a
-FileStorage was closed and reopened, new transaction ids could be
-smaller than transaction ids already in the storage, violating a
-key invariant.  Now transaction ids are guaranteed to be increasing
-even when this happens.  If time appears to have run backwards at all
-when a FileStorage is opened, a new message saying so is logged at
-warning level; if time appears to have run backwards at least 30
-minutes, the message is logged at critical level (and you should
-investigate to find and repair the true cause).
-
-Tools
------
-
-repozo.py:  Thanks to a suggestion from Toby Dickenson, backups
-(whether incremental or full) are first written to a temp file now,
-which is fsync'ed at the end, and only after that succeeds is the
-file renamed to YYYY-MM-DD-HH-MM-SS.ext form.  In case of a system
-crash during a repozo backup, this at least makes it much less
-likely that a backup file with incomplete or incorrect data will be
-left behind.
-
-fsrefs.py:  Fleshed out the module docstring, and repaired a bug
-wherein spurious error msgs could be produced after reporting a
-problem with an unloadable object.
-
-Test suite
-----------
-
-Collector #1397: testTimeStamp fails on FreeBSD
-
-    The BSD distributions are unique in that their mktime()
-    implementation usually ignores the input tm_isdst value.  Test
-    checkFullTimeStamp() was sensitive to this platform quirk.
-
-Reworked the way some of the ZEO tests use threads, so that unittest is
-more likely to notice the real cause of a failure (which usually occurs in
-a thread), and less likely to latch on to spurious problems resulting from
-the real failure.
-
-
-What's new in ZODB3 3.3 beta 1
-==============================
-Release date: 07-Jun-2004
-
-3.3b1 is the first ZODB release built using the new zpkg tools:
-
-    http://zope.org/Members/fdrake/zpkgtools/
-
-This appears to have worked very well.  The structure of the tarball
-release differs from previous releases because of it, and the set of
-installed files includes some that were not installed in previous
-releases.  That shouldn't create problems, so let us know if it does!
-We'll fine-tune this for the next release.
-
-BTrees
-------
-
-Fixed bug indexing BTreeItems objects with negative indexes.  This
-caused reverse iteration to return each item twice.  Thanks to Casey
-Duncan for the fix.
-
-ZODB
-----
-
-Methods removed from the database (ZODB.DB.DB) class:  cacheStatistics(),
-cacheMeanAge(), cacheMeanDeac(), and cacheMeanDeal().  These were
-undocumented, untested, and unused.  The first always returned an empty
-tuple, and the rest always returned None.
-
-When trying to do recovery to a time earlier than that of the most recent
-full backup, repozo.py failed to find the appropriate files, erroneously
-claiming "No files in repository before <specified time>".  This has
-been repaired.
-
-Collector #1330:  repozo.py -R can create corrupt .fs.
-When looking for the backup files needed to recreate a Data.fs file,
-repozo could (unintentionally) include its meta .dat files in the list,
-or random files of any kind created by the user in the backup directory.
-These would then get copied verbatim into the reconstructed file, filling
-parts with junk.  Repaired by filtering the file list to include only
-files with the data extensions repozo.py creates (.fs, .fsz, .deltafs,
-and .deltafsz).  Thanks to James Henderson for the diagnosis.
-
-fsrecover.py couldn't work, because it referenced attributes that no
-longer existed after the MVCC changes.  Repaired that, and added new
-tests to ensure it continues working.
-
-Collector #1309:  The reference counts reported by DB.cacheExtremeDetails()
-for ghosts were one too small.  Thanks to Dieter Maurer for the diagnosis.
-
-Collector #1208:  Infinite loop in cPickleCache.
-If a persistent object had a __del__ method (probably not a good idea
-regardless, but we don't prevent it) that referenced an attribute of
-self, the code to deactivate objects in the cache could get into an
-infinite loop:  ghostifying the object could lead to calling its __del__
-method, the latter would load the object into cache again to
-satsify the attribute reference, the cache would again decide that
-the object should be ghostified, and so on.  The infinite loop no longer
-occurs, but note that objects of this kind still aren't sensible (they're
-effectively immortal).  Thanks to Toby Dickenson for suggesting a nice
-cure.
-
-
-What's new in ZODB3 3.3 alpha 3
-===============================
-Release date: 16-Apr-2004
-
-transaction
------------
-
-There is a new transaction package, which provides new interfaces for
-application code and for the interaction between transactions and
-resource managers.
-
-The top-level transaction package has functions ``commit()``, ``abort()``,
-``get()``, and ``begin()``.  They should be used instead of the magic
-``get_transaction()`` builtin, which will be deprecated.  For example:
-
-    >>> get_transaction().commit()
-
-should now be written as
-
-    >>> import transaction
-    >>> transaction.commit()
-
-The new API provides explicit transaction manager objects.  A transaction
-manager (TM) is responsible for associating resource managers with a
-"current" transaction.  The default TM, implemented by class
-``ThreadedTransactionManager``, assigns each thread its own current
-transaction.  This default TM is available as ``transaction.manager``.  The
-``TransactionManager`` class assigns all threads to the same transaction,
-and is an explicit replacement for the ``Connection.setLocalTransaction()``
-method:
-
-A transaction manager instance can be passed as the transaction_manager
-argument to ``DB.open()``.  If you do, the connection will use the specified
-transaction manager instead of the default TM.  The current transaction is
-obtained by calling ``get()`` on a TM. For example:
-
-    >>> tm = transaction.TransactionManager()
-    >>> cn = db.open(transaction_manager=tm)
-    [...]
-    >>> tm.get().commit()
-
-The ``setLocalTransaction()`` and ``getTransaction()`` methods of
-Connection are deprecated.  Use an explicit TM passed via
-``transaction_manager=`` to ``DB.open()`` instead.  The
-``setLocalTransaction()`` method still works, but it returns a TM instead of
-a Transaction.
-
-A TM creates Transaction objects, which are used for exactly one
-transaction.  Transaction objects still have ``commit()``, ``abort()``,
-``note()``, ``setUser()``, and ``setExtendedInfo()`` methods.
-
-Resource managers, e.g. Connection or RDB adapter, should use a
-Transaction's ``join()`` method instead of its ``register()`` method.  An
-object that calls ``join()`` manages its own resources.  An object that
-calls ``register()`` expects the TM to manage the objects.
-
-Data managers written against the ZODB 4 transaction API are now
-supported in ZODB 3.
-
 persistent
 ----------
 
-A database can now contain persistent weak references.  An object that
-is only reachable from persistent weak references will be removed by
-pack().
+- (3.7a1) Suppressed warnings about signedness of characters when
+  compiling under GCC 4.0.x.  See http://www.zope.org/Collectors/Zope/2027.
 
-The persistence API now distinguishes between deactivation and
-invalidation.  This change is intended to support objects that can't
-be ghosts, like persistent classes.  Deactivation occurs when a user
-calls _p_deactivate() or when the cache evicts objects because it is
-full.  Invalidation occurs when a transaction updates the object.  An
-object that can't be a ghost must load new state when it is
-invalidated, but can ignore deactivation.
-
-Persistent objects can implement a __getnewargs__() method that will
-be used to provide arguments that should be passed to __new__() when
-instances (including ghosts) are created.  An object that implements
-__getnewargs__() must be loaded from storage even to create a ghost.
-
-There is new support for writing hooks like __getattr__ and
-__getattribute__.  The new hooks require that user code call special
-persistence methods like _p_getattr() inside their hook.  See the ZODB
-programming guide for details.
-
-The format of serialized persistent references has changed; that is,
-the on-disk format for references has changed.  The old format is
-still supported, but earlier versions of ZODB will not be able to read
-the new format.
-
-ZODB
-----
-
-Closing a ZODB Connection while it is registered with a transaction,
-e.g. has pending modifications, will raise a ConnnectionStateError.
-Trying to load objects from or store objects to a closed connection
-will also raise a ConnnectionStateError.
-
-ZODB connections are synchronized on commit, even when they didn't
-modify objects.  This feature assumes that the thread that opened the
-connection is also the thread that uses it.  If not, this feature will
-cause problems.  It can be disabled by passing synch=False to open().
-
-New broken object support.
-
-New add() method on Connection.  User code should not assign the
-_p_jar attribute of a new persistent object directly; a deprecation
-warning is issued in this case.
-
-Added a get() method to Connection as a preferred synonym for
-__getitem__().
-
-Several methods and/or specific optional arguments of methods have
-been deprecated.  The cache_deactivate_after argument used by DB() and
-Connection() is deprecated.  The DB methods getCacheDeactivateAfter(),
-getVersionCacheDeactivateAfter(), setCacheDeactivateAfter(), and
-setVersionCacheDeactivateAfter() are also deprecated.
-
-The old-style undo() method was removed from the storage API, and
-transactionalUndo() was renamed to undo().
-
-The BDBStorages are no longer distributed with ZODB.
-
-Fixed a serious bug in the new pack implementation.  If pack was
-called on the storage and passed a time earlier than a previous pack
-time, data could be lost.  In other words, if there are any two pack
-calls, where the time argument passed to the second call was earlier
-than the first call, data loss could occur.  The bug was fixed by
-causing the second call to raise a StorageError before performing any
-work.
-
-Fixed a rare bug in pack:  if a pack started during a small window of
-time near the end of a concurrent transaction's commit, it was possible
-for the pack attempt to raise a spurious
-
-     CorruptedError: ... transaction with checkpoint flag set
-
-exception.  This did no damage to the database, or to the transaction
-in progress, but no pack was performed then.
-
-By popular demand, FileStorage.pack() no longer propagates a
-
-    FileStorageError:  The database has already been packed to a
-    later time or no changes have been made since the last pack
-
-exception.  Instead that message is logged (at INFO level), and
-the pack attempt simply returns then (no pack is performed).
-
-ZEO
----
-
-Fixed a bug that prevented the -m / --monitor argument from working.
-
-zdaemon
--------
-
-Added a -m / --mask option that controls the umask of the subprocess.
-
-zLOG
-----
-
-The zLOG backend has been removed.  zLOG is now just a facade over the
-standard Python logging package.  Environment variables like
-STUPID_LOG_FILE are no longer honored.  To configure logging, you need
-to follow the directions in the logging package documentation.  The
-process is currently more complicated than configured zLOG.  See
-test.py for an example.
-
-ZConfig
--------
-
-This release of ZODB contains ZConfig 2.1.
-
-More documentation has been written.
-
-Make sure keys specified as attributes of the <default> element are
-converted by the appropriate key type, and are re-checked for derived
-sections.
-
-Refactored the ZConfig.components.logger schema components so that a
-schema can import just one of the "eventlog" or "logger" sections if
-desired.  This can be helpful to avoid naming conflicts.
-
-Added a reopen() method to the logger factories.
-
-Always use an absolute pathname when opening a FileHandler.
-
-
-Miscellaneous
--------------
-
-The layout of the ZODB source release has changed.  All the source
-code is contained in a src subdirectory.  The primary motivation for
-this change was to avoid confusion caused by installing ZODB and then
-testing it interactively from the source directory; the interpreter
-would find the uncompiled ZODB package in the source directory and
-report an import error.
-
-A reference-counting bug was fixed, in the logic calling a modified
-persistent object's data manager's register() method.  The primary symptom
-was rare assertion failures in Python's cyclic garbage collection.
-
-The Connection class's onCommitAction() method was removed.
-
-Some of the doc strings in ZODB are now written for processing by
-epydoc.
-
-Several new test suites were written using doctest instead of the
-standard unittest TestCase framework.
-
-MappingStorage now implements getTid().
-
-ThreadedAsync: Provide a way to shutdown the servers using an exit
-status.
-
-The mkzeoinstance script looks for a ZODB installation, not a Zope
-installation.  The received wisdom is that running a ZEO server
-without access to the appserver code avoids many mysterious problems.
-
-
-What's new in ZODB3 3.3 alpha 2
-===============================
-Release date: 06-Jan-2004
-
-This release contains a major overhaul of the persistence machinery,
-including some user-visible changes.  The Persistent base class is now
-a new-style class instead of an ExtensionClass.  The change enables
-the use of features like properties with persistent object classes.
-The Persistent base class is now contained in the persistent package.
-
-The Persistence package is included for backwards compatibility.  The
-Persistence package is used by Zope to provide special
-ExtensionClass-compatibility features like a non-C3 MRO and an __of__
-method.  ExtensionClass is not included with this release of ZODB3.
-If you use the Persistence package, it will print a warning and import
-Persistent from persistent.
-
-In short, the new persistent package is recommended for non-Zope
-applications.  The following dotted class names are now preferred over
-earlier names:
-
-- persistent.Persistent
-- persistent.list.PersistentList
-- persistent.mapping.PersistentMapping
-- persistent.TimeStamp
-
-The in-memory, per-connection object cache (pickle cache) was changed
-to participate in garbage collection.  This should reduce the number
-of memory leaks, although we are still tracking a few problems.
-
-Multi-version concurrency control
----------------------------------
-
-ZODB now supports multi-version concurrency control (MVCC) for
-storages that support multiple revisions.  FileStorage and
-BDBFullStorage both support MVCC.  In short, MVCC means that read
-conflicts should almost never occur.  When an object is modified in
-one transaction, other concurrent transactions read old revisions of
-the object to preserve consistency.  In earlier versions of ZODB, any
-access of the modified object would raise a ReadConflictError.
-
-The ZODB internals changed significantly to accommodate MVCC.  There
-are relatively few user visible changes, aside from the lack of read
-conflicts.  It is possible to disable the MVCC feature using the mvcc
-keyword argument to the DB open() method, ex.: db.open(mvcc=False).
-
-ZEO
----
-
-Changed the ZEO server and control process to work with a single
-configuration file; this is now the default way to configure these
-processes.  (It's still possible to use separate configuration files.)
-The ZEO configuration file can now include a "runner" section used by
-the control process and ignored by the ZEO server process itself.  If
-present, the control process can use the same configuration file.
-
-Fixed a performance problem in the logging code for the ZEO protocol.
-The logging code could call repr() on arbitrarily long lists, even
-though it only logged the first 60 bytes; worse, it called repr() even
-if logging was currently disabled.  Fixed to call repr() on individual
-elements until the limit is reached.
-
-Fixed a bug in zrpc (when using authentication) where the MAC header
-wasn't being read for large messages, generating errors while unpickling
-commands sent over the wire. Also fixed the zeopasswd.py script, added
-testcases and provided a more complete commandline interface.
-
-Fixed a misuse of the _map variable in zrpc Connectio objects, which
-are also asyncore.dispatcher objects.  This allows ZEO to work with
-CVS Python (2.4). _map is used to indicate whether the dispatcher
-users the default socket_map or a custom socket_map.  A recent change
-to asyncore caused it to use _map in its add_channel() and
-del_channel() methods, which presumes to be a bug fix (may get ported
-to 2.3).  That causes our dubious use of _map to be a problem, because
-we also put the Connections in the global socket_map.  The new
-asyncore won't remove it from the global socket map, because it has a
-custom _map.
-
-The prefix used for log messages from runzeo.py was changed from
-RUNSVR to RUNZEO.
-
-Miscellaneous
--------------
-
-ReadConflictError objects now have an ignore() method.  Normally, a
-transaction that causes a read conflict can't be committed.  If the
-exception is caught and its ignore() method called, the transaction
-can be committed.  Application code may need this in advanced
-applications.
-
-
-What's new in ZODB3 3.3 alpha 1
-===============================
-Release date: 17-Jul-2003
-
-New features of Persistence
----------------------------
-
-The Persistent base class is a regular Python type implemented in C.
-It should be possible to create new-style classes that inherit from
-Persistent, and, thus, use all the new Python features introduced in
-Python 2.2 and 2.3.
-
-The __changed__() method on Persistent objects is no longer supported.
-
-New features in BTrees
-----------------------
-
-BTree, Bucket, TreeSet and Set objects are now iterable objects, playing
-nicely with the iteration protocol introduced in Python 2.2, and can
-be used in any context that accepts an iterable object.  As for Python
-dicts, the iterator constructed for BTrees and Buckets iterates
-over the keys.
-
->>> from BTrees.OOBTree import OOBTree
->>> b = OOBTree({"one": 1, "two": 2, "three": 3, "four": 4})
->>> for key in b: # iterates over the keys
-...    print key
-four
-one
-three
-two
->>> list(enumerate(b))
-[(0, 'four'), (1, 'one'), (2, 'three'), (3, 'two')]
->>> i = iter(b)
->>> i.next()
-'four'
->>> i.next()
-'one'
->>> i.next()
-'three'
->>> i.next()
-'two'
->>>
-
-As for Python dicts in 2.2, BTree and Bucket objects have new
-.iterkeys(), .iteritems(), and .itervalues() methods.  TreeSet and Set
-objects have a new .iterkeys() method.  Unlike as for Python dicts,
-these new methods accept optional min and max arguments to effect
-range searches.  While Bucket.keys() produces a list, Bucket.iterkeys()
-produces an iterator, and similarly for Bucket values() versus
-itervalues(), Bucket items() versus iteritems(), and Set keys() versus
-iterkeys().  The iter{keys,values,items} methods of BTrees and the
-iterkeys() method of Treesets also produce iterators, while their
-keys() (etc) methods continue to produce BTreeItems objects (a form of
-"lazy" iterator that predates Python 2.2's iteration protocol).
-
->>> sum(b.itervalues())
-10
->>> zip(b.itervalues(), b.iterkeys())
-[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
->>>
-
-BTree, Bucket, TreeSet and Set objects also implement the __contains__
-method new in Python 2.2, which means that testing for key membership
-can be done directly now via the "in" and "not in" operators:
-
->>> "won" in b
-False
->>> "won" not in b
-True
->>> "one" in b
-True
->>>
-
-All old and new range-search methods now accept keyword arguments,
-and new optional excludemin and excludemax keyword arguments.  The
-new keyword arguments allow doing a range search that's exclusive
-at one or both ends (doesn't include min, and/or doesn't include
-max).
-
->>> list(b.keys())
-['four', 'one', 'three', 'two']
->>> list(b.keys(max='three'))
-['four', 'one', 'three']
->>> list(b.keys(max='three', excludemax=True))
-['four', 'one']
->>>
-
-Other improvements
+After Commit hooks
 ------------------
 
-The exceptions generated by write conflicts now contain the name of
-the conflicted object's class.  This feature requires support for the
-storage.  All the standard storages support it.
-
-What's new in ZODB3 3.2
-========================
-Release date: 08-Oct-2003
-
-Nothing has changed since release candidate 1.
-
-What's new in ZODB3 3.2 release candidate 1
-===========================================
-Release date: 01-Oct-2003
-
-Added a summary to the Doc directory.  There are several new documents
-in the 3.2 release, including "Using zdctl and zdrun to manage server
-processes" and "Running a ZEO Server HOWTO."
-
-Fixed ZEO's protocol negotiation mechanism so that a client ZODB 3.1
-can talk to a ZODB 3.2 server.
-
-Fixed a memory leak in the ZEO server.  The server was leaking a few
-KB of memory per connection.
-
-Fixed a memory leak in the ZODB object cache (cPickleCache).  The
-cache did not release two references to its Connection, causing a
-large cycle of objects to leak when a database was closed.
-
-Fixed a bug in the ZEO code that caused it to leak socket objects on
-Windows.  Specifically, fix the trigger mechanism so that both sockets
-created for a trigger are closed.
-
-Fixed a bug in the ZEO storage server that caused it to leave temp
-files behind.  The CommitLog class contains a temp file, but it was
-not closing the file.
-
-Changed the order of setuid() and setgid() calls in zdrun, so that
-setgid() is called first.
-
-Added a timeout to the ZEO test suite that prevents hangs.  The test
-suite creates ZEO servers with randomly assigned ports.  If the port
-happens to be in use, the test suite would hang because the ZEO client
-would never stop trying to connect.  The fix will cause the test to
-fail after a minute, but should prevent the test runner from hanging.
-
-The logging package was updated to include the latest version of the
-logging package from Python CVS.  Note that this package is only
-installed for Python 2.2.  In later versions of Python, it is
-available in the Python standard library.
-
-The ZEO1 directory was removed from the source distribution.  ZEO1 is
-not supported, and we never intended to include it in the release.
-
-What's new in ZODB3 3.2 beta 3
-==============================
-Release date: 23-Sep-2003
-
-Note: The changes listed for this release include changes also made in
-ZODB 3.1.x releases and ported to the 3.2 release.
-
-This version of ZODB 3.2 is not compatible with Python 2.1.  Early
-versions were explicitly designed to be compatible with Zope 2.6.
-That plan has been dropped, because Zope 2.7 is already in beta
-release.
-
-Several of the classes in ZEO and ZODB now inherit from object, making
-them new-style classes.  The primary motivation for the change was to
-make it easier to debug memory leaks.  We don't expect any behavior to
-change as a result.
-
-A new feature to allow removal of connection pools for versions was
-ported from Zope 2.6.  This feature is needed by Zope to avoid denial
-of service attacks that allow a client to create an arbitrary number
-of version pools.
-
-Fixed several critical ZEO bugs.
-
-- If several client transactions were blocked waiting for the storage
-  and one of the blocked clients disconnected, the server would
-  attempt to restart one of the other waiting clients.  Since the
-  disconnected client did not have the storage lock, this could lead
-  to deadlock.  It could also cause the assertion "self._client is
-  None" to fail.
-
-- If a storage server fails or times out between the vote and the
-  finish, the ZEO cache could get populated with objects that didn't
-  make it to the storage server.
-
-- If a client loses its connection to the server near the end of a
-  transaction, it is now guaranteed to get a ClientDisconnected error
-  even if it reconnects before the transaction finishes.  This is
-  necessary because the server will always abort the transaction.
-  In some cases, the client would never see an error for the aborted
-  transaction.
-
-- In tpc_finish(), reordered the calls so that the server's tpc_finish()
-  is called (and must succeed) before we update the ZEO client cache.
-
-- The storage name is now prepended to the sort key, to ensure a
-  unique global sort order if storages are named uniquely.  This
-  can prevent deadlock in some unusual cases.
-
-Fixed several serious flaws in the implementation of the ZEO
-authentication protocol.
-
-- The smac layer would accept a message without a MAC even after the
-  session key was established.
-
-- The client never initialized its session key, so it never checked
-  incoming messages or created MACs for outgoing messags.
-
-- The smac layer used a single HMAC instance for sending and receiving
-  messages.  This approach could only work if client and server were
-  guaranteed to process all messages in the same total order, which
-  could only happen in simple scenarios like unit tests.
-
-Fixed a bug in ExtensionClass when comparing ExtensionClass instances.
-The code could raise RuntimeWarning under Python 2.3, and produce
-incorrect results on 64-bit platforms.
-
-Fixed bug in BDBStorage that could lead to DBRunRecoveryErrors when a
-transaction was aborted after performing operations like commit
-version or undo that create new references to existing pickles.
-
-Fixed a bug in Connection.py that caused it to fail with an
-AttributeError if close() was called after the database was closed.
-
-The test suite leaves fewer log files behind, although it still leaves
-a lot of junk.  The test.py script puts each tests temp files in a
-separate directory, so it is easier to see which tests are causing
-problems.  Unfortunately, it is still to tedious to figure out why the
-identified tests are leaving files behind.
-
-This release contains the latest and greatest version of the
-BDBStorage.  This storage has still not seen testing in a production
-environment, but it represents the current best design and most recent
-code culled from various branches where development has occurred.
-
-The Tools directory contains a number of small improvements, a few new
-tools, and README.txt that catalogs the tools.  Many of the tools are
-installed by setup.py; those scripts will now have a #! line set
-automatically on Unix.
-
-Fixed bugs in Tools/repozo.py, including a timing-dependent one that
-could cause the following invocation of repozo to do a full backup when
-an incremental backup would have sufficed.
-
-A pair of new scripts from Jim Fulton can be used to synthesize
-workloads and measure ZEO performance:  see zodbload.py and
-zeoserverlog.py in the Tools directory.  Note that these require
-Zope.
-
-Tools/checkbtrees.py was strengthened in two ways:
-
-- In addition to running the _check() method on each BTree B found,
-  BTrees.check.check(B) is also run.  The check() function was written
-  after checkbtrees.py, and identifies kinds of damage B._check()
-  cannot find.
-
-- Cycles in the object graph no longer lead to unbounded output.
-  Note that preventing this requires remembering the oid of each
-  persistent object found, which increases the memory needed by the
-  script.
-
-What's new in ZODB3 3.2 beta 2
-==============================
-Release date: 16-Jun-2003
-
-Fixed critical race conditions in ZEO's cache consistency code that
-could cause invalidations to be lost or stale data to be written to
-the cache.  These bugs can lead to data loss or data corruption.
-These bugs are relatively unlikely to be provoked in sites with few
-conflicts, but the possibility of failure existed any time an object
-was loaded and stored concurrently.
-
-Fixed a bug in conflict resolution that failed to ghostify an object
-if it was involved in a conflict.  (This code may be redundant, but it
-has been fixed regardless.)
-
-The ZEO server was fixed so that it does not perform any I/O until all
-of a transactions' invalidations are queued.  If it performs I/O in the
-middle of sending invalidations, it would be possible to overlap a
-load from a client with the invalidation being sent to it.
-
-The ZEO cache now handles invalidations atomically.  This is the same
-sort of bug that is described in the 3.1.2b1 section below, but it
-affects the ZEO cache.
-
-Fixed several serious bugs in fsrecover that caused it to fail
-catastrophically in certain cases because it thought it had found a
-checkpoint (status "c") record when it was in the middle of the file.
-
-Two new features snuck into this beta release.
-
-The ZODB.transact module provides a helper function that converts a
-regular function or method into a transactional one.
-
-The ZEO client cache now supports Adaptable Persistence (APE).  The
-cache used to expect that all OIDs were eight bytes long.
-
-What's new in ZODB3 3.2 beta 1
-==============================
-Release date: 30-May-2003
-
-ZODB
-----
-
-Invalidations are now processed atomically.  Each transaction will see
-all the changes caused by an earlier transaction or none of them.
-Before this patch, it was possible for a transaction to see invalid
-data because it saw only a subset of the invalidations.  This is the
-most likely cause of reported BTrees corruption, where keys were
-stored in the wrong bucket.  When a BTree bucket splits, the bucket
-and the bucket's parent are both modified.  If a transaction sees the
-invalidation for the bucket but not the parent, the BTree in memory
-will be internally inconsistent and keys can be put in the wrong
-bucket.  The atomic invalidation fix prevents this problem.
-
-A number of minor reference count fixes in the object cache were
-fixed.  That's the cPickleCache.c file.
-
-It was possible for a transaction that failed in tpc_finish() to lose
-the traceback that caused the failure.  The transaction code was fixed
-to report the original error as well as any errors that occur while
-trying to recover from the original error.
-
-The "other" argument to copyTransactionsFrom() only needs to have an
-.iterator() method.  For convenience, change FileStorage's and
-BDBFullStorage's iterator to have this method, which just returns
-self.
-
-Mount points are now visible from mounted objects.
-
-Fixed memory leak involving database connections and caches.  When a
-connection or database was closed, the cache and database leaked,
-because of a circular reference involving the cache.  Fixed the cache
-to explicitly clear out its contents when its connection is closed.
-
-The ZODB cache has fewer methods.  It used to expose methods that
-could mutate the dictionary, which allowed users to violate internal
-invariants.
-
-ZConfig
--------
-
-It is now possible to configure ZODB databases and storages and ZEO
-servers using ZConfig.
-
-ZEO & zdaemon
--------------
-
-ZEO now supports authenticated client connections.  The default
-authentication protocol uses a hash-based challenge-response protocol
-to prove identity and establish a session key for message
-authentication.  The architecture is pluggable to allow third-parties
-to developer better authentication protocols.
-
-There is a new HOWTO for running a ZEO server.  The draft in this
-release is incomplete, but provides more guidance than previous
-releases.  See the file Doc/ZEO/howto.txt.
-
-
-The ZEO storage server's transaction timeout feature was refactored
-and made slightly more rebust.
-
-A new ZEO utility script, ZEO/mkzeoinst.py, was added.  This creates a
-standard directory structure and writes a configuration file with
-mostly default values, and a bootstrap script that can be used to
-manage and monitor the server using zdctl.py (see below).
-
-Much work was done to improve zdaemon's zdctl.py and zdrun.py scripts.
-(In the alpha 1 release, zdrun.py was called zdaemon.py, but
-installing it in <prefix>/bin caused much breakage due to the name
-conflict with the zdaemon package.)  Together with the new
-mkzeoinst.py script, this makes controlling a ZEO server a breeze.
-
-A ZEO client will not read from its cache during cache verification.
-This fix was necessary to prevent the client from reading inconsistent
-data.
-
-The isReadOnly() method of a ZEO client was fixed to return the false
-when the client is connected to a read-only fallback server.
-
-The sync() method of ClientStorage and the pending() method of a zrpc
-connection now do both input and output.
-
-The short_repr() function used to generate log messages was fixed so
-that it does not blow up creating a repr of very long tuples.
-
-Storages
---------
-
-FileStorage has a new pack() implementation that fixes several
-reported problems that could lead to data loss.
-
-Two small bugs were fixed in DemoStorage.  undoLog() did not handle
-its arguments correctly and pack() could accidentally delete objects
-created in versions.
-
-Fixed trivial bug in fsrecover that prevented it from working at all.
-
-FileStorage will use fsync() on Windows starting with Python 2.2.3.
-
-FileStorage's commit version was fixed.  It used to stop after the
-first object, leaving all the other objects in the version.
-
-BTrees
-------
-
-Trying to store an object of a non-integer type into an IIBTree
-or OIBTree could leave the bucket in a variety of insane states.  For
-example, trying
-
-    b[obj] = "I'm a string, not an integer"
-
-where b is an OIBTree.  This manifested as a refcount leak in the test
-suite, but could have been much worse (most likely in real life is that
-a seemingly arbitrary existing key would "go missing").
-
-When deleting the first child of a BTree node with more than one
-child, a reference to the second child leaked.  This could cause
-the entire bucket chain to leak (not be collected as garbage
-despite not being referenced anymore).
-
-Other minor BTree leak scenarios were also fixed.
-
-Tools
------
-
-New tool zeoqueue.py for parsing ZEO log files, looking for blocked
-transactions.
-
-New tool repozo.py (originally by Anthony Baxter) for performing
-incremental backups of Data.fs files.
-
-The fsrecover.py script now does a better job of recovering from
-errors the occur in the middle of a transaction record.  Fixed several
-bugs that caused partial or total failures in earlier versions.
-
-
-What's new in ZODB3 3.2 alpha 1
-===============================
-Release date: 17-Jan-2003
-
-Most of the changes in this release are performance and stability
-improvements to ZEO.  A major packaging change is that there won't be
-a separate ZEO release.  The new ZConfig is a noteworthy addtion (see
-below).
-
-ZODB
-----
-
-An experimental new transaction API was added.  The Connection class
-has a new method, setLocalTransaction().  ZODB applications can call
-this method to bind transactions to connections rather than threads.
-This is especially useful for GUI applications, which often have only
-one thread but multiple independent activities within that thread
-(generally one per window).  Thanks to Christian Reis for championing
-this feature.
-
-Applications that take advantage of this feature should not use the
-get_transaction() function.  Until now, ZODB itself sometimes assumed
-get_transaction() was the only way to get the transaction.  Minor
-corrections have been added.  The ZODB test suite, on the other hand,
-can continue to use get_transaction(), since it is free to assume that
-transactions are bound to threads.
-
-ZEO
----
-
-There is a new recommended script for starting a storage server.  We
-recommend using ZEO/runzeo.py instead of ZEO/start.py.  The start.py
-script is still available in this release, but it will no longer be
-maintained and will eventually be removed.
-
-There is a new zdaemon implementation.  This version is a separate
-script that runs an arbitrary daemon.  To run the ZEO server as a
-daemon, you would run "zdrun.py runzeo.py".  There is also a simple
-shell, zdctl.py, that can be used to manage a daemon.  Try
-"zdctl.py -p runzeo.py".
-
-There is a new version of the ZEO protocol in this release and a first
-stab at protocol negotiation.  (It's a first stab because the protocol
-checking supporting in ZODB 3.1 was too primitive to support anything
-better.)  A ZODB 3.2 ZEO client can talk to an old server, but a ZODB
-3.2 server can't talk to an old client.  It's safe to upgrade all the
-clients first and upgrade the server last.  The ZEO client cache
-format changed, so you'll need to delete persistent caches before
-restarting clients.
-
-The ZEO cache verification protocol was revised to require many fewer
-messages in cases where a client or server restarts quickly.
-
-The performance of full cache verification has improved dramatically.
-Measurements from Jim were somewhere in 2x-5x.  The
-implementation was fixed to use the very-fast getSerial() method on
-the storage instead of the comparatively slow load().
-
-The ZEO server has an optional timeout feature that will abort a
-connection that does not commit within a certain amount of time.  The
-timeout works by closing the socket the client is using, causing both
-client and server to abort the transaction and continue.  This is a
-drastic step, but can be useful to prevent a hung client or other bug
-from blocking a server indefinitely.
-
-A bug was fixed in the ZEO protocol that allowed clients to read stale
-cache data while cache verification was being performed.  The fixed
-version prevents the client from using the storage until after
-verification completes.
-
-The ZEO server has an experimental monitoring interface that reports
-usage statistics for the storage server including number of connected
-clients and number of transactions active and committed.  It can be
-enabled by passing the -m flag to runsvr.py.
-
-The ZEO ClientStorage no longer supports the environment variables
-CLIENT_HOME, INSTANCE_HOME, or ZEO_CLIENT.
-
-The ZEO1 package is still included with this release, but there is no
-longer an option to install it.
-
-BTrees
-------
-
-The BTrees package now has a check module that inspects a BTree to
-check internal invariants.  Bugs in older versions of the code code
-leave a BTree in an inconsistent state.  Calling BTrees.check.check()
-on a BTree object should verify its consistency.  (See the NEWS
-section for 3.1 beta 1 below to for the old BTrees bugs.)
-
-Fixed a rare conflict resolution problem in the BTrees that could
-cause an segfault when the conflict resolution resulted in any
-empty bucket.
-
-Installation
-------------
-
-The distutils setup now installs several Python scripts.  The
-runzeo.py and zdrun.py scripts mentioned above and several fsXXX.py
-scripts from the Tools directory.
-
-The test.py script does not run all the ZEO tests by default, because
-the ZEO tests take a long time to run.  Use --all to run all the
-tests.  Otherwise a subset of the tests, mostly using MappingStorage,
-are run.
-
-Storages
---------
-
-There are two new storages based on Sleepycat's BerkeleyDB in the
-BDBStorage package.  Barry will have to write more here, because I
-don't know how different they are from the old bsddb3Storage
-storages.  See Doc/BDBStorage.txt for more information.
-
-It now takes less time to open an existing FileStorage.  The
-FileStorage uses a BTree-based index that is faster to pickle and
-unpickle.  It also saves the index periodically so that subsequent
-opens will go fast even if the storage was not closed cleanly.
-
-Misc
-----
-
-The new ZConfig package, which will be used by Zope and ZODB, is
-included.  ZConfig provides a configuration syntax, similar to
-Apache's syntax.  The package can be used to configure the ZEO server
-and ZODB databases.  See the module ZODB.config for functions to open
-the database from configuration.  See ZConfig/doc for more info.
-
-The zLOG package now uses the logging package by Vinay Sajip, which
-will be included in Python 2.3.
-
-The Sync extension was removed from ExtensionClass, because it was not
-used by ZODB.
-
-What's new in ZODB3 3.1.4?
-==========================
-Release date: 11-Sep-2003
-
-A new feature to allow removal of connection pools for versions was
-ported from Zope 2.6.  This feature is needed by Zope to avoid denial
-of service attacks that allow a client to create an arbitrary number
-of version pools.
-
-A pair of new scripts from Jim Fulton can be used to synthesize
-workloads and measure ZEO performance:  see zodbload.py and
-zeoserverlog.py in the Tools directory.  Note that these require
-Zope.
-
-Tools/checkbtrees.py was strengthened in two ways:
-
-- In addition to running the _check() method on each BTree B found,
-  BTrees.check.check(B) is also run.  The check() function was written
-  after checkbtrees.py, and identifies kinds of damage B._check()
-  cannot find.
-
-- Cycles in the object graph no longer lead to unbounded output.
-  Note that preventing this requires remembering the oid of each
-  persistent object found, which increases the memory needed by the
-  script.
-
-What's new in ZODB3 3.1.3?
-==========================
-Release date: 18-Aug-2003
-
-Fixed several critical ZEO bugs.
-
-- If a storage server fails or times out between the vote and the
-  finish, the ZEO cache could get populated with objects that didn't
-  make it to the storage server.
-
-- If a client loses its connection to the server near the end of a
-  transaction, it is now guaranteed to get a ClientDisconnected error
-  even if it reconnects before the transaction finishes.  This is
-  necessary because the server will always abort the transaction.
-  In some cases, the client would never see an error for the aborted
-  transaction.
-
-- In tpc_finish(), reordered the calls so that the server's tpc_finish()
-  is called (and must succeed) before we update the ZEO client cache.
-
-- The storage name is now prepended to the sort key, to ensure a
-  unique global sort order if storages are named uniquely.  This
-  can prevent deadlock in some unusual cases.
-
-A variety of fixes and improvements to Berkeley storage (aka BDBStorage)
-were back-ported from ZODB 4.  This release now contains the most
-current version of the Berkeley storage code.  Many tests have been
-back-ported, but not all.
-
-Modified the Windows tests to wait longer at the end of ZEO tests for
-the server to shut down.  Before Python 2.3, there is no waitpid() on
-Windows, and, thus, no way to know if the server has shut down.  The
-change makes the Windows ZEO tests much less likely to fail or hang,
-at the cost of increasing the time needed to run the tests.
-
-Fixed a bug in ExtensionClass when comparing ExtensionClass instances.
-The code could raise RuntimeWarning under Python 2.3, and produce
-incorrect results on 64-bit platforms.
-
-Fixed bugs in Tools/repozo.py, including a timing-dependent one that
-could cause the following invocation of repozo to do a full backup when
-an incremental backup would have sufficed.
-
-Added Tools/README.txt that explains what each of the scripts in the
-Tools directory does.
-
-There were many small changes and improvements to the test suite.
-
-What's new in ZODB3 3.1.2 final?
-================================
-
-Fixed bug in FileStorage pack that caused it to fail if it encountered
-an old undo record (status "u").
-
-Fixed several bugs in FileStorage pack that could cause OverflowErrors
-for storages > 2 GB.
-
-Fixed memory leak in TimeStamp.laterThan() that only occurred when it
-had to create a new TimeStamp.
-
-Fixed two BTree bugs that were fixed on the head a while ago:
-
-   - bug in fsBTree that would cause byValue searches to end early.
-     (fsBTrees are never used this way, but it was still a bug.)
-
-   -  bug that lead to segfault if BTree was mutated via deletion
-      while it was being iterated over.
-
-What's new in ZODB3 3.1.2 beta 2?
-=================================
-
-Fixed critical race conditions in ZEO's cache consistency code that
-could cause invalidations to be lost or stale data to be written to
-the cache.  These bugs can lead to data loss or data corruption.
-These bugs are relatively unlikely to be provoked in sites with few
-conflicts, but the possibility of failure existed any time an object
-was loaded and stored concurrently.
-
-Fixed a bug in conflict resolution that failed to ghostify an object
-if it was involved in a conflict.  (This code may be redundant, but it
-has been fixed regardless.)
-
-The ZEO server was fixed so that it does not perform any I/O until all
-of a transactions' invalidations are queued.  If it performs I/O in the
-middle of sending invalidations, it would be possible to overlap a
-load from a client with the invalidation being sent to it.
-
-The ZEO cache now handles invalidations atomically.  This is the same
-sort of bug that is described in the 3.1.2b1 section below, but it
-affects the ZEO cache.
-
-Fixed several serious bugs in fsrecover that caused it to fail
-catastrophically in certain cases because it thought it had found a
-checkpoint (status "c") record when it was in the middle of the file.
-
-What's new in ZODB3 3.1.2 beta 1?
-=================================
-
-ZODB
-----
-
-Invalidations are now processed atomically.  Each transaction will see
-all the changes caused by an earlier transaction or none of them.
-Before this patch, it was possible for a transaction to see invalid
-data because it saw only a subset of the invalidations.  This is the
-most likely cause of reported BTrees corruption, where keys were
-stored in the wrong bucket.  When a BTree bucket splits, the bucket
-and the bucket's parent are both modified.  If a transaction sees the
-invalidation for the bucket but not the parent, the BTree in memory
-will be internally inconsistent and keys can be put in the wrong
-bucket.  The atomic invalidation fix prevents this problem.
-
-A number of minor reference count fixes in the object cache were
-fixed.  That's the cPickleCache.c file.
-
-It was possible for a transaction that failed in tpc_finish() to lose
-the traceback that caused the failure.  The transaction code was fixed
-to report the original error as well as any errors that occur while
-trying to recover from the original error.
-
-ZEO
----
-
-A ZEO client will not read from its cache during cache verification.
-This fix was necessary to prevent the client from reading inconsistent
-data.
-
-The isReadOnly() method of a ZEO client was fixed to return the false
-when the client is connected to a read-only fallback server.
-
-The sync() method of ClientStorage and the pending() method of a zrpc
-connection now do both input and output.
-
-The short_repr() function used to generate log messages was fixed so
-that it does not blow up creating a repr of very long tuples.
-
-Storages
---------
-
-FileStorage has a new pack() implementation that fixes several
-reported problems that could lead to data loss.
-
-Two small bugs were fixed in DemoStorage.  undoLog() did not handle
-its arguments correctly and pack() could accidentally delete objects
-created in versions.
-
-Fixed trivial bug in fsrecover that prevented it from working at all.
-
-FileStorage will use fsync() on Windows starting with Python 2.2.3.
-
-FileStorage's commit version was fixed.  It used to stop after the
-first object, leaving all the other objects in the version.
-
-BTrees
-------
-
-Trying to store an object of a non-integer type into an IIBTree
-or OIBTree could leave the bucket in a variety of insane states.  For
-example, trying
-
-    b[obj] = "I'm a string, not an integer"
-
-where b is an OIBTree.  This manifested as a refcount leak in the test
-suite, but could have been much worse (most likely in real life is that
-a seemingly arbitrary existing key would "go missing").
-
-When deleting the first child of a BTree node with more than one
-child, a reference to the second child leaked.  This could cause
-the entire bucket chain to leak (not be collected as garbage
-despite not being referenced anymore).
-
-Other minor BTree leak scenarios were also fixed.
-
-Other
------
-
-Comparing a Missing.Value object to a C type that provide its own
-comparison operation could lead to a segfault when the Missing.Value
-was on the right-hand side of the comparison operator.  The Missing
-class was fixed so that its coercion and comparison operations are
-safe.
-
-Tools
------
-
-Four tools are now installed by setup.py: fsdump.py, fstest.py,
-repozo.py, and zeopack.py.
-
-What's new in ZODB3 3.1.1 final?
-================================
-Release date: 11-Feb-2003
-
-Tools
------
-
-Updated repozo.py tool
-
-What's new in ZODB3 3.1.1 beta 2?
-=================================
-Release date: 03-Feb-2003
-
-The Transaction "hosed" feature is disabled in this release.  If a
-transaction fails during the tpc_finish() it is not possible, in
-general, to know whether the storage is in a consistent state.  For
-example, a ZEO server may commit the data and then fail before sending
-confirmation of the commit to the client.  If multiple storages are
-involved in a transaction, the problem is exacerbated: One storage may
-commit the data while another fails to commit.  In previous versions
-of ZODB, the database would set a global "hosed" flag that prevented
-any other transaction from committing until an administrator could
-check the status of the various failed storages and ensure that the
-database is in a consistent state.  This approach favors data
-consistency over availability.  The new approach is to log a panic but
-continue.  In practice, availability seems to be more important than
-consistency.  The failure mode is exceedingly rare in either case.
-
-The BTrees-based fsIndex for FileStorage is enabled.  This version of
-the index is faster to load and store via pickle and uses less memory
-to store keys.  We had intended to enable this feature in an earlier
-release, but failed to actually do it; thus, it's getting enabled as a
-bug fix now.
-
-Two rare bugs were fixed in BTrees conflict resolution.  The most
-probable symptom of the bug would have been a segfault.  The bugs
-were found via synthetic stress tests rather than bug reports.
-
-A value-based consistency checker for BTrees was added.  See the
-module BTrees.check for the checker and other utilities for working
-with BTrees.
-
-A new script called repozo.py was added.  This script, originally
-written by Anthony Baxter, provides an incremental backup scheme for
-FileStorage based storages.
-
-zeopack.py has been fixed to use a read-only connection.
-
-Various small autopack-related race conditions have been fixed in the
-Berkeley storage implementations.  There have been some table changes
-to the Berkeley storages so any storage you created in 3.1.1b1 may not
-work.  Part of these changes was to add a storage version number to
-the schema so these types of incompatible changes can be avoided in
-the future.
-
-Removed the chance of bogus warnings in the FileStorage iterator.
-
-ZEO
----
-
-The ZEO version number was bumped to 2.0.2 on account of the following
-minor feature additions.
-
-The performance of full cache verification has improved dramatically.
-Measurements from Jim were somewhere in 2x-5x.  The
-implementation was fixed to use the very-fast getSerial() method on
-the storage instead of the comparatively slow load().
-
-The ZEO server has an optional timeout feature that will abort a
-connection that does not commit within a certain amount of time.  The
-timeout works by closing the socket the client is using, causing both
-client and server to abort the transaction and continue.  This is a
-drastic step, but can be useful to prevent a hung client or other bug
-from blocking a server indefinitely.
-
-If a client was disconnected during a transaction, the tpc_abort()
-call did not properly reset the internal state about the transaction.
-The bug caused the next transaction to fail in its tpc_finish().
-Also, any ClientDisconnected exceptions raised during tpc_abort() are
-ignored.
-
-ZEO logging has been improved by adding more logging for important
-events, and changing the logging level for existing messages to a more
-appropriate level (usually lower).
-
-What's new in ZODB3 3.1.1 beta 1?
-=================================
-Release date: 10-Dev-2002
-
-It was possible for earlier versions of ZODB to deadlock when using
-multiple storages.  If multiple transactions committed concurrently
-and both transactions involved two or more shared storages, deadlock
-was possible.  This problem has been fixed by introducing a sortKey()
-method to the transaction and storage APIs that is used to define an
-ordering on transaction participants.  This solution will prevent
-deadlocks provided that all transaction participants that use locks
-define a valid sortKey() method.  A warning is raised if a participant
-does not define sortKey().  For backwards compatibility, BaseStorage
-provides a sortKey() that uses __name__.
-
-Added code to ThreadedAsync/LoopCallback.py to work around a bug in
-asyncore.py: a handled signal can cause unwanted reads to happen.
-
-A bug in FileStorage related to object uncreation was fixed.  If an
-a transaction that created an object was undone, FileStorage could
-write a bogus data record header that could lead to strange errors if
-the object was loaded.  An attempt to load an uncreated object now
-raises KeyError, as expected.
-
-The restore() implementation in FileStorage wrote incorrect
-backpointers for a few corner cases involving versions and undo.  It
-also failed if the backpointer pointed to a record that was before the
-pack time.  These specific bugs have been fixed and new test cases
-were added to cover them.
-
-A bug was fixed in conflict resolution that raised a NameError when a
-class involved in a conflict could not be loaded.  The bug did not
-affect correctness, but prevent ZODB from caching the fact that the
-class was unloadable.  A related bug prevented spurious
-AttributeErrors when a class could not be loaded.  It was also fixed.
-
-The script Tools/zeopack.py was fixed to work with ZEO 2.  It was
-untested and had two silly bugs.
-
-Some C extensions included standard header files before including
-Python.h, which is not allowed.  They now include Python.h first,
-which eliminates compiler warnings in certain configurations.
-
-The BerkeleyDB based storages have been merged from the trunk,
-providing a much more robust version of the storages.  They are not
-backwards compatible with the old storages, but the decision was made
-to update them in this micro release because the old storages did not
-work for all practical purposes.  For details, see Doc/BDBStorage.txt.
-
-What's new in ZODB3 3.1 final?
-===============================
-Release date: 28-Oct-2002
-
-If an error occurs during conflict resolution, the store will silently
-catch the error, log it, and continue as if the conflict was
-unresolvable.  ZODB used to behave this way, and the change to catch
-only ConflictError was causing problems in deployed systems.  There
-are a lot of legitimate errors that should be caught, but it's too
-close to the final release to make the substantial changes needed to
-correct this.
-
-What's new in ZODB3 3.1 beta 3?
-===============================
-Release date: 21-Oct-2002
-
-A small extension was made to the iterator protocol.  The Record
-objects, which are returned by the per-transaction iterators, contain
-a new `data_txn` attribute.  It is None, unless the data contained in
-the record is a logical copy of an earlier transaction's data.  For
-example, when transactional undo modifies an object, it creates a
-logical copy of the earlier transaction's data.  Note that this
-provide a stronger statement about consistency than whether the data
-in two records is the same; it's possible for two different updates to
-an object to coincidentally have the same data.
-
-The restore() method was extended to take the data_txn attribute
-mentioned above as an argument.  FileStorage uses the new argument to
-write a backpointer if possible.
-
-A few bugs were fixed.
-
-The setattr slot of the cPersistence C API was being initialized to
-NULL.  The proper initialization was restored, preventing crashes in
-some applications with C extensions that used persistence.
-
-The return value of TimeStamp's __cmp__ method was clipped to return
-only 1, 0, -1.
-
-The restore() method was fixed to write a valid backpointer if the
-update being restored is in a version.
-
-Several bugs and improvements were made to zdaemon, which can be used
-to run the ZEO server.  The parent now forwards signals to the child
-as intended.  Pidfile handling was improved and the trailing newline
-was omitted.
-
-What's new in ZODB3 3.1 beta 2?
-===============================
-Release date: 4-Oct-2002
-
-A few bugs have been fixed, some that were found with the help of
-Neal Norwitz's PyChecker.
-
-The zeoup.py tool has been fixed to allow connecting to a read-only
-storage, when the --nowrite option is given.
-
-Casey Duncan fixed a few bugs in the recent changes to undoLog().
-
-The fstest.py script no longer checks that each object modified in a
-transaction has a serial number that matches the transaction id.
-This invariant is no longer maintained; several new features in the
-3.1 release depend on it.
-
-The ZopeUndo package was added.  If ZODB3 is being used to run a ZEO
-server that will be used with Zope, it is usually best if the server
-and the Zope client don't share any software.  The Zope undo
-framework, however, requires that a Prefix object be passed between
-client and server.  To support this use, ZopeUndo was created to hold
-the Prefix object.
-
-Many bugs were fixed in ZEO, and a couple of features added.  See
-`ZEO-NEWS.txt` for details.
-
-The ZODB guide included in the Doc directory has been updated.  It is
-still incomplete, but most of the references to old ZODB packages have
-been removed.  There is a new section that briefly explains how to use
-BTrees.
-
-The zeoup.py tool connects using a read-only connection when --nowrite
-is specifified.  This feature is useful for checking on read-only ZEO
-servers.
-
-What's new in ZODB3 3.1 beta 1?
-===============================
-Release date: 12-Sep-2002
-
-We've changed the name and version number of the project, but it's
-still the same old ZODB.  There have been a lot of changes since the
-last release.
-
-New ZODB cache
---------------
-
-Toby Dickenson implemented a new Connection cache for ZODB.  The cache
-is responsible for pointer swizzling (translating between oids and
-Python objects) and for keeping recently used objects in memory.  The
-new cache is a big improvement over the old cache.  It strictly honors
-its size limit, where size is specified in number of objects, and it
-evicts objects in least recently used (LRU) order.
-
-Users should take care when setting the cache size, which has a
-default value of 400 objects.  The old version of the cache often held
-many more objects than its specified size.  An application may not
-perform as well with a small cache size, because the cache no longer
-exceeds the limit.
-
-Storages
---------
-
-The index used by FileStorage was reimplemented using a custom BTrees
-object.  The index maps oids to file offsets, and is kept in memory at
-all times.  The new index uses about 1/4 the memory of the old,
-dictionary-based index.  See the module ZODB.fsIndex for details.
-
-A security flaw was corrected in transactionalUndo().  The transaction
-ids returned by undoLog() and used for transactionalUndo() contained a
-file offset.  An attacker could construct a pickle with a bogus
-transaction record in its binary data, deduce the position of the
-pickle in the file from the undo log, then submit an undo with a bogus
-file position that caused the pickle to get written as a regular data
-record.  The implementation was fixed so that file offsets are not
-included in the transaction ids.
-
-Several storages now have an explicit read-only mode.  For example,
-passing the keyword argument read_only=1 to FileStorage will make it
-read-only.  If a write operation is performed on a read-only storage,
-a ReadOnlyError will be raised.
-
-The storage API was extended with new methods that support the Zope
-Replication Service (ZRS), a proprietary Zope Corp product.  We expect
-these methods to be generally useful.  The methods are:
-
-    - restore(oid, serialno, data, version, transaction)
-
-      Perform a store without doing consistency checks.  A client can
-      use this method to provide a new current revision of an object.
-      The ``serialno`` argument is the new serialno to use for the
-      object, not the serialno of the previous revision.
-
-    - lastTransaction()
-
-      Returns the transaction id of the last committed transaction.
-
-    - lastSerial(oid)
-
-      Return the current serialno for ``oid`` or None.
-
-    - iterator(start=None, stop=None)
-
-      The iterator method isn't new, but the optional ``start`` and
-      ``stop`` arguments are.  These arguments can be used to specify
-      the range of the iterator -- an inclusive range [start, stop].
-
-FileStorage is now more cautious about creating a new file when it
-believes a file does not exist.  This change is a workaround for bug
-in Python versions upto and including 2.1.3.  If the interpreter was
-builtin without large file support but the platform had it,
-os.path.exists() would return false for large files.  The fix is to
-try to open the file first, and decide whether to create a new file
-based on errno.
-
-The undoLog() and undoInfo() methods of FileStorage can run
-concurrently with other methods.  The internal storage lock is
-released periodically to give other threads a chance to run.  This
-should increase responsiveness of ZEO clients when used with ZEO 2.
-
-New serial numbers are assigned consistently for abortVersion() and
-commitVersion().  When a version is committed, the non-version data
-gets a new serial number.  When a version is aborted, the serial
-number for non-version data does not change.  This means that the
-abortVersion() transaction record has the unique property that its
-transaction id is not the serial number of the data records.
-
-
-Berkeley Storages
------------------
-
-Berkeley storage constructors now take an optional `config` argument,
-which is an instance whose attributes can be used to configure such
-BerkeleyDB policies as an automatic checkpointing interval, lock table
-sizing, and the log directory.  See bsddb3Storage/BerkeleyBase.py for
-details.
-
-A getSize() method has been added to all Berkeley storages.
-
-Berkeley storages open their environments with the DB_THREAD flag.
-
-Some performance optimizations have been implemented in Full storage,
-including the addition of a helper C extension when used with Python
-2.2.  More performance improvements will be added for the ZODB 3.1
-final release.
-
-A new experimental Autopack storage was added which keeps only a
-certain amount of old revision information.  The concepts in this
-storage will be folded into Full and Autopack will likely go away in
-ZODB 3.1 final.  ZODB 3.1 final will also have much improved Minimal
-and Full storages, which eliminate Berkeley lock exhaustion problems,
-reduce memory use, and improve performance.
-
-It is recommended that you use BerkeleyDB 4.0.14 and PyBSDDB 3.4.0
-with the Berkeley storages.  See bsddb3Storage/README.txt for details.
-
-
-BTrees
-------
-
-BTrees no longer ignore exceptions raised when two keys are compared.
-
-Tim Peters fixed several endcase bugs in the BTrees code.  Most
-importantly, after a mix of inserts and deletes in a BTree or TreeSet, it
-was possible (but unlikely) for the internal state of the object to become
-inconsistent.  Symptoms then varied; most often this manifested as a
-mysterious failure to find a key that you knew was present, or that
-tree.keys() would yield an object that disgreed with the tree about
-how many keys there were.
-
-If you suspect such a problem, BTrees and TreeSets now support a ._check()
-method, which does a thorough job of examining the internal tree pointers
-for consistency.  It raises AssertionError if it finds any problems, else
-returns None.  If ._check() raises an exception, the object is damaged,
-and rebuilding the object is the best solution.  All known ways for a
-BTree or TreeSet object to become internally inconsistent have been
-repaired.
-
-Other fixes include:
-
-- Many fixes for range search endcases, including the "range search bug:"
-  If the smallest key S in a bucket in a BTree was deleted, doing a range
-  search on the BTree with S on the high end could claim that the range
-  was empty even when it wasn't.
-
-- Zope Collector #419:  repaired off-by-1 errors and IndexErrors when
-  slicing BTree-based data structures.  For example,
-  an_IIBTree.items()[0:0] had length 1 (should be empty) if the tree
-  wasn't empty.
-
-- The BTree module functions weightedIntersection() and weightedUnion()
-  now treat negative weights as documented.  It's hard to explain what
-  their effects were before this fix, as the sign bits were getting
-  confused with an internal distinction between whether the result
-  should be a set or a mapping.
-
-ZEO
-----
-
-For news about ZEO2, see the file ZEO-NEWS.txt.
-
-This version of ZODB ships with two different versions of ZEO.  It
-includes ZEO 2.0 beta 1, the recommended new version.  (ZEO 2 will
-reach final release before ZODB3.)  The ZEO 2.0 protocol is not
-compatible with ZEO 1.0, so we have also included ZEO 1.0 to support
-people already using ZEO 1.0.
-
-Other features
---------------
-
-When a ConflictError is raised, the exception object now has a
-sensible structure, thanks to a patch from Greg Ward.  The exception
-now uses the following standard attributes: oid, class_name, message,
-serials.  See the ZODB.POSException.ConflictError doc string for
-details.
-
-It is now easier to customize the registration of persistent objects
-with a transaction.  The low-level persistence mechanism in
-cPersistence.c registers with the object's jar instead of with the
-current transaction.  The jar (Connection) then registers with the
-transaction.  This redirection would allow specialized Connections to
-change the default policy on how the transaction manager is selected
-without hacking the Transaction module.
-
-Empty transactions can be committed without interacting with the
-storage.  It is possible for registration to occur unintentionally and
-for a persistent object to compensate by making itself as unchanged.
-When this happens, it's possible to commit a transaction with no
-modified objects.  The change allows such transactions to finish even
-on a read-only storage.
-
-Two new tools were added to the Tools directory.  The ``analyze.py``
-script, based on a tool by Matt Kromer, prints a summary of space
-usage in a FileStorage Data.fs.  The ``checkbtrees.py`` script scans a
-FileStorage Data.fs.  When it finds a BTrees object, it loads the
-object and calls the ``_check`` method.  It prints warning messages
-for any corrupt BTrees objects found.
-
-Documentation
--------------
-
-The user's guide included with this release is still woefully out of date.
-
-Other bugs fixed
-----------------
-
-If an exception occurs inside an _p_deactivate() method, a traceback
-is printed on stderr.  Previous versions of ZODB silently cleared the
-exception.
-
-ExtensionClass and ZODB now work correctly with a Python debug build.
-
-All C code has been fixed to use a consistent set of functions from
-the Python memory API.  This allows ZODB to be used in conjunction
-with pymalloc, the default allocator in Python 2.3.
-
-zdaemon, which can be used to run a ZEO server, more clearly reports
-the exit status of its child processes.
-
-The ZEO server will reinitialize zLOG when it receives a SIGHUP.  This
-allows log file rotation without restarting the server.
-
-What's new in StandaloneZODB 1.0 final?
-=======================================
-Release date: 08-Feb-2002
-
-All copyright notices have been updated to reflect the fact that the
-ZPL 2.0 covers this release.
-
-Added a cleanroom PersistentList.py implementation, which multiply
-inherits from UserDict and Persistent.
-
-Some improvements in setup.py and test.py for sites that don't have
-the Berkeley libraries installed.
-
-A new program, zeoup.py was added which simply verifies that a ZEO
-server is reachable.  Also, a new program zeopack.py was added which
-connects to a ZEO server and packs it.
-
-
-What's new in StandaloneZODB 1.0 c1?
-====================================
-Release Date: 25-Jan-2002
-
-This was the first public release of the StandaloneZODB from Zope
-Corporation.   Everything's new! :)
+- (3.7a1) Transaction objects have a new method,
+  ``addAfterCommitHook(hook, *args, **kws)``.  Hook functions
+  registered with a transaction are called after the transaction
+  commits or aborts. For example, one might want to launch non
+  transactional or asynchrnonous code after a successful, or aborted,
+  commit. See ``test_afterCommitHook()`` in
+  ``transaction/tests/test_transaction.py`` for a tutorial doctest,
+  and the ``ITransaction`` interface for details.

Modified: ZODB/branches/blob-merge-branch/PACKAGE.cfg
===================================================================
--- ZODB/branches/blob-merge-branch/PACKAGE.cfg	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/PACKAGE.cfg	2006-02-17 22:01:38 UTC (rev 41650)
@@ -13,5 +13,5 @@
   setup.py      -
   src           -
   zpkg.conf     -
-  zpkgsetup     -
+  buildsupport  -
 </collection>

Modified: ZODB/branches/blob-merge-branch/README.txt
===================================================================
--- ZODB/branches/blob-merge-branch/README.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/README.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,4 +1,4 @@
-ZODB 3.6
+ZODB 3.7
 ========
 
 Introduction
@@ -28,15 +28,14 @@
 Compatibility
 -------------
 
-ZODB 3.6 requires Python 2.3.4 or later.  For best results, we recommend
-Python 2.3.5.  Python 2.4.1 can also be used.
+ZODB 3.7 requires Python 2.4.2 or later.
 
 The Zope 2.8 release, and Zope3 releases, should be compatible with this
 version of ZODB.  Note that Zope 2.7 and higher includes ZEO, so this package
 should only be needed to run a ZEO server.
 
-ZEO servers and clients are wholly compatible among 3.3, 3.3.1, 3.4, 3.5, and
-3.6; a ZEO client from any of those versions can talk with a ZEO server from
+ZEO servers and clients are wholly compatible among 3.3, 3.4, 3.5, 3.6 and
+3.7; a ZEO client from any of those versions can talk with a ZEO server from
 any.
 
 Trying to mix ZEO clients and servers from 3.3 or later from ZODB releases
@@ -93,13 +92,15 @@
 
 This should now make all of ZODB accessible to your Python programs.
 
-Testing
--------
+Testing for Developers
+----------------------
 
-ZODB comes with a large test suite that can be run from the source
-directory before ZODB is installed.  The simplest way to run the tests
-is::
+When working from a ZODB checkout, do an in-place build instead::
 
+    % python setup.py build_ext -i
+
+followed by::
+
     % python test.py -v
 
 This command will run all the tests, printing a single dot for each

Modified: ZODB/branches/blob-merge-branch/doc/guide/zodb.tex
===================================================================
--- ZODB/branches/blob-merge-branch/doc/guide/zodb.tex	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/doc/guide/zodb.tex	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,7 +1,7 @@
 \documentclass{howto}
 
 \title{ZODB/ZEO Programming Guide}
-\release{3.6.0a3}
+\release{3.7.0a0}
 \date{\today}
 
 \author{A.M.\ Kuchling}

Modified: ZODB/branches/blob-merge-branch/setup.py
===================================================================
--- ZODB/branches/blob-merge-branch/setup.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/setup.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -34,7 +34,7 @@
 import zpkgsetup.setup
 
 # Note that release.py must be able to recognize the VERSION line.
-VERSION = "3.6.0a3"
+VERSION = "3.7.0a0"
 
 context = zpkgsetup.setup.SetupContext(
     "ZODB", VERSION, __file__)


Property changes on: ZODB/branches/blob-merge-branch/src
___________________________________________________________________
Name: svn:externals
   - ZConfig    svn://svn.zope.org/repos/main/ZConfig/tags/ZConfig-2.3.1
zdaemon    svn://svn.zope.org/repos/main/zdaemon/tags/zdaemon-1.1

   + ZConfig          svn://svn.zope.org/repos/main/ZConfig/tags/ZConfig-2.3.1
zdaemon -r 40792 svn://svn.zope.org/repos/main/zdaemon/trunk/src/zdaemon


Copied: ZODB/branches/blob-merge-branch/src/BTrees/Development.txt (from rev 41649, ZODB/trunk/src/BTrees/Development.txt)

Deleted: ZODB/branches/blob-merge-branch/src/BTrees/Maintainer.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/BTrees/Maintainer.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/BTrees/Maintainer.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,374 +0,0 @@
-This document provides information for developers who maintain or
-extend BTrees.
-
-Macros
-======
-BTrees are defined using a "template", roughly akin to a a C++
-template.  To create a new family of BTrees, create a source file that
-defines macros used to handle differences in key and value types:
-
-
-Configuration Macros
-
-MASTER_ID
-A string to hold an RCS/CVS Id key to be included in compiled binaries.
-
-MOD_NAME_PREFIX
-A string (like "IO" or "OO") that provides the prefix used for the
-module.  This gets used to generate type names and the internal module
-name string.
-
-DEFAULT_MAX_BUCKET_SIZE
-An int giving the maximum bucket size (number of key/value pairs).
-When a bucket gets larger than this due to an insertion *into a BTREE*,
-it splits.  Inserting into a bucket directly doesn't split, and
-functions that produce a bucket output (e.g., union()) also have no
-bound on how large a bucket may get.  Someday this will be tunable
-on BTree instances.
-
-DEFAULT_MAX_BTREE_SIZE
-An int giving the maximum size (number of children) of an internal
-btree node.  Someday this will be tunable on BTree instances.
-
-Macros for Keys
-
-KEY_TYPE
-The C type declaration for keys (e.g., int or PyObject*).
-
-KEY_TYPE_IS_PYOBJECT
-Define if KEY_TYPE is a PyObject*, else undef.
-
-KEY_CHECK(K)
-Tests whether the PyObject* K can be converted to the (C) key type
-(KEY_TYPE).  The macro should return a boolean (zero for false,
-non-zero for true).  When it returns false, its caller should probably
-set a TypeError exception.
-
-TEST_KEY_SET_OR(V, K, T)
-Like Python's cmp().  Compares K(ey) to T(arget), where K & T are C
-values of type KEY_TYPE.  V is assigned an int value depending on
-the outcome:
-   < 0 if K < T
-  == 0 if K == T
-   > 0 if K > T
-This macro acts like an 'if', where the following statement is
-executed only if a Python exception has been raised because the
-values could not be compared.
-
-DECREF_KEY(K)
-K is a value of KEY_TYPE.  If KEY_TYPE is a flavor of PyObject*, write
-this to do Py_DECREF(K).  Else (e.g., KEY_TYPE is int) make it a nop.
-
-INCREF_KEY(K)
-K is a value of KEY_TYPE.  If KEY_TYPE is a flavor of PyObject*, write
-this to do Py_INCREF(K).  Else (e.g., KEY_TYPE is int) make it a nop.
-
-COPY_KEY(K, E)
-Like K=E.  Copy a key from E to K, both of KEY_TYPE.  Note that this
-doesn't decref K or incref E when KEY_TYPE is a PyObject*; the caller
-is responsible for keeping refcounts straight.
-
-COPY_KEY_TO_OBJECT(O, K)
-Roughly like O=K.  O is a PyObject*, and the macro must build a Python
-object form of K, assign it to O, and ensure that O owns the reference
-to its new value.  It may do this by creating a new Python object based
-on K (e.g., PyInt_FromLong(K) when KEY_TYPE is int), or simply by doing
-Py_INCREF(K) if KEY_TYPE is a PyObject*.
-
-COPY_KEY_FROM_ARG(TARGET, ARG, STATUS)
-Copy an argument to the target without creating a new reference to ARG.
-ARG is a PyObject*, and TARGET is of type KEY_TYPE.  If this can't be
-done (for example, KEY_CHECK(ARG) returns false), set a Python error
-and set status to 0.  If there is no error, leave status alone.
-
-
-Macros for Values
-
-VALUE_TYPE
-The C type declaration for values (e.g., int or PyObject*).
-
-VALUE_TYPE_IS_PYOBJECT
-Define if VALUE_TYPE is a PyObject*, else undef.
-
-TEST_VALUE(X, Y)
-Like Python's cmp().  Compares X to Y, where X & Y are C values of
-type VALUE_TYPE.  The macro returns an int, with value
-   < 0 if X < Y
-  == 0 if X == Y
-   > 0 if X > Y
-Bug:  There is no provision for determining whether the comparison
-attempt failed (set a Python exception).
-
-DECREF_VALUE(K)
-Like DECREF_KEY, except applied to values of VALUE_TYPE.
-
-INCREF_VALUE(K)
-Like INCREF_KEY, except applied to values of VALUE_TYPE.
-
-COPY_VALUE(K, E)
-Like COPY_KEY, except applied to values of VALUE_TYPE.
-
-COPY_VALUE_TO_OBJECT(O, K)
-Like COPY_KEY_TO_OBJECT, except applied to values of VALUE_TYPE.
-
-COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS)
-Like COPY_KEY_FROM_ARG, except applied to values of VALUE_TYPE.
-
-NORMALIZE_VALUE(V, MIN)
-Normalize the value, V, using the parameter MIN.  This is almost
-certainly a YAGNI.  It is a no op for most types. For integers, V is
-replaced by V/MIN only if MIN > 0.
-
-
-Macros for Set Operations
-
-MERGE_DEFAULT
-A value of VALUE_TYPE specifying the value to associate with set
-elements when sets are merged with mappings via weighed union or
-weighted intersection.
-
-MERGE(O1, w1, O2, w2)
-Performs a weighted merge of two values, O1 and O2, using weights w1
-and w2.  The result must be of VALUE_TYPE.  Note that weighted unions
-and weighted intersections are not enabled if this macro is left
-undefined.
-
-MERGE_WEIGHT(O, w)
-Computes a weighted value for O.  The result must be of VALUE_TYPE.
-This is used for "filling out" weighted unions, i.e. to compute a
-weighted value for keys that appear in only one of the input
-mappings.  If left undefined, MERGE_WEIGHT defaults to
-
-    #define MERGE_WEIGHT(O, w) (O)
-
-MULTI_INT_UNION
-The value doesn't matter.  If defined, SetOpTemplate.c compiles
-code for a multiunion() function (compute a union of many input sets
-at high speed).  This currently makes sense only for structures with
-integer keys.
-
-
-BTree Clues
-===========
-More or less random bits of helpful info.
-
-+ In papers and textbooks, this flavor of BTree is usually called
-  a B+-Tree, where "+" is a superscript.
-
-+ All keys and all values live in the bucket leaf nodes.  Keys in
-  interior (BTree) nodes merely serve to guide a search efficiently
-  toward the correct leaf.
-
-+ When a key is deleted, it's physically removed from the bucket
-  it's in, but this doesn't propagate back up the tree:  since keys
-  in interior nodes only serve to guide searches, it's OK-- and
-  saves time --to leave "stale" keys in interior nodes.
-
-+ No attempt is made to rebalance the tree after a deletion, unless
-  a bucket thereby becomes entirely empty.  "Classic BTrees" do
-  rebalance, keeping all buckets at least half full (provided there
-  are enough keys in the entire tree to fill half a bucket).  The
-  tradeoffs are murky.  Pathological cases in the presence of
-  deletion do exist.  Pathologies include trees tending toward only
-  one key per bucket, and buckets at differing depths (all buckets
-  are at the same depth in a classic BTree).
-
-+ DEFAULT_MAX_BUCKET_SIZE and DEFAULT_MAX_BTREE_SIZE are chosen
-  mostly to "even out" pickle sizes in storage.  That's why, e.g.,
-  an IIBTree has larger values than an OOBTree:  pickles store ints
-  more efficiently than they can store arbitrary Python objects.
-
-+ In a non-empty BTree, every bucket node contains at least one key,
-  and every BTree node contains at least one child and a non-NULL
-  firstbucket pointer.  However, a BTree node may not contain any keys.
-
-+ An empty BTree consists solely of a BTree node with len==0 and
-  firstbucket==NULL.
-
-+ Although a BTree can become unbalanced under a mix of inserts and
-  deletes (meaning both that there's nothing stronger that can be
-  said about buckets than that they're not empty, and that buckets
-  can appear at different depths), a BTree node always has children
-  of the same kind:  they're all buckets, or they're all BTree nodes.
-
-
-The BTREE_SEARCH Macro
-======================
-For notational ease, consider a fixed BTree node x, and let
-
-    K(i) mean x->data.key[i]
-    C(i) mean all the keys reachable from x->data.child[i]
-
-For each i in 0 to x->len-1 inclusive,
-
-    K(i) <= C(i) < K(i+1)
-
-is a BTree node invariant, where we pretend that K(0) holds a key
-smaller than any possible key, and K(x->len) holds a key larger
-than any possible key.  (Note that K(x->len) doesn't actually exist,
-and K(0) is never used although space for it exists in non-empty
-BTree nodes.)
-
-When searching for a key k, then, the child pointer we want to follow
-is the one at index i such that K(i) <= k < K(i+1).  There can be
-at most one such i, since the K(i) are strictly increasing.  And there
-is at least one such i provided the tree isn't empty (so that 0 < len).
-For the moment, assume the tree isn't empty (we'll get back to that
-later).
-
-The macro's chief loop invariant is
-
-    K(lo) < k < K(hi)
-
-This holds trivially at the start, since lo is set to 0, and hi to
-x->len, and we pretend K(0) is minus infinity and K(len) is plus
-infinity.  Inside the loop, if K(i) < k we set lo to i, and if
-K(i) > k we set hi to i.  These obviously preserve the invariant.
-If K(i) == k, the loop breaks and sets the result to i, and since
-K(i) == k in that case i is obviously the correct result.
-
-Other cases depend on how i = floor((lo + hi)/2) works, exactly.
-Suppose lo + d = hi for some d >= 0.  Then i = floor((lo + lo + d)/2) =
-floor(lo + d/2) = lo + floor(d/2).  So:
-
-a. [d == 0] (lo == i == hi) if and only if (lo   == hi).
-b. [d == 1] (lo == i  < hi) if and only if (lo+1 == hi).
-c. [d  > 1] (lo  < i  < hi) if and only if (lo+1  < hi).
-
-If the node is empty (x->len == 0), then lo==i==hi==0 at the start,
-and the loop exits immediately (the first "i > lo" test fails),
-without entering the body.
-
-Else lo < hi at the start, and the invariant K(lo) < k < K(hi) holds.
-
-If lo+1 < hi, we're in case #c:  i is strictly between lo and hi,
-so the loop body is entered, and regardless of whether the body sets
-the new lo or the new hi to i, the new lo is strictly less than the
-new hi, and the difference between the new lo and new hi is strictly
-less than the difference between the old lo and old hi.  So long as
-the new lo + 1 remains < the new hi, we stay in this case.  We can't
-stay in this case forever, though:  because hi-lo decreases on each
-trip but remains > 0, lo+1 == hi must eventually become true.  (In
-fact, it becomes true quickly, in about log2(x->len) trips; the
-point is more that lo doesn't equal hi when the loop ends, it has to
-end with lo+1==hi and i==lo).
-
-Then we're in case #b:  i==lo==hi-1 then, and the loop exits.  The
-invariant still holds, with lo==i and hi==lo+1==i+1:
-
-    K(i) < k < K(i+1)
-
-so i is again the correct answer.
-
-Optimization points:
-
-+ Division by 2 is done via shift rather via "/2".  These are
-  signed ints, and almost all C compilers treat signed int division
-  as truncating, and shifting is not the same as truncation for
-  signed int division.  The compiler has no way to know these values
-  aren't negative, so has to generate longer-winded code for "/2".
-  But we know these values aren't negative, and exploit it.
-
-+ The order of _cmp comparisons matters.  We're in an interior
-  BTree node, and are looking at only a tiny fraction of all the
-  keys that exist.  So finding the key exactly in this node is
-  unlikely, and checking _cmp == 0 is a waste of time to the same
-  extent.  It doesn't matter whether we check for _cmp < 0 or
-  _cmp > 0 first, so long as we do both before worrying about
-  equality.
-
-+ At the start of a routine, it's better to run this macro even
-  if x->len is 0 (check for that afterwards).  We just called a
-  function and so probably drained the pipeline.  If the first thing
-  we do then is read up self->len and check it against 0, we just
-  sit there waiting for the data to get read up, and then another
-  immediate test-and-branch, and for a very unlikely case (BTree
-  nodes are rarely empty).  It's better to get into the loop right
-  away so the normal case makes progress ASAP.
-
-
-The BUCKET_SEARCH Macro
-=======================
-This has a different job than BTREE_SEARCH:  the key 0 slot is
-legitimate in a bucket, and we want to find the index at which the
-key belongs.  If the key is larger than the bucket's largest key, a
-new slot at index len is where it belongs, else it belongs at the
-smallest i with keys[i] >= the key we're looking for.  We also need
-to know whether or not the key is present (BTREE_SEARCH didn't care;
-it only wanted to find the next node to search).
-
-The mechanics of the search are quite similar, though.  The primary
-loop invariant changes to (say we're searching for key k):
-
-    K(lo-1) < k < K(hi)
-
-where K(i) means keys[i], and we pretend K(-1) is minus infinity and
-K(len) is plus infinity.
-
-If the bucket is empty, lo=hi=i=0 at the start, the loop body is never
-entered, and the macro sets INDEX to 0 and ABSENT to true.  That's why
-_cmp is initialized to 1 (_cmp becomes ABSENT).
-
-Else the bucket is not empty, lo<hi at the start, and the loop body
-is entered.  The invariant is obviously satisfied then, as lo=0 and
-hi=len.
-
-If K[i]<k, lo is set to i+1, preserving that K(lo-1) = K[i] < k.
-If K[i]>k, hi is set to i, preserving that K[hi] = K[i] > k.
-If the loop exits after either of those, _cmp != 0, so ABSENT becomes
-true.
-If K[i]=k, the loop breaks, so that INDEX becomes i, and ABSENT
-becomes false (_cmp=0 in this case).
-
-The same case analysis for BTREE_SEARCH on lo and hi holds here:
-
-a. (lo == i == hi) if and only if (lo   == hi).
-b. (lo == i  < hi) if and only if (lo+1 == hi).
-c. (lo  < i  < hi) if and only if (lo+1  < hi).
-
-So long as lo+1 < hi, we're in case #c, and either break with
-equality (in which case the right results are obviously computed) or
-narrow the range.  If equality doesn't obtain, the range eventually
-narrows to cases #a or #b.
-
-To go from #c to #a, we must have lo+2==hi at the start, and
-K[i]=K[lo+1]<k.  Then the new lo gets set to i+1 = lo+2 = hi, and the
-loop exits with lo=hi=i and _cmp<0.  This is correct, because we
-know that k != K(i) (loop invariant! we actually know something
-stronger, that k < K(hi); since i=hi, this implies k != K(i)).
-
-Else #c eventually falls into case #b, lo+1==hi and i==lo.  The
-invariant tells us K(lo-1) < k < K(hi) = K(lo+1), so if the key
-is present it must be at K(lo).  i==lo in this case, so we test
-K(lo) against k.  As always, if equality obtains we do the right
-thing, else case #b becomes case #a.
-
-When #b becomes #a, the last comparison was non-equal, so _cmp is
-non-zero, and the loop exits because lo==hi==i in case #a.  The
-invariant then tells us K(lo-1) < k < K(lo), so the key is in fact
-not present, it's correct to exit with _cmp non-zero, and i==lo is
-again the index at which k belongs.
-
-Optimization points:
-
-+ As for BTREE_SEARCH, shifting of signed ints is cheaper than
-  division.
-
-+ Unlike as for BTREE_SEARCH, there's nothing special about searching
-  an empty bucket, and the macro computes thoroughly sensible results
-  in that case.
-
-+ The order of _cmp comparisons differs from BTREE_SEARCH.  When
-  searching a bucket, it's much more likely (than when searching a
-  BTree node) that the key is present, so testing __cmp==0 isn't a
-  systematic waste of cycles.  At the extreme, if all searches are
-  successful (key present), on average this saves one comparison per
-  search, against leaving the determination of _cmp==0 implicit (as
-  BTREE_SEARCH does).  But even on successful searches, __cmp != 0 is
-  a more popular outcome than __cmp == 0 across iterations (unless
-  the bucket has only a few keys), so it's important to check one
-  of the inequality cases first.  It turns out it's better on average
-  to check K(i) < key (than to check K(i) > key), because when it
-  pays it narrows the range more (we get a little boost from setting
-  lo=i+1 in this case; the other case sets hi=i, which isn't as much
-  of a narrowing).

Modified: ZODB/branches/blob-merge-branch/src/BTrees/_fsBTree.c
===================================================================
--- ZODB/branches/blob-merge-branch/src/BTrees/_fsBTree.c	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/BTrees/_fsBTree.c	2006-02-17 22:01:38 UTC (rev 41650)
@@ -45,7 +45,7 @@
 #define DECREF_KEY(KEY)
 #define INCREF_KEY(k)
 #define COPY_KEY(KEY, E) (*(KEY)=*(E), (KEY)[1]=(E)[1])
-#define COPY_KEY_TO_OBJECT(O, K) O=PyString_FromStringAndSize(K,2)
+#define COPY_KEY_TO_OBJECT(O, K) O=PyString_FromStringAndSize((const char*)K,2)
 #define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
   if (KEY_CHECK(ARG)) memcpy(TARGET, PyString_AS_STRING(ARG), 2); else { \
       PyErr_SetString(PyExc_TypeError, "expected two-character string key"); \
@@ -59,7 +59,7 @@
 #define DECREF_VALUE(k)
 #define INCREF_VALUE(k)
 #define COPY_VALUE(V, E) (memcpy(V, E, 6))
-#define COPY_VALUE_TO_OBJECT(O, K) O=PyString_FromStringAndSize(K,6)
+#define COPY_VALUE_TO_OBJECT(O, K) O=PyString_FromStringAndSize((const char*)K,6)
 #define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
   if ((PyString_Check(ARG) && PyString_GET_SIZE(ARG)==6)) \
       memcpy(TARGET, PyString_AS_STRING(ARG), 6); else { \

Modified: ZODB/branches/blob-merge-branch/src/ZEO/Exceptions.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/Exceptions.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/Exceptions.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -16,7 +16,7 @@
 from ZODB.POSException import StorageError
 
 class ClientStorageError(StorageError):
-    """An error occured in the ZEO Client Storage."""
+    """An error occurred in the ZEO Client Storage."""
 
 class UnrecognizedResult(ClientStorageError):
     """A server call returned an unrecognized result."""

Modified: ZODB/branches/blob-merge-branch/src/ZEO/StorageServer.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/StorageServer.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/StorageServer.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -64,7 +64,7 @@
     logger.log(level, message, exc_info=exc_info)
 
 class StorageServerError(StorageError):
-    """Error reported when an unpickleable exception is raised."""
+    """Error reported when an unpicklable exception is raised."""
 
 class ZEOStorage:
     """Proxy to underlying storage for a single remote client."""

Modified: ZODB/branches/blob-merge-branch/src/ZEO/__init__.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/__init__.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/__init__.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -22,4 +22,4 @@
 """
 
 # The next line must use double quotes, so release.py recognizes it.
-version = "3.6.0a3"
+version = "3.7.0a0"

Modified: ZODB/branches/blob-merge-branch/src/ZEO/tests/forker.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/tests/forker.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/tests/forker.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -176,8 +176,14 @@
     # superstition.
     for i in range(3):
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.settimeout(.3)
         try:
             s.connect(adminaddr)
+        except socket.timeout:
+            # On FreeBSD 5.3 the connection just timed out
+            if i > 0:
+                break
+            raise
         except socket.error, e:
             if e[0] == errno.ECONNREFUSED and i > 0:
                 break

Modified: ZODB/branches/blob-merge-branch/src/ZEO/version.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/version.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/version.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1 +1 @@
-3.6.0a3
+3.7.0a0

Modified: ZODB/branches/blob-merge-branch/src/ZEO/zrpc/connection.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZEO/zrpc/connection.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZEO/zrpc/connection.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -460,9 +460,13 @@
         return hasattr(self.obj, name)
 
     def send_reply(self, msgid, ret):
+        # encode() can pass on a wide variety of exceptions from cPickle.
+        # While a bare `except` is generally poor practice, in this case
+        # it's acceptable -- we really do want to catch every exception
+        # cPickle may raise.
         try:
             msg = self.marshal.encode(msgid, 0, REPLY, ret)
-        except self.marshal.errors:
+        except: # see above
             try:
                 r = short_repr(ret)
             except:
@@ -480,9 +484,13 @@
         if type(err_value) is not types.InstanceType:
             err_value = err_type, err_value
 
+        # encode() can pass on a wide variety of exceptions from cPickle.
+        # While a bare `except` is generally poor practice, in this case
+        # it's acceptable -- we really do want to catch every exception
+        # cPickle may raise.
         try:
             msg = self.marshal.encode(msgid, 0, REPLY, (err_type, err_value))
-        except self.marshal.errors:
+        except: # see above
             try:
                 r = short_repr(err_value)
             except:

Modified: ZODB/branches/blob-merge-branch/src/ZODB/BaseStorage.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/BaseStorage.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/BaseStorage.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -34,6 +34,7 @@
 
     A subclass must define the following methods:
     load()
+    store()
     close()
     cleanup()
     lastSerial()
@@ -53,7 +54,6 @@
 
     If the subclass wants to implement undo, it should implement the
     multiple revision methods and:
-    loadSerial()
     undo()
     undoInfo()
     undoLog()
@@ -94,9 +94,9 @@
         self._commit_lock_acquire = l.acquire
         self._commit_lock_release = l.release
 
-        t=time.time()
-        t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
-        self._tid = `t`
+        t = time.time()
+        t = self._ts = TimeStamp(*(time.gmtime(t)[:5] + (t%60,)))
+        self._tid = repr(t)
 
         # ._oid is the highest oid in use (0 is always in use -- it's
         # a reserved oid for the root object).  Our new_oid() method
@@ -189,10 +189,12 @@
         try:
             if transaction is not self._transaction:
                 return
-            self._abort()
-            self._clear_temp()
-            self._transaction = None
-            self._commit_lock_release()
+            try:
+                self._abort()
+                self._clear_temp()
+                self._transaction = None
+            finally:
+                self._commit_lock_release()
         finally:
             self._lock_release()
 
@@ -226,7 +228,7 @@
                 now = time.time()
                 t = TimeStamp(*(time.gmtime(now)[:5] + (now % 60,)))
                 self._ts = t = t.laterThan(self._ts)
-                self._tid = `t`
+                self._tid = repr(t)
             else:
                 self._ts = TimeStamp(tid)
                 self._tid = tid

Modified: ZODB/branches/blob-merge-branch/src/ZODB/Connection.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/Connection.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/Connection.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -45,7 +45,6 @@
 from ZODB.POSException import Unsupported
 from ZODB.POSException import POSKeyError
 from ZODB.serialize import ObjectWriter, ObjectReader, myhasattr
-from ZODB.utils import DEPRECATED_ARGUMENT, deprecated36
 from ZODB.utils import p64, u64, z64, oid_repr, positive_id
 from ZODB import utils
 
@@ -132,6 +131,9 @@
         # will execute atomically by virtue of the GIL.  But some storage
         # might generate oids where hash or compare invokes Python code.  In
         # that case, the GIL can't save us.
+        # Note:  since that was written, it was officially declared that the
+        # type of an oid is str.  TODO:  remove the related now-unnecessary
+        # critical sections (if any -- this needs careful thought).
 
         self._inv_lock = threading.Lock()
         self._invalidated = d = {}
@@ -213,10 +215,8 @@
         self._cache[oid] = obj
         return obj
 
-    def cacheMinimize(self, dt=DEPRECATED_ARGUMENT):
+    def cacheMinimize(self):
         """Deactivate all unmodified objects in the cache."""
-        if dt is not DEPRECATED_ARGUMENT:
-            deprecated36("cacheMinimize() dt= is ignored.")
         self._cache.minimize()
 
     # TODO: we should test what happens when cacheGC is called mid-transaction.
@@ -266,7 +266,7 @@
             # Return the connection to the pool.
             if self._opened is not None:
                 self._db._returnToPool(self)
-                
+
                 # _returnToPool() set self._opened to None.
                 # However, we can't assert that here, because self may
                 # have been reused (by another thread) by the time we
@@ -323,7 +323,7 @@
         This is used in a check to avoid implicitly adding an object
         to a database in a multi-database situation.
         See serialize.ObjectWriter.persistent_id.
-        
+
         """
         return (self._creating.get(oid, 0)
                 or
@@ -546,11 +546,11 @@
                 # Because obj was added, it is now in _creating, so it
                 # can be removed from _added.  If oid wasn't in
                 # adding, then we are adding it implicitly.
-                
+
                 implicitly_adding = self._added.pop(oid, None) is None
 
                 self._creating[oid] = implicitly_adding
-                
+
             else:
                 if (oid in self._invalidated
                     and not hasattr(obj, '_p_resolveConflict')):
@@ -647,8 +647,8 @@
 
         self._storage.tpc_abort(transaction)
 
-        # Note: If we invalidate a non-justifiable object (i.e. a
-        # persistent class), the object will immediately reread it's
+        # Note: If we invalidate a non-ghostifiable object (i.e. a
+        # persistent class), the object will immediately reread its
         # state.  That means that the following call could result in a
         # call to self.setstate, which, of course, must succeed.  In
         # general, it would be better if the read could be delayed
@@ -787,10 +787,8 @@
         # dict update could go on in another thread, but we don't care
         # because we have to check again after the load anyway.
 
-        if (obj._p_oid in self._invalidated
-            and not myhasattr(obj, "_p_independent")
-            and not self._invalidated
-            ):
+        if (obj._p_oid in self._invalidated and
+                not myhasattr(obj, "_p_independent")):
             # If the object has _p_independent(), we will handle it below.
             self._load_before_or_conflict(obj)
             return
@@ -883,16 +881,11 @@
         """
         assert obj._p_jar is self
         if obj._p_oid is None:
-            # There is some old Zope code that assigns _p_jar
-            # directly.  That is no longer allowed, but we need to
-            # provide support for old code that still does it.
-
             # The actual complaint here is that an object without
             # an oid is being registered.  I can't think of any way to
             # achieve that without assignment to _p_jar.  If there is
-            # a way, this will be a very confusing warning.
-            deprecated36("Assigning to _p_jar is deprecated, and will be "
-                         "changed to raise an exception.")
+            # a way, this will be a very confusing exception.
+            raise ValueError("assigning to _p_jar is not supported")
         elif obj._p_oid in self._added:
             # It was registered before it was added to _added.
             return
@@ -961,7 +954,7 @@
 
         if transaction_manager is None:
             transaction_manager = transaction.manager
-        
+
         self.transaction_manager = transaction_manager
 
         if self._reset_counter != global_reset_counter:
@@ -1036,49 +1029,8 @@
     ##########################################################################
     # DEPRECATED methods
 
-    def cacheFullSweep(self, dt=None):
-        deprecated36("cacheFullSweep is deprecated. "
-                     "Use cacheMinimize instead.")
-        if dt is None:
-            self._cache.full_sweep()
-        else:
-            self._cache.full_sweep(dt)
+    # None at present.
 
-    def getTransaction(self):
-        """Get the current transaction for this connection.
-
-        :deprecated:
-
-        The transaction manager's get method works the same as this
-        method.  You can pass a transaction manager (TM) to DB.open()
-        to control which TM the Connection uses.
-        """
-        deprecated36("getTransaction() is deprecated. "
-                     "Use the transaction_manager argument "
-                     "to DB.open() instead, or access "
-                     ".transaction_manager directly on the Connection.")
-        return self.transaction_manager.get()
-
-    def setLocalTransaction(self):
-        """Use a transaction bound to the connection rather than the thread.
-
-        :deprecated:
-
-        Returns the transaction manager used by the connection.  You
-        can pass a transaction manager (TM) to DB.open() to control
-        which TM the Connection uses.
-        """
-        deprecated36("setLocalTransaction() is deprecated. "
-                     "Use the transaction_manager argument "
-                     "to DB.open() instead.")
-        if self.transaction_manager is transaction.manager:
-            if self._synch:
-                self.transaction_manager.unregisterSynch(self)
-            self.transaction_manager = transaction.TransactionManager()
-            if self._synch:
-                self.transaction_manager.registerSynch(self)
-        return self.transaction_manager
-
     # DEPRECATED methods
     ##########################################################################
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/DB.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/DB.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/DB.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -25,7 +25,6 @@
 from ZODB.Connection import Connection
 from ZODB.serialize import referencesf
 from ZODB.utils import WeakSet
-from ZODB.utils import DEPRECATED_ARGUMENT, deprecated36
 
 from zope.interface import implements
 from ZODB.interfaces import IDatabase
@@ -119,6 +118,19 @@
         while len(self.available) > target:
             c = self.available.pop(0)
             self.all.remove(c)
+            # While application code may still hold a reference to `c`,
+            # there's little useful that can be done with this Connection
+            # anymore.  Its cache may be holding on to limited resources,
+            # and we replace the cache with an empty one now so that we
+            # don't have to wait for gc to reclaim it.  Note that it's not
+            # possible for DB.open() to return `c` again:  `c` can never
+            # be in an open state again.
+            # TODO:  Perhaps it would be better to break the reference
+            # cycles between `c` and `c._cache`, so that refcounting reclaims
+            # both right now.  But if user code _does_ have a strong
+            # reference to `c` now, breaking the cycle would not reclaim `c`
+            # now, and `c` would be left in a user-visible crazy state.
+            c._resetCache()
 
     # Pop an available connection and return it, or return None if none are
     # available.  In the latter case, the caller should create a new
@@ -177,9 +189,6 @@
         cacheFullSweep, cacheLastGCTime, cacheMinimize, cacheSize,
         cacheDetailSize, getCacheSize, getVersionCacheSize, setCacheSize,
         setVersionCacheSize
-      - `Deprecated Methods`: getCacheDeactivateAfter,
-        setCacheDeactivateAfter,
-        getVersionCacheDeactivateAfter, setVersionCacheDeactivateAfter
     """
     implements(IDatabase)
 
@@ -189,12 +198,10 @@
     def __init__(self, storage,
                  pool_size=7,
                  cache_size=400,
-                 cache_deactivate_after=DEPRECATED_ARGUMENT,
                  version_pool_size=3,
                  version_cache_size=100,
                  database_name='unnamed',
                  databases=None,
-                 version_cache_deactivate_after=DEPRECATED_ARGUMENT,
                  ):
         """Create an object database.
 
@@ -206,8 +213,6 @@
             version)
           - `version_cache_size`: target size of Connection object cache for
             version connections
-          - `cache_deactivate_after`: ignored
-          - `version_cache_deactivate_after`: ignored
         """
         # Allocate lock.
         x = threading.RLock()
@@ -222,12 +227,6 @@
         self._version_pool_size = version_pool_size
         self._version_cache_size = version_cache_size
 
-        # warn about use of deprecated arguments
-        if cache_deactivate_after is not DEPRECATED_ARGUMENT:
-            deprecated36("cache_deactivate_after has no effect")
-        if version_cache_deactivate_after is not DEPRECATED_ARGUMENT:
-            deprecated36("version_cache_deactivate_after has no effect")
-
         self._miv_cache = {}
 
         # Setup storage
@@ -494,10 +493,7 @@
     def objectCount(self):
         return len(self._storage)
 
-    def open(self, version='',
-             transaction=DEPRECATED_ARGUMENT, temporary=DEPRECATED_ARGUMENT,
-             force=DEPRECATED_ARGUMENT, waitflag=DEPRECATED_ARGUMENT,
-             mvcc=True, txn_mgr=DEPRECATED_ARGUMENT,
+    def open(self, version='', mvcc=True,
              transaction_manager=None, synch=True):
         """Return a database Connection for use by application code.
 
@@ -518,29 +514,6 @@
              register for afterCompletion() calls.
         """
 
-        if temporary is not DEPRECATED_ARGUMENT:
-            deprecated36("DB.open() temporary= ignored. "
-                         "open() no longer blocks.")
-
-        if force is not DEPRECATED_ARGUMENT:
-            deprecated36("DB.open() force= ignored. "
-                         "open() no longer blocks.")
-
-        if waitflag is not DEPRECATED_ARGUMENT:
-            deprecated36("DB.open() waitflag= ignored. "
-                         "open() no longer blocks.")
-
-        if transaction is not DEPRECATED_ARGUMENT:
-            deprecated36("DB.open() transaction= ignored.")
-
-        if txn_mgr is not DEPRECATED_ARGUMENT:
-            deprecated36("use transaction_manager= instead of txn_mgr=")
-            if transaction_manager is None:
-                transaction_manager = txn_mgr
-            else:
-                raise ValueError("cannot specify both transaction_manager= "
-                                 "and txn_mgr=")
-
         self._a()
         try:
             # pool <- the _ConnectionPool for this version
@@ -567,7 +540,7 @@
 
             # Tell the connection it belongs to self.
             result.open(transaction_manager, mvcc, synch)
-            
+
             # A good time to do some cache cleanup.
             self._connectionMap(lambda c: c.cacheGC())
 
@@ -706,24 +679,9 @@
     def versionEmpty(self, version):
         return self._storage.versionEmpty(version)
 
-    # The following methods are deprecated and have no effect
+resource_counter_lock = threading.Lock()
+resource_counter = 0
 
-    def getCacheDeactivateAfter(self):
-        """Deprecated"""
-        deprecated36("getCacheDeactivateAfter has no effect")
-
-    def getVersionCacheDeactivateAfter(self):
-        """Deprecated"""
-        deprecated36("getVersionCacheDeactivateAfter has no effect")
-
-    def setCacheDeactivateAfter(self, v):
-        """Deprecated"""
-        deprecated36("setCacheDeactivateAfter has no effect")
-
-    def setVersionCacheDeactivateAfter(self, v):
-        """Deprecated"""
-        deprecated36("setVersionCacheDeactivateAfter has no effect")
-
 class ResourceManager(object):
     """Transaction participation for a version or undo resource."""
 
@@ -734,8 +692,20 @@
         self.tpc_finish = self._db._storage.tpc_finish
         self.tpc_abort = self._db._storage.tpc_abort
 
+        # Get a number from a simple thread-safe counter, then
+        # increment it, for the purpose of sorting ResourceManagers by
+        # creation order.  This ensures that multiple ResourceManagers
+        # within a transaction commit in a predictable sequence.
+        resource_counter_lock.acquire()
+        try:
+            global resource_counter
+            self._count = resource_counter
+            resource_counter += 1
+        finally:
+            resource_counter_lock.release()
+
     def sortKey(self):
-        return "%s:%s" % (self._db._storage.sortKey(), id(self))
+        return "%s:%016x" % (self._db._storage.sortKey(), self._count)
 
     def tpc_begin(self, txn, sub=False):
         if sub:

Modified: ZODB/branches/blob-merge-branch/src/ZODB/DemoStorage.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/DemoStorage.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/DemoStorage.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -21,7 +21,7 @@
   - Provide a volatile storage that is useful for giving demonstrations.
 
 The demo storage can have a "base" storage that is used in a
-read-only fashion. The base storage must not not to contain version
+read-only fashion. The base storage must not contain version
 data.
 
 There are three main data structures:

Modified: ZODB/branches/blob-merge-branch/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/FileStorage/FileStorage.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/FileStorage/FileStorage.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -890,7 +890,7 @@
                 self._file.write(p64(tl))
                 self._file.flush()
             except:
-                # Hm, an error occured writing out the data. Maybe the
+                # Hm, an error occurred writing out the data. Maybe the
                 # disk is full. We don't want any turd at the end.
                 self._file.truncate(self._pos)
                 raise
@@ -993,8 +993,12 @@
             return "", None
 
     def _transactionalUndoRecord(self, oid, pos, tid, pre, version):
-        """Get the indo information for a data record
+        """Get the undo information for a data record
 
+        'pos' points to the data header for 'oid' in the transaction
+        being undone.  'tid' refers to the transaction being undone.
+        'pre' is the 'prev' field of the same data header.
+
         Return a 5-tuple consisting of a pickle, data pointer,
         version, packed non-version data pointer, and current
         position.  If the pickle is true, then the data pointer must

Modified: ZODB/branches/blob-merge-branch/src/ZODB/POSException.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/POSException.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/POSException.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -31,7 +31,7 @@
         return oid_repr(self.args[0])
 
 class TransactionError(POSError):
-    """An error occured due to normal transaction processing."""
+    """An error occurred due to normal transaction processing."""
 
 class TransactionFailedError(POSError):
     """Cannot perform an operation on a transaction that previously failed.
@@ -252,7 +252,7 @@
         return _fmt_undo(self._oid, self._reason)
 
 class MultipleUndoErrors(UndoError):
-    """Several undo errors occured during a single transaction."""
+    """Several undo errors occurred during a single transaction."""
 
     def __init__(self, errs):
         # provide a reason and oid for clients that only look at that

Modified: ZODB/branches/blob-merge-branch/src/ZODB/__init__.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/__init__.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/__init__.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -13,10 +13,9 @@
 ##############################################################################
 
 # The next line must use double quotes, so release.py recognizes it.
-__version__ = "3.6.0a3"
+__version__ = "3.7.0a0"
 
 import sys
-import __builtin__
 
 from persistent import TimeStamp
 from persistent import list
@@ -30,9 +29,3 @@
 del mapping, list, sys
 
 from DB import DB
-
-# TODO:  get_transaction() scheduled to go away in ZODB 3.6.
-from transaction import get_transaction
-__builtin__.get_transaction = get_transaction
-
-del __builtin__

Modified: ZODB/branches/blob-merge-branch/src/ZODB/collaborations.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/collaborations.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/collaborations.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,14 +1,27 @@
+=======================
+Collabortation Diagrams
+=======================
+
+This file contains several collaboration diagrams for the ZODB.
+
+Simple fetch, modify, commit
+============================
+
 Participants
-    DB:  ZODB.DB.DB
-    C:  ZODB.Connection.Connection
-    S:  ZODB.FileStorage.FileStorage
-    T:  transaction.interfaces.ITransaction
-    TM:  transaction.interfaces.ITransactionManager
-    o1, o2, ...:  pre-existing persistent objects
+------------
 
+- ``DB``:  ``ZODB.DB.DB``
+- ``C``:  ``ZODB.Connection.Connection``
+- ``S``:  ``ZODB.FileStorage.FileStorage``
+- ``T``:  ``transaction.interfaces.ITransaction``
+- ``TM``: ``transaction.interfaces.ITransactionManager``
+- ``o1``, ``o2``, ...:  pre-existing persistent objects
+
 Scenario
-    """Simple fetch, modify, commit."""
+--------
 
+::
+
     DB.open()
         create C
         TM.registerSynch(C)
@@ -50,17 +63,24 @@
             # transactions.
 
 
+Simple fetch, modify, abort
+===========================
+
 Participants
-    DB:  ZODB.DB.DB
-    C:  ZODB.Connection.Connection
-    S:  ZODB.FileStorage.FileStorage
-    T:  transaction.interfaces.ITransaction
-    TM:  transaction.interfaces.ITransactionManager
-    o1, o2, ...:  pre-existing persistent objects
+------------
 
+- ``DB``:  ``ZODB.DB.DB``
+- ``C``:  ``ZODB.Connection.Connection``
+- ``S``:  ``ZODB.FileStorage.FileStorage``
+- ``T``:  ``transaction.interfaces.ITransaction``
+- ``TM``: ``transaction.interfaces.ITransactionManager``
+- ``o1``, ``o2``, ...:  pre-existing persistent objects
+
 Scenario
-    """Simple fetch, modify, abort."""
+--------
 
+::
+
     DB.open()
         create C
         TM.registerSynch(C)
@@ -91,16 +111,23 @@
             # transactions.
 
 
-Participants:
-  T: ITransaction
-  o1, o2, o3: some persistent objects
-  C1, C2, C3: resource managers
-  S1, S2: Transaction savepoint objects
-  s11, s21, s22: resource-manager savepoints
+Rollback of a savepoint
+=======================
 
+Participants
+------------
+
+- ``T``:  ``transaction.interfaces.ITransaction``
+- ``o1``, ``o2``, ``o3``: some persistent objects
+- ``C1``, ``C2``, ``C3``: resource managers
+- ``S1``, ``S2``: Transaction savepoint objects
+- ``s11``, ``s21``, ``s22``: resource-manager savepoints
+
 Scenario
-    """Rollback of a savepoint"""
+--------
 
+::
+
         create T
         o1.modify()
             C1.regisiter(o1)
@@ -140,8 +167,8 @@
                         o2.invalidate()
                         # truncates temporary storage to beginning, because
                         # s22 was the first savepoint.  (Perhaps conection
-                        # savepoints record the log position before the 
-                        # data were written, which is 0 in this case. 
+                        # savepoints record the log position before the
+                        # data were written, which is 0 in this case.
         T.commit()
             C1.beforeCompletion(T)
             C2.beforeCompletion(T)

Modified: ZODB/branches/blob-merge-branch/src/ZODB/component.xml
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/component.xml	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/component.xml	2006-02-17 22:01:38 UTC (rev 41650)
@@ -158,9 +158,40 @@
                implements="ZODB.database">
     <section type="ZODB.storage" name="*" attribute="storage"/>
     <key name="cache-size" datatype="integer" default="5000"/>
+      <description>
+        Target size, in number of objects, of each connection's
+        object cache.
+      </description>
     <key name="pool-size" datatype="integer" default="7"/>
+      <description>
+        The expected maximum number of simultaneously open connections.
+        There is no hard limit (as many connections as are requested
+        will be opened, until system resources are exhausted).  Exceeding
+        pool-size connections causes a warning message to be logged,
+        and exceeding twice pool-size connections causes a critical
+        message to be logged.
+      </description>
     <key name="version-pool-size" datatype="integer" default="3"/>
+      <description>
+        The expected maximum number of connections simultaneously open
+        per version.
+      </description>
     <key name="version-cache-size" datatype="integer" default="100"/>
+      <description>
+        Target size, in number of objects, of each version connection's
+        object cache.
+      </description>
+    <key name="database-name" default="unnamed"/>
+      <description>
+        When multidatabases are in use, this is the name given to this
+        database in the collection.  The name must be unique across all
+        databases in the collection.  The collection must also be given
+        a mapping from its databases' names to their databases, but that
+        cannot be specified in a ZODB config file.  Applications using
+        multidatabases typical supply a way to configure the mapping in
+        their own config files, using the "databases" parameter of a DB
+        constructor.
+      </description>
   </sectiontype>
 
   <sectiontype name="blobstorage" datatype=".BlobStorage"

Modified: ZODB/branches/blob-merge-branch/src/ZODB/config.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/config.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/config.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -92,7 +92,7 @@
 
 class ZODBDatabase(BaseConfig):
 
-    def open(self, database_name='unnamed', databases=None):
+    def open(self, databases=None):
         section = self.config
         storage = section.storage.open()
         try:
@@ -101,8 +101,8 @@
                            cache_size=section.cache_size,
                            version_pool_size=section.version_pool_size,
                            version_cache_size=section.version_cache_size,
-                           databases=databases,
-                           database_name=database_name)
+                           database_name=section.database_name,
+                           databases=databases)
         except:
             storage.close()
             raise

Modified: ZODB/branches/blob-merge-branch/src/ZODB/cross-database-references.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/cross-database-references.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/cross-database-references.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,3 +1,4 @@
+=========================
 Cross-Database References
 =========================
 
@@ -36,7 +37,7 @@
     >>> tm.commit()
 
 Now, let's open a separate connection to database 2.  We use it to
-read p2, use p2 to get to p1, and verify that it is in database 1:
+read `p2`, use `p2` to get to `p1`, and verify that it is in database 1:
 
     >>> conn = db2.open()
     >>> p2x = conn.root()['p']
@@ -77,8 +78,8 @@
     >>> p1.p4 = p4
     >>> p2.p4 = p4
 
-In this example, the new object is reachable from both p1 in database
-1 and p2 in database 2.  If we commit, which database will p4 end up
+In this example, the new object is reachable from both `p1` in database
+1 and `p2` in database 2.  If we commit, which database will `p4` end up
 in?  This sort of ambiguity can lead to subtle bugs. For that reason,
 an error is generated if we commit changes when new objects are
 reachable from multiple databases:
@@ -126,7 +127,7 @@
     >>> p1.p5 = p5
     >>> p2.p5 = p5
     >>> conn1.add(p5)
-    >>> tm.commit() 
+    >>> tm.commit()
     >>> p5._p_jar.db().database_name
     '1'
 
@@ -141,6 +142,7 @@
 missing:
 
 cross-database garbage collection
+
     Garbage collection is done on a database by database basis.
     If an object on a database only has references to it from other
     databases, then the object will be garbage collected when its
@@ -148,11 +150,13 @@
     broken.
 
 cross-database undo
+
     Undo is only applied to a single database.  Fixing this for
     multiple databases is going to be extremely difficult.  Undo
     currently poses consistency problems, so it is not (or should not
     be) widely used.
 
 Cross-database aware (tolerant) export/import
+
     The export/import facility needs to be aware, at least, of cross-database
     references.

Modified: ZODB/branches/blob-merge-branch/src/ZODB/persistentclass.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/persistentclass.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/persistentclass.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,3 +1,4 @@
+==================
 Persistent Classes
 ==================
 
@@ -39,7 +40,7 @@
 
 Also note that we explictly set the module.  Persistent classes don't
 live in normal Python modules. Rather, they live in the database.  We
-use information in __module__ to record where in the database.  When
+use information in ``__module__`` to record where in the database.  When
 we want to use a database, we will need to supply a custom class
 factory to load instances of the class.
 
@@ -176,7 +177,7 @@
 Instances of Persistent Classes
 -------------------------------
 
-We can, of course, store instances of perstent classes in the
+We can, of course, store instances of persistent classes in the
 database:
 
     >>> c.color = 'blue'
@@ -189,7 +190,7 @@
 
 NOTE: If a non-persistent instance of a persistent class is copied,
       the class may be copied as well. This is usually not the desired
-      result. 
+      result.
 
 
 Persistent instances of persistent classes
@@ -228,10 +229,10 @@
     >>> connection2.root()['obs']['p']
     <persistent broken __zodb__.P instance '\x00\x00\x00\x00\x00\x00\x00\x04'>
 
-because the module, "__zodb__" can't be loaded.  We need to provide a
+because the module, `__zodb__` can't be loaded.  We need to provide a
 class factory that knows about this special module. Here we'll supply a
 sample class factory that looks up a class name in the database root
-if the module is "__zodb__".  It falls back to the normal class lookup 
+if the module is `__zodb__`.  It falls back to the normal class lookup
 for other modules:
 
     >>> from ZODB.broken import find_global
@@ -242,7 +243,7 @@
 
     >>> some_database.classFactory = classFactory
 
-Normally, the classFactory should be set before a database is opened. 
+Normally, the classFactory should be set before a database is opened.
 We'll reopen the connections we're using.  We'll assign the old
 connections to a variable first to prevent getting them from the
 connection pool:
@@ -250,7 +251,7 @@
     >>> old = connection, connection2
     >>> connection = some_database.open(transaction_manager=tm)
     >>> connection2 = some_database.open(transaction_manager=tm2)
-   
+
 Now, we can read the object:
 
     >>> connection2.root()['obs']['p'].color

Modified: ZODB/branches/blob-merge-branch/src/ZODB/serialize.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/serialize.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/serialize.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -340,7 +340,7 @@
             if self._jar.get_connection(database_name) is not obj._p_jar:
                 raise InvalidObjectReference(
                     "Attempt to store a reference to an object from "
-                    "a separate onnection to the same database or "
+                    "a separate connection to the same database or "
                     "multidatabase"
                     )
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/subtransactions.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/subtransactions.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/subtransactions.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -8,44 +8,44 @@
 indicating whether it is a subtransaction or a top-level transaction.
 Consider the following exampler commit calls:
 
-- commit()
+- ``commit()``
 
   A regular top-level transaction is committed.
 
-- commit(1)
+- ``commit(1)``
 
   A subtransaction is committed. There is now one subtransaction of
   the current top-level transaction.
 
-- commit(1)
+- ``commit(1)``
 
   A subtransaction is committed. There are now two subtransactions of
   the current top-level transaction.
 
-- abort(1)
+- ``abort(1)``
 
   A subtransaction is aborted. There are still two subtransactions of
   the current top-level transaction; work done since the last
-  commit(1) call is discarded.
+  ``commit(1)`` call is discarded.
 
-- commit()
+- ``commit()``
 
   We now commit a top-level transaction. The work done in the previous
-  two subtransactions *plus* work done since the last abort(1) call
+  two subtransactions *plus* work done since the last ``abort(1)`` call
   is saved.
 
-- commit(1)
+- ``commit(1)``
 
   A subtransaction is committed. There is now one subtransaction of
   the current top-level transaction.
 
-- commit(1)
+- ``commit(1)``
 
   A subtransaction is committed. There are now two subtransactions of
   the current top-level transaction.
 
-- abort()
+- ``abort()``
 
   We now abort a top-level transaction. We discard the work done in
   the previous two subtransactions *plus* work done since the last
-  commit(1) call.
+  ``commit(1)`` call.

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/dbopen.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/dbopen.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/dbopen.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -272,6 +272,54 @@
     >>> len(pool.available), len(pool.all)
     (0, 2)
 
+Next:  when a closed Connection is removed from .available due to exceeding
+pool_size, that Connection's cache is cleared (this behavior was new in
+ZODB 3.6b6).  While user code may still hold a reference to that
+Connection, once it vanishes from .available it's really not usable for
+anything sensible (it can never be in the open state again).  Waiting for
+gc to reclaim the Connection and its cache eventually works, but that can
+take "a long time" and caches can hold on to many objects, and limited
+resources (like RDB connections), for the duration.
+
+    >>> st.close()
+    >>> st = Storage()
+    >>> db = DB(st, pool_size=2)
+    >>> conn0 = db.open()
+    >>> len(conn0._cache)  # empty now
+    0
+    >>> import transaction
+    >>> conn0.root()['a'] = 1
+    >>> transaction.commit()
+    >>> len(conn0._cache)  # but now the cache holds the root object
+    1
+
+Now open more connections so that the total exceeds pool_size (2):
+
+    >>> conn1 = db.open()
+    >>> conn2 = db.open()
+    >>> pool = db._pools['']
+    >>> len(pool.all), len(pool.available)  # all Connections are in use
+    (3, 0)
+
+Return pool_size (2) Connections to the pool:
+
+    >>> conn0.close()
+    >>> conn1.close()
+    >>> len(pool.all), len(pool.available)
+    (3, 2)
+    >>> len(conn0._cache)  # nothing relevant has changed yet
+    1
+
+When we close the third connection, conn0 will be booted from .all, and
+we expect its cache to be cleared then:
+
+    >>> conn2.close()
+    >>> len(pool.all), len(pool.available)
+    (2, 2)
+    >>> len(conn0._cache)  # conn0's cache is empty again
+    0
+    >>> del conn0, conn1, conn2
+
 Clean up.
 
     >>> st.close()

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/multidb.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/multidb.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/multidb.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,20 +1,7 @@
-##############################################################################
-#
-# Copyright (c) 2005 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
+==================
+Multiple Databases
+==================
 
-Multi-database tests
-====================
-
 Multi-database support adds the ability to tie multiple databases into a
 collection.  The original proposal is in the fishbowl:
 
@@ -25,29 +12,29 @@
 
 No private attributes were added, and one new method was introduced.
 
-DB:
+``DB``:
 
-- a new .database_name attribute holds the name of this database
+- a new ``.database_name`` attribute holds the name of this database.
 
-- a new .databases attribute maps from database name to DB object; all DBs
-  in a multi-database collection share the same .databases object
+- a new ``.databases`` attribute maps from database name to ``DB`` object; all
+  databases in a multi-database collection share the same ``.databases`` object
 
-- the DB constructor has new optional arguments with the same names
-  (database_name= and databases=).
+- the ``DB`` constructor has new optional arguments with the same names
+  (``database_name=`` and ``databases=``).
 
-Connection:
+``Connection``:
 
-- a new .connections attribute maps from database name to a Connection for
-  the database with that name; the .connections mapping object is also
-  shared among databases in a collection
+- a new ``.connections`` attribute maps from database name to a ``Connection``
+  for the database with that name; the ``.connections`` mapping object is also
+  shared among databases in a collection.
 
-- a new .get_connection(database_name) method returns a Connection for a
-  database in the collection; if a connection is already open, it's returned
-  (this is the value .connections[database_name]), else a new connection is
-  opened (and stored as .connections[database_name])
+- a new ``.get_connection(database_name)`` method returns a ``Connection`` for
+  a database in the collection; if a connection is already open, it's returned
+  (this is the value ``.connections[database_name]``), else a new connection
+  is opened (and stored as ``.connections[database_name]``)
 
 
-Creating a multi-database starts with creating a named DB:
+Creating a multi-database starts with creating a named ``DB``:
 
     >>> from ZODB.tests.test_storage import MinimalMemoryStorage
     >>> from ZODB import DB
@@ -69,7 +56,8 @@
     ...     database_name='notroot',
     ...     databases=dbmap)
 
-The new db2 now shares the 'databases' dictionary with db and has two entries:
+The new ``db2`` now shares the ``databases`` dictionary with db and has two
+entries:
 
     >>> db2.databases is db.databases is dbmap
     True
@@ -87,7 +75,7 @@
         ...
     ValueError: database_name 'root' already in databases
 
-Because that failed, db.databases wasn't changed:
+Because that failed, ``db.databases`` wasn't changed:
 
     >>> len(db.databases)  # still 2
     2
@@ -127,7 +115,7 @@
     >>> names = cn.connections.keys(); names.sort(); print names
     ['notroot', 'root']
 
-So long as this database group remains open, the same Connection objects
+So long as this database group remains open, the same ``Connection`` objects
 are returned:
 
     >>> cn.get_connection('root') is cn
@@ -151,3 +139,59 @@
 
     >>> for a_db in dbmap.values():
     ...     a_db.close()
+
+
+Configuration from File
+-----------------------
+
+The database name can also be specified in a config file, starting in
+ZODB 3.6:
+
+    >>> from ZODB.config import databaseFromString
+    >>> config = """
+    ... <zodb>
+    ...   <mappingstorage/>
+    ...   database-name this_is_the_name
+    ... </zodb>
+    ... """
+    >>> db = databaseFromString(config)
+    >>> print db.database_name
+    this_is_the_name
+    >>> db.databases.keys()
+    ['this_is_the_name']
+
+However, the ``.databases`` attribute cannot be configured from file.  It
+can be passed to the `ZConfig` factory.  I'm not sure of the clearest way
+to test that here; this is ugly:
+
+    >>> from ZODB.config import getDbSchema
+    >>> import ZConfig
+    >>> from cStringIO import StringIO
+
+Derive a new `config2` string from the `config` string, specifying a
+different database_name:
+
+    >>> config2 = config.replace("this_is_the_name", "another_name")
+
+Now get a `ZConfig` factory from `config2`:
+
+    >>> f = StringIO(config2)
+    >>> zconfig, handle = ZConfig.loadConfigFile(getDbSchema(), f)
+    >>> factory = zconfig.database
+
+The desired ``databases`` mapping can be passed to this factory:
+
+    >>> db2 = factory.open(databases=db.databases)
+    >>> print db2.database_name   # has the right name
+    another_name
+    >>> db.databases is db2.databases # shares .databases with `db`
+    True
+    >>> all = db2.databases.keys()
+    >>> all.sort()
+    >>> all   # and db.database_name & db2.database_name are the keys
+    ['another_name', 'this_is_the_name']
+
+Cleanup.
+
+    >>> db.close()
+    >>> db2.close()

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/synchronizers.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/synchronizers.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/synchronizers.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,7 +1,11 @@
-Here are some tests that storage sync() methods get called at appropriate
+=============
+Synchronizers
+=============
+
+Here are some tests that storage ``sync()`` methods get called at appropriate
 times in the life of a transaction.  The tested behavior is new in ZODB 3.4.
 
-First define a lightweight storage with a sync() method:
+First define a lightweight storage with a ``sync()`` method:
 
     >>> import ZODB
     >>> from ZODB.MappingStorage import MappingStorage
@@ -27,14 +31,14 @@
     False
 
 
-sync is called by the Connection's afterCompletion() hook after the commit
-completes.
+``sync()`` is called by the Connection's ``afterCompletion()`` hook after the
+commit completes.
 
     >>> transaction.commit()
     >>> st.sync_called  # False before 3.4
     True
 
-sync is also called by the afterCompletion() hook after an abort.
+``sync()`` is also called by the ``afterCompletion()`` hook after an abort.
 
     >>> st.sync_called = False
     >>> rt['b'] = 2
@@ -42,8 +46,8 @@
     >>> st.sync_called  # False before 3.4
     True
 
-And sync is called whenever we explicitly start a new txn, via the
-newTransaction() hook.
+And ``sync()`` is called whenever we explicitly start a new transaction, via
+the ``newTransaction()`` hook.
 
     >>> st.sync_called = False
     >>> dummy = transaction.begin()
@@ -51,19 +55,19 @@
     True
 
 Clean up.  Closing db isn't enough -- closing a DB doesn't close its
-Connections.  Leaving our Connection open here can cause the
-SimpleStorage.sync() method to get called later, during another test, and
-our doctest-synthesized module globals no longer exist then.  You get
-a weird traceback then ;-)
+`Connections`.  Leaving our `Connection` open here can cause the
+``SimpleStorage.sync()`` method to get called later, during another test, and
+our doctest-synthesized module globals no longer exist then.  You get a weird
+traceback then ;-)
 
     >>> cn.close()
 
 One more, very obscure.  It was the case that if the first action a new
-threaded transaction manager saw was a begin() call, then synchronizers
-registered after that in the same transaction weren't communicated to
-the Transaction object, and so the synchronizers' afterCompletion() hooks
+threaded transaction manager saw was a ``begin()`` call, then synchronizers
+registered after that in the same transaction weren't communicated to the
+`Transaction` object, and so the synchronizers' ``afterCompletion()`` hooks
 weren't called when the transaction commited.  None of the test suites
-(ZODB's, Zope 2.8's, or Zope3's) caught that, but apparently Zope3 takes this
+(ZODB's, Zope 2.8's, or Zope3's) caught that, but apparently Zope 3 takes this
 path at some point when serving pages.
 
     >>> tm = transaction.ThreadTransactionManager()
@@ -75,14 +79,14 @@
     >>> st.sync_called
     False
 
-Now ensure that cn.afterCompletion() -> st.sync() gets called by commit
-despite that the Connection registered after the transaction began:
+Now ensure that ``cn.afterCompletion() -> st.sync()`` gets called by commit
+despite that the `Connection` registered after the transaction began:
 
     >>> tm.commit()
     >>> st.sync_called
     True
 
-And try the same thing with a non-threaded TM:
+And try the same thing with a non-threaded transaction manager:
 
     >>> cn.close()
     >>> tm = transaction.TransactionManager()

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnection.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnection.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnection.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -390,11 +390,11 @@
         """
 
     def test_cache(self):
-        r"""doctest of cacheMinimize() and cacheFullSweep() methods.
+        r"""doctest of cacheMinimize().
 
-        These tests are fairly minimal, just verifying that the
-        methods can be called and have some effect.  We need other
-        tests that verify the cache works as intended.
+        Thus test us minimal, just verifying that the method can be called
+        and has some effect.  We need other tests that verify the cache works
+        as intended.
 
         >>> db = databaseFromString("<zodb>\n<mappingstorage/>\n</zodb>")
         >>> cn = db.open()
@@ -403,71 +403,12 @@
         >>> r._p_state
         -1
 
-        The next couple of tests are involved because they have to
-        cater to backwards compatibility issues.  The cacheMinimize()
-        method used to take an argument, but now ignores it.
-        cacheFullSweep() used to do something different than
-        cacheMinimize(), but it doesn't anymore.  We want to verify
-        that these methods do something, but all cause deprecation
-        warnings.  To do that, we need a warnings hook.
-
-        >>> hook = WarningsHook()
-        >>> hook.install()
-
-        More problems in case this test is run more than once:  fool the
-        warnings module into delivering the warnings despite that they've
-        been seen before.
-
-        >>> import warnings
-        >>> warnings.filterwarnings("always", category=DeprecationWarning)
-
         >>> r._p_activate()
-        >>> cn.cacheMinimize(12)
-        >>> r._p_state
-        -1
-        >>> len(hook.warnings)
-        1
-        >>> message, category, filename, lineno = hook.warnings[0]
-        >>> print message
-        This will be removed in ZODB 3.6:
-        cacheMinimize() dt= is ignored.
-        >>> category.__name__
-        'DeprecationWarning'
-        >>> hook.clear()
-
-        cacheFullSweep() is a doozy.  It generates two deprecation
-        warnings, one from the Connection and one from the
-        cPickleCache.  Maybe we should drop the cPickleCache warning,
-        but it's there for now.  When passed an argument, it acts like
-        cacheGC().  When it isn't passed an argument it acts like
-        cacheMinimize().
-
-        >>> r._p_activate()
-        >>> cn.cacheFullSweep(12)
-        >>> r._p_state
+        >>> r._p_state  # up to date
         0
-        >>> len(hook.warnings)
-        2
-        >>> message, category, filename, lineno = hook.warnings[0]
-        >>> print message
-        This will be removed in ZODB 3.6:
-        cacheFullSweep is deprecated. Use cacheMinimize instead.
-        >>> category.__name__
-        'DeprecationWarning'
-        >>> message, category, filename, lineno = hook.warnings[1]
-        >>> message
-        'No argument expected'
-        >>> category.__name__
-        'DeprecationWarning'
-
-        We have to uninstall the hook so that other warnings don't get lost.
-
-        >>> hook.uninstall()
-
-        Obscure:  There is no API call for removing the filter we added, but
-        filters appears to be a public variable.
-
-        >>> del warnings.filters[0]
+        >>> cn.cacheMinimize()
+        >>> r._p_state  # ghost again
+        -1
         """
 
 class InvalidationTests(unittest.TestCase):

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnectionSavepoint.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnectionSavepoint.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testConnectionSavepoint.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,17 +1,19 @@
+==========
 Savepoints
 ==========
 
-Savepoints provide a way to save to disk intermediate work done during
-a transaction allowing:
+Savepoints provide a way to save to disk intermediate work done during a
+transaction allowing:
 
 - partial transaction (subtransaction) rollback (abort)
 
 - state of saved objects to be freed, freeing on-line memory for other
   uses
 
-Savepoints make it possible to write atomic subroutines that don't
-make top-level transaction commitments.
+Savepoints make it possible to write atomic subroutines that don't make
+top-level transaction commitments.
 
+
 Applications
 ------------
 
@@ -39,13 +41,13 @@
     >>> root['name']
     'bob'
 
-Now, let's look at an application that manages funds for people.
-It allows deposits and debits to be entered for multiple people.
-It accepts a sequence of entries and generates a sequence of status
-messages.  For each entry, it applies the change and then validates
-the user's account.  If the user's account is invalid, we roll back
-the change for that entry.  The success or failure of an entry is
-indicated in the output status.  First we'll initialize some accounts:
+Now, let's look at an application that manages funds for people.  It allows
+deposits and debits to be entered for multiple people.  It accepts a sequence
+of entries and generates a sequence of status messages.  For each entry, it
+applies the change and then validates the user's account.  If the user's
+account is invalid, we roll back the change for that entry.  The success or
+failure of an entry is indicated in the output status.  First we'll initialize
+some accounts:
 
     >>> root['bob-balance'] = 0.0
     >>> root['bob-credit'] = 0.0
@@ -59,8 +61,8 @@
     ...     if root[name+'-balance'] + root[name+'-credit'] < 0:
     ...         raise ValueError('Overdrawn', name)
 
-And a function to apply entries.  If the function fails in some
-unexpected way, it rolls back all of its changes and prints the error:
+And a function to apply entries.  If the function fails in some unexpected
+way, it rolls back all of its changes and prints the error:
 
     >>> def apply_entries(entries):
     ...     savepoint = transaction.savepoint()
@@ -114,9 +116,9 @@
     Updated sally
     Unexpected exception unsupported operand type(s) for +=: 'float' and 'str'
 
-Because the apply_entries used a savepoint for the entire function,
-it was able to rollback the partial changes without rolling back
-changes made in the previous call to apply_entries:
+Because the apply_entries used a savepoint for the entire function, it was
+able to rollback the partial changes without rolling back changes made in the
+previous call to ``apply_entries``:
 
     >>> root['bob-balance']
     30.0
@@ -135,6 +137,7 @@
     >>> root['sally-balance']
     0.0
 
+
 Savepoint invalidation
 ----------------------
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testDB.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testDB.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testDB.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -54,15 +54,6 @@
     # make sure the basic methods are callable
 
     def testSets(self):
-        # test set methods that have non-trivial implementations
-        warnings.filterwarnings("error", category=DeprecationWarning)
-        self.assertRaises(DeprecationWarning,
-                          self.db.setCacheDeactivateAfter, 12)
-        self.assertRaises(DeprecationWarning,
-                          self.db.setVersionCacheDeactivateAfter, 12)
-        # Obscure:  There is no API call for removing the warning we just
-        # added, but filters appears to be a public variable.
-        del warnings.filters[0]
         self.db.setCacheSize(15)
         self.db.setVersionCacheSize(15)
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testZODB.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testZODB.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testZODB.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -213,58 +213,6 @@
             conn1.close()
             conn2.close()
 
-    def checkLocalTransactions(self):
-        # Test of transactions that apply to only the connection,
-        # not the thread.
-        conn1 = self._db.open()
-        conn2 = self._db.open()
-        hook = WarningsHook()
-        hook.install()
-        try:
-            conn1.setLocalTransaction()
-            conn2.setLocalTransaction()
-            r1 = conn1.root()
-            r2 = conn2.root()
-            if r1.has_key('item'):
-                del r1['item']
-                conn1.getTransaction().commit()
-            r1.get('item')
-            r2.get('item')
-            r1['item'] = 1
-            conn1.getTransaction().commit()
-            self.assertEqual(r1['item'], 1)
-            # r2 has not seen a transaction boundary,
-            # so it should be unchanged.
-            self.assertEqual(r2.get('item'), None)
-            conn2.sync()
-            # Now r2 is updated.
-            self.assertEqual(r2['item'], 1)
-
-            # Now, for good measure, send an update in the other direction.
-            r2['item'] = 2
-            conn2.getTransaction().commit()
-            self.assertEqual(r1['item'], 1)
-            self.assertEqual(r2['item'], 2)
-            conn1.sync()
-            conn2.sync()
-            self.assertEqual(r1['item'], 2)
-            self.assertEqual(r2['item'], 2)
-            for msg, obj, filename, lineno in hook.warnings:
-                self.assert_(msg in [
-                    "This will be removed in ZODB 3.6:\n"
-                        "setLocalTransaction() is deprecated. "
-                        "Use the transaction_manager argument "
-                        "to DB.open() instead.",
-                    "This will be removed in ZODB 3.6:\n"
-                        "getTransaction() is deprecated. "
-                        "Use the transaction_manager argument "
-                        "to DB.open() instead, or access "
-                        ".transaction_manager directly on the Connection."])
-        finally:
-            conn1.close()
-            conn2.close()
-            hook.uninstall()
-
     def checkReadConflict(self):
         self.obj = P()
         self.readConflict()
@@ -584,58 +532,9 @@
         # transaction, and, in fact, when this test was written,
         # Transaction.begin() didn't do anything (everything from here
         # down failed).
+        # Later (ZODB 3.6):  Transaction.begin() no longer exists, so the
+        # rest of this test was tossed.
 
-        # Oh, bleech.  Since Transaction.begin is also deprecated, we have
-        # to goof around suppressing the deprecation warning.
-        import warnings
-
-        # First verify that Transaction.begin *is* deprecated, by turning
-        # the warning into an error.
-        warnings.filterwarnings("error", category=DeprecationWarning)
-        self.assertRaises(DeprecationWarning, transaction.get().begin)
-        del warnings.filters[0]
-
-        # Now ignore DeprecationWarnings for the duration.  Use a
-        # try/finally block to ensure we reenable DeprecationWarnings
-        # no matter what.
-        warnings.filterwarnings("ignore", category=DeprecationWarning)
-        try:
-            cn = self._db.open()
-            rt = cn.root()
-            rt['a'] = 1
-
-            transaction.get().begin()  # should abort adding 'a' to the root
-            rt = cn.root()
-            self.assertRaises(KeyError, rt.__getitem__, 'a')
-
-            # A longstanding bug:  this didn't work if changes were only in
-            # subtransactions.
-            transaction.get().begin()
-            rt = cn.root()
-            rt['a'] = 2
-            transaction.get().commit(1)
-
-            transaction.get().begin()
-            rt = cn.root()
-            self.assertRaises(KeyError, rt.__getitem__, 'a')
-
-            # One more time, mixing "top level" and subtransaction changes.
-            transaction.get().begin()
-            rt = cn.root()
-            rt['a'] = 3
-            transaction.get().commit(1)
-            rt['b'] = 4
-
-            transaction.get().begin()
-            rt = cn.root()
-            self.assertRaises(KeyError, rt.__getitem__, 'a')
-            self.assertRaises(KeyError, rt.__getitem__, 'b')
-
-            cn.close()
-
-        finally:
-            del warnings.filters[0]
-
     def checkFailingCommitSticks(self):
         # See also checkFailingSubtransactionCommitSticks.
         # See also checkFailingSavepointSticks.
@@ -829,6 +728,42 @@
         cn.close()
         cn2.close()
 
+    def checkMultipleUndoInOneTransaction(self):
+        # Verify that it's possible to perform multiple undo
+        # operations within a transaction.  If ZODB performs the undo
+        # operations in a nondeterministic order, this test will often
+        # fail.
+
+        conn = self._db.open()
+        try:
+            root = conn.root()
+
+            # Add transactions that set root["state"] to (0..5)
+            for state_num in range(6):
+                transaction.begin()
+                root['state'] = state_num
+                transaction.get().note('root["state"] = %d' % state_num)
+                transaction.commit()
+
+            # Undo all but the first.  Note that no work is actually
+            # performed yet.
+            transaction.begin()
+            log = self._db.undoLog()
+            for i in range(5):
+                self._db.undo(log[i]['id'])
+            transaction.get().note('undo states 1 through 5')
+
+            # Now attempt all those undo operations.
+            transaction.commit()
+
+            # Sanity check: we should be back to the first state.
+            self.assertEqual(root['state'], 0)
+        finally:
+            transaction.abort()
+            conn.close()
+
+        
+
 class PoisonedError(Exception):
     pass
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testcrossdatabasereferences.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testcrossdatabasereferences.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testcrossdatabasereferences.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -56,7 +56,7 @@
     Traceback (most recent call last):
     ...
     InvalidObjectReference: Attempt to store a reference to an object
-    from a separate onnection to the same database or multidatabase
+    from a separate connection to the same database or multidatabase
 
     >>> tm.abort()
 
@@ -72,7 +72,7 @@
     Traceback (most recent call last):
     ...
     InvalidObjectReference: Attempt to store a reference to an object
-    from a separate onnection to the same database or multidatabase
+    from a separate connection to the same database or multidatabase
 
     >>> tm.abort()
 

Modified: ZODB/branches/blob-merge-branch/src/ZODB/tests/testmvcc.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/tests/testmvcc.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/tests/testmvcc.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -37,9 +37,9 @@
 >>> from ZODB import DB
 >>> db = DB(MinimalMemoryStorage())
 
-We will use two different connections with the experimental
-setLocalTransaction() method to make sure that the connections act
-independently, even though they'll be run from a single thread.
+We will use two different connections with different transaction managers
+to make sure that the connections act independently, even though they'll
+be run from a single thread.
 
 >>> import transaction
 >>> tm1 = transaction.TransactionManager()

Modified: ZODB/branches/blob-merge-branch/src/ZODB/transact.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/transact.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/transact.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -14,6 +14,7 @@
 """Tools to simplify transactions within applications."""
 
 from ZODB.POSException import ReadConflictError, ConflictError
+import transaction
 
 def _commit(note):
     t = transaction.get()

Modified: ZODB/branches/blob-merge-branch/src/ZODB/utils.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZODB/utils.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZODB/utils.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -39,7 +39,6 @@
            'readable_tid_repr',
            'WeakSet',
            'DEPRECATED_ARGUMENT',
-           'deprecated36',
            'deprecated37',
            'deprecated38',
            'get_pickle_metadata',
@@ -55,13 +54,6 @@
 DEPRECATED_ARGUMENT = object()
 
 # Raise DeprecationWarning, noting that the deprecated thing will go
-# away in ZODB 3.6.  Point to the caller of our caller (i.e., at the
-# code using the deprecated thing).
-def deprecated36(msg):
-    warnings.warn("This will be removed in ZODB 3.6:\n%s" % msg,
-                  DeprecationWarning, stacklevel=3)
-
-# Raise DeprecationWarning, noting that the deprecated thing will go
 # away in ZODB 3.7.  Point to the caller of our caller (i.e., at the
 # code using the deprecated thing).
 def deprecated37(msg):

Modified: ZODB/branches/blob-merge-branch/src/ZopeUndo/Prefix.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZopeUndo/Prefix.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZopeUndo/Prefix.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -39,6 +39,10 @@
 
     def __cmp__(self, o):
         other_path = o.split('/')
+        if other_path and ' ' in other_path[-1]:
+            # don't include logged username in comparison
+            pos = other_path[-1].rfind(' ')
+            other_path[-1] = other_path[-1][:pos]
         return cmp(other_path[:self.length], self.path)
 
     def __repr__(self):

Modified: ZODB/branches/blob-merge-branch/src/ZopeUndo/tests/testPrefix.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/ZopeUndo/tests/testPrefix.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/ZopeUndo/tests/testPrefix.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -28,5 +28,19 @@
         for equal in ("", "/", "/def", "/a/b", "/a/b/c", "/a/b/c/d"):
             self.assertEqual(p2, equal)
 
+    def test_username_info(self):
+        # Zope Collector 1810; user paths have username appended
+        p1 = Prefix('/a/b')
+        for equal in ('/a/b spam', '/a/b/c spam', '/a/b/c/b spam'):
+            self.assertEqual(p1, equal)
+        for notEqual in (" spam", "/a/c spam", "/a/bbb spam", "/// spam"):
+            self.assertNotEqual(p1, notEqual)
+
+        p2 = Prefix("")
+        for equal in (" eggs", "/ eggs", "/def eggs", "/a/b eggs", 
+                      "/a/b/c eggs", "/a/b/c/d eggs"):
+            self.assertEqual(p2, equal)
+
+
 def test_suite():
     return unittest.makeSuite(PrefixTest)

Modified: ZODB/branches/blob-merge-branch/src/persistent/README.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/README.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/README.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -2,18 +2,18 @@
 Persistence support
 ===================
 
-(This document is under construction. More basic documentation will
- eventually appear here.)
+(This document is under construction. More basic documentation will eventually
+appear here.)
 
 
-Overriding __getattr__, __getattribute__, __setattr__, and __delattr__
------------------------------------------------------------------------  
+Overriding `__getattr__`, `__getattribute__`, `__setattr__`, and `__delattr__`
+------------------------------------------------------------------------------
 
 Subclasses can override the attribute-management methods.  For the
-__getattr__ method, the behavior is like that for regular Python
+`__getattr__` method, the behavior is like that for regular Python
 classes and for earlier versions of ZODB 3.
 
-For __getattribute__, __setattr__, and __delattr__, it is necessary to
-call certain methods defined by persistent.Persistent.  Detailed
+For `__getattribute__`, __setattr__`, and `__delattr__`, it is necessary
+to call certain methods defined by `persistent.Persistent`.  Detailed
 examples and documentation is provided in the test module,
-persistent.tests.test_overriding_attrs.
+`persistent.tests.test_overriding_attrs`.

Modified: ZODB/branches/blob-merge-branch/src/persistent/TimeStamp.c
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/TimeStamp.c	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/TimeStamp.c	2006-02-17 22:01:38 UTC (rev 41650)
@@ -218,7 +218,7 @@
 static PyObject *
 TimeStamp_raw(TimeStamp *self)
 {
-    return PyString_FromStringAndSize(self->data, 8);
+    return PyString_FromStringAndSize((const char*)self->data, 8);
 }
 
 static PyObject *
@@ -261,7 +261,7 @@
 	    new[i] = 0;
 	else {
 	    new[i]++;
-	    return TimeStamp_FromString(new);
+	    return TimeStamp_FromString((const char*)new);
 	}
     }
 

Modified: ZODB/branches/blob-merge-branch/src/persistent/dict.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/dict.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/dict.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -39,6 +39,7 @@
     __super_clear = IterableUserDict.clear
     __super_update = IterableUserDict.update
     __super_setdefault = IterableUserDict.setdefault
+    __super_pop = IterableUserDict.pop
     __super_popitem = IterableUserDict.popitem
 
     __super_p_init = persistent.Persistent.__init__
@@ -72,6 +73,10 @@
             self._p_changed = True
         return self.__super_setdefault(key, failobj)
 
+    def pop(self, key, *args):
+        self._p_changed = True
+        return self.__super_pop(key, *args)
+
     def popitem(self):
         self._p_changed = True
         return self.__super_popitem()

Modified: ZODB/branches/blob-merge-branch/src/persistent/interfaces.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/interfaces.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/interfaces.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -167,7 +167,9 @@
 
         It is up to the data manager to assign this.
         The special value None is reserved to indicate that an object
-        id has not been assigned.  Non-None object ids must be strings.
+        id has not been assigned.  Non-None object ids must be non-empty
+        strings.  The 8-byte string '\0'*8 (8 NUL bytes) is reserved to
+        identify the database root object.
         """)
 
     _p_changed = Attribute(

Modified: ZODB/branches/blob-merge-branch/src/persistent/mapping.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/mapping.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/mapping.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -41,6 +41,8 @@
     __super_clear = UserDict.clear
     __super_update = UserDict.update
     __super_setdefault = UserDict.setdefault
+    __super_pop = UserDict.pop
+    __super_popitem = UserDict.popitem
 
     def __delitem__(self, key):
         self.__super_delitem(key)
@@ -66,15 +68,14 @@
             self._p_changed = 1
         return self.__super_setdefault(key, failobj)
 
-    try:
-        __super_popitem = UserDict.popitem
-    except AttributeError:
-        pass
-    else:
-        def popitem(self):
-            self._p_changed = 1
-            return self.__super_popitem()
+    def pop(self, key, *args):
+        self._p_changed = 1
+        return self.__super_pop(key, *args)
 
+    def popitem(self):
+        self._p_changed = 1
+        return self.__super_popitem()
+
     # __iter__ was added in ZODB 3.4.2, but should have been added long
     # before.  We could inherit from Python's IterableUserDict instead
     # (which just adds __iter__ to Python's UserDict), but that class isn't

Modified: ZODB/branches/blob-merge-branch/src/persistent/tests/persistent.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/tests/persistent.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/tests/persistent.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,452 +1,458 @@
-Tests for persistent.Persistent
-===============================
+Tests for `persistent.Persistent`
+=================================
 
 This document is an extended doc test that covers the basics of the
-Persistent base class.  The test expects a class named 'P' to be
-provided in its globals.  The P class implements the Persistent
+Persistent base class.  The test expects a class named `P` to be
+provided in its globals.  The `P` class implements the `Persistent`
 interface.
 
 Test framework
 --------------
 
-The class P needs to behave like ExampleP.  (Note that the code below
+The class `P` needs to behave like `ExampleP`.  (Note that the code below
 is *not* part of the tests.)
 
-class ExampleP(Persistent):
-    def __init__(self):
-        self.x = 0
-    def inc(self):
-        self.x += 1
+::
 
+  class ExampleP(Persistent):
+      def __init__(self):
+          self.x = 0
+      def inc(self):
+          self.x += 1
+
 The tests use stub data managers.  A data manager is responsible for
 loading and storing the state of a persistent object.  It's stored in
-the _p_jar attribute of a persistent object.
+the ``_p_jar`` attribute of a persistent object.
 
->>> class DM:
-...     def __init__(self):
-...         self.called = 0
-...     def register(self, ob):
-...         self.called += 1
-...     def setstate(self, ob):
-...         ob.__setstate__({'x': 42})
+  >>> class DM:
+  ...     def __init__(self):
+  ...         self.called = 0
+  ...     def register(self, ob):
+  ...         self.called += 1
+  ...     def setstate(self, ob):
+  ...         ob.__setstate__({'x': 42})
 
->>> class BrokenDM(DM):
-...     def register(self,ob):
-...         self.called += 1
-...         raise NotImplementedError
-...     def setstate(self,ob):
-...         raise NotImplementedError
+  >>> class BrokenDM(DM):
+  ...     def register(self,ob):
+  ...         self.called += 1
+  ...         raise NotImplementedError
+  ...     def setstate(self,ob):
+  ...         raise NotImplementedError
 
->>> from persistent import Persistent
+  >>> from persistent import Persistent
 
+
 Test Persistent without Data Manager
 ------------------------------------
 
 First do some simple tests of a Persistent instance that does not have
-a data manager (_p_jar).
+a data manager (``_p_jar``).
 
->>> p = P()
->>> p.x
-0
->>> p._p_changed
-False
->>> p._p_state
-0
->>> p._p_jar
->>> p._p_oid
+  >>> p = P()
+  >>> p.x
+  0
+  >>> p._p_changed
+  False
+  >>> p._p_state
+  0
+  >>> p._p_jar
+  >>> p._p_oid
 
-Verify that modifications have no effect on _p_state of _p_changed.
+Verify that modifications have no effect on ``_p_state`` of ``_p_changed``.
 
->>> p.inc()
->>> p.inc()
->>> p.x
-2
->>> p._p_changed
-False
->>> p._p_state
-0
+  >>> p.inc()
+  >>> p.inc()
+  >>> p.x
+  2
+  >>> p._p_changed
+  False
+  >>> p._p_state
+  0
 
 Try all sorts of different ways to change the object's state.
 
->>> p._p_deactivate()
->>> p._p_state
-0
->>> p._p_changed = True
->>> p._p_state
-0
->>> del p._p_changed
->>> p._p_changed
-False
->>> p._p_state
-0
->>> p.x
-2
+  >>> p._p_deactivate()
+  >>> p._p_state
+  0
+  >>> p._p_changed = True
+  >>> p._p_state
+  0
+  >>> del p._p_changed
+  >>> p._p_changed
+  False
+  >>> p._p_state
+  0
+  >>> p.x
+  2
 
+
 Test Persistent with Data Manager
 ---------------------------------
 
-Next try some tests of an object with a data manager.  The DM class is
+Next try some tests of an object with a data manager.  The `DM` class is
 a simple testing stub.
 
->>> p = P()
->>> dm = DM()
->>> p._p_oid = "00000012"
->>> p._p_jar = dm
->>> p._p_changed
-0
->>> dm.called
-0
+  >>> p = P()
+  >>> dm = DM()
+  >>> p._p_oid = "00000012"
+  >>> p._p_jar = dm
+  >>> p._p_changed
+  0
+  >>> dm.called
+  0
 
-Modifying the object marks it as changed and registers it with the
-data manager.  Subsequent modifications don't have additional
-side-effects.
+Modifying the object marks it as changed and registers it with the data
+manager.  Subsequent modifications don't have additional side-effects.
 
->>> p.inc()
->>> p._p_changed
-1
->>> dm.called
-1
->>> p.inc()
->>> p._p_changed
-1
->>> dm.called
-1
+  >>> p.inc()
+  >>> p._p_changed
+  1
+  >>> dm.called
+  1
+  >>> p.inc()
+  >>> p._p_changed
+  1
+  >>> dm.called
+  1
 
 It's not possible to deactivate a modified object.
 
->>> p._p_deactivate()
->>> p._p_changed
-1
+  >>> p._p_deactivate()
+  >>> p._p_changed
+  1
 
-It is possible to invalidate it.  That's the key difference
-between deactivation and invalidation.
+It is possible to invalidate it.  That's the key difference between
+deactivation and invalidation.
 
->>> p._p_invalidate()
->>> p._p_state
--1
+  >>> p._p_invalidate()
+  >>> p._p_state
+  -1
 
-Now that the object is a ghost, any attempt to modify it will
-require that it be unghosted first.  The test data manager
-has the odd property that it sets the object's 'x' attribute
-to 42 when it is unghosted.
+Now that the object is a ghost, any attempt to modify it will require that it
+be unghosted first.  The test data manager has the odd property that it sets
+the object's ``x`` attribute to ``42`` when it is unghosted.
 
->>> p.inc()
->>> p.x
-43
->>> dm.called
-2
+  >>> p.inc()
+  >>> p.x
+  43
+  >>> dm.called
+  2
 
-You can manually reset the changed field to False, although
-it's not clear why you would want to do that.  The object
-changes to the UPTODATE state but retains its modifications.
+You can manually reset the changed field to ``False``, although it's not clear
+why you would want to do that.  The object changes to the ``UPTODATE`` state
+but retains its modifications.
 
->>> p._p_changed = False
->>> p._p_state
-0
->>> p._p_changed
-False
->>> p.x
-43
+  >>> p._p_changed = False
+  >>> p._p_state
+  0
+  >>> p._p_changed
+  False
+  >>> p.x
+  43
 
->>> p.inc()
->>> p._p_changed
-True
->>> dm.called
-3
+  >>> p.inc()
+  >>> p._p_changed
+  True
+  >>> dm.called
+  3
 
-__getstate__() and __setstate__()
----------------------------------
+``__getstate__()`` and ``__setstate__()``
+-----------------------------------------
 
-The next several tests cover the __getstate__() and __setstate__()
+The next several tests cover the ``__getstate__()`` and ``__setstate__()``
 implementations.
 
->>> p = P()
->>> state = p.__getstate__()
->>> isinstance(state, dict)
-True
->>> state['x']
-0
->>> p._p_state
-0
+  >>> p = P()
+  >>> state = p.__getstate__()
+  >>> isinstance(state, dict)
+  True
+  >>> state['x']
+  0
+  >>> p._p_state
+  0
 
 Calling setstate always leaves the object in the uptodate state?
 (I'm not entirely clear on this one.)
 
->>> p.__setstate__({'x': 5})
->>> p._p_state
-0
+  >>> p.__setstate__({'x': 5})
+  >>> p._p_state
+  0
 
 Assigning to a volatile attribute has no effect on the object state.
 
->>> p._v_foo = 2
->>> p.__getstate__()
-{'x': 5}
->>> p._p_state
-0
+  >>> p._v_foo = 2
+  >>> p.__getstate__()
+  {'x': 5}
+  >>> p._p_state
+  0
 
-The _p_serial attribute is not affected by calling setstate.
+The ``_p_serial`` attribute is not affected by calling setstate.
 
->>> p._p_serial = "00000012"
->>> p.__setstate__(p.__getstate__())
->>> p._p_serial
-'00000012'
+  >>> p._p_serial = "00000012"
+  >>> p.__setstate__(p.__getstate__())
+  >>> p._p_serial
+  '00000012'
 
+
 Change Ghost test
 -----------------
 
-If an object is a ghost and its _p_changed is set to True (any true value),
-it should activate (unghostify) the object.  This behavior is new in ZODB
-3.6; before then, an attempt to do "ghost._p_changed = True" was ignored.
+If an object is a ghost and its ``_p_changed`` is set to ``True`` (any true
+value), it should activate (unghostify) the object.  This behavior is new in
+ZODB 3.6; before then, an attempt to do ``ghost._p_changed = True`` was
+ignored.
 
->>> p = P()
->>> p._p_jar = DM()
->>> p._p_oid = 1
->>> p._p_deactivate()
->>> p._p_changed # None
->>> p._p_state # ghost state
--1
->>> p._p_changed = True
->>> p._p_changed
-1
->>> p._p_state # changed state
-1
->>> p.x
-42
+  >>> p = P()
+  >>> p._p_jar = DM()
+  >>> p._p_oid = 1
+  >>> p._p_deactivate()
+  >>> p._p_changed # None
+  >>> p._p_state # ghost state
+  -1
+  >>> p._p_changed = True
+  >>> p._p_changed
+  1
+  >>> p._p_state # changed state
+  1
+  >>> p.x
+  42
 
+
 Activate, deactivate, and invalidate
 ------------------------------------
 
 Some of these tests are redundant, but are included to make sure there
-are explicit and simple tests of _p_activate(), _p_deactivate(), and
-_p_invalidate().
+are explicit and simple tests of ``_p_activate()``, ``_p_deactivate()``, and
+``_p_invalidate()``.
 
->>> p = P()
->>> p._p_oid = 1
->>> p._p_jar = DM()
->>> p._p_deactivate()
->>> p._p_state
--1
->>> p._p_activate()
->>> p._p_state
-0
->>> p.x
-42
->>> p.inc()
->>> p.x
-43
->>> p._p_state
-1
->>> p._p_invalidate()
->>> p._p_state
--1
->>> p.x
-42
+  >>> p = P()
+  >>> p._p_oid = 1
+  >>> p._p_jar = DM()
+  >>> p._p_deactivate()
+  >>> p._p_state
+  -1
+  >>> p._p_activate()
+  >>> p._p_state
+  0
+  >>> p.x
+  42
+  >>> p.inc()
+  >>> p.x
+  43
+  >>> p._p_state
+  1
+  >>> p._p_invalidate()
+  >>> p._p_state
+  -1
+  >>> p.x
+  42
 
+
 Test failures
 -------------
 
 The following tests cover various errors cases.
 
-When an object is modified, it registers with its data manager.  If
-that registration fails, the exception is propagated and the object
-stays in the up-to-date state.  It shouldn't change to the modified
-state, because it won't be saved when the transaction commits.
+When an object is modified, it registers with its data manager.  If that
+registration fails, the exception is propagated and the object stays in the
+up-to-date state.  It shouldn't change to the modified state, because it won't
+be saved when the transaction commits.
 
->>> p = P()
->>> p._p_oid = 1
->>> p._p_jar = BrokenDM()
->>> p._p_state
-0
->>> p._p_jar.called
-0
->>> p._p_changed = 1
-Traceback (most recent call last):
-  ...
-NotImplementedError
->>> p._p_jar.called
-1
->>> p._p_state
-0
+  >>> p = P()
+  >>> p._p_oid = 1
+  >>> p._p_jar = BrokenDM()
+  >>> p._p_state
+  0
+  >>> p._p_jar.called
+  0
+  >>> p._p_changed = 1
+  Traceback (most recent call last):
+    ...
+  NotImplementedError
+  >>> p._p_jar.called
+  1
+  >>> p._p_state
+  0
 
-Make sure that exceptions that occur inside the data manager's
-setstate() method propagate out to the caller.
+Make sure that exceptions that occur inside the data manager's ``setstate()``
+method propagate out to the caller.
 
->>> p = P()
->>> p._p_oid = 1
->>> p._p_jar = BrokenDM()
->>> p._p_deactivate()
->>> p._p_state
--1
->>> p._p_activate()
-Traceback (most recent call last):
-  ...
-NotImplementedError
->>> p._p_state
--1
+  >>> p = P()
+  >>> p._p_oid = 1
+  >>> p._p_jar = BrokenDM()
+  >>> p._p_deactivate()
+  >>> p._p_state
+  -1
+  >>> p._p_activate()
+  Traceback (most recent call last):
+    ...
+  NotImplementedError
+  >>> p._p_state
+  -1
 
 
-Special test to cover layout of __dict__
-----------------------------------------
+Special test to cover layout of ``__dict__``
+--------------------------------------------
 
-We once had a bug in the Persistent class that calculated an incorrect
-offset for the __dict__ attribute.  It assigned __dict__ and _p_jar to
-the same location in memory.  This is a simple test to make sure they
-have different locations.
+We once had a bug in the `Persistent` class that calculated an incorrect
+offset for the ``__dict__`` attribute.  It assigned ``__dict__`` and
+``_p_jar`` to the same location in memory.  This is a simple test to make sure
+they have different locations.
 
->>> p = P()
->>> p.inc()
->>> p.inc()
->>> 'x' in p.__dict__
-True
->>> p._p_jar
+  >>> p = P()
+  >>> p.inc()
+  >>> p.inc()
+  >>> 'x' in p.__dict__
+  True
+  >>> p._p_jar
 
 
 Inheritance and metaclasses
 ---------------------------
 
-Simple tests to make sure it's possible to inherit from the Persistent
-base class multiple times.  There used to be metaclasses involved in
-Persistent that probably made this a more interesting test.
+Simple tests to make sure it's possible to inherit from the `Persistent` base
+class multiple times.  There used to be metaclasses involved in `Persistent`
+that probably made this a more interesting test.
 
->>> class A(Persistent):
-...     pass
->>> class B(Persistent):
-...     pass
->>> class C(A, B):
-...     pass
->>> class D(object):
-...     pass
->>> class E(D, B):
-...     pass
->>> a = A()
->>> b = B()
->>> c = C()
->>> d = D()
->>> e = E()
+  >>> class A(Persistent):
+  ...     pass
+  >>> class B(Persistent):
+  ...     pass
+  >>> class C(A, B):
+  ...     pass
+  >>> class D(object):
+  ...     pass
+  >>> class E(D, B):
+  ...     pass
+  >>> a = A()
+  >>> b = B()
+  >>> c = C()
+  >>> d = D()
+  >>> e = E()
 
-Also make sure that it's possible to define Persistent classes that
-have a custom metaclass.
+Also make sure that it's possible to define `Persistent` classes that have a
+custom metaclass.
 
->>> class alternateMeta(type):
-...     type
->>> class alternate(object):
-...     __metaclass__ = alternateMeta
->>> class mixedMeta(alternateMeta, type):
-...     pass
->>> class mixed(alternate, Persistent):
-...     pass
->>> class mixed(Persistent, alternate):
-...     pass
+  >>> class alternateMeta(type):
+  ...     type
+  >>> class alternate(object):
+  ...     __metaclass__ = alternateMeta
+  >>> class mixedMeta(alternateMeta, type):
+  ...     pass
+  >>> class mixed(alternate, Persistent):
+  ...     pass
+  >>> class mixed(Persistent, alternate):
+  ...     pass
 
 
 Basic type structure
 --------------------
 
->>> Persistent.__dictoffset__
-0
->>> Persistent.__weakrefoffset__
-0
->>> Persistent.__basicsize__ > object.__basicsize__
-True
->>> P.__dictoffset__ > 0
-True
->>> P.__weakrefoffset__ > 0
-True
->>> P.__dictoffset__ < P.__weakrefoffset__
-True
->>> P.__basicsize__ > Persistent.__basicsize__
-True
+  >>> Persistent.__dictoffset__
+  0
+  >>> Persistent.__weakrefoffset__
+  0
+  >>> Persistent.__basicsize__ > object.__basicsize__
+  True
+  >>> P.__dictoffset__ > 0
+  True
+  >>> P.__weakrefoffset__ > 0
+  True
+  >>> P.__dictoffset__ < P.__weakrefoffset__
+  True
+  >>> P.__basicsize__ > Persistent.__basicsize__
+  True
 
 
 Slots
 -----
 
-These are some simple tests of classes that have an __slots__
+These are some simple tests of classes that have an ``__slots__``
 attribute.  Some of the classes should have slots, others shouldn't.
 
->>> class noDict(object):
-...     __slots__ = ['foo']
->>> class p_noDict(Persistent):
-...     __slots__ = ['foo']
->>> class p_shouldHaveDict(p_noDict):
-...     pass
+  >>> class noDict(object):
+  ...     __slots__ = ['foo']
+  >>> class p_noDict(Persistent):
+  ...     __slots__ = ['foo']
+  >>> class p_shouldHaveDict(p_noDict):
+  ...     pass
 
->>> p_noDict.__dictoffset__
-0
->>> x = p_noDict()
->>> x.foo = 1
->>> x.foo
-1
->>> x.bar = 1
-Traceback (most recent call last):
-  ...
-AttributeError: 'p_noDict' object has no attribute 'bar'
->>> x._v_bar = 1
-Traceback (most recent call last):
-  ...
-AttributeError: 'p_noDict' object has no attribute '_v_bar'
->>> x.__dict__
-Traceback (most recent call last):
-  ...
-AttributeError: 'p_noDict' object has no attribute '__dict__'
+  >>> p_noDict.__dictoffset__
+  0
+  >>> x = p_noDict()
+  >>> x.foo = 1
+  >>> x.foo
+  1
+  >>> x.bar = 1
+  Traceback (most recent call last):
+    ...
+  AttributeError: 'p_noDict' object has no attribute 'bar'
+  >>> x._v_bar = 1
+  Traceback (most recent call last):
+    ...
+  AttributeError: 'p_noDict' object has no attribute '_v_bar'
+  >>> x.__dict__
+  Traceback (most recent call last):
+    ...
+  AttributeError: 'p_noDict' object has no attribute '__dict__'
 
-The various _p_ attributes are unaffected by slots.
->>> p._p_oid
->>> p._p_jar
->>> p._p_state
-0
+  The various _p_ attributes are unaffected by slots.
+  >>> p._p_oid
+  >>> p._p_jar
+  >>> p._p_state
+  0
 
 If the most-derived class does not specify
 
->>> p_shouldHaveDict.__dictoffset__ > 0
-True
->>> x = p_shouldHaveDict()
->>> isinstance(x.__dict__, dict)
-True
+  >>> p_shouldHaveDict.__dictoffset__ > 0
+  True
+  >>> x = p_shouldHaveDict()
+  >>> isinstance(x.__dict__, dict)
+  True
 
 
 Pickling
 --------
 
 There's actually a substantial effort involved in making subclasses of
-Persistent work with plain-old pickle.  The ZODB serialization layer
-never calls pickle on an object; it pickles the object's class
-description and its state as two separate pickles.
+`Persistent` work with plain-old pickle.  The ZODB serialization layer never
+calls pickle on an object; it pickles the object's class description and its
+state as two separate pickles.
 
->>> import pickle
->>> p = P()
->>> p.inc()
->>> p2 = pickle.loads(pickle.dumps(p))
->>> p2.__class__ is P
-True
->>> p2.x == p.x
-True
+  >>> import pickle
+  >>> p = P()
+  >>> p.inc()
+  >>> p2 = pickle.loads(pickle.dumps(p))
+  >>> p2.__class__ is P
+  True
+  >>> p2.x == p.x
+  True
 
-We should also test that pickle works with custom getstate and
-setstate.  Perhaps even reduce.  The problem is that pickling depends
-on finding the class in a particular module, and classes defined here
-won't appear in any module.  We could require each user of the tests
-to define a base class, but that might be tedious.
+We should also test that pickle works with custom getstate and setstate.
+Perhaps even reduce.  The problem is that pickling depends on finding the
+class in a particular module, and classes defined here won't appear in any
+module.  We could require each user of the tests to define a base class, but
+that might be tedious.
 
+
 Interfaces
 ----------
 
-Some versions of Zope and ZODB have the zope.interfaces package
-available.  If it is available, then persistent will be associated
-with several interfaces.  It's hard to write a doctest test that runs
-the tests only if zope.interface is available, so this test looks a
-little unusual.  One problem is that the assert statements won't do
-anything if you run with -O.
+Some versions of Zope and ZODB have the `zope.interfaces` package available.
+If it is available, then persistent will be associated with several
+interfaces.  It's hard to write a doctest test that runs the tests only if
+`zope.interface` is available, so this test looks a little unusual.  One
+problem is that the assert statements won't do anything if you run with `-O`.
 
->>> try:
-...     import zope.interface
-... except ImportError:
-...     pass
-... else:
-...     from persistent.interfaces import IPersistent
-...     assert IPersistent.implementedBy(Persistent)
-...     p = Persistent()
-...     assert IPersistent.providedBy(p)
-...     assert IPersistent.implementedBy(P)
-...     p = P()
-...     assert IPersistent.providedBy(p)
+  >>> try:
+  ...     import zope.interface
+  ... except ImportError:
+  ...     pass
+  ... else:
+  ...     from persistent.interfaces import IPersistent
+  ...     assert IPersistent.implementedBy(Persistent)
+  ...     p = Persistent()
+  ...     assert IPersistent.providedBy(p)
+  ...     assert IPersistent.implementedBy(P)
+  ...     p = P()
+  ...     assert IPersistent.providedBy(p)

Modified: ZODB/branches/blob-merge-branch/src/persistent/tests/persistenttestbase.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/persistent/tests/persistenttestbase.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/persistent/tests/persistenttestbase.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -16,6 +16,9 @@
 from persistent import Persistent
 from persistent.interfaces import IPersistent
 
+# Confusing:  ZODB doesn't use this file.  It appears to be used only
+# by Zope3, where it's imported by zope/app/schema/tests/test_wrapper.py.
+
 try:
     import zope.interface
 except ImportError:
@@ -115,8 +118,10 @@
         self.assertEqual(dm.called, 1)
 
     def testGhostChanged(self):
-        # An object is a ghost, and it's _p_changed it set to True.
-        # This assignment should have no effect.
+        # If an object is a ghost and its _p_changed is set to True (any
+        # true value), it should activate (unghostify) the object.  This
+        # behavior is new in ZODB 3.6; before then, an attempt to do
+        # "ghost._p_changed = True" was ignored.
         p = self.klass()
         p._p_oid = 1
         dm = DM()
@@ -124,7 +129,7 @@
         p._p_deactivate()
         self.assertEqual(p._p_changed, None)
         p._p_changed = True
-        self.assertEqual(p._p_changed, None)
+        self.assertEqual(p._p_changed, 1)
 
     def testRegistrationFailure(self):
         p = self.klass()

Copied: ZODB/branches/blob-merge-branch/src/persistent/tests/test_mapping.py (from rev 41649, ZODB/trunk/src/persistent/tests/test_mapping.py)

Modified: ZODB/branches/blob-merge-branch/src/scripts/zeoup.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/scripts/zeoup.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/scripts/zeoup.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -27,6 +27,7 @@
 """
 
 import getopt
+import logging
 import socket
 import sys
 import time
@@ -41,6 +42,18 @@
 
 ZEO_VERSION = 2
 
+def setup_logging():
+    # Set up logging to stderr which will show messages originating
+    # at severity ERROR or higher.
+    root = logging.getLogger()
+    root.setLevel(logging.ERROR)
+    fmt = logging.Formatter(
+        "------\n%(asctime)s %(levelname)s %(name)s %(message)s",
+        "%Y-%m-%dT%H:%M:%S")
+    handler = logging.StreamHandler()
+    handler.setFormatter(fmt)
+    root.addHandler(handler)
+
 def check_server(addr, storage, write):
     t0 = time.time()
     if ZEO_VERSION == 2:
@@ -122,6 +135,7 @@
             usage()
         addr = host, port
 
+    setup_logging()
     check_server(addr, storage, write)
 
 if __name__ == "__main__":

Modified: ZODB/branches/blob-merge-branch/src/transaction/README.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/README.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/README.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,14 +1,13 @@
-This package is currently a facade of the ZODB.Transaction module.
+============
+Transactions
+============
 
-It exists to support:
+This package contains a generic transaction implementation for Python. It is
+mainly used by the ZODB, though.
 
-- Application code that uses the ZODB 4 transaction API
-
-- ZODB4-style data managers (transaction.interfaces.IDataManager)
-
-Note that the data manager API, transaction.interfaces.IDataManager,
+Note that the data manager API, ``transaction.interfaces.IDataManager``,
 is syntactically simple, but semantically complex.  The semantics
 were not easy to express in the interface. This could probably use
 more work.  The semantics are presented in detail through examples of
-a sample data manager in transaction.tests.test_SampleDataManager.
+a sample data manager in ``transaction.tests.test_SampleDataManager``.
 

Modified: ZODB/branches/blob-merge-branch/src/transaction/__init__.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/__init__.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/__init__.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -25,10 +25,3 @@
 commit = manager.commit
 abort = manager.abort
 savepoint = manager.savepoint
-
-def get_transaction():
-    from ZODB.utils import deprecated36
-    deprecated36("""   use transaction.get() instead of get_transaction().
-   transaction.commit() is a shortcut spelling of transaction.get().commit(),
-   and transaction.abort() of transaction.get().abort().""")
-    return get()

Modified: ZODB/branches/blob-merge-branch/src/transaction/_transaction.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/_transaction.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/_transaction.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -30,7 +30,7 @@
 Subtransactions
 ---------------
 
-Note: Suntransactions are deprecated!  Use savepoint/rollback instead.
+Note: Subtransactions are deprecated!  Use savepoint/rollback instead.
 
 A subtransaction applies the transaction notion recursively.  It
 allows a set of modifications within a transaction to be committed or
@@ -115,6 +115,20 @@
 passing it a callable and arguments.  The callable will be called with its
 arguments at the start of the commit (but not for substransaction commits).
 
+After-commit hook
+------------------
+
+Sometimes, applications want to execute code after a transaction is
+committed or aborted. For example, one might want to launch non
+transactional code after a successful commit. Or still someone might
+want to launch asynchronous code after.  A post-commit hook is
+available for such use cases: use addAfterCommitHook(), passing it a
+callable and arguments.  The callable will be called with a Boolean
+value representing the status of the commit operation as first
+argument (true if successfull or false iff aborted) preceding its
+arguments at the start of the commit (but not for substransaction
+commits).
+
 Error handling
 --------------
 
@@ -241,6 +255,9 @@
         # List of (hook, args, kws) tuples added by addBeforeCommitHook().
         self._before_commit = []
 
+        # List of (hook, args, kws) tuples added by addAfterCommitHook().
+        self._after_commit = []
+
     # Raise TransactionFailedError, due to commit()/join()/register()
     # getting called when the current transaction has already suffered
     # a commit/savepoint failure.
@@ -292,7 +309,7 @@
             savepoint = Savepoint(self, optimistic, *self._resources)
         except:
             self._cleanup(self._resources)
-            self._saveCommitishError() # reraises!
+            self._saveAndRaiseCommitishError() # reraises!
 
         if self._savepoint2index is None:
             self._savepoint2index = weakref.WeakKeyDictionary()
@@ -345,32 +362,25 @@
             assert id(obj) not in map(id, adapter.objects)
             adapter.objects.append(obj)
 
-    def begin(self):
-        from ZODB.utils import deprecated36
-
-        deprecated36("Transaction.begin() should no longer be used; use "
-                      "the begin() method of a transaction manager.")
-        if (self._resources or self._synchronizers):
-            self.abort()
-        # Else aborting wouldn't do anything, except if _manager is non-None,
-        # in which case it would do nothing besides uselessly free() this
-        # transaction.
-
     def commit(self, subtransaction=_marker, deprecation_wng=True):
         if subtransaction is _marker:
             subtransaction = 0
         elif deprecation_wng:
             from ZODB.utils import deprecated37
-            deprecated37("subtransactions are deprecated; use "
-                         "transaction.savepoint() instead of "
-                         "transaction.commit(1)")
+            deprecated37("subtransactions are deprecated; instead of "
+                         "transaction.commit(1), use "
+                         "transaction.savepoint(optimistic=True) in "
+                         "contexts where a subtransaction abort will never "
+                         "occur, or sp=transaction.savepoint() if later "
+                         "rollback is possible and then sp.rollback() "
+                         "instead of transaction.abort(1)")
 
         if self._savepoint2index:
             self._invalidate_all_savepoints()
 
         if subtransaction:
             # TODO deprecate subtransactions
-            self._subtransaction_savepoint = self.savepoint(1)
+            self._subtransaction_savepoint = self.savepoint(optimistic=True)
             return
 
         if self.status is Status.COMMITFAILED:
@@ -383,16 +393,19 @@
 
         try:
             self._commitResources()
+            self.status = Status.COMMITTED
         except:
-            self._saveCommitishError() # This raises!
-
-        self.status = Status.COMMITTED
-        if self._manager:
-            self._manager.free(self)
-        self._synchronizers.map(lambda s: s.afterCompletion(self))
+            t, v, tb = self._saveAndGetCommitishError()
+            self._callAfterCommitHooks(status=False)
+            raise t, v, tb
+        else:
+            if self._manager:
+                self._manager.free(self)
+            self._synchronizers.map(lambda s: s.afterCompletion(self))
+            self._callAfterCommitHooks(status=True)
         self.log.debug("commit")
 
-    def _saveCommitishError(self):
+    def _saveAndGetCommitishError(self):
         self.status = Status.COMMITFAILED
         # Save the traceback for TransactionFailedError.
         ft = self._failure_traceback = StringIO()
@@ -403,6 +416,10 @@
         traceback.print_tb(tb, None, ft)
         # Append the exception type and value.
         ft.writelines(traceback.format_exception_only(t, v))
+        return t, v, tb
+
+    def _saveAndRaiseCommitishError(self):
+        t, v, tb = self._saveAndGetCommitishError()
         raise t, v, tb
 
     def getBeforeCommitHooks(self):
@@ -428,6 +445,44 @@
             hook(*args, **kws)
         self._before_commit = []
 
+    def getAfterCommitHooks(self):
+        return iter(self._after_commit)
+
+    def addAfterCommitHook(self, hook, args=(), kws=None):
+        if kws is None:
+            kws = {}
+        self._after_commit.append((hook, tuple(args), kws))
+
+    def _callAfterCommitHooks(self, status=True):
+        # Avoid to abort anything at the end if no hooks are registred.
+        if not self._after_commit:
+            return
+        # Call all hooks registered, allowing further registrations
+        # during processing.  Note that calls to addAterCommitHook() may
+        # add additional hooks while hooks are running, and iterating over a
+        # growing list is well-defined in Python.
+        for hook, args, kws in self._after_commit:
+            # The first argument passed to the hook is a Boolean value,
+            # true if the commit succeeded, or false if the commit aborted.
+            try:
+                hook(status, *args, **kws)
+            except:
+                # We need to catch the exceptions if we want all hooks
+                # to be called
+                self.log.error("Error in after commit hook exec in %s ",
+                               hook, exc_info=sys.exc_info())
+        # The transaction is already committed. It must not have
+        # further effects after the commit.
+        for rm in self._resources:
+            try:
+                rm.abort(self)
+            except:
+                # XXX should we take further actions here ?
+                self.log.error("Error in abort() on manager %s",
+                               rm, exc_info=sys.exc_info())
+        self._after_commit = []
+        self._before_commit = []
+
     def _commitResources(self):
         # Execute the two-phase commit protocol.
 
@@ -450,7 +505,7 @@
                 # TODO: do we need to make this warning stronger?
                 # TODO: It would be nice if the system could be configured
                 # to stop committing transactions at this point.
-                self.log.critical("A storage error occured during the second "
+                self.log.critical("A storage error occurred during the second "
                                   "phase of the two-phase commit.  Resources "
                                   "may be in an inconsistent state.")
                 raise
@@ -694,7 +749,7 @@
                 savepoint.rollback()
         except:
             # Mark the transaction as failed.
-            transaction._saveCommitishError() # reraises!
+            transaction._saveAndRaiseCommitishError() # reraises!
 
 class AbortSavepoint:
 

Modified: ZODB/branches/blob-merge-branch/src/transaction/interfaces.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/interfaces.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/interfaces.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -156,7 +156,7 @@
         """Add extension data to the transaction.
 
         name is the name of the extension property to set, of Python type
-        str; value must be pickleable.  Multiple calls may be made to set
+        str; value must be picklable.  Multiple calls may be made to set
         multiple extension properties, provided the names are distinct.
 
         Storages record the extension data, as meta-data, when a transaction
@@ -232,6 +232,43 @@
         by a top-level transaction commit.
         """
 
+    def addAfterCommitHook(hook, args=(), kws=None):
+         """Register a hook to call after a transaction commit attempt.
+         
+         The specified hook function will be called after the transaction
+         commit succeeds or aborts.  The first argument passed to the hook
+         is a Boolean value, true if the commit succeeded, or false if the
+         commit aborted.  `args` specifies additional positional, and `kws`
+         keyword, arguments to pass to the hook.  `args` is a sequence of
+         positional arguments to be passed, defaulting to an empty tuple
+         (only the true/false success argument is passed).  `kws` is a
+         dictionary of keyword argument names and values to be passed, or
+         the default None (no keyword arguments are passed).
+         
+         Multiple hooks can be registered and will be called in the order they
+         were registered (first registered, first called).  This method can
+         also be called from a hook:  an executing hook can register more
+         hooks.  Applications should take care to avoid creating infinite loops
+         by recursively registering hooks.
+         
+         Hooks are called only for a top-level commit.  A subtransaction
+         commit or savepoint creation does not call any hooks.  Calling a
+         hook "consumes" its registration:  hook registrations do not
+         persist across transactions.  If it's desired to call the same
+         hook on every transaction commit, then addAfterCommitHook() must be
+         called with that hook during every transaction; in such a case
+         consider registering a synchronizer object via a TransactionManager's
+         registerSynch() method instead.
+         """
+
+    def getAfterCommitHooks():
+        """Return iterable producing the registered addAfterCommit hooks.
+
+        A triple (hook, args, kws) is produced for each registered hook.
+        The hooks are produced in the order in which they would be invoked
+        by a top-level transaction commit.
+        """
+
 class ITransactionDeprecated(zope.interface.Interface):
     """Deprecated parts of the transaction API."""
 

Modified: ZODB/branches/blob-merge-branch/src/transaction/savepoint.txt
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/savepoint.txt	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/savepoint.txt	2006-02-17 22:01:38 UTC (rev 41650)
@@ -12,17 +12,17 @@
 Savepoints make it possible to write atomic subroutines that don't
 make top-level transaction commitments.
 
+
 Applications
 ------------
 
-To demonstrate how savepoints work with transactions, we've provided a
-sample data manager implementation that provides savepoint support.
-The primary purpose of this data manager is to provide code that can
-be read to understand how savepoints work.  The secondary purpose is to
-provide support for demonstrating the correct operation of savepoint
-support within the transaction system.  This data manager is very
-simple.  It provides flat storage of named immutable values, like strings
-and numbers.
+To demonstrate how savepoints work with transactions, we've provided a sample
+data manager implementation that provides savepoint support.  The primary
+purpose of this data manager is to provide code that can be read to understand
+how savepoints work.  The secondary purpose is to provide support for
+demonstrating the correct operation of savepoint support within the
+transaction system.  This data manager is very simple.  It provides flat
+storage of named immutable values, like strings and numbers.
 
     >>> import transaction.tests.savepointsample
     >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
@@ -43,13 +43,13 @@
     >>> dm['name']
     'bob'
 
-Now, let's look at an application that manages funds for people.
-It allows deposits and debits to be entered for multiple people.
-It accepts a sequence of entries and generates a sequence of status
-messages.  For each entry, it applies the change and then validates
-the user's account.  If the user's account is invalid, we roll back
-the change for that entry.  The success or failure of an entry is
-indicated in the output status.  First we'll initialize some accounts:
+Now, let's look at an application that manages funds for people.  It allows
+deposits and debits to be entered for multiple people.  It accepts a sequence
+of entries and generates a sequence of status messages.  For each entry, it
+applies the change and then validates the user's account.  If the user's
+account is invalid, we roll back the change for that entry.  The success or
+failure of an entry is indicated in the output status.  First we'll initialize
+some accounts:
 
     >>> dm['bob-balance'] = 0.0
     >>> dm['bob-credit'] = 0.0
@@ -63,8 +63,8 @@
     ...     if dm[name+'-balance'] + dm[name+'-credit'] < 0:
     ...         raise ValueError('Overdrawn', name)
 
-And a function to apply entries.  If the function fails in some
-unexpected way, it rolls back all of its changes and prints the error:
+And a function to apply entries.  If the function fails in some unexpected
+way, it rolls back all of its changes and prints the error:
 
     >>> def apply_entries(entries):
     ...     savepoint = transaction.savepoint()
@@ -118,9 +118,9 @@
     Updated sally
     Unexpected exception unsupported operand type(s) for +=: 'float' and 'str'
 
-Because the apply_entries used a savepoint for the entire function,
-it was able to rollback the partial changes without rolling back
-changes made in the previous call to apply_entries:
+Because the apply_entries used a savepoint for the entire function, it was
+able to rollback the partial changes without rolling back changes made in the
+previous call to ``apply_entries``:
 
     >>> dm['bob-balance']
     30.0
@@ -195,11 +195,12 @@
 
     >>> transaction.abort()
 
+
 Databases without savepoint support
 -----------------------------------
 
-Normally it's an error to use savepoints with databases that don't
-support savepoints:
+Normally it's an error to use savepoints with databases that don't support
+savepoints:
 
     >>> dm_no_sp = transaction.tests.savepointsample.SampleDataManager()
     >>> dm_no_sp['name'] = 'bob'
@@ -212,10 +213,10 @@
 
     >>> transaction.abort()
 
-However, a flag can be passed to the transaction savepoint method to
-indicate that databases without savepoint support should be tolerated
-until a savepoint is rolled back.  This allows transactions to proceed
-if there are no reasons to roll back:
+However, a flag can be passed to the transaction savepoint method to indicate
+that databases without savepoint support should be tolerated until a savepoint
+is rolled back.  This allows transactions to proceed if there are no reasons
+to roll back:
 
     >>> dm_no_sp['name'] = 'sally'
     >>> savepoint = transaction.savepoint(1)
@@ -231,13 +232,14 @@
     ...
     TypeError: ('Savepoints unsupported', {'name': 'sam'})
 
+
 Failures
 --------
 
-If a failure occurs when creating or rolling back a savepoint, the
-transaction state will be uncertain and the transaction will become
-uncommitable.  From that point on, most transaction operations,
-including commit, will fail until the transaction is aborted.
+If a failure occurs when creating or rolling back a savepoint, the transaction
+state will be uncertain and the transaction will become uncommitable.  From
+that point on, most transaction operations, including commit, will fail until
+the transaction is aborted.
 
 In the previous example, we got an error when we tried to rollback the
 savepoint.  If we try to commit the transaction, the commit will fail:
@@ -254,8 +256,8 @@
 
     >>> transaction.abort()
 
-Similarly, in our earlier example, where we tried to take a savepoint
-with a data manager that didn't support savepoints:
+Similarly, in our earlier example, where we tried to take a savepoint with a
+data manager that didn't support savepoints:
 
     >>> dm_no_sp['name'] = 'sally'
     >>> dm['name'] = 'sally'

Modified: ZODB/branches/blob-merge-branch/src/transaction/tests/test_transaction.py
===================================================================
--- ZODB/branches/blob-merge-branch/src/transaction/tests/test_transaction.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/src/transaction/tests/test_transaction.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001, 2002, 2005 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-"""Test tranasction behavior for variety of cases.
+"""Test transaction behavior for variety of cases.
 
 I wrote these unittests to investigate some odd transaction
 behavior when doing unittests of integrating non sub transaction
@@ -241,7 +241,6 @@
 
         assert self.nosub1._p_jar.ctpc_abort == 1
 
-
     # last test, check the hosing mechanism
 
 ##    def testHoserStoppage(self):
@@ -728,8 +727,270 @@
                "arg '-' kw1 'no_kw1' kw2 'no_kw2'",
        'rec0']
       >>> reset_log()
+
+    When modifing persitent objects within before commit hooks
+    modifies the objects, of course :)
+    
+    Start a new transaction
+
+      >>> t = transaction.begin()
+
+    Create a DB instance and add a IOBTree within
+
+      >>> from ZODB.tests.util import DB
+      >>> from ZODB.tests.util import P
+      >>> db = DB()
+      >>> con = db.open()
+      >>> root = con.root()
+      >>> root['p'] = P('julien')
+      >>> p = root['p']
+
+      >>> p.name
+      'julien'
+      
+    This hook will get the object from the `DB` instance and change
+    the flag attribute.
+
+      >>> def hookmodify(status, arg=None, kw1='no_kw1', kw2='no_kw2'):
+      ...     p.name = 'jul'
+
+    Now register this hook and commit.
+
+      >>> t.addBeforeCommitHook(hookmodify, (p, 1))
+      >>> transaction.commit()
+
+    Nothing should have changed since it should have been aborted.
+
+      >>> p.name
+      'jul'
+
+      >>> db.close()
     """
 
+def test_addAfterCommitHook():
+    """Test addAfterCommitHook.
+
+    Let's define a hook to call, and a way to see that it was called.
+
+      >>> log = []
+      >>> def reset_log():
+      ...     del log[:]
+
+      >>> def hook(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'):
+      ...     log.append("%r arg %r kw1 %r kw2 %r" % (status, arg, kw1, kw2))
+
+    Now register the hook with a transaction.
+
+      >>> import transaction
+      >>> t = transaction.begin()
+      >>> t.addAfterCommitHook(hook, '1')
+
+    We can see that the hook is indeed registered.
+
+      >>> [(hook.func_name, args, kws)
+      ...  for hook, args, kws in t.getAfterCommitHooks()]
+      [('hook', ('1',), {})]
+
+    When transaction commit is done, the hook is called, with its
+    arguments.
+
+      >>> log
+      []
+      >>> t.commit()
+      >>> log
+      ["True arg '1' kw1 'no_kw1' kw2 'no_kw2'"]
+      >>> reset_log()
+
+    A hook's registration is consumed whenever the hook is called.  Since
+    the hook above was called, it's no longer registered:
+
+      >>> len(list(t.getAfterCommitHooks()))
+      0
+      >>> transaction.commit()
+      >>> log
+      []
+
+    The hook is only called after a full commit, not for a savepoint or
+    subtransaction.
+
+      >>> t = transaction.begin()
+      >>> t.addAfterCommitHook(hook, 'A', dict(kw1='B'))
+      >>> dummy = t.savepoint()
+      >>> log
+      []
+      >>> t.commit(subtransaction=True)
+      >>> log
+      []
+      >>> t.commit()
+      >>> log
+      ["True arg 'A' kw1 'B' kw2 'no_kw2'"]
+      >>> reset_log()
+
+    If a transaction is aborted, no hook is called.
+
+      >>> t = transaction.begin()
+      >>> t.addAfterCommitHook(hook, ["OOPS!"])
+      >>> transaction.abort()
+      >>> log
+      []
+      >>> transaction.commit()
+      >>> log
+      []
+
+    The hook is called after the commit is done, so even if the
+    commit fails the hook will have been called.  To provoke failures in
+    commit, we'll add failing resource manager to the transaction.
+
+      >>> class CommitFailure(Exception):
+      ...     pass
+      >>> class FailingDataManager:
+      ...     def tpc_begin(self, txn, sub=False):
+      ...         raise CommitFailure
+      ...     def abort(self, txn):
+      ...         pass
+
+      >>> t = transaction.begin()
+      >>> t.join(FailingDataManager())
+
+      >>> t.addAfterCommitHook(hook, '2')
+      >>> t.commit()
+      Traceback (most recent call last):
+      ...
+      CommitFailure
+      >>> log
+      ["False arg '2' kw1 'no_kw1' kw2 'no_kw2'"]
+      >>> reset_log()
+
+    Let's register several hooks.
+
+      >>> t = transaction.begin()
+      >>> t.addAfterCommitHook(hook, '4', dict(kw1='4.1'))
+      >>> t.addAfterCommitHook(hook, '5', dict(kw2='5.2'))
+
+    They are returned in the same order by getAfterCommitHooks.
+
+      >>> [(hook.func_name, args, kws)     #doctest: +NORMALIZE_WHITESPACE
+      ...  for hook, args, kws in t.getAfterCommitHooks()]
+      [('hook', ('4',), {'kw1': '4.1'}),
+       ('hook', ('5',), {'kw2': '5.2'})]
+
+    And commit also calls them in this order.
+
+      >>> t.commit()
+      >>> len(log)
+      2
+      >>> log  #doctest: +NORMALIZE_WHITESPACE
+      ["True arg '4' kw1 '4.1' kw2 'no_kw2'",
+       "True arg '5' kw1 'no_kw1' kw2 '5.2'"]
+      >>> reset_log()
+
+    While executing, a hook can itself add more hooks, and they will all
+    be called before the real commit starts.
+
+      >>> def recurse(status, txn, arg):
+      ...     log.append('rec' + str(arg))
+      ...     if arg:
+      ...         txn.addAfterCommitHook(hook, '-')
+      ...         txn.addAfterCommitHook(recurse, (txn, arg-1))
+
+      >>> t = transaction.begin()
+      >>> t.addAfterCommitHook(recurse, (t, 3))
+      >>> transaction.commit()
+      >>> log  #doctest: +NORMALIZE_WHITESPACE
+      ['rec3',
+               "True arg '-' kw1 'no_kw1' kw2 'no_kw2'",
+       'rec2',
+               "True arg '-' kw1 'no_kw1' kw2 'no_kw2'",
+       'rec1',
+               "True arg '-' kw1 'no_kw1' kw2 'no_kw2'",
+       'rec0']
+      >>> reset_log()
+
+    If an after commit hook is raising an exception then it will log a
+    message at error level so that if other hooks are registered they
+    can be executed. We don't support execution dependencies at this level.
+
+      >>> mgr = transaction.TransactionManager()
+      >>> do = DataObject(mgr)
+
+      >>> def hookRaise(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'):
+      ...     raise TypeError("Fake raise")
+
+      >>> t = transaction.begin()
+
+      >>> t.addAfterCommitHook(hook, ('-', 1))
+      >>> t.addAfterCommitHook(hookRaise, ('-', 2))
+      >>> t.addAfterCommitHook(hook, ('-', 3))
+      >>> transaction.commit()
+
+      >>> log
+      ["True arg '-' kw1 1 kw2 'no_kw2'", "True arg '-' kw1 3 kw2 'no_kw2'"]
+
+      >>> reset_log()
+
+    Test that the associated transaction manager has been cleanup when
+    after commit hooks are registered
+
+      >>> mgr = transaction.TransactionManager()
+      >>> do = DataObject(mgr)
+
+      >>> t = transaction.begin()
+      >>> len(t._manager._txns)
+      1
+
+      >>> t.addAfterCommitHook(hook, ('-', 1))
+      >>> transaction.commit()
+
+      >>> log
+      ["True arg '-' kw1 1 kw2 'no_kw2'"]
+
+      >>> len(t._manager._txns)
+      0
+
+      >>> reset_log()
+
+
+    The transaction is already committed when the after commit hooks
+    will be executed. Executing the hooks must not have further
+    effects on persistent objects.
+
+    Start a new transaction
+
+      >>> t = transaction.begin()
+
+    Create a DB instance and add a IOBTree within
+
+      >>> from ZODB.tests.util import DB
+      >>> from ZODB.tests.util import P
+      >>> db = DB()
+      >>> con = db.open()
+      >>> root = con.root()
+      >>> root['p'] = P('julien')
+      >>> p = root['p']
+
+      >>> p.name
+      'julien'
+      
+    This hook will get the object from the `DB` instance and change
+    the flag attribute.
+
+      >>> def badhook(status, arg=None, kw1='no_kw1', kw2='no_kw2'):
+      ...     p.name = 'jul'
+
+    Now register this hook and commit.
+
+      >>> t.addAfterCommitHook(badhook, (p, 1))
+      >>> transaction.commit()
+
+    Nothing should have changed since it should have been aborted.
+
+      >>> p.name
+      'julien'
+
+      >>> db.close()
+
+    """
+
 def test_suite():
     from zope.testing.doctest import DocTestSuite
     return unittest.TestSuite((


Property changes on: ZODB/branches/blob-merge-branch/src/zope
___________________________________________________________________
Name: svn:externals
   - interface  svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.0-Zope-2.8-a4/src/zope/interface
proxy      svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.0-Zope-2.8-a4/src/zope/proxy
testing    svn://svn.zope.org/repos/main/zope.testing/trunk/src/zope/testing

   + interface  svn://svn.zope.org/repos/main/Zope3/tags/Zope-3.2.0/src/zope/interface
proxy      svn://svn.zope.org/repos/main/Zope3/tags/Zope-3.2.0/src/zope/proxy
testing    svn://svn.zope.org/repos/main/zope.testing/trunk/src/zope/testing


Modified: ZODB/branches/blob-merge-branch/test.py
===================================================================
--- ZODB/branches/blob-merge-branch/test.py	2006-02-17 21:02:44 UTC (rev 41649)
+++ ZODB/branches/blob-merge-branch/test.py	2006-02-17 22:01:38 UTC (rev 41650)
@@ -36,7 +36,11 @@
     path = LIB_DIR
 print "Running tests from", path
 
-sys.path.append(path)
+# Insert the ZODB src dir first in the sys.path to avoid a name conflict
+# with zope.whatever librairies that might be installed on the Python
+# version used to launch these tests.
+sys.path.insert(0, path)
+
 from zope.testing import testrunner
 
 # Persistence/__init__.py generates a long warning message about the



More information about the Zodb-checkins mailing list