qubes-events.rst 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. :py:mod:`qubes.events` -- Qubes events
  2. ======================================
  3. Some objects in qubes (most notably domains) emit events. You may hook them and
  4. execute your code when particular event is fired. Events in qubes are added
  5. class-wide -- it is not possible to add event handler to one instance only, you
  6. have to add handler for whole class.
  7. Firing events
  8. -------------
  9. Events are fired by calling :py:meth:`qubes.events.Emitter.fire_event`. The
  10. first argument is event name (a string). You can fire any event you wish, the
  11. names are not checked in any way, however each class' documentation tells what
  12. standard events will be fired on it. The rest of arguments are dependent on the
  13. particular event in question -- they are passed as-is to handlers.
  14. Event handlers are fired in reverse method resolution order, that is, first for
  15. parent class and then for it's child. For each class, first are called handlers
  16. defined in it's source, then handlers from extensions and last the callers added
  17. manually.
  18. There is second method, :py:meth:`qubes.events.Emitter.fire_event_pre`, which
  19. fires events in reverse order. It is suitable for events fired before some
  20. action is performed. You may at your own responsibility raise exceptions from
  21. such events to try to prevent such action.
  22. Handling events
  23. ---------------
  24. There are several ways to handle events. In all cases you supply a callable
  25. (most likely function or method) that will be called when someone fires the
  26. event. The first argument passed to the callable will be the object instance on
  27. which the event was fired and the second one is the event name. The rest are
  28. passed from :py:meth:`qubes.events.Emitter.fire_event` as described previously.
  29. One callable can handle more than one event.
  30. The easiest way to hook an event is to invoke
  31. :py:meth:`qubes.events.Emitter.add_handler` classmethod.
  32. .. code-block:: python
  33. import qubes.events
  34. class MyClass(qubes.events.Emitter):
  35. pass
  36. def event_handler(subject, event):
  37. if event == 'event1':
  38. print('Got event 1')
  39. elif event == 'event2':
  40. print('Got event 2')
  41. MyClass.add_handler('event1', event_handler)
  42. MyClass.add_handler('event2', event_handler)
  43. o = MyClass()
  44. o.fire_event('event1')
  45. If you wish to define handler in the class definition, the best way is to use
  46. :py:func:`qubes.events.handler` decorator.
  47. .. code-block:: python
  48. import qubes.events
  49. class MyClass(qubes.events.Emitter):
  50. @qubes.events.handler('event1', 'event2')
  51. def event_handler(self, event):
  52. if event == 'event1':
  53. print('Got event 1')
  54. elif event == 'event2':
  55. print('Got event 2')
  56. o = MyClass()
  57. o.fire_event('event1')
  58. .. TODO: extensions
  59. Handling events with variable signature
  60. ---------------------------------------
  61. Some events are specified with variable signature (i.e. they may have different
  62. number of arguments on each call to handlers). You can write handlers just like
  63. every other python function with variable signature.
  64. .. code-block:: python
  65. import qubes
  66. def on_property_change(subject, event, name, newvalue, oldvalue=None):
  67. if oldvalue is None:
  68. print('Property {} initialised to {!r}'.format(name, newvalue))
  69. else:
  70. print('Property {} changed {!r} -> {!r}'.format(name, oldvalue, newvalue))
  71. qubes.Qubes.add_handler('property-set:default_netvm')
  72. If you expect :py:obj:`None` to be a reasonable value of the property, you have
  73. a problem. One way to solve it is to invent your very own, magic
  74. :py:class:`object` instance.
  75. .. code-block:: python
  76. import qubes
  77. MAGIC_NO_VALUE = object()
  78. def on_property_change(subject, event, name, newvalue, oldvalue=MAGIC_NO_VALUE):
  79. if oldvalue is MAGIC_NO_VALUE:
  80. print('Property {} initialised to {!r}'.format(name, newvalue))
  81. else:
  82. print('Property {} changed {!r} -> {!r}'.format(name, oldvalue, newvalue))
  83. qubes.Qubes.add_handler('property-set:default_netvm')
  84. There is no possible way of collision other than intentionally passing this very
  85. object (not even passing similar featureless ``object()``), because ``is``
  86. python syntax checks object's :py:meth:`id`\ entity, which will be different for
  87. each :py:class:`object` instance.
  88. Module contents
  89. ---------------
  90. .. automodule:: qubes.events
  91. :members:
  92. :show-inheritance:
  93. .. vim: ts=3 sw=3 et