events: add support for wildcard event handlers

Support registering handlers for more flexible wildcard events: not only
'*', but also 'something*'. This allows to register handlers for
'property-set:*' and such.
This commit is contained in:
Marek Marczykowski-Górecki 2018-01-06 00:40:19 +01:00
parent a66c9afb18
commit 5a39e77708
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 35 additions and 6 deletions

View File

@ -62,6 +62,7 @@ Note that your handler will be called for all instances of this class.
.. TODO: extensions
.. TODO: add/remove_handler
.. TODO: wildcards (property-set:*)
Handling events with variable signature

View File

@ -25,6 +25,7 @@ etc.
'''
import asyncio
import collections
import fnmatch
import itertools
@ -35,14 +36,15 @@ def handler(*events):
To hook an event, decorate a method in your plugin class with this
decorator.
Some event handlers may be defined as coroutine. In such a case, *async*
should be set to :py:obj:``True``.
Some event handlers may be defined as coroutine. In such a case
:py:func:`asyncio.coroutine` decorator should be used after this one,
i.e. you should decorate a coroutine.
See appropriate event documentation for details.
.. note::
For hooking events from extensions, see :py:func:`qubes.ext.handler`.
:param str events: events
:param str events: events names, can contain basic wildcards (`*`, `?`)
'''
def decorator(func):
@ -155,9 +157,9 @@ class Emitter(object, metaclass=EmitterMeta):
handlers_dict = i.__handlers__
except AttributeError:
continue
handlers = handlers_dict.get(event, set())
if '*' in handlers_dict:
handlers = handlers_dict['*'] | handlers
handlers = [h_func for h_name, h_func_set in handlers_dict.items()
for h_func in h_func_set
if fnmatch.fnmatch(event, h_name)]
for func in sorted(handlers,
key=(lambda handler: hasattr(handler, 'ha_bound')),
reverse=True):

View File

@ -165,3 +165,29 @@ class TC_00_Emitter(qubes.tests.QubesTestCase):
self.assertCountEqual(effect,
('testvalue1', 'testvalue2', 'testvalue3', 'testvalue4'))
def test_006_wildcard(self):
# need something mutable
testevent_fired = [0]
def on_foobar(subject, event, *args, **kwargs):
# pylint: disable=unused-argument
testevent_fired[0] += 1
def on_foo(subject, event, *args, **kwargs):
# pylint: disable=unused-argument
testevent_fired[0] += 1
emitter = qubes.events.Emitter()
emitter.add_handler('foo:*', on_foo)
emitter.add_handler('foo:bar', on_foobar)
emitter.events_enabled = True
emitter.fire_event('foo:testevent')
self.assertEqual(testevent_fired[0], 1)
emitter.fire_event('foo:bar')
# now foo:bar and foo:* should be executed
self.assertEqual(testevent_fired[0], 3)
emitter.fire_event('foo:')
self.assertEqual(testevent_fired[0], 4)
emitter.fire_event('testevent')
self.assertEqual(testevent_fired[0], 4)