[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)