PageRenderTime 26ms CodeModel.GetById 11ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/circuits/core/handlers.py

https://bitbucket.org/prologic/circuits/
Python | 128 lines | 96 code | 9 blank | 23 comment | 8 complexity | 35f09e28ee5df909fe9ba4d14f399a8f MD5 | raw file
  1# Package:  handlers
  2# Date:     11th April 2010
  3# Author:   James Mills, prologic at shortcircuit dot net dot au
  4
  5"""
  6This module define the @handler decorator/function and the HandlesType type.
  7"""
  8
  9from inspect import getargspec
 10from collections import Callable
 11
 12
 13def handler(*names, **kwargs):
 14    """Creates an Event Handler
 15
 16    This decorator can be applied to methods of classes derived from
 17    :class:`circuits.core.components.BaseComponent`. It marks the method as a
 18    handler for the events passed as arguments to the ``@handler`` decorator.
 19    The events are specified by their name.
 20
 21    The decorated method's arguments must match the arguments passed to the
 22    :class:`circuits.core.events.Event` on creation. Optionally, the
 23    method may have an additional first argument named *event*. If declared,
 24    the event object that caused the handler to be invoked is assigned to it.
 25
 26    By default, the handler is invoked by the component's root
 27    :class:`~.manager.Manager` for events that are propagated on the channel
 28    determined by the BaseComponent's *channel* attribute.
 29    This may be overridden by specifying a different channel as a keyword
 30    parameter of the decorator (``channel=...``).
 31
 32    Keyword argument ``priority`` influences the order in which handlers
 33    for a specific event are invoked. The higher the priority, the earlier
 34    the handler is executed.
 35
 36    If you want to override a handler defined in a base class of your
 37    component, you must specify ``override=True``, else your method becomes
 38    an additional handler for the event.
 39
 40    **Return value**
 41
 42    Normally, the results returned by the handlers for an event are simply
 43    collected in the :class:`circuits.core.events.Event`'s :attr:`value`
 44    attribute. As a special case, a handler may return a
 45    :class:`types.GeneratorType`. This signals to the dispatcher that the
 46    handler isn't ready to deliver a result yet.
 47    Rather, it has interrupted it's execution with a ``yield None``
 48    statement, thus preserving its current execution state.
 49
 50    The dispatcher saves the returned generator object as a task.
 51    All tasks are reexamined (i.e. their :meth:`next()` method is invoked)
 52    when the pending events have been executed.
 53
 54    This feature avoids an unnecessarily complicated chaining of event
 55    handlers. Imagine a handler A that needs the results from firing an
 56    event E in order to complete. Then without this feature, the final
 57    action of A would be to fire event E, and another handler for
 58    an event ``SuccessE`` would be required to complete handler A's
 59    operation, now having the result from invoking E available
 60    (actually it's even a bit more complicated).
 61
 62    Using this "suspend" feature, the handler simply fires event E and
 63    then yields ``None`` until e.g. it finds a result in E's :attr:`value`
 64    attribute. For the simplest scenario, there even is a utility
 65    method :meth:`circuits.core.manager.Manager.callEvent` that combines
 66    firing and waiting.
 67    """
 68
 69    def wrapper(f):
 70        if names and isinstance(names[0], bool) and not names[0]:
 71            f.handler = False
 72            return f
 73
 74        f.handler = True
 75
 76        f.names = names
 77        f.priority = kwargs.get("priority", 0)
 78        f.channel = kwargs.get("channel", None)
 79        f.override = kwargs.get("override", False)
 80
 81        args = getargspec(f)[0]
 82
 83        if args and args[0] == "self":
 84            del args[0]
 85        f.event = getattr(f, "event", bool(args and args[0] == "event"))
 86
 87        return f
 88
 89    return wrapper
 90
 91
 92class Unknown(object):
 93    """Unknown Dummy Component"""
 94
 95
 96def reprhandler(handler):
 97    format = "<handler[%s.%s] (%s.%s)>"
 98
 99    channel = getattr(handler, "channel", "*")
100    if channel is None:
101        channel = "*"
102
103    from circuits.core.manager import Manager
104    if isinstance(channel, Manager):
105        channel = "<instance of " + channel.__class__.__name__ + ">"
106
107    names = ".".join(handler.names)
108
109    instance = getattr(
110        handler, "im_self", getattr(
111            handler, "__self__", Unknown()
112        )
113    ).__class__.__name__
114
115    method = handler.__name__
116
117    return format % (channel, names, instance, method)
118
119
120class HandlerMetaClass(type):
121
122    def __init__(cls, name, bases, ns):
123        super(HandlerMetaClass, cls).__init__(name, bases, ns)
124
125        callables = (x for x in ns.items() if isinstance(x[1], Callable))
126        for name, callable in callables:
127            if not (name.startswith("_") or hasattr(callable, "handler")):
128                setattr(cls, name, handler(name)(callable))