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:
parent
a66c9afb18
commit
5a39e77708
@ -62,6 +62,7 @@ Note that your handler will be called for all instances of this class.
|
|||||||
|
|
||||||
.. TODO: extensions
|
.. TODO: extensions
|
||||||
.. TODO: add/remove_handler
|
.. TODO: add/remove_handler
|
||||||
|
.. TODO: wildcards (property-set:*)
|
||||||
|
|
||||||
|
|
||||||
Handling events with variable signature
|
Handling events with variable signature
|
||||||
|
@ -25,6 +25,7 @@ etc.
|
|||||||
'''
|
'''
|
||||||
import asyncio
|
import asyncio
|
||||||
import collections
|
import collections
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
@ -35,14 +36,15 @@ def handler(*events):
|
|||||||
To hook an event, decorate a method in your plugin class with this
|
To hook an event, decorate a method in your plugin class with this
|
||||||
decorator.
|
decorator.
|
||||||
|
|
||||||
Some event handlers may be defined as coroutine. In such a case, *async*
|
Some event handlers may be defined as coroutine. In such a case
|
||||||
should be set to :py:obj:``True``.
|
:py:func:`asyncio.coroutine` decorator should be used after this one,
|
||||||
|
i.e. you should decorate a coroutine.
|
||||||
See appropriate event documentation for details.
|
See appropriate event documentation for details.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
For hooking events from extensions, see :py:func:`qubes.ext.handler`.
|
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):
|
def decorator(func):
|
||||||
@ -155,9 +157,9 @@ class Emitter(object, metaclass=EmitterMeta):
|
|||||||
handlers_dict = i.__handlers__
|
handlers_dict = i.__handlers__
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
handlers = handlers_dict.get(event, set())
|
handlers = [h_func for h_name, h_func_set in handlers_dict.items()
|
||||||
if '*' in handlers_dict:
|
for h_func in h_func_set
|
||||||
handlers = handlers_dict['*'] | handlers
|
if fnmatch.fnmatch(event, h_name)]
|
||||||
for func in sorted(handlers,
|
for func in sorted(handlers,
|
||||||
key=(lambda handler: hasattr(handler, 'ha_bound')),
|
key=(lambda handler: hasattr(handler, 'ha_bound')),
|
||||||
reverse=True):
|
reverse=True):
|
||||||
|
@ -165,3 +165,29 @@ class TC_00_Emitter(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
self.assertCountEqual(effect,
|
self.assertCountEqual(effect,
|
||||||
('testvalue1', 'testvalue2', 'testvalue3', 'testvalue4'))
|
('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)
|
||||||
|
Loading…
Reference in New Issue
Block a user