[Checkins] SVN: zc.demostorage2/branches/dev/ Consolidated modules
and got tests to pass.
Jim Fulton
jim at zope.com
Mon Jan 21 15:47:56 EST 2008
Log message for revision 83076:
Consolidated modules and got tests to pass.
Need tests of loadBefore and configuration.
Changed:
U zc.demostorage2/branches/dev/buildout.cfg
U zc.demostorage2/branches/dev/setup.py
U zc.demostorage2/branches/dev/src/zc/demostorage2/README.txt
U zc.demostorage2/branches/dev/src/zc/demostorage2/__init__.py
U zc.demostorage2/branches/dev/src/zc/demostorage2/component.xml
D zc.demostorage2/branches/dev/src/zc/demostorage2/config.py
D zc.demostorage2/branches/dev/src/zc/demostorage2/storage.py
U zc.demostorage2/branches/dev/src/zc/demostorage2/synchronized.txt
U zc.demostorage2/branches/dev/src/zc/demostorage2/tests.py
-=-
Modified: zc.demostorage2/branches/dev/buildout.cfg
===================================================================
--- zc.demostorage2/branches/dev/buildout.cfg 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/buildout.cfg 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1,7 +1,12 @@
[buildout]
develop = .
-parts = test
+parts = test py
[test]
recipe = zc.recipe.testrunner
-eggs =
+eggs = zc.demostorage2
+
+[py]
+recipe = zc.recipe.egg
+eggs = ${test:eggs}
+interpreter = py
Modified: zc.demostorage2/branches/dev/setup.py
===================================================================
--- zc.demostorage2/branches/dev/setup.py 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/setup.py 2008-01-21 20:47:56 UTC (rev 83076)
@@ -4,7 +4,7 @@
"""
setup(
- name = '',
+ name = 'zc.demostorage2',
version = '0.1',
author = 'Jim Fulton',
author_email = 'jim at zope.com',
@@ -14,7 +14,7 @@
packages = find_packages('src'),
namespace_packages = ['zc'],
package_dir = {'': 'src'},
- install_requires = 'setuptools',
+ install_requires = ['ZODB3', 'setuptools'],
zip_safe = False,
entry_points=entry_points,
)
Modified: zc.demostorage2/branches/dev/src/zc/demostorage2/README.txt
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/README.txt 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/README.txt 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1,7 +1,7 @@
Second-generation demo storage
==============================
-The demostorage2 module provides a storage implementation that
+The zc.demostorage2 module provides a storage implementation that
wraps two storages, a base storage and a storage to hold changes.
The base storage is never written to. All new records are written to
the changes storage. Both storages are expected to:
@@ -21,33 +21,30 @@
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:
- >>> import os, tempfile
- >>> tempdir = tempfile.mkdtemp()
- >>> base_path = os.path.join(tempdir, 'base.fs')
-
>>> from ZODB.FileStorage import FileStorage
- >>> base = FileStorage(base_path)
+ >>> base = FileStorage('base.fs')
>>> from ZODB.DB import DB
>>> db = DB(base)
- >>> from ZODB.PersistentMapping import PersistentMapping
+ >>> from persistent.mapping import PersistentMapping
>>> conn = db.open()
>>> conn.root()['1'] = PersistentMapping({'a': 1, 'b':2})
- >>> get_transaction().commit()
+ >>> import transaction
+ >>> transaction.commit()
>>> db.close()
- >>> original_size = os.path.getsize(base_path)
+ >>> import os
+ >>> original_size = os.path.getsize('base.fs')
Now, lets reopen the base storage in read-only mode:
- >>> base = FileStorage(base_path, read_only=True)
+ >>> base = FileStorage('base.fs', read_only=True)
And open a new storage to store changes:
- >>> changes_path = os.path.join(tempdir, 'changes.fs')
- >>> changes = FileStorage(changes_path)
+ >>> changes = FileStorage('changes.fs')
and combine the 2 in a demofilestorage:
- >>> from demostorage2 import DemoStorage2
+ >>> from zc.demostorage2 import DemoStorage2
>>> storage = DemoStorage2(base, changes)
If there are no transactions, the storage reports the lastTransaction
@@ -66,14 +63,14 @@
[('a', 1), ('b', 2)]
>>> conn.root()['2'] = PersistentMapping({'a': 3, 'b':4})
- >>> get_transaction().commit()
+ >>> transaction.commit()
>>> conn.root()['2']['c'] = 5
- >>> get_transaction().commit()
+ >>> transaction.commit()
Here we can see that we haven't modified the base storage:
- >>> original_size == os.path.getsize(base_path)
+ >>> original_size == os.path.getsize('base.fs')
True
But we have modified the changes database:
@@ -89,7 +86,6 @@
>>> storage.lastTransaction() == changes.lastTransaction()
True
-
Let's walk over some of the methods so ewe can see how we delegate to
the new oderlying storages:
@@ -101,11 +97,11 @@
>>> storage.load(p64(1), '') == base.load(p64(1), '')
True
- >>> serial = base.getSerial(p64(0))
+ >>> serial = base.load(p64(0), '')[1]
>>> storage.loadSerial(p64(0), serial) == base.loadSerial(p64(0), serial)
True
- >>> serial = changes.getSerial(p64(0))
+ >>> serial = changes.load(p64(0), '')[1]
>>> storage.loadSerial(p64(0), serial) == changes.loadSerial(p64(0),
... serial)
True
@@ -115,27 +111,25 @@
>>> u64(conn.root()['2']._p_oid)
9223372036854775809L
-Versions aren't supported:
+Let's look at some other methods:
- >>> storage.supportsVersions()
- False
- >>> storage.versions()
- ()
- >>> storage.versionEmpty(p64(0))
+ >>> storage.getName()
+ 'DemoStorage2(base.fs, changes.fs)'
+
+ >>> storage.sortKey() == changes.sortKey()
True
- >>> storage.versionEmpty(p64(60))
+
+ >>> storage.getSize() == changes.getSize()
True
- >>> storage.modifiedInVersion(p64(0))
- ''
- >>> storage.modifiedInVersion(p64(60))
- ''
-Many methods are simply copied from the base storage:
+ >>> len(storage) == len(changes)
+ True
+
+Undo methods are simply copied from the changes storage:
+
>>> [getattr(storage, name) == getattr(changes, name)
- ... for name in ('getName', 'sortKey', 'getSize', '__len__',
- ... 'supportsUndo', 'undo', 'undoLog', 'undoInfo',
- ... 'supportsTransactionalUndo')
+ ... for name in ('supportsUndo', 'undo', 'undoLog', 'undoInfo')
... ]
- [True, True, True, True, True, True, True, True, True]
+ [True, True, True, True]
Modified: zc.demostorage2/branches/dev/src/zc/demostorage2/__init__.py
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/__init__.py 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/__init__.py 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1 +1,175 @@
-from storage import DemoStorage2
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Demo storage that stores changes in a non-memory storage
+"""
+
+import threading
+
+import ZODB.POSException
+from ZODB.utils import p64, u64, z64
+
+from zc.demostorage2.synchronized import synchronized
+
+class DemoStorage2:
+
+ def __init__(self, base, changes):
+ self.changes = changes
+ self.base = base
+
+ supportsUndo = getattr(changes, 'supportsUndo', None)
+ if supportsUndo is not None and supportsUndo():
+ self.supportsUndo = changes.supportsUndo
+ self.undo = changes.undo
+ self.undoLog = changes.undoLog
+ self.undoInfo = changes.undoInfo
+
+ self._oid = max(u64(changes.new_oid()), 1l << 63)
+ self._lock = threading.RLock()
+ self._commit_lock = threading.Lock()
+
+ self._transaction = None
+
+ def close(self):
+ self.base.close()
+ self.changes.close()
+
+ def getName(self):
+ return "DemoStorage2(%s, %s)" % (
+ self.base.getName(), self.changes.getName())
+
+ def __repr__(self):
+ return "<%s: %s>" % (self.__class__.__name__, self.getName())
+
+ def getSize(self):
+ return self.changes.getSize()
+
+ def history(self, *args, **kw):
+ return self.changes.history(*args, **kw)
+
+ def isReadOnly(self):
+ return self.changes.isReadOnly()
+
+ @synchronized
+ def lastTransaction(self):
+ t = self.changes.lastTransaction()
+ if t == z64:
+ t = self.base.lastTransaction()
+ return t
+
+ def __len__(self):
+ return len(self.changes)
+
+ @synchronized
+ def load(self, oid, version=''):
+ try:
+ return self.changes.load(oid, version)
+ except ZODB.POSException.POSKeyError:
+ return self.base.load(oid, version)
+
+ @synchronized
+ def loadBefore(self, oid, tid):
+ try:
+ result = self.changes.loadBefore(oid, tid)
+ except ZODB.POSException.POSKeyError:
+ return self.base.loadBefore(oid, tid)
+
+ @synchronized
+ def loadSerial(self, oid, serial):
+ try:
+ return self.changes.loadSerial(oid, serial)
+ except ZODB.POSException.POSKeyError:
+ return self.base.loadSerial(oid, serial)
+
+ @synchronized
+ def new_oid(self):
+ self._oid += 1
+ return p64(self._oid)
+
+ def pack(self, pack_time, referencesf):
+ pass
+
+ def registerDB(self, db):
+ self.base.registerDB(db)
+ self.changes.registerDB(db)
+
+ def sortKey(self):
+ return self.changes.sortKey()
+
+ @synchronized
+ def store(self, oid, serial, data, version, transaction):
+ assert version==''
+ if transaction is not self._transaction:
+ raise ZODB.POSException.StorageTransactionError(self, transaction)
+
+ # See if we already have changes for this oid
+ try:
+ old = self.changes.load(oid, '')[1]
+ except ZODB.POSException.POSKeyError:
+ try:
+ old = self.base.load(oid, '')[1]
+ except ZODB.POSException.POSKeyError:
+ old = serial
+
+ if old != serial:
+ raise ZODB.POSException.ConflictError(
+ oid=oid, serials=(oserial, serial))
+
+ return self.changes.store(oid, serial, data, '', transaction)
+
+
+ @synchronized
+ def tpc_abort(self, transaction):
+ if self._transaction is not transaction:
+ return
+ self._transaction = None
+ try:
+ self.changes.tpc_abort(transaction)
+ finally:
+ self._commit_lock.release()
+
+ def tpc_begin(self, transaction, tid=None, status=' '):
+ if self._transaction is transaction:
+ return
+ self._commit_lock.acquire()
+ self._begin(transaction, tid, status)
+
+ @synchronized
+ def _begin(self, transaction, tid, status):
+ self._transaction = transaction
+ self.changes.tpc_begin(transaction, tid, status)
+
+ @synchronized
+ def tpc_finish(self, transaction, func = lambda: None):
+ if self._transaction is not transaction:
+ return
+ self._transaction = None
+ self.changes.tpc_finish(transaction)
+ self._commit_lock.release()
+
+ @synchronized
+ def tpc_vote(self, transaction):
+ if self._transaction is not transaction:
+ return
+ return self.changes.tpc_vote(transaction)
+
+class ZConfig:
+
+ def __init__(self, config):
+ self.config = config
+ self.name = config.getSectionName()
+
+ def open(self):
+ base = self.config.base.open()
+ changes = self.config.changes.open()
+ return DemoStorage2(base, changes)
Modified: zc.demostorage2/branches/dev/src/zc/demostorage2/component.xml
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/component.xml 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/component.xml 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1,9 +1,13 @@
-<component prefix="demostorage2.config">
- <sectiontype name="demostorage2" datatype=".DemoStorage2"
- implements="ZODB.storage">
- <section type="ZODB.storage" name="base" attribute="base"/>
- <section type="ZODB.storage" name="changes" attribute="changes"/>
- </sectiontype>
+<component>
+ <sectiontype
+ name="demostorage2"
+ datatype="zc.demostorage2.ZConfig"
+ implements="ZODB.storage"
+ >
+ <section type="ZODB.storage" name="base" attribute="base" required="yes" />
+ <section type="ZODB.storage" name="changes" attribute="changes"
+ required="yes" />
+ </sectiontype>
</component>
Deleted: zc.demostorage2/branches/dev/src/zc/demostorage2/config.py
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/config.py 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/config.py 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1,10 +0,0 @@
-
-import demostorage2
-from ZODB.config import BaseConfig
-
-class DemoStorage2(BaseConfig):
-
- def open(self):
- base = self.config.base.open()
- changes = self.config.changes.open()
- return demostorage2.DemoStorage2(base, changes)
Deleted: zc.demostorage2/branches/dev/src/zc/demostorage2/storage.py
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/storage.py 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/storage.py 2008-01-21 20:47:56 UTC (rev 83076)
@@ -1,159 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 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.
-#
-##############################################################################
-"""Demo storage that stores changes in a file storage
-
-$Id$
-"""
-
-import threading
-
-import ZODB.POSException
-from ZODB.utils import p64, u64, z64
-
-from demostorage2.synchronized import synchronized
-
-class DemoStorage2:
-
- def __init__(self, base, changes):
- self.changes = changes
- self.base = base
-
- self.getName = changes.getName
- self.sortKey = changes.sortKey
- self.getSize = changes.getSize
- self.__len__ = changes.__len__
- self.supportsUndo = changes.supportsUndo
- self.supportsTransactionalUndo = changes.supportsTransactionalUndo
- self.undo = changes.undo
- self.undoLog = changes.undoLog
- self.undoInfo = changes.undoInfo
-
- self._oid = max(u64(changes.new_oid()), 1l << 63)
- self._lock = threading.RLock()
- self._commit_lock = threading.Lock()
-
- self._transaction = None
-
- def registerDB(self, db, limit):
- self.base.registerDB(db, limit)
- self.changes.registerDB(db, limit)
-
- def close(self):
- self.base.close()
- self.changes.close()
-
- def load(self, oid, version):
- try:
- return self.changes.load(oid, version)
- except ZODB.POSException.POSKeyError:
- return self.base.load(oid, version)
- load = synchronized(load)
-
- def getSerial(self, oid):
- return self.load(oid, '')[1]
-
- def loadSerial(self, oid, serial):
- try:
- return self.changes.loadSerial(oid, serial)
- except ZODB.POSException.POSKeyError:
- return self.base.loadSerial(oid, serial)
- loadSerial = synchronized(loadSerial)
-
- def new_oid(self):
- self._oid += 1
- return p64(self._oid)
- new_oid = synchronized(new_oid)
-
- def tpc_begin(self, transaction, tid=None, status=' '):
- if self._transaction is transaction:
- return
- self._commit_lock.acquire()
- self._begin(transaction, tid, status)
-
- def _begin(self, transaction, tid, status):
- self._transaction = transaction
- self.changes.tpc_begin(transaction, tid, status)
- _begin = synchronized(_begin)
-
- def tpc_abort(self, transaction):
- if self._transaction is not transaction:
- return
- self._transaction = None
- try:
- self.changes.tpc_abort(transaction)
- finally:
- self._commit_lock.release()
- tpc_abort = synchronized(tpc_abort)
-
- def store(self, oid, serial, data, version, transaction):
- if transaction is not self._transaction:
- raise ZODB.POSException.StorageTransactionError(self, transaction)
-
- if version:
- raise ValueError("Invalid version", version)
-
- # See if we already have changes for this oid
- try:
- old = self.changes.getSerial(oid)
- except ZODB.POSException.POSKeyError:
- try:
- old = self.base.getSerial(oid)
- except ZODB.POSException.POSKeyError:
- old = serial
-
- if old != serial:
- raise ZODB.POSException.ConflictError(
- oid=oid, serials=(oserial, serial))
-
- return self.changes.store(oid, serial, data, '', transaction)
- store = synchronized(store)
-
- def supportsVersions(self):
- return False
-
- def tpc_vote(self, transaction):
- if self._transaction is not transaction:
- return
- return self.changes.tpc_vote(transaction)
- tpc_vote = synchronized(tpc_vote)
-
- def tpc_finish(self, transaction, func = lambda: None):
- if self._transaction is not transaction:
- return
- self._transaction = None
- self.changes.tpc_finish(transaction)
- self._commit_lock.release()
- tpc_finish = synchronized(tpc_finish)
-
- def history(self, *args, **kw):
- return self.changes.history(*args, **kw)
-
- def lastTransaction(self):
- t = self.changes.lastTransaction()
- if t == z64:
- t = self.base.lastTransaction()
- return t
- lastTransaction = synchronized(lastTransaction)
-
- def isReadOnly(self):
- return False
-
- def versionEmpty(*args, **kw):
- return True
-
- def modifiedInVersion(*args, **kw):
- return ''
-
- def versions(*args, **kw):
- return ()
Modified: zc.demostorage2/branches/dev/src/zc/demostorage2/synchronized.txt
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/synchronized.txt 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/synchronized.txt 2008-01-21 20:47:56 UTC (rev 83076)
@@ -31,33 +31,32 @@
fairly burdonsome. The synchonized module provides a simple
decorator/descriptor that automates this:
-
- >>> from demostorage2.synchronized import synchronized
+ >>> from zc.demostorage2.synchronized import synchronized
>>> class Counter:
... def __init__(self):
... self._lock = threading.RLock()
... self.value = 0
...
+ ... @synchronized
... def inc(self):
... # looking for trouble
... old = self.value
... time.sleep(0.0001)
... self.value = old + 1
- ... inc = synchronized(inc)
...
+ ... @synchronized
... def dec(self):
... # looking for trouble
... old = self.value
... time.sleep(0.0001)
... self.value = old - 1
- ... dec = synchronized('_lock')(dec)
...
+ ... @synchronized
... def getvalue(self):
... return self.value
- ... getvalue = synchronized(getvalue)
-The decorator can be passed a lock name. If no name is passed, then
-'_lock' is assumed.
+The class using te decorator must have a _lock attribute, which is
+typically a threading.RLock.
With something like this in place, we can safely update instances of
our class from multiple threads:
Modified: zc.demostorage2/branches/dev/src/zc/demostorage2/tests.py
===================================================================
--- zc.demostorage2/branches/dev/src/zc/demostorage2/tests.py 2008-01-21 19:56:31 UTC (rev 83075)
+++ zc.demostorage2/branches/dev/src/zc/demostorage2/tests.py 2008-01-21 20:47:56 UTC (rev 83076)
@@ -11,23 +11,24 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-import unittest
-import shutil
-from ZODB.Transaction import get_transaction
+import time, unittest
+
from zope.testing import doctest
+import zope.testing.setupstack
-def cleanupReadme(test):
- get_transaction().abort()
- test.globs['db'].close()
- shutil.rmtree(test.globs['tempdir'])
+import transaction
-def testSomeDlegation():
+def setUp(test):
+ 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, limit):
- ... print self.name, db, limit
+ ... def registerDB(self, db):
+ ... print self.name, db
... def close(self):
... print self.name, 'closed'
... getName = sortKey = getSize = __len__ = None
@@ -40,12 +41,12 @@
... def tpc_abort(self, t):
... pass
- >>> from demostorage2 import DemoStorage2
+ >>> from zc.demostorage2 import DemoStorage2
>>> storage = DemoStorage2(S(1), S(2))
- >>> storage.registerDB(1, 2)
- 1 1 2
- 2 1 2
+ >>> storage.registerDB(1)
+ 1 1
+ 2 1
>>> storage.close()
1 closed
@@ -60,8 +61,13 @@
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('synchronized.txt'),
- doctest.DocTestSuite(),
- doctest.DocFileSuite('README.txt', tearDown=cleanupReadme),
+ doctest.DocTestSuite(
+ setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
+ ),
+ doctest.DocFileSuite(
+ 'README.txt',
+ setUp=setUp, tearDown=zope.testing.setupstack.tearDown,
+ ),
))
if __name__ == '__main__':
More information about the Checkins
mailing list