/circuits/core/events.py

https://bitbucket.org/prologic/circuits/ · Python · 360 lines · 301 code · 27 blank · 32 comment · 14 complexity · f2e6ea269e8cb71a03a32624d60f573a MD5 · raw file

  1. # Package: events
  2. # Date: 11th April 2010
  3. # Author: James Mills, prologic at shortcircuit dot net dot au
  4. """
  5. This module defines the basic event class and common events.
  6. """
  7. from inspect import ismethod
  8. class EventType(type):
  9. __cache__ = {}
  10. def __new__(cls, name, bases, ns):
  11. key = (cls, name, bases)
  12. try:
  13. return cls.__cache__[key]
  14. except KeyError:
  15. cls = type.__new__(cls, name, bases, ns)
  16. setattr(cls, "name", ns.get("name", cls.__name__))
  17. return cls
  18. class Event(object):
  19. __metaclass__ = EventType
  20. channels = ()
  21. "The channels this message is sent to."
  22. parent = None
  23. notify = False
  24. success = False
  25. failure = False
  26. complete = False
  27. alert_done = False
  28. waitingHandlers = 0
  29. @classmethod
  30. def create(cls, name, *args, **kwargs):
  31. return type(cls)(name, (cls,), {})(*args, **kwargs)
  32. def child(self, name, *args, **kwargs):
  33. e = Event.create(
  34. "{0:s}_{1:s}".format(self.name, name), *args, **kwargs
  35. )
  36. e.parent = self
  37. return e
  38. def __init__(self, *args, **kwargs):
  39. """An event is a message send to one or more channels.
  40. It is eventually dispatched to all components
  41. that have handlers for one of the channels and the event type.
  42. All normal arguments and keyword arguments passed to the constructor
  43. of an event are passed on to the handler. When declaring a
  44. handler, its argument list must therefore match the arguments
  45. used for creating the event.
  46. Every event has a :attr:`name` attribute that is used for matching
  47. the event with the handlers.
  48. :cvar channels: an optional attribute that may be set before firing
  49. the event. If defined (usually as a class variable), the attribute
  50. specifies the channels that the event should be delivered
  51. to as a tuple. This overrides the default behavior
  52. of sending the event to the firing component's channel.
  53. When an event is fired, the value in this attribute
  54. is replaced for the instance with the channels that
  55. the event is actually sent to. This information may be used
  56. e.g. when the event is passed as a parameter to a handler.
  57. :ivar value: this is a :class:`circuits.core.values.Value`
  58. object that holds the results returned by the handlers invoked
  59. for the event.
  60. :var success: if this optional attribute is set to
  61. ``True``, an associated event ``success`` (original name
  62. with "_success" appended) will automatically be fired when all
  63. handlers for the event have been invoked successfully.
  64. :var success_channels: the success event is, by default, delivered
  65. to same channels as the successfully dispatched event itself.
  66. This may be overridden by specifying an alternative list of
  67. destinations using this attribute.
  68. :var complete: if this optional attribute is set to
  69. ``True``, an associated event ``complete`` (original name
  70. with "_complete" appended) will automatically be fired when all
  71. handlers for the event and all events fired by these handlers
  72. (recursively) have been invoked successfully.
  73. :var complete_channels: the complete event is, by default, delivered
  74. to same channels as the initially dispatched event itself.
  75. This may be overridden by specifying an alternative list of
  76. destinations using this attribute.
  77. """
  78. self.args = list(args)
  79. self.kwargs = kwargs
  80. self.uid = None
  81. self.value = None
  82. self.handler = None
  83. self.stopped = False
  84. self.cancelled = False
  85. if not hasattr(self, 'name'):
  86. self.name = self.__class__.__name__
  87. def __getstate__(self):
  88. odict = self.__dict__.copy()
  89. del odict["handler"]
  90. return odict
  91. def __setstate__(self, dict):
  92. self.__dict__.update(dict)
  93. def __le__(self, other):
  94. return False
  95. def __gt__(self, other):
  96. return False
  97. def __repr__(self):
  98. "x.__repr__() <==> repr(x)"
  99. if len(self.channels) > 1:
  100. channels = repr(self.channels)
  101. elif len(self.channels) == 1:
  102. channels = str(self.channels[0])
  103. else:
  104. channels = ""
  105. data = "%s %s" % (
  106. ", ".join(repr(arg) for arg in self.args),
  107. ", ".join("%s=%s" % (k, repr(v)) for k, v in self.kwargs.items())
  108. )
  109. return "<%s[%s] (%s)>" % (self.name, channels, data)
  110. def __getitem__(self, x):
  111. """x.__getitem__(y) <==> x[y]
  112. Get and return data from the event object requested by "x".
  113. If an int is passed to x, the requested argument from self.args
  114. is returned index by x. If a str is passed to x, the requested
  115. keyword argument from self.kwargs is returned keyed by x.
  116. Otherwise a TypeError is raised as nothing else is valid.
  117. """
  118. if isinstance(x, int):
  119. return self.args[x]
  120. elif isinstance(x, str):
  121. return self.kwargs[x]
  122. else:
  123. raise TypeError("Expected int or str, got %r" % type(x))
  124. def __setitem__(self, i, y):
  125. """x.__setitem__(i, y) <==> x[i] = y
  126. Modify the data in the event object requested by "x".
  127. If i is an int, the ith requested argument from self.args
  128. shall be changed to y. If i is a str, the requested value
  129. keyed by i from self.kwargs, shall by changed to y.
  130. Otherwise a TypeError is raised as nothing else is valid.
  131. """
  132. if isinstance(i, int):
  133. self.args[i] = y
  134. elif isinstance(i, str):
  135. self.kwargs[i] = y
  136. else:
  137. raise TypeError("Expected int or str, got %r" % type(i))
  138. def cancel(self):
  139. """Cancel the event from being processed (if not already)"""
  140. self.cancelled = True
  141. def stop(self):
  142. """Stop further processing of this event"""
  143. self.stopped = True
  144. class exception(Event):
  145. """exception Event
  146. This event is sent for any exceptions that occur during the execution
  147. of an event Handler that is not SystemExit or KeyboardInterrupt.
  148. :param type: type of exception
  149. :type type: type
  150. :param value: exception object
  151. :type value: exceptions.TypeError
  152. :param traceback: traceback of exception
  153. :type traceback: traceback
  154. :param handler: handler that raised the exception
  155. :type handler: @handler(<method>)
  156. :param fevent: event that failed
  157. :type fevent: event
  158. """
  159. def __init__(self, type, value, traceback, handler=None, fevent=None):
  160. super(exception, self).__init__(type, value, traceback,
  161. handler=handler, fevent=fevent)
  162. class started(Event):
  163. """started Event
  164. This Event is sent when a Component or Manager has started running.
  165. :param manager: The component or manager that was started
  166. :type manager: Component or Manager
  167. """
  168. def __init__(self, manager):
  169. super(started, self).__init__(manager)
  170. class stopped(Event):
  171. """stopped Event
  172. This Event is sent when a Component or Manager has stopped running.
  173. :param manager: The component or manager that has stopped
  174. :type manager: Component or Manager
  175. """
  176. def __init__(self, manager):
  177. super(stopped, self).__init__(manager)
  178. class signal(Event):
  179. """signal Event
  180. This Event is sent when a Component receives a signal.
  181. :param signo: The signal number received.
  182. :type int: An int value for the signal
  183. :param stack: The interrupted stack frame.
  184. :type object: A stack frame
  185. """
  186. def __init__(self, signo, stack):
  187. super(signal, self).__init__(signo, stack)
  188. class registered(Event):
  189. """registered Event
  190. This Event is sent when a Component has registered with another Component
  191. or Manager. This Event is only sent if the Component or Manager being
  192. registered which is not itself.
  193. :param component: The Component being registered
  194. :type component: Component
  195. :param manager: The Component or Manager being registered with
  196. :type manager: Component or Manager
  197. """
  198. def __init__(self, component, manager):
  199. super(registered, self).__init__(component, manager)
  200. class unregistered(Event):
  201. """unregistered Event
  202. This Event is sent when a Component has been unregistered from its
  203. Component or Manager.
  204. """
  205. class generate_events(Event):
  206. """generate_events Event
  207. This Event is sent by the circuits core. All components that generate
  208. timed events or events from external sources (e.g. data becoming
  209. available) should fire any pending events in their "generate_events"
  210. handler.
  211. The handler must either call :meth:`~stop` (*preventing other handlers
  212. from being called in the same iteration)
  213. or must invoke :meth:`~.reduce_time_left` with parameter 0.
  214. :param max_wait: maximum time available for generating events.
  215. :type time_left: float
  216. Components that actually consume time waiting for events to be generated,
  217. thus suspending normal execution, must provide a method ``resume``
  218. that interrupts waiting for events.
  219. """
  220. def __init__(self, lock, max_wait):
  221. super(generate_events, self).__init__()
  222. self._time_left = max_wait
  223. self._lock = lock
  224. @property
  225. def time_left(self):
  226. """
  227. The time left for generating events. A value less than 0
  228. indicates unlimited time. You should have only
  229. one component in your system (usually a poller component) that
  230. spends up to "time left" until it generates an event.
  231. """
  232. return self._time_left
  233. def reduce_time_left(self, time_left):
  234. """
  235. Update the time left for generating events. This is typically
  236. used by event generators that currently don't want to generate
  237. an event but know that they will within a certain time. By
  238. reducing the time left, they make sure that they are reinvoked
  239. when the time for generating the event has come (at the latest).
  240. This method can only be used to reduce the time left. If the
  241. parameter is larger than the current value of time left, it is
  242. ignored.
  243. If the time left is reduced to 0 and the event is currently
  244. being handled, the handler's *resume* method is invoked.
  245. """
  246. with self._lock:
  247. if time_left >= 0 and (self._time_left < 0
  248. or self._time_left > time_left):
  249. self._time_left = time_left
  250. if self._time_left == 0 and self.handler is not None:
  251. m = getattr(
  252. getattr(
  253. self.handler, "im_self", getattr(
  254. self.handler, "__self__"
  255. )
  256. ),
  257. "resume", None
  258. )
  259. if m is not None and ismethod(m):
  260. m()
  261. @property
  262. def lock(self):
  263. return self._lock