[Checkins] SVN: z3c.mountpoint/trunk/ And here is the code ...

Stephan Richter srichter at cosmos.phy.tufts.edu
Mon Oct 1 23:02:33 EDT 2007


Log message for revision 80457:
  And here is the code ...
  

Changed:
  A   z3c.mountpoint/trunk/
  A   z3c.mountpoint/trunk/README.txt
  A   z3c.mountpoint/trunk/__init__.py
  A   z3c.mountpoint/trunk/browser.py
  A   z3c.mountpoint/trunk/browser.zcml
  A   z3c.mountpoint/trunk/configure.zcml
  A   z3c.mountpoint/trunk/connection.py
  A   z3c.mountpoint/trunk/interfaces.py
  A   z3c.mountpoint/trunk/mountpoint.py
  A   z3c.mountpoint/trunk/remoteproxy.py
  A   z3c.mountpoint/trunk/tests.py

-=-
Added: z3c.mountpoint/trunk/README.txt
===================================================================
--- z3c.mountpoint/trunk/README.txt	                        (rev 0)
+++ z3c.mountpoint/trunk/README.txt	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,122 @@
+================
+ZODB Mount Point
+================
+
+This package provides a very simple implementation of a mount point for an
+object in another ZODB connection. If you have multiple connections defined in
+your ``zope.conf`` configuration file or multiple databases defined in your
+Python code, you can use this package to mount any object from any database at
+any location of another database.
+
+Let's start by creating two databases in the typical Zope 3 application layout:
+
+  >>> from ZODB.tests.test_storage import MinimalMemoryStorage
+  >>> from ZODB import DB
+  >>> from zope.app.folder import rootFolder, Folder
+  >>> import transaction
+
+  >>> dbmap = {}
+
+  >>> db1 = DB(MinimalMemoryStorage(), database_name='db1', databases=dbmap)
+  >>> conn1 = db1.open()
+  >>> conn1.root()['Application'] = rootFolder()
+
+  >>> db2 = DB(MinimalMemoryStorage(), database_name='db2', databases=dbmap)
+  >>> conn2 = db2.open()
+  >>> conn2.root()['Application'] = rootFolder()
+
+  >>> transaction.commit()
+
+Let's now add a sub-folder to the second database, which will serve as the
+object which we wish to mount:
+
+  >>> conn2.root()['Application']['Folder2-1'] = Folder()
+  >>> transaction.commit()
+
+We can now create a mount point:
+
+  >>> from z3c.mountpoint import mountpoint
+  >>> mountPoint = mountpoint.MountPoint(
+  ...     'db2', objectPath=u'/Folder2-1', objectName=u'F2-1')
+
+The first argument to the constructor is the connection name of the database,
+the second argument is the path to the mounted object within the mounted DB
+and the object name is the name under which the object is mounted.
+
+Now we can add the mount point to the first database:
+
+  >>> conn1.root()['Application']['mp'] = mountPoint
+  >>> transaction.commit()
+
+We can now access the mounted object as follows:
+
+  >>> conn1.root()['Application']['mp'].object
+  <zope.app.folder.folder.Folder object at ...>
+
+Note that the object name is not yet used; it is for traversal only.
+
+
+Traversal
+---------
+
+So let's have a look at the traversal next. Before being able to traverse, we
+need to register the special mount point traverser:
+
+  >>> import zope.component
+  >>> zope.component.provideAdapter(mountpoint.MountPointTraverser)
+
+We should now be able to traverse to the mounted object now:
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> req = TestRequest()
+
+  >>> from zope.app.publication.publicationtraverse import PublicationTraverser
+  >>> traverser = PublicationTraverser()
+  >>> traverser.traversePath(req, conn1.root()['Application'], 'mp/F2-1')
+  <zope.app.folder.folder.Folder object at ...>
+
+When we add a new object remotely, it available via the mount point as well:
+
+  >>> conn2.root()['Application']['Folder2-1']['Folder2-1.1'] = Folder()
+  >>> transaction.commit()
+
+  >>> tuple(traverser.traversePath(
+  ...    req, conn1.root()['Application'], 'mp/F2-1').keys())
+  (u'Folder2-1.1',)
+
+Now, by default the objects refer to their original path:
+
+  >>> f211 = traverser.traversePath(
+  ...    req, conn1.root()['Application'], 'mp/F2-1/Folder2-1.1')
+
+  >>> from zope.traversing.browser import absoluteurl
+  >>> absoluteurl.absoluteURL(f211, req)
+  'http://127.0.0.1/Folder2-1/Folder2-1.1'
+
+This package solves that problem by wrapping all object by a special remote
+location proxy and providing a special wrapping traverser for those proxies:
+
+  >>> from z3c.mountpoint import remoteproxy
+  >>> zope.component.provideAdapter(remoteproxy.RemoteLocationProxyTraverser)
+
+  >>> f211 = traverser.traversePath(
+  ...    req, conn1.root()['Application'], 'mp/F2-1/Folder2-1.1')
+  >>> absoluteurl.absoluteURL(f211, req)
+  'http://127.0.0.1/mp/F2-1/Folder2-1.1'
+
+
+Updating the Mount Point
+------------------------
+
+Whenever any attribute on the mount point is modified, the mount object is
+updated. For example, when the object path is changed, the object is adjusted
+as well. This is done with an event subscriber:
+
+  >>> mountPoint.objectPath = u'/Folder2-1/Folder2-1.1'
+
+  >>> modifiedEvent = object()
+  >>> mountpoint.updateMountedObject(mountPoint, modifiedEvent)
+
+  >>> f211 == mountPoint.object
+  True
+


Property changes on: z3c.mountpoint/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.mountpoint/trunk/__init__.py
===================================================================
--- z3c.mountpoint/trunk/__init__.py	                        (rev 0)
+++ z3c.mountpoint/trunk/__init__.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.mountpoint/trunk/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/browser.py
===================================================================
--- z3c.mountpoint/trunk/browser.py	                        (rev 0)
+++ z3c.mountpoint/trunk/browser.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""Browser code
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.app.publisher.browser import menu, menumeta
+from zope.security.checker import CheckerPublic
+
+from z3c.mountpoint import interfaces
+
+def ObjectMenuItem(context, request):
+    factory = menumeta.MenuItemFactory(
+        menu.BrowserMenuItem,
+        title=u'Object',
+        action=context.objectName + '/@@SelectedManagementView.html',
+        permission=CheckerPublic,
+        order=2,
+        _for=interfaces.IMountPoint)
+    return factory(context, request)


Property changes on: z3c.mountpoint/trunk/browser.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/browser.zcml
===================================================================
--- z3c.mountpoint/trunk/browser.zcml	                        (rev 0)
+++ z3c.mountpoint/trunk/browser.zcml	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,40 @@
+<configure
+    xmlns="http://namespaces.zope.org/browser"
+    xmlns:zope="http://namespaces.zope.org/zope">
+
+  <addform
+      name="addMounPoint.html"
+      schema=".interfaces.IMountPoint"
+      label="Add a Mount Point"
+      content_factory=".mountpoint.MountPoint"
+      fields="connectionName objectPath objectName"
+      set_before_add="connectionName objectPath objectName"
+      permission="zope.ManageContent"
+      />
+
+  <addMenuItem
+      class=".mountpoint.MountPoint"
+      title="Mount Point"
+      description="A Mount Point"
+      permission="zope.ManageContent"
+      view="addMounPoint.html"
+      />
+
+  <editform
+      name="edit.html"
+      schema=".interfaces.IMountPoint"
+      label="Edit Mount Point"
+      fields="connectionName objectPath objectName"
+      permission="zope.ManageContent"
+      menu="zmi_views" title="Edit"
+      />
+
+  <zope:adapter
+      factory=".browser.ObjectMenuItem"
+      for=".interfaces.IMountPoint
+           zope.publisher.interfaces.browser.IDefaultBrowserLayer"
+      provides="zope.app.menus.zmi_views"
+      name="Object"
+      />
+
+</configure>


Property changes on: z3c.mountpoint/trunk/browser.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.mountpoint/trunk/configure.zcml
===================================================================
--- z3c.mountpoint/trunk/configure.zcml	                        (rev 0)
+++ z3c.mountpoint/trunk/configure.zcml	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,39 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain='zope'
+    >
+
+  <utility
+      component=".connection.ZODBConnectionNamesVocabulary"
+      provides="zope.schema.interfaces.IVocabularyFactory"
+      name="ZODB Connection Names"
+      />
+
+  <class class=".mountpoint.MountPoint">
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable"
+        />
+    <require
+        permission="zope.View"
+        interface=".interfaces.IMountPoint"
+        />
+    <require
+        permission="zope.ManageContent"
+        set_schema=".interfaces.IMountPoint"
+        />
+  </class>
+
+  <adapter
+      factory=".mountpoint.MountPointTraverser"
+      />
+
+  <adapter
+      factory=".remoteproxy.RemoteLocationProxyTraverser"
+      />
+
+  <subscriber handler=".mountpoint.updateMountedObject" />
+
+  <include file="browser.zcml" />
+
+</configure>


Property changes on: z3c.mountpoint/trunk/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.mountpoint/trunk/connection.py
===================================================================
--- z3c.mountpoint/trunk/connection.py	                        (rev 0)
+++ z3c.mountpoint/trunk/connection.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,25 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""A simple multi-database mount-point implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.schema import vocabulary
+from zope.app.component import hooks
+
+def ZODBConnectionNamesVocabulary(context):
+    return vocabulary.SimpleVocabulary(
+        [vocabulary.SimpleTerm(name)
+         for name in hooks.getSite()._p_jar.db().databases])


Property changes on: z3c.mountpoint/trunk/connection.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/interfaces.py
===================================================================
--- z3c.mountpoint/trunk/interfaces.py	                        (rev 0)
+++ z3c.mountpoint/trunk/interfaces.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""A simple multi-database mount-point implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.schema
+
+
+class IMountPoint(zope.interface.Interface):
+    """A simple mount point."""
+
+    connectionName = zope.schema.Choice(
+        title=u'Conenction Name',
+        vocabulary='ZODB Connection Names',
+        required=True)
+
+    objectPath = zope.schema.TextLine(
+        title=u'Object Path',
+        default=u'/',
+        required=True)
+
+    objectName = zope.schema.TextLine(
+        title=u'Object Name',
+        description=u'The name under which the object will be known '
+                    u'when traversing from the mount point.',
+        default=u'object',
+        required=True)
+
+    object = zope.schema.Field(
+        title=u'Foreign object')
+
+    def update():
+        """Update the mounted object."""
+
+
+class IRemoteLocationProxy(zope.interface.Interface):
+    """Remote Location Proxy
+
+    When an object from a different object database is used, the directly
+    specified parent is not the correct one anymore. The parent should point
+    to the local mount point and subsequent objects in the path to the
+    location proxied parent.
+    """


Property changes on: z3c.mountpoint/trunk/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/mountpoint.py
===================================================================
--- z3c.mountpoint/trunk/mountpoint.py	                        (rev 0)
+++ z3c.mountpoint/trunk/mountpoint.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,82 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""A simple multi-database mount-point implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import persistent
+import zope.component
+import zope.interface
+import zope.lifecycleevent
+import zope.location
+from zope.app.container import contained
+from zope.app.publication import traversers
+from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserPublisher
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing import api
+
+from z3c.mountpoint import interfaces, remoteproxy
+
+class MountPoint(persistent.Persistent, contained.Contained):
+    """A simple mount point."""
+    zope.interface.implements(interfaces.IMountPoint)
+
+    def __init__(self, connectionName=None, objectPath=u'/', objectName=u'object'):
+        self.connectionName = connectionName
+        self.objectPath = objectPath
+        self.objectName = objectName
+        self.object = None
+
+    def update(self):
+        parent = self.__parent__
+        if parent is not None:
+            # Get the connection by name
+            conn = parent._p_jar.get_connection(self.connectionName)
+            obj = conn.root()['Application']
+            obj = api.traverse(obj, self.objectPath)
+            self.object = obj
+        else:
+            self.object = None
+
+    @apply
+    def __parent__():
+        def get(self):
+            return self and self.__dict__.get('__parent__', None)
+        def set(self, parent):
+            self.__dict__['__parent__'] = parent
+            self.update()
+        return property(get, set)
+
+
+class MountPointTraverser(traversers.SimpleComponentTraverser):
+
+    zope.component.adapts(interfaces.IMountPoint, IBrowserRequest)
+    zope.interface.implementsOnly(IBrowserPublisher)
+
+    def publishTraverse(self, request, name):
+        if name == self.context.objectName:
+            # Remove the security proxy, because we need a bare object to wrap
+            # the location proxy around.
+            context = removeSecurityProxy(self.context)
+            return remoteproxy.RemoteLocationProxy(
+                context.object, context, self.context.objectName)
+        return super(MountPointTraverser, self).publishTraverse(
+            request, name)
+
+
+ at zope.component.adapter(
+    interfaces.IMountPoint, zope.lifecycleevent.IObjectModifiedEvent)
+def updateMountedObject(mountPoint, event):
+    mountPoint.update()


Property changes on: z3c.mountpoint/trunk/mountpoint.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/remoteproxy.py
===================================================================
--- z3c.mountpoint/trunk/remoteproxy.py	                        (rev 0)
+++ z3c.mountpoint/trunk/remoteproxy.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,70 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""Remote object traverser.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+from zope.interface import declarations
+from zope.proxy import getProxiedObject, removeAllProxies
+from zope.proxy.decorator import DecoratorSpecificationDescriptor
+from zope.publisher.interfaces.browser import IBrowserPublisher, IBrowserRequest
+
+from z3c.mountpoint import interfaces
+
+class RemoteLocationProxyDecoratorSpecificationDescriptor(
+    DecoratorSpecificationDescriptor):
+
+    def __get__(self, inst, cls=None):
+        if inst is None:
+            return declarations.getObjectSpecification(cls)
+        else:
+            provided = zope.interface.providedBy(getProxiedObject(inst))
+
+            # Use type rather than __class__ because inst is a proxy and
+            # will return the proxied object's class.
+            cls = type(inst)
+            # Create a special version of Provides, which forces the remote
+            # location proxy to the front, so that a special traverser can be
+            # enforced.
+            return declarations.Provides(
+                cls, interfaces.IRemoteLocationProxy, provided)
+
+
+class RemoteLocationProxy(zope.location.LocationProxy):
+    """A location proxy for remote objects."""
+    __providedBy__ = RemoteLocationProxyDecoratorSpecificationDescriptor()
+
+
+class RemoteLocationProxyTraverser(object):
+    zope.component.adapts(interfaces.IRemoteLocationProxy, IBrowserRequest)
+    zope.interface.implements(IBrowserPublisher)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def browserDefault(self, request):
+        ob = self.context
+        view_name = zapi.getDefaultViewName(ob, request)
+        return ob, (view_name,)
+
+    def publishTraverse(self, request, name):
+        pureContext = removeAllProxies(self.context)
+        traverser = zope.component.getMultiAdapter(
+            (pureContext, self.request), IBrowserPublisher)
+        result = traverser.publishTraverse(request, name)
+        # Only remove the security proxy from the context.
+        return RemoteLocationProxy(result, getProxiedObject(self.context), name)


Property changes on: z3c.mountpoint/trunk/remoteproxy.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.mountpoint/trunk/tests.py
===================================================================
--- z3c.mountpoint/trunk/tests.py	                        (rev 0)
+++ z3c.mountpoint/trunk/tests.py	2007-10-02 03:02:33 UTC (rev 80457)
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation 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.
+#
+##############################################################################
+"""Browser code
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+import zope.component
+import zope.interface
+from zope.app.folder import folder
+from zope.app.folder.interfaces import IFolder
+from zope.app.publication import traversers
+from zope.app.testing import placelesssetup, setup
+from zope.publisher.interfaces.browser import IBrowserPublisher, IBrowserRequest
+from zope.security import checker
+from zope.testing import doctest
+from zope.traversing.testing import setUp as traversalSetUp
+
+from z3c.mountpoint import interfaces, mountpoint
+
+def setUp(test):
+    placelesssetup.setUp(test)
+    traversalSetUp()
+    # The test traverser is all we need.
+    zope.component.provideAdapter(
+        traversers.TestTraverser,
+        (zope.interface.Interface, IBrowserRequest), IBrowserPublisher)
+    # A simple interface checker for: MountPoint
+    checker.defineChecker(
+        mountpoint.MountPoint, checker.InterfaceChecker(interfaces.IMountPoint))
+    # A simple interface checker for: Folder
+    checker.defineChecker(
+        folder.Folder, checker.InterfaceChecker(IFolder))
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+                     setUp=setUp, tearDown=placelesssetup.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        ))


Property changes on: z3c.mountpoint/trunk/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id



More information about the Checkins mailing list