[Zope-Checkins] SVN: Zope/branches/zodb-blobs-branch/lib/python/ Work to simplify mounting:

Chris McDonough chrism at plope.com
Mon Sep 26 09:35:02 EDT 2005


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.



More information about the Zope-Checkins mailing list