[Zope-Checkins] CVS: Packages/ZODB/tests - sampledm.py:1.1.2.1
    Jeremy Hylton 
    jeremy at zope.com
       
    Thu Feb 12 16:41:46 EST 2004
    
    
  
Update of /cvs-repository/Packages/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv1343
Added Files:
      Tag: zope3-zodb3-devel-branch
	sampledm.py 
Log Message:
Quick fix for ZODB package.
Copy the test_SampleDataManager module from the transaction package
into the ZODB package.  Then the test doesn't depend on a test module
from a package that isn't part of ZODB3.
=== Added File Packages/ZODB/tests/sampledm.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Sample objects for use in tests
$Id: sampledm.py,v 1.1.2.1 2004/02/12 21:41:46 jeremy Exp $
"""
class DataManager(object):
    """Sample data manager
       This class provides a trivial data-manager implementation and doc
       strings to illustrate the the protocol and to provide a tool for
       writing tests.
       Our sample data manager has state that is updated through an inc
       method and through transaction operations.
       When we create a sample data manager:
       >>> dm = DataManager()
       It has two bits of state, state:
       >>> dm.state
       0
       and delta:
       >>> dm.delta
       0
       Both of which are initialized to 0.  state is meant to model
       committed state, while delta represents tentative changes within a
       transaction.  We change the state by calling inc:
       >>> dm.inc()
       which updates delta:
       >>> dm.delta
       1
       but state isn't changed until we commit the transaction:
       >>> dm.state
       0
       To commit the changes, we use 2-phase commit. We execute the first
       stage by calling prepare.  We need to pass a transation. Our
       sample data managers don't really use the transactions for much,
       so we'll be lazy and use strings for transactions:
       >>> t1 = '1'
       >>> dm.prepare(t1)
       The sample data manager updates the state when we call prepare:
       >>> dm.state
       1
       >>> dm.delta
       1
       This is mainly so we can detect some affect of calling the methods.
       Now if we call commit:
       >>> dm.commit(t1)
       Our changes are"permanent".  The state reflects the changes and the
       delta has been reset to 0. 
       >>> dm.state
       1
       >>> dm.delta
       0
       """
    def __init__(self):
        self.state = 0
        self.sp = 0
        self.transaction = None
        self.delta = 0
        self.prepared = False
    def inc(self, n=1):
        self.delta += n
    def prepare(self, transaction):
        """Prepare to commit data
        >>> dm = DataManager()
        >>> dm.inc()
        >>> t1 = '1'
        >>> dm.prepare(t1)
        >>> dm.commit(t1)
        >>> dm.state
        1
        >>> dm.inc()
        >>> t2 = '2'
        >>> dm.prepare(t2)
        >>> dm.abort(t2)
        >>> dm.state
        1
        It is en error to call prepare more than once without an intervening
        commit or abort:
        >>> dm.prepare(t1)
        >>> dm.prepare(t1)
        Traceback (most recent call last):
        ...
        TypeError: Already prepared
        >>> dm.prepare(t2)
        Traceback (most recent call last):
        ...
        TypeError: Already prepared
        >>> dm.abort(t1)
        If there was a preceeding savepoint, the transaction must match:
        >>> rollback = dm.savepoint(t1)
        >>> dm.prepare(t2)
        Traceback (most recent call last):
        ,,,
        TypeError: ('Transaction missmatch', '2', '1')
        >>> dm.prepare(t1)
        
        """
        if self.prepared:
            raise TypeError('Already prepared')
        self._checkTransaction(transaction)
        self.prepared = True
        self.transaction = transaction
        self.state += self.delta
    def _checkTransaction(self, transaction):
        if (transaction is not self.transaction
            and self.transaction is not None):
            raise TypeError("Transaction missmatch",
                            transaction, self.transaction)
    def abort(self, transaction):
        """Abort a transaction
        The abort method can be called before two-phase commit to
        throw away work done in the transaction:
        >>> dm = DataManager()
        >>> dm.inc()
        >>> dm.state, dm.delta
        (0, 1)
        >>> t1 = '1'
        >>> dm.abort(t1)
        >>> dm.state, dm.delta
        (0, 0)
        The abort method also throws away work done in savepoints:
        >>> dm.inc()
        >>> r = dm.savepoint(t1)
        >>> dm.inc()
        >>> r = dm.savepoint(t1)
        >>> dm.state, dm.delta
        (0, 2)
        >>> dm.abort(t1)
        >>> dm.state, dm.delta
        (0, 0)
        If savepoints are used, abort must be passed the same
        transaction:
        
        >>> dm.inc()
        >>> r = dm.savepoint(t1)
        >>> t2 = '2'
        >>> dm.abort(t2)
        Traceback (most recent call last):
        ...
        TypeError: ('Transaction missmatch', '2', '1')
        >>> dm.abort(t1)
        The abort method is also used to abort a two-phase commit:
        >>> dm.inc()
        >>> dm.state, dm.delta
        (0, 1)
        >>> dm.prepare(t1)
        >>> dm.state, dm.delta
        (1, 1)
        >>> dm.abort(t1)
        >>> dm.state, dm.delta
        (0, 0)
        Of course, the transactions passed to prepare and abort must
        match:
        
        >>> dm.prepare(t1)
        >>> dm.abort(t2)
        Traceback (most recent call last):
        ...
        TypeError: ('Transaction missmatch', '2', '1')
        
        >>> dm.abort(t1)
        
        """
        self._checkTransaction(transaction)
        if self.transaction is not None:
            self.transaction = None
        if self.prepared:
            self.state -= self.delta
            self.prepared = False
        self.delta = 0
    def commit(self, transaction):
        """Complete two-phase commit
        >>> dm = DataManager()
        >>> dm.state
        0
        >>> dm.inc()
        We start two-phase commit by calling prepare:
        >>> t1 = '1'
        >>> dm.prepare(t1)
        We complete it by calling commit:
        >>> dm.commit(t1)
        >>> dm.state
        1
        It is an error ro call commit without calling prepare first:
        >>> dm.inc()
        >>> t2 = '2'
        >>> dm.commit(t2)
        Traceback (most recent call last):
        ...
        TypeError: Not prepared to commit
        >>> dm.prepare(t2)
        >>> dm.commit(t2)
        If course, the transactions given to prepare and commit must
        be the same:
        
        >>> dm.inc()
        >>> t3 = '3'
        >>> dm.prepare(t3)
        >>> dm.commit(t2)
        Traceback (most recent call last):
        ...
        TypeError: ('Transaction missmatch', '2', '3')
        
        """
        if not self.prepared:
            raise TypeError('Not prepared to commit')
        self._checkTransaction(transaction)
        self.delta = 0
        self.transaction = None
        self.prepared = False
    def savepoint(self, transaction):
        """Provide the ability to rollback transaction state
        Savepoints provide a way to:
        - Save partial transaction work. For some data managers, this
          could allow resources to be used more efficiently.
        - Provide the ability to revert state to a point in a
          transaction without aborting the entire transaction.  In
          other words, savepoints support partial aborts.
        Savepoints don't use two-phase commit. If there are errors in
        setting or rolling back to savepoints, the application should
        abort the containing transaction.  This is *not* the
        responsibility of the data manager.
        Savepoints are always associated with a transaction. Any work
        done in a savepoint's transaction is tentative until the
        transaction is committed using two-phase commit.
        >>> dm = DataManager()
        >>> dm.inc()
        >>> t1 = '1'
        >>> r = dm.savepoint(t1)
        >>> dm.state, dm.delta
        (0, 1)
        >>> dm.inc()
        >>> dm.state, dm.delta
        (0, 2)
        >>> r.rollback()
        >>> dm.state, dm.delta
        (0, 1)
        >>> dm.prepare(t1)
        >>> dm.commit(t1)
        >>> dm.state, dm.delta
        (1, 0)
        Savepoints must have the same transaction:
        >>> r1 = dm.savepoint(t1)
        >>> dm.state, dm.delta
        (1, 0)
        >>> dm.inc()
        >>> dm.state, dm.delta
        (1, 1)
        >>> t2 = '2'
        >>> r2 = dm.savepoint(t2)
        Traceback (most recent call last):
        ...
        TypeError: ('Transaction missmatch', '2', '1')
        >>> r2 = dm.savepoint(t1)
        >>> dm.inc()
        >>> dm.state, dm.delta
        (1, 2)
        If we rollback to an earlier savepoint, we discard all work
        done later:
        >>> r1.rollback()
        >>> dm.state, dm.delta
        (1, 0)
        and we can no longer rollback to the later savepoint:
        >>> r2.rollback()
        Traceback (most recent call last):
        ...
        TypeError: ('Attempt to roll back to invalid save point', 3, 2)
        We can roll back to a savepoint as often as we like:
        >>> r1.rollback()
        >>> r1.rollback()
        >>> r1.rollback()
        >>> dm.state, dm.delta
        (1, 0)
        >>> dm.inc()
        >>> dm.inc()
        >>> dm.inc()
        >>> dm.state, dm.delta
        (1, 3)
        >>> r1.rollback()
        >>> dm.state, dm.delta
        (1, 0)
        But we can't rollback to a savepoint after it has been
        committed:
        >>> dm.prepare(t1)
        >>> dm.commit(t1)
        >>> r1.rollback()
        Traceback (most recent call last):
        ...
        TypeError: Attempt to rollback stale rollback
        """
        if self.prepared:
            raise TypeError("Can't get savepoint during two-phase commit")
        self._checkTransaction(transaction)
        self.transaction = transaction
        self.sp += 1
        return Rollback(self)
class Rollback(object):
    def __init__(self, dm):
        self.dm = dm
        self.sp = dm.sp
        self.delta = dm.delta
        self.transaction = dm.transaction
    def rollback(self):
        if self.transaction is not self.dm.transaction:
            raise TypeError("Attempt to rollback stale rollback")
        if self.dm.sp < self.sp:
            raise TypeError("Attempt to roll back to invalid save point",
                            self.sp, self.dm.sp)
        self.dm.sp = self.sp
        self.dm.delta = self.delta
def test_suite():
    from doctest import DocTestSuite
    return DocTestSuite()
if __name__ == '__main__':
    unittest.main()
    
    
More information about the Zope-Checkins
mailing list