diff -r d77f1ae3786c -r 43b2279b06e1 tuikit/events.py --- a/tuikit/events.py Wed Jan 02 11:48:36 2013 +0100 +++ b/tuikit/events.py Fri Jan 04 00:13:59 2013 +0100 @@ -9,6 +9,7 @@ """ import logging +import inspect class Event: @@ -88,57 +89,69 @@ """Event emitter mixin class.""" def add_events(self, *events): - """Add events which may be registered by user. + """Add events which may be emitted on instances of this class. This should be called only by subclasses. - This serves also as initializer, other methods of Emitter + It also serves as initializer, other methods of Emitter will not work if add_events was not called. *events -- Arguments must be given in pairs. Each pair consists of event_name, event_class: - event_name -- a string used in connect(), emit() + event_name -- a string used in add_handler(), emit() etc. event_class -- class of event payload + It will also inspect base classes of instance on which add_events() + is called, adding all methods meeting naming convention to handler list. + The convention for method names is "on_event_name", "after_event_name". + Prefix on/after determines whether the handler is added as first or last. + """ + if len(events) % 2: + raise ValueError('add_events(): Event names and classes must be passed in pairs.') if not hasattr(self, '_event_handlers'): self._event_handlers = dict() self._event_class = dict() for event_name, event_class in zip(events[::2], events[1::2]): + if not isinstance(event_name, str): + raise TypeError('add_events(): Event name must be a string: %r.' % event_name) + if not issubclass(event_class, Event): + raise TypeError('add_events(): Class %s does not inherit from Event.' % event_class) self._event_handlers[event_name] = [] self._event_class[event_name] = event_class - # add default dummy handler if no handler exists for this event - handler_name = '_handle_' + event_name - if not hasattr(Emitter, handler_name): - setattr(Emitter, handler_name, lambda self, ev: False) + self._add_default_handlers(event_name) - def connect(self, event_name, handler): - """Connect event handler to event name. + def add_handler(self, event_name, handler, last=False): + """Add handler to event name. - Add handler to the end of handler list. + last=False -- Add handler as first in handler list. + last=True -- Add handler to the end of handler list. """ if event_name in self._event_handlers: - self._event_handlers[event_name].append(handler) + if last: + self._event_handlers[event_name].append(handler) + else: + self._event_handlers[event_name].insert(0, handler) else: raise KeyError('Unknown event: %s', event_name) - def disconnect(self, event_name, handler=None): - """Remove event handler from the list. - - If no handler is given, remove all handlers. - - """ + def remove_handler(self, event_name, handler): + """Remove event handler from the list.""" if event_name in self._event_handlers: - if handler: - self._event_handlers[event_name].remove(handler) - else: - self._event_handlers[event_name][:] = [] + self._event_handlers[event_name].remove(handler) else: raise KeyError('Unknown event: %s', event_name) - def is_connected(self, event_name): - """Test if any handlers are connected to event name. + def remove_all_handlers(self, event_name): + """Remove all handlers for event.""" + if event_name in self._event_handlers: + self._event_handlers[event_name][:] = [] + else: + raise KeyError('Unknown event: %s', event_name) + + def has_handlers(self, event_name): + """Test if any handlers are attached to event name. Return True if event handler list is not empty, False otherwise. @@ -153,17 +166,20 @@ """Emit the event. Call all handlers from event's handler list, - starting from first added handler. + starting from last added handler, going to those added + before, ending with those added with last=True. + + Stop if any handler returns True. Return True when one of the handlers returns True, False otherwise. - This creates new instance of event_class given to + Creates new instance of event_class given to add_events() and passes all arguments after event_name to its __init__ method. Unless first of these arguments is Event instance - in which case no object is created and the instance + in which case no object is created and the Event instance is passed to handlers. """ @@ -172,18 +188,50 @@ self.__class__.__name__, getattr(self, 'name', None) or id(self)) # create event from specified event class, or use first argument - if len(args) and isinstance(args[0], Event): + if len(args) == 1 and isinstance(args[0], Event): event = args[0] else: event = self._event_class[event_name](*args, **kwargs) + # set originator to instance on which emit() was called event.originator = self - # try default handler, stop if satisfied - handled = getattr(self, '_handle_' + event_name)(event) - if handled: - return True - # try custom handlers, stop if satisfied + # call handlers from first to last, stop if satisfied for handler in self._event_handlers[event_name]: handled = handler(event) if handled: return True + return False + def _add_default_handlers(self, event_name): + """Add default handlers from the instance and its base classes. + + Handlers are looked up in methods by their names. + Method name is composited from prefix and event_name. + Prefix is one of "on_" or "after_". + Depending on prefix the handler is added to top or bottom of handler stack. + + See _add_default_handler method for more information. + + """ + for cls in reversed(inspect.getmro(self.__class__)): + self._add_default_handler(cls, 'on_', event_name, last=False) + self._add_default_handler(cls, 'after_', event_name, last=True) + + def _add_default_handler(self, cls, prefix, event_name, last): + """Add handler from one of base classes to handler list. + + cls -- one of self's base classes + prefix -- method name prefix + event_name -- event name + last -- if True, add handler to end of handler list (will be called last) + + Look for method of name prefix + event_name in class 'cls', + if exists, add it to handler list. + + """ + method_name = prefix + event_name + if method_name in cls.__dict__: + unbound_handler = cls.__dict__[method_name] + if callable(unbound_handler): + handler = unbound_handler.__get__(self, cls) + self.add_handler(event_name, handler, last) +