qubes/events: fix event handling order

Events are divided into "pre" and "post" events. "Pre" events fire handlers in
MRO, "post" fire them in reverse.
This commit is contained in:
Wojtek Porczyk 2014-12-29 13:07:20 +01:00
parent 41fef46db2
commit 9fa3d60d0b
3 changed files with 48 additions and 16 deletions

View File

@ -510,9 +510,9 @@ class property(object):
value = self._type(value) value = self._type(value)
if has_oldvalue: if has_oldvalue:
instance.fire_event('property-pre-set:' + self.__name__, value, oldvalue) instance.fire_event_pre('property-pre-set:' + self.__name__, value, oldvalue)
else: else:
instance.fire_event('property-pre-set:' + self.__name__, value) instance.fire_event_pre('property-pre-set:' + self.__name__, value)
instance._init_property(self, value) instance._init_property(self, value)

View File

@ -92,24 +92,17 @@ class Emitter(object):
cls.__handlers__[event].add(handler) cls.__handlers__[event].add(handler)
def fire_event(self, event, *args, **kwargs): def _fire_event_in_order(self, order, event, *args, **kwargs):
'''Call all handlers for an event. '''Fire event for classes in given order.
Handlers are called for class and all parent classess, in method Do not use this method. Use :py:meth:`fire_event` or
resolution order. For each class first are called bound handlers :py:meth:`fire_event_pre`.
(specified in class definition), then handlers from extensions. Aside
from above, remaining order is undefined.
:param str event: event identificator
All *args* and *kwargs* are passed verbatim. They are different for
different events.
''' '''
if not self.events_enabled: if not self.events_enabled:
return return
for cls in self.__class__.__mro__: for cls in order:
# first fire bound (= our own) handlers, then handlers from extensions # first fire bound (= our own) handlers, then handlers from extensions
if not hasattr(cls, '__handlers__'): if not hasattr(cls, '__handlers__'):
continue continue
@ -122,3 +115,42 @@ class Emitter(object):
# this is from extension or hand-added, so we see method as # this is from extension or hand-added, so we see method as
# unbound, therefore we need to pass self # unbound, therefore we need to pass self
handler(self, event, *args, **kwargs) handler(self, event, *args, **kwargs)
def fire_event(self, event, *args, **kwargs):
'''Call all handlers for an event.
Handlers are called for class and all parent classess, in **reversed**
method resolution order. For each class first are called bound handlers
(specified in class definition), then handlers from extensions. Aside
from above, remaining order is undefined.
.. seealso::
:py:meth:`fire_event_pre`
:param str event: event identificator
All *args* and *kwargs* are passed verbatim. They are different for
different events.
'''
self._fire_event_in_order(reversed(self.__class__.__mro__), event, *args, **kwargs)
def fire_event_pre(self, event, *args, **kwargs):
'''Call all handlers for an event.
Handlers are called for class and all parent classess, in **true**
method resolution order. This is intended for ``-pre-`` events, where
order of invocation should be reversed.
.. seealso::
:py:meth:`fire_event`
:param str event: event identificator
All *args* and *kwargs* are passed verbatim. They are different for
different events.
'''
self._fire_event_in_order(self.__class__.__mro__, event, *args, **kwargs)

View File

@ -93,7 +93,7 @@ class DeviceCollection(object):
raise KeyError( raise KeyError(
'device {!r} of class {} already attached to {!r}'.format( 'device {!r} of class {} already attached to {!r}'.format(
device, self._class, self._vm)) device, self._class, self._vm))
self._vm.fire_event('device-pre-attached:{}'.format(self._class), device) self._vm.fire_event_pre('device-pre-attached:{}'.format(self._class), device)
self._set.add(device) self._set.add(device)
self._vm.fire_event('device-attached:{}'.format(self._class), device) self._vm.fire_event('device-attached:{}'.format(self._class), device)
@ -108,7 +108,7 @@ class DeviceCollection(object):
raise KeyError( raise KeyError(
'device {!r} of class {} not attached to {!r}'.format( 'device {!r} of class {} not attached to {!r}'.format(
device, self._class, self._vm)) device, self._class, self._vm))
self._vm.fire_event('device-pre-detached:{}'.format(self._class), device) self._vm.fire_event_pre('device-pre-detached:{}'.format(self._class), device)
self._set.remove(device) self._set.remove(device)
self._vm.fire_event('device-detached:{}'.format(self._class), device) self._vm.fire_event('device-detached:{}'.format(self._class), device)