core-admin/qubes/events.py

111 lines
2.7 KiB
Python
Raw Normal View History

2014-11-13 14:38:41 +01:00
#!/usr/bin/python2 -O
2014-11-13 18:10:27 +01:00
'''Qubes events.
Events are fired when something happens, like VM start or stop, property change
etc.
'''
2014-11-13 14:38:41 +01:00
import collections
def handler(event):
'''Event handler decorator factory.
2014-11-13 18:10:27 +01:00
To hook an event, decorate a method in your plugin class with this
decorator.
.. note::
For hooking events from extensions, see :py:func:`qubes.ext.handler`.
2014-11-13 18:10:27 +01:00
:param str event: event type
'''
2014-11-13 14:38:41 +01:00
def decorator(f):
f.ha_event = event
f.ha_bound = True
2014-11-13 14:38:41 +01:00
return f
return decorator
def ishandler(o):
2014-11-13 18:10:27 +01:00
'''Test if a method is hooked to an event.
:param object o: suspected hook
:return: :py:obj:`True` when function is a hook, :py:obj:`False` otherwise
:rtype: bool
'''
2014-11-13 14:38:41 +01:00
return callable(o) \
and hasattr(o, 'ha_event')
2014-11-13 14:38:41 +01:00
2014-11-13 18:10:27 +01:00
class EmitterMeta(type):
'''Metaclass for :py:class:`Emitter`'''
def __init__(cls, name, bases, dict_):
super(type, cls).__init__(name, bases, dict_)
cls.__handlers__ = collections.defaultdict(set)
class Emitter(object):
'''Subject that can emit events
2014-11-13 18:10:27 +01:00
'''
__metaclass__ = EmitterMeta
2014-11-13 14:38:41 +01:00
def __init__(self, *args, **kwargs):
super(Emitter, self).__init__(*args, **kwargs)
self.events_enabled = True
try:
propnames = set(prop.__name__ for prop in self.get_props_list())
except AttributeError:
propnames = set()
2014-11-13 18:10:27 +01:00
for attr in dir(self):
if attr in propnames:
# we have to be careful, not to getattr() on properties which
# may be unset
continue
2014-11-13 18:10:27 +01:00
attr = getattr(self, attr)
if not ishandler(attr):
continue
self.add_handler(attr.ha_event, attr)
@classmethod
def add_handler(cls, event, handler):
'''Add event handler to subject's class
:param str event: event identificator
:param collections.Callable handler: handler callable
'''
cls.__handlers__[event].add(handler)
def fire_event(self, event, *args, **kwargs):
'''Call all handlers for an event
:param str event: event identificator
All *args* and *kwargs* are passed verbatim. They are different for
different events.
'''
2014-11-13 18:10:27 +01:00
if not self.events_enabled:
return
for handler in self.__handlers__[event]:
if hasattr(handler, 'ha_bound'):
# 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)