Browse Source

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.
Wojtek Porczyk 9 years ago
parent
commit
9fa3d60d0b
3 changed files with 48 additions and 16 deletions
  1. 2 2
      qubes/__init__.py
  2. 44 12
      qubes/events.py
  3. 2 2
      qubes/vm/__init__.py

+ 2 - 2
qubes/__init__.py

@@ -510,9 +510,9 @@ class property(object):
             value = self._type(value)
 
         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:
-            instance.fire_event('property-pre-set:' + self.__name__, value)
+            instance.fire_event_pre('property-pre-set:' + self.__name__, value)
 
 
         instance._init_property(self, value)

+ 44 - 12
qubes/events.py

@@ -92,24 +92,17 @@ class Emitter(object):
         cls.__handlers__[event].add(handler)
 
 
-    def fire_event(self, event, *args, **kwargs):
-        '''Call all handlers for an event.
-
-        Handlers are called for class and all parent classess, in 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.
+    def _fire_event_in_order(self, order, event, *args, **kwargs):
+        '''Fire event for classes in given order.
 
-        :param str event: event identificator
-
-        All *args* and *kwargs* are passed verbatim. They are different for
-        different events.
+        Do not use this method. Use :py:meth:`fire_event` or
+        :py:meth:`fire_event_pre`.
         '''
 
         if not self.events_enabled:
             return
 
-        for cls in self.__class__.__mro__:
+        for cls in order:
             # first fire bound (= our own) handlers, then handlers from extensions
             if not hasattr(cls, '__handlers__'):
                 continue
@@ -122,3 +115,42 @@ class Emitter(object):
                     # this is from extension or hand-added, so we see method as
                     # unbound, therefore we need to pass self
                     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)

+ 2 - 2
qubes/vm/__init__.py

@@ -93,7 +93,7 @@ class DeviceCollection(object):
             raise KeyError(
                 'device {!r} of class {} already attached to {!r}'.format(
                     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._vm.fire_event('device-attached:{}'.format(self._class), device)
 
@@ -108,7 +108,7 @@ class DeviceCollection(object):
             raise KeyError(
                 'device {!r} of class {} not attached to {!r}'.format(
                     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._vm.fire_event('device-detached:{}'.format(self._class), device)