Log message for revision 38638: Work to simplify mounting: - Remove the DBTab package (moved code into Zope2.Startup.datatypes) - Consolidate Mount.py code into MountedObject.py in ZODBMountPoint. - Remove cant-work-now autoClassFactory. - Removed test that was dependent on old mounting implementation.
Changed: D Zope/branches/zodb-blobs-branch/lib/python/DBTab/ D Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/Mount.py U Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/MountedObject.py U Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/tests/testMountPoint.py U Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/datatypes.py U Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/zopeschema.xml -=- Deleted: Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/Mount.py =================================================================== --- Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/Mount.py 2005-09-26 11:40:59 UTC (rev 38637) +++ Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/Mount.py 2005-09-26 13:35:01 UTC (rev 38638) @@ -1,137 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 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 -# -############################################################################## -"""ZODB Mounted database support, simplified for DBTab. - -$Id$""" - -import sys -from logging import getLogger - -try: - from cStringIO import StringIO -except: - from StringIO import StringIO -import traceback - -import Persistence, Acquisition -from Acquisition import aq_base -from ZODB.POSException import MountedStorageError, ConnectionStateError - - -LOG = getLogger('Zope.ZODBMountPoint') - -class MountPoint(Persistence.Persistent, Acquisition.Implicit): - '''The base class for a Zope object which, when traversed, - accesses a different database. - ''' - - # Default values for non-persistent variables. - _v_data = None # An object in an open connection - _v_connect_error = None - - def __init__(self, id): - self.id = id - - def _getDB(self): - """Hook for getting the DB object for this mount point. - """ - raise NotImplementedError - - def _getDBName(self): - """Hook for getting the name of the database for this mount point. - """ - raise NotImplementedError - - def _getRootDBName(self): - """Hook for getting the name of the root database. - """ - raise NotImplementedError - - def _traverseToMountedRoot(self, root, mount_parent): - """Hook for getting the object to be mounted. - """ - raise NotImplementedError - - def __repr__(self): - return "%s(id=%s)" % (self.__class__.__name__, repr(self.id)) - - - def _getMountedConnection(self, anyjar): - db_name = self._getDBName() - conn = anyjar.get_connection(db_name) - return conn - - def _getOrOpenObject(self, parent): - t = self._v_data - if t is not None: - data = t[0] - else: - self._v_connect_error = None - conn = None - try: - anyjar = self._p_jar - if anyjar is None: - anyjar = parent._p_jar - conn = self._getMountedConnection(anyjar) - root = conn.root() - obj = self._traverseToMountedRoot(root, parent) - data = aq_base(obj) - # Store the data object in a tuple to hide from acquisition. - self._v_data = (data,) - except: - # Possibly broken database. - self._logConnectException() - raise - - try: - # XXX This method of finding the mount point is deprecated. - # Do not use the _v_mount_point_ attribute. - data._v_mount_point_ = (aq_base(self),) - except: - # Might be a read-only object. - pass - - return data.__of__(parent) - - - def __of__(self, parent): - # Accesses the database, returning an acquisition - # wrapper around the connected object rather than around self. - try: - return self._getOrOpenObject(parent) - except: - return Acquisition.ImplicitAcquisitionWrapper(self, parent) - - - def _test(self, parent): - '''Tests the database connection. - ''' - self._getOrOpenObject(parent) - return 1 - - - def _logConnectException(self): - '''Records info about the exception that just occurred. - ''' - try: - from cStringIO import StringIO - except: - from StringIO import StringIO - import traceback - exc = sys.exc_info() - LOG.error('Failed to mount database. %s (%s)' % exc[:2], exc_info=exc) - f=StringIO() - traceback.print_tb(exc[2], 100, f) - self._v_connect_error = (exc[0], exc[1], f.getvalue()) - exc = None Modified: Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/MountedObject.py =================================================================== --- Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/MountedObject.py 2005-09-26 11:40:59 UTC (rev 38637) +++ Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/MountedObject.py 2005-09-26 13:35:01 UTC (rev 38638) @@ -11,23 +11,29 @@ # FOR A PARTICULAR PURPOSE # ############################################################################## -"""DBTab mount point (stored in ZODB). +"""Mount point (stored in ZODB). $Id$ """ import os +import sys +import traceback +from cStringIO import StringIO +from logging import getLogger import transaction import Globals +import Acquisition from Acquisition import aq_base, aq_inner, aq_parent from AccessControl.ZopeGuards import guarded_getattr from OFS.SimpleItem import SimpleItem from OFS.Folder import Folder from Products.PageTemplates.PageTemplateFile import PageTemplateFile -from Mount import MountPoint +from ZODB.POSException import MountedStorageError, ConnectionStateError +LOG = getLogger('Zope.ZODBMountPoint') _www = os.path.join(os.path.dirname(__file__), 'www') @@ -111,8 +117,8 @@ return obj -class MountedObject(MountPoint, SimpleItem): - '''A MountPoint with a basic interface for displaying the +class MountedObject(SimpleItem): + '''A database mount point with a basic interface for displaying the reason the database did not connect. ''' meta_type = 'ZODB Mount Point' @@ -124,6 +130,8 @@ icon = 'p_/broken' manage_options = ({'label':'Traceback', 'action':'manage_traceback'},) _v_mount_params = None + _v_data = None + _v_connect_error = None manage_traceback = PageTemplateFile('mountfail.pt', _www) @@ -131,7 +139,7 @@ path = str(path) self._path = path id = path.split('/')[-1] - MountPoint.__init__(self, id) + self.id = id def _getMountedConnection(self, anyjar): db_name = self._getDBName() @@ -205,6 +213,73 @@ raise return obj + def _logConnectException(self): + '''Records info about the exception that just occurred. + ''' + try: + from cStringIO import StringIO + except: + from StringIO import StringIO + import traceback + exc = sys.exc_info() + LOG.error('Failed to mount database. %s (%s)' % exc[:2], exc_info=exc) + f=StringIO() + traceback.print_tb(exc[2], 100, f) + self._v_connect_error = (exc[0], exc[1], f.getvalue()) + exc = None + + + def __of__(self, parent): + # Accesses the database, returning an acquisition + # wrapper around the connected object rather than around self. + try: + return self._getOrOpenObject(parent) + except: + return Acquisition.ImplicitAcquisitionWrapper(self, parent) + + + def _test(self, parent): + '''Tests the database connection. + ''' + self._getOrOpenObject(parent) + return 1 + + def _getOrOpenObject(self, parent): + t = self._v_data + if t is not None: + data = t[0] + else: + self._v_connect_error = None + conn = None + try: + anyjar = self._p_jar + if anyjar is None: + anyjar = parent._p_jar + conn = self._getMountedConnection(anyjar) + root = conn.root() + obj = self._traverseToMountedRoot(root, parent) + data = aq_base(obj) + # Store the data object in a tuple to hide from acquisition. + self._v_data = (data,) + except: + # Possibly broken database. + self._logConnectException() + raise + + try: + # XXX This method of finding the mount point is deprecated. + # Do not use the _v_mount_point_ attribute. + data._v_mount_point_ = (aq_base(self),) + except: + # Might be a read-only object. + pass + + return data.__of__(parent) + + def __repr__(self): + return "%s(id=%s)" % (self.__class__.__name__, repr(self.id)) + + Globals.InitializeClass(MountedObject) @@ -239,7 +314,7 @@ manage_addMountsForm = PageTemplateFile('addMountsForm.pt', _www) def manage_getMountStatus(dispatcher): - """Returns the status of each mount point specified by dbtab.conf. + """Returns the status of each mount point specified by zope.conf """ res = [] conf = getConfiguration() @@ -324,3 +399,4 @@ REQUEST['URL1'] + ('/manage_main?manage_tabs_message=' 'Added %d mount points.' % count)) + Modified: Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/tests/testMountPoint.py =================================================================== --- Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/tests/testMountPoint.py 2005-09-26 11:40:59 UTC (rev 38637) +++ Zope/branches/zodb-blobs-branch/lib/python/Products/ZODBMountPoint/tests/testMountPoint.py 2005-09-26 13:35:01 UTC (rev 38638) @@ -24,7 +24,7 @@ from OFS.Folder import Folder import App.config from Products.ZODBMountPoint.MountedObject import manage_addMounts, getMountPoint -from DBTab.DBTab import DBTab +from Zope2.Startup.datatypes import DBTab try: __file__ @@ -64,8 +64,6 @@ class DBTabTests (unittest.TestCase): - - def setUp(self): global original_config if original_config is None: @@ -129,41 +127,6 @@ self.assertEqual(app.mount2._p_changed, 0) self.assertEqual(app._p_changed, 0) - - def testRaceOnClose(self): - # There used to be a race condition in - # ConnectionPatches.close(). The root connection was returned - # to the pool before the mounted connections were closed. If - # another thread pulled the root connection out of the pool - # before the original thread finished closing mounted - # connections, when the original thread got control back it - # closed the mounted connections even though the new thread - # was using them. - - # Test by patching to watch for a vulnerable moment. - - from ZODB.DB import DB - - def _closeConnection(self, connection): - self._real_closeConnection(connection) - mc = connection._mounted_connections - if mc is not None: - for c in mc.values(): - if c._storage is not None: - raise AssertionError, "Connection remained partly open" - - DB._real_closeConnection = DB._closeConnection - DB._closeConnection = _closeConnection - try: - conn = self.db.open() - conn.root()['Application']['mount1'] - conn.root()['Application']['mount2'] - conn.close() - finally: - DB._closeConnection = DB._real_closeConnection - del DB._real_closeConnection - - def testGetMountPoint(self): self.assert_(getMountPoint(self.app) is None) self.assert_(getMountPoint(self.app.mount1) is not None) Modified: Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/datatypes.py =================================================================== --- Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/datatypes.py 2005-09-26 11:40:59 UTC (rev 38637) +++ Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/datatypes.py 2005-09-26 13:35:01 UTC (rev 38638) @@ -18,6 +18,7 @@ from ZConfig.components.logger import logger from ZODB.config import ZODBDatabase +import OFS.Uninstalled # generic datatypes @@ -109,7 +110,7 @@ # Datatype for the root configuration object # (adds the softwarehome and zopehome fields; default values for some -# computed paths, configures dbtab) +# computed paths, configures the dbtab) def root_config(section): from ZConfig import ConfigurationError @@ -147,9 +148,7 @@ raise ConfigurationError(dup_err % (mount_points[point], name, point)) mount_points[point] = name - from DBTab.DBTab import DBTab section.dbtab = DBTab(mount_factories, mount_points) - return section class ZopeDatabase(ZODBDatabase): @@ -173,10 +172,6 @@ def getName(self): return self.name - def getOpenAtStartup(self): - # XXX implement - return 0 - def computeMountPaths(self): mps = [] for part in self.config.mount_points: @@ -211,3 +206,110 @@ return (real_root, real_path, container_class) raise LookupError('Nothing known about mount path %s' % mount_path) + +class DBTab: + """A Zope database configuration, similar in purpose to /etc/fstab. + """ + + def __init__(self, db_factories, mount_paths): + self.db_factories = db_factories # { name -> DatabaseFactory } + self.mount_paths = mount_paths # { virtual path -> name } + self.databases = {} # { name -> DB instance } + + def listMountPaths(self): + """Returns a sequence of (virtual_mount_path, database_name). + """ + return self.mount_paths.items() + + + def listDatabaseNames(self): + """Returns a sequence of names. + """ + return self.db_factories.keys() + + + def hasDatabase(self, name): + """Returns true if name is the name of a configured database.""" + return self.db_factories.has_key(name) + + + def _mountPathError(self, mount_path): + from ZConfig import ConfigurationError + if mount_path == '/': + raise ConfigurationError( + "No root database configured") + else: + raise ConfigurationError( + "No database configured for mount point at %s" + % mount_path) + + def getDatabase(self, mount_path=None, name=None, is_root=0): + """Returns an opened database. Requires either mount_path or name. + """ + if name is None: + name = self.getName(mount_path) + db = self.databases.get(name, None) + if db is None: + factory = self.getDatabaseFactory(name=name) + db = factory.open(name, self.databases) + return db + + def getDatabaseFactory(self, mount_path=None, name=None): + if name is None: + name = self.getName(mount_path) + if not self.db_factories.has_key(name): + raise KeyError('%s is not a configured database' % repr(name)) + return self.db_factories[name] + + def getName(self, mount_path): + name = self.mount_paths.get(mount_path) + if name is None: + self._mountPathError(mount_path) + return name + +# class factories (potentially) used by the class-factory parameter in +# zopeschema.xml + +def minimalClassFactory(jar, module, name, + _silly=('__doc__',), _globals={}, + ): + """Minimal class factory. + + If any class is not found, this class factory will propagate + the exception to the application, unlike the other class factories. + """ + m = __import__(module, _globals, _globals, _silly) + return getattr(m, name) + +def simpleClassFactory(jar, module, name, + _silly=('__doc__',), _globals={}, + ): + """Class factory without ZClass support. + """ + try: + m = __import__(module, _globals, _globals, _silly) + return getattr(m, name) + except: + return OFS.Uninstalled.Broken(jar, None, (module, name)) + +def zopeClassFactory(jar, module, name, + _silly=('__doc__',), _globals={}, + ): + """Class factory with ZClass support. + """ + try: + if module[:1]=='*': + # ZCLass! Yee ha! + return jar.root()['ZGlobals'][module] + else: + m=__import__(module, _globals, _globals, _silly) + + return getattr(m, name) + except: + return OFS.Uninstalled.Broken(jar, None, (module, name)) + +# There used to be an "autoClassFactory" whose docstring read "If not the root +# connection, use the class factory from the root database, otherwise use the +# Zope class factory." This no longer works with the implementation of +# mounted databases, so we just use the zopeClassFactory as the default + Modified: Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/zopeschema.xml =================================================================== --- Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/zopeschema.xml 2005-09-26 11:40:59 UTC (rev 38637) +++ Zope/branches/zodb-blobs-branch/lib/python/Zope2/Startup/zopeschema.xml 2005-09-26 13:35:01 UTC (rev 38638) @@ -217,7 +217,7 @@ </key> <key name="class-factory" datatype=".importable_name" - default="DBTab.ClassFactories.autoClassFactory"> + default="Zope2.Startup.datatypes.zopeClassFactory"> <description> Change the class factory function a database uses on a per-database basis to support different class factory policy. _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins