Commit dcfb60ab authored by Jim Fulton's avatar Jim Fulton

Reimplemented DemoStorage to be just a wrapper around a base storage

and a changes storage.
parent 13c9c035
This diff is collapsed.
DemoStorage demo (doctest)
--------------------------
Note that most people will configure the storage through ZConfig. If
you are one of those people, you may want to stop here. :) The
examples below show you how to use the storage from Python, but they
also exercise lots of details you might not be interested in.
To see how this works, we'll start by creating a base storage and
puting an object (in addition to the root object) in it:
>>> from ZODB.FileStorage import FileStorage
>>> base = FileStorage('base.fs')
>>> from ZODB.DB import DB
>>> db = DB(base)
>>> from persistent.mapping import PersistentMapping
>>> conn = db.open()
>>> conn.root()['1'] = PersistentMapping({'a': 1, 'b':2})
>>> import transaction
>>> transaction.commit()
>>> db.close()
>>> import os
>>> original_size = os.path.getsize('base.fs')
Now, lets reopen the base storage in read-only mode:
>>> base = FileStorage('base.fs', read_only=True)
And open a new storage to store changes:
>>> changes = FileStorage('changes.fs')
and combine the 2 in a demofilestorage:
>>> from ZODB.DemoStorage import DemoStorage
>>> storage = DemoStorage(base=base, changes=changes)
If there are no transactions, the storage reports the lastTransaction
of the base database:
>>> storage.lastTransaction() == base.lastTransaction()
True
Let's add some data:
>>> db = DB(storage)
>>> conn = db.open()
>>> items = conn.root()['1'].items()
>>> items.sort()
>>> items
[('a', 1), ('b', 2)]
>>> conn.root()['2'] = PersistentMapping({'a': 3, 'b':4})
>>> transaction.commit()
>>> conn.root()['2']['c'] = 5
>>> transaction.commit()
Here we can see that we haven't modified the base storage:
>>> original_size == os.path.getsize('base.fs')
True
But we have modified the changes database:
>>> len(changes)
2
Our lastTransaction reflects the lastTransaction of the changes:
>>> storage.lastTransaction() > base.lastTransaction()
True
>>> storage.lastTransaction() == changes.lastTransaction()
True
Let's walk over some of the methods so ewe can see how we delegate to
the new underlying storages:
>>> from ZODB.utils import p64, u64
>>> storage.load(p64(0), '') == changes.load(p64(0), '')
True
>>> storage.load(p64(0), '') == base.load(p64(0), '')
False
>>> storage.load(p64(1), '') == base.load(p64(1), '')
True
>>> serial = base.getTid(p64(0))
>>> storage.loadSerial(p64(0), serial) == base.loadSerial(p64(0), serial)
True
>>> serial = changes.getTid(p64(0))
>>> storage.loadSerial(p64(0), serial) == changes.loadSerial(p64(0),
... serial)
True
The object id of the new object is quite random, and typically large:
>>> print u64(conn.root()['2']._p_oid)
7106521602475165646
Let's look at some other methods:
>>> storage.getName()
"DemoStorage('base.fs', 'changes.fs')"
>>> storage.sortKey() == changes.sortKey()
True
>>> storage.getSize() == changes.getSize()
True
>>> len(storage) == len(changes)
True
Undo methods are simply copied from the changes storage:
>>> [getattr(storage, name) == getattr(changes, name)
... for name in ('supportsUndo', 'undo', 'undoLog', 'undoInfo')
... ]
[True, True, True, True]
...@@ -174,9 +174,9 @@ ...@@ -174,9 +174,9 @@
<sectiontype name="demostorage" datatype=".DemoStorage" <sectiontype name="demostorage" datatype=".DemoStorage"
implements="ZODB.storage"> implements="ZODB.storage">
<key name="name" default="Demo Storage"/> <key name="name" />
<section type="ZODB.storage" name="*" attribute="base"/> <section type="ZODB.storage" name="*" attribute="base" />
<key name="quota" datatype="integer"/> <section type="ZODB.storage" name="changes" attribute="changes" />
</sectiontype> </sectiontype>
......
...@@ -125,9 +125,11 @@ class DemoStorage(BaseConfig): ...@@ -125,9 +125,11 @@ class DemoStorage(BaseConfig):
base = self.config.base.open() base = self.config.base.open()
else: else:
base = None base = None
return DemoStorage(self.config.name, if self.config.changes:
base=base, changes = self.config.changes.open()
quota=self.config.quota) else:
changes = None
return DemoStorage(self.config.name, base=base, changes=changes)
class FileStorage(BaseConfig): class FileStorage(BaseConfig):
......
...@@ -12,16 +12,34 @@ ...@@ -12,16 +12,34 @@
# #
############################################################################## ##############################################################################
import unittest import unittest
import random
import transaction import transaction
from ZODB.DB import DB from ZODB.DB import DB
from zope.testing import doctest
import zope.testing.setupstack
import ZODB.utils import ZODB.utils
import ZODB.DemoStorage import ZODB.DemoStorage
from ZODB.tests import StorageTestBase, BasicStorage from ZODB.tests import (
from ZODB.tests import Synchronization BasicStorage,
HistoryStorage,
class DemoStorageTests(StorageTestBase.StorageTestBase, IteratorStorage,
MTStorage,
PackableStorage,
RevisionStorage,
StorageTestBase,
Synchronization,
)
class DemoStorageTests(
StorageTestBase.StorageTestBase,
BasicStorage.BasicStorage, BasicStorage.BasicStorage,
HistoryStorage.HistoryStorage,
IteratorStorage.ExtendedIteratorStorage,
IteratorStorage.IteratorStorage,
MTStorage.MTStorage,
PackableStorage.PackableStorage,
RevisionStorage.RevisionStorage,
Synchronization.SynchronizedStorage, Synchronization.SynchronizedStorage,
): ):
...@@ -44,6 +62,25 @@ class DemoStorageTests(StorageTestBase.StorageTestBase, ...@@ -44,6 +62,25 @@ class DemoStorageTests(StorageTestBase.StorageTestBase,
self.assertEqual(s2.load(ZODB.utils.z64, ''), self.assertEqual(s2.load(ZODB.utils.z64, ''),
self._storage.load(ZODB.utils.z64, '')) self._storage.load(ZODB.utils.z64, ''))
def checkLengthAndBool(self):
self.assertEqual(len(self._storage), 0)
self.assert_(not self._storage)
db = DB(self._storage) # creates object 0. :)
self.assertEqual(len(self._storage), 1)
self.assert_(self._storage)
conn = db.open()
for i in range(10):
conn.root()[i] = conn.root().__class__()
transaction.commit()
self.assertEqual(len(self._storage), 11)
self.assert_(self._storage)
def checkLoadBeforeUndo(self):
pass # we don't support undo yet
checkUndoZombie = checkLoadBeforeUndo
def checkPackWithMultiDatabaseReferences(self):
pass # we never do gc
class DemoStorageWrappedBase(DemoStorageTests): class DemoStorageWrappedBase(DemoStorageTests):
...@@ -59,29 +96,92 @@ class DemoStorageWrappedBase(DemoStorageTests): ...@@ -59,29 +96,92 @@ class DemoStorageWrappedBase(DemoStorageTests):
def _makeBaseStorage(self): def _makeBaseStorage(self):
raise NotImplementedError raise NotImplementedError
class DemoStorageWrappedAroundFileStorage(DemoStorageWrappedBase): class DemoStorageWrappedAroundMappingStorage(DemoStorageWrappedBase):
def _makeBaseStorage(self): def _makeBaseStorage(self):
from ZODB.MappingStorage import MappingStorage from ZODB.MappingStorage import MappingStorage
return MappingStorage() return MappingStorage()
class DemoStorageWrappedAroundMappingStorage(DemoStorageWrappedBase): class DemoStorageWrappedAroundFileStorage(DemoStorageWrappedBase):
def _makeBaseStorage(self): def _makeBaseStorage(self):
from ZODB.FileStorage import FileStorage from ZODB.FileStorage import FileStorage
return FileStorage('FileStorageTests.fs') return FileStorage('FileStorageTests.fs')
def setUp(test):
random.seed(0)
zope.testing.setupstack.setUpDirectory(test)
zope.testing.setupstack.register(test, transaction.abort)
def testSomeDelegation():
r"""
>>> class S:
... def __init__(self, name):
... self.name = name
... def registerDB(self, db):
... print self.name, db
... def close(self):
... print self.name, 'closed'
... sortKey = getSize = __len__ = history = getTid = None
... tpc_finish = tpc_vote = tpc_transaction = None
... _lock_acquire = _lock_release = lambda: None
... getName = lambda self: 'S'
... isReadOnly = tpc_transaction = None
... supportsUndo = undo = undoLog = undoInfo = None
... supportsTransactionalUndo = None
... def new_oid(self):
... return '\0' * 8
... def tpc_begin(self, t, tid, status):
... print 'begin', tid, status
... def tpc_abort(self, t):
... pass
>>> from ZODB.DemoStorage import DemoStorage
>>> storage = DemoStorage(base=S(1), changes=S(2))
>>> storage.registerDB(1)
2 1
>>> storage.close()
1 closed
2 closed
>>> storage.tpc_begin(1, 2, 3)
begin 2 3
>>> storage.tpc_abort(1)
"""
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('synchronized.txt'),
doctest.DocTestSuite(
setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
),
doctest.DocFileSuite(
'README.txt',
setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
),
))
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite((
doctest.DocTestSuite(
setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
),
doctest.DocFileSuite(
'../DemoStorage.test',
setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
),
))
suite.addTest(unittest.makeSuite(DemoStorageTests, 'check')) suite.addTest(unittest.makeSuite(DemoStorageTests, 'check'))
suite.addTest(unittest.makeSuite(DemoStorageWrappedAroundFileStorage, suite.addTest(unittest.makeSuite(DemoStorageWrappedAroundFileStorage,
'check')) 'check'))
suite.addTest(unittest.makeSuite(DemoStorageWrappedAroundMappingStorage, suite.addTest(unittest.makeSuite(DemoStorageWrappedAroundMappingStorage,
'check')) 'check'))
return suite return suite
if __name__ == "__main__":
loader = unittest.TestLoader()
loader.testMethodPrefix = "check"
unittest.main(testLoader=loader)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment