qubes/events: Rework firing events for effect

From now, the handlers should yield their values, not return.
This commit is contained in:
Wojtek Porczyk 2016-04-08 12:35:11 +02:00
parent 6c2f675b5c
commit c5962910d0
3 changed files with 62 additions and 4 deletions

View File

@ -26,9 +26,8 @@ fires events in reverse order. It is suitable for events fired before some
action is performed. You may at your own responsibility raise exceptions from action is performed. You may at your own responsibility raise exceptions from
such events to try to prevent such action. such events to try to prevent such action.
Event handlers may return a value. Those values are aggregated and returned Events handlers may yield values. Those values are aggregated and returned
to the caller as a list of those values. The order of this list is undefined. to the caller as a list of those values. See below for details.
:py:obj:`None` values are omitted.
Handling events Handling events
--------------- ---------------
@ -125,6 +124,41 @@ python syntax checks object's :py:meth:`id`\ entity, which will be different for
each :py:class:`object` instance. each :py:class:`object` instance.
Returning values from events
----------------------------
Some events may be called to collect values from the handlers. For example the
event ``is-fully-usable`` allows plugins to report a domain as not fully usable.
Such handlers, instead of returning :py:obj:`None` (which is the default when
the function does not include ``return`` statement), should return an iterable
or itself be a generator. Those values are aggregated from all handlers and
returned to the caller as list. The order of this list is undefined.
.. code-block:: python
import qubes.events
class MyClass(qubes.events.Emitter):
@qubes.events.handler('event1')
def event1_handler1(self, event):
# do not return anything, equivalent to "return" and "return None"
pass
@qubes.events.handler('event1')
def event1_handler2(self, event):
yield 'aqq'
yield 'zxc'
@qubes.events.handler('event1')
def event1_handler3(self, event):
return ('123', '456')
o = MyClass()
# returns ['aqq', 'zxc', '123', '456'], possibly not in order
effect = o.fire_event('event1')
Module contents Module contents
--------------- ---------------

View File

@ -138,7 +138,7 @@ class Emitter(object):
reverse=True): reverse=True):
effect = func(self, event, *args, **kwargs) effect = func(self, event, *args, **kwargs)
if effect is not None: if effect is not None:
effects.append(effect) effects.extend(effect)
return effects return effects

View File

@ -58,3 +58,27 @@ class TC_00_Emitter(qubes.tests.QubesTestCase):
emitter.events_enabled = True emitter.events_enabled = True
emitter.fire_event('testevent') emitter.fire_event('testevent')
self.assertTrue(emitter.testevent_fired) self.assertTrue(emitter.testevent_fired)
def test_002_fire_for_effect(self):
class TestEmitter(qubes.events.Emitter):
@qubes.events.handler('testevent')
def on_testevent_1(self, event):
pass
@qubes.events.handler('testevent')
def on_testevent_2(self, event):
yield 'testvalue1'
yield 'testvalue2'
@qubes.events.handler('testevent')
def on_testevent_3(self, event):
return ('testvalue3', 'testvalue4')
emitter = TestEmitter()
emitter.events_enabled = True
emitter.fire_event('testevent')
effect = emitter.fire_event('testevent')
self.assertItemsEqual(effect,
('testvalue1', 'testvalue2', 'testvalue3', 'testvalue4'))