[Zope3-checkins] CVS: Zope3/src/zope/app/index - __init__.py:1.2 configure.zcml:1.2 processors.py:1.2 queries.py:1.2 subscribers.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:13:55 -0500
Update of /cvs-repository/Zope3/src/zope/app/index
In directory cvs.zope.org:/tmp/cvs-serv15352/src/zope/app/index
Added Files:
__init__.py configure.zcml processors.py queries.py
subscribers.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/app/index/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:54 2002
+++ Zope3/src/zope/app/index/__init__.py Wed Dec 25 09:12:54 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
=== Zope3/src/zope/app/index/configure.zcml 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:54 2002
+++ Zope3/src/zope/app/index/configure.zcml Wed Dec 25 09:12:54 2002
@@ -0,0 +1,16 @@
+<zopeConfigure xmlns="http://namespaces.zope.org/zope">
+
+ <content class="zope.app.index.subscribers.Registration">
+ <require
+ permission="zope.ManageServices"
+ interface="zope.app.index.subscribers.ISubscriptionControl"
+ />
+ <factory
+ id="zope.app.index.subscribers.Registration"
+ permission="zope.ManageServices"
+ />
+ </content>
+
+ <include package=".text" />
+
+</zopeConfigure>
=== Zope3/src/zope/app/index/processors.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:55 2002
+++ Zope3/src/zope/app/index/processors.py Wed Dec 25 09:12:54 2002
@@ -0,0 +1,85 @@
+##############################################################################
+#
+# Copyright (c) 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.
+#
+##############################################################################
+"""Generic query processors for use with multiple indexes..
+
+$Id$
+"""
+
+from __future__ import generators
+
+from zope.app.interfaces.index.interfaces import IRankedObjectIterator, IRankedObjectRecord, \
+ IRankedHubIdList, IBatchedResult
+from zope.app.interfaces.services.query import IQueryProcessor
+
+from zope.component import getAdapter, getService
+from zope.proxy.context import ContextMethod
+
+
+
+class ObjectRetrievingProcessor:
+ """Converts a RankedHubIdList into an iteratable
+ list of ranked objects by retrieving the objects
+ from the ObjectHub.
+ """
+
+ __implements__ = IQueryProcessor
+
+ input_interface = IRankedHubIdList, IBatchedResult
+ output_interface = IRankedObjectIterator
+
+ def __call__(wrapped_self, query):
+ list = getAdapter(query, IRankedHubIdList)
+ batch = getAdapter(query, IBatchedResult)
+
+ objectHub = getService(wrapped_self, "ObjectHub")
+
+ # XXX do we need wrapping for the objects returned by the hub?
+ iterator = RankedObjectIterator(list, objectHub.getObject, batch.startPosition,
+ batch.batchSize, batch.totalSize)
+
+ return iterator
+ __call__ = ContextMethod(__call__)
+
+class RankedObjectIterator:
+ """Iterates over a given list of IRankedObjectRecord."""
+
+ __implements__ = IRankedObjectIterator, IBatchedResult
+
+ def __init__(self, recordlist, objectfetcher, startposition, batchsize, totalsize):
+ self._records = recordlist
+ self.startPosition = startposition
+ self.batchSize = batchsize
+ self.totalSize = totalsize
+ self.__objectfetcher = objectfetcher
+
+ def __iter__(self):
+ objectfetcher = self.__objectfetcher
+
+ for hubid, rank in self._records:
+ # XXX maybe we should catch some exceptions like security related
+ # ones or NotFoundError, to avoid breaking the iteration. Think
+ # about yielding an NotFound-Indicator in such a case.
+ yield RankedObjectRecord(objectfetcher(hubid), rank)
+ raise StopIteration
+
+class RankedObjectRecord:
+ """Contains a reference to a ranked object."""
+
+ __slots__ = ["rank", "object"]
+
+ __implements__ = IRankedObjectRecord
+
+ def __init__(self, object, rank):
+ self.rank = rank
+ self.object = object
=== Zope3/src/zope/app/index/queries.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:55 2002
+++ Zope3/src/zope/app/index/queries.py Wed Dec 25 09:12:54 2002
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# Copyright (c) 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.
+#
+##############################################################################
+"""Generic queries for indexes.
+
+$Id$
+"""
+
+from zope.component import getAdapter
+from zope.app.interfaces.index.interfaces import IBatchedResult, IRankedHubIdList
+
+class BatchedRankedResult:
+
+ __implements__ = IBatchedResult, IRankedHubIdList
+
+ def __init__(self, hubidlist, startposition, batchsize, totalsize):
+ self.__hubidlist = hubidlist
+ self.startPosition = startposition
+ self.batchSize = batchsize
+ self.totalSize = totalsize
+
+ def __getitem__(self, index):
+ return self.__hubidlist[index]
=== Zope3/src/zope/app/index/subscribers.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:55 2002
+++ Zope3/src/zope/app/index/subscribers.py Wed Dec 25 09:12:54 2002
@@ -0,0 +1,152 @@
+##############################################################################
+#
+# Copyright (c) 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.
+#
+##############################################################################
+"""A stupid registration thingie.
+
+In order to do indexing, objects need to be registered with the object
+hub. A real site should define a policy for when objects are to be
+registered. This particular implementation subscribes to
+IObjectAddedEvent events from the event service, and registers
+absolutely everything. It also has a way of registering all
+pre-existing objects.
+
+XXX This is really just an example! There are no unit tests, and it
+hardcodes all the policy decisions. Also, it has some "viewish"
+properties. The traversal code in registerExisting could be useful
+for creating a general "Find" facility like the Zope2 Find tab.
+
+$Id$
+"""
+__metaclass__ = type
+
+from zope.interface import Interface
+from persistence import Persistent
+
+from zope.interfaces.event import ISubscriber
+from zope.app.interfaces.event import IObjectAddedEvent
+from zope.app.interfaces.content.folder import IFolder
+from zope.proxy.context import ContextMethod
+from zope.component import getService, queryAdapter
+
+from zope.app.traversing import traverse, traverseName, \
+ getPhysicalPath, getPhysicalRoot
+from zope.app.interfaces.services.hub import ObjectHubError
+
+class ISubscriptionControl(Interface):
+ def subscribe():
+ """Subscribe to the prevailing object hub service."""
+
+ def unsubscribe():
+ """Unsubscribe from the object hub service."""
+
+ def isSubscribed():
+ """Return whether we are currently subscribed."""
+
+ def registerExisting():
+ """Register all existing objects (for some definition of all)."""
+
+class Registration(Persistent):
+
+ __implements__ = ISubscriptionControl, ISubscriber
+
+ def notify(wrapped_self, event):
+ """An event occured. Perhaps register this object with the hub."""
+ hub = getService(wrapped_self, "ObjectHub")
+ wrapped_self._registerObject(event.object, hub)
+ notify = ContextMethod(notify)
+
+ currentlySubscribed = False # Default subscription state
+
+ def subscribe(wrapped_self):
+ if wrapped_self.currentlySubscribed:
+ raise RuntimeError, "already subscribed; please unsubscribe first"
+ channel = wrapped_self._getChannel(None)
+ channel.subscribe(wrapped_self, IObjectAddedEvent)
+ wrapped_self.currentlySubscribed = True
+ subscribe = ContextMethod(subscribe)
+
+ def unsubscribe(wrapped_self):
+ if not wrapped_self.currentlySubscribed:
+ raise RuntimeError, "not subscribed; please subscribe first"
+ channel = wrapped_self._getChannel(None)
+ channel.unsubscribe(wrapped_self, IObjectAddedEvent)
+ wrapped_self.currentlySubscribed = False
+ unsubscribe = ContextMethod(unsubscribe)
+
+ def isSubscribed(self):
+ return self.currentlySubscribed
+
+ def registerExisting(wrapped_self):
+ object = findContentObject(wrapped_self)
+ hub = getService(wrapped_self, "ObjectHub")
+ wrapped_self._registerTree(object, hub)
+ registerExisting = ContextMethod(registerExisting)
+
+ def _registerTree(wrapped_self, object, hub):
+ wrapped_self._registerObject(object, hub)
+ # XXX Policy decision: only traverse into folders
+ if not IFolder.isImplementedBy(object):
+ return
+ # Register subobjects
+ names = object.keys()
+ for name in names:
+ # XXX Once traverseName is refactored, should get an
+ # ITraversable from object and pass it to traverseName
+ sub_object = traverseName(object, name)
+ wrapped_self._registerTree(sub_object, hub)
+ _registerTree = ContextMethod(_registerTree)
+
+ def _registerObject(wrapped_self, object, hub):
+ # XXX Policy decision: register absolutely everything
+ try:
+ hub.register(object)
+ except ObjectHubError:
+ # Already registered
+ pass
+ _registerObject = ContextMethod(_registerObject)
+
+ def _getChannel(wrapped_self, channel):
+ if channel is None:
+ channel = getService(wrapped_self, "ObjectHub")
+ return channel
+ _getChannel = ContextMethod(_getChannel)
+
+def findContentObject(context):
+ # We want to find the (content) Folder in whose service manager we
+ # live. There are man y way to do this. Perhaps the simplest is
+ # looking for '++etc++Services' in the location. We could also
+ # walk up the path looking for something that implements IFolder;
+ # the service manager and packages don't implement this. Or
+ # (perhaps better, because a service manager might be attached to
+ # a non-folder container) assume we're in service space, and walk
+ # up until we find a service manager, and then go up one more
+ # step. Walking up the path could be done by stripping components
+ # from the end of the path one at a time and doing a lookup each
+ # time, or more directly by traversing the context. Traversing
+ # the context can be done by getting the context and following the
+ # chain back; there's a convenience class, ContainmentIterator to
+ # do that. Use the version of ContainmentIterator from
+ # zope.proxy, which is aware of the complications caused by
+ # security proxies.
+
+ # For now, we pick the first approach.
+ location = getPhysicalPath(context)
+ # Location is a tuple of strings, starting with '' (for the root)
+ for i in range(len(location)):
+ if location[i] == "++etc++Services":
+ location = location[:i]
+ break
+ else:
+ raise ValueError, "can't find '++etc++Services' in path"
+ root = getPhysicalRoot(context)
+ return traverse(root, location)