[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services/LocalEventService - ProtoServiceEventChannel.py:1.1 IPathSubscriber.py:1.3 LocalEventChannel.py:1.3 LocalEventService.py:1.6 LocalServiceSubscribable.py:1.6 LocalSubscribable.py:1.5 LocalSubscriptionAware.py:1.4 configure.zcml:1.9

Gary Poster gary@modernsongs.com
Mon, 21 Oct 2002 02:15:16 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services/LocalEventService
In directory cvs.zope.org:/tmp/cvs-serv13005/Zope/App/OFS/Services/LocalEventService

Modified Files:
	IPathSubscriber.py LocalEventChannel.py LocalEventService.py 
	LocalServiceSubscribable.py LocalSubscribable.py 
	LocalSubscriptionAware.py configure.zcml 
Added Files:
	ProtoServiceEventChannel.py 
Log Message:
sorry for the huge honking checkin.

Adds a simple local objecthub implementation and made ObjectHub a service

Modifies the main objecthub as we have been discussing:
 * objecthub attribute is hubid, not hid (sorry Jim, I'll change it back if you want but there were a lot of "yay"s and no "nay"s :-)
 * no more IObjectAddedHubEvent
 * IObjectRemovedEvent now (hopefully) actually has the effect on the ObjectHub that is described in the interface, and that we agreed upon, namely (assuming removed object was cataloged in objecthub) removing catalog of object in objecthub and sending out an IObjectRemovedHubEvent, subclass of IObjectUnregisteredHubEvent, to the ObjectHub subscribers

I tried to spruce up the LocalEventService a bit but the code still looks as opaque as ever, I'm afraid.  Among other small improvements, though, at least you actually can see the silly "user interface" now without a traceback.  Now for a *real* user interface sometime. :-)

Fixed a few typos while I was at it as well...and I'm sure made my share of new ones :-)




=== Added File Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/ProtoServiceEventChannel.py ===
##############################################################################
#
# 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.
# 
##############################################################################
"""An event channel that wants to subscribe to the nearest parent
event service when bound, and unsubscribe
when unbound, as one needs for an event channel that will be a service

$Id: ProtoServiceEventChannel.py,v 1.1 2002/10/21 06:14:46 poster Exp $
"""

from Zope.App.Traversing.ITraverser import ITraverser
from Zope.Event.GlobalEventService import eventService
from Zope.Proxy.ContextWrapper import ContextWrapper
from Zope.Proxy.ProxyIntrospection import removeAllProxies
from Zope.ComponentArchitecture import getAdapter, getService
from PathSubscriber import PathSubscriber
from LocalSubscriptionAware import LocalSubscriptionAware
from Zope.ContextWrapper import ContextMethod
from Zope.App.ComponentArchitecture.NextService import getNextService
from LocalEventChannel import LocalEventChannel
from LocalServiceSubscribable import LocalServiceSubscribable
from Interface.Attribute import Attribute
from Zope.Event.IEventChannel import IEventChannel
from Zope.App.OFS.Services.ServiceManager.IBindingAware import IBindingAware

class ProtoServiceEventChannel(
    LocalSubscriptionAware,
    LocalServiceSubscribable,
    LocalEventChannel):
    """An event channel that wants to subscribe to the nearest
    event service when bound, and unsubscribe when unbound, as one
    needs for an event channel that will be a service"""
    
    __implements__ = (
        LocalEventChannel.__implements__,
        LocalServiceSubscribable.__implements__,
        LocalSubscriptionAware.__implements__,
        IBindingAware
        )
    
    def __init__(self):
        LocalServiceSubscribable.__init__(self)
        LocalSubscriptionAware.__init__(self)
    
    subscribeOnBind = True
        # if true, event service will subscribe
        # to the parent event service on binding, unless the parent
        # service is the global event service; see 'bound' method
        # below
    
    _serviceName = None
        # the name of the service that this object is providing, or
        # None if unbound
    
    def bound(wrapped_self, name):
        "see IBindingAware"
        clean_self=removeAllProxies(wrapped_self)
        clean_self._serviceName = name # for LocalServiceSubscribable
        if clean_self.subscribeOnBind:
            es=getService(wrapped_self, "Events")
            if es is not eventService:
                # XXX if we really want to receive events from the
                # global event service we're going to have to
                # set something special up--something that subscribes
                # every startup...
                es.subscribe(PathSubscriber(wrapped_self))
    
    bound=ContextMethod(bound)
    
    def unbound(wrapped_self, name):
        "see IBindingAware"
        clean_self=removeAllProxies(wrapped_self)
        subscriber=PathSubscriber(wrapped_self)
        for subscription in clean_self._subscriptions:
            subscribable=getAdapter(
                wrapped_self, ITraverser).traverse(subscription[0])
            subscribable.unsubscribe(subscriber)
        clean_self._subscriptions = ()
        clean_self._serviceName = None

    unbound=ContextMethod(unbound)
    

=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/IPathSubscriber.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/IPathSubscriber.py:1.2	Mon Jun 10 19:28:10 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/IPathSubscriber.py	Mon Oct 21 02:14:45 2002
@@ -26,5 +26,5 @@
         """creates new PathSubscriber for the given wrapped_subscriber"""
     
     subscriber_path = Attribute(
-        """the slash-delineated absolute url to the subscriber"""
+        """the slash-delineated physical path to the subscriber"""
         )


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventChannel.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventChannel.py:1.2	Mon Jun 10 19:28:10 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventChannel.py	Mon Oct 21 02:14:45 2002
@@ -23,14 +23,25 @@
 from Zope.Proxy.ProxyIntrospection import removeAllProxies
 from Zope.Proxy.ContextWrapper import ContextWrapper
 
-class LocalEventChannel(LocalSubscribable):
+class LocalEventChannel:
+    # a mix-in: also needs LocalSubscribable to work.
+    # LocalSubscribable was in base class but produced
+    # TypeError: multiple bases have instance lay-out conflict
+    # when used in ProtoServiceEventChannel
     
-    __implements__ = IEventChannel
+    __implements__ = (
+        IEventChannel,
+        LocalSubscribable.__implements__)
+    
+    # uses (and needs) __init__ from Zope.Event.Subscribable (via
+    # LocalSubscribable)
         
     def notify(wrapped_self, event):
         clean_self=removeAllProxies(wrapped_self)
         
-        subscriptionses = clean_self._registry.getAllForObject(event)
+        subscriptionses = clean_self.subscriptionsForEvent(event)
+        # that's a non-interface shortcut for
+        # subscriptionses = clean_self._registry.getAllForObject(event)
 
         for subscriptions in subscriptionses:
             


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventService.py 1.5 => 1.6 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventService.py:1.5	Thu Aug  1 14:42:11 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalEventService.py	Mon Oct 21 02:14:46 2002
@@ -20,10 +20,7 @@
 from Zope.Event.GlobalEventService import eventService
 from Zope.Event.IEvent import IEvent
 from Zope.Event.IEventService import IEventService
-from Zope.Event.ISubscriber import ISubscriber
 from Zope.Event.ISubscriptionAware import ISubscriptionAware
-
-from Zope.App.OFS.Services.ServiceManager.IBindingAware import IBindingAware
 from Zope.App.Traversing.ITraverser import ITraverser
 from Zope.ComponentArchitecture import getAdapter, getService
 from Zope.App.ComponentArchitecture.NextService import getNextService
@@ -33,40 +30,25 @@
 from Zope.Proxy.ProxyIntrospection import removeAllProxies
 
 from PathSubscriber import PathSubscriber
-from LocalSubscriptionAware import LocalSubscriptionAware
-from LocalServiceSubscribable import LocalServiceSubscribable
-
-from Interface.Attribute import Attribute
+from ProtoServiceEventChannel import ProtoServiceEventChannel
 
-class ILocalEventService(
-    IEventService, ISubscriber, IBindingAware, ISubscriptionAware):
-    
-    def isPromotableEvent(event):
-        """a hook.  Returns a true value if, when publishing an
-        event, the event should also be promoted to the
-        next (higher) level of event service, and a false value
-        otherwise.  The default implementation is to always return
-        true."""
-    
-    subscribeOnBind=Attribute(
-        """if true, event service will subscribe
-        to the parent event service on binding, unless the parent
-        service is the global event service""")
-
-class LocalEventService(LocalServiceSubscribable, LocalSubscriptionAware):
+class LocalEventService(ProtoServiceEventChannel):
         
-    __implements__ = ILocalEventService
-    
-    _serviceName="Events" # used by LocalServiceSubscribable
+    __implements__ = (
+        IEventService,
+        ProtoServiceEventChannel.__implements__
+        )
     
-    subscribeOnBind=1
-    
-    def __init__(self):
-        LocalServiceSubscribable.__init__(self)
-        LocalSubscriptionAware.__init__(self)
+    # uses (and needs) __init__ from base class
     
     def isPromotableEvent(self, event):
-        "see ILocalEventService"
+        """a hook.  Returns a true value if, when publishing an
+        event, the event should also be promoted to the
+        next (higher) level of event service, and a false value
+        otherwise."""
+        # XXX A probably temporary appendage.  Depending on the usage,
+        # this should be (a) kept as is, (b) made into a registry, or
+        # (c) removed.
         return 1
     
     def publishEvent(wrapped_self, event):
@@ -108,14 +90,14 @@
     
     notify=ContextMethod(notify)
     
-    
     def bound(wrapped_self, name):
         "see IBindingAware"
         clean_self=removeAllProxies(wrapped_self)
+        clean_self._serviceName = name # for LocalServiceSubscribable
         if clean_self.subscribeOnBind:
-            es=getNextService(wrapped_self, name)
-            if es is not eventService: # if we really want the top-level
-                # placeful event service to receive events from the
+            es=getNextService(wrapped_self, "Events")
+            if es is not eventService:
+                # XXX if we really want to receive events from the
                 # global event service we're going to have to
                 # set something special up--something that subscribes
                 # every startup...
@@ -123,18 +105,33 @@
     
     bound=ContextMethod(bound)
     
+    # _unbound = ProtoServiceEventChannel.unbound # see comment below
+    
     def unbound(wrapped_self, name):
         "see IBindingAware"
         clean_self=removeAllProxies(wrapped_self)
-        subscriber=PathSubscriber(wrapped_self)
         clean_self._v_unbinding=1
+        # this flag is used by the unsubscribedFrom method (below) to
+        # determine that it doesn't need to further unsubscribe beyond
+        # what we're already doing.
+        # wrapped_self._unbound(name) # or, instead, ...
+        # ... this seems fine also, even though it's a ContextMethod:
+        # ProtoServiceEventChannel.unbound(wrapped_self, name)
+        # but in actuality we're doing a copy and paste because of
+        # various wrapper/security problems:
+        # start copy/paste
+        subscriber=PathSubscriber(wrapped_self)
         for subscription in clean_self._subscriptions:
             subscribable=getAdapter(
                 wrapped_self, ITraverser).traverse(subscription[0])
             subscribable.unsubscribe(subscriber)
-        clean_self._subscriptions=()
+        clean_self._subscriptions = ()
+        clean_self._serviceName = None
+        # end copy/paste
+        
         for subscriber in clean_self._subscribers:
             clean_self.__unsubscribeAllFromSelf(wrapped_self, subscriber[0])
+        # unset flag
         clean_self._v_unbinding=None
 
     unbound=ContextMethod(unbound)
@@ -152,6 +149,7 @@
                 break
         else:
             raise NotFoundError(subscriber)
+        clean_self._p_changed = 1 # trigger persistence before change
         do_alert=ISubscriptionAware.isImplementedBy(subscriber)
         for ev_type in ev_set:
             subscriptions = clean_self._registry.get(ev_type)
@@ -166,7 +164,6 @@
                 else: # kept (added back)
                     subscriptions.append(sub)
         del clean_self._subscribers[subscriber_index]
-        clean_self._registry=clean_self._registry #trigger persistence
     
     def unsubscribedFrom(wrapped_self, subscribable, event_type, filter):
         "see ISubscriptionAware"
@@ -177,7 +174,7 @@
             # itself: we need to remove the higher level event service
             # from our subscriptions list and try to find another event
             # service to which to attach
-            LocalSubscriptionAware.unsubscribedFrom(
+            ProtoServiceEventChannel.unsubscribedFrom(
                 clean_self, subscribable, event_type, filter)
             clean_subscribable=removeAllProxies(subscribable)
             if IEventService.isImplementedBy(removeAllProxies(clean_subscribable)):
@@ -196,5 +193,4 @@
                 context.subscribe(PathSubscriber(wrapped_self))
     
     unsubscribedFrom=ContextMethod(unsubscribedFrom)
-            
-        
+


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalServiceSubscribable.py 1.5 => 1.6 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalServiceSubscribable.py:1.5	Thu Sep  5 22:14:31 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalServiceSubscribable.py	Mon Oct 21 02:14:46 2002
@@ -24,13 +24,18 @@
 from Zope.Proxy.ProxyIntrospection import removeAllProxies
 from Zope.Proxy.ContextWrapper import ContextWrapper
 from LocalSubscribable import LocalSubscribable
-from Persistence import Persistent
 from Zope.App.ComponentArchitecture.NextService import getNextService
 
-class LocalServiceSubscribable(LocalSubscribable, Persistent):
+class LocalServiceSubscribable(LocalSubscribable):
     """a local mix-in for services"""
     
-    _serviceName = None # replace me
+    __implements__ = LocalSubscribable.__implements__
+    
+    _serviceName = None # should be replaced; usually done in "bound"
+    # method of a subclass
+    
+    # uses (and needs) __init__ from Zope.Event.Subscribable (via
+    # LocalSubscribable)
     
     def unsubscribe(wrapped_self,
                     subscriber,
@@ -58,9 +63,13 @@
         do_alert = ISubscriptionAware.isImplementedBy(subscriber)
         
         if event_type:
+            # we only have to clean the one event_type out
             ev_type = event_type
             if event_type is IEvent:
-                ev_type = None # handle optimization
+                ev_type = None # *** handle optimization: a subscription
+                # to IEvent is a subscription to all events; this is
+                # converted to 'None' so that the _registry can
+                # shortcut some of its tests
             if ev_type not in ev_set:
                 getNextService(
                     wrapped_self, clean_self._serviceName).unsubscribe(
@@ -81,6 +90,7 @@
                     else:
                         del clean_self._subscribers[subscriber_index]
         else:
+            # we have to clean all the event types out (ignoring filter)
             for ev_type in ev_set:
                 subscriptions = clean_self._registry.get(ev_type)
                 subs = subscriptions[:]
@@ -91,7 +101,8 @@
                             wrapped_subscriber.unsubscribedFrom(
                                 wrapped_self, ev_type or IEvent, sub[1])
                             # IEvent switch is to make optimization
-                            # transparent
+                            # transparent (see *** comment above in
+                            # this method)
                     else: # kept (added back)
                         subscriptions.append(sub)
             del clean_self._subscribers[subscriber_index]


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscribable.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscribable.py:1.4	Thu Sep  5 22:14:31 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscribable.py	Mon Oct 21 02:14:46 2002
@@ -28,6 +28,12 @@
 
 class LocalSubscribable(Subscribable, Persistent):
     """a local mix-in"""
+    
+    __implements__ = (
+        Subscribable.__implements__,
+        Persistent.__implements__)
+    
+    # uses (and needs) __init__ from Zope.Event.Subscribable
 
     def subscribe(wrapped_self,
                   subscriber,
@@ -63,6 +69,8 @@
             subs.append((subscriber,{ev_type:1}))
         
         clean_self._p_changed = 1 #trigger persistence
+        # XXX should this and similar be done earlier in the method?
+        # XXX Ask Shane
         
     
     subscribe=ContextMethod(subscribe)
@@ -126,5 +134,6 @@
                         subscriptions.append(sub)
             del clean_self._subscribers[subscriber_index]
         clean_self._p_changed = 1
+        # XXX should be done earlier?  Ask Shane
     
     unsubscribe = ContextMethod(unsubscribe)


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscriptionAware.py 1.3 => 1.4 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscriptionAware.py:1.3	Thu Jul 11 14:21:31 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/LocalSubscriptionAware.py	Mon Oct 21 02:14:46 2002
@@ -11,19 +11,18 @@
 # FOR A PARTICULAR PURPOSE.
 # 
 ##############################################################################
-"""
+"""mix-in for subscribers that want to know to whom they are subscribed
 
 Revision information:
 $Id$
 """
 
 from Zope.Event.ISubscriptionAware import ISubscriptionAware
-from Zope.Proxy.ProxyIntrospection import removeAllProxies
 from Zope.App.Traversing import getPhysicalPathString
 
 
 class LocalSubscriptionAware:
-    # a mix-in
+    "mix-in for subscribers that want to know to whom they are subscribed"
     
     __implements__=ISubscriptionAware
     
@@ -31,11 +30,15 @@
         self._subscriptions=()
     
     def subscribedTo(self, subscribable, event_type, filter):
+        # This breaks for subscriptions to global event service.
+        # Unless the global event service becomes persistent, this
+        # is probably correct behavior.
         subscribable_path = getPhysicalPathString(subscribable)
         if (subscribable_path, event_type, filter) not in self._subscriptions:
             self._subscriptions+=((subscribable_path,event_type, filter),)
     
     def unsubscribedFrom(self, subscribable, event_type, filter):
+        # global event service breaks, as above
         subscribable_path = getPhysicalPathString(subscribable)
         sub=list(self._subscriptions)
         sub.remove((subscribable_path, event_type, filter))


=== Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/configure.zcml 1.8 => 1.9 ===
--- Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/configure.zcml:1.8	Thu Jul 11 14:21:31 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/LocalEventService/configure.zcml	Mon Oct 21 02:14:46 2002
@@ -19,13 +19,6 @@
                     unsubscribedFrom subscribedTo" />
   </content>
 
-  <browser:menuItem menu="add_component" for="Zope.App.OFS.Container.IAdding."
-     action="Events"  title='Events'
-     description='An event service: use sparingly' />
-  
-  <browser:icon name="zmi_icon" for=".LocalEventService.ILocalEventService" 
-                file="./event_service.gif" />
-
   <include package=".Views" />
 
 </zopeConfigure>