[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