PageRenderTime 45ms CodeModel.GetById 28ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/circuits/core/events.py

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