From bd13e9201ed580ddd0dbaafbe74c8cff16a7a4ac Mon Sep 17 00:00:00 2001 From: Jim Fulton <jim@zope.com> Date: Thu, 30 Apr 2009 11:43:49 +0000 Subject: [PATCH] The databasesFrom... methods now support multiple databases. Added an option to disallow cross-database references. --- src/ZODB/config.py | 25 +++++-- src/ZODB/config.xml | 3 +- src/ZODB/tests/testConfig.py | 135 ++++++++++++++++++++++++++--------- 3 files changed, 123 insertions(+), 40 deletions(-) diff --git a/src/ZODB/config.py b/src/ZODB/config.py index 9ce5d612..3d198014 100644 --- a/src/ZODB/config.py +++ b/src/ZODB/config.py @@ -49,8 +49,15 @@ def databaseFromURL(url): config, handler = ZConfig.loadConfig(getDbSchema(), url) return databaseFromConfig(config.database) -def databaseFromConfig(section): - return section.open() +def databaseFromConfig(database_factories): + databases = {} + first = None + for factory in database_factories: + db = factory.open(databases) + if first is None: + first = db + + return first def storageFromString(s): return storageFromFile(StringIO(s)) @@ -93,8 +100,16 @@ class ZODBDatabase(BaseConfig): section = self.config storage = section.storage.open() options = {} - if section.pool_timeout is not None: - options['pool_timeout'] = section.pool_timeout + + def _option(name, oname=None): + v = getattr(section, name) + if v is not None: + if oname is None: + oname = name + options[oname] = v + + _option('pool_timeout') + _option('allow_implicit_cross_references', 'xrefs') try: return ZODB.DB( @@ -106,7 +121,7 @@ class ZODBDatabase(BaseConfig): historical_cache_size=section.historical_cache_size, historical_cache_size_bytes=section.historical_cache_size_bytes, historical_timeout=section.historical_timeout, - database_name=section.database_name, + database_name=section.database_name or self.name or '', databases=databases, **options) except: diff --git a/src/ZODB/config.xml b/src/ZODB/config.xml index 28f8ad75..6ab59c5c 100644 --- a/src/ZODB/config.xml +++ b/src/ZODB/config.xml @@ -2,6 +2,7 @@ <import package="ZODB"/> - <section type="ZODB.database" name="*" attribute="database"/> + <multisection type="ZODB.database" name="*" attribute="database" + required="yes" /> </schema> diff --git a/src/ZODB/tests/testConfig.py b/src/ZODB/tests/testConfig.py index 3672b5a5..8d39073b 100644 --- a/src/ZODB/tests/testConfig.py +++ b/src/ZODB/tests/testConfig.py @@ -12,23 +12,26 @@ # ############################################################################## -import os -import transaction +import doctest +import tempfile import unittest -import ZEO.ClientStorage + +import transaction import ZODB.config -import ZODB.POSException -import ZODB.tests.util -from zope.testing import doctest +from ZODB.POSException import ReadOnlyError -class ConfigTestBase(ZODB.tests.util.TestCase): +class ConfigTestBase(unittest.TestCase): def _opendb(self, s): return ZODB.config.databaseFromString(s) + def tearDown(self): + if getattr(self, "storage", None) is not None: + self.storage.cleanup() + def _test(self, s): db = self._opendb(s) - self.storage = db.storage + self.storage = db._storage # Do something with the database to make sure it works cn = db.open() rt = cn.root() @@ -56,26 +59,28 @@ class ZODBConfigTest(ConfigTestBase): """) def test_file_config1(self): + path = tempfile.mktemp() self._test( """ <zodb> <filestorage> - path Data.fs + path %s </filestorage> </zodb> - """) + """ % path) def test_file_config2(self): + path = tempfile.mktemp() cfg = """ <zodb> <filestorage> - path Data.fs + path %s create false read-only true </filestorage> </zodb> - """ - self.assertRaises(ZODB.POSException.ReadOnlyError, self._test, cfg) + """ % path + self.assertRaises(ReadOnlyError, self._test, cfg) def test_demo_config(self): cfg = """ @@ -110,54 +115,116 @@ class ZEOConfigTest(ConfigTestBase): </zodb> """ config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg)) - self.assertEqual(config.database.config.storage.config.blob_dir, + self.assertEqual(config.database[0].config.storage.config.blob_dir, None) self.assertRaises(ClientDisconnected, self._test, cfg) cfg = """ <zodb> <zeoclient> - blob-dir blobs + blob-dir /tmp server localhost:56897 wait false </zeoclient> </zodb> """ config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg)) - self.assertEqual( - os.path.abspath(config.database.config.storage.config.blob_dir), - os.path.abspath('blobs')) + self.assertEqual(config.database[0].config.storage.config.blob_dir, + '/tmp') self.assertRaises(ClientDisconnected, self._test, cfg) -def db_connection_pool_timeout(): +def database_xrefs_config(): + r""" + >>> db = ZODB.config.databaseFromString( + ... "<zodb>\n<mappingstorage>\n</mappingstorage>\n</zodb>\n") + >>> db.xrefs + True + >>> db = ZODB.config.databaseFromString( + ... "<zodb>\nallow-implicit-cross-references true\n" + ... "<mappingstorage>\n</mappingstorage>\n</zodb>\n") + >>> db.xrefs + True + >>> db = ZODB.config.databaseFromString( + ... "<zodb>\nallow-implicit-cross-references false\n" + ... "<mappingstorage>\n</mappingstorage>\n</zodb>\n") + >>> db.xrefs + False """ -Test that the database pool timeout option works: - >>> db = ZODB.config.databaseFromString(''' - ... <zodb> - ... <mappingstorage/> - ... </zodb> - ... ''') - >>> db.pool._timeout == 1<<31 - True +def multi_atabases(): + r"""If there are multiple codb sections -> multidatabase >>> db = ZODB.config.databaseFromString(''' - ... <zodb> - ... pool-timeout 600 - ... <mappingstorage/> - ... </zodb> + ... <zodb> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... <zodb Foo> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... <zodb> + ... database-name Bar + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> ... ''') - >>> db.pool._timeout == 600 + >>> sorted(db.databases) + ['', 'Bar', 'foo'] + + >>> db.database_name + '' + >>> db.databases[db.database_name] is db + True + >>> db.databases['foo'] is not db + True + >>> db.databases['Bar'] is not db + True + >>> db.databases['Bar'] is not db.databases['foo'] True - """ + Can't have repeats: + + >>> ZODB.config.databaseFromString(''' + ... <zodb 1> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... <zodb 1> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... <zodb 1> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... ''') # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ConfigurationSyntaxError: + section names must not be re-used within the same container:'1' (line 9) + + >>> ZODB.config.databaseFromString(''' + ... <zodb> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... <zodb> + ... <mappingstorage> + ... </mappingstorage> + ... </zodb> + ... ''') # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: database_name '' already in databases + """ def test_suite(): suite = unittest.TestSuite() + suite.addTest(doctest.DocTestSuite()) suite.addTest(unittest.makeSuite(ZODBConfigTest)) suite.addTest(unittest.makeSuite(ZEOConfigTest)) - suite.addTest(doctest.DocTestSuite()) return suite -- 2.30.9