qubes: fix event framework

Two important fixes are in this commit: handlers from decorators are added when
class is defined (and not when class is instantiated); also multiple events can
be specified in the decorator.
This commit is contained in:
Wojtek Porczyk 2015-01-12 14:57:24 +01:00
parent 7971342811
commit 99edcb56c1
2 changed files with 28 additions and 35 deletions

View File

@ -10,7 +10,7 @@ etc.
import collections import collections
def handler(event): def handler(*events):
'''Event handler decorator factory. '''Event handler decorator factory.
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
@ -27,8 +27,7 @@ def handler(event):
''' '''
def decorator(f): def decorator(f):
f.ha_event = event f.ha_events = events
f.ha_bound = True
return f return f
return decorator return decorator
@ -43,7 +42,7 @@ def ishandler(o):
''' '''
return callable(o) \ return callable(o) \
and hasattr(o, 'ha_event') and hasattr(o, 'ha_events')
class EmitterMeta(type): class EmitterMeta(type):
@ -52,6 +51,24 @@ class EmitterMeta(type):
super(type, cls).__init__(name, bases, dict_) super(type, cls).__init__(name, bases, dict_)
cls.__handlers__ = collections.defaultdict(set) cls.__handlers__ = collections.defaultdict(set)
try:
propnames = set(prop.__name__ for prop in cls.get_props_list())
except AttributeError:
propnames = set()
for attr in dict_:
if attr in propnames:
# we have to be careful, not to getattr() on properties which
# may be unset
continue
attr = dict_[attr]
if not ishandler(attr):
continue
for event in attr.ha_events:
cls.add_handler(event, attr)
class Emitter(object): class Emitter(object):
'''Subject that can emit events '''Subject that can emit events
@ -63,23 +80,6 @@ class Emitter(object):
super(Emitter, self).__init__(*args, **kwargs) super(Emitter, self).__init__(*args, **kwargs)
self.events_enabled = True self.events_enabled = True
try:
propnames = set(prop.__name__ for prop in self.get_props_list())
except AttributeError:
propnames = set()
for attr in dir(self):
if attr in propnames:
# we have to be careful, not to getattr() on properties which
# may be unset
continue
attr = getattr(self, attr)
if not ishandler(attr):
continue
self.add_handler(attr.ha_event, attr)
@classmethod @classmethod
def add_handler(cls, event, handler): def add_handler(cls, event, handler):
@ -103,18 +103,11 @@ class Emitter(object):
return return
for cls in order: for cls in order:
# first fire bound (= our own) handlers, then handlers from extensions
if not hasattr(cls, '__handlers__'): if not hasattr(cls, '__handlers__'):
continue continue
for handler in sorted(cls.__handlers__[event], for handler in sorted(cls.__handlers__[event],
key=(lambda handler: hasattr(handler, 'ha_bound')), reverse=True): key=(lambda handler: hasattr(handler, 'ha_bound')), reverse=True):
if hasattr(handler, 'ha_bound'): handler(self, event, *args, **kwargs)
# this is our (bound) method, self is implicit
handler(event, *args, **kwargs)
else:
# 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): def fire_event(self, event, *args, **kwargs):

View File

@ -55,7 +55,7 @@ class Extension(object):
self.app.add_hook(attr.ha_event, attr) self.app.add_hook(attr.ha_event, attr)
def handler(event, vm=None, system=False): def handler(*events, **kwargs):
'''Event handler decorator factory. '''Event handler decorator factory.
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
@ -71,14 +71,14 @@ def handler(event, vm=None, system=False):
''' '''
def decorator(f): def decorator(f):
f.ho_event = event f.ha_events = events
if system: if kwargs.get('system', False):
f.ha_vm = None f.ha_vm = None
elif vm is None: elif 'vm' in kwargs:
f.ha_vm = qubes.vm.BaseVM f.ha_vm = kwargs['vm']
else: else:
f.ha_vm = vm f.ha_vm = qubes.vm.BaseVM
return f return f