[Zope3-checkins] CVS: Zope3/src/zope/app/event - globalservice.py:1.1 subs.py:1.1 __init__.py:1.3 configure.zcml:1.3 meta.zcml:1.3 globaleventservice.py:NONE logger.py:NONE metaconfigure.py:NONE
Steve Alexander
steve@cat-box.net
Mon, 30 Dec 2002 09:03:36 -0500
Update of /cvs-repository/Zope3/src/zope/app/event
In directory cvs.zope.org:/tmp/cvs-serv23914/src/zope/app/event
Modified Files:
__init__.py configure.zcml meta.zcml
Added Files:
globalservice.py subs.py
Removed Files:
globaleventservice.py logger.py metaconfigure.py
Log Message:
Large refactoring of the event service.
=== Added File Zope3/src/zope/app/event/globalservice.py ===
##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
"""
Revision information:
$Id: globalservice.py,v 1.1 2002/12/30 14:03:02 stevea Exp $
"""
__metaclass__ = type
from zope.interface.type import TypeRegistry
from zope.exceptions import NotFoundError
from zope.proxy.introspection import removeAllProxies
from zope.app.interfaces.event import IEvent, ISubscriber, ISubscribingAware
from zope.app.interfaces.event import IGlobalSubscribable, IPublisher
from zope.configuration.action import Action
import logging
import pprint
from StringIO import StringIO
def checkEventType(event_type, allow_none=False):
if not (
(allow_none and event_type is None)
or event_type.extends(IEvent, strict=False)
):
raise TypeError('event_type must extend IEvent: %s' % repr(event_type))
directive_counter = 0
def subscribeDirective(_context, subscriber,
event_types=(IEvent,), filter=None):
global directive_counter
directive_counter += 1
subscriber = _context.resolve(subscriber)
event_type_names = event_types
event_types=[]
for event_type_name in event_type_names.split():
event_types.append(_context.resolve(event_type_name))
if filter is not None:
filter = _context.resolve(filter)
return [
Action(
# subscriptions can never conflict
discriminator = ('subscribe', directive_counter),
callable = globalSubscribeMany,
args = (subscriber, event_types, filter)
)
]
class Logger:
"""Class to log all events sent out by an event service.
This is an event subscriber that you can add via ZCML to log all
events sent out by Zope.
"""
__implements__ = ISubscriber
def __init__(self, severity=logging.INFO):
self.severity = severity
self.logger = logging.getLogger("Event.Logger")
def notify(self, event):
c = event.__class__
detail = StringIO()
if 0:
# XXX Apparently this doesn't work; why not?
data = event.__dict__.items()
data.sort()
pprint(data, detail)
else:
print >>detail, 'XXX detail temporarily disabled'
self.logger.log(self.severity, "%s.%s: %s",
c.__module__, c.__name__, detail.getvalue())
class GlobalSubscribable:
"""A global mix-in"""
__implements__ = IGlobalSubscribable
def __init__(self):
self._registry = TypeRegistry()
self._subscribers = [] # use dict?
_clear = __init__
def globalSubscribe(self, subscriber, event_type=IEvent, filter=None):
checkEventType(event_type)
clean_subscriber = removeAllProxies(subscriber)
if ISubscribingAware.isImplementedBy(subscriber):
subscriber.subscribedTo(self, event_type, filter)
if event_type is IEvent:
event_type = None # optimization
subscribers = self._registry.setdefault(event_type, [])
subscribers.append((clean_subscriber, filter))
for sub in self._subscribers:
if sub[0] == clean_subscriber:
try:
sub[1][event_type] += 1
except KeyError:
sub[1][event_type] = 1
break
else:
self._subscribers.append((clean_subscriber, {event_type: 1}))
# Trigger persistence, if pertinent
# self._registry = self._registry
def unsubscribe(self, subscriber, event_type=None, filter=None):
checkEventType(event_type, allow_none=True)
clean_subscriber = removeAllProxies(subscriber)
for subscriber_index in range(len(self._subscribers)):
sub = self._subscribers[subscriber_index]
if sub[0] == clean_subscriber:
ev_set = sub[1] # the dict of type:subscriptionCount
break
else:
if event_type is not None:
raise NotFoundError(subscriber)
else:
# this was a generic unsubscribe all request; work may have
# been done by a local service
return
do_alert = ISubscribingAware.isImplementedBy(clean_subscriber)
if event_type:
ev_type = event_type
if event_type is IEvent:
ev_type = None # handle optimization
if ev_type not in ev_set:
raise NotFoundError(subscriber, event_type, filter)
subscriptions = self._registry.get(ev_type)
if not subscriptions:
raise NotFoundError(subscriber, event_type, filter)
try:
subscriptions.remove((clean_subscriber, filter))
except ValueError:
raise NotFoundError(subscriber, event_type, filter)
if do_alert:
subscriber.unsubscribedFrom(self, event_type, filter)
ev_set[ev_type] -= 1
if ev_set[ev_type] < 1:
for sub in subscriptions:
if sub[0] == clean_subscriber:
break
else:
if len(ev_set) > 1:
del ev_set[ev_type]
else: # len(ev_set) == 1, and we just eliminated it
del self._subscribers[subscriber_index]
else:
for ev_type in ev_set:
subscriptions = self._registry.get(ev_type)
if ev_type is None:
ev_type = IEvent
subs = subscriptions[:]
subscriptions[:] = []
for sub in subs:
if sub[0] == clean_subscriber: # deleted (not added back)
if do_alert:
subscriber.unsubscribedFrom(self, ev_type, sub[1])
else: # kept (added back)
subscriptions.append(sub)
del self._subscribers[subscriber_index]
# Trigger persistence, if pertinent
# self._registry = self._registry
def listSubscriptions(self, subscriber, event_type=None):
checkEventType(event_type, allow_none=True)
subscriber = removeAllProxies(subscriber)
result = []
if event_type:
ev_type = event_type
if event_type is IEvent:
ev_type = None # handle optimization
subscriptions = self._registry.get(ev_type)
if subscriptions:
for sub in subscriptions:
if sub[0] == subscriber:
result.append((event_type, sub[1]))
else:
for subscriber_index in range(len(self._subscribers)):
sub = self._subscribers[subscriber_index]
if sub[0] == subscriber:
ev_set = sub[1]
break
else:
return result
for ev_type in ev_set:
subscriptions = self._registry.get(ev_type)
if subscriptions:
if ev_type is None:
ev_type = IEvent
for sub in subscriptions:
if sub[0] == subscriber:
result.append((ev_type, sub[1]))
return result
class GlobalEventChannel(GlobalSubscribable):
__implements__ = IGlobalSubscribable, ISubscriber
def notify(self, event):
subscriptionsForEvent = self._registry.getAllForObject(event)
for subscriptions in subscriptionsForEvent:
for subscriber, filter in subscriptions:
if filter is not None and not filter(event):
continue
subscriber.notify(event)
class GlobalEventPublisher(GlobalSubscribable):
__implements__ = IGlobalSubscribable, IPublisher
def publish(self, event):
assert IEvent.isImplementedBy(event)
subscriptionsForEvent = self._registry.getAllForObject(event)
for subscriptions in subscriptionsForEvent:
for subscriber, filter in subscriptions:
if filter is not None and not filter(event):
continue
subscriber.notify(event)
# Repeated here, and in zope/app/event/__init__.py to avoid circular import.
def globalSubscribeMany(subscriber, event_types=(IEvent,), filter=None):
subscribe_func = eventPublisher.globalSubscribe
for event_type in event_types:
subscribe_func(subscriber, event_type, filter)
eventPublisher = GlobalEventPublisher()
_clear = eventPublisher._clear
# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
from zope.testing.cleanup import addCleanUp
addCleanUp(_clear)
del addCleanUp
=== Added File Zope3/src/zope/app/event/subs.py ===
##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
"""
Revision information:
$Id: subs.py,v 1.1 2002/12/30 14:03:02 stevea Exp $
"""
from __future__ import generators
from zope.exceptions import NotFoundError
from persistence import Persistent
from types import StringTypes
from zope.proxy.context import ContextMethod
from zope.proxy.introspection import removeAllProxies
from zope.app.traversing import getPhysicalPathString
from zope.app.traversing import locationAsUnicode, getPhysicalPath, traverse
from zope.app.interfaces.event import IEvent, ISubscriber, ISubscribable
from zope.app.interfaces.event import ISubscribingAware
from zope.component import getService, getAdapter
from zope.interface.type import TypeRegistry
__metaclass__ = type
try:
enumerate # python 2.3
except NameError:
def enumerate(collection):
'Generates an indexed series: (0,coll[0]), (1,coll[1]) ...'
i = 0
it = iter(collection)
while 1:
yield (i, it.next())
i += 1
class Subscribable(Persistent):
"""A local mix-in"""
__implements__ = ISubscribable
def __init__(self):
self._registry = TypeRegistry()
# using a list rather than a dict so that subscribers may define
# custom __eq__ methods
self._subscribers = []
def subscribe(wrapped_self, subscriber, event_type=IEvent, filter=None):
'''See ISubscribable'''
clean_subObj = removeAllProxies(subscriber)
if isinstance(clean_subObj, int):
hub = getService(wrapped_self, "HubIds")
subObj = hub.getObject(subscriber)
clean_subObj = removeAllProxies(subObj)
elif isinstance(clean_subObj, StringTypes):
subObj = traverse(wrapped_self, subscriber)
clean_subObj = removeAllProxies(subObj)
subscriber = locationAsUnicode(subscriber)
else:
subObj = subscriber
hub = getService(wrapped_self, "HubIds")
try:
subscriber = hub.getHubId(subObj)
except NotFoundError:
# XXX this should be superfluous. getPhysicalPathString should
# always return a canonical path.
subscriber = locationAsUnicode(
getPhysicalPath(subscriber))
if ISubscribingAware.isImplementedBy(clean_subObj):
subObj.subscribedTo(wrapped_self, event_type, filter)
ev_type = event_type
if ev_type is IEvent:
ev_type = None # optimization
clean_self = removeAllProxies(wrapped_self)
clean_self._p_changed = 1
subscribers = clean_self._registry.get(ev_type)
if subscribers is None:
subscribers = []
clean_self._registry.register(ev_type, subscribers)
subscribers.append((subscriber, filter))
subs = clean_self._subscribers
for sub in subs:
if sub[0] == subscriber:
try:
sub[1][ev_type] += 1
except KeyError:
sub[1][ev_type] = 1
break
else:
subs.append((subscriber,{ev_type:1}))
return subscriber
subscribe = ContextMethod(subscribe)
def _getSubscribers(clean_self, wrapped_self, subscriber):
subscribers = []
# XXX This comment needs explanation:
# shortcut; useful for notify
if wrapped_self is clean_self:
return [subscriber], None, None
clean_subObj = removeAllProxies(subscriber)
if isinstance(clean_subObj, int):
hub = getService(wrapped_self, "HubIds")
try:
subObj = hub.getObject(subscriber)
except NotFoundError:
subObj = None
else:
clean_subObj = removeAllProxies(subObj)
subscribers.append(subscriber)
elif isinstance(clean_subObj, StringTypes):
try:
subObj = traverse(wrapped_self, subscriber)
except NotFoundError:
subObj = None
else:
clean_subObj = removeAllProxies(subObj)
subscribers.append(locationAsUnicode(subscriber))
else:
subObj = subscriber
hub = getService(wrapped_self, "HubIds")
try:
subscribers.append(hub.getHubId(subObj))
except NotFoundError:
pass
subscribers.append(locationAsUnicode(
getPhysicalPath(subscriber)))
return subscribers, clean_subObj, subObj
def _getEventSets(self, subscribers):
ev_sets = {}
for self_ix, sub in enumerate(self._subscribers):
for arg_ix, subscriber in enumerate(subscribers):
if sub[0] == subscriber:
ev_sets[(subscriber, self_ix)] = sub[1]
del subscribers[arg_ix]
break
if not subscribers:
break
else:
if len(ev_sets.keys()) == 0:
raise NotFoundError(subscribers)
return ev_sets
def _cleanAllForSubscriber(clean_self,
wrapped_self,
ev_sets,
do_alert,
subObj):
for (subscriber, subscriber_index), ev_set in ev_sets.items():
for ev_type in ev_set:
subscriptions = clean_self._registry.get(ev_type)
if ev_type is None:
ev_type = IEvent
subs = subscriptions[:]
subscriptions[:] = []
for sub in subs:
if sub[0] == subscriber: # deleted (not added back)
if do_alert:
subObj.unsubscribedFrom(
wrapped_self, ev_type, sub[1]
)
else: # kept (added back)
subscriptions.append(sub)
del clean_self._subscribers[subscriber_index]
def unsubscribe(wrapped_self, subscriber, event_type=None, filter=None):
'''See ISubscribable'''
clean_self = removeAllProxies(wrapped_self)
subscribers, clean_subObj, subObj = clean_self._getSubscribers(
wrapped_self, subscriber)
ev_sets = clean_self._getEventSets(subscribers)
do_alert = (subObj is not None and
ISubscribingAware.isImplementedBy(clean_subObj)
)
clean_self._p_changed = 1
if event_type:
# we have to clean out one and only one subscription of this
# subscriber for event_type, filter (there may be more,
# even for this exact combination of subscriber,
# event_type, filter; we only do *one*)
ev_type = event_type
if event_type is IEvent:
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
for (subscriber, subscriber_index), ev_set in ev_sets.items():
if ev_type in ev_set:
subscriptions = clean_self._registry.get(ev_type)
if subscriptions:
try:
subscriptions.remove((subscriber, filter))
except ValueError:
pass
else:
if do_alert:
subObj.unsubscribedFrom(
wrapped_self, event_type, filter)
ev_set[ev_type] -= 1
if ev_set[ev_type] < 1:
for sub in subscriptions:
if sub[0] == subscriber:
break
else:
if len(ev_set) > 1:
del ev_set[ev_type]
else: # len(ev_set) == 1
del clean_self._subscribers[
subscriber_index]
break
else:
raise NotFoundError(subscriber, event_type, filter)
else:
# we have to clean all the event types out (ignoring filter)
clean_self._cleanAllForSubscriber(wrapped_self,
ev_sets,
do_alert,
subObj)
unsubscribe = ContextMethod(unsubscribe)
def listSubscriptions(wrapped_self, subscriber, event_type=None):
'''See ISubscribable'''
clean_self = removeAllProxies(wrapped_self)
subscribers, clean_subObj, subObj = clean_self._getSubscribers(
wrapped_self, subscriber)
result=[]
if event_type:
ev_type=event_type
if event_type is IEvent:
ev_type=None # handle optimization
subscriptions = self._registry.get(ev_type)
if subscriptions:
for sub in subscriptions:
for subscriber in subscribers:
if sub[0]==subscriber:
result.append((event_type, sub[1]))
else:
try:
ev_sets = clean_self._getEventSets(subscribers)
except NotFoundError:
return result
for (subscriber, subscriber_index), ev_set in ev_sets.items():
for ev_type in ev_set:
subscriptions = self._registry.get(ev_type)
if subscriptions:
if ev_type is None:
ev_type = IEvent
for sub in subscriptions:
if sub[0]==subscriber:
result.append((ev_type, sub[1]))
return result
listSubscriptions = ContextMethod(listSubscriptions)
class SubscriptionTracker:
"Mix-in for subscribers that want to know to whom they are subscribed"
__implements__ = ISubscribingAware
def __init__(self):
self._subscriptions = ()
def subscribedTo(self, subscribable, event_type, filter):
# XXX insert super() call here
# This raises an error for subscriptions to global event service.
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):
# XXX insert super() call here
# This raises an error for subscriptions to global event service.
subscribable_path = getPhysicalPathString(subscribable)
sub = list(self._subscriptions)
sub.remove((subscribable_path, event_type, filter))
self._subscriptions = tuple(sub)
=== Zope3/src/zope/app/event/__init__.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/event/__init__.py:1.2 Wed Dec 25 09:12:51 2002
+++ Zope3/src/zope/app/event/__init__.py Mon Dec 30 09:03:02 2002
@@ -2,14 +2,14 @@
#
# 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.
-#
+#
##############################################################################
"""
@@ -17,18 +17,27 @@
$Id$
"""
-from zope.interfaces.event import IEvent
-from zope.event import getEventService
+from zope.component import getService
+from zope.app.interfaces.event import IEvent
+from zope.app.event.globalservice import eventPublisher, checkEventType
-def globalSubscribe(subscriber, event_type=IEvent, filter=None, context=None):
- if context is None:
- context = subscriber
- return getEventService(None).globalSubscribe(
- subscriber, event_type, filter)
-
-def globalSubscribeMany(subscriber, event_types=(IEvent,),
- filter=None, context=None):
- if context is None: context=subscriber
- subscribe_func = getEventService(None).globalSubscribe
+def getEventService(context): # the "publish" service
+ return getService(context, 'Events')
+
+def publish(context, event):
+ return getEventService(context).publish(event)
+
+def globalSubscribe(subscriber, event_type=IEvent, filter=None):
+ return eventPublisher.globalSubscribe(subscriber, event_type, filter)
+
+def globalSubscribeMany(subscriber, event_types=(IEvent,), filter=None):
+ subscribe_func = eventPublisher.globalSubscribe
for event_type in event_types:
subscribe_func(subscriber, event_type, filter)
+
+def globalUnsubscribe(subscriber, event_type=None, filter=None):
+ return eventPublisher.unsubscribe(subscriber, event_type, filter)
+
+def globalListSubscriptions(subscriber, event_type=None):
+ return eventPublisher.listSubscriptions(subscriber, event_type)
+
=== Zope3/src/zope/app/event/configure.zcml 1.2 => 1.3 ===
--- Zope3/src/zope/app/event/configure.zcml:1.2 Wed Dec 25 09:12:51 2002
+++ Zope3/src/zope/app/event/configure.zcml Mon Dec 30 09:03:02 2002
@@ -1,12 +1,13 @@
<zopeConfigure
xmlns='http://namespaces.zope.org/zope'
- xmlns:browser='http://namespaces.zope.org/browser'
>
-<serviceType id='Events'
- interface='zope.interfaces.event.IEventService' />
+<serviceType
+ id='Events'
+ interface='zope.app.interfaces.event.IPublisher' />
-<service serviceType='Events'
- component='zope.app.event.globaleventservice.eventService' />
+<service
+ serviceType='Events'
+ component='zope.app.event.globalservice.eventPublisher' />
</zopeConfigure>
=== Zope3/src/zope/app/event/meta.zcml 1.2 => 1.3 ===
--- Zope3/src/zope/app/event/meta.zcml:1.2 Wed Dec 25 09:12:51 2002
+++ Zope3/src/zope/app/event/meta.zcml Mon Dec 30 09:03:02 2002
@@ -3,7 +3,7 @@
<directives namespace="http://namespaces.zope.org/event">
<directive name="subscribe" attributes="subscriber event_types filter"
- handler="zope.app.event.metaconfigure.subscribe" />
+ handler="zope.app.event.globalservice.subscribeDirective" />
</directives>
=== Removed File Zope3/src/zope/app/event/globaleventservice.py ===
=== Removed File Zope3/src/zope/app/event/logger.py ===
=== Removed File Zope3/src/zope/app/event/metaconfigure.py ===